BEP3: support multiple assets (#616)

* wip: refactor to allow multiple bep3 assets

* update tests

* sims: validate asset before claiming

* move asset supply to params

* update tests

* fix sims

* fix tests

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

* fix build and migration tests

* nit: rename file

* move asset supply out of params

* update committee tests

* fix sims

* address review comments

* address review comments

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

View File

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

View File

@ -3,6 +3,7 @@ package app
import ( import (
"math/rand" "math/rand"
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -110,6 +111,33 @@ func (tApp TestApp) InitializeFromGenesisStates(genesisStates ...GenesisState) T
return tApp 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) { func (tApp TestApp) CheckBalance(t *testing.T, ctx sdk.Context, owner sdk.AccAddress, expectedCoins sdk.Coins) {
acc := tApp.GetAccountKeeper().GetAccount(ctx, owner) acc := tApp.GetAccountKeeper().GetAccount(ctx, owner)
require.NotNilf(t, acc, "account with address '%s' doesn't exist", owner) require.NotNilf(t, acc, "account with address '%s' doesn't exist", owner)

View File

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

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

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

View File

@ -31,7 +31,7 @@ import (
v032tendermint "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_32" v032tendermint "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_32"
v033tendermint "github.com/kava-labs/kava/migrate/v0_8/tendermint/v0_33" 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/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/cdp"
"github.com/kava-labs/kava/x/committee" "github.com/kava-labs/kava/x/committee"
"github.com/kava-labs/kava/x/incentive" "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) // migrate new modules (by adding new gen states)
v0_8AppState[auction.ModuleName] = v0_8Codec.MustMarshalJSON(auction.DefaultGenesisState()) 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[cdp.ModuleName] = v0_8Codec.MustMarshalJSON(cdp.DefaultGenesisState())
v0_8AppState[committee.ModuleName] = v0_8Codec.MustMarshalJSON(committee.DefaultGenesisState()) v0_8AppState[committee.ModuleName] = v0_8Codec.MustMarshalJSON(committee.DefaultGenesisState())
v0_8AppState[incentive.ModuleName] = v0_8Codec.MustMarshalJSON(incentive.DefaultGenesisState()) v0_8AppState[incentive.ModuleName] = v0_8Codec.MustMarshalJSON(incentive.DefaultGenesisState())

View File

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

View File

@ -2,7 +2,6 @@ package bep3_test
import ( import (
"testing" "testing"
"time"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -31,14 +30,14 @@ func (suite *ABCITestSuite) SetupTest() {
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()}) ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
// Set up auth GenesisState // Set up auth GenesisState
_, addrs := app.GeneratePrivKeyAddressPairs(11) _, addrs := app.GeneratePrivKeyAddressPairs(12)
coins := []sdk.Coins{} 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))) coins = append(coins, cs(c("bnb", 10000000000), c("ukava", 10000000000)))
} }
authGS := app.NewAuthGenState(addrs, coins) authGS := app.NewAuthGenState(addrs, coins)
// Initialize test app // Initialize test app
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(addrs[0])) tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(addrs[11]))
suite.ctx = ctx suite.ctx = ctx
suite.app = tApp suite.app = tApp
@ -61,12 +60,12 @@ func (suite *ABCITestSuite) ResetKeeper() {
// Create atomic swap and check err to confirm creation // Create atomic swap and check err to confirm creation
err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight, 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) amount, true)
suite.Nil(err) suite.Nil(err)
// Store swap's calculated ID and secret random number // 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) swapIDs = append(swapIDs, swapID)
randomNumbers = append(randomNumbers, randomNumber[:]) 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) { func TestABCITestSuite(t *testing.T) {
suite.Run(t, new(ABCITestSuite)) suite.Run(t, new(ABCITestSuite))
} }

View File

