From 790753f1568684f7acc1a28a180e8e78a6a4557c Mon Sep 17 00:00:00 2001 From: Kevin Davis Date: Mon, 17 Aug 2020 11:06:59 -0400 Subject: [PATCH] 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 --- app/params/params.go | 12 +- app/test_common.go | 28 ++ contrib/kava-3/params.go | 26 +- migrate/v0_11/migrate.go | 68 +++++ migrate/v0_8/migrate.go | 4 +- x/bep3/abci.go | 3 - x/bep3/abci_test.go | 66 +--- x/bep3/alias.go | 198 ++++++------ x/bep3/client/cli/query.go | 2 +- x/bep3/client/rest/query.go | 2 +- x/bep3/genesis.go | 60 ++-- x/bep3/genesis_test.go | 99 +++--- x/bep3/integration_test.go | 67 ++-- x/bep3/keeper/asset.go | 63 ++-- x/bep3/keeper/asset_test.go | 59 +--- x/bep3/keeper/integration_test.go | 57 ++-- x/bep3/keeper/keeper.go | 32 +- x/bep3/keeper/keeper_test.go | 54 +--- x/bep3/keeper/params.go | 132 +++++--- x/bep3/keeper/params_test.go | 127 ++++---- x/bep3/keeper/querier.go | 2 +- x/bep3/keeper/querier_test.go | 19 +- x/bep3/keeper/swap.go | 32 +- x/bep3/keeper/swap_test.go | 68 +---- x/bep3/legacy/v0_11/types.go | 81 +++++ x/bep3/legacy/v0_9/types.go | 151 +++++++++ x/bep3/simulation/decoder.go | 11 +- x/bep3/simulation/decoder_test.go | 4 +- x/bep3/simulation/genesis.go | 126 ++++---- x/bep3/simulation/operations.go | 179 +++++++---- x/bep3/simulation/params.go | 46 +-- x/bep3/types/errors.go | 6 +- x/bep3/types/genesis.go | 36 ++- x/bep3/types/genesis_test.go | 69 ++--- x/bep3/types/keys.go | 9 +- x/bep3/types/params.go | 255 +++++----------- x/bep3/types/params_test.go | 286 ++++++++---------- x/bep3/types/querier.go | 4 +- x/bep3/types/{asset.go => supply.go} | 31 +- .../types/{asset_test.go => supply_test.go} | 17 +- x/committee/keeper/param_permission_test.go | 40 ++- x/committee/types/param_permissions_test.go | 77 +++-- x/committee/types/permissions.go | 7 +- 43 files changed, 1434 insertions(+), 1281 deletions(-) create mode 100644 migrate/v0_11/migrate.go create mode 100644 x/bep3/legacy/v0_11/types.go create mode 100644 x/bep3/legacy/v0_9/types.go rename x/bep3/types/{asset.go => supply.go} (63%) rename x/bep3/types/{asset_test.go => supply_test.go} (72%) diff --git a/app/params/params.go b/app/params/params.go index 7853bc39..c3c16219 100644 --- a/app/params/params.go +++ b/app/params/params.go @@ -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 ) diff --git a/app/test_common.go b/app/test_common.go index 5303d630..b02fd822 100644 --- a/app/test_common.go +++ b/app/test_common.go @@ -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) diff --git a/contrib/kava-3/params.go b/contrib/kava-3/params.go index 845b90e2..3472ebcf 100644 --- a/contrib/kava-3/params.go +++ b/contrib/kava-3/params.go @@ -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, diff --git a/migrate/v0_11/migrate.go b/migrate/v0_11/migrate.go new file mode 100644 index 00000000..76f2e74c --- /dev/null +++ b/migrate/v0_11/migrate.go @@ -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, + } +} diff --git a/migrate/v0_8/migrate.go b/migrate/v0_8/migrate.go index a31a5ab1..43bd239f 100644 --- a/migrate/v0_8/migrate.go +++ b/migrate/v0_8/migrate.go @@ -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()) diff --git a/x/bep3/abci.go b/x/bep3/abci.go index 97b14027..8675da52 100644 --- a/x/bep3/abci.go +++ b/x/bep3/abci.go @@ -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) - } } diff --git a/x/bep3/abci_test.go b/x/bep3/abci_test.go index e4687c35..e6f1021b 100644 --- a/x/bep3/abci_test.go +++ b/x/bep3/abci_test.go @@ -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)) } diff --git a/x/bep3/alias.go b/x/bep3/alias.go index cec60425..139bf057 100644 --- a/x/bep3/alias.go +++ b/x/bep3/alias.go @@ -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 ) diff --git a/x/bep3/client/cli/query.go b/x/bep3/client/cli/query.go index 0cef3abc..a17a0132 100644 --- a/x/bep3/client/cli/query.go +++ b/x/bep3/client/cli/query.go @@ -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 } diff --git a/x/bep3/client/rest/query.go b/x/bep3/client/rest/query.go index e4aa7f77..5c573c68 100644 --- a/x/bep3/client/rest/query.go +++ b/x/bep3/client/rest/query.go @@ -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) diff --git a/x/bep3/genesis.go b/x/bep3/genesis.go index a8737e72..f8fb49e9 100644 --- a/x/bep3/genesis.go +++ b/x/bep3/genesis.go @@ -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) } diff --git a/x/bep3/genesis_test.go b/x/bep3/genesis_test.go index 37443e31..7da01676 100644 --- a/x/bep3/genesis_test.go +++ b/x/bep3/genesis_test.go @@ -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) + } + }) + } } diff --git a/x/bep3/integration_test.go b/x/bep3/integration_test.go index e8794dca..c5b407ad 100644 --- a/x/bep3/integration_test.go +++ b/x/bep3/integration_test.go @@ -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 } diff --git a/x/bep3/keeper/asset.go b/x/bep3/keeper/asset.go index 75b11786..4e4de878 100644 --- a/x/bep3/keeper/asset.go +++ b/x/bep3/keeper/asset.go @@ -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)) - } - } -} diff --git a/x/bep3/keeper/asset_test.go b/x/bep3/keeper/asset_test.go index 4b8bc58a..b5546d9a 100644 --- a/x/bep3/keeper/asset_test.go +++ b/x/bep3/keeper/asset_test.go @@ -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)) } diff --git a/x/bep3/keeper/integration_test.go b/x/bep3/keeper/integration_test.go index 977edb2f..b0c5d4fc 100644 --- a/x/bep3/keeper/integration_test.go +++ b/x/bep3/keeper/integration_test.go @@ -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)) } diff --git a/x/bep3/keeper/keeper.go b/x/bep3/keeper/keeper.go index 7ea4d1a8..7fd005e1 100644 --- a/x/bep3/keeper/keeper.go +++ b/x/bep3/keeper/keeper.go @@ -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) diff --git a/x/bep3/keeper/keeper_test.go b/x/bep3/keeper/keeper_test.go index 37fe230e..a28463d9 100644 --- a/x/bep3/keeper/keeper_test.go +++ b/x/bep3/keeper/keeper_test.go @@ -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) { diff --git a/x/bep3/keeper/params.go b/x/bep3/keeper/params.go index 7075dfc1..9c35dcf9 100644 --- a/x/bep3/keeper/params.go +++ b/x/bep3/keeper/params.go @@ -18,63 +18,100 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { k.paramSubspace.SetParamSet(ctx, ¶ms) } -// 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 +} diff --git a/x/bep3/keeper/params_test.go b/x/bep3/keeper/params_test.go index c9270999..e962510f 100644 --- a/x/bep3/keeper/params_test.go +++ b/x/bep3/keeper/params_test.go @@ -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) diff --git a/x/bep3/keeper/querier.go b/x/bep3/keeper/querier.go index c4ca9e46..7e784254 100644 --- a/x/bep3/keeper/querier.go +++ b/x/bep3/keeper/querier.go @@ -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)) } diff --git a/x/bep3/keeper/querier_test.go b/x/bep3/keeper/querier_test.go index 5143f694..5078f31e 100644 --- a/x/bep3/keeper/querier_test.go +++ b/x/bep3/keeper/querier_test.go @@ -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) } diff --git a/x/bep3/keeper/swap.go b/x/bep3/keeper/swap.go index 3b1d8e13..07da40c8 100644 --- a/x/bep3/keeper/swap.go +++ b/x/bep3/keeper/swap.go @@ -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]) diff --git a/x/bep3/keeper/swap_test.go b/x/bep3/keeper/swap_test.go index 133afe8d..4480b4e0 100644 --- a/x/bep3/keeper/swap_test.go +++ b/x/bep3/keeper/swap_test.go @@ -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) diff --git a/x/bep3/legacy/v0_11/types.go b/x/bep3/legacy/v0_11/types.go new file mode 100644 index 00000000..bd8cac57 --- /dev/null +++ b/x/bep3/legacy/v0_11/types.go @@ -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 +) diff --git a/x/bep3/legacy/v0_9/types.go b/x/bep3/legacy/v0_9/types.go new file mode 100644 index 00000000..3338e744 --- /dev/null +++ b/x/bep3/legacy/v0_9/types.go @@ -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 +) diff --git a/x/bep3/simulation/decoder.go b/x/bep3/simulation/decoder.go index 8fc8dd61..cca456d9 100644 --- a/x/bep3/simulation/decoder.go +++ b/x/bep3/simulation/decoder.go @@ -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])) diff --git a/x/bep3/simulation/decoder_test.go b/x/bep3/simulation/decoder_test.go index 0aa27ab2..3c0ee97b 100644 --- a/x/bep3/simulation/decoder_test.go +++ b/x/bep3/simulation/decoder_test.go @@ -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}}, diff --git a/x/bep3/simulation/genesis.go b/x/bep3/simulation/genesis.go index 9ab107cb..04ebd45e 100644 --- a/x/bep3/simulation/genesis.go +++ b/x/bep3/simulation/genesis.go @@ -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 } diff --git a/x/bep3/simulation/operations.go b/x/bep3/simulation/operations.go index 9df0d445..4b1b6077 100644 --- a/x/bep3/simulation/operations.go +++ b/x/bep3/simulation/operations.go @@ -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 } diff --git a/x/bep3/simulation/params.go b/x/bep3/simulation/params.go index 9a8e6e48..6ebaa469 100644 --- a/x/bep3/simulation/params.go +++ b/x/bep3/simulation/params.go @@ -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)) }, ), } diff --git a/x/bep3/types/errors.go b/x/bep3/types/errors.go index a07560cd..a3ee3773 100644 --- a/x/bep3/types/errors.go +++ b/x/bep3/types/errors.go @@ -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") ) diff --git a/x/bep3/types/genesis.go b/x/bep3/types/genesis.go index ef3af6d1..ef12237e 100644 --- a/x/bep3/types/genesis.go +++ b/x/bep3/types/genesis.go @@ -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 } diff --git a/x/bep3/types/genesis_test.go b/x/bep3/types/genesis_test.go index 016291f8..187ff91f 100644 --- a/x/bep3/types/genesis_test.go +++ b/x/bep3/types/genesis_test.go @@ -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) + } + + }) } } diff --git a/x/bep3/types/keys.go b/x/bep3/types/keys.go index 76470bb9..a41e94df 100644 --- a/x/bep3/types/keys.go +++ b/x/bep3/types/keys.go @@ -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 diff --git a/x/bep3/types/params.go b/x/bep3/types/params.go index 0725daac..3813140e 100644 --- a/x/bep3/types/params.go +++ b/x/bep3/types/params.go @@ -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 diff --git a/x/bep3/types/params_test.go b/x/bep3/types/params_test.go index 9ac07a14..808e383d 100644 --- a/x/bep3/types/params_test.go +++ b/x/bep3/types/params_test.go @@ -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) - } + }) } } diff --git a/x/bep3/types/querier.go b/x/bep3/types/querier.go index 28e2d3de..ec108d58 100644 --- a/x/bep3/types/querier.go +++ b/x/bep3/types/querier.go @@ -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, } diff --git a/x/bep3/types/asset.go b/x/bep3/types/supply.go similarity index 63% rename from x/bep3/types/asset.go rename to x/bep3/types/supply.go index 4d2b9d06..71f863c8 100644 --- a/x/bep3/types/asset.go +++ b/x/bep3/types/supply.go @@ -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 diff --git a/x/bep3/types/asset_test.go b/x/bep3/types/supply_test.go similarity index 72% rename from x/bep3/types/asset_test.go rename to x/bep3/types/supply_test.go index 1b25230f..3f6b9137 100644 --- a/x/bep3/types/asset_test.go +++ b/x/bep3/types/supply_test.go @@ -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 { diff --git a/x/committee/keeper/param_permission_test.go b/x/committee/keeper/param_permission_test.go index 213028bd..59f12c97 100644 --- a/x/committee/keeper/param_permission_test.go +++ b/x/committee/keeper/param_permission_test.go @@ -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)), }, { diff --git a/x/committee/types/param_permissions_test.go b/x/committee/types/param_permissions_test.go index 961bedca..c7d5acb3 100644 --- a/x/committee/types/param_permissions_test.go +++ b/x/committee/types/param_permissions_test.go @@ -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 diff --git a/x/committee/types/permissions.go b/x/committee/types/permissions.go index 13b3f369..7d53e8b4 100644 --- a/x/committee/types/permissions.go +++ b/x/committee/types/permissions.go @@ -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, ¤tAPs) // panics if something goes wrong + subspace.Get(ctx, bep3types.KeyAssetParams, ¤tAPs) // 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 }