BEP3: support multiple assets (#616)

* wip: refactor to allow multiple bep3 assets

* update tests

* sims: validate asset before claiming

* move asset supply to params

* update tests

* fix sims

* fix tests

* wip: add migration from v0.9 -> v0.10 bep3

* fix build and migration tests

* nit: rename file

* move asset supply out of params

* update committee tests

* fix sims

* address review comments

* address review comments

* address review comments
This commit is contained in:
Kevin Davis 2020-08-17 11:06:59 -04:00 committed by GitHub
parent 928ce5f064
commit 790753f156
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1434 additions and 1281 deletions

View File

@ -8,10 +8,10 @@ const (
// Default simulation operation weights for messages and gov proposals
const (
DefaultWeightMsgPlaceBid int = 75
DefaultWeightMsgCreateAtomicSwap int = 50
DefaultWeightMsgUpdatePrices int = 50
DefaultWeightMsgCdp int = 100
DefaultWeightMsgClaimReward int = 50
OpWeightSubmitCommitteeChangeProposal int = 50
DefaultWeightMsgPlaceBid int = 20
DefaultWeightMsgCreateAtomicSwap int = 20
DefaultWeightMsgUpdatePrices int = 20
DefaultWeightMsgCdp int = 20
DefaultWeightMsgClaimReward int = 20
OpWeightSubmitCommitteeChangeProposal int = 20
)

View File

@ -3,6 +3,7 @@ package app
import (
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/require"
@ -110,6 +111,33 @@ func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) T
return tApp
}
// InitializeFromGenesisStatesWithTime calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states and genesis Time
func (tApp TestApp) InitializeFromGenesisStatesWithTime(genTime time.Time, genesisStates ...GenesisState) TestApp {
// Create a default genesis state and overwrite with provided values
genesisState := NewDefaultGenesisState()
for _, state := range genesisStates {
for k, v := range state {
genesisState[k] = v
}
}
// Initialize the chain
stateBytes, err := codec.MarshalJSONIndent(tApp.cdc, genesisState)
if err != nil {
panic(err)
}
tApp.InitChain(
abci.RequestInitChain{
Time: genTime,
Validators: []abci.ValidatorUpdate{},
AppStateBytes: stateBytes,
},
)
tApp.Commit()
tApp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: tApp.LastBlockHeight() + 1}})
return tApp
}
func (tApp TestApp) CheckBalance(t *testing.T, ctx sdk.Context, owner sdk.AccAddress, expectedCoins sdk.Coins) {
acc := tApp.GetAccountKeeper().GetAccount(ctx, owner)
require.NotNilf(t, acc, "account with address '%s' doesn't exist", owner)

View File

@ -14,7 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/supply"
"github.com/kava-labs/kava/x/auction"
"github.com/kava-labs/kava/x/bep3"
v0_8bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9"
"github.com/kava-labs/kava/x/cdp"
"github.com/kava-labs/kava/x/committee"
"github.com/kava-labs/kava/x/incentive"
@ -108,23 +108,23 @@ func addAuctionState(cdc *codec.Codec, appState genutil.AppMap) {
}
func addBep3State(cdc *codec.Codec, appState genutil.AppMap) {
appState[bep3.ModuleName] = cdc.MustMarshalJSON(bep3.NewGenesisState(
bep3.NewParams(
appState[v0_8bep3.ModuleName] = cdc.MustMarshalJSON(v0_8bep3.NewGenesisState(
v0_8bep3.NewParams(
mustAccAddressFromBech32(deputyAddressBech32),
bep3.DefaultBnbDeputyFixedFee,
bep3.DefaultMinAmount,
bep3.DefaultMaxAmount,
bep3.DefaultMinBlockLock,
bep3.DefaultMaxBlockLock,
bep3.AssetParams{{
v0_8bep3.DefaultBnbDeputyFixedFee,
v0_8bep3.DefaultMinAmount,
v0_8bep3.DefaultMaxAmount,
v0_8bep3.DefaultMinBlockLock,
v0_8bep3.DefaultMaxBlockLock,
v0_8bep3.AssetParams{{
Denom: bnbDenom,
CoinID: 714,
Limit: sdk.NewInt(4_000_000_000_000),
Active: true,
}},
),
bep3.AtomicSwaps{},
bep3.AssetSupplies{},
v0_8bep3.AtomicSwaps{},
v0_8bep3.AssetSupplies{},
))
}
@ -203,8 +203,8 @@ func addCommitteeState(cdc *codec.Codec, appState genutil.AppMap) {
Key: string(auction.KeyIncrementCollateral),
},
{
Subspace: bep3.ModuleName,
Key: string(bep3.KeySupportedAssets),
Subspace: v0_8bep3.ModuleName,
Key: string(v0_8bep3.KeySupportedAssets),
},
{
Subspace: cdp.ModuleName,

68
migrate/v0_11/migrate.go Normal file
View File

@ -0,0 +1,68 @@
package v0_11
import (
sdk "github.com/cosmos/cosmos-sdk/types"
v0_11bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_11"
v0_9bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9"
)
// MigrateBep3 migrates from a v0.9 (or v0.10) bep3 genesis state to a v0.11 bep3 genesis state
func MigrateBep3(oldGenState v0_9bep3.GenesisState) v0_11bep3.GenesisState {
var assetParams v0_11bep3.AssetParams
v0_9Params := oldGenState.Params
for _, asset := range v0_9Params.SupportedAssets {
v10AssetParam := v0_11bep3.AssetParam{
Active: asset.Active,
Denom: asset.Denom,
CoinID: asset.CoinID,
DeputyAddress: v0_9Params.BnbDeputyAddress,
FixedFee: v0_9Params.BnbDeputyFixedFee,
MinSwapAmount: v0_9Params.MinAmount,
MaxSwapAmount: v0_9Params.MaxAmount,
MinBlockLock: v0_9Params.MinBlockLock,
MaxBlockLock: v0_9Params.MaxBlockLock,
SupplyLimit: v0_11bep3.AssetSupply{
SupplyLimit: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
CurrentSupply: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
IncomingSupply: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
OutgoingSupply: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
},
}
assetParams = append(assetParams, v10AssetParam)
}
for _, supply := range oldGenState.AssetSupplies {
for _, asset := range assetParams {
if asset.Denom == supply.Denom {
asset.SupplyLimit.SupplyLimit = supply.SupplyLimit
asset.SupplyLimit.CurrentSupply = supply.CurrentSupply
asset.SupplyLimit.IncomingSupply = supply.IncomingSupply
asset.SupplyLimit.OutgoingSupply = supply.OutgoingSupply
}
}
}
var swaps v0_11bep3.AtomicSwaps
for _, oldSwap := range oldGenState.AtomicSwaps {
newSwap := v0_11bep3.AtomicSwap{
Amount: oldSwap.Amount,
RandomNumberHash: oldSwap.RandomNumberHash,
ExpireHeight: oldSwap.ExpireHeight,
Timestamp: oldSwap.Timestamp,
Sender: oldSwap.Sender,
Recipient: oldSwap.Recipient,
SenderOtherChain: oldSwap.SenderOtherChain,
RecipientOtherChain: oldSwap.RecipientOtherChain,
ClosedBlock: oldSwap.ClosedBlock,
Status: v0_11bep3.SwapStatus(oldSwap.Status),
CrossChain: oldSwap.CrossChain,
Direction: v0_11bep3.SwapDirection(oldSwap.Direction),
}
swaps = append(swaps, newSwap)
}
return v0_11bep3.GenesisState{
Params: v0_11bep3.Params{
AssetParams: assetParams},
AtomicSwaps: swaps,
}
}

View File

@ -31,7 +31,7 @@ import (
v032tendermint "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_32"
v033tendermint "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_33"
"github.com/kava-labs/kava/x/auction"
"github.com/kava-labs/kava/x/bep3"
v0_8bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9"
"github.com/kava-labs/kava/x/cdp"
"github.com/kava-labs/kava/x/committee"
"github.com/kava-labs/kava/x/incentive"
@ -90,7 +90,7 @@ func MigrateAppState(v0_3AppState v038genutil.AppMap) v038genutil.AppMap {
// migrate new modules (by adding new gen states)
v0_8AppState[auction.ModuleName] = v0_8Codec.MustMarshalJSON(auction.DefaultGenesisState())
v0_8AppState[bep3.ModuleName] = v0_8Codec.MustMarshalJSON(bep3.DefaultGenesisState())
v0_8AppState[v0_8bep3.ModuleName] = v0_8Codec.MustMarshalJSON(v0_8bep3.DefaultGenesisState())
v0_8AppState[cdp.ModuleName] = v0_8Codec.MustMarshalJSON(cdp.DefaultGenesisState())
v0_8AppState[committee.ModuleName] = v0_8Codec.MustMarshalJSON(committee.DefaultGenesisState())
v0_8AppState[incentive.ModuleName] = v0_8Codec.MustMarshalJSON(incentive.DefaultGenesisState())

View File

@ -9,7 +9,4 @@ import (
func BeginBlocker(ctx sdk.Context, k Keeper) {
k.UpdateExpiredAtomicSwaps(ctx)
k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx)
if ctx.BlockTime().After(SupplyLimitUpgradeTime) {
k.UpdateAssetSupplies(ctx)
}
}

View File

@ -2,7 +2,6 @@ package bep3_test
import (
"testing"
"time"
"github.com/stretchr/testify/suite"
@ -31,14 +30,14 @@ func (suite *ABCITestSuite) SetupTest() {
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
// Set up auth GenesisState
_, addrs := app.GeneratePrivKeyAddressPairs(11)
_, addrs := app.GeneratePrivKeyAddressPairs(12)
coins := []sdk.Coins{}
for j := 0; j < 11; j++ {
for j := 0; j < 12; j++ {
coins = append(coins, cs(c("bnb", 10000000000), c("ukava", 10000000000)))
}
authGS := app.NewAuthGenState(addrs, coins)
// Initialize test app
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(addrs[0]))
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(addrs[11]))
suite.ctx = ctx
suite.app = tApp
@ -61,12 +60,12 @@ func (suite *ABCITestSuite) ResetKeeper() {
// Create atomic swap and check err to confirm creation
err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight,
suite.addrs[0], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain,
suite.addrs[11], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain,
amount, true)
suite.Nil(err)
// Store swap's calculated ID and secret random number
swapID := bep3.CalculateSwapID(randomNumberHash, suite.addrs[0], TestSenderOtherChain)
swapID := bep3.CalculateSwapID(randomNumberHash, suite.addrs[11], TestSenderOtherChain)
swapIDs = append(swapIDs, swapID)
randomNumbers = append(randomNumbers, randomNumber[:])
}
@ -240,61 +239,6 @@ func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongterm
}
}
func (suite *ABCITestSuite) TestBeginBlocker_UpdateAssetSupplies() {
// set new asset limit in the params
newBnbLimit := c("bnb", 100)
params := suite.keeper.GetParams(suite.ctx)
for i := range params.SupportedAssets {
if params.SupportedAssets[i].Denom != newBnbLimit.Denom {
continue
}
params.SupportedAssets[i].Limit = newBnbLimit.Amount
}
suite.keeper.SetParams(suite.ctx, params)
// store the old limit for future reference
supply, found := suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom))
suite.True(found)
oldBnbLimit := supply.SupplyLimit
// run before the upgrade time, check limit was not changed
bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime.Add(-time.Hour)), suite.keeper)
supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom))
suite.True(found)
suite.True(supply.SupplyLimit.IsEqual(oldBnbLimit))
// run at precise upgrade time, check limit was not changed
bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime), suite.keeper)
supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom))
suite.True(found)
suite.True(supply.SupplyLimit.IsEqual(oldBnbLimit))
// run after upgrade time, check limit was changed
bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime.Add(time.Nanosecond)), suite.keeper)
supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom))
suite.True(found)
suite.True(supply.SupplyLimit.IsEqual(newBnbLimit))
// run again with new params, check limit was updated to new param limit
finalBnbLimit := c("bnb", 5000000000000)
params = suite.keeper.GetParams(suite.ctx)
for i := range params.SupportedAssets {
if params.SupportedAssets[i].Denom != finalBnbLimit.Denom {
continue
}
params.SupportedAssets[i].Limit = finalBnbLimit.Amount
}
suite.keeper.SetParams(suite.ctx, params)
bep3.BeginBlocker(suite.ctx.WithBlockTime(bep3.SupplyLimitUpgradeTime.Add(time.Hour)), suite.keeper)
supply, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom))
suite.True(found)
suite.True(supply.SupplyLimit.IsEqual(finalBnbLimit))
}
func TestABCITestSuite(t *testing.T) {
suite.Run(t, new(ABCITestSuite))
}

View File