@ -1,140 +1,146 @@
package bep3 package bep3
import ( 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/keeper"
"github.com/kava-labs/kava/x/bep3/types" "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 ( 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 EventTypeCreateAtomicSwap = types.EventTypeCreateAtomicSwap
EventTypeClaimAtomicSwap = types.EventTypeClaimAtomicSwap
EventTypeRefundAtomicSwap = types.EventTypeRefundAtomicSwap EventTypeRefundAtomicSwap = types.EventTypeRefundAtomicSwap
EventTypeSwapsExpired = types.EventTypeSwapsExpired EventTypeSwapsExpired = types.EventTypeSwapsExpired
Expired = types.Expired AttributeValueCategory = types.AttributeValueCategory
INVALID = types.INVALID AttributeKeySender = types.AttributeKeySender
Incoming = types.Incoming AttributeKeyRecipient = types.AttributeKeyRecipient
Int64Size = types.Int64Size AttributeKeyAtomicSwapID = types.AttributeKeyAtomicSwapID
MaxExpectedIncomeLength = types.MaxExpectedIncomeLength AttributeKeyRandomNumberHash = types.AttributeKeyRandomNumberHash
MaxOtherChainAddrLength = types.MaxOtherChainAddrLength 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 ModuleName = types.ModuleName
NULL = types.NULL StoreKey = types.StoreKey
Open = types.Open RouterKey = types.RouterKey
Outgoing = types.Outgoing
QuerierRoute = types.QuerierRoute 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 QueryGetAssetSupply = types.QueryGetAssetSupply
QueryGetAssetSupplies = types.QueryGetAssetSupplies
QueryGetAtomicSwap = types.QueryGetAtomicSwap QueryGetAtomicSwap = types.QueryGetAtomicSwap
QueryGetAtomicSwaps = types.QueryGetAtomicSwaps QueryGetAtomicSwaps = types.QueryGetAtomicSwaps
QueryGetParams = types.QueryGetParams QueryGetParams = types.QueryGetParams
RandomNumberHashLength = types.RandomNumberHashLength NULL = types.NULL
RandomNumberLength = types.RandomNumberLength Open = types.Open
RefundAtomicSwap = types.RefundAtomicSwap Completed = types.Completed
RouterKey = types.RouterKey Expired = types.Expired
StoreKey = types.StoreKey INVALID = types.INVALID
SwapIDLength = types.SwapIDLength Incoming = types.Incoming
Outgoing = types.Outgoing
) )
var ( var (
// functions aliases
NewKeeper = keeper.NewKeeper NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier NewQuerier = keeper.NewQuerier
RegisterRoutes = rest.RegisterRoutes NewAssetSupply = types.NewAssetSupply
RegisterCodec = types.RegisterCodec
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
GenerateSecureRandomNumber = types.GenerateSecureRandomNumber
CalculateRandomHash = types.CalculateRandomHash CalculateRandomHash = types.CalculateRandomHash
CalculateSwapID = types.CalculateSwapID 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 GetAtomicSwapByHeightKey = types.GetAtomicSwapByHeightKey
NewAssetSupply = types.NewAssetSupply
NewAtomicSwap = types.NewAtomicSwap
NewGenesisState = types.NewGenesisState
NewMsgClaimAtomicSwap = types.NewMsgClaimAtomicSwap
NewMsgCreateAtomicSwap = types.NewMsgCreateAtomicSwap NewMsgCreateAtomicSwap = types.NewMsgCreateAtomicSwap
NewMsgClaimAtomicSwap = types.NewMsgClaimAtomicSwap
NewMsgRefundAtomicSwap = types.NewMsgRefundAtomicSwap NewMsgRefundAtomicSwap = types.NewMsgRefundAtomicSwap
NewParams = types.NewParams NewParams = types.NewParams
DefaultParams = types.DefaultParams
NewAssetParam = types.NewAssetParam
ParamKeyTable = types.ParamKeyTable
NewQueryAssetSupply = types.NewQueryAssetSupply NewQueryAssetSupply = types.NewQueryAssetSupply
NewQueryAssetSupplies = types.NewQueryAssetSupplies
NewQueryAtomicSwapByID = types.NewQueryAtomicSwapByID NewQueryAtomicSwapByID = types.NewQueryAtomicSwapByID
NewQueryAtomicSwaps = types.NewQueryAtomicSwaps NewQueryAtomicSwaps = types.NewQueryAtomicSwaps
NewSwapDirectionFromString = types.NewSwapDirectionFromString NewAtomicSwap = types.NewAtomicSwap
NewSwapStatusFromString = types.NewSwapStatusFromString NewSwapStatusFromString = types.NewSwapStatusFromString
ParamKeyTable = types.ParamKeyTable NewSwapDirectionFromString = types.NewSwapDirectionFromString
RegisterCodec = types.RegisterCodec NewAugmentedAtomicSwap = types.NewAugmentedAtomicSwap
// variable aliases // variable aliases
AssetSupplyKeyPrefix = types.AssetSupplyKeyPrefix ModuleCdc = types.ModuleCdc
AtomicSwapByBlockPrefix = types.AtomicSwapByBlockPrefix ErrInvalidTimestamp = types.ErrInvalidTimestamp
AtomicSwapCoinsAccAddr = types.AtomicSwapCoinsAccAddr 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 AtomicSwapKeyPrefix = types.AtomicSwapKeyPrefix
AtomicSwapByBlockPrefix = types.AtomicSwapByBlockPrefix
AtomicSwapLongtermStoragePrefix = types.AtomicSwapLongtermStoragePrefix AtomicSwapLongtermStoragePrefix = types.AtomicSwapLongtermStoragePrefix
AtomicSwapCoinsAccAddr = types.AtomicSwapCoinsAccAddr
KeyAssetParams = types.KeyAssetParams
DefaultBnbDeputyFixedFee = types.DefaultBnbDeputyFixedFee DefaultBnbDeputyFixedFee = types.DefaultBnbDeputyFixedFee
DefaultMinAmount = types.DefaultMinAmount DefaultMinAmount = types.DefaultMinAmount
DefaultMaxAmount = types.DefaultMaxAmount DefaultMaxAmount = types.DefaultMaxAmount
DefaultMaxBlockLock = types.DefaultMaxBlockLock
DefaultMinBlockLock = types.DefaultMinBlockLock DefaultMinBlockLock = types.DefaultMinBlockLock
DefaultSupportedAssets = types.DefaultSupportedAssets DefaultMaxBlockLock = types.DefaultMaxBlockLock
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
) )
type ( type (
Keeper = keeper.Keeper Keeper = keeper.Keeper
AssetParam = types.AssetParam AssetSupply = types.AssetSupply
AssetParams = types.AssetParams AssetSupplies = types.AssetSupplies
AssetSupplies = types.AssetSupplies GenesisState = types.GenesisState
AssetSupply = types.AssetSupply MsgCreateAtomicSwap = types.MsgCreateAtomicSwap
AtomicSwap = types.AtomicSwap MsgClaimAtomicSwap = types.MsgClaimAtomicSwap
AtomicSwaps = types.AtomicSwaps MsgRefundAtomicSwap = types.MsgRefundAtomicSwap
GenesisState = types.GenesisState Params = types.Params
MsgClaimAtomicSwap = types.MsgClaimAtomicSwap AssetParam = types.AssetParam
MsgCreateAtomicSwap = types.MsgCreateAtomicSwap AssetParams = types.AssetParams
MsgRefundAtomicSwap = types.MsgRefundAtomicSwap QueryAssetSupply = types.QueryAssetSupply
Params = types.Params QueryAssetSupplies = types.QueryAssetSupplies
QueryAssetSupply = types.QueryAssetSupply QueryAtomicSwapByID = types.QueryAtomicSwapByID
QueryAtomicSwapByID = types.QueryAtomicSwapByID QueryAtomicSwaps = types.QueryAtomicSwaps
QueryAtomicSwaps = types.QueryAtomicSwaps AtomicSwap = types.AtomicSwap
SwapDirection = types.SwapDirection AtomicSwaps = types.AtomicSwaps
SwapStatus = types.SwapStatus SwapStatus = types.SwapStatus
SwapDirection = types.SwapDirection
AugmentedAtomicSwap = types.AugmentedAtomicSwap
AugmentedAtomicSwaps = types.AugmentedAtomicSwaps
) )

View File

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

View File

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

View File