@ -1,140 +1,146 @@
package bep3
import (
"github.com/kava-labs/kava/x/bep3/client/rest"
"github.com/kava-labs/kava/x/bep3/keeper"
"github.com/kava-labs/kava/x/bep3/types"
)
// nolint
// autogenerated code using github.com/rigelrozanski/multitool
// aliases generated for the following subdirectories:
// ALIASGEN: github.com/kava-labs/kava/x/bep3/keeper
// ALIASGEN: github.com/kava-labs/kava/x/bep3/types
const (
AddrByteCount = types.AddrByteCount
AttributeKeyAmount = types.AttributeKeyAmount
AttributeKeyAtomicSwapID = types.AttributeKeyAtomicSwapID
AttributeKeyAtomicSwapIDs = types.AttributeKeyAtomicSwapIDs
AttributeKeyClaimSender = types.AttributeKeyClaimSender
AttributeKeyDirection = types.AttributeKeyDirection
AttributeKeyExpireHeight = types.AttributeKeyExpireHeight
AttributeKeyRandomNumber = types.AttributeKeyRandomNumber
AttributeKeyRandomNumberHash = types.AttributeKeyRandomNumberHash
AttributeKeyRecipient = types.AttributeKeyRecipient
AttributeKeyRefundSender = types.AttributeKeyRefundSender
AttributeKeySender = types.AttributeKeySender
AttributeKeySenderOtherChain = types.AttributeKeySenderOtherChain
AttributeKeyTimestamp = types.AttributeKeyTimestamp
AttributeValueCategory = types.AttributeValueCategory
CalcSwapID = types.CalcSwapID
ClaimAtomicSwap = types.ClaimAtomicSwap
Completed = types.Completed
CreateAtomicSwap = types.CreateAtomicSwap
DefaultLongtermStorageDuration = types.DefaultLongtermStorageDuration
DefaultParamspace = types.DefaultParamspace
EventTypeClaimAtomicSwap = types.EventTypeClaimAtomicSwap
EventTypeCreateAtomicSwap = types.EventTypeCreateAtomicSwap
EventTypeClaimAtomicSwap = types.EventTypeClaimAtomicSwap
EventTypeRefundAtomicSwap = types.EventTypeRefundAtomicSwap
EventTypeSwapsExpired = types.EventTypeSwapsExpired
Expired = types.Expired
INVALID = types.INVALID
Incoming = types.Incoming
Int64Size = types.Int64Size
MaxExpectedIncomeLength = types.MaxExpectedIncomeLength
MaxOtherChainAddrLength = types.MaxOtherChainAddrLength
AttributeValueCategory = types.AttributeValueCategory
AttributeKeySender = types.AttributeKeySender
AttributeKeyRecipient = types.AttributeKeyRecipient
AttributeKeyAtomicSwapID = types.AttributeKeyAtomicSwapID
AttributeKeyRandomNumberHash = types.AttributeKeyRandomNumberHash
AttributeKeyTimestamp = types.AttributeKeyTimestamp
AttributeKeySenderOtherChain = types.AttributeKeySenderOtherChain
AttributeKeyExpireHeight = types.AttributeKeyExpireHeight
AttributeKeyAmount = types.AttributeKeyAmount
AttributeKeyDirection = types.AttributeKeyDirection
AttributeKeyClaimSender = types.AttributeKeyClaimSender
AttributeKeyRandomNumber = types.AttributeKeyRandomNumber
AttributeKeyRefundSender = types.AttributeKeyRefundSender
AttributeKeyAtomicSwapIDs = types.AttributeKeyAtomicSwapIDs
AttributeExpirationBlock = types.AttributeExpirationBlock
ModuleName = types.ModuleName
NULL = types.NULL
Open = types.Open
Outgoing = types.Outgoing
StoreKey = types.StoreKey
RouterKey = types.RouterKey
QuerierRoute = types.QuerierRoute
DefaultParamspace = types.DefaultParamspace
DefaultLongtermStorageDuration = types.DefaultLongtermStorageDuration
CreateAtomicSwap = types.CreateAtomicSwap
ClaimAtomicSwap = types.ClaimAtomicSwap
RefundAtomicSwap = types.RefundAtomicSwap
CalcSwapID = types.CalcSwapID
Int64Size = types.Int64Size
RandomNumberHashLength = types.RandomNumberHashLength
RandomNumberLength = types.RandomNumberLength
AddrByteCount = types.AddrByteCount
MaxOtherChainAddrLength = types.MaxOtherChainAddrLength
SwapIDLength = types.SwapIDLength
MaxExpectedIncomeLength = types.MaxExpectedIncomeLength
QueryGetAssetSupply = types.QueryGetAssetSupply
QueryGetAssetSupplies = types.QueryGetAssetSupplies
QueryGetAtomicSwap = types.QueryGetAtomicSwap
QueryGetAtomicSwaps = types.QueryGetAtomicSwaps
QueryGetParams = types.QueryGetParams
RandomNumberHashLength = types.RandomNumberHashLength
RandomNumberLength = types.RandomNumberLength
RefundAtomicSwap = types.RefundAtomicSwap
RouterKey = types.RouterKey
StoreKey = types.StoreKey
SwapIDLength = types.SwapIDLength
NULL = types.NULL
Open = types.Open
Completed = types.Completed
Expired = types.Expired
INVALID = types.INVALID
Incoming = types.Incoming
Outgoing = types.Outgoing
)
var (
// functions aliases
NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier
RegisterRoutes = rest.RegisterRoutes
NewAssetSupply = types.NewAssetSupply
RegisterCodec = types.RegisterCodec
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
GenerateSecureRandomNumber = types.GenerateSecureRandomNumber
CalculateRandomHash = types.CalculateRandomHash
CalculateSwapID = types.CalculateSwapID
DefaultGenesisState = types.DefaultGenesisState
DefaultParams = types.DefaultParams
ErrAssetNotActive = types.ErrAssetNotActive
ErrAssetNotSupported = types.ErrAssetNotSupported
ErrAssetSupplyNotFound = types.ErrAssetSupplyNotFound
ErrAtomicSwapAlreadyExists = types.ErrAtomicSwapAlreadyExists
ErrAtomicSwapNotFound = types.ErrAtomicSwapNotFound
ErrExceedsAvailableSupply = types.ErrExceedsAvailableSupply
ErrExceedsSupplyLimit = types.ErrExceedsSupplyLimit
ErrInvalidClaimSecret = types.ErrInvalidClaimSecret
ErrInvalidCurrentSupply = types.ErrInvalidCurrentSupply
ErrInvalidHeightSpan = types.ErrInvalidHeightSpan
ErrInvalidAmount = types.ErrInvalidAmount
ErrInvalidIncomingSupply = types.ErrInvalidIncomingSupply
ErrInvalidOutgoingSupply = types.ErrInvalidOutgoingSupply
ErrInvalidTimestamp = types.ErrInvalidTimestamp
ErrSwapNotClaimable = types.ErrSwapNotClaimable
ErrSwapNotRefundable = types.ErrSwapNotRefundable
GenerateSecureRandomNumber = types.GenerateSecureRandomNumber
GetAtomicSwapByHeightKey = types.GetAtomicSwapByHeightKey
NewAssetSupply = types.NewAssetSupply
NewAtomicSwap = types.NewAtomicSwap
NewGenesisState = types.NewGenesisState
NewMsgClaimAtomicSwap = types.NewMsgClaimAtomicSwap
NewMsgCreateAtomicSwap = types.NewMsgCreateAtomicSwap
NewMsgClaimAtomicSwap = types.NewMsgClaimAtomicSwap
NewMsgRefundAtomicSwap = types.NewMsgRefundAtomicSwap
NewParams = types.NewParams
DefaultParams = types.DefaultParams
NewAssetParam = types.NewAssetParam
ParamKeyTable = types.ParamKeyTable
NewQueryAssetSupply = types.NewQueryAssetSupply
NewQueryAssetSupplies = types.NewQueryAssetSupplies
NewQueryAtomicSwapByID = types.NewQueryAtomicSwapByID
NewQueryAtomicSwaps = types.NewQueryAtomicSwaps
NewSwapDirectionFromString = types.NewSwapDirectionFromString
NewAtomicSwap = types.NewAtomicSwap
NewSwapStatusFromString = types.NewSwapStatusFromString
ParamKeyTable = types.ParamKeyTable
RegisterCodec = types.RegisterCodec
NewSwapDirectionFromString = types.NewSwapDirectionFromString
NewAugmentedAtomicSwap = types.NewAugmentedAtomicSwap
// variable aliases
AssetSupplyKeyPrefix = types.AssetSupplyKeyPrefix
AtomicSwapByBlockPrefix = types.AtomicSwapByBlockPrefix
AtomicSwapCoinsAccAddr = types.AtomicSwapCoinsAccAddr
ModuleCdc = types.ModuleCdc
ErrInvalidTimestamp = types.ErrInvalidTimestamp
ErrInvalidHeightSpan = types.ErrInvalidHeightSpan
ErrInsufficientAmount = types.ErrInsufficientAmount
ErrAssetNotSupported = types.ErrAssetNotSupported
ErrAssetNotActive = types.ErrAssetNotActive
ErrAssetSupplyNotFound = types.ErrAssetSupplyNotFound
ErrExceedsSupplyLimit = types.ErrExceedsSupplyLimit
ErrExceedsAvailableSupply = types.ErrExceedsAvailableSupply
ErrInvalidCurrentSupply = types.ErrInvalidCurrentSupply
ErrInvalidIncomingSupply = types.ErrInvalidIncomingSupply
ErrInvalidOutgoingSupply = types.ErrInvalidOutgoingSupply
ErrInvalidClaimSecret = types.ErrInvalidClaimSecret
ErrAtomicSwapAlreadyExists = types.ErrAtomicSwapAlreadyExists
ErrAtomicSwapNotFound = types.ErrAtomicSwapNotFound
ErrSwapNotRefundable = types.ErrSwapNotRefundable
ErrSwapNotClaimable = types.ErrSwapNotClaimable
ErrInvalidAmount = types.ErrInvalidAmount
ErrInvalidSwapAccount = types.ErrInvalidSwapAccount
AtomicSwapKeyPrefix = types.AtomicSwapKeyPrefix
AtomicSwapByBlockPrefix = types.AtomicSwapByBlockPrefix
AtomicSwapLongtermStoragePrefix = types.AtomicSwapLongtermStoragePrefix
AtomicSwapCoinsAccAddr = types.AtomicSwapCoinsAccAddr
KeyAssetParams = types.KeyAssetParams
DefaultBnbDeputyFixedFee = types.DefaultBnbDeputyFixedFee
DefaultMinAmount = types.DefaultMinAmount
DefaultMaxAmount = types.DefaultMaxAmount
DefaultMaxBlockLock = types.DefaultMaxBlockLock
DefaultMinBlockLock = types.DefaultMinBlockLock
DefaultSupportedAssets = types.DefaultSupportedAssets
KeyBnbDeputyAddress = types.KeyBnbDeputyAddress
KeyBnbDeputyFixedFee = types.KeyBnbDeputyFixedFee
KeyMinAmount = types.KeyMinAmount
KeyMaxAmount = types.KeyMaxAmount
KeyMaxBlockLock = types.KeyMaxBlockLock
KeyMinBlockLock = types.KeyMinBlockLock
KeySupportedAssets = types.KeySupportedAssets
ModuleCdc = types.ModuleCdc
SupplyLimitUpgradeTime = types.SupplyLimitUpgradeTime
DefaultMaxBlockLock = types.DefaultMaxBlockLock
)
type (
Keeper = keeper.Keeper
AssetParam = types.AssetParam
AssetParams = types.AssetParams
AssetSupplies = types.AssetSupplies
AssetSupply = types.AssetSupply
AtomicSwap = types.AtomicSwap
AtomicSwaps = types.AtomicSwaps
GenesisState = types.GenesisState
MsgClaimAtomicSwap = types.MsgClaimAtomicSwap
MsgCreateAtomicSwap = types.MsgCreateAtomicSwap
MsgRefundAtomicSwap = types.MsgRefundAtomicSwap
Params = types.Params
QueryAssetSupply = types.QueryAssetSupply
QueryAtomicSwapByID = types.QueryAtomicSwapByID
QueryAtomicSwaps = types.QueryAtomicSwaps
SwapDirection = types.SwapDirection
SwapStatus = types.SwapStatus
Keeper = keeper.Keeper
AssetSupply = types.AssetSupply
AssetSupplies = types.AssetSupplies
GenesisState = types.GenesisState
MsgCreateAtomicSwap = types.MsgCreateAtomicSwap
MsgClaimAtomicSwap = types.MsgClaimAtomicSwap
MsgRefundAtomicSwap = types.MsgRefundAtomicSwap
Params = types.Params
AssetParam = types.AssetParam
AssetParams = types.AssetParams
QueryAssetSupply = types.QueryAssetSupply
QueryAssetSupplies = types.QueryAssetSupplies
QueryAtomicSwapByID = types.QueryAtomicSwapByID
QueryAtomicSwaps = types.QueryAtomicSwaps
AtomicSwap = types.AtomicSwap
AtomicSwaps = types.AtomicSwaps
SwapStatus = types.SwapStatus
SwapDirection = types.SwapDirection
AugmentedAtomicSwap = types.AugmentedAtomicSwap
AugmentedAtomicSwaps = types.AugmentedAtomicSwaps
)

View File

@ -133,7 +133,7 @@ func QueryGetAssetSupplyCmd(queryRoute string, cdc *codec.Codec) *cobra.Command
cliCtx := context.NewCLIContext().WithCodec(cdc)
// Prepare query params
bz, err := cdc.MarshalJSON(types.NewQueryAssetSupply([]byte(args[0])))
bz, err := cdc.MarshalJSON(types.NewQueryAssetSupply(args[0]))
if err != nil {
return err
}

View File

@ -154,7 +154,7 @@ func queryAssetSupplyHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
// Prepare params for querier
vars := mux.Vars(r)
denom := []byte(vars[restDenom])
denom := vars[restDenom]
params := types.NewQueryAssetSupply(denom)
bz, err := cliCtx.Codec.MarshalJSON(params)

View File

@ -21,39 +21,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper
}
keeper.SetParams(ctx, gs.Params)
// Initialize supported assets
for _, asset := range gs.Params.SupportedAssets {
zeroCoin := sdk.NewCoin(asset.Denom, sdk.NewInt(0))
supply := NewAssetSupply(asset.Denom, zeroCoin, zeroCoin, zeroCoin, sdk.NewCoin(asset.Denom, asset.Limit))
keeper.SetAssetSupply(ctx, supply, []byte(asset.Denom))
}
// Increment an asset's incoming, current, and outgoing supply
// It it required that assets are supported but they do not have to be active
for _, supply := range gs.AssetSupplies {
// Asset must be supported but does not have to be active
coin, found := keeper.GetAssetByDenom(ctx, supply.Denom)
if !found {
panic(fmt.Sprintf("invalid asset supply: %s is not a supported asset", coin.Denom))
}
if !coin.Limit.Equal(supply.SupplyLimit.Amount) {
panic(fmt.Sprintf("supported asset limit %s does not equal asset supply %s", coin.Limit, supply.SupplyLimit.Amount))
}
// Increment current, incoming, and outgoing asset supplies
err := keeper.IncrementCurrentAssetSupply(ctx, supply.CurrentSupply)
if err != nil {
panic(err)
}
err = keeper.IncrementIncomingAssetSupply(ctx, supply.IncomingSupply)
if err != nil {
panic(err)
}
err = keeper.IncrementOutgoingAssetSupply(ctx, supply.OutgoingSupply)
if err != nil {
panic(err)
}
for _, supply := range gs.Supplies {
keeper.SetAssetSupply(ctx, supply, supply.GetDenom())
}
var incomingSupplies sdk.Coins
@ -108,16 +77,33 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper
// Asset's given incoming/outgoing supply much match the amount of coins in incoming/outgoing atomic swaps
supplies := keeper.GetAllAssetSupplies(ctx)
for _, supply := range supplies {
incomingSupply := incomingSupplies.AmountOf(supply.Denom)
incomingSupply := incomingSupplies.AmountOf(supply.GetDenom())
if !supply.IncomingSupply.Amount.Equal(incomingSupply) {
panic(fmt.Sprintf("asset's incoming supply %s does not match amount %s in incoming atomic swaps",
supply.IncomingSupply, incomingSupply))
}
outgoingSupply := outgoingSupplies.AmountOf(supply.Denom)
outgoingSupply := outgoingSupplies.AmountOf(supply.GetDenom())
if !supply.OutgoingSupply.Amount.Equal(outgoingSupply) {
panic(fmt.Sprintf("asset's outgoing supply %s does not match amount %s in outgoing atomic swaps",
supply.OutgoingSupply, outgoingSupply))
}
limit, err := keeper.GetSupplyLimit(ctx, supply.GetDenom())
if err != nil {
panic(err)
}
if supply.CurrentSupply.Amount.GT(limit) {
panic(fmt.Sprintf("asset's current supply %s is over the supply limit %s", supply.CurrentSupply, limit))
}
if supply.IncomingSupply.Amount.GT(limit) {
panic(fmt.Sprintf("asset's incoming supply %s is over the supply limit %s", supply.IncomingSupply, limit))
}
if supply.IncomingSupply.Amount.Add(supply.CurrentSupply.Amount).GT(limit) {
panic(fmt.Sprintf("asset's incoming supply + current supply %s is over the supply limit %s", supply.IncomingSupply.Add(supply.CurrentSupply), limit))
}
if supply.OutgoingSupply.Amount.GT(limit) {
panic(fmt.Sprintf("asset's outgoing supply %s is over the supply limit %s", supply.OutgoingSupply, limit))
}
}
}
@ -125,6 +111,6 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper
func ExportGenesis(ctx sdk.Context, k Keeper) (data GenesisState) {
params := k.GetParams(ctx)
swaps := k.GetAllAtomicSwaps(ctx)
assets := k.GetAllAssetSupplies(ctx)
return NewGenesisState(params, swaps, assets)
supplies := k.GetAllAssetSupplies(ctx)
return NewGenesisState(params, swaps, supplies)
}

View File

@ -53,16 +53,16 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "import atomic swaps and asset supplies",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
_, addrs := app.GeneratePrivKeyAddressPairs(3)
_, addrs := app.GeneratePrivKeyAddressPairs(2)
var swaps bep3.AtomicSwaps
var supplies bep3.AssetSupplies
for i := 0; i < 3; i++ {
for i := 0; i < 2; i++ {
swap, supply := loadSwapAndSupply(addrs[i], i)
swaps = append(swaps, swap)
supplies = append(supplies, supply)
}
gs.AtomicSwaps = swaps
gs.AssetSupplies = supplies
gs.Supplies = supplies
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
},
expectPass: true,
@ -71,7 +71,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "0 deputy fees",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
gs.Params.BnbDeputyFixedFee = sdk.ZeroInt()
gs.Params.AssetParams[0].FixedFee = sdk.ZeroInt()
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
},
expectPass: true,
@ -91,14 +91,12 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "current supply above limit",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
assetParam, _ := suite.keeper.GetAssetByDenom(suite.ctx, "bnb")
gs.AssetSupplies = bep3.AssetSupplies{
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
gs.Supplies = bep3.AssetSupplies{
bep3.AssetSupply{
Denom: "bnb",
IncomingSupply: c("bnb", 0),
OutgoingSupply: c("bnb", 0),
CurrentSupply: c("bnb", assetParam.Limit.Add(i(1)).Int64()),
SupplyLimit: c("bnb", assetParam.Limit.Int64()),
CurrentSupply: c("bnb", assetParam.SupplyLimit.Add(i(1)).Int64()),
},
}
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
@ -110,8 +108,8 @@ func (suite *GenesisTestSuite) TestGenesisState() {
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
// Set up overlimit amount
assetParam, _ := suite.keeper.GetAssetByDenom(suite.ctx, "bnb")
overLimitAmount := assetParam.Limit.Add(i(1))
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
overLimitAmount := assetParam.SupplyLimit.Add(i(1))
// Set up an atomic swap with amount equal to the currently asset supply
_, addrs := app.GeneratePrivKeyAddressPairs(2)
@ -124,13 +122,11 @@ func (suite *GenesisTestSuite) TestGenesisState() {
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
// Set up asset supply with overlimit current supply
gs.AssetSupplies = bep3.AssetSupplies{
gs.Supplies = bep3.AssetSupplies{
bep3.AssetSupply{
Denom: "bnb",
IncomingSupply: c("bnb", assetParam.Limit.Add(i(1)).Int64()),
IncomingSupply: c("bnb", assetParam.SupplyLimit.Add(i(1)).Int64()),
OutgoingSupply: c("bnb", 0),
CurrentSupply: c("bnb", 0),
SupplyLimit: c("bnb", assetParam.Limit.Int64()),
},
}
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
@ -142,8 +138,8 @@ func (suite *GenesisTestSuite) TestGenesisState() {
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
// Set up overlimit amount
assetParam, _ := suite.keeper.GetAssetByDenom(suite.ctx, "bnb")
halfLimit := assetParam.Limit.Int64() / 2
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
halfLimit := assetParam.SupplyLimit.Int64() / 2
overHalfLimit := halfLimit + 1
// Set up an atomic swap with amount equal to the currently asset supply
@ -157,13 +153,41 @@ func (suite *GenesisTestSuite) TestGenesisState() {
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
// Set up asset supply with overlimit current supply
gs.AssetSupplies = bep3.AssetSupplies{
gs.Supplies = bep3.AssetSupplies{
bep3.AssetSupply{
Denom: "bnb",
IncomingSupply: c("bnb", halfLimit),
OutgoingSupply: c("bnb", 0),
CurrentSupply: c("bnb", overHalfLimit),
SupplyLimit: c("bnb", assetParam.Limit.Int64()),
},
}
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
},
expectPass: false,
},
{
name: "outgoing supply above limit",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
// Set up overlimit amount
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
overLimitAmount := assetParam.SupplyLimit.Add(i(1))
// Set up an atomic swap with amount equal to the currently asset supply
_, addrs := app.GeneratePrivKeyAddressPairs(2)
timestamp := ts(0)
randomNumber, _ := bep3.GenerateSecureRandomNumber()
randomNumberHash := bep3.CalculateRandomHash(randomNumber[:], timestamp)
swap := bep3.NewAtomicSwap(cs(c("bnb", overLimitAmount.Int64())), randomNumberHash,
bep3.DefaultMinBlockLock, timestamp, addrs[1], suite.addrs[0], TestSenderOtherChain,
TestRecipientOtherChain, 0, bep3.Open, true, bep3.Outgoing)
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
// Set up asset supply with overlimit current supply
gs.Supplies = bep3.AssetSupplies{
bep3.AssetSupply{
IncomingSupply: c("bnb", 0),
OutgoingSupply: c("bnb", 0),
CurrentSupply: c("bnb", assetParam.SupplyLimit.Add(i(1)).Int64()),
},
}
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
@ -174,13 +198,11 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "asset supply denom is not a supported asset",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
gs.AssetSupplies = bep3.AssetSupplies{
gs.Supplies = bep3.AssetSupplies{
bep3.AssetSupply{
Denom: "fake",
IncomingSupply: c("fake", 0),
OutgoingSupply: c("fake", 0),
CurrentSupply: c("fake", 0),
SupplyLimit: c("fake", StandardSupplyLimit.Int64()),
},
}
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
@ -225,8 +247,8 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "minimum block lock cannot be > maximum block lock",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
gs.Params.MinBlockLock = 201
gs.Params.MaxBlockLock = 200
gs.Params.AssetParams[0].MinBlockLock = 201
gs.Params.AssetParams[0].MaxBlockLock = 200
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
},
expectPass: false,
@ -235,7 +257,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "empty supported asset denom",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
gs.Params.SupportedAssets[0].Denom = ""
gs.Params.AssetParams[0].Denom = ""
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
},
expectPass: false,
@ -244,7 +266,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "negative supported asset limit",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
gs.Params.SupportedAssets[0].Limit = i(-100)
gs.Params.AssetParams[0].SupplyLimit = i(-100)
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
},
expectPass: false,
@ -253,7 +275,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
name: "duplicate supported asset denom",
genState: func() app.GenesisState {
gs := baseGenState(suite.addrs[0])
gs.Params.SupportedAssets[1].Denom = "bnb"
gs.Params.AssetParams[1].Denom = "bnb"
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
},
expectPass: false,
@ -261,15 +283,18 @@ func (suite *GenesisTestSuite) TestGenesisState() {
}
for _, tc := range testCases {
if tc.expectPass {
suite.NotPanics(func() {
suite.app.InitializeFromGenesisStates(tc.genState())
}, tc.name)
} else {
suite.Panics(func() {
suite.app.InitializeFromGenesisStates(tc.genState())
}, tc.name)
}
suite.Run(tc.name, func() {
if tc.expectPass {
suite.NotPanics(func() {
suite.app.InitializeFromGenesisStates(tc.genState())
}, tc.name)
} else {
suite.Panics(func() {
suite.app.InitializeFromGenesisStates(tc.genState())
}, tc.name)
}
})
}
}

View File

@ -20,7 +20,7 @@ const (
var (
StandardSupplyLimit = i(100000000000)
DenomMap = map[int]string{0: "btc", 1: "eth", 2: "bnb", 3: "xrp", 4: "dai"}
DenomMap = map[int]string{0: "bnb", 1: "inc"}
)
func i(in int64) sdk.Int { return sdk.NewInt(in) }
@ -35,41 +35,48 @@ func NewBep3GenStateMulti(deputy sdk.AccAddress) app.GenesisState {
}
func baseGenState(deputy sdk.AccAddress) bep3.GenesisState {
bep3Genesis := bep3.GenesisState{
Params: bep3.Params{
BnbDeputyAddress: deputy,
BnbDeputyFixedFee: bep3.DefaultBnbDeputyFixedFee, // 1,000
MinAmount: bep3.DefaultMinAmount, // 0
MaxAmount: bep3.DefaultMaxAmount, // 10,000
MinBlockLock: bep3.DefaultMinBlockLock, // 220
MaxBlockLock: bep3.DefaultMaxBlockLock, // 270
SupportedAssets: bep3.AssetParams{
AssetParams: bep3.AssetParams{
bep3.AssetParam{
Denom: "btc",
CoinID: 714,
Limit: StandardSupplyLimit,
Active: true,
Denom: "bnb",
CoinID: 714,
SupplyLimit: sdk.NewInt(350000000000000),
Active: true,
DeputyAddress: deputy,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3.DefaultMinBlockLock,
MaxBlockLock: bep3.DefaultMaxBlockLock,
},
bep3.AssetParam{
Denom: "eth",
CoinID: 999999,
Limit: StandardSupplyLimit,
Active: true,
},
bep3.AssetParam{
Denom: "bnb",
CoinID: 99999,
Limit: StandardSupplyLimit,
Active: true,
},
bep3.AssetParam{
Denom: "inc",
CoinID: 9999,
Limit: i(100),
Active: false,
Denom: "inc",
CoinID: 9999,
SupplyLimit: sdk.NewInt(100000000000),
Active: true,
DeputyAddress: deputy,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3.DefaultMinBlockLock,
MaxBlockLock: bep3.DefaultMaxBlockLock,
},
},
},
Supplies: bep3.AssetSupplies{
bep3.NewAssetSupply(
sdk.NewCoin("bnb", sdk.ZeroInt()),
sdk.NewCoin("bnb", sdk.ZeroInt()),
sdk.NewCoin("bnb", sdk.ZeroInt()),
),
bep3.NewAssetSupply(
sdk.NewCoin("inc", sdk.ZeroInt()),
sdk.NewCoin("inc", sdk.ZeroInt()),
sdk.NewCoin("inc", sdk.ZeroInt()),
),
},
}
return bep3Genesis
}
@ -84,8 +91,8 @@ func loadSwapAndSupply(addr sdk.AccAddress, index int) (bep3.AtomicSwap, bep3.As
expireOffset, timestamp, addr, addr, TestSenderOtherChain,
TestRecipientOtherChain, 1, bep3.Open, true, bep3.Incoming)
supply := bep3.NewAssetSupply(coin.Denom, coin, c(coin.Denom, 0),
c(coin.Denom, 0), c(coin.Denom, StandardSupplyLimit.Int64()))
supply := bep3.NewAssetSupply(coin, c(coin.Denom, 0),
c(coin.Denom, 0))
return swap, supply
}

View File

@ -9,24 +9,30 @@ import (
// IncrementCurrentAssetSupply increments an asset's supply by the coin
func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
supply, found := k.GetAssetSupply(ctx, coin.Denom)
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
}
limit, err := k.GetSupplyLimit(ctx, coin.Denom)
if err != nil {
return err
}
supplyLimit := sdk.NewCoin(coin.Denom, limit)
// Resulting current supply must be under asset's limit
if supply.SupplyLimit.IsLT(supply.CurrentSupply.Add(coin)) {
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supply.SupplyLimit)
if supplyLimit.IsLT(supply.CurrentSupply.Add(coin)) {
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supplyLimit)
}
supply.CurrentSupply = supply.CurrentSupply.Add(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
k.SetAssetSupply(ctx, supply, coin.Denom)
return nil
}
// DecrementCurrentAssetSupply decrement an asset's supply by the coin
func (k Keeper) DecrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
supply, found := k.GetAssetSupply(ctx, coin.Denom)
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
}
@ -38,31 +44,37 @@ func (k Keeper) DecrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) erro
}
supply.CurrentSupply = supply.CurrentSupply.Sub(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
k.SetAssetSupply(ctx, supply, coin.Denom)
return nil
}
// IncrementIncomingAssetSupply increments an asset's incoming supply
func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
supply, found := k.GetAssetSupply(ctx, coin.Denom)
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
}
// Result of (current + incoming + amount) must be under asset's limit
totalSupply := supply.CurrentSupply.Add(supply.IncomingSupply)
if supply.SupplyLimit.IsLT(totalSupply.Add(coin)) {
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supply.SupplyLimit)
limit, err := k.GetSupplyLimit(ctx, coin.Denom)
if err != nil {
return err
}
supplyLimit := sdk.NewCoin(coin.Denom, limit)
if supplyLimit.IsLT(totalSupply.Add(coin)) {
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supplyLimit)
}
supply.IncomingSupply = supply.IncomingSupply.Add(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
k.SetAssetSupply(ctx, supply, coin.Denom)
return nil
}
// DecrementIncomingAssetSupply decrements an asset's incoming supply
func (k Keeper) DecrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
supply, found := k.GetAssetSupply(ctx, coin.Denom)
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
}
@ -74,13 +86,13 @@ func (k Keeper) DecrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) err
}
supply.IncomingSupply = supply.IncomingSupply.Sub(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
k.SetAssetSupply(ctx, supply, coin.Denom)
return nil
}
// IncrementOutgoingAssetSupply increments an asset's outoing supply
// IncrementOutgoingAssetSupply increments an asset's outgoing supply
func (k Keeper) IncrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
supply, found := k.GetAssetSupply(ctx, coin.Denom)
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
}
@ -92,13 +104,13 @@ func (k Keeper) IncrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) err
}
supply.OutgoingSupply = supply.OutgoingSupply.Add(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
k.SetAssetSupply(ctx, supply, coin.Denom)
return nil
}
// DecrementOutgoingAssetSupply decrements an asset's outoing supply
// DecrementOutgoingAssetSupply decrements an asset's outgoing supply
func (k Keeper) DecrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
supply, found := k.GetAssetSupply(ctx, coin.Denom)
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
}
@ -110,21 +122,6 @@ func (k Keeper) DecrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) err
}
supply.OutgoingSupply = supply.OutgoingSupply.Sub(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
k.SetAssetSupply(ctx, supply, coin.Denom)
return nil
}
// UpdateAssetSupplies applies updates to the asset limit from parameters to the asset supplies
func (k Keeper) UpdateAssetSupplies(ctx sdk.Context) {
params := k.GetParams(ctx)
for _, supportedAsset := range params.SupportedAssets {
asset, found := k.GetAssetSupply(ctx, []byte(supportedAsset.Denom))
if !found {
continue
}
if !asset.SupplyLimit.Amount.Equal(supportedAsset.Limit) {
asset.SupplyLimit = sdk.NewCoin(supportedAsset.Denom, supportedAsset.Limit)
k.SetAssetSupply(ctx, asset, []byte(supportedAsset.Denom))
}
}
}

View File