@ -21,39 +21,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper
} }
keeper.SetParams(ctx, gs.Params) keeper.SetParams(ctx, gs.Params)
for _, supply := range gs.Supplies {
// Initialize supported assets keeper.SetAssetSupply(ctx, supply, supply.GetDenom())
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)
}
} }
var incomingSupplies sdk.Coins 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 // Asset's given incoming/outgoing supply much match the amount of coins in incoming/outgoing atomic swaps
supplies := keeper.GetAllAssetSupplies(ctx) supplies := keeper.GetAllAssetSupplies(ctx)
for _, supply := range supplies { for _, supply := range supplies {
incomingSupply := incomingSupplies.AmountOf(supply.Denom) incomingSupply := incomingSupplies.AmountOf(supply.GetDenom())
if !supply.IncomingSupply.Amount.Equal(incomingSupply) { if !supply.IncomingSupply.Amount.Equal(incomingSupply) {
panic(fmt.Sprintf("asset's incoming supply %s does not match amount %s in incoming atomic swaps", panic(fmt.Sprintf("asset's incoming supply %s does not match amount %s in incoming atomic swaps",
supply.IncomingSupply, incomingSupply)) supply.IncomingSupply, incomingSupply))
} }
outgoingSupply := outgoingSupplies.AmountOf(supply.Denom) outgoingSupply := outgoingSupplies.AmountOf(supply.GetDenom())
if !supply.OutgoingSupply.Amount.Equal(outgoingSupply) { if !supply.OutgoingSupply.Amount.Equal(outgoingSupply) {
panic(fmt.Sprintf("asset's outgoing supply %s does not match amount %s in outgoing atomic swaps", panic(fmt.Sprintf("asset's outgoing supply %s does not match amount %s in outgoing atomic swaps",
supply.OutgoingSupply, outgoingSupply)) 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) { func ExportGenesis(ctx sdk.Context, k Keeper) (data GenesisState) {
params := k.GetParams(ctx) params := k.GetParams(ctx)
swaps := k.GetAllAtomicSwaps(ctx) swaps := k.GetAllAtomicSwaps(ctx)
assets := k.GetAllAssetSupplies(ctx) supplies := k.GetAllAssetSupplies(ctx)
return NewGenesisState(params, swaps, assets) return NewGenesisState(params, swaps, supplies)
} }

View File

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

View File

@ -20,7 +20,7 @@ const (
var ( var (
StandardSupplyLimit = i(100000000000) 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) } 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 { func baseGenState(deputy sdk.AccAddress) bep3.GenesisState {
bep3Genesis := bep3.GenesisState{ bep3Genesis := bep3.GenesisState{
Params: bep3.Params{ Params: bep3.Params{
BnbDeputyAddress: deputy, AssetParams: bep3.AssetParams{
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{
bep3.AssetParam{ bep3.AssetParam{
Denom: "btc", Denom: "bnb",
CoinID: 714, CoinID: 714,
Limit: StandardSupplyLimit, SupplyLimit: sdk.NewInt(350000000000000),
Active: true, Active: true,
DeputyAddress: deputy,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: bep3.DefaultMinBlockLock,
MaxBlockLock: bep3.DefaultMaxBlockLock,
}, },
bep3.AssetParam{ bep3.AssetParam{
Denom: "eth", Denom: "inc",
CoinID: 999999, CoinID: 9999,
Limit: StandardSupplyLimit, SupplyLimit: sdk.NewInt(100000000000),
Active: true, Active: true,
}, DeputyAddress: deputy,
bep3.AssetParam{ FixedFee: sdk.NewInt(1000),
Denom: "bnb", MinSwapAmount: sdk.OneInt(),
CoinID: 99999, MaxSwapAmount: sdk.NewInt(1000000000000),
Limit: StandardSupplyLimit, MinBlockLock: bep3.DefaultMinBlockLock,
Active: true, MaxBlockLock: bep3.DefaultMaxBlockLock,
},
bep3.AssetParam{
Denom: "inc",
CoinID: 9999,
Limit: i(100),
Active: false,
}, },
}, },
}, },
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 return bep3Genesis
} }
@ -84,8 +91,8 @@ func loadSwapAndSupply(addr sdk.AccAddress, index int) (bep3.AtomicSwap, bep3.As
expireOffset, timestamp, addr, addr, TestSenderOtherChain, expireOffset, timestamp, addr, addr, TestSenderOtherChain,
TestRecipientOtherChain, 1, bep3.Open, true, bep3.Incoming) TestRecipientOtherChain, 1, bep3.Open, true, bep3.Incoming)
supply := bep3.NewAssetSupply(coin.Denom, coin, c(coin.Denom, 0), supply := bep3.NewAssetSupply(coin, c(coin.Denom, 0),
c(coin.Denom, 0), c(coin.Denom, StandardSupplyLimit.Int64())) c(coin.Denom, 0))
return swap, supply return swap, supply
} }

View File

@ -9,24 +9,30 @@ import (
// IncrementCurrentAssetSupply increments an asset's supply by the coin // IncrementCurrentAssetSupply increments an asset's supply by the coin
func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error { 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 { if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) 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 // Resulting current supply must be under asset's limit
if supply.SupplyLimit.IsLT(supply.CurrentSupply.Add(coin)) { if supplyLimit.IsLT(supply.CurrentSupply.Add(coin)) {
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supply.SupplyLimit) return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supplyLimit)
} }
supply.CurrentSupply = supply.CurrentSupply.Add(coin) supply.CurrentSupply = supply.CurrentSupply.Add(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom)) k.SetAssetSupply(ctx, supply, coin.Denom)
return nil return nil
} }
// DecrementCurrentAssetSupply decrement an asset's supply by the coin // DecrementCurrentAssetSupply decrement an asset's supply by the coin
func (k Keeper) DecrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error { 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 { if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) 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) supply.CurrentSupply = supply.CurrentSupply.Sub(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom)) k.SetAssetSupply(ctx, supply, coin.Denom)
return nil return nil
} }
// IncrementIncomingAssetSupply increments an asset's incoming supply // IncrementIncomingAssetSupply increments an asset's incoming supply
func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error { 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 { if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
} }
// Result of (current + incoming + amount) must be under asset's limit // Result of (current + incoming + amount) must be under asset's limit
totalSupply := supply.CurrentSupply.Add(supply.IncomingSupply) 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) supply.IncomingSupply = supply.IncomingSupply.Add(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom)) k.SetAssetSupply(ctx, supply, coin.Denom)
return nil return nil
} }
// DecrementIncomingAssetSupply decrements an asset's incoming supply // DecrementIncomingAssetSupply decrements an asset's incoming supply
func (k Keeper) DecrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error { 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 { if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) 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) supply.IncomingSupply = supply.IncomingSupply.Sub(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom)) k.SetAssetSupply(ctx, supply, coin.Denom)
return nil 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 { 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 { if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) 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) supply.OutgoingSupply = supply.OutgoingSupply.Add(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom)) k.SetAssetSupply(ctx, supply, coin.Denom)
return nil 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 { 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 { if !found {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) 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) supply.OutgoingSupply = supply.OutgoingSupply.Sub(coin)
k.SetAssetSupply(ctx, supply, []byte(coin.Denom)) k.SetAssetSupply(ctx, supply, coin.Denom)
return nil return nil
} }
// UpdateAssetSupplies applies updates to the asset limit from parameters to the asset supplies
func (k Keeper) UpdateAssetSupplies(ctx sdk.Context) {
params := k.GetParams(ctx)
for _, supportedAsset := range params.SupportedAssets {
asset, found := k.GetAssetSupply(ctx, []byte(supportedAsset.Denom))
if !found {
continue
}
if !asset.SupplyLimit.Amount.Equal(supportedAsset.Limit) {
asset.SupplyLimit = sdk.NewCoin(supportedAsset.Denom, supportedAsset.Limit)
k.SetAssetSupply(ctx, asset, []byte(supportedAsset.Denom))
}
}
}

View File

@ -36,16 +36,16 @@ func (suite *AssetTestSuite) SetupTest() {
tApp.InitializeFromGenesisStates(NewBep3GenStateMulti(deputy)) tApp.InitializeFromGenesisStates(NewBep3GenStateMulti(deputy))
keeper := tApp.GetBep3Keeper() 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 // Set asset supply with standard value for testing
supply := types.AssetSupply{ supply := types.AssetSupply{
Denom: "bnb",
IncomingSupply: c("bnb", 5), IncomingSupply: c("bnb", 5),
OutgoingSupply: c("bnb", 5), OutgoingSupply: c("bnb", 5),
CurrentSupply: c("bnb", 40), 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.app = tApp
suite.ctx = ctx suite.ctx = ctx
@ -95,11 +95,10 @@ func (suite *AssetTestSuite) TestIncrementCurrentAssetSupply() {
for _, tc := range testCases { for _, tc := range testCases {
suite.SetupTest() suite.SetupTest()
suite.Run(tc.name, func() { 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) 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 { if tc.expectPass {
suite.True(found) suite.True(found)
@ -155,11 +154,10 @@ func (suite *AssetTestSuite) TestDecrementCurrentAssetSupply() {
for _, tc := range testCases { for _, tc := range testCases {
suite.SetupTest() suite.SetupTest()
suite.Run(tc.name, func() { 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) 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 { if tc.expectPass {
suite.True(found) suite.True(found)
@ -215,11 +213,9 @@ func (suite *AssetTestSuite) TestIncrementIncomingAssetSupply() {
for _, tc := range testCases { for _, tc := range testCases {
suite.SetupTest() suite.SetupTest()
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom) preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
err := suite.keeper.IncrementIncomingAssetSupply(suite.ctx, tc.args.coin) 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 { if tc.expectPass {
suite.True(found) suite.True(found)
@ -275,11 +271,10 @@ func (suite *AssetTestSuite) TestDecrementIncomingAssetSupply() {
for _, tc := range testCases { for _, tc := range testCases {
suite.SetupTest() suite.SetupTest()
suite.Run(tc.name, func() { 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) 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 { if tc.expectPass {
suite.True(found) suite.True(found)
@ -335,11 +330,10 @@ func (suite *AssetTestSuite) TestIncrementOutgoingAssetSupply() {
for _, tc := range testCases { for _, tc := range testCases {
suite.SetupTest() suite.SetupTest()
suite.Run(tc.name, func() { 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) 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 { if tc.expectPass {
suite.True(found) suite.True(found)
@ -395,11 +389,9 @@ func (suite *AssetTestSuite) TestDecrementOutgoingAssetSupply() {
for _, tc := range testCases { for _, tc := range testCases {
suite.SetupTest() suite.SetupTest()
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
supplyKeyPrefix := []byte(tc.args.coin.Denom) preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
err := suite.keeper.DecrementOutgoingAssetSupply(suite.ctx, tc.args.coin) 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 { if tc.expectPass {
suite.True(found) 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) { func TestAssetTestSuite(t *testing.T) {
suite.Run(t, new(AssetTestSuite)) suite.Run(t, new(AssetTestSuite))
} }

View File

@ -20,10 +20,9 @@ const (
) )
var ( var (
StandardSupplyLimit = i(350000000000000) DenomMap = map[int]string{0: "btc", 1: "eth", 2: "bnb", 3: "xrp", 4: "dai"}
DenomMap = map[int]string{0: "btc", 1: "eth", 2: "bnb", 3: "xrp", 4: "dai"} TestUser1 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1")))
TestUser1 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1"))) TestUser2 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser2")))
TestUser2 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser2")))
) )
func i(in int64) sdk.Int { return sdk.NewInt(in) } 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 { func NewBep3GenStateMulti(deputyAddress sdk.AccAddress) app.GenesisState {
bep3Genesis := types.GenesisState{ bep3Genesis := types.GenesisState{
Params: bep3.Params{ Params: bep3.Params{
BnbDeputyAddress: deputyAddress, AssetParams: types.AssetParams{
BnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee, // 1000
MinAmount: types.DefaultMinAmount, // 0
MaxAmount: types.DefaultMaxAmount, // 10,000
MinBlockLock: types.DefaultMinBlockLock, // 220
MaxBlockLock: types.DefaultMaxBlockLock, // 270
SupportedAssets: types.AssetParams{
types.AssetParam{ types.AssetParam{
Denom: "bnb", Denom: "bnb",
CoinID: 714, CoinID: 714,
Limit: StandardSupplyLimit, SupplyLimit: sdk.NewInt(350000000000000),
Active: true, Active: true,
DeputyAddress: deputyAddress,
FixedFee: sdk.NewInt(1000),
MinSwapAmount: sdk.OneInt(),
MaxSwapAmount: sdk.NewInt(1000000000000),
MinBlockLock: types.DefaultMinBlockLock,
MaxBlockLock: types.DefaultMaxBlockLock,
}, },
types.AssetParam{ types.AssetParam{
Denom: "inc", Denom: "inc",
CoinID: 9999, CoinID: 9999,
Limit: i(100), SupplyLimit: sdk.NewInt(100),
Active: false, 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)} 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 { func assetSupply(denom string) types.AssetSupply {
return types.NewAssetSupply(denom, c(denom, 0), c(denom, 0), c(denom, 0), c(denom, 10000)) return types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 0))
} }

View File

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

View File

@ -308,64 +308,32 @@ func (suite *KeeperTestSuite) TestIterateAtomicSwapsLongtermStorage() {
} }
func (suite *KeeperTestSuite) TestGetSetAssetSupply() { func (suite *KeeperTestSuite) TestGetSetAssetSupply() {
suite.ResetChain()
denom := "bnb" denom := "bnb"
// Put asset supply in store // Put asset supply in store
assetSupply := types.NewAssetSupply(denom, c(denom, 0), c(denom, 0), c(denom, 50000), c(denom, 100000)) assetSupply := types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 50000))
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, []byte(denom)) suite.keeper.SetAssetSupply(suite.ctx, assetSupply, denom)
// Check asset in store // 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.True(found)
suite.Equal(assetSupply, storedAssetSupply) suite.Equal(assetSupply, storedAssetSupply)
// Check fake asset supply not in store // Check fake asset supply not in store
fakeDenom := "xyz" fakeDenom := "xyz"
_, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(fakeDenom)) _, found = suite.keeper.GetAssetSupply(suite.ctx, fakeDenom)
suite.False(found) 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() { func (suite *KeeperTestSuite) TestGetAllAssetSupplies() {
suite.ResetChain()
// Set asset supplies // Put asset supply in store
count := 3 assetSupply := types.NewAssetSupply(c("bnb", 0), c("bnb", 0), c("bnb", 50000))
supplies := assetSupplies(count) suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "bnb")
for _, supply := range supplies { assetSupply = types.NewAssetSupply(c("inc", 0), c("inc", 0), c("inc", 50000))
suite.keeper.SetAssetSupply(suite.ctx, supply, []byte(supply.Denom)) suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "inc")
}
// Get all asset supplies supplies := suite.keeper.GetAllAssetSupplies(suite.ctx)
readSupplies := suite.keeper.GetAllAssetSupplies(suite.ctx) suite.Equal(2, len(supplies))
suite.Equal(count, len(readSupplies))
// Check expected values
for i := 0; i < count; i++ {
suite.Contains(readSupplies, supplies[i])
}
} }
func TestKeeperTestSuite(t *testing.T) { func TestKeeperTestSuite(t *testing.T) {

View File

@ -18,63 +18,100 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramSubspace.SetParamSet(ctx, &params) k.paramSubspace.SetParamSet(ctx, &params)
} }
// GetBnbDeputyAddress returns the Bnbchain's deputy address // ------------------------------------------
func (k Keeper) GetBnbDeputyAddress(ctx sdk.Context) sdk.AccAddress { // Asset
// ------------------------------------------
// GetAsset returns the asset param associated with the input denom
func (k Keeper) GetAsset(ctx sdk.Context, denom string) (types.AssetParam, error) {
params := k.GetParams(ctx) 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 // SetAsset sets an asset in the params
func (k Keeper) GetBnbDeputyFixedFee(ctx sdk.Context) sdk.Int { func (k Keeper) SetAsset(ctx sdk.Context, asset types.AssetParam) {
params := k.GetParams(ctx) params := k.GetParams(ctx)
return params.BnbDeputyFixedFee for i := range params.AssetParams {
} if params.AssetParams[i].Denom == asset.Denom {
params.AssetParams[i] = asset
// GetMinAmount returns the minimum amount }
func (k Keeper) GetMinAmount(ctx sdk.Context) sdk.Int { }
params := k.GetParams(ctx) k.SetParams(ctx, params)
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
} }
// GetAssets returns a list containing all supported assets // GetAssets returns a list containing all supported assets
func (k Keeper) GetAssets(ctx sdk.Context) (types.AssetParams, bool) { func (k Keeper) GetAssets(ctx sdk.Context) (types.AssetParams, bool) {
params := k.GetParams(ctx) 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) { // Asset-specific getters
params := k.GetParams(ctx) // ------------------------------------------
for _, asset := range params.SupportedAssets {
if asset.Denom == denom { // GetDeputyAddress returns the deputy address for the input denom
return asset, true 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 // GetAssetByCoinID returns an asset by its denom
func (k Keeper) GetAssetByCoinID(ctx sdk.Context, coinID int) (types.AssetParam, bool) { func (k Keeper) GetAssetByCoinID(ctx sdk.Context, coinID int) (types.AssetParam, bool) {
params := k.GetParams(ctx) params := k.GetParams(ctx)
for _, asset := range params.SupportedAssets { for _, asset := range params.AssetParams {
if asset.CoinID == coinID { if asset.CoinID == coinID {
return asset, true 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 // ValidateLiveAsset checks if an asset is both supported and active
func (k Keeper) ValidateLiveAsset(ctx sdk.Context, coin sdk.Coin) error { func (k Keeper) ValidateLiveAsset(ctx sdk.Context, coin sdk.Coin) error {
asset, found := k.GetAssetByDenom(ctx, coin.Denom) asset, err := k.GetAsset(ctx, coin.Denom)
if !found { if err != nil {
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) return err
} }
if !asset.Active { if !asset.Active {
return sdkerrors.Wrap(types.ErrAssetNotActive, asset.Denom) return sdkerrors.Wrap(types.ErrAssetNotActive, asset.Denom)
} }
return nil return nil
} }
// GetSupplyLimit returns the supply limit for the input denom
func (k Keeper) GetSupplyLimit(ctx sdk.Context, denom string) (sdk.Int, error) {
asset, err := k.GetAsset(ctx, denom)
if err != nil {
return sdk.Int{}, err
}
return asset.SupplyLimit, nil
}

View File

@ -35,78 +35,81 @@ func (suite *ParamsTestSuite) SetupTest() {
suite.addrs = addrs suite.addrs = addrs
} }
func (suite *ParamsTestSuite) TestGetSetBnbDeputyAddress() { func (suite *ParamsTestSuite) TestGetSetAsset() {
params := suite.keeper.GetParams(suite.ctx) asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
params.BnbDeputyAddress = suite.addrs[1] suite.Require().NoError(err)
suite.NotPanics(func() { suite.keeper.SetParams(suite.ctx, params) }) 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) _, err = suite.keeper.GetAsset(suite.ctx, "inc")
suite.Equal(suite.addrs[1], params.BnbDeputyAddress) suite.Require().NoError(err)
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)
} }
func (suite *ParamsTestSuite) TestGetAssets() { func (suite *ParamsTestSuite) TestGetAssets() {
params := suite.keeper.GetParams(suite.ctx) assets, found := suite.keeper.GetAssets(suite.ctx)
assets := params.SupportedAssets suite.Require().True(found)
suite.Require().Equal(2, len(assets))
res, found := suite.keeper.GetAssets(suite.ctx)
suite.True(found)
suite.Equal(assets, res)
} }
func (suite *ParamsTestSuite) TestGetAssetByDenom() { func (suite *ParamsTestSuite) TestGetSetDeputyAddress() {
params := suite.keeper.GetParams(suite.ctx) asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
asset := params.SupportedAssets[0] 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) asset, err = suite.keeper.GetAsset(suite.ctx, "bnb")
suite.True(found) suite.Require().NoError(err)
suite.Equal(asset, res) 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() { func (suite *ParamsTestSuite) TestGetAssetByCoinID() {
params := suite.keeper.GetParams(suite.ctx) asset, err := suite.keeper.GetAsset(suite.ctx, "bnb")
asset := params.SupportedAssets[0] suite.Require().NoError(err)
res, found := suite.keeper.GetAssetByCoinID(suite.ctx, asset.CoinID) res, found := suite.keeper.GetAssetByCoinID(suite.ctx, asset.CoinID)
suite.True(found) suite.True(found)

View File

@ -39,7 +39,7 @@ func queryAssetSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) 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 { if !found {
return nil, sdkerrors.Wrap(types.ErrAssetSupplyNotFound, string(requestParams.Denom)) return nil, sdkerrors.Wrap(types.ErrAssetSupplyNotFound, string(requestParams.Denom))
} }

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

@ -27,12 +27,12 @@ func TestDecodeDistributionStore(t *testing.T) {
oneCoin := sdk.NewCoin("coin", sdk.OneInt()) 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) 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}) bz := tmbytes.HexBytes([]byte{1, 2})
kvPairs := kv.Pairs{ kvPairs := kv.Pairs{
kv.Pair{Key: types.AtomicSwapKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(swap)}, 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: types.AtomicSwapByBlockPrefix, Value: bz}, kv.Pair{Key: types.AtomicSwapByBlockPrefix, Value: bz},
kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}},

View File

@ -16,21 +16,13 @@ import (
"github.com/kava-labs/kava/x/bep3/types" "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 ( var (
MaxSupplyLimit = sdk.NewInt(1000000000000) MaxSupplyLimit = 1000000000000
accs []simulation.Account MinSupplyLimit = 100000000
ConsistentDenoms = [3]string{"bnb", "xrp", "btc"} MinSwapAmountLimit = 999
accs []simulation.Account
ConsistentDenoms = [3]string{"bnb", "xrp", "btc"}
MinBlockLock = uint64(5)
) )
// GenRandBnbDeputy randomized BnbDeputyAddress // GenRandBnbDeputy randomized BnbDeputyAddress
@ -39,39 +31,48 @@ func GenRandBnbDeputy(r *rand.Rand) simulation.Account {
return acc return acc
} }
// GenRandBnbDeputyFixedFee randomized BnbDeputyFixedFee in range [2, 10000] // GenRandFixedFee randomized FixedFee in range [1, 10000]
func GenRandBnbDeputyFixedFee(r *rand.Rand) sdk.Int { func GenRandFixedFee(r *rand.Rand) sdk.Int {
min := int(1) min := int(1)
max := types.DefaultBnbDeputyFixedFee.Int64() max := types.DefaultBnbDeputyFixedFee.Int64()
return sdk.NewInt(int64(r.Intn(int(max)-min) + min)) return sdk.NewInt(int64(r.Intn(int(max)-min) + min))
} }
// GenMinAmount randomized MinAmount in range [0, 1000000000000] // GenMinSwapAmount randomized MinAmount in range [1, 1000]
func GenMinAmount(r *rand.Rand) sdk.Int { func GenMinSwapAmount(r *rand.Rand) sdk.Int {
min := types.DefaultMinAmount.Int64() return sdk.OneInt().Add(simulation.RandomAmount(r, sdk.NewInt(int64(MinSwapAmountLimit))))
max := types.DefaultMaxAmount.Int64() }
// 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)) return sdk.NewInt((int64(r.Intn(int(max-min))) + min))
} }
// GenMaxAmount randomized MaxAmount // GenSupplyLimit generates a random SupplyLimit
func GenMaxAmount(r *rand.Rand, minAmount sdk.Int) sdk.Int { func GenSupplyLimit(r *rand.Rand, max int) sdk.Int {
min := minAmount.Int64() max = simulation.RandIntBetween(r, MinSupplyLimit, max)
max := types.DefaultMaxAmount.Int64() return sdk.NewInt(int64(max))
return sdk.NewInt((int64(r.Intn(int(max-min))) + min)) }
// 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 // GenMinBlockLock randomized MinBlockLock
func GenMinBlockLock(r *rand.Rand) uint64 { func GenMinBlockLock(r *rand.Rand) uint64 {
min := int(1) return MinBlockLock
max := int(types.DefaultMaxBlockLock)
return uint64(r.Intn(max-min) + min)
} }
// GenMaxBlockLock randomized MaxBlockLock // GenMaxBlockLock randomized MaxBlockLock
func GenMaxBlockLock(r *rand.Rand, minBlockLock uint64) uint64 { func GenMaxBlockLock(r *rand.Rand, minBlockLock uint64) uint64 {
min := int(minBlockLock) max := int(50)
max := int(types.DefaultMaxBlockLock) return uint64(r.Intn(max-int(MinBlockLock)) + int(MinBlockLock+1))
return uint64(r.Intn(max-min) + min)
} }
// GenSupportedAssets gets randomized SupportedAssets // GenSupportedAssets gets randomized SupportedAssets
@ -92,12 +93,21 @@ func GenSupportedAssets(r *rand.Rand) types.AssetParams {
func genSupportedAsset(r *rand.Rand, denom string) types.AssetParam { func genSupportedAsset(r *rand.Rand, denom string) types.AssetParam {
coinID, _ := simulation.RandPositiveInt(r, sdk.NewInt(100000)) 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{ return types.AssetParam{
Denom: denom, Denom: denom,
CoinID: int(coinID.Int64()), CoinID: int(coinID.Int64()),
Limit: limit, SupplyLimit: limit,
Active: true, 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 { 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 supportedAssets := GenSupportedAssets(simState.Rand)
minBlockLock := uint64(50) supplies := types.AssetSupplies{}
maxBlockLock := minBlockLock * 2 for _, asset := range supportedAssets {
supply := GenAssetSupply(simState.Rand, asset.Denom)
var supportedAssets types.AssetParams supplies = append(supplies, supply)
simState.AppParams.GetOrGenerate( }
simState.Cdc, SupportedAssets, &supportedAssets, simState.Rand,
func(r *rand.Rand) { supportedAssets = GenSupportedAssets(r) },
)
bep3Genesis := types.GenesisState{ bep3Genesis := types.GenesisState{
Params: types.Params{ Params: types.Params{
BnbDeputyAddress: bnbDeputy.Address, AssetParams: supportedAssets,
BnbDeputyFixedFee: bnbDeputyFixedFee,
MinAmount: minAmount,
MaxAmount: maxAmount,
MinBlockLock: minBlockLock,
MaxBlockLock: maxBlockLock,
SupportedAssets: supportedAssets,
}, },
Supplies: supplies,
} }
return bep3Genesis 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) { func loadAuthGenState(simState *module.SimulationState, bep3Genesis types.GenesisState) (auth.GenesisState, []sdk.Coins) {
var authGenesis auth.GenesisState var authGenesis auth.GenesisState
simState.Cdc.MustUnmarshalJSON(simState.GenState[auth.ModuleName], &authGenesis) 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 // Load total limit of each supported asset to deputy's account
var totalCoins []sdk.Coins var totalCoins []sdk.Coins
for _, asset := range bep3Genesis.Params.SupportedAssets { for _, asset := range bep3Genesis.Params.AssetParams {
assetCoin := sdk.NewCoins(sdk.NewCoin(asset.Denom, asset.Limit)) 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 { if err := deputy.SetCoins(deputy.GetCoins().Add(assetCoin...)); err != nil {
panic(err) panic(err)
} }
totalCoins = append(totalCoins, assetCoin) totalCoins = append(totalCoins, assetCoin)
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, deputy)
} }
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, deputy)
return authGenesis, totalCoins return authGenesis, totalCoins
} }

View File

@ -16,7 +16,7 @@ import (
) )
var ( 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} 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( return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
) (simulation.OperationMsg, []simulation.FutureOperation, error) { ) (simulation.OperationMsg, []simulation.FutureOperation, error) {
// Get asset supplies and shuffle them
// Set up deputy address as it's required for all atomic swaps assets, found := k.GetAssets(ctx)
deputyAddr := k.GetBnbDeputyAddress(ctx) if !found {
deputyAcc, foundDeputy := simulation.FindAccount(accs, deputyAddr)
if !foundDeputy {
return noOpMsg, nil, nil return noOpMsg, nil, nil
} }
r.Shuffle(len(assets), func(i, j int) {
// Get asset supplies and shuffle them assets[i], assets[j] = assets[j], assets[i]
supplies := k.GetAllAssetSupplies(ctx)
r.Shuffle(len(supplies), func(i, j int) {
supplies[i], supplies[j] = supplies[j], supplies[i]
}) })
senderOutgoing, selectedAsset, found := findValidAccountAssetPair(accs, assets, func(simAcc simulation.Account, asset types.AssetParam) bool {
// Search for an account that holds coins received by an atomic swap supply, found := k.GetAssetSupply(ctx, asset.Denom)
minAmountPlusFee := k.GetMinAmount(ctx).Add(k.GetBnbDeputyFixedFee(ctx)) if !found {
senderOut, asset, found := findValidAccountAssetSupplyPair(accs, supplies, func(acc simulation.Account, asset types.AssetSupply) bool { return false
if asset.CurrentSupply.Amount.IsPositive() { }
authAcc := ak.GetAccount(ctx, acc.Address) 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) { if authAcc.SpendableCoins(ctx.BlockTime()).AmountOf(asset.Denom).GT(minAmountPlusFee) {
return true return true
} }
} }
return false return false
}) })
// Set sender, recipient, and denom depending on swap direction
var sender simulation.Account var sender simulation.Account
var recipient 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 an outgoing swap can be created, it's chosen 50% of the time.
if found && r.Intn(100) < 50 { if found && r.Intn(100) < 50 {
sender = senderOut deputy, found := simulation.FindAccount(accs, selectedAsset.DeputyAddress)
recipient = deputyAcc if !found {
denom = asset.Denom return noOpMsg, nil, nil
} 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")
} }
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) recipientOtherChain := simulation.RandStringOfLength(r, 43)
@ -111,32 +126,33 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
} }
// Get maximum valid amount // 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 // The maximum amount for outgoing swaps is limited by the asset's current supply
if recipient.Equals(deputyAcc) { if recipient.Address.Equals(asset.DeputyAddress) {
assetSupply, foundAssetSupply := k.GetAssetSupply(ctx, []byte(denom)) assetSupply, foundAssetSupply := k.GetAssetSupply(ctx, asset.Denom)
if !foundAssetSupply { 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) { if maximumAmount.GT(assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)) {
maximumAmount = assetSupply.CurrentSupply.Amount maximumAmount = assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)
} }
} }
// The maximum amount for all swaps is limited by the total max limit // The maximum amount for all swaps is limited by the total max limit
if maximumAmount.GT(k.GetMaxAmount(ctx)) { if maximumAmount.GT(asset.MaxSwapAmount) {
maximumAmount = k.GetMaxAmount(ctx) maximumAmount = asset.MaxSwapAmount
} }
// Get an amount of coins between 0.1 and 2% of total coins // Get an amount of coins between 0.1 and 2% of total coins
amount := maximumAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000)))) amount := maximumAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000))))
minAmountPlusFee := asset.MinSwapAmount.Add(asset.FixedFee)
if amount.LT(minAmountPlusFee) { 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 // 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( msg := types.NewMsgCreateAtomicSwap(
sender.Address, recipient.Address, recipientOtherChain, senderOtherChain, sender.Address, recipient.Address, recipientOtherChain, senderOtherChain,
@ -162,8 +178,10 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
var futureOp simulation.FutureOperation var futureOp simulation.FutureOperation
swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain) swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain)
if r.Intn(100) < 50 { if r.Intn(100) < 50 {
// Claim future operation // Claim future operation - choose between next block and the block before height span
executionBlock := uint64(ctx.BlockHeight()) + msg.HeightSpan/2 executionBlock := uint64(
int(ctx.BlockHeight()+1) + r.Intn(int(heightSpan-1)))
futureOp = simulation.FutureOperation{ futureOp = simulation.FutureOperation{
BlockHeight: int(executionBlock), BlockHeight: int(executionBlock),
Op: operationClaimAtomicSwap(ak, k, swapID, randomNumber), Op: operationClaimAtomicSwap(ak, k, swapID, randomNumber),
@ -188,13 +206,51 @@ func operationClaimAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID []
simAccount, _ := simulation.RandomAcc(r, accs) simAccount, _ := simulation.RandomAcc(r, accs)
acc := ak.GetAccount(ctx, simAccount.Address) 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())) fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime()))
if err != nil { if err != nil {
return simulation.NoOpMsg(types.ModuleName), nil, err return simulation.NoOpMsg(types.ModuleName), nil, err
} }
tx := helpers.GenTx( tx := helpers.GenTx(
[]sdk.Msg{msg}, []sdk.Msg{msg},
fees, fees,
@ -204,12 +260,10 @@ func operationClaimAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID []
[]uint64{acc.GetSequence()}, []uint64{acc.GetSequence()},
simAccount.PrivKey, simAccount.PrivKey,
) )
_, result, err := app.Deliver(tx) _, result, err := app.Deliver(tx)
if err != nil { if err != nil {
return simulation.NoOpMsg(types.ModuleName), nil, err return simulation.NoOpMsg(types.ModuleName), nil, err
} }
return simulation.NewOperationMsg(msg, true, result.Log), nil, nil 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) simAccount, _ := simulation.RandomAcc(r, accs)
acc := ak.GetAccount(ctx, simAccount.Address) 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) msg := types.NewMsgRefundAtomicSwap(acc.GetAddress(), swapID)
fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime())) 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 { if err != nil {
return simulation.NoOpMsg(types.ModuleName), nil, err return simulation.NoOpMsg(types.ModuleName), nil, err
} }
return simulation.NewOperationMsg(msg, true, result.Log), nil, nil return simulation.NewOperationMsg(msg, true, result.Log), nil, nil
} }
} }
// findValidAccountAssetSupplyPair finds an account for which the callback func returns true // findValidAccountAssetSupplyPair finds an account for which the callback func returns true
func findValidAccountAssetSupplyPair(accounts []simulation.Account, supplies types.AssetSupplies, func findValidAccountAssetPair(accounts []simulation.Account, assets types.AssetParams,
cb func(simulation.Account, types.AssetSupply) bool) (simulation.Account, types.AssetSupply, bool) { cb func(simulation.Account, types.AssetParam) bool) (simulation.Account, types.AssetParam, bool) {
for _, supply := range supplies { for _, asset := range assets {
for _, acc := range accounts { for _, acc := range accounts {
if isValid := cb(acc, supply); isValid { if isValid := cb(acc, asset); isValid {
return acc, supply, true return acc, asset, true
} }
} }
} }
return simulation.Account{}, types.AssetSupply{}, false return simulation.Account{}, types.AssetParam{}, false
} }

View File

@ -10,55 +10,15 @@ import (
) )
const ( const (
keyBnbDeputyAddress = "BnbDeputyAddress" keyAssetParams = "AssetParams"
keyBnbDeputyFixedFee = "BnbDeputyFixedFee"
keyMinAmount = "MinAmount"
keyMaxAmount = "MaxAmount"
keyMinBlockLock = "MinBlockLock"
keyMaxBlockLock = "MaxBlockLock"
keySupportedAssets = "SupportedAssets"
) )
// ParamChanges defines the parameters that can be modified by param change proposals // ParamChanges defines the parameters that can be modified by param change proposals
func ParamChanges(r *rand.Rand) []simulation.ParamChange { 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{ return []simulation.ParamChange{
simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyAddress, simulation.NewSimParamChange(types.ModuleName, keyAssetParams,
func(r *rand.Rand) string { func(r *rand.Rand) string {
return fmt.Sprintf("\"%s\"", GenRandBnbDeputy(r).Address) return fmt.Sprintf("\"%s\"", GenSupportedAssets(r))
},
),
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))
}, },
), ),
} }

View File

@ -14,7 +14,7 @@ var (
// ErrInsufficientAmount error for when a swap's amount cannot cover the deputy's fixed fee // 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") ErrInsufficientAmount = sdkerrors.Register(ModuleName, 4, "amount cannot cover the deputy fixed fee")
// ErrAssetNotSupported error for when an asset is not supported // 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 error for when an asset is currently inactive
ErrAssetNotActive = sdkerrors.Register(ModuleName, 6, "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 // 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") ErrSwapNotClaimable = sdkerrors.Register(ModuleName, 17, "atomic swap is not claimable")
// ErrInvalidAmount error for when a swap's amount is outside acceptable range // ErrInvalidAmount error for when a swap's amount is outside acceptable range
ErrInvalidAmount = sdkerrors.Register(ModuleName, 18, "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 // ErrInvalidSwapAccount error for when a swap involves an invalid account
ErrInvalidOutgoingAccount = sdkerrors.Register(ModuleName, 19, "invalid recipient address for outgoing swap") ErrInvalidSwapAccount = sdkerrors.Register(ModuleName, 19, "atomic swap has invalid account")
) )

View File

@ -8,17 +8,17 @@ import (
// GenesisState - all bep3 state that must be provided at genesis // GenesisState - all bep3 state that must be provided at genesis
type GenesisState struct { type GenesisState struct {
Params Params `json:"params" yaml:"params"` Params Params `json:"params" yaml:"params"`
AtomicSwaps AtomicSwaps `json:"atomic_swaps" yaml:"atomic_swaps"` AtomicSwaps AtomicSwaps `json:"atomic_swaps" yaml:"atomic_swaps"`
AssetSupplies AssetSupplies `json:"assets_supplies" yaml:"assets_supplies"` Supplies AssetSupplies `json:"supplies" yaml:"supplies"`
} }
// NewGenesisState creates a new GenesisState object // NewGenesisState creates a new GenesisState object
func NewGenesisState(params Params, swaps AtomicSwaps, supplies AssetSupplies) GenesisState { func NewGenesisState(params Params, swaps AtomicSwaps, supplies AssetSupplies) GenesisState {
return GenesisState{ return GenesisState{
Params: params, Params: params,
AtomicSwaps: swaps, AtomicSwaps: swaps,
AssetSupplies: supplies, Supplies: supplies,
} }
} }
@ -49,19 +49,6 @@ func (gs GenesisState) Validate() error {
return err 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{} ids := map[string]bool{}
for _, swap := range gs.AtomicSwaps { for _, swap := range gs.AtomicSwaps {
if ids[hex.EncodeToString(swap.GetSwapID())] { if ids[hex.EncodeToString(swap.GetSwapID())] {
@ -74,5 +61,16 @@ func (gs GenesisState) Validate() error {
ids[hex.EncodeToString(swap.GetSwapID())] = true 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 return nil
} }

View File

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

View File

@ -1,8 +1,6 @@
package types package types
import ( import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
@ -28,13 +26,10 @@ const (
// Key prefixes // Key prefixes
var ( 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 AtomicSwapKeyPrefix = []byte{0x00} // prefix for keys that store AtomicSwaps
AtomicSwapByBlockPrefix = []byte{0x01} // prefix for keys of the AtomicSwapsByBlock index AtomicSwapByBlockPrefix = []byte{0x01} // prefix for keys of the AtomicSwapsByBlock index
AssetSupplyKeyPrefix = []byte{0x02} // prefix for keys that store global asset supply counts AtomicSwapLongtermStoragePrefix = []byte{0x02} // prefix for keys of the AtomicSwapLongtermStorage index
AtomicSwapLongtermStoragePrefix = []byte{0x03} // prefix for keys of the AtomicSwapLongtermStorage index AssetSupplyPrefix = []byte{0x03}
) )
// GetAtomicSwapByHeightKey is used by the AtomicSwapByBlock index and AtomicSwapLongtermStorage index // GetAtomicSwapByHeightKey is used by the AtomicSwapByBlock index and AtomicSwapLongtermStorage index

View File

@ -1,100 +1,80 @@
package types package types
import ( import (
"errors"
"fmt" "fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/params"
) )
const (
bech32MainPrefix = "kava"
)
// Parameter keys // Parameter keys
var ( var (
KeyBnbDeputyAddress = []byte("BnbDeputyAddress") KeyAssetParams = []byte("AssetParams")
KeyBnbDeputyFixedFee = []byte("BnbDeputyFixedFee")
KeyMinAmount = []byte("MinAmount")
KeyMaxAmount = []byte("MaxAmount")
KeyMinBlockLock = []byte("MinBlockLock")
KeyMaxBlockLock = []byte("MaxBlockLock")
KeySupportedAssets = []byte("SupportedAssets")
DefaultBnbDeputyFixedFee sdk.Int = sdk.NewInt(1000) // 0.00001 BNB DefaultBnbDeputyFixedFee sdk.Int = sdk.NewInt(1000) // 0.00001 BNB
DefaultMinAmount sdk.Int = sdk.ZeroInt() DefaultMinAmount sdk.Int = sdk.ZeroInt()
DefaultMaxAmount sdk.Int = sdk.NewInt(1000000000000) // 10,000 BNB DefaultMaxAmount sdk.Int = sdk.NewInt(1000000000000) // 10,000 BNB
DefaultMinBlockLock uint64 = 220 DefaultMinBlockLock uint64 = 220
DefaultMaxBlockLock uint64 = 270 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 // Params governance parameters for bep3 module
type Params struct { type Params struct {
BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // Bnbchain deputy address AssetParams AssetParams `json:"asset_params" yaml:"asset_params"`
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
} }
// String implements fmt.Stringer // String implements fmt.Stringer
func (p Params) String() string { func (p Params) String() string {
return fmt.Sprintf(`Params: return fmt.Sprintf(`Params:
Bnbchain deputy address: %s, AssetParams: %s`,
Deputy fixed fee (BNB): %d, p.AssetParams)
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)
} }
// NewParams returns a new params object // NewParams returns a new params object
func NewParams(bnbDeputyAddress sdk.AccAddress, bnbDeputyFixedFee, minAmount, func NewParams(ap AssetParams,
maxAmount sdk.Int, minBlockLock, maxBlockLock uint64, supportedAssets AssetParams,
) Params { ) Params {
return Params{ return Params{
BnbDeputyAddress: bnbDeputyAddress, AssetParams: ap,
BnbDeputyFixedFee: bnbDeputyFixedFee,
MinAmount: minAmount,
MaxAmount: maxAmount,
MinBlockLock: minBlockLock,
MaxBlockLock: maxBlockLock,
SupportedAssets: supportedAssets,
} }
} }
// DefaultParams returns default params for bep3 module // DefaultParams returns default params for bep3 module
func DefaultParams() Params { func DefaultParams() Params {
defaultBnbDeputyAddress, err := sdk.AccAddressFromBech32("kava1r4v2zdhdalfj2ydazallqvrus9fkphmglhn6u6") return NewParams(AssetParams{})
if err != nil {
panic(err)
}
return NewParams(defaultBnbDeputyAddress, DefaultBnbDeputyFixedFee, DefaultMinAmount,
DefaultMaxAmount, DefaultMinBlockLock, DefaultMaxBlockLock, DefaultSupportedAssets)
} }
// AssetParam governance parameters for each asset within a supported chain // AssetParam parameters that must be specified for each bep3 asset
type AssetParam struct { type AssetParam struct {
Denom string `json:"denom" yaml:"denom"` // name of the asset Denom string `json:"denom" yaml:"denom"` // name of the asset
CoinID int `json:"coin_id" yaml:"coin_id"` // internationally recognized coin ID CoinID int `json:"coin_id" yaml:"coin_id"` // SLIP-0044 registered coin type - see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
Limit sdk.Int `json:"limit" yaml:"limit"` // asset supply limit 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 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 // String implements fmt.Stringer
@ -103,8 +83,15 @@ func (ap AssetParam) String() string {
Denom: %s Denom: %s
Coin ID: %d Coin ID: %d
Limit: %s Limit: %s
Active: %t`, Active: %t
ap.Denom, ap.CoinID, ap.Limit.String(), ap.Active) 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 // AssetParams array of AssetParam
@ -129,149 +116,69 @@ func ParamKeyTable() params.KeyTable {
// nolint // nolint
func (p *Params) ParamSetPairs() params.ParamSetPairs { func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{ return params.ParamSetPairs{
params.NewParamSetPair(KeyBnbDeputyAddress, &p.BnbDeputyAddress, validateBnbDeputyAddressParam), params.NewParamSetPair(KeyAssetParams, &p.AssetParams, validateAssetParams),
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),
} }
} }
// Validate ensure that params have valid values // Validate ensure that params have valid values
func (p Params) Validate() error { func (p Params) Validate() error {
if err := validateBnbDeputyAddressParam(p.BnbDeputyAddress); err != nil { return validateAssetParams(p.AssetParams)
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)
} }
func validateBnbDeputyAddressParam(i interface{}) error { func validateAssetParams(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 {
assetParams, ok := i.(AssetParams) assetParams, ok := i.(AssetParams)
if !ok { if !ok {
return fmt.Errorf("invalid parameter type: %T", i) return fmt.Errorf("invalid parameter type: %T", i)
} }
coinIDs := make(map[int]bool)
coinDenoms := make(map[string]bool) coinDenoms := make(map[string]bool)
for _, asset := range assetParams { for _, asset := range assetParams {
if strings.TrimSpace(asset.Denom) == "" { if err := sdk.ValidateDenom(asset.Denom); err != nil {
return errors.New("asset denom cannot be empty") return fmt.Errorf("asset denom invalid: %s", asset.Denom)
} }
if asset.CoinID < 0 { 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() { if asset.SupplyLimit.IsNegative() {
return fmt.Errorf(fmt.Sprintf("asset %s must have a positive supply limit", asset.Denom)) return fmt.Errorf("asset %s has invalid (negative) supply limit: %s", asset.Denom, asset.SupplyLimit)
} }
_, found := coinDenoms[asset.Denom] _, found := coinDenoms[asset.Denom]
if found { 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 coinDenoms[asset.Denom] = true
_, found = coinIDs[asset.CoinID] if asset.DeputyAddress.Empty() {
if found { return fmt.Errorf("deputy address cannot be empty for %s", asset.Denom)
return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate coin id %d", asset.Denom, asset.CoinID))
} }
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 return nil

View File

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

View File

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

View File

@ -9,21 +9,17 @@ import (
// AssetSupply contains information about an asset's supply // AssetSupply contains information about an asset's supply
type AssetSupply struct { type AssetSupply struct {
Denom string `json:"denom" yaml:"denom"`
IncomingSupply sdk.Coin `json:"incoming_supply" yaml:"incoming_supply"` IncomingSupply sdk.Coin `json:"incoming_supply" yaml:"incoming_supply"`
OutgoingSupply sdk.Coin `json:"outgoing_supply" yaml:"outgoing_supply"` OutgoingSupply sdk.Coin `json:"outgoing_supply" yaml:"outgoing_supply"`
CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"` CurrentSupply sdk.Coin `json:"current_supply" yaml:"current_supply"`
SupplyLimit sdk.Coin `json:"supply_limit" yaml:"supply_limit"`
} }
// NewAssetSupply initializes a new AssetSupply // 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{ return AssetSupply{
Denom: denom,
IncomingSupply: incomingSupply, IncomingSupply: incomingSupply,
OutgoingSupply: outgoingSupply, OutgoingSupply: outgoingSupply,
CurrentSupply: currentSupply, CurrentSupply: currentSupply,
SupplyLimit: supplyLimit,
} }
} }
@ -38,22 +34,35 @@ func (a AssetSupply) Validate() error {
if !a.CurrentSupply.IsValid() { if !a.CurrentSupply.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "current supply %s", a.CurrentSupply) return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "current supply %s", a.CurrentSupply)
} }
if !a.SupplyLimit.IsValid() { denom := a.CurrentSupply.Denom
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "supply limit %s", a.SupplyLimit) 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 // String implements stringer
func (a AssetSupply) String() string { func (a AssetSupply) String() string {
return fmt.Sprintf(` return fmt.Sprintf(`
%s supply: asset supply:
Incoming supply: %s Incoming supply: %s
Outgoing supply: %s Outgoing supply: %s
Current 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 // AssetSupplies is a slice of AssetSupply

View File

@ -17,7 +17,7 @@ func TestAssetSupplyValidate(t *testing.T) {
}{ }{
{ {
msg: "valid asset", msg: "valid asset",
asset: NewAssetSupply("kava", coin, coin, coin, coin), asset: NewAssetSupply(coin, coin, coin),
expPass: true, expPass: true,
}, },
{ {
@ -42,21 +42,6 @@ func TestAssetSupplyValidate(t *testing.T) {
}, },
false, 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 { for _, tc := range testCases {

View File

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

View File

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

View File

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