@ -36,16 +36,16 @@ func (suite *AssetTestSuite) SetupTest() {
tApp.InitializeFromGenesisStates(NewBep3GenStateMulti(deputy))
keeper := tApp.GetBep3Keeper()
params := keeper.GetParams(ctx)
params.AssetParams[0].SupplyLimit = sdk.NewInt(50)
keeper.SetParams(ctx, params)
// Set asset supply with standard value for testing
supply := types.AssetSupply{
Denom: "bnb",
IncomingSupply: c("bnb", 5),
OutgoingSupply: c("bnb", 5),
CurrentSupply: c("bnb", 40),
SupplyLimit: c("bnb", 50),
}
keeper.SetAssetSupply(ctx, supply, []byte(supply.Denom))
keeper.SetAssetSupply(ctx, supply, supply.GetDenom())
suite.app = tApp
suite.ctx = ctx
@ -95,11 +95,10 @@ func (suite *AssetTestSuite) TestIncrementCurrentAssetSupply() {
for _, tc := range testCases {
suite.SetupTest()
suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, tc.args.coin)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
if tc.expectPass {
suite.True(found)
@ -155,11 +154,10 @@ func (suite *AssetTestSuite) TestDecrementCurrentAssetSupply() {
for _, tc := range testCases {
suite.SetupTest()
suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
err := suite.keeper.DecrementCurrentAssetSupply(suite.ctx, tc.args.coin)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
if tc.expectPass {
suite.True(found)
@ -215,11 +213,9 @@ func (suite *AssetTestSuite) TestIncrementIncomingAssetSupply() {
for _, tc := range testCases {
suite.SetupTest()
suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
err := suite.keeper.IncrementIncomingAssetSupply(suite.ctx, tc.args.coin)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
if tc.expectPass {
suite.True(found)
@ -275,11 +271,10 @@ func (suite *AssetTestSuite) TestDecrementIncomingAssetSupply() {
for _, tc := range testCases {
suite.SetupTest()
suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
err := suite.keeper.DecrementIncomingAssetSupply(suite.ctx, tc.args.coin)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
if tc.expectPass {
suite.True(found)
@ -335,11 +330,10 @@ func (suite *AssetTestSuite) TestIncrementOutgoingAssetSupply() {
for _, tc := range testCases {
suite.SetupTest()
suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
err := suite.keeper.IncrementOutgoingAssetSupply(suite.ctx, tc.args.coin)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
if tc.expectPass {
suite.True(found)
@ -395,11 +389,9 @@ func (suite *AssetTestSuite) TestDecrementOutgoingAssetSupply() {
for _, tc := range testCases {
suite.SetupTest()
suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
err := suite.keeper.DecrementOutgoingAssetSupply(suite.ctx, tc.args.coin)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
if tc.expectPass {
suite.True(found)
@ -413,25 +405,6 @@ func (suite *AssetTestSuite) TestDecrementOutgoingAssetSupply() {
}
}
func (suite *AssetTestSuite) TestUpdateAssetSupplies() {
// set new asset limit in the params
newBnbLimit := c("bnb", 100)
params := suite.keeper.GetParams(suite.ctx)
for i := range params.SupportedAssets {
if params.SupportedAssets[i].Denom != newBnbLimit.Denom {
continue
}
params.SupportedAssets[i].Limit = newBnbLimit.Amount
}
suite.keeper.SetParams(suite.ctx, params)
suite.keeper.UpdateAssetSupplies(suite.ctx)
supply, found := suite.keeper.GetAssetSupply(suite.ctx, []byte(newBnbLimit.Denom))
suite.True(found)
suite.True(supply.SupplyLimit.IsEqual(newBnbLimit))
}
func TestAssetTestSuite(t *testing.T) {
suite.Run(t, new(AssetTestSuite))
}

View File

@ -20,10 +20,9 @@ const (
)
var (
StandardSupplyLimit = i(350000000000000)
DenomMap = map[int]string{0: "btc", 1: "eth", 2: "bnb", 3: "xrp", 4: "dai"}
TestUser1 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1")))
TestUser2 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser2")))
DenomMap = map[int]string{0: "btc", 1: "eth", 2: "bnb", 3: "xrp", 4: "dai"}
TestUser1 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1")))
TestUser2 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser2")))
)
func i(in int64) sdk.Int { return sdk.NewInt(in) }
@ -34,27 +33,45 @@ func ts(minOffset int) int64 { return tmtime.Now().Add(time.Durat
func NewBep3GenStateMulti(deputyAddress sdk.AccAddress) app.GenesisState {
bep3Genesis := types.GenesisState{
Params: bep3.Params{
BnbDeputyAddress: deputyAddress,
BnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee, // 1000
MinAmount: types.DefaultMinAmount, // 0
MaxAmount: types.DefaultMaxAmount, // 10,000
MinBlockLock: types.DefaultMinBlockLock, // 220
MaxBlockLock: types.DefaultMaxBlockLock, // 270
SupportedAssets: types.AssetParams{
AssetParams: types.AssetParams{
types.AssetParam{
Denom: "bnb",
CoinID: 714,
Limit: StandardSupplyLimit,
Active: true,
Denom: "bnb",
CoinID: 714,
SupplyLimit: sdk.NewInt(350000000000000),
Active: true,
DeputyAddress: deputyAddress,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: types.DefaultMinBlockLock,
MaxBlockLock: types.DefaultMaxBlockLock,
},
types.AssetParam{
Denom: "inc",
CoinID: 9999,
Limit: i(100),
Active: false,
Denom: "inc",
CoinID: 9999,
SupplyLimit: sdk.NewInt(100),
Active: false,
DeputyAddress: deputyAddress,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: types.DefaultMinBlockLock,
MaxBlockLock: types.DefaultMaxBlockLock,
},
},
},
Supplies: bep3.AssetSupplies{
bep3.NewAssetSupply(
sdk.NewCoin("bnb", sdk.ZeroInt()),
sdk.NewCoin("bnb", sdk.ZeroInt()),
sdk.NewCoin("bnb", sdk.ZeroInt()),
),
bep3.NewAssetSupply(
sdk.NewCoin("inc", sdk.ZeroInt()),
sdk.NewCoin("inc", sdk.ZeroInt()),
sdk.NewCoin("inc", sdk.ZeroInt()),
),
},
}
return app.GenesisState{bep3.ModuleName: bep3.ModuleCdc.MustMarshalJSON(bep3Genesis)}
}
@ -95,5 +112,5 @@ func assetSupplies(count int) types.AssetSupplies {
}
func assetSupply(denom string) types.AssetSupply {
return types.NewAssetSupply(denom, c(denom, 0), c(denom, 0), c(denom, 0), c(denom, 10000))
return types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 0))
}

View File

@ -182,35 +182,31 @@ func (k Keeper) IterateAtomicSwapsLongtermStorage(ctx sdk.Context, inclusiveCuto
// ------------------------------------------
// GetAssetSupply gets an asset's current supply from the store.
func (k Keeper) GetAssetSupply(ctx sdk.Context, denom []byte) (types.AssetSupply, bool) {
var supply types.AssetSupply
store := prefix.NewStore(ctx.KVStore(k.key), types.AssetSupplyKeyPrefix)
bz := store.Get(denom)
func (k Keeper) GetAssetSupply(ctx sdk.Context, denom string) (types.AssetSupply, bool) {
var assetSupply types.AssetSupply
store := prefix.NewStore(ctx.KVStore(k.key), types.AssetSupplyPrefix)
bz := store.Get([]byte(denom))
if bz == nil {
return types.AssetSupply{}, false
}
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &supply)
return supply, true
k.cdc.MustUnmarshalBinaryBare(bz, &assetSupply)
return assetSupply, true
}
// SetAssetSupply updates an asset's current active supply
func (k Keeper) SetAssetSupply(ctx sdk.Context, supply types.AssetSupply, denom []byte) {
store := prefix.NewStore(ctx.KVStore(k.key), types.AssetSupplyKeyPrefix)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(supply)
store.Set(denom, bz)
// SetAssetSupply updates an asset's supply
func (k Keeper) SetAssetSupply(ctx sdk.Context, supply types.AssetSupply, denom string) {
store := prefix.NewStore(ctx.KVStore(k.key), types.AssetSupplyPrefix)
store.Set([]byte(denom), k.cdc.MustMarshalBinaryBare(supply))
}
// IterateAssetSupplies provides an iterator over current asset supplies.
// For each asset supply, cb will be called. If cb returns true, the iterator will close and stop.
// IterateAssetSupplies provides an iterator over all stored AssetSupplies.
func (k Keeper) IterateAssetSupplies(ctx sdk.Context, cb func(supply types.AssetSupply) (stop bool)) {
iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.key), types.AssetSupplyKeyPrefix)
iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.key), types.AssetSupplyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var supply types.AssetSupply
k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &supply)
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &supply)
if cb(supply) {
break
@ -218,7 +214,7 @@ func (k Keeper) IterateAssetSupplies(ctx sdk.Context, cb func(supply types.Asset
}
}
// GetAllAssetSupplies returns current asset supplies from the store as an array of sdk.Coin
// GetAllAssetSupplies returns all asset supplies from the store
func (k Keeper) GetAllAssetSupplies(ctx sdk.Context) (supplies types.AssetSupplies) {
k.IterateAssetSupplies(ctx, func(supply types.AssetSupply) bool {
supplies = append(supplies, supply)

View File

@ -308,64 +308,32 @@ func (suite *KeeperTestSuite) TestIterateAtomicSwapsLongtermStorage() {
}
func (suite *KeeperTestSuite) TestGetSetAssetSupply() {
suite.ResetChain()
denom := "bnb"
// Put asset supply in store
assetSupply := types.NewAssetSupply(denom, c(denom, 0), c(denom, 0), c(denom, 50000), c(denom, 100000))
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, []byte(denom))
assetSupply := types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 50000))
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, denom)
// Check asset in store
storedAssetSupply, found := suite.keeper.GetAssetSupply(suite.ctx, []byte(denom))
storedAssetSupply, found := suite.keeper.GetAssetSupply(suite.ctx, denom)
suite.True(found)
suite.Equal(assetSupply, storedAssetSupply)
// Check fake asset supply not in store
fakeDenom := "xyz"
_, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(fakeDenom))
_, found = suite.keeper.GetAssetSupply(suite.ctx, fakeDenom)
suite.False(found)
}
func (suite *KeeperTestSuite) TestIterateAssetSupplies() {
suite.ResetChain()
// Set asset supplies
supplies := assetSupplies(5)
for _, supply := range supplies {
suite.keeper.SetAssetSupply(suite.ctx, supply, []byte(supply.Denom))
}
// Read each asset supply from the store
var readSupplies types.AssetSupplies
suite.keeper.IterateAssetSupplies(suite.ctx, func(a types.AssetSupply) bool {
readSupplies = append(readSupplies, a)
return false
})
// Check expected values
for i := 0; i < len(supplies); i++ {
suite.Contains(readSupplies, supplies[i])
}
}
func (suite *KeeperTestSuite) TestGetAllAssetSupplies() {
suite.ResetChain()
// Set asset supplies
count := 3
supplies := assetSupplies(count)
for _, supply := range supplies {
suite.keeper.SetAssetSupply(suite.ctx, supply, []byte(supply.Denom))
}
// Put asset supply in store
assetSupply := types.NewAssetSupply(c("bnb", 0), c("bnb", 0), c("bnb", 50000))
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "bnb")
assetSupply = types.NewAssetSupply(c("inc", 0), c("inc", 0), c("inc", 50000))
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "inc")
// Get all asset supplies
readSupplies := suite.keeper.GetAllAssetSupplies(suite.ctx)
suite.Equal(count, len(readSupplies))
// Check expected values
for i := 0; i < count; i++ {
suite.Contains(readSupplies, supplies[i])
}
supplies := suite.keeper.GetAllAssetSupplies(suite.ctx)
suite.Equal(2, len(supplies))
}
func TestKeeperTestSuite(t *testing.T) {

View File

@ -18,63 +18,100 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramSubspace.SetParamSet(ctx, &params)
}
// GetBnbDeputyAddress returns the Bnbchain's deputy address
func (k Keeper) GetBnbDeputyAddress(ctx sdk.Context) sdk.AccAddress {
// ------------------------------------------
// Asset
// ------------------------------------------
// GetAsset returns the asset param associated with the input denom
func (k Keeper) GetAsset(ctx sdk.Context, denom string) (types.AssetParam, error) {
params := k.GetParams(ctx)
return params.BnbDeputyAddress
for _, asset := range params.AssetParams {
if denom == asset.Denom {
return asset, nil
}
}
return types.AssetParam{}, sdkerrors.Wrap(types.ErrAssetNotSupported, denom)
}
// GetBnbDeputyFixedFee returns the deputy's fixed fee
func (k Keeper) GetBnbDeputyFixedFee(ctx sdk.Context) sdk.Int {
// SetAsset sets an asset in the params
func (k Keeper) SetAsset(ctx sdk.Context, asset types.AssetParam) {
params := k.GetParams(ctx)
return params.BnbDeputyFixedFee
}
// GetMinAmount returns the minimum amount
func (k Keeper) GetMinAmount(ctx sdk.Context) sdk.Int {
params := k.GetParams(ctx)
return params.MinAmount
}
// GetMaxAmount returns the maximum amount
func (k Keeper) GetMaxAmount(ctx sdk.Context) sdk.Int {
params := k.GetParams(ctx)
return params.MaxAmount
}
// GetMinBlockLock returns the minimum block lock
func (k Keeper) GetMinBlockLock(ctx sdk.Context) uint64 {
params := k.GetParams(ctx)
return params.MinBlockLock
}
// GetMaxBlockLock returns the maximum block lock
func (k Keeper) GetMaxBlockLock(ctx sdk.Context) uint64 {
params := k.GetParams(ctx)
return params.MaxBlockLock
for i := range params.AssetParams {
if params.AssetParams[i].Denom == asset.Denom {
params.AssetParams[i] = asset
}
}
k.SetParams(ctx, params)
}
// GetAssets returns a list containing all supported assets
func (k Keeper) GetAssets(ctx sdk.Context) (types.AssetParams, bool) {
params := k.GetParams(ctx)
return params.SupportedAssets, len(params.SupportedAssets) > 0
return params.AssetParams, len(params.AssetParams) > 0
}
// GetAssetByDenom returns an asset by its denom
func (k Keeper) GetAssetByDenom(ctx sdk.Context, denom string) (types.AssetParam, bool) {
params := k.GetParams(ctx)
for _, asset := range params.SupportedAssets {
if asset.Denom == denom {
return asset, true
}
// ------------------------------------------
// Asset-specific getters
// ------------------------------------------
// GetDeputyAddress returns the deputy address for the input denom
func (k Keeper) GetDeputyAddress(ctx sdk.Context, denom string) (sdk.AccAddress, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return sdk.AccAddress{}, err
}
return types.AssetParam{}, false
return asset.DeputyAddress, nil
}
// GetFixedFee returns the fixed fee for incoming swaps
func (k Keeper) GetFixedFee(ctx sdk.Context, denom string) (sdk.Int, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return sdk.Int{}, err
}
return asset.FixedFee, nil
}
// GetMinSwapAmount returns the minimum swap amount
func (k Keeper) GetMinSwapAmount(ctx sdk.Context, denom string) (sdk.Int, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return sdk.Int{}, err
}
return asset.MinSwapAmount, nil
}
// GetMaxSwapAmount returns the maximum swap amount
func (k Keeper) GetMaxSwapAmount(ctx sdk.Context, denom string) (sdk.Int, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return sdk.Int{}, err
}
return asset.MaxSwapAmount, nil
}
// GetMinBlockLock returns the minimum block lock
func (k Keeper) GetMinBlockLock(ctx sdk.Context, denom string) (uint64, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return uint64(0), err
}
return asset.MinBlockLock, nil
}
// GetMaxBlockLock returns the maximum block lock
func (k Keeper) GetMaxBlockLock(ctx sdk.Context, denom string) (uint64, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return uint64(0), err
}
return asset.MaxBlockLock, nil
}
// GetAssetByCoinID returns an asset by its denom
func (k Keeper) GetAssetByCoinID(ctx sdk.Context, coinID int) (types.AssetParam, bool) {
params := k.GetParams(ctx)
for _, asset := range params.SupportedAssets {
for _, asset := range params.AssetParams {
if asset.CoinID == coinID {
return asset, true
}
@ -84,12 +121,21 @@ func (k Keeper) GetAssetByCoinID(ctx sdk.Context, coinID int) (types.AssetParam,
// ValidateLiveAsset checks if an asset is both supported and active
func (k Keeper) ValidateLiveAsset(ctx sdk.Context, coin sdk.Coin) error {
asset, found := k.GetAssetByDenom(ctx, coin.Denom)
if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
asset, err := k.GetAsset(ctx, coin.Denom)
if err != nil {
return err
}
if !asset.Active {
return sdkerrors.Wrap(types.ErrAssetNotActive, asset.Denom)
}
return nil
}
// GetSupplyLimit returns the supply limit for the input denom
func (k Keeper) GetSupplyLimit(ctx sdk.Context, denom string) (sdk.Int, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return sdk.Int{}, err
}
return asset.SupplyLimit, nil
}

View File

@ -35,78 +35,81 @@ func (suite *ParamsTestSuite) SetupTest() {
suite.addrs = addrs
}
func (suite *ParamsTestSuite) TestGetSetBnbDeputyAddress() {
params := suite.keeper.GetParams(suite.ctx)
params.BnbDeputyAddress = suite.addrs[1]
suite.NotPanics(func() { suite.keeper.SetParams(suite.ctx, params) })
func (suite *ParamsTestSuite) TestGetSetAsset() {
asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
suite.Require().NoError(err)
suite.NotPanics(func() { suite.keeper.SetAsset(suite.ctx, asset) })
_, err = suite.keeper.GetAsset(suite.ctx, "dne")
suite.Require().Error(err)
params = suite.keeper.GetParams(suite.ctx)
suite.Equal(suite.addrs[1], params.BnbDeputyAddress)
addr := suite.keeper.GetBnbDeputyAddress(suite.ctx)
suite.Equal(suite.addrs[1], addr)
}
func (suite *ParamsTestSuite) TestGetBnbDeputyFixedFee() {
params := suite.keeper.GetParams(suite.ctx)
bnbDeputyFixedFee := params.BnbDeputyFixedFee
res := suite.keeper.GetBnbDeputyFixedFee(suite.ctx)
suite.Equal(bnbDeputyFixedFee, res)
}
func (suite *ParamsTestSuite) TestGetMinAmount() {
params := suite.keeper.GetParams(suite.ctx)
minAmount := params.MinAmount
res := suite.keeper.GetMinAmount(suite.ctx)
suite.Equal(minAmount, res)
}
func (suite *ParamsTestSuite) TestGetMaxAmount() {
params := suite.keeper.GetParams(suite.ctx)
maxAmount := params.MaxAmount
res := suite.keeper.GetMaxAmount(suite.ctx)
suite.Equal(maxAmount, res)
}
func (suite *ParamsTestSuite) TestGetMinBlockLock() {
params := suite.keeper.GetParams(suite.ctx)
minBlockLock := params.MinBlockLock
res := suite.keeper.GetMinBlockLock(suite.ctx)
suite.Equal(minBlockLock, res)
}
func (suite *ParamsTestSuite) TestGetMaxBlockLock() {
params := suite.keeper.GetParams(suite.ctx)
maxBlockLock := params.MaxBlockLock
res := suite.keeper.GetMaxBlockLock(suite.ctx)
suite.Equal(maxBlockLock, res)
_, err = suite.keeper.GetAsset(suite.ctx, "inc")
suite.Require().NoError(err)
}
func (suite *ParamsTestSuite) TestGetAssets() {
params := suite.keeper.GetParams(suite.ctx)
assets := params.SupportedAssets
res, found := suite.keeper.GetAssets(suite.ctx)
suite.True(found)
suite.Equal(assets, res)
assets, found := suite.keeper.GetAssets(suite.ctx)
suite.Require().True(found)
suite.Require().Equal(2, len(assets))
}
func (suite *ParamsTestSuite) TestGetAssetByDenom() {
params := suite.keeper.GetParams(suite.ctx)
asset := params.SupportedAssets[0]
func (suite *ParamsTestSuite) TestGetSetDeputyAddress() {
asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
suite.Require().NoError(err)
asset.DeputyAddress = suite.addrs[1]
suite.NotPanics(func() { suite.keeper.SetAsset(suite.ctx, asset) })
res, found := suite.keeper.GetAssetByDenom(suite.ctx, asset.Denom)
suite.True(found)
suite.Equal(asset, res)
asset, err = suite.keeper.GetAsset(suite.ctx, "bnb")
suite.Require().NoError(err)
suite.Equal(suite.addrs[1], asset.DeputyAddress)
addr, err := suite.keeper.GetDeputyAddress(suite.ctx, "bnb")
suite.Require().NoError(err)
suite.Equal(suite.addrs[1], addr)
}
func (suite *ParamsTestSuite) TestGetDeputyFixedFee() {
asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
suite.Require().NoError(err)
bnbDeputyFixedFee := asset.FixedFee
res, err := suite.keeper.GetFixedFee(suite.ctx, asset.Denom)
suite.Require().NoError(err)
suite.Equal(bnbDeputyFixedFee, res)
}
func (suite *ParamsTestSuite) TestGetMinMaxSwapAmount() {
asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
suite.Require().NoError(err)
minAmount := asset.MinSwapAmount
res, err := suite.keeper.GetMinSwapAmount(suite.ctx, asset.Denom)
suite.Require().NoError(err)
suite.Equal(minAmount, res)
maxAmount := asset.MaxSwapAmount
res, err = suite.keeper.GetMaxSwapAmount(suite.ctx, asset.Denom)
suite.Require().NoError(err)
suite.Equal(maxAmount, res)
}
func (suite *ParamsTestSuite) TestGetMinMaxBlockLock() {
asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
suite.Require().NoError(err)
minLock := asset.MinBlockLock
res, err := suite.keeper.GetMinBlockLock(suite.ctx, asset.Denom)
suite.Require().NoError(err)
suite.Equal(minLock, res)
maxLock := asset.MaxBlockLock
res, err = suite.keeper.GetMaxBlockLock(suite.ctx, asset.Denom)
suite.Require().NoError(err)
suite.Equal(maxLock, res)
}
func (suite *ParamsTestSuite) TestGetAssetByCoinID() {
params := suite.keeper.GetParams(suite.ctx)
asset := params.SupportedAssets[0]
asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
suite.Require().NoError(err)
res, found := suite.keeper.GetAssetByCoinID(suite.ctx, asset.CoinID)
suite.True(found)

View File

@ -39,7 +39,7 @@ func queryAssetSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
assetSupply, found := keeper.GetAssetSupply(ctx, []byte(requestParams.Denom))
assetSupply, found := keeper.GetAssetSupply(ctx, requestParams.Denom)
if !found {
return nil, sdkerrors.Wrap(types.ErrAssetSupplyNotFound, string(requestParams.Denom))
}

View File

@ -39,16 +39,16 @@ func (suite *QuerierTestSuite) SetupTest() {
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
// Set up auth GenesisState
_, addrs := app.GeneratePrivKeyAddressPairs(10)
_, addrs := app.GeneratePrivKeyAddressPairs(11)
coins := []sdk.Coins{}
for j := 0; j < 10; j++ {
for j := 0; j < 11; j++ {
coins = append(coins, cs(c("bnb", 10000000000), c("ukava", 10000000000)))
}
authGS := app.NewAuthGenState(addrs, coins)
tApp.InitializeFromGenesisStates(
authGS,
NewBep3GenStateMulti(addrs[0]),
NewBep3GenStateMulti(addrs[10]),
)
suite.ctx = ctx
@ -70,11 +70,11 @@ func (suite *QuerierTestSuite) SetupTest() {
// Create atomic swap and check err
err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight,
addrs[0], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain, amount, true)
addrs[10], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain, amount, true)
suite.Nil(err)
// Calculate swap ID and save
swapID := types.CalculateSwapID(randomNumberHash, addrs[0], TestSenderOtherChain)
swapID := types.CalculateSwapID(randomNumberHash, addrs[10], TestSenderOtherChain)
swapIDs = append(swapIDs, swapID)
isSwapID[hex.EncodeToString(swapID)] = true
}
@ -89,7 +89,7 @@ func (suite *QuerierTestSuite) TestQueryAssetSupply() {
denom := "bnb"
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAssetSupply}, "/"),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAssetSupply(tmbytes.HexBytes(denom))),
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAssetSupply(denom)),
}
// Execute query and check the []byte result
@ -101,8 +101,8 @@ func (suite *QuerierTestSuite) TestQueryAssetSupply() {
var supply types.AssetSupply
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &supply))
expectedSupply := types.NewAssetSupply(denom, c(denom, 1000),
c(denom, 0), c(denom, 0), c(denom, StandardSupplyLimit.Int64()))
expectedSupply := types.NewAssetSupply(c(denom, 1000),
c(denom, 0), c(denom, 0))
suite.Equal(supply, expectedSupply)
}
@ -179,9 +179,10 @@ func (suite *QuerierTestSuite) TestQueryParams() {
var p types.Params
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &p))
bep3GenesisState := NewBep3GenStateMulti(suite.addrs[0])
bep3GenesisState := NewBep3GenStateMulti(suite.addrs[10])
gs := types.GenesisState{}
types.ModuleCdc.UnmarshalJSON(bep3GenesisState["bep3"], &gs)
// update asset supply to account for swaps that were created in setup
suite.Equal(gs.Params, p)
}

View File

@ -31,29 +31,38 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times
if len(amount) != 1 {
return fmt.Errorf("amount must contain exactly one coin")
}
asset, err := k.GetAsset(ctx, amount[0].Denom)
if err != nil {
return err
}
err := k.ValidateLiveAsset(ctx, amount[0])
err = k.ValidateLiveAsset(ctx, amount[0])
if err != nil {
return err
}
// Swap amount must be within the specified swap amount limits
if amount[0].Amount.LT(k.GetMinAmount(ctx)) || amount[0].Amount.GT(k.GetMaxAmount(ctx)) {
return sdkerrors.Wrapf(types.ErrInvalidAmount, "amount %d outside range [%d, %d]", amount[0].Amount, k.GetMinAmount(ctx), k.GetMaxAmount(ctx))
if amount[0].Amount.LT(asset.MinSwapAmount) || amount[0].Amount.GT(asset.MaxSwapAmount) {
return sdkerrors.Wrapf(types.ErrInvalidAmount, "amount %d outside range [%s, %s]", amount[0].Amount, asset.MinSwapAmount, asset.MaxSwapAmount)
}
// Unix timestamp must be in range [-15 mins, 30 mins] of the current time
pastTimestampLimit := ctx.BlockTime().Add(time.Duration(-15) * time.Minute).Unix()
futureTimestampLimit := ctx.BlockTime().Add(time.Duration(30) * time.Minute).Unix()
if timestamp < pastTimestampLimit || timestamp >= futureTimestampLimit {
return sdkerrors.Wrap(types.ErrInvalidTimestamp, time.Unix(timestamp, 0).UTC().String())
return sdkerrors.Wrap(types.ErrInvalidTimestamp, fmt.Sprintf("block time: %s, timestamp: %s", ctx.BlockTime().String(), time.Unix(timestamp, 0).UTC().String()))
}
var direction types.SwapDirection
deputy := k.GetBnbDeputyAddress(ctx)
if sender.Equals(deputy) {
if sender.Equals(asset.DeputyAddress) {
if recipient.Equals(asset.DeputyAddress) {
return sdkerrors.Wrapf(types.ErrInvalidSwapAccount, "deputy cannot be both sender and receiver: %s", asset.DeputyAddress)
}
direction = types.Incoming
} else {
if !recipient.Equals(asset.DeputyAddress) {
return sdkerrors.Wrapf(types.ErrInvalidSwapAccount, "deputy must be recipient for outgoing account: %s", recipient)
}
direction = types.Outgoing
}
@ -69,18 +78,13 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times
// Incoming swaps have already had their fees collected by the deputy during the relay process.
err = k.IncrementIncomingAssetSupply(ctx, amount[0])
case types.Outgoing:
if ctx.BlockTime().After(types.SupplyLimitUpgradeTime) {
if !recipient.Equals(deputy) {
return sdkerrors.Wrapf(types.ErrInvalidOutgoingAccount, "%s", recipient)
}
}
// Outoing swaps must have a height span within the accepted range
if heightSpan < k.GetMinBlockLock(ctx) || heightSpan > k.GetMaxBlockLock(ctx) {
return sdkerrors.Wrapf(types.ErrInvalidHeightSpan, "height span %d outside range [%d, %d]", heightSpan, k.GetMinBlockLock(ctx), k.GetMaxBlockLock(ctx))
if heightSpan < asset.MinBlockLock || heightSpan > asset.MaxBlockLock {
return sdkerrors.Wrapf(types.ErrInvalidHeightSpan, "height span %d outside range [%d, %d]", heightSpan, asset.MinBlockLock, asset.MaxBlockLock)
}
// Amount in outgoing swaps must be able to pay the deputy's fixed fee.
if amount[0].Amount.LTE(k.GetBnbDeputyFixedFee(ctx).Add(k.GetMinAmount(ctx))) {
if amount[0].Amount.LTE(asset.FixedFee.Add(asset.MinSwapAmount)) {
return sdkerrors.Wrap(types.ErrInsufficientAmount, amount[0].String())
}
err = k.IncrementOutgoingAssetSupply(ctx, amount[0])

View File

@ -156,60 +156,6 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
true,
true,
},
{
"outgoing swap recipient before update",
types.SupplyLimitUpgradeTime.Add(-time.Hour),
args{
randomNumberHash: suite.randomNumberHashes[12],
timestamp: types.SupplyLimitUpgradeTime.Add(-time.Hour).Unix(),
heightSpan: types.DefaultMinBlockLock,
sender: suite.addrs[1],
recipient: suite.addrs[2],
senderOtherChain: TestUser1.String(),
recipientOtherChain: TestUser2.String(),
coins: cs(c(BNB_DENOM, 50000)),
crossChain: true,
direction: types.Outgoing,
},
true,
true,
},
{
"outgoing swap recipient at update time",
types.SupplyLimitUpgradeTime,
args{
randomNumberHash: suite.randomNumberHashes[13],
timestamp: types.SupplyLimitUpgradeTime.Unix(),
heightSpan: types.DefaultMinBlockLock,
sender: suite.addrs[1],
recipient: suite.addrs[2],
senderOtherChain: TestUser1.String(),
recipientOtherChain: TestUser2.String(),
coins: cs(c(BNB_DENOM, 50000)),
crossChain: true,
direction: types.Outgoing,
},
true,
true,
},
{
"outgoing swap recipient after update",
types.SupplyLimitUpgradeTime.Add(time.Nanosecond),
args{
randomNumberHash: suite.randomNumberHashes[14],
timestamp: types.SupplyLimitUpgradeTime.Add(time.Nanosecond).Unix(),
heightSpan: types.DefaultMinBlockLock,
sender: suite.addrs[1],
recipient: suite.addrs[2],
senderOtherChain: TestUser1.String(),
recipientOtherChain: TestUser2.String(),
coins: cs(c(BNB_DENOM, 50000)),
crossChain: true,
direction: types.Outgoing,
},
false,
false,
},
{
"outgoing swap amount not greater than fixed fee",
@ -222,7 +168,7 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
recipient: suite.addrs[2],
senderOtherChain: TestSenderOtherChain,
recipientOtherChain: TestRecipientOtherChain,
coins: cs(c(BNB_DENOM, suite.keeper.GetBnbDeputyFixedFee(suite.ctx).Int64())),
coins: cs(c(BNB_DENOM, 1000)),
crossChain: true,
direction: types.Outgoing,
},
@ -432,7 +378,7 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
ak := suite.app.GetAccountKeeper()
senderAccPre := ak.GetAccount(suite.ctx, tc.args.sender)
senderBalancePre := senderAccPre.GetCoins().AmountOf(swapAssetDenom)
assetSupplyPre, _ := suite.keeper.GetAssetSupply(suite.ctx, []byte(swapAssetDenom))
assetSupplyPre, _ := suite.keeper.GetAssetSupply(suite.ctx, swapAssetDenom)
// Create atomic swap
err := suite.keeper.CreateAtomicSwap(suite.ctx, tc.args.randomNumberHash, tc.args.timestamp,
@ -442,7 +388,7 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
// Load sender's account after swap creation
senderAccPost := ak.GetAccount(suite.ctx, tc.args.sender)
senderBalancePost := senderAccPost.GetCoins().AmountOf(swapAssetDenom)
assetSupplyPost, _ := suite.keeper.GetAssetSupply(suite.ctx, []byte(swapAssetDenom))
assetSupplyPost, _ := suite.keeper.GetAssetSupply(suite.ctx, swapAssetDenom)
// Load expected swap ID
expectedSwapID := types.CalculateSwapID(tc.args.randomNumberHash, tc.args.sender, tc.args.senderOtherChain)
@ -625,7 +571,7 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
expectedRecipientAccPre := ak.GetAccount(tc.claimCtx, expectedRecipient)
expectedRecipientBalancePre := expectedRecipientAccPre.GetCoins().AmountOf(expectedClaimAmount[0].Denom)
// Load asset supplies prior to claim attempt
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.claimCtx, []byte(expectedClaimAmount[0].Denom))
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.claimCtx, expectedClaimAmount[0].Denom)
// Attempt to claim atomic swap
err = suite.keeper.ClaimAtomicSwap(tc.claimCtx, expectedRecipient, claimSwapID, claimRandomNumber)
@ -634,7 +580,7 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
expectedRecipientAccPost := ak.GetAccount(tc.claimCtx, expectedRecipient)
expectedRecipientBalancePost := expectedRecipientAccPost.GetCoins().AmountOf(expectedClaimAmount[0].Denom)
// Load asset supplies after the claim attempt
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.claimCtx, []byte(expectedClaimAmount[0].Denom))
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.claimCtx, expectedClaimAmount[0].Denom)
if tc.expectPass {
suite.NoError(err)
@ -773,7 +719,7 @@ func (suite *AtomicSwapTestSuite) TestRefundAtomicSwap() {
originalSenderAccPre := ak.GetAccount(tc.refundCtx, sender)
originalSenderBalancePre := originalSenderAccPre.GetCoins().AmountOf(expectedRefundAmount[0].Denom)
// Load asset supply prior to swap refund
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.refundCtx, []byte(expectedRefundAmount[0].Denom))
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.refundCtx, expectedRefundAmount[0].Denom)
// Attempt to refund atomic swap
err = suite.keeper.RefundAtomicSwap(tc.refundCtx, sender, refundSwapID)
@ -782,7 +728,7 @@ func (suite *AtomicSwapTestSuite) TestRefundAtomicSwap() {
originalSenderAccPost := ak.GetAccount(tc.refundCtx, sender)
originalSenderBalancePost := originalSenderAccPost.GetCoins().AmountOf(expectedRefundAmount[0].Denom)
// Load asset supply after to swap refund
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.refundCtx, []byte(expectedRefundAmount[0].Denom))
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.refundCtx, expectedRefundAmount[0].Denom)
if tc.expectPass {
suite.NoError(err)

View File

@ -0,0 +1,81 @@
package v0_11
import (
sdk "github.com/cosmos/cosmos-sdk/types"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
)
// GenesisState - all bep3 state that must be provided at genesis
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
AtomicSwaps AtomicSwaps `json:"atomic_swaps" yaml:"atomic_swaps"`
}
// Params governance parameters for the bep3 module
type Params struct {
AssetParams AssetParams `json:"asset_params" yaml:"asset_params"`
}
// AssetParam parameters that must be specified for each bep3 asset
type AssetParam struct {
Denom string `json:"denom" yaml:"denom"` // name of the asset
CoinID int `json:"coin_id" yaml:"coin_id"` // SLIP-0044 registered coin type - see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
SupplyLimit AssetSupply `json:"supply_limit" yaml:"supply_limit"` // asset supply limit
Active bool `json:"active" yaml:"active"` // denotes if asset is available or paused
DeputyAddress sdk.AccAddress `json:"deputy_address" yaml:"deputy_address"` // the address of the relayer process
FixedFee sdk.Int `json:"incoming_swap_fixed_fee" yaml:"incoming_swap_fixed_fee"` // the fixed fee charged by the relayer process for incoming swaps
MinSwapAmount sdk.Int `json:"min_swap_amount" yaml:"min_swap_amount"` // Minimum swap amount
MaxSwapAmount sdk.Int `json:"max_swap_amount" yaml:"max_swap_amount"` // Maximum swap amount
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // Minimum swap block lock
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // Maximum swap block lock
}
// AssetParams array of AssetParam
type AssetParams []AssetParam
// AssetSupply contains information about an asset's supply
type AssetSupply struct {
IncomingSupply sdk.Coin `json:"incoming_supply" yaml:"incoming_supply"`
OutgoingSupply sdk.Coin `json:"outgoing_supply" yaml:"outgoing_supply"`
CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"`
SupplyLimit sdk.Coin `json:"supply_limit" yaml:"supply_limit"`
}
// AtomicSwap contains the information for an atomic swap
type AtomicSwap struct {
Amount sdk.Coins `json:"amount" yaml:"amount"`
RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"`
ExpireHeight uint64 `json:"expire_height" yaml:"expire_height"`
Timestamp int64 `json:"timestamp" yaml:"timestamp"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"`
SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"`
RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"`
ClosedBlock int64 `json:"closed_block" yaml:"closed_block"`
Status SwapStatus `json:"status" yaml:"status"`
CrossChain bool `json:"cross_chain" yaml:"cross_chain"`
Direction SwapDirection `json:"direction" yaml:"direction"`
}
// AtomicSwaps is a slice of AtomicSwap
type AtomicSwaps []AtomicSwap
// SwapStatus is the status of an AtomicSwap
type SwapStatus byte
// swap statuses
const (
NULL SwapStatus = 0x00
Open SwapStatus = 0x01
Completed SwapStatus = 0x02
Expired SwapStatus = 0x03
)
// SwapDirection is the direction of an AtomicSwap
type SwapDirection byte
const (
INVALID SwapDirection = 0x00
Incoming SwapDirection = 0x01
Outgoing SwapDirection = 0x02
)

151
x/bep3/legacy/v0_9/types.go Normal file
View File

@ -0,0 +1,151 @@
package v0_9
import (
sdk "github.com/cosmos/cosmos-sdk/types"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
)
const (
ModuleName = "bep3"
)
var (
DefaultBnbDeputyFixedFee sdk.Int = sdk.NewInt(1000) // 0.00001 BNB
DefaultMinAmount sdk.Int = sdk.ZeroInt()
DefaultMaxAmount sdk.Int = sdk.NewInt(1000000000000) // 10,000 BNB
DefaultMinBlockLock uint64 = 220
DefaultMaxBlockLock uint64 = 270
DefaultSupportedAssets = AssetParams{
AssetParam{
Denom: "bnb",
CoinID: 714,
Limit: sdk.NewInt(350000000000000), // 3,500,000 BNB
Active: true,
},
}
KeySupportedAssets = []byte("SupportedAssets")
)
// Params v0.9 governance parameters for bep3 module
type Params struct {
BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // Bnbchain deputy address
BnbDeputyFixedFee sdk.Int `json:"bnb_deputy_fixed_fee" yaml:"bnb_deputy_fixed_fee"` // Deputy fixed fee in BNB
MinAmount sdk.Int `json:"min_amount" yaml:"min_amount"` // Minimum swap amount
MaxAmount sdk.Int `json:"max_amount" yaml:"max_amount"` // Maximum swap amount
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // Minimum swap block lock
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // Maximum swap block lock
SupportedAssets AssetParams `json:"supported_assets" yaml:"supported_assets"` // Supported assets
}
// NewParams returns a new params object
func NewParams(bnbDeputyAddress sdk.AccAddress, bnbDeputyFixedFee, minAmount,
maxAmount sdk.Int, minBlockLock, maxBlockLock uint64, supportedAssets AssetParams,
) Params {
return Params{
BnbDeputyAddress: bnbDeputyAddress,
BnbDeputyFixedFee: bnbDeputyFixedFee,
MinAmount: minAmount,
MaxAmount: maxAmount,
MinBlockLock: minBlockLock,
MaxBlockLock: maxBlockLock,
SupportedAssets: supportedAssets,
}
}
// AssetParam v0.9 governance parameters for each asset supported on kava chain
type AssetParam struct {
Denom string `json:"denom" yaml:"denom"` // name of the asset
CoinID int `json:"coin_id" yaml:"coin_id"` // internationally recognized coin ID
Limit sdk.Int `json:"limit" yaml:"limit"` // asset supply limit
Active bool `json:"active" yaml:"active"` // denotes if asset is available or paused
}
// AssetParams is a slice of AssetParams
type AssetParams []AssetParam
// GenesisState - all bep3 state that must be provided at genesis
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
AtomicSwaps AtomicSwaps `json:"atomic_swaps" yaml:"atomic_swaps"`
AssetSupplies AssetSupplies `json:"assets_supplies" yaml:"assets_supplies"`
}
// NewGenesisState creates a new GenesisState object
func NewGenesisState(params Params, swaps AtomicSwaps, supplies AssetSupplies) GenesisState {
return GenesisState{
Params: params,
AtomicSwaps: swaps,
AssetSupplies: supplies,
}
}
// DefaultGenesisState - default GenesisState used by Cosmos Hub
func DefaultGenesisState() GenesisState {
return NewGenesisState(
DefaultParams(),
AtomicSwaps{},
AssetSupplies{},
)
}
// DefaultParams returns default params for bep3 module
func DefaultParams() Params {
defaultBnbDeputyAddress, err := sdk.AccAddressFromBech32("kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6")
if err != nil {
panic(err)
}
return NewParams(defaultBnbDeputyAddress, DefaultBnbDeputyFixedFee, DefaultMinAmount,
DefaultMaxAmount, DefaultMinBlockLock, DefaultMaxBlockLock, DefaultSupportedAssets)
}
// AtomicSwap contains the information for an atomic swap
type AtomicSwap struct {
Amount sdk.Coins `json:"amount" yaml:"amount"`
RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"`
ExpireHeight uint64 `json:"expire_height" yaml:"expire_height"`
Timestamp int64 `json:"timestamp" yaml:"timestamp"`
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"`
SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"`
RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"`
ClosedBlock int64 `json:"closed_block" yaml:"closed_block"`
Status SwapStatus `json:"status" yaml:"status"`
CrossChain bool `json:"cross_chain" yaml:"cross_chain"`
Direction SwapDirection `json:"direction" yaml:"direction"`
}
// AtomicSwaps is a slice of AtomicSwap
type AtomicSwaps []AtomicSwap
// AssetSupply contains information about an asset's supply
type AssetSupply struct {
Denom string `json:"denom" yaml:"denom"`
IncomingSupply sdk.Coin `json:"incoming_supply" yaml:"incoming_supply"`
OutgoingSupply sdk.Coin `json:"outgoing_supply" yaml:"outgoing_supply"`
CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"`
SupplyLimit sdk.Coin `json:"supply_limit" yaml:"supply_limit"`
}
// AssetSupplies is a slice of AssetSupply
type AssetSupplies []AssetSupply
// SwapStatus is the status of an AtomicSwap
type SwapStatus byte
// swap statuses
const (
NULL SwapStatus = 0x00
Open SwapStatus = 0x01
Completed SwapStatus = 0x02
Expired SwapStatus = 0x03
)
// SwapDirection is the direction of an AtomicSwap
type SwapDirection byte
const (
INVALID SwapDirection = 0x00
Incoming SwapDirection = 0x01
Outgoing SwapDirection = 0x02
)

View File

@ -21,17 +21,16 @@ func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string {
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &swapB)
return fmt.Sprintf("%v\n%v", swapA, swapB)
case bytes.Equal(kvA.Key[:1], types.AssetSupplyKeyPrefix):
var supplyA, supplyB types.AssetSupply
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA)
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB)
return fmt.Sprintf("%s\n%s", supplyA, supplyB)
case bytes.Equal(kvA.Key[:1], types.AtomicSwapByBlockPrefix),
bytes.Equal(kvA.Key[:1], types.AtomicSwapLongtermStoragePrefix):
var bytesA tmbytes.HexBytes = kvA.Value
var bytesB tmbytes.HexBytes = kvA.Value
return fmt.Sprintf("%s\n%s", bytesA.String(), bytesB.String())
case bytes.Equal(kvA.Key[:1], types.AssetSupplyPrefix):
var supplyA, supplyB types.AssetSupply
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA)
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB)
return fmt.Sprintf("%s\n%s", supplyA, supplyB)
default:
panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1]))

View File

@ -27,12 +27,12 @@ func TestDecodeDistributionStore(t *testing.T) {
oneCoin := sdk.NewCoin("coin", sdk.OneInt())
swap := types.NewAtomicSwap(sdk.Coins{oneCoin}, nil, 10, 100, nil, nil, "otherChainSender", "otherChainRec", 200, types.Completed, true, types.Outgoing)
supply := types.AssetSupply{Denom: "coin", IncomingSupply: oneCoin, OutgoingSupply: oneCoin, CurrentSupply: oneCoin, SupplyLimit: oneCoin}
supply := types.AssetSupply{IncomingSupply: oneCoin, OutgoingSupply: oneCoin, CurrentSupply: oneCoin}
bz := tmbytes.HexBytes([]byte{1, 2})
kvPairs := kv.Pairs{
kv.Pair{Key: types.AtomicSwapKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(swap)},
kv.Pair{Key: types.AssetSupplyKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(supply)},
kv.Pair{Key: types.AssetSupplyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(supply)},
kv.Pair{Key: types.AtomicSwapByBlockPrefix, Value: bz},
kv.Pair{Key: types.AtomicSwapByBlockPrefix, Value: bz},
kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}},

View File

@ -16,21 +16,13 @@ import (
"github.com/kava-labs/kava/x/bep3/types"
)
// Simulation parameter constants
const (
BnbDeputyAddress = "bnb_deputy_address"
BnbDeputyFixedFee = "bnb_deputy_fixed_fee"
MinAmount = "min_amount"
MaxAmount = "max_amount"
MinBlockLock = "min_block_lock"
MaxBlockLock = "max_block_lock"
SupportedAssets = "supported_assets"
)
var (
MaxSupplyLimit = sdk.NewInt(1000000000000)
accs []simulation.Account
ConsistentDenoms = [3]string{"bnb", "xrp", "btc"}
MaxSupplyLimit = 1000000000000
MinSupplyLimit = 100000000
MinSwapAmountLimit = 999
accs []simulation.Account
ConsistentDenoms = [3]string{"bnb", "xrp", "btc"}
MinBlockLock = uint64(5)
)
// GenRandBnbDeputy randomized BnbDeputyAddress
@ -39,39 +31,48 @@ func GenRandBnbDeputy(r *rand.Rand) simulation.Account {
return acc
}
// GenRandBnbDeputyFixedFee randomized BnbDeputyFixedFee in range [2, 10000]
func GenRandBnbDeputyFixedFee(r *rand.Rand) sdk.Int {
// GenRandFixedFee randomized FixedFee in range [1, 10000]
func GenRandFixedFee(r *rand.Rand) sdk.Int {
min := int(1)
max := types.DefaultBnbDeputyFixedFee.Int64()
return sdk.NewInt(int64(r.Intn(int(max)-min) + min))
}
// GenMinAmount randomized MinAmount in range [0, 1000000000000]
func GenMinAmount(r *rand.Rand) sdk.Int {
min := types.DefaultMinAmount.Int64()
max := types.DefaultMaxAmount.Int64()
// GenMinSwapAmount randomized MinAmount in range [1, 1000]
func GenMinSwapAmount(r *rand.Rand) sdk.Int {
return sdk.OneInt().Add(simulation.RandomAmount(r, sdk.NewInt(int64(MinSwapAmountLimit))))
}
// GenMaxSwapAmount randomized MaxAmount
func GenMaxSwapAmount(r *rand.Rand, minAmount sdk.Int, supplyMax sdk.Int) sdk.Int {
min := minAmount.Int64()
max := supplyMax.Quo(sdk.NewInt(100)).Int64()
return sdk.NewInt((int64(r.Intn(int(max-min))) + min))
}
// GenMaxAmount randomized MaxAmount
func GenMaxAmount(r *rand.Rand, minAmount sdk.Int) sdk.Int {
min := minAmount.Int64()
max := types.DefaultMaxAmount.Int64()
return sdk.NewInt((int64(r.Intn(int(max-min))) + min))
// GenSupplyLimit generates a random SupplyLimit
func GenSupplyLimit(r *rand.Rand, max int) sdk.Int {
max = simulation.RandIntBetween(r, MinSupplyLimit, max)
return sdk.NewInt(int64(max))
}
// GenSupplyLimit generates a random SupplyLimit
func GenAssetSupply(r *rand.Rand, denom string) types.AssetSupply {
return types.NewAssetSupply(
sdk.NewCoin(denom, sdk.ZeroInt()), sdk.NewCoin(denom, sdk.ZeroInt()),
sdk.NewCoin(denom, sdk.ZeroInt()))
}
// GenMinBlockLock randomized MinBlockLock
func GenMinBlockLock(r *rand.Rand) uint64 {
min := int(1)
max := int(types.DefaultMaxBlockLock)
return uint64(r.Intn(max-min) + min)
return MinBlockLock
}
// GenMaxBlockLock randomized MaxBlockLock
func GenMaxBlockLock(r *rand.Rand, minBlockLock uint64) uint64 {
min := int(minBlockLock)
max := int(types.DefaultMaxBlockLock)
return uint64(r.Intn(max-min) + min)
max := int(50)
return uint64(r.Intn(max-int(MinBlockLock)) + int(MinBlockLock+1))
}
// GenSupportedAssets gets randomized SupportedAssets
@ -92,12 +93,21 @@ func GenSupportedAssets(r *rand.Rand) types.AssetParams {
func genSupportedAsset(r *rand.Rand, denom string) types.AssetParam {
coinID, _ := simulation.RandPositiveInt(r, sdk.NewInt(100000))
limit, _ := simulation.RandPositiveInt(r, MaxSupplyLimit)
limit := GenSupplyLimit(r, MaxSupplyLimit)
minSwapAmount := GenMinSwapAmount(r)
minBlockLock := GenMinBlockLock(r)
return types.AssetParam{
Denom: denom,
CoinID: int(coinID.Int64()),
Limit: limit,
Active: true,
Denom: denom,
CoinID: int(coinID.Int64()),
SupplyLimit: limit,
Active: true,
DeputyAddress: GenRandBnbDeputy(r).Address,
FixedFee: GenRandFixedFee(r),
MinSwapAmount: minSwapAmount,
MaxSwapAmount: GenMaxSwapAmount(r, minSwapAmount, limit),
MinBlockLock: minBlockLock,
MaxBlockLock: GenMaxBlockLock(r, minBlockLock),
}
}
@ -123,31 +133,19 @@ func RandomizedGenState(simState *module.SimulationState) {
}
func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState {
bnbDeputy := GenRandBnbDeputy(simState.Rand)
bnbDeputyFixedFee := GenRandBnbDeputyFixedFee(simState.Rand)
minAmount := types.DefaultMinAmount
maxAmount := GenMaxAmount(simState.Rand, minAmount)
// min/max block lock are hardcoded to 50/100 for expected -NumBlocks=100
minBlockLock := uint64(50)
maxBlockLock := minBlockLock * 2
var supportedAssets types.AssetParams
simState.AppParams.GetOrGenerate(
simState.Cdc, SupportedAssets, &supportedAssets, simState.Rand,
func(r *rand.Rand) { supportedAssets = GenSupportedAssets(r) },
)
supportedAssets := GenSupportedAssets(simState.Rand)
supplies := types.AssetSupplies{}
for _, asset := range supportedAssets {
supply := GenAssetSupply(simState.Rand, asset.Denom)
supplies = append(supplies, supply)
}
bep3Genesis := types.GenesisState{
Params: types.Params{
BnbDeputyAddress: bnbDeputy.Address,
BnbDeputyFixedFee: bnbDeputyFixedFee,
MinAmount: minAmount,
MaxAmount: maxAmount,
MinBlockLock: minBlockLock,
MaxBlockLock: maxBlockLock,
SupportedAssets: supportedAssets,
AssetParams: supportedAssets,
},
Supplies: supplies,
}
return bep3Genesis
@ -156,22 +154,20 @@ func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState
func loadAuthGenState(simState *module.SimulationState, bep3Genesis types.GenesisState) (auth.GenesisState, []sdk.Coins) {
var authGenesis auth.GenesisState
simState.Cdc.MustUnmarshalJSON(simState.GenState[auth.ModuleName], &authGenesis)
deputy, found := getAccount(authGenesis.Accounts, bep3Genesis.Params.BnbDeputyAddress)
if !found {
panic("deputy address not found in available accounts")
}
// Load total limit of each supported asset to deputy's account
var totalCoins []sdk.Coins
for _, asset := range bep3Genesis.Params.SupportedAssets {
assetCoin := sdk.NewCoins(sdk.NewCoin(asset.Denom, asset.Limit))
for _, asset := range bep3Genesis.Params.AssetParams {
deputy, found := getAccount(authGenesis.Accounts, asset.DeputyAddress)
if !found {
panic("deputy address not found in available accounts")
}
assetCoin := sdk.NewCoins(sdk.NewCoin(asset.Denom, asset.SupplyLimit))
if err := deputy.SetCoins(deputy.GetCoins().Add(assetCoin...)); err != nil {
panic(err)
}
totalCoins = append(totalCoins, assetCoin)
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, deputy)
}
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, deputy)
return authGenesis, totalCoins
}

View File

@ -16,7 +16,7 @@ import (
)
var (
noOpMsg = simulation.NoOpMsg(types.ModuleName)
noOpMsg = simulation.NoOpMsg(types.ModuleName)
randomNumber = []byte{114, 21, 74, 180, 81, 92, 21, 91, 173, 164, 143, 111, 120, 58, 241, 58, 40, 22, 59, 133, 102, 233, 55, 149, 12, 199, 231, 63, 122, 23, 88, 9}
)
@ -50,50 +50,65 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
// Set up deputy address as it's required for all atomic swaps
deputyAddr := k.GetBnbDeputyAddress(ctx)
deputyAcc, foundDeputy := simulation.FindAccount(accs, deputyAddr)
if !foundDeputy {
// Get asset supplies and shuffle them
assets, found := k.GetAssets(ctx)
if !found {
return noOpMsg, nil, nil
}
// Get asset supplies and shuffle them
supplies := k.GetAllAssetSupplies(ctx)
r.Shuffle(len(supplies), func(i, j int) {
supplies[i], supplies[j] = supplies[j], supplies[i]
r.Shuffle(len(assets), func(i, j int) {
assets[i], assets[j] = assets[j], assets[i]
})
// Search for an account that holds coins received by an atomic swap
minAmountPlusFee := k.GetMinAmount(ctx).Add(k.GetBnbDeputyFixedFee(ctx))
senderOut, asset, found := findValidAccountAssetSupplyPair(accs, supplies, func(acc simulation.Account, asset types.AssetSupply) bool {
if asset.CurrentSupply.Amount.IsPositive() {
authAcc := ak.GetAccount(ctx, acc.Address)
senderOutgoing, selectedAsset, found := findValidAccountAssetPair(accs, assets, func(simAcc simulation.Account, asset types.AssetParam) bool {
supply, found := k.GetAssetSupply(ctx, asset.Denom)
if !found {
return false
}
if supply.CurrentSupply.Amount.IsPositive() {
authAcc := ak.GetAccount(ctx, simAcc.Address)
// deputy cannot be sender of outgoing swap
if authAcc.GetAddress().Equals(asset.DeputyAddress) {
return false
}
// Search for an account that holds coins received by an atomic swap
minAmountPlusFee := asset.MinSwapAmount.Add(asset.FixedFee)
if authAcc.SpendableCoins(ctx.BlockTime()).AmountOf(asset.Denom).GT(minAmountPlusFee) {
return true
}
}
return false
})
// Set sender, recipient, and denom depending on swap direction
var sender simulation.Account
var recipient simulation.Account
var denom string
var asset types.AssetParam
// If an outgoing swap can be created, it's chosen 50% of the time.
if found && r.Intn(100) < 50 {
sender = senderOut
recipient = deputyAcc
denom = asset.Denom
} else {
sender = deputyAcc
recipient, _ = simulation.RandomAcc(r, accs)
// Randomly select an asset from supported assets
assets, foundAsset := k.GetAssets(ctx)
if !foundAsset {
return noOpMsg, nil, fmt.Errorf("no supported assets found")
deputy, found := simulation.FindAccount(accs, selectedAsset.DeputyAddress)
if !found {
return noOpMsg, nil, nil
}
denom = assets[r.Intn(len(assets))].Denom
sender = senderOutgoing
recipient = deputy
asset = selectedAsset
} else {
// if an outgoing swap cannot be created or was not selected, simulate an incoming swap
assets, _ := k.GetAssets(ctx)
asset = assets[r.Intn(len(assets))]
var eligibleAccs []simulation.Account
for _, simAcc := range accs {
// don't allow recipient of incoming swap to be the deputy
if simAcc.Address.Equals(asset.DeputyAddress) {
continue
}
eligibleAccs = append(eligibleAccs, simAcc)
}
recipient, _ = simulation.RandomAcc(r, eligibleAccs)
deputy, found := simulation.FindAccount(accs, asset.DeputyAddress)
if !found {
return noOpMsg, nil, nil
}
sender = deputy
}
recipientOtherChain := simulation.RandStringOfLength(r, 43)
@ -111,32 +126,33 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
}
// Get maximum valid amount
maximumAmount := senderAcc.SpendableCoins(ctx.BlockTime()).Sub(fees).AmountOf(denom)
maximumAmount := senderAcc.SpendableCoins(ctx.BlockTime()).Sub(fees).AmountOf(asset.Denom)
// The maximum amount for outgoing swaps is limited by the asset's current supply
if recipient.Equals(deputyAcc) {
assetSupply, foundAssetSupply := k.GetAssetSupply(ctx, []byte(denom))
if recipient.Address.Equals(asset.DeputyAddress) {
assetSupply, foundAssetSupply := k.GetAssetSupply(ctx, asset.Denom)
if !foundAssetSupply {
return noOpMsg, nil, fmt.Errorf("no asset supply found")
return noOpMsg, nil, fmt.Errorf("no asset supply found for %s", asset.Denom)
}
if maximumAmount.GT(assetSupply.CurrentSupply.Amount) {
maximumAmount = assetSupply.CurrentSupply.Amount
if maximumAmount.GT(assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)) {
maximumAmount = assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)
}
}
// The maximum amount for all swaps is limited by the total max limit
if maximumAmount.GT(k.GetMaxAmount(ctx)) {
maximumAmount = k.GetMaxAmount(ctx)
if maximumAmount.GT(asset.MaxSwapAmount) {
maximumAmount = asset.MaxSwapAmount
}
// Get an amount of coins between 0.1 and 2% of total coins
amount := maximumAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000))))
minAmountPlusFee := asset.MinSwapAmount.Add(asset.FixedFee)
if amount.LT(minAmountPlusFee) {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", denom), "", false, nil), nil, nil
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", asset.Denom), "", false, nil), nil, nil
}
coins := sdk.NewCoins(sdk.NewCoin(denom, amount))
coins := sdk.NewCoins(sdk.NewCoin(asset.Denom, amount))
// We're assuming that sims are run with -NumBlocks=100
heightSpan := uint64(55)
heightSpan := uint64(simulation.RandIntBetween(r, int(asset.MinBlockLock), int(asset.MaxBlockLock)))
msg := types.NewMsgCreateAtomicSwap(
sender.Address, recipient.Address, recipientOtherChain, senderOtherChain,
@ -162,8 +178,10 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
var futureOp simulation.FutureOperation
swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain)
if r.Intn(100) < 50 {
// Claim future operation
executionBlock := uint64(ctx.BlockHeight()) + msg.HeightSpan/2
// Claim future operation - choose between next block and the block before height span
executionBlock := uint64(
int(ctx.BlockHeight()+1) + r.Intn(int(heightSpan-1)))
futureOp = simulation.FutureOperation{
BlockHeight: int(executionBlock),
Op: operationClaimAtomicSwap(ak, k, swapID, randomNumber),
@ -188,13 +206,51 @@ func operationClaimAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID []
simAccount, _ := simulation.RandomAcc(r, accs)
acc := ak.GetAccount(ctx, simAccount.Address)
msg := types.NewMsgClaimAtomicSwap(acc.GetAddress(), swapID, randomNumber)
swap, found := k.GetAtomicSwap(ctx, swapID)
if !found {
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("cannot claim: swap with ID %s not found", swapID)
}
// check that asset supply supports claiming (it could have changed due to a param change proposal)
// use CacheContext so changes don't take effect
cacheCtx, _ := ctx.CacheContext()
switch swap.Direction {
case types.Incoming:
err := k.DecrementIncomingAssetSupply(cacheCtx, swap.Amount[0])
if err != nil {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to decrement incoming asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
err = k.IncrementCurrentAssetSupply(cacheCtx, swap.Amount[0])
if err != nil {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to increment current asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
case types.Outgoing:
err := k.DecrementOutgoingAssetSupply(cacheCtx, swap.Amount[0])
if err != nil {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to decrement outgoing asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
err = k.DecrementCurrentAssetSupply(cacheCtx, swap.Amount[0])
if err != nil {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to decrement current asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
}
asset, err := k.GetAsset(ctx, swap.Amount[0].Denom)
if err != nil {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - asset not found %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
supply, found := k.GetAssetSupply(ctx, asset.Denom)
if !found {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - asset supply not found %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
if asset.SupplyLimit.LT(supply.CurrentSupply.Amount.Add(swap.Amount[0].Amount)) {
return simulation.NoOpMsg(types.ModuleName), nil, nil
}
msg := types.NewMsgClaimAtomicSwap(acc.GetAddress(), swapID, randomNumber)
fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime()))
if err != nil {
return simulation.NoOpMsg(types.ModuleName), nil, err
}
tx := helpers.GenTx(
[]sdk.Msg{msg},
fees,
@ -204,12 +260,10 @@ func operationClaimAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID []
[]uint64{acc.GetSequence()},
simAccount.PrivKey,
)
_, result, err := app.Deliver(tx)
if err != nil {
return simulation.NoOpMsg(types.ModuleName), nil, err
}
return simulation.NewOperationMsg(msg, true, result.Log), nil, nil
}
}
@ -221,6 +275,22 @@ func operationRefundAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID [
simAccount, _ := simulation.RandomAcc(r, accs)
acc := ak.GetAccount(ctx, simAccount.Address)
swap, found := k.GetAtomicSwap(ctx, swapID)
if !found {
return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("cannot refund: swap with ID %s not found", swapID)
}
cacheCtx, _ := ctx.CacheContext()
switch swap.Direction {
case types.Incoming:
if err := k.DecrementIncomingAssetSupply(cacheCtx, swap.Amount[0]); err != nil {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not refund - unable to decrement incoming asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
case types.Outgoing:
if err := k.DecrementOutgoingAssetSupply(cacheCtx, swap.Amount[0]); err != nil {
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not refund - unable to decrement outgoing asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
}
}
msg := types.NewMsgRefundAtomicSwap(acc.GetAddress(), swapID)
fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime()))
@ -242,20 +312,19 @@ func operationRefundAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID [
if err != nil {
return simulation.NoOpMsg(types.ModuleName), nil, err
}
return simulation.NewOperationMsg(msg, true, result.Log), nil, nil
}
}
// findValidAccountAssetSupplyPair finds an account for which the callback func returns true
func findValidAccountAssetSupplyPair(accounts []simulation.Account, supplies types.AssetSupplies,
cb func(simulation.Account, types.AssetSupply) bool) (simulation.Account, types.AssetSupply, bool) {
for _, supply := range supplies {
func findValidAccountAssetPair(accounts []simulation.Account, assets types.AssetParams,
cb func(simulation.Account, types.AssetParam) bool) (simulation.Account, types.AssetParam, bool) {
for _, asset := range assets {
for _, acc := range accounts {
if isValid := cb(acc, supply); isValid {
return acc, supply, true
if isValid := cb(acc, asset); isValid {
return acc, asset, true
}
}
}
return simulation.Account{}, types.AssetSupply{}, false
return simulation.Account{}, types.AssetParam{}, false
}

View File

@ -10,55 +10,15 @@ import (
)
const (
keyBnbDeputyAddress = "BnbDeputyAddress"
keyBnbDeputyFixedFee = "BnbDeputyFixedFee"
keyMinAmount = "MinAmount"
keyMaxAmount = "MaxAmount"
keyMinBlockLock = "MinBlockLock"
keyMaxBlockLock = "MaxBlockLock"
keySupportedAssets = "SupportedAssets"
keyAssetParams = "AssetParams"
)
// ParamChanges defines the parameters that can be modified by param change proposals
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
// We generate MinBlockLock first because the result is required by GenMaxBlockLock()
minBlockLockVal := GenMinBlockLock(r)
minAmount := GenMinAmount(r)
return []simulation.ParamChange{
simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyAddress,
simulation.NewSimParamChange(types.ModuleName, keyAssetParams,
func(r *rand.Rand) string {
return fmt.Sprintf("\"%s\"", GenRandBnbDeputy(r).Address)
},
),
simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyFixedFee,
func(r *rand.Rand) string {
return fmt.Sprintf("\"%s\"", GenRandBnbDeputyFixedFee(r).String())
},
),
simulation.NewSimParamChange(types.ModuleName, keyMinAmount,
func(r *rand.Rand) string {
return fmt.Sprintf("\"%s\"", minAmount.String())
},
),
simulation.NewSimParamChange(types.ModuleName, keyMaxAmount,
func(r *rand.Rand) string {
return fmt.Sprintf("\"%s\"", GenMaxAmount(r, minAmount).String())
},
),
simulation.NewSimParamChange(types.ModuleName, keyMinBlockLock,
func(r *rand.Rand) string {
return fmt.Sprintf("\"%d\"", minBlockLockVal)
},
),
simulation.NewSimParamChange(types.ModuleName, keyMaxBlockLock,
func(r *rand.Rand) string {
return fmt.Sprintf("\"%d\"", GenMaxBlockLock(r, minBlockLockVal))
},
),
simulation.NewSimParamChange(types.ModuleName, keySupportedAssets,
func(r *rand.Rand) string {
return fmt.Sprintf("\"%v\"", GenSupportedAssets(r))
return fmt.Sprintf("\"%s\"", GenSupportedAssets(r))
},
),
}

View File

@ -14,7 +14,7 @@ var (
// ErrInsufficientAmount error for when a swap's amount cannot cover the deputy's fixed fee
ErrInsufficientAmount = sdkerrors.Register(ModuleName, 4, "amount cannot cover the deputy fixed fee")
// ErrAssetNotSupported error for when an asset is not supported
ErrAssetNotSupported = sdkerrors.Register(ModuleName, 5, "asset not on the list of supported assets")
ErrAssetNotSupported = sdkerrors.Register(ModuleName, 5, "asset not found")
// ErrAssetNotActive error for when an asset is currently inactive
ErrAssetNotActive = sdkerrors.Register(ModuleName, 6, "asset is currently inactive")
// ErrAssetSupplyNotFound error for when an asset's supply is not found in the store
@ -41,6 +41,6 @@ var (
ErrSwapNotClaimable = sdkerrors.Register(ModuleName, 17, "atomic swap is not claimable")
// ErrInvalidAmount error for when a swap's amount is outside acceptable range
ErrInvalidAmount = sdkerrors.Register(ModuleName, 18, "amount is outside acceptable range")
// ErrInvalidOutgoingAccount error for when an outgoing swap has a recipient value that is not the deputy address
ErrInvalidOutgoingAccount = sdkerrors.Register(ModuleName, 19, "invalid recipient address for outgoing swap")
// ErrInvalidSwapAccount error for when a swap involves an invalid account
ErrInvalidSwapAccount = sdkerrors.Register(ModuleName, 19, "atomic swap has invalid account")
)

View File

@ -8,17 +8,17 @@ import (
// GenesisState - all bep3 state that must be provided at genesis
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
AtomicSwaps AtomicSwaps `json:"atomic_swaps" yaml:"atomic_swaps"`
AssetSupplies AssetSupplies `json:"assets_supplies" yaml:"assets_supplies"`
Params Params `json:"params" yaml:"params"`
AtomicSwaps AtomicSwaps `json:"atomic_swaps" yaml:"atomic_swaps"`
Supplies AssetSupplies `json:"supplies" yaml:"supplies"`
}
// NewGenesisState creates a new GenesisState object
func NewGenesisState(params Params, swaps AtomicSwaps, supplies AssetSupplies) GenesisState {
return GenesisState{
Params: params,
AtomicSwaps: swaps,
AssetSupplies: supplies,
Params: params,
AtomicSwaps: swaps,
Supplies: supplies,
}
}
@ -49,19 +49,6 @@ func (gs GenesisState) Validate() error {
return err
}
denoms := map[string]bool{}
for _, asset := range gs.AssetSupplies {
if denoms[asset.Denom] {
return fmt.Errorf("found duplicate asset denom %s", asset.Denom)
}
if err := asset.Validate(); err != nil {
return err
}
denoms[asset.Denom] = true
}
ids := map[string]bool{}
for _, swap := range gs.AtomicSwaps {
if ids[hex.EncodeToString(swap.GetSwapID())] {
@ -74,5 +61,16 @@ func (gs GenesisState) Validate() error {
ids[hex.EncodeToString(swap.GetSwapID())] = true
}
supplyDenoms := map[string]bool{}
for _, supply := range gs.Supplies {
if err := supply.Validate(); err != nil {
return err
}
if supplyDenoms[supply.GetDenom()] {
return fmt.Errorf("found duplicate denom in asset supplies %s", supply.GetDenom())
}
supplyDenoms[supply.GetDenom()] = true
}
return nil
}

View File

@ -24,14 +24,13 @@ func (suite *GenesisTestSuite) SetupTest() {
coin := sdk.NewCoin("kava", sdk.OneInt())
suite.swaps = atomicSwaps(10)
supply := types.NewAssetSupply("kava", coin, coin, coin, coin)
supply := types.NewAssetSupply(coin, coin, coin)
suite.supplies = types.AssetSupplies{supply}
}
func (suite *GenesisTestSuite) TestValidate() {
type args struct {
swaps types.AtomicSwaps
supplies types.AssetSupplies
swaps types.AtomicSwaps
}
testCases := []struct {
name string
@ -41,74 +40,50 @@ func (suite *GenesisTestSuite) TestValidate() {
{
"default",
args{
swaps: types.AtomicSwaps{},
supplies: types.AssetSupplies{},
swaps: types.AtomicSwaps{},
},
true,
},
{
"with swaps",
args{
swaps: suite.swaps,
supplies: types.AssetSupplies{},
},
true,
},
{
"with supplies",
args{
swaps: types.AtomicSwaps{},
supplies: suite.supplies,
swaps: suite.swaps,
},
true,
},
{
"duplicate swaps",
args{
swaps: types.AtomicSwaps{suite.swaps[2], suite.swaps[2]},
supplies: types.AssetSupplies{},
swaps: types.AtomicSwaps{suite.swaps[2], suite.swaps[2]},
},
false,
},
{
"invalid swap",
args{
swaps: types.AtomicSwaps{types.AtomicSwap{Amount: sdk.Coins{sdk.Coin{Denom: "Invalid Denom", Amount: sdk.NewInt(-1)}}}},
supplies: types.AssetSupplies{},
swaps: types.AtomicSwaps{types.AtomicSwap{Amount: sdk.Coins{sdk.Coin{Denom: "Invalid Denom", Amount: sdk.NewInt(-1)}}}},
},
false,
},
{
"invalid supply",
args{
swaps: types.AtomicSwaps{},
supplies: types.AssetSupplies{types.AssetSupply{Denom: "Invalid Denom"}},
},
false,
},
{
"duplicate supplies",
args{
swaps: types.AtomicSwaps{},
supplies: types.AssetSupplies{suite.supplies[0], suite.supplies[0]},
},
false,
}}
}
for _, tc := range testCases {
var gs types.GenesisState
if tc.name == "default" {
gs = types.DefaultGenesisState()
} else {
gs = types.NewGenesisState(types.DefaultParams(), tc.args.swaps, tc.args.supplies)
}
suite.Run(tc.name, func() {
var gs types.GenesisState
if tc.name == "default" {
gs = types.DefaultGenesisState()
} else {
gs = types.NewGenesisState(types.DefaultParams(), tc.args.swaps, suite.supplies)
}
err := gs.Validate()
if tc.expectPass {
suite.Require().NoError(err, tc.name)
} else {
suite.Require().Error(err, tc.name)
}
err := gs.Validate()
if tc.expectPass {
suite.Require().NoError(err, tc.name)
} else {
suite.Require().Error(err, tc.name)
}
})
}
}

View File

@ -1,8 +1,6 @@
package types
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -28,13 +26,10 @@ const (
// Key prefixes
var (
// SupplyLimitUpgradeTime is the block time after which the asset supply limits are updated from params
SupplyLimitUpgradeTime time.Time = time.Date(2020, 7, 10, 14, 0, 0, 0, time.UTC)
AtomicSwapKeyPrefix = []byte{0x00} // prefix for keys that store AtomicSwaps
AtomicSwapByBlockPrefix = []byte{0x01} // prefix for keys of the AtomicSwapsByBlock index
AssetSupplyKeyPrefix = []byte{0x02} // prefix for keys that store global asset supply counts
AtomicSwapLongtermStoragePrefix = []byte{0x03} // prefix for keys of the AtomicSwapLongtermStorage index
AtomicSwapLongtermStoragePrefix = []byte{0x02} // prefix for keys of the AtomicSwapLongtermStorage index
AssetSupplyPrefix = []byte{0x03}
)
// GetAtomicSwapByHeightKey is used by the AtomicSwapByBlock index and AtomicSwapLongtermStorage index

View File

@ -1,100 +1,80 @@
package types
import (
"errors"
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params"
)
const (
bech32MainPrefix = "kava"
)
// Parameter keys
var (
KeyBnbDeputyAddress = []byte("BnbDeputyAddress")
KeyBnbDeputyFixedFee = []byte("BnbDeputyFixedFee")
KeyMinAmount = []byte("MinAmount")
KeyMaxAmount = []byte("MaxAmount")
KeyMinBlockLock = []byte("MinBlockLock")
KeyMaxBlockLock = []byte("MaxBlockLock")
KeySupportedAssets = []byte("SupportedAssets")
KeyAssetParams = []byte("AssetParams")
DefaultBnbDeputyFixedFee sdk.Int = sdk.NewInt(1000) // 0.00001 BNB
DefaultMinAmount sdk.Int = sdk.ZeroInt()
DefaultMaxAmount sdk.Int = sdk.NewInt(1000000000000) // 10,000 BNB
DefaultMinBlockLock uint64 = 220
DefaultMaxBlockLock uint64 = 270
DefaultSupportedAssets = AssetParams{
AssetParam{
Denom: "bnb",
CoinID: 714,
Limit: sdk.NewInt(350000000000000), // 3,500,000 BNB
Active: true,
},
}
)
// Params governance parameters for bep3 module
type Params struct {
BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // Bnbchain deputy address
BnbDeputyFixedFee sdk.Int `json:"bnb_deputy_fixed_fee" yaml:"bnb_deputy_fixed_fee"` // Deputy fixed fee in BNB
MinAmount sdk.Int `json:"min_amount" yaml:"min_amount"` // Minimum swap amount
MaxAmount sdk.Int `json:"max_amount" yaml:"max_amount"` // Maximum swap amount
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // Minimum swap block lock
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // Maximum swap block lock
SupportedAssets AssetParams `json:"supported_assets" yaml:"supported_assets"` // Supported assets
AssetParams AssetParams `json:"asset_params" yaml:"asset_params"`
}
// String implements fmt.Stringer
func (p Params) String() string {
return fmt.Sprintf(`Params:
Bnbchain deputy address: %s,
Deputy fixed fee (BNB): %d,
Min amount: %d,
Max amount: %d,
Min block lock: %d,
Max block lock: %d,
Supported assets: %s`,
p.BnbDeputyAddress.String(), p.BnbDeputyFixedFee, p.MinAmount,
p.MaxAmount, p.MinBlockLock, p.MaxBlockLock, p.SupportedAssets)
AssetParams: %s`,
p.AssetParams)
}
// NewParams returns a new params object
func NewParams(bnbDeputyAddress sdk.AccAddress, bnbDeputyFixedFee, minAmount,
maxAmount sdk.Int, minBlockLock, maxBlockLock uint64, supportedAssets AssetParams,
func NewParams(ap AssetParams,
) Params {
return Params{
BnbDeputyAddress: bnbDeputyAddress,
BnbDeputyFixedFee: bnbDeputyFixedFee,
MinAmount: minAmount,
MaxAmount: maxAmount,
MinBlockLock: minBlockLock,
MaxBlockLock: maxBlockLock,
SupportedAssets: supportedAssets,
AssetParams: ap,
}
}
// DefaultParams returns default params for bep3 module
func DefaultParams() Params {
defaultBnbDeputyAddress, err := sdk.AccAddressFromBech32("kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6")
if err != nil {
panic(err)
}
return NewParams(defaultBnbDeputyAddress, DefaultBnbDeputyFixedFee, DefaultMinAmount,
DefaultMaxAmount, DefaultMinBlockLock, DefaultMaxBlockLock, DefaultSupportedAssets)
return NewParams(AssetParams{})
}
// AssetParam governance parameters for each asset within a supported chain
// AssetParam parameters that must be specified for each bep3 asset
type AssetParam struct {
Denom string `json:"denom" yaml:"denom"` // name of the asset
CoinID int `json:"coin_id" yaml:"coin_id"` // internationally recognized coin ID
Limit sdk.Int `json:"limit" yaml:"limit"` // asset supply limit
Active bool `json:"active" yaml:"active"` // denotes if asset is available or paused
Denom string `json:"denom" yaml:"denom"` // name of the asset
CoinID int `json:"coin_id" yaml:"coin_id"` // SLIP-0044 registered coin type - see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
SupplyLimit sdk.Int `json:"supply_limit" yaml:"supply_limit"` // asset supply limit
Active bool `json:"active" yaml:"active"` // denotes if asset is available or paused
DeputyAddress sdk.AccAddress `json:"deputy_address" yaml:"deputy_address"` // the address of the relayer process
FixedFee sdk.Int `json:"fixed_fee" yaml:"fixed_fee"` // the fixed fee charged by the relayer process for outgoing swaps
MinSwapAmount sdk.Int `json:"min_swap_amount" yaml:"min_swap_amount"` // Minimum swap amount
MaxSwapAmount sdk.Int `json:"max_swap_amount" yaml:"max_swap_amount"` // Maximum swap amount
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // Minimum swap block lock
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // Maximum swap block lock
}
// NewAssetParam returns a new AssetParam
func NewAssetParam(
denom string, coinID int, limit sdk.Int, active bool,
deputyAddr sdk.AccAddress, fixedFee sdk.Int, minSwapAmount sdk.Int,
maxSwapAmount sdk.Int, minBlockLock uint64, maxBlockLock uint64,
) AssetParam {
return AssetParam{
Denom: denom,
CoinID: coinID,
SupplyLimit: limit,
Active: active,
DeputyAddress: deputyAddr,
FixedFee: fixedFee,
MinSwapAmount: minSwapAmount,
MaxSwapAmount: maxSwapAmount,
MinBlockLock: minBlockLock,
MaxBlockLock: maxBlockLock,
}
}
// String implements fmt.Stringer
@ -103,8 +83,15 @@ func (ap AssetParam) String() string {
Denom: %s
Coin ID: %d
Limit: %s
Active: %t`,
ap.Denom, ap.CoinID, ap.Limit.String(), ap.Active)
Active: %t
Deputy Address: %s
Fixed Fee: %s
Min Swap Amount: %s
Max Swap Amount: %s
Min Block Lock: %d
Max Block Lock: %d`,
ap.Denom, ap.CoinID, ap.SupplyLimit, ap.Active, ap.DeputyAddress, ap.FixedFee,
ap.MinSwapAmount, ap.MaxSwapAmount, ap.MinBlockLock, ap.MaxBlockLock)
}
// AssetParams array of AssetParam
@ -129,149 +116,69 @@ func ParamKeyTable() params.KeyTable {
// nolint
func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{
params.NewParamSetPair(KeyBnbDeputyAddress, &p.BnbDeputyAddress, validateBnbDeputyAddressParam),
params.NewParamSetPair(KeyBnbDeputyFixedFee, &p.BnbDeputyFixedFee, validateBnbDeputyFixedFeeParam),
params.NewParamSetPair(KeyMinAmount, &p.MinAmount, validateMinAmountParam),
params.NewParamSetPair(KeyMaxAmount, &p.MaxAmount, validateMaxAmountParam),
params.NewParamSetPair(KeyMinBlockLock, &p.MinBlockLock, validateMinBlockLockParam),
params.NewParamSetPair(KeyMaxBlockLock, &p.MaxBlockLock, validateMaxBlockLockParam),
params.NewParamSetPair(KeySupportedAssets, &p.SupportedAssets, validateSupportedAssetsParams),
params.NewParamSetPair(KeyAssetParams, &p.AssetParams, validateAssetParams),
}
}
// Validate ensure that params have valid values
func (p Params) Validate() error {
if err := validateBnbDeputyAddressParam(p.BnbDeputyAddress); err != nil {
return err
}
if err := validateBnbDeputyFixedFeeParam(p.BnbDeputyFixedFee); err != nil {
return err
}
if err := validateMinAmountParam(p.MinAmount); err != nil {
return err
}
if err := validateMaxAmountParam(p.MaxAmount); err != nil {
return err
}
if p.MinAmount.GT(p.MaxAmount) {
return fmt.Errorf("minimum amount cannot be > maximum amount, got %d > %d", p.MinAmount, p.MaxAmount)
}
if err := validateMinBlockLockParam(p.MinBlockLock); err != nil {
return err
}
if err := validateMaxBlockLockParam(p.MaxBlockLock); err != nil {
return err
}
if p.MinBlockLock > p.MaxBlockLock {
return fmt.Errorf("minimum block lock cannot be > maximum block lock, got %d > %d", p.MinBlockLock, p.MaxBlockLock)
}
return validateSupportedAssetsParams(p.SupportedAssets)
return validateAssetParams(p.AssetParams)
}
func validateBnbDeputyAddressParam(i interface{}) error {
addr, ok := i.(sdk.AccAddress)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
if addr.Empty() {
return errors.New("bnb deputy address cannot be empty")
}
if len(addr.Bytes()) != sdk.AddrLen {
return fmt.Errorf("bnb deputy address invalid bytes length got %d, want %d", len(addr.Bytes()), sdk.AddrLen)
}
return nil
}
func validateBnbDeputyFixedFeeParam(i interface{}) error {
_, ok := i.(sdk.Int)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return nil
}
func validateMinAmountParam(i interface{}) error {
_, ok := i.(sdk.Int)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return nil
}
func validateMaxAmountParam(i interface{}) error {
_, ok := i.(sdk.Int)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return nil
}
func validateMinBlockLockParam(i interface{}) error {
_, ok := i.(uint64)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return nil
}
func validateMaxBlockLockParam(i interface{}) error {
_, ok := i.(uint64)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
return nil
}
func validateSupportedAssetsParams(i interface{}) error {
func validateAssetParams(i interface{}) error {
assetParams, ok := i.(AssetParams)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}
coinIDs := make(map[int]bool)
coinDenoms := make(map[string]bool)
for _, asset := range assetParams {
if strings.TrimSpace(asset.Denom) == "" {
return errors.New("asset denom cannot be empty")
if err := sdk.ValidateDenom(asset.Denom); err != nil {
return fmt.Errorf("asset denom invalid: %s", asset.Denom)
}
if asset.CoinID < 0 {
return fmt.Errorf(fmt.Sprintf("asset %s must be a non negative integer", asset.Denom))
return fmt.Errorf("asset %s coin id must be a non negative integer", asset.Denom)
}
if !asset.Limit.IsPositive() {
return fmt.Errorf(fmt.Sprintf("asset %s must have a positive supply limit", asset.Denom))
if asset.SupplyLimit.IsNegative() {
return fmt.Errorf("asset %s has invalid (negative) supply limit: %s", asset.Denom, asset.SupplyLimit)
}
_, found := coinDenoms[asset.Denom]
if found {
return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate denom", asset.Denom))
return fmt.Errorf("asset %s cannot have duplicate denom", asset.Denom)
}
coinDenoms[asset.Denom] = true
_, found = coinIDs[asset.CoinID]
if found {
return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate coin id %d", asset.Denom, asset.CoinID))
if asset.DeputyAddress.Empty() {
return fmt.Errorf("deputy address cannot be empty for %s", asset.Denom)
}
coinIDs[asset.CoinID] = true
if len(asset.DeputyAddress.Bytes()) != sdk.AddrLen {
return fmt.Errorf("%s deputy address invalid bytes length got %d, want %d", asset.Denom, len(asset.DeputyAddress.Bytes()), sdk.AddrLen)
}
if asset.FixedFee.IsNegative() {
return fmt.Errorf("asset %s cannot have a negative fixed fee %s", asset.Denom, asset.FixedFee)
}
if asset.MinBlockLock > asset.MaxBlockLock {
return fmt.Errorf("asset %s has minimum block lock > maximum block lock %d > %d", asset.Denom, asset.MinBlockLock, asset.MaxBlockLock)
}
if !asset.MinSwapAmount.IsPositive() {
return fmt.Errorf("asset %s must have a positive minimum swap amount, got %s", asset.Denom, asset.MinSwapAmount)
}
if !asset.MaxSwapAmount.IsPositive() {
return fmt.Errorf("asset %s must have a positive maximum swap amount, got %s", asset.Denom, asset.MaxSwapAmount)
}
if asset.MinSwapAmount.GT(asset.MaxSwapAmount) {
return fmt.Errorf("asset %s has minimum swap amount > maximum swap amount %s > %s", asset.Denom, asset.MinSwapAmount, asset.MaxSwapAmount)
}
}
return nil

View File

@ -13,7 +13,8 @@ import (
type ParamsTestSuite struct {
suite.Suite
addr sdk.AccAddress
addr sdk.AccAddress
supply []sdk.Int
}
func (suite *ParamsTestSuite) SetupTest() {
@ -21,20 +22,14 @@ func (suite *ParamsTestSuite) SetupTest() {
app.SetBech32AddressPrefixes(config)
_, addrs := app.GeneratePrivKeyAddressPairs(1)
suite.addr = addrs[0]
suite.supply = append(suite.supply, sdk.NewInt(10000000000000), sdk.NewInt(10000000000000))
return
}
func (suite *ParamsTestSuite) TestParamValidation() {
type LoadParams func() types.Params
type args struct {
bnbDeputyAddress sdk.AccAddress
bnbDeputyFixedFee sdk.Int
minAmount sdk.Int
maxAmount sdk.Int
minBlockLock uint64
maxBlockLock uint64
supportedAssets types.AssetParams
assetParams types.AssetParams
}
testCases := []struct {
@ -46,189 +41,168 @@ func (suite *ParamsTestSuite) TestParamValidation() {
{
name: "default",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: types.DefaultMinBlockLock,
maxBlockLock: types.DefaultMaxBlockLock,
supportedAssets: types.DefaultSupportedAssets,
assetParams: types.AssetParams{},
},
expectPass: true,
expectedErr: "",
},
{
name: "minimum block lock == maximum block lock",
name: "valid single asset",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: 243,
maxBlockLock: 243,
supportedAssets: types.DefaultSupportedAssets,
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: true,
expectedErr: "",
},
{
name: "minimum amount greater than maximum amount",
name: "valid multi asset",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: sdk.NewInt(10000000),
maxAmount: sdk.NewInt(100000),
minBlockLock: types.DefaultMinBlockLock,
maxBlockLock: types.DefaultMaxBlockLock,
supportedAssets: types.DefaultSupportedAssets,
},
expectPass: false,
expectedErr: "minimum amount cannot be > maximum amount",
},
{
name: "minimum block lock greater than maximum block lock",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: 500,
maxBlockLock: 400,
supportedAssets: types.DefaultSupportedAssets,
},
expectPass: false,
expectedErr: "minimum block lock cannot be > maximum block lock",
},
{
name: "empty asset denom",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: types.DefaultMinBlockLock,
maxBlockLock: types.DefaultMaxBlockLock,
supportedAssets: types.AssetParams{
types.AssetParam{
Denom: "",
CoinID: 714,
Limit: sdk.NewInt(100000000000),
Active: true,
},
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock),
types.NewAssetParam(
"btcb", 0, suite.supply[1], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(10000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock),
},
},
expectPass: false,
expectedErr: "asset denom cannot be empty",
expectPass: true,
expectedErr: "",
},
{
name: "negative asset coin ID",
name: "invalid denom - empty",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: types.DefaultMinBlockLock,
maxBlockLock: types.DefaultMaxBlockLock,
supportedAssets: types.AssetParams{
types.AssetParam{
Denom: "bnb",
CoinID: -1,
Limit: sdk.NewInt(100000000000),
Active: true,
},
},
assetParams: types.AssetParams{types.NewAssetParam(
"", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: false,
expectedErr: "must be a non negative integer",
expectedErr: "denom invalid",
},
{
name: "invalid denom - bad format",
args: args{
assetParams: types.AssetParams{types.NewAssetParam(
"BNB", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: false,
expectedErr: "denom invalid",
},
{
name: "min block lock equal max block lock",
args: args{
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
243, 243)},
},
expectPass: true,
expectedErr: "",
},
{
name: "min block lock greater max block lock",
args: args{
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
244, 243)},
},
expectPass: false,
expectedErr: "minimum block lock > maximum block lock",
},
{
name: "min swap not positive",
args: args{
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(0), sdk.NewInt(10000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: false,
expectedErr: "must have a positive minimum swap",
},
{
name: "max swap not positive",
args: args{
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(10000), sdk.NewInt(0),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: false,
expectedErr: "must have a positive maximum swap",
},
{
name: "min swap greater max swap",
args: args{
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000000), sdk.NewInt(10000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: false,
expectedErr: "minimum swap amount > maximum swap amount",
},
{
name: "negative coin id",
args: args{
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", -714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: false,
expectedErr: "coin id must be a non negative",
},
{
name: "negative asset limit",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: types.DefaultMinBlockLock,
maxBlockLock: types.DefaultMaxBlockLock,
supportedAssets: types.AssetParams{
types.AssetParam{
Denom: "bnb",
CoinID: 714,
Limit: sdk.NewInt(-10000),
Active: true,
},
},
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714,
sdk.NewInt(-10000000000000), true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
},
expectPass: false,
expectedErr: "must have a positive supply limit",
expectedErr: "invalid (negative) supply limit",
},
{
name: "duplicate asset denom",
name: "duplicate denom",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: types.DefaultMinBlockLock,
maxBlockLock: types.DefaultMaxBlockLock,
supportedAssets: types.AssetParams{
types.AssetParam{
Denom: "bnb",
CoinID: 714,
Limit: sdk.NewInt(100000000000),
Active: true,
},
types.AssetParam{
Denom: "bnb",
CoinID: 114,
Limit: sdk.NewInt(500000000),
Active: false,
},
assetParams: types.AssetParams{types.NewAssetParam(
"bnb", 714, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock),
types.NewAssetParam(
"bnb", 0, suite.supply[0], true,
suite.addr, sdk.NewInt(1000), sdk.NewInt(10000000), sdk.NewInt(100000000000),
types.DefaultMinBlockLock, types.DefaultMaxBlockLock),
},
},
expectPass: false,
expectedErr: "cannot have duplicate denom",
},
{
name: "duplicate asset coin ID",
args: args{
bnbDeputyAddress: suite.addr,
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
minAmount: types.DefaultMinAmount,
maxAmount: types.DefaultMaxAmount,
minBlockLock: types.DefaultMinBlockLock,
maxBlockLock: types.DefaultMaxBlockLock,
supportedAssets: types.AssetParams{
types.AssetParam{
Denom: "bnb",
CoinID: 714,
Limit: sdk.NewInt(100000000000),
Active: true,
},
types.AssetParam{
Denom: "fake",
CoinID: 714,
Limit: sdk.NewInt(500000000),
Active: false,
},
},
},
expectPass: false,
expectedErr: "cannot have duplicate coin id",
expectedErr: "duplicate denom",
},
}
for _, tc := range testCases {
params := types.NewParams(tc.args.bnbDeputyAddress, tc.args.bnbDeputyFixedFee, tc.args.minAmount,
tc.args.maxAmount, tc.args.minBlockLock, tc.args.maxBlockLock, tc.args.supportedAssets)
suite.Run(tc.name, func() {
params := types.NewParams(tc.args.assetParams)
err := params.Validate()
if tc.expectPass {
suite.Require().NoError(err, tc.name)
} else {
suite.Require().Error(err, tc.name)
suite.Require().Contains(err.Error(), tc.expectedErr)
}
err := params.Validate()
if tc.expectPass {
suite.Require().NoError(err, tc.name)
} else {
suite.Require().Error(err, tc.name)
suite.Require().Contains(err.Error(), tc.expectedErr)
}
})
}
}

View File

@ -20,11 +20,11 @@ const (
// QueryAssetSupply contains the params for query 'custom/bep3/supply'
type QueryAssetSupply struct {
Denom tmbytes.HexBytes `json:"denom" yaml:"denom"`
Denom string `json:"denom" yaml:"denom"`
}
// NewQueryAssetSupply creates a new QueryAssetSupply
func NewQueryAssetSupply(denom tmbytes.HexBytes) QueryAssetSupply {
func NewQueryAssetSupply(denom string) QueryAssetSupply {
return QueryAssetSupply{
Denom: denom,
}

View File

@ -9,21 +9,17 @@ import (
// AssetSupply contains information about an asset's supply
type AssetSupply struct {
Denom string `json:"denom" yaml:"denom"`
IncomingSupply sdk.Coin `json:"incoming_supply" yaml:"incoming_supply"`
OutgoingSupply sdk.Coin `json:"outgoing_supply" yaml:"outgoing_supply"`
CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"`
SupplyLimit sdk.Coin `json:"supply_limit" yaml:"supply_limit"`
}
// NewAssetSupply initializes a new AssetSupply
func NewAssetSupply(denom string, incomingSupply, outgoingSupply, currentSupply, supplyLimit sdk.Coin) AssetSupply {
func NewAssetSupply(incomingSupply, outgoingSupply, currentSupply sdk.Coin) AssetSupply {
return AssetSupply{
Denom: denom,
IncomingSupply: incomingSupply,
OutgoingSupply: outgoingSupply,
CurrentSupply: currentSupply,
SupplyLimit: supplyLimit,
}
}
@ -38,22 +34,35 @@ func (a AssetSupply) Validate() error {
if !a.CurrentSupply.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "current supply %s", a.CurrentSupply)
}
if !a.SupplyLimit.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "supply limit %s", a.SupplyLimit)
denom := a.CurrentSupply.Denom
if (a.IncomingSupply.Denom != denom) ||
(a.OutgoingSupply.Denom != denom) {
return fmt.Errorf("asset supply denoms do not match %s %s %s", a.CurrentSupply.Denom, a.IncomingSupply.Denom, a.OutgoingSupply.Denom)
}
return sdk.ValidateDenom(a.Denom)
return nil
}
// Equal returns if two asset supplies are equal
func (a AssetSupply) Equal(b AssetSupply) bool {
return (a.IncomingSupply.IsEqual(b.IncomingSupply) &&
a.CurrentSupply.IsEqual(b.CurrentSupply) &&
a.OutgoingSupply.IsEqual(b.OutgoingSupply))
}
// String implements stringer
func (a AssetSupply) String() string {
return fmt.Sprintf(`
%s supply:
asset supply:
Incoming supply: %s
Outgoing supply: %s
Current supply: %s
Supply limit: %s
`,
a.Denom, a.IncomingSupply, a.OutgoingSupply, a.CurrentSupply, a.SupplyLimit)
a.IncomingSupply, a.OutgoingSupply, a.CurrentSupply)
}
// GetDenom getter method for the denom of the asset supply
func (a AssetSupply) GetDenom() string {
return a.CurrentSupply.Denom
}
// AssetSupplies is a slice of AssetSupply

View File

@ -17,7 +17,7 @@ func TestAssetSupplyValidate(t *testing.T) {
}{
{
msg: "valid asset",
asset: NewAssetSupply("kava", coin, coin, coin, coin),
asset: NewAssetSupply(coin, coin, coin),
expPass: true,
},
{
@ -42,21 +42,6 @@ func TestAssetSupplyValidate(t *testing.T) {
},
false,
},
{
"invalid supply limit",
AssetSupply{
IncomingSupply: coin,
OutgoingSupply: coin,
CurrentSupply: coin,
SupplyLimit: invalidCoin,
},
false,
},
{
msg: "invalid denom",
asset: NewAssetSupply("Invalid Denom", coin, coin, coin, coin),
expPass: false,
},
}
for _, tc := range testCases {

View File

@ -76,19 +76,33 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
testCDPParams.DebtParam = testDP
testCDPParams.GlobalDebtLimit = testCPs[0].DebtLimit.Add(testCPs[0].DebtLimit) // correct global debt limit to pass genesis validation
testDeputy, err := sdk.AccAddressFromBech32("kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj")
suite.Require().NoError(err)
// bep3 Asset Params
testAPs := bep3types.AssetParams{
{
Denom: "bnb",
CoinID: 714,
Limit: i(100000000000),
Active: true,
bep3types.AssetParam{
Denom: "bnb",
CoinID: 714,
SupplyLimit: sdk.NewInt(350000000000000),
Active: true,
DeputyAddress: testDeputy,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3types.DefaultMinBlockLock,
MaxBlockLock: bep3types.DefaultMaxBlockLock,
},
{
Denom: "inc",
CoinID: 9999,
Limit: i(100),
Active: false,
bep3types.AssetParam{
Denom: "inc",
CoinID: 9999,
SupplyLimit: sdk.NewInt(100),
Active: false,
DeputyAddress: testDeputy,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3types.DefaultMinBlockLock,
MaxBlockLock: bep3types.DefaultMaxBlockLock,
},
}
testAPsUpdatedActive := make(bep3types.AssetParams, len(testAPs))
@ -97,7 +111,7 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
// bep3 Genesis
testBep3Params := bep3types.DefaultParams()
testBep3Params.SupportedAssets = testAPs
testBep3Params.AssetParams = testAPs
// pricefeed Markets
testMs := pricefeedtypes.Markets{
@ -139,7 +153,7 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
{Subspace: cdptypes.ModuleName, Key: string(cdptypes.KeyDebtThreshold)},
{Subspace: cdptypes.ModuleName, Key: string(cdptypes.KeyCollateralParams)},
{Subspace: cdptypes.ModuleName, Key: string(cdptypes.KeyDebtParam)},
{Subspace: bep3types.ModuleName, Key: string(bep3types.KeySupportedAssets)},
{Subspace: bep3types.ModuleName, Key: string(bep3types.KeyAssetParams)},
{Subspace: pricefeedtypes.ModuleName, Key: string(pricefeedtypes.KeyMarkets)},
},
AllowedCollateralParams: types.AllowedCollateralParams{
@ -195,7 +209,7 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
},
{
Subspace: bep3types.ModuleName,
Key: string(bep3types.KeySupportedAssets),
Key: string(bep3types.KeyAssetParams),
Value: string(suite.cdc.MustMarshalJSON(testAPsUpdatedActive)),
},
{

View File

@ -5,6 +5,7 @@ import (
bep3types "github.com/kava-labs/kava/x/bep3/types"
cdptypes "github.com/kava-labs/kava/x/cdp/types"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
"github.com/tendermint/tendermint/crypto"
)
// Avoid cluttering test cases with long function names
@ -155,24 +156,43 @@ func (suite *PermissionsTestSuite) TestAllowedCollateralParams_Allows() {
}
func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
deputyAddress := sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1")))
testAPs := bep3types.AssetParams{
{
Denom: "bnb",
CoinID: 714,
Limit: i(1000000000000),
Active: true,
bep3types.AssetParam{
Denom: "btc",
CoinID: 0,
SupplyLimit: sdk.NewInt(100),
Active: false,
DeputyAddress: deputyAddress,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3types.DefaultMinBlockLock,
MaxBlockLock: bep3types.DefaultMaxBlockLock,
},
{
Denom: "btc",
CoinID: 0,
Limit: i(1000000000000),
Active: true,
bep3types.AssetParam{
Denom: "bnb",
CoinID: 714,
SupplyLimit: sdk.NewInt(350000000000000),
Active: true,
DeputyAddress: deputyAddress,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3types.DefaultMinBlockLock,
MaxBlockLock: bep3types.DefaultMaxBlockLock,
},
{
Denom: "xrp",
CoinID: 144,
Limit: i(1000000000000),
Active: true,
bep3types.AssetParam{
Denom: "xrp",
CoinID: 414,
SupplyLimit: sdk.NewInt(350000000000000),
Active: true,
DeputyAddress: deputyAddress,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3types.DefaultMinBlockLock,
MaxBlockLock: bep3types.DefaultMaxBlockLock,
},
}
updatedTestAPs := make(bep3types.AssetParams, len(testAPs))
@ -180,10 +200,10 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
updatedTestAPs[1] = testAPs[0]
updatedTestAPs[2] = testAPs[2]
updatedTestAPs[0].Limit = i(1000) // btc
updatedTestAPs[1].Active = false // bnb
updatedTestAPs[2].Limit = i(1000) // xrp
updatedTestAPs[2].Active = false // xrp
updatedTestAPs[0].SupplyLimit = i(1000) // btc
updatedTestAPs[1].Active = false // bnb
updatedTestAPs[2].SupplyLimit = i(1000) // xrp
updatedTestAPs[2].Active = false // xrp
testcases := []struct {
name string
@ -238,6 +258,7 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
{
Denom: "bnb",
Active: true,
Limit: true,
},
{
Denom: "btc",
@ -575,20 +596,26 @@ func (suite *PermissionsTestSuite) TestAllowedDebtParam_Allows() {
func (suite *PermissionsTestSuite) TestAllowedAssetParam_Allows() {
testAP := bep3types.AssetParam{
Denom: "usdx",
CoinID: 999,
Limit: i(1000000000),
Active: true,
Denom: "usdx",
CoinID: 999,
SupplyLimit: sdk.NewInt(1000000000),
Active: true,
DeputyAddress: sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1"))),
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3types.DefaultMinBlockLock,
MaxBlockLock: bep3types.DefaultMaxBlockLock,
}
newCoinidAP := testAP
newCoinidAP.CoinID = 0
newLimitAP := testAP
newLimitAP.Limit = i(1000)
newLimitAP.SupplyLimit = i(1000)
newCoinidAndLimitAP := testAP
newCoinidAndLimitAP.CoinID = 0
newCoinidAndLimitAP.Limit = i(1000)
newCoinidAndLimitAP.SupplyLimit = i(1000)
testcases := []struct {
name string

View File

@ -260,7 +260,7 @@ func (perm SubParamChangePermission) Allows(ctx sdk.Context, appCdc *codec.Codec
var foundIncomingAPs bool
var incomingAPs bep3types.AssetParams
for _, change := range proposal.Changes {
if !(change.Subspace == bep3types.ModuleName && change.Key == string(bep3types.KeySupportedAssets)) {
if !(change.Subspace == bep3types.ModuleName && change.Key == string(bep3types.KeyAssetParams)) {
continue
}
// note: in case of duplicates take the last value
@ -277,7 +277,7 @@ func (perm SubParamChangePermission) Allows(ctx sdk.Context, appCdc *codec.Codec
return false // not using a panic to help avoid begin blocker panics
}
var currentAPs bep3types.AssetParams
subspace.Get(ctx, bep3types.KeySupportedAssets, &currentAPs) // panics if something goes wrong
subspace.Get(ctx, bep3types.KeyAssetParams, &currentAPs) // panics if something goes wrong
// Check all the incoming changes in the CollateralParams are allowed
assetParamsChangesAllowed := perm.AllowedAssetParams.Allows(currentAPs, incomingAPs)
@ -473,9 +473,10 @@ type AllowedAssetParam struct {
}
func (aap AllowedAssetParam) Allows(current, incoming bep3types.AssetParam) bool {
allowed := ((aap.Denom == current.Denom) && (aap.Denom == incoming.Denom)) && // require denoms to be all equal
((current.CoinID == incoming.CoinID) || aap.CoinID) &&
(current.Limit.Equal(incoming.Limit) || aap.Limit) &&
(current.SupplyLimit.Equal(incoming.SupplyLimit) || aap.Limit) &&
((current.Active == incoming.Active) || aap.Active)
return allowed
}