mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +00:00
Add rate limiting to bep3 assets (#623)
* feat: use only module account for bep3 txs * wip: add time-based supply limits * add tests and sims * update genesis tests * fix migration, committee tests * update migrations * fix: set previous block time in begin block * update store decoder * add additional bep3 params to committee * revert incorrect rebase changes * add migration test * address review comments
This commit is contained in:
parent
c0006ca8eb
commit
5fc85f10a6
@ -97,7 +97,7 @@ var (
|
|||||||
cdp.ModuleName: {supply.Minter, supply.Burner},
|
cdp.ModuleName: {supply.Minter, supply.Burner},
|
||||||
cdp.LiquidatorMacc: {supply.Minter, supply.Burner},
|
cdp.LiquidatorMacc: {supply.Minter, supply.Burner},
|
||||||
cdp.SavingsRateMacc: {supply.Minter},
|
cdp.SavingsRateMacc: {supply.Minter},
|
||||||
bep3.ModuleName: nil,
|
bep3.ModuleName: {supply.Minter, supply.Burner},
|
||||||
kavadist.ModuleName: {supply.Minter},
|
kavadist.ModuleName: {supply.Minter},
|
||||||
issuance.ModuleAccountName: {supply.Minter, supply.Burner},
|
issuance.ModuleAccountName: {supply.Minter, supply.Burner},
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package v0_11
|
package v0_11
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
v0_11bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_11"
|
v0_11bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_11"
|
||||||
@ -10,6 +12,7 @@ import (
|
|||||||
// MigrateBep3 migrates from a v0.9 (or v0.10) bep3 genesis state to a v0.11 bep3 genesis state
|
// 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 {
|
func MigrateBep3(oldGenState v0_9bep3.GenesisState) v0_11bep3.GenesisState {
|
||||||
var assetParams v0_11bep3.AssetParams
|
var assetParams v0_11bep3.AssetParams
|
||||||
|
var assetSupplies v0_11bep3.AssetSupplies
|
||||||
v0_9Params := oldGenState.Params
|
v0_9Params := oldGenState.Params
|
||||||
|
|
||||||
for _, asset := range v0_9Params.SupportedAssets {
|
for _, asset := range v0_9Params.SupportedAssets {
|
||||||
@ -19,28 +22,22 @@ func MigrateBep3(oldGenState v0_9bep3.GenesisState) v0_11bep3.GenesisState {
|
|||||||
CoinID: asset.CoinID,
|
CoinID: asset.CoinID,
|
||||||
DeputyAddress: v0_9Params.BnbDeputyAddress,
|
DeputyAddress: v0_9Params.BnbDeputyAddress,
|
||||||
FixedFee: v0_9Params.BnbDeputyFixedFee,
|
FixedFee: v0_9Params.BnbDeputyFixedFee,
|
||||||
MinSwapAmount: v0_9Params.MinAmount,
|
MinSwapAmount: sdk.OneInt(), // set min swap to one - prevents accounts that hold zero bnb from creating spam txs
|
||||||
MaxSwapAmount: v0_9Params.MaxAmount,
|
MaxSwapAmount: v0_9Params.MaxAmount,
|
||||||
MinBlockLock: v0_9Params.MinBlockLock,
|
MinBlockLock: v0_9Params.MinBlockLock,
|
||||||
MaxBlockLock: v0_9Params.MaxBlockLock,
|
MaxBlockLock: v0_9Params.MaxBlockLock,
|
||||||
SupplyLimit: v0_11bep3.AssetSupply{
|
SupplyLimit: v0_11bep3.SupplyLimit{
|
||||||
SupplyLimit: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
|
Limit: asset.Limit,
|
||||||
CurrentSupply: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
|
TimeLimited: false,
|
||||||
IncomingSupply: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
|
TimePeriod: time.Duration(0),
|
||||||
OutgoingSupply: sdk.NewCoin(asset.Denom, sdk.ZeroInt()),
|
TimeBasedLimit: sdk.ZeroInt(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assetParams = append(assetParams, v10AssetParam)
|
assetParams = append(assetParams, v10AssetParam)
|
||||||
}
|
}
|
||||||
for _, supply := range oldGenState.AssetSupplies {
|
for _, supply := range oldGenState.AssetSupplies {
|
||||||
for _, asset := range assetParams {
|
newSupply := v0_11bep3.NewAssetSupply(supply.IncomingSupply, supply.OutgoingSupply, supply.CurrentSupply, sdk.NewCoin(supply.CurrentSupply.Denom, sdk.ZeroInt()), time.Duration(0))
|
||||||
if asset.Denom == supply.Denom {
|
assetSupplies = append(assetSupplies, newSupply)
|
||||||
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
|
var swaps v0_11bep3.AtomicSwaps
|
||||||
for _, oldSwap := range oldGenState.AtomicSwaps {
|
for _, oldSwap := range oldGenState.AtomicSwaps {
|
||||||
@ -64,5 +61,7 @@ func MigrateBep3(oldGenState v0_9bep3.GenesisState) v0_11bep3.GenesisState {
|
|||||||
Params: v0_11bep3.Params{
|
Params: v0_11bep3.Params{
|
||||||
AssetParams: assetParams},
|
AssetParams: assetParams},
|
||||||
AtomicSwaps: swaps,
|
AtomicSwaps: swaps,
|
||||||
|
Supplies: assetSupplies,
|
||||||
|
PreviousBlockTime: v0_11bep3.DefaultPreviousBlockTime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
migrate/v0_11/migrate_test.go
Normal file
37
migrate/v0_11/migrate_test.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package v0_11
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
v0_9bep3 "github.com/kava-labs/kava/x/bep3/legacy/v0_9"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
config := sdk.GetConfig()
|
||||||
|
app.SetBech32AddressPrefixes(config)
|
||||||
|
app.SetBip44CoinType(config)
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMigrateBep3(t *testing.T) {
|
||||||
|
bz, err := ioutil.ReadFile(filepath.Join("testdata", "bep3-v09.json"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
var oldGenState v0_9bep3.GenesisState
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
require.NotPanics(t, func() {
|
||||||
|
cdc.MustUnmarshalJSON(bz, &oldGenState)
|
||||||
|
})
|
||||||
|
|
||||||
|
newGenState := MigrateBep3(oldGenState)
|
||||||
|
err = newGenState.Validate()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
13265
migrate/v0_11/testdata/bep3-v09.json
vendored
Normal file
13265
migrate/v0_11/testdata/bep3-v09.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ import (
|
|||||||
// BeginBlocker on every block expires outdated atomic swaps and removes closed
|
// BeginBlocker on every block expires outdated atomic swaps and removes closed
|
||||||
// swap from long term storage (default storage time of 1 week)
|
// swap from long term storage (default storage time of 1 week)
|
||||||
func BeginBlocker(ctx sdk.Context, k Keeper) {
|
func BeginBlocker(ctx sdk.Context, k Keeper) {
|
||||||
|
k.UpdateTimeBasedSupplyLimits(ctx)
|
||||||
k.UpdateExpiredAtomicSwaps(ctx)
|
k.UpdateExpiredAtomicSwaps(ctx)
|
||||||
k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx)
|
k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx)
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,7 @@ var (
|
|||||||
DefaultMaxAmount = types.DefaultMaxAmount
|
DefaultMaxAmount = types.DefaultMaxAmount
|
||||||
DefaultMinBlockLock = types.DefaultMinBlockLock
|
DefaultMinBlockLock = types.DefaultMinBlockLock
|
||||||
DefaultMaxBlockLock = types.DefaultMaxBlockLock
|
DefaultMaxBlockLock = types.DefaultMaxBlockLock
|
||||||
|
DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -141,6 +142,7 @@ type (
|
|||||||
AtomicSwaps = types.AtomicSwaps
|
AtomicSwaps = types.AtomicSwaps
|
||||||
SwapStatus = types.SwapStatus
|
SwapStatus = types.SwapStatus
|
||||||
SwapDirection = types.SwapDirection
|
SwapDirection = types.SwapDirection
|
||||||
|
SupplyLimit = types.SupplyLimit
|
||||||
AugmentedAtomicSwap = types.AugmentedAtomicSwap
|
AugmentedAtomicSwap = types.AugmentedAtomicSwap
|
||||||
AugmentedAtomicSwaps = types.AugmentedAtomicSwaps
|
AugmentedAtomicSwaps = types.AugmentedAtomicSwaps
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper
|
|||||||
panic(fmt.Sprintf("failed to validate %s genesis state: %s", ModuleName, err))
|
panic(fmt.Sprintf("failed to validate %s genesis state: %s", ModuleName, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keeper.SetPreviousBlockTime(ctx, gs.PreviousBlockTime)
|
||||||
|
|
||||||
keeper.SetParams(ctx, gs.Params)
|
keeper.SetParams(ctx, gs.Params)
|
||||||
for _, supply := range gs.Supplies {
|
for _, supply := range gs.Supplies {
|
||||||
keeper.SetAssetSupply(ctx, supply, supply.GetDenom())
|
keeper.SetAssetSupply(ctx, supply, supply.GetDenom())
|
||||||
@ -91,17 +93,17 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if supply.CurrentSupply.Amount.GT(limit) {
|
if supply.CurrentSupply.Amount.GT(limit.Limit) {
|
||||||
panic(fmt.Sprintf("asset's current supply %s is over the supply limit %s", supply.CurrentSupply, limit))
|
panic(fmt.Sprintf("asset's current supply %s is over the supply limit %s", supply.CurrentSupply, limit.Limit))
|
||||||
}
|
}
|
||||||
if supply.IncomingSupply.Amount.GT(limit) {
|
if supply.IncomingSupply.Amount.GT(limit.Limit) {
|
||||||
panic(fmt.Sprintf("asset's incoming supply %s is over the supply limit %s", supply.IncomingSupply, limit))
|
panic(fmt.Sprintf("asset's incoming supply %s is over the supply limit %s", supply.IncomingSupply, limit.Limit))
|
||||||
}
|
}
|
||||||
if supply.IncomingSupply.Amount.Add(supply.CurrentSupply.Amount).GT(limit) {
|
if supply.IncomingSupply.Amount.Add(supply.CurrentSupply.Amount).GT(limit.Limit) {
|
||||||
panic(fmt.Sprintf("asset's incoming supply + current supply %s is over the supply limit %s", supply.IncomingSupply.Add(supply.CurrentSupply), limit))
|
panic(fmt.Sprintf("asset's incoming supply + current supply %s is over the supply limit %s", supply.IncomingSupply.Add(supply.CurrentSupply), limit.Limit))
|
||||||
}
|
}
|
||||||
if supply.OutgoingSupply.Amount.GT(limit) {
|
if supply.OutgoingSupply.Amount.GT(limit.Limit) {
|
||||||
panic(fmt.Sprintf("asset's outgoing supply %s is over the supply limit %s", supply.OutgoingSupply, limit))
|
panic(fmt.Sprintf("asset's outgoing supply %s is over the supply limit %s", supply.OutgoingSupply, limit.Limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -112,5 +114,9 @@ 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)
|
||||||
supplies := k.GetAllAssetSupplies(ctx)
|
supplies := k.GetAllAssetSupplies(ctx)
|
||||||
return NewGenesisState(params, swaps, supplies)
|
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
|
||||||
|
if !found {
|
||||||
|
previousBlockTime = DefaultPreviousBlockTime
|
||||||
|
}
|
||||||
|
return NewGenesisState(params, swaps, supplies, previousBlockTime)
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
|
|||||||
bep3.AssetSupply{
|
bep3.AssetSupply{
|
||||||
IncomingSupply: c("bnb", 0),
|
IncomingSupply: c("bnb", 0),
|
||||||
OutgoingSupply: c("bnb", 0),
|
OutgoingSupply: c("bnb", 0),
|
||||||
CurrentSupply: c("bnb", assetParam.SupplyLimit.Add(i(1)).Int64()),
|
CurrentSupply: c("bnb", assetParam.SupplyLimit.Limit.Add(i(1)).Int64()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||||
@ -109,7 +109,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
|
|||||||
gs := baseGenState(suite.addrs[0])
|
gs := baseGenState(suite.addrs[0])
|
||||||
// Set up overlimit amount
|
// Set up overlimit amount
|
||||||
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
|
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
|
||||||
overLimitAmount := assetParam.SupplyLimit.Add(i(1))
|
overLimitAmount := assetParam.SupplyLimit.Limit.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,7 +124,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
|
|||||||
// Set up asset supply with overlimit current supply
|
// Set up asset supply with overlimit current supply
|
||||||
gs.Supplies = bep3.AssetSupplies{
|
gs.Supplies = bep3.AssetSupplies{
|
||||||
bep3.AssetSupply{
|
bep3.AssetSupply{
|
||||||
IncomingSupply: c("bnb", assetParam.SupplyLimit.Add(i(1)).Int64()),
|
IncomingSupply: c("bnb", assetParam.SupplyLimit.Limit.Add(i(1)).Int64()),
|
||||||
OutgoingSupply: c("bnb", 0),
|
OutgoingSupply: c("bnb", 0),
|
||||||
CurrentSupply: c("bnb", 0),
|
CurrentSupply: c("bnb", 0),
|
||||||
},
|
},
|
||||||
@ -139,7 +139,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
|
|||||||
gs := baseGenState(suite.addrs[0])
|
gs := baseGenState(suite.addrs[0])
|
||||||
// Set up overlimit amount
|
// Set up overlimit amount
|
||||||
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
|
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
|
||||||
halfLimit := assetParam.SupplyLimit.Int64() / 2
|
halfLimit := assetParam.SupplyLimit.Limit.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
|
||||||
@ -170,7 +170,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
|
|||||||
gs := baseGenState(suite.addrs[0])
|
gs := baseGenState(suite.addrs[0])
|
||||||
// Set up overlimit amount
|
// Set up overlimit amount
|
||||||
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
|
assetParam, _ := suite.keeper.GetAsset(suite.ctx, "bnb")
|
||||||
overLimitAmount := assetParam.SupplyLimit.Add(i(1))
|
overLimitAmount := assetParam.SupplyLimit.Limit.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)
|
||||||
@ -187,7 +187,7 @@ func (suite *GenesisTestSuite) TestGenesisState() {
|
|||||||
bep3.AssetSupply{
|
bep3.AssetSupply{
|
||||||
IncomingSupply: c("bnb", 0),
|
IncomingSupply: c("bnb", 0),
|
||||||
OutgoingSupply: c("bnb", 0),
|
OutgoingSupply: c("bnb", 0),
|
||||||
CurrentSupply: c("bnb", assetParam.SupplyLimit.Add(i(1)).Int64()),
|
CurrentSupply: c("bnb", assetParam.SupplyLimit.Limit.Add(i(1)).Int64()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||||
@ -266,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.AssetParams[0].SupplyLimit = i(-100)
|
gs.Params.AssetParams[0].SupplyLimit.Limit = i(-100)
|
||||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||||
},
|
},
|
||||||
expectPass: false,
|
expectPass: false,
|
||||||
|
@ -42,7 +42,12 @@ func baseGenState(deputy sdk.AccAddress) bep3.GenesisState {
|
|||||||
bep3.AssetParam{
|
bep3.AssetParam{
|
||||||
Denom: "bnb",
|
Denom: "bnb",
|
||||||
CoinID: 714,
|
CoinID: 714,
|
||||||
SupplyLimit: sdk.NewInt(350000000000000),
|
SupplyLimit: bep3.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(350000000000000),
|
||||||
|
TimeLimited: false,
|
||||||
|
TimeBasedLimit: sdk.ZeroInt(),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: deputy,
|
DeputyAddress: deputy,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -54,7 +59,12 @@ func baseGenState(deputy sdk.AccAddress) bep3.GenesisState {
|
|||||||
bep3.AssetParam{
|
bep3.AssetParam{
|
||||||
Denom: "inc",
|
Denom: "inc",
|
||||||
CoinID: 9999,
|
CoinID: 9999,
|
||||||
SupplyLimit: sdk.NewInt(100000000000),
|
SupplyLimit: bep3.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(100000000000),
|
||||||
|
TimeLimited: false,
|
||||||
|
TimeBasedLimit: sdk.ZeroInt(),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: deputy,
|
DeputyAddress: deputy,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -70,13 +80,18 @@ func baseGenState(deputy sdk.AccAddress) bep3.GenesisState {
|
|||||||
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
|
time.Duration(0),
|
||||||
),
|
),
|
||||||
bep3.NewAssetSupply(
|
bep3.NewAssetSupply(
|
||||||
sdk.NewCoin("inc", sdk.ZeroInt()),
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("inc", sdk.ZeroInt()),
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("inc", sdk.ZeroInt()),
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
|
time.Duration(0),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
PreviousBlockTime: bep3.DefaultPreviousBlockTime,
|
||||||
}
|
}
|
||||||
return bep3Genesis
|
return bep3Genesis
|
||||||
}
|
}
|
||||||
@ -92,7 +107,7 @@ func loadSwapAndSupply(addr sdk.AccAddress, index int) (bep3.AtomicSwap, bep3.As
|
|||||||
TestRecipientOtherChain, 1, bep3.Open, true, bep3.Incoming)
|
TestRecipientOtherChain, 1, bep3.Open, true, bep3.Incoming)
|
||||||
|
|
||||||
supply := bep3.NewAssetSupply(coin, c(coin.Denom, 0),
|
supply := bep3.NewAssetSupply(coin, c(coin.Denom, 0),
|
||||||
c(coin.Denom, 0))
|
c(coin.Denom, 0), c(coin.Denom, 0), time.Duration(0))
|
||||||
|
|
||||||
return swap, supply
|
return swap, supply
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
|
||||||
@ -18,13 +20,21 @@ func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
supplyLimit := sdk.NewCoin(coin.Denom, limit)
|
supplyLimit := sdk.NewCoin(coin.Denom, limit.Limit)
|
||||||
|
|
||||||
// Resulting current supply must be under asset's limit
|
// Resulting current supply must be under asset's limit
|
||||||
if 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, supplyLimit)
|
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supplyLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if limit.TimeLimited {
|
||||||
|
timeBasedSupplyLimit := sdk.NewCoin(coin.Denom, limit.TimeBasedLimit)
|
||||||
|
if timeBasedSupplyLimit.IsLT(supply.TimeLimitedCurrentSupply.Add(coin)) {
|
||||||
|
return sdkerrors.Wrapf(types.ErrExceedsTimeBasedSupplyLimit, "increase %s, current time-based asset supply %s, limit %s", coin, supply.TimeLimitedCurrentSupply, timeBasedSupplyLimit)
|
||||||
|
}
|
||||||
|
supply.TimeLimitedCurrentSupply = supply.TimeLimitedCurrentSupply.Add(coin)
|
||||||
|
}
|
||||||
|
|
||||||
supply.CurrentSupply = supply.CurrentSupply.Add(coin)
|
supply.CurrentSupply = supply.CurrentSupply.Add(coin)
|
||||||
k.SetAssetSupply(ctx, supply, coin.Denom)
|
k.SetAssetSupply(ctx, supply, coin.Denom)
|
||||||
return nil
|
return nil
|
||||||
@ -62,11 +72,19 @@ func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
supplyLimit := sdk.NewCoin(coin.Denom, limit)
|
supplyLimit := sdk.NewCoin(coin.Denom, limit.Limit)
|
||||||
if supplyLimit.IsLT(totalSupply.Add(coin)) {
|
if supplyLimit.IsLT(totalSupply.Add(coin)) {
|
||||||
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supplyLimit)
|
return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supplyLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if limit.TimeLimited {
|
||||||
|
timeLimitedTotalSupply := supply.TimeLimitedCurrentSupply.Add(supply.IncomingSupply)
|
||||||
|
timeBasedSupplyLimit := sdk.NewCoin(coin.Denom, limit.TimeBasedLimit)
|
||||||
|
if timeBasedSupplyLimit.IsLT(timeLimitedTotalSupply.Add(coin)) {
|
||||||
|
return sdkerrors.Wrapf(types.ErrExceedsTimeBasedSupplyLimit, "increase %s, time-based asset supply %s, limit %s", coin, supply.TimeLimitedCurrentSupply, timeBasedSupplyLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
supply.IncomingSupply = supply.IncomingSupply.Add(coin)
|
supply.IncomingSupply = supply.IncomingSupply.Add(coin)
|
||||||
k.SetAssetSupply(ctx, supply, coin.Denom)
|
k.SetAssetSupply(ctx, supply, coin.Denom)
|
||||||
return nil
|
return nil
|
||||||
@ -125,3 +143,42 @@ func (k Keeper) DecrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) err
|
|||||||
k.SetAssetSupply(ctx, supply, coin.Denom)
|
k.SetAssetSupply(ctx, supply, coin.Denom)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateNewAssetSupply creates a new AssetSupply in the store for the input denom
|
||||||
|
func (k Keeper) CreateNewAssetSupply(ctx sdk.Context, denom string) types.AssetSupply {
|
||||||
|
supply := types.NewAssetSupply(
|
||||||
|
sdk.NewCoin(denom, sdk.ZeroInt()), sdk.NewCoin(denom, sdk.ZeroInt()),
|
||||||
|
sdk.NewCoin(denom, sdk.ZeroInt()), sdk.NewCoin(denom, sdk.ZeroInt()), time.Duration(0))
|
||||||
|
k.SetAssetSupply(ctx, supply, denom)
|
||||||
|
return supply
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateTimeBasedSupplyLimits updates the time based supply for each asset, resetting it if the current time window has elapsed.
|
||||||
|
func (k Keeper) UpdateTimeBasedSupplyLimits(ctx sdk.Context) {
|
||||||
|
assets, found := k.GetAssets(ctx)
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
|
||||||
|
if !found {
|
||||||
|
previousBlockTime = ctx.BlockTime()
|
||||||
|
k.SetPreviousBlockTime(ctx, previousBlockTime)
|
||||||
|
}
|
||||||
|
timeElapsed := ctx.BlockTime().Sub(previousBlockTime)
|
||||||
|
for _, asset := range assets {
|
||||||
|
supply, found := k.GetAssetSupply(ctx, asset.Denom)
|
||||||
|
// if a new asset has been added by governance, create a new asset supply for it in the store
|
||||||
|
if !found {
|
||||||
|
supply = k.CreateNewAssetSupply(ctx, asset.Denom)
|
||||||
|
}
|
||||||
|
newTimeElapsed := supply.TimeElapsed + timeElapsed
|
||||||
|
if asset.SupplyLimit.TimeLimited && newTimeElapsed < asset.SupplyLimit.TimePeriod {
|
||||||
|
supply.TimeElapsed = newTimeElapsed
|
||||||
|
} else {
|
||||||
|
supply.TimeElapsed = time.Duration(0)
|
||||||
|
supply.TimeLimitedCurrentSupply = sdk.NewCoin(asset.Denom, sdk.ZeroInt())
|
||||||
|
}
|
||||||
|
k.SetAssetSupply(ctx, supply, asset.Denom)
|
||||||
|
}
|
||||||
|
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
|
||||||
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package keeper_test
|
package keeper_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
@ -37,15 +39,17 @@ func (suite *AssetTestSuite) SetupTest() {
|
|||||||
|
|
||||||
keeper := tApp.GetBep3Keeper()
|
keeper := tApp.GetBep3Keeper()
|
||||||
params := keeper.GetParams(ctx)
|
params := keeper.GetParams(ctx)
|
||||||
params.AssetParams[0].SupplyLimit = sdk.NewInt(50)
|
params.AssetParams[0].SupplyLimit.Limit = sdk.NewInt(50)
|
||||||
|
params.AssetParams[1].SupplyLimit.Limit = sdk.NewInt(100)
|
||||||
|
params.AssetParams[1].SupplyLimit.TimeBasedLimit = sdk.NewInt(15)
|
||||||
keeper.SetParams(ctx, params)
|
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.NewAssetSupply(c("bnb", 5), c("bnb", 5), c("bnb", 40), c("bnb", 0), time.Duration(0))
|
||||||
IncomingSupply: c("bnb", 5),
|
keeper.SetAssetSupply(ctx, supply, supply.IncomingSupply.Denom)
|
||||||
OutgoingSupply: c("bnb", 5),
|
|
||||||
CurrentSupply: c("bnb", 40),
|
supply = types.NewAssetSupply(c("inc", 10), c("inc", 5), c("inc", 5), c("inc", 0), time.Duration(0))
|
||||||
}
|
keeper.SetAssetSupply(ctx, supply, supply.IncomingSupply.Denom)
|
||||||
keeper.SetAssetSupply(ctx, supply, supply.GetDenom())
|
keeper.SetPreviousBlockTime(ctx, ctx.BlockTime())
|
||||||
|
|
||||||
suite.app = tApp
|
suite.app = tApp
|
||||||
suite.ctx = ctx
|
suite.ctx = ctx
|
||||||
@ -112,6 +116,64 @@ func (suite *AssetTestSuite) TestIncrementCurrentAssetSupply() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AssetTestSuite) TestIncrementTimeLimitedCurrentAssetSupply() {
|
||||||
|
type args struct {
|
||||||
|
coin sdk.Coin
|
||||||
|
expectedSupply types.AssetSupply
|
||||||
|
}
|
||||||
|
type errArgs struct {
|
||||||
|
expectPass bool
|
||||||
|
contains string
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
errArgs errArgs
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"normal",
|
||||||
|
args{
|
||||||
|
coin: c("inc", 5),
|
||||||
|
expectedSupply: types.AssetSupply{
|
||||||
|
IncomingSupply: c("inc", 10),
|
||||||
|
OutgoingSupply: c("inc", 5),
|
||||||
|
CurrentSupply: c("inc", 10),
|
||||||
|
TimeLimitedCurrentSupply: c("inc", 5),
|
||||||
|
TimeElapsed: time.Duration(0)},
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPass: true,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"over limit",
|
||||||
|
args{
|
||||||
|
coin: c("inc", 16),
|
||||||
|
expectedSupply: types.AssetSupply{},
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPass: false,
|
||||||
|
contains: "asset supply over limit for current time period",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
suite.SetupTest()
|
||||||
|
suite.Run(tc.name, func() {
|
||||||
|
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, tc.args.coin)
|
||||||
|
if tc.errArgs.expectPass {
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
supply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
|
||||||
|
suite.Require().Equal(tc.args.expectedSupply, supply)
|
||||||
|
} else {
|
||||||
|
suite.Require().Error(err)
|
||||||
|
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *AssetTestSuite) TestDecrementCurrentAssetSupply() {
|
func (suite *AssetTestSuite) TestDecrementCurrentAssetSupply() {
|
||||||
type args struct {
|
type args struct {
|
||||||
coin sdk.Coin
|
coin sdk.Coin
|
||||||
@ -229,6 +291,64 @@ func (suite *AssetTestSuite) TestIncrementIncomingAssetSupply() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AssetTestSuite) TestIncrementTimeLimitedIncomingAssetSupply() {
|
||||||
|
type args struct {
|
||||||
|
coin sdk.Coin
|
||||||
|
expectedSupply types.AssetSupply
|
||||||
|
}
|
||||||
|
type errArgs struct {
|
||||||
|
expectPass bool
|
||||||
|
contains string
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
errArgs errArgs
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"normal",
|
||||||
|
args{
|
||||||
|
coin: c("inc", 5),
|
||||||
|
expectedSupply: types.AssetSupply{
|
||||||
|
IncomingSupply: c("inc", 15),
|
||||||
|
OutgoingSupply: c("inc", 5),
|
||||||
|
CurrentSupply: c("inc", 5),
|
||||||
|
TimeLimitedCurrentSupply: c("inc", 0),
|
||||||
|
TimeElapsed: time.Duration(0)},
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPass: true,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"over limit",
|
||||||
|
args{
|
||||||
|
coin: c("inc", 6),
|
||||||
|
expectedSupply: types.AssetSupply{},
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPass: false,
|
||||||
|
contains: "asset supply over limit for current time period",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
suite.SetupTest()
|
||||||
|
suite.Run(tc.name, func() {
|
||||||
|
err := suite.keeper.IncrementIncomingAssetSupply(suite.ctx, tc.args.coin)
|
||||||
|
if tc.errArgs.expectPass {
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
supply, _ := suite.keeper.GetAssetSupply(suite.ctx, tc.args.coin.Denom)
|
||||||
|
suite.Require().Equal(tc.args.expectedSupply, supply)
|
||||||
|
} else {
|
||||||
|
suite.Require().Error(err)
|
||||||
|
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *AssetTestSuite) TestDecrementIncomingAssetSupply() {
|
func (suite *AssetTestSuite) TestDecrementIncomingAssetSupply() {
|
||||||
type args struct {
|
type args struct {
|
||||||
coin sdk.Coin
|
coin sdk.Coin
|
||||||
@ -405,6 +525,181 @@ func (suite *AssetTestSuite) TestDecrementOutgoingAssetSupply() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AssetTestSuite) TestUpdateTimeBasedSupplyLimits() {
|
||||||
|
type args struct {
|
||||||
|
asset string
|
||||||
|
duration time.Duration
|
||||||
|
expectedSupply types.AssetSupply
|
||||||
|
}
|
||||||
|
type errArgs struct {
|
||||||
|
expectPanic bool
|
||||||
|
contains string
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
errArgs errArgs
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"rate-limited increment time",
|
||||||
|
args{
|
||||||
|
asset: "inc",
|
||||||
|
duration: time.Second,
|
||||||
|
expectedSupply: types.NewAssetSupply(c("inc", 10), c("inc", 5), c("inc", 5), c("inc", 0), time.Second),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPanic: false,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate-limited increment time half",
|
||||||
|
args{
|
||||||
|
asset: "inc",
|
||||||
|
duration: time.Minute * 30,
|
||||||
|
expectedSupply: types.NewAssetSupply(c("inc", 10), c("inc", 5), c("inc", 5), c("inc", 0), time.Minute*30),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPanic: false,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate-limited period change",
|
||||||
|
args{
|
||||||
|
asset: "inc",
|
||||||
|
duration: time.Hour + time.Second,
|
||||||
|
expectedSupply: types.NewAssetSupply(c("inc", 10), c("inc", 5), c("inc", 5), c("inc", 0), time.Duration(0)),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPanic: false,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate-limited period change exact",
|
||||||
|
args{
|
||||||
|
asset: "inc",
|
||||||
|
duration: time.Hour,
|
||||||
|
expectedSupply: types.NewAssetSupply(c("inc", 10), c("inc", 5), c("inc", 5), c("inc", 0), time.Duration(0)),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPanic: false,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rate-limited period change big",
|
||||||
|
args{
|
||||||
|
asset: "inc",
|
||||||
|
duration: time.Hour * 4,
|
||||||
|
expectedSupply: types.NewAssetSupply(c("inc", 10), c("inc", 5), c("inc", 5), c("inc", 0), time.Duration(0)),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPanic: false,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"non rate-limited increment time",
|
||||||
|
args{
|
||||||
|
asset: "bnb",
|
||||||
|
duration: time.Second,
|
||||||
|
expectedSupply: types.NewAssetSupply(c("bnb", 5), c("bnb", 5), c("bnb", 40), c("bnb", 0), time.Duration(0)),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPanic: false,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"new asset increment time",
|
||||||
|
args{
|
||||||
|
asset: "lol",
|
||||||
|
duration: time.Second,
|
||||||
|
expectedSupply: types.NewAssetSupply(c("lol", 0), c("lol", 0), c("lol", 0), c("lol", 0), time.Second),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPanic: false,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
suite.SetupTest()
|
||||||
|
suite.Run(tc.name, func() {
|
||||||
|
deputy, _ := sdk.AccAddressFromBech32(TestDeputy)
|
||||||
|
newParams := types.Params{
|
||||||
|
AssetParams: types.AssetParams{
|
||||||
|
types.AssetParam{
|
||||||
|
Denom: "bnb",
|
||||||
|
CoinID: 714,
|
||||||
|
SupplyLimit: types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(350000000000000),
|
||||||
|
TimeLimited: false,
|
||||||
|
TimeBasedLimit: sdk.ZeroInt(),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
|
Active: true,
|
||||||
|
DeputyAddress: deputy,
|
||||||
|
FixedFee: sdk.NewInt(1000),
|
||||||
|
MinSwapAmount: sdk.OneInt(),
|
||||||
|
MaxSwapAmount: sdk.NewInt(1000000000000),
|
||||||
|
MinBlockLock: types.DefaultMinBlockLock,
|
||||||
|
MaxBlockLock: types.DefaultMaxBlockLock,
|
||||||
|
},
|
||||||
|
types.AssetParam{
|
||||||
|
Denom: "inc",
|
||||||
|
CoinID: 9999,
|
||||||
|
SupplyLimit: types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(100),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(10),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
|
Active: false,
|
||||||
|
DeputyAddress: deputy,
|
||||||
|
FixedFee: sdk.NewInt(1000),
|
||||||
|
MinSwapAmount: sdk.OneInt(),
|
||||||
|
MaxSwapAmount: sdk.NewInt(1000000000000),
|
||||||
|
MinBlockLock: types.DefaultMinBlockLock,
|
||||||
|
MaxBlockLock: types.DefaultMaxBlockLock,
|
||||||
|
},
|
||||||
|
types.AssetParam{
|
||||||
|
Denom: "lol",
|
||||||
|
CoinID: 9999,
|
||||||
|
SupplyLimit: types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(100),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(10),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
|
Active: false,
|
||||||
|
DeputyAddress: deputy,
|
||||||
|
FixedFee: sdk.NewInt(1000),
|
||||||
|
MinSwapAmount: sdk.OneInt(),
|
||||||
|
MaxSwapAmount: sdk.NewInt(1000000000000),
|
||||||
|
MinBlockLock: types.DefaultMinBlockLock,
|
||||||
|
MaxBlockLock: types.DefaultMaxBlockLock,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
suite.keeper.SetParams(suite.ctx, newParams)
|
||||||
|
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(tc.args.duration))
|
||||||
|
suite.NotPanics(
|
||||||
|
func() {
|
||||||
|
suite.keeper.UpdateTimeBasedSupplyLimits(suite.ctx)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if !tc.errArgs.expectPanic {
|
||||||
|
supply, found := suite.keeper.GetAssetSupply(suite.ctx, tc.args.asset)
|
||||||
|
suite.Require().True(found)
|
||||||
|
suite.Require().Equal(tc.args.expectedSupply, supply)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAssetTestSuite(t *testing.T) {
|
func TestAssetTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(AssetTestSuite))
|
suite.Run(t, new(AssetTestSuite))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
tmtime "github.com/tendermint/tendermint/types/time"
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
|
||||||
"github.com/kava-labs/kava/app"
|
"github.com/kava-labs/kava/app"
|
||||||
"github.com/kava-labs/kava/x/bep3"
|
|
||||||
"github.com/kava-labs/kava/x/bep3/types"
|
"github.com/kava-labs/kava/x/bep3/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,12 +31,17 @@ 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: types.Params{
|
||||||
AssetParams: types.AssetParams{
|
AssetParams: types.AssetParams{
|
||||||
types.AssetParam{
|
types.AssetParam{
|
||||||
Denom: "bnb",
|
Denom: "bnb",
|
||||||
CoinID: 714,
|
CoinID: 714,
|
||||||
SupplyLimit: sdk.NewInt(350000000000000),
|
SupplyLimit: types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(350000000000000),
|
||||||
|
TimeLimited: false,
|
||||||
|
TimeBasedLimit: sdk.ZeroInt(),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: deputyAddress,
|
DeputyAddress: deputyAddress,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -49,31 +53,41 @@ func NewBep3GenStateMulti(deputyAddress sdk.AccAddress) app.GenesisState {
|
|||||||
types.AssetParam{
|
types.AssetParam{
|
||||||
Denom: "inc",
|
Denom: "inc",
|
||||||
CoinID: 9999,
|
CoinID: 9999,
|
||||||
SupplyLimit: sdk.NewInt(100),
|
SupplyLimit: types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(100000000000000),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(50000000000),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: false,
|
Active: false,
|
||||||
DeputyAddress: deputyAddress,
|
DeputyAddress: deputyAddress,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
MinSwapAmount: sdk.OneInt(),
|
MinSwapAmount: sdk.OneInt(),
|
||||||
MaxSwapAmount: sdk.NewInt(1000000000000),
|
MaxSwapAmount: sdk.NewInt(100000000000),
|
||||||
MinBlockLock: types.DefaultMinBlockLock,
|
MinBlockLock: types.DefaultMinBlockLock,
|
||||||
MaxBlockLock: types.DefaultMaxBlockLock,
|
MaxBlockLock: types.DefaultMaxBlockLock,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Supplies: bep3.AssetSupplies{
|
Supplies: types.AssetSupplies{
|
||||||
bep3.NewAssetSupply(
|
types.NewAssetSupply(
|
||||||
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
|
sdk.NewCoin("bnb", sdk.ZeroInt()),
|
||||||
|
time.Duration(0),
|
||||||
),
|
),
|
||||||
bep3.NewAssetSupply(
|
types.NewAssetSupply(
|
||||||
sdk.NewCoin("inc", sdk.ZeroInt()),
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("inc", sdk.ZeroInt()),
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
sdk.NewCoin("inc", sdk.ZeroInt()),
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
|
sdk.NewCoin("inc", sdk.ZeroInt()),
|
||||||
|
time.Duration(0),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
PreviousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
}
|
}
|
||||||
return app.GenesisState{bep3.ModuleName: bep3.ModuleCdc.MustMarshalJSON(bep3Genesis)}
|
return app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(bep3Genesis)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func atomicSwaps(ctx sdk.Context, count int) types.AtomicSwaps {
|
func atomicSwaps(ctx sdk.Context, count int) types.AtomicSwaps {
|
||||||
@ -112,5 +126,5 @@ func assetSupplies(count int) types.AssetSupplies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assetSupply(denom string) types.AssetSupply {
|
func assetSupply(denom string) types.AssetSupply {
|
||||||
return types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 0))
|
return types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 0), c(denom, 0), time.Duration(0))
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package keeper
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||||
@ -222,3 +223,20 @@ func (k Keeper) GetAllAssetSupplies(ctx sdk.Context) (supplies types.AssetSuppli
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPreviousBlockTime get the blocktime for the previous block
|
||||||
|
func (k Keeper) GetPreviousBlockTime(ctx sdk.Context) (blockTime time.Time, found bool) {
|
||||||
|
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousBlockTimeKey)
|
||||||
|
b := store.Get([]byte{})
|
||||||
|
if b == nil {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &blockTime)
|
||||||
|
return blockTime, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPreviousBlockTime set the time of the previous block
|
||||||
|
func (k Keeper) SetPreviousBlockTime(ctx sdk.Context, blockTime time.Time) {
|
||||||
|
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousBlockTimeKey)
|
||||||
|
store.Set([]byte{}, k.cdc.MustMarshalBinaryLengthPrefixed(blockTime))
|
||||||
|
}
|
||||||
|
@ -310,7 +310,7 @@ func (suite *KeeperTestSuite) TestIterateAtomicSwapsLongtermStorage() {
|
|||||||
func (suite *KeeperTestSuite) TestGetSetAssetSupply() {
|
func (suite *KeeperTestSuite) TestGetSetAssetSupply() {
|
||||||
denom := "bnb"
|
denom := "bnb"
|
||||||
// Put asset supply in store
|
// Put asset supply in store
|
||||||
assetSupply := types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 50000))
|
assetSupply := types.NewAssetSupply(c(denom, 0), c(denom, 0), c(denom, 50000), c(denom, 0), time.Duration(0))
|
||||||
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, denom)
|
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, denom)
|
||||||
|
|
||||||
// Check asset in store
|
// Check asset in store
|
||||||
@ -327,9 +327,9 @@ func (suite *KeeperTestSuite) TestGetSetAssetSupply() {
|
|||||||
func (suite *KeeperTestSuite) TestGetAllAssetSupplies() {
|
func (suite *KeeperTestSuite) TestGetAllAssetSupplies() {
|
||||||
|
|
||||||
// Put asset supply in store
|
// Put asset supply in store
|
||||||
assetSupply := types.NewAssetSupply(c("bnb", 0), c("bnb", 0), c("bnb", 50000))
|
assetSupply := types.NewAssetSupply(c("bnb", 0), c("bnb", 0), c("bnb", 50000), c("bnb", 0), time.Duration(0))
|
||||||
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "bnb")
|
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "bnb")
|
||||||
assetSupply = types.NewAssetSupply(c("inc", 0), c("inc", 0), c("inc", 50000))
|
assetSupply = types.NewAssetSupply(c("inc", 0), c("inc", 0), c("inc", 50000), c("inc", 0), time.Duration(0))
|
||||||
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "inc")
|
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, "inc")
|
||||||
|
|
||||||
supplies := suite.keeper.GetAllAssetSupplies(suite.ctx)
|
supplies := suite.keeper.GetAllAssetSupplies(suite.ctx)
|
||||||
|
@ -132,10 +132,10 @@ func (k Keeper) ValidateLiveAsset(ctx sdk.Context, coin sdk.Coin) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSupplyLimit returns the supply limit for the input denom
|
// GetSupplyLimit returns the supply limit for the input denom
|
||||||
func (k Keeper) GetSupplyLimit(ctx sdk.Context, denom string) (sdk.Int, error) {
|
func (k Keeper) GetSupplyLimit(ctx sdk.Context, denom string) (types.SupplyLimit, error) {
|
||||||
asset, err := k.GetAsset(ctx, denom)
|
asset, err := k.GetAsset(ctx, denom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.Int{}, err
|
return types.SupplyLimit{}, err
|
||||||
}
|
}
|
||||||
return asset.SupplyLimit, nil
|
return asset.SupplyLimit, nil
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ func (suite *QuerierTestSuite) TestQueryAssetSupply() {
|
|||||||
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &supply))
|
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &supply))
|
||||||
|
|
||||||
expectedSupply := types.NewAssetSupply(c(denom, 1000),
|
expectedSupply := types.NewAssetSupply(c(denom, 1000),
|
||||||
c(denom, 0), c(denom, 0))
|
c(denom, 0), c(denom, 0), c(denom, 0), time.Duration(0))
|
||||||
suite.Equal(supply, expectedSupply)
|
suite.Equal(supply, expectedSupply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times
|
|||||||
err = k.IncrementIncomingAssetSupply(ctx, amount[0])
|
err = k.IncrementIncomingAssetSupply(ctx, amount[0])
|
||||||
case types.Outgoing:
|
case types.Outgoing:
|
||||||
|
|
||||||
// Outoing swaps must have a height span within the accepted range
|
// Outgoing swaps must have a height span within the accepted range
|
||||||
if heightSpan < asset.MinBlockLock || heightSpan > asset.MaxBlockLock {
|
if heightSpan < asset.MinBlockLock || heightSpan > asset.MaxBlockLock {
|
||||||
return sdkerrors.Wrapf(types.ErrInvalidHeightSpan, "height span %d outside range [%d, %d]", heightSpan, asset.MinBlockLock, asset.MaxBlockLock)
|
return sdkerrors.Wrapf(types.ErrInvalidHeightSpan, "height span %d outside range [%d, %d]", heightSpan, asset.MinBlockLock, asset.MaxBlockLock)
|
||||||
}
|
}
|
||||||
@ -88,15 +88,14 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times
|
|||||||
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])
|
||||||
default:
|
|
||||||
err = fmt.Errorf("invalid swap direction: %s", direction.String())
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Transfer coins to module - only needed for outgoing swaps
|
||||||
// Transfer coins to module
|
|
||||||
err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount)
|
err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("invalid swap direction: %s", direction.String())
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -161,6 +160,16 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// incoming case - coins should be MINTED, then sent to user
|
||||||
|
err = k.supplyKeeper.MintCoins(ctx, types.ModuleName, atomicSwap.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Send intended recipient coins
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Recipient, atomicSwap.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
case types.Outgoing:
|
case types.Outgoing:
|
||||||
err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0])
|
err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -170,15 +179,14 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
// outgoing case - coins should be burned
|
||||||
return fmt.Errorf("invalid swap direction: %s", atomicSwap.Direction.String())
|
err = k.supplyKeeper.BurnCoins(ctx, types.ModuleName, atomicSwap.Amount)
|
||||||
}
|
|
||||||
|
|
||||||
// Send intended recipient coins
|
|
||||||
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Recipient, atomicSwap.Amount)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid swap direction: %s", atomicSwap.Direction.String())
|
||||||
|
}
|
||||||
|
|
||||||
// Complete swap
|
// Complete swap
|
||||||
atomicSwap.Status = types.Completed
|
atomicSwap.Status = types.Completed
|
||||||
@ -221,6 +229,11 @@ func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []
|
|||||||
err = k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0])
|
err = k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0])
|
||||||
case types.Outgoing:
|
case types.Outgoing:
|
||||||
err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0])
|
err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Refund coins to original swap sender for outgoing swaps
|
||||||
|
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Sender, atomicSwap.Amount)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("invalid swap direction: %s", atomicSwap.Direction.String())
|
err = fmt.Errorf("invalid swap direction: %s", atomicSwap.Direction.String())
|
||||||
}
|
}
|
||||||
@ -229,12 +242,6 @@ func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refund coins to original swap sender
|
|
||||||
err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Sender, atomicSwap.Amount)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complete swap
|
// Complete swap
|
||||||
atomicSwap.Status = types.Completed
|
atomicSwap.Status = types.Completed
|
||||||
atomicSwap.ClosedBlock = ctx.BlockHeight()
|
atomicSwap.ClosedBlock = ctx.BlockHeight()
|
||||||
|
@ -36,6 +36,8 @@ type AtomicSwapTestSuite struct {
|
|||||||
const (
|
const (
|
||||||
STARING_BNB_BALANCE = int64(3000000000000)
|
STARING_BNB_BALANCE = int64(3000000000000)
|
||||||
BNB_DENOM = "bnb"
|
BNB_DENOM = "bnb"
|
||||||
|
OTHER_DENOM = "inc"
|
||||||
|
STARING_OTHER_BALANCE = int64(3000000000000)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (suite *AtomicSwapTestSuite) SetupTest() {
|
func (suite *AtomicSwapTestSuite) SetupTest() {
|
||||||
@ -49,7 +51,7 @@ func (suite *AtomicSwapTestSuite) SetupTest() {
|
|||||||
// Create and load 20 accounts with bnb tokens
|
// Create and load 20 accounts with bnb tokens
|
||||||
coins := []sdk.Coins{}
|
coins := []sdk.Coins{}
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
coins = append(coins, cs(c(BNB_DENOM, STARING_BNB_BALANCE)))
|
coins = append(coins, cs(c(BNB_DENOM, STARING_BNB_BALANCE), c(OTHER_DENOM, STARING_OTHER_BALANCE)))
|
||||||
}
|
}
|
||||||
_, addrs := app.GeneratePrivKeyAddressPairs(20)
|
_, addrs := app.GeneratePrivKeyAddressPairs(20)
|
||||||
deputy := addrs[0]
|
deputy := addrs[0]
|
||||||
@ -58,11 +60,16 @@ func (suite *AtomicSwapTestSuite) SetupTest() {
|
|||||||
// Initialize genesis state
|
// Initialize genesis state
|
||||||
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(deputy))
|
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(deputy))
|
||||||
|
|
||||||
|
keeper := tApp.GetBep3Keeper()
|
||||||
|
params := keeper.GetParams(ctx)
|
||||||
|
params.AssetParams[1].Active = true
|
||||||
|
keeper.SetParams(ctx, params)
|
||||||
|
|
||||||
suite.app = tApp
|
suite.app = tApp
|
||||||
suite.ctx = ctx
|
suite.ctx = ctx
|
||||||
suite.deputy = deputy
|
suite.deputy = deputy
|
||||||
suite.addrs = addrs
|
suite.addrs = addrs
|
||||||
suite.keeper = suite.app.GetBep3Keeper()
|
suite.keeper = keeper
|
||||||
|
|
||||||
// Load a random module account to test blacklisting
|
// Load a random module account to test blacklisting
|
||||||
i := 0
|
i := 0
|
||||||
@ -138,6 +145,42 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"incoming swap rate limited",
|
||||||
|
currentTmTime.Add(time.Minute * 10),
|
||||||
|
args{
|
||||||
|
randomNumberHash: suite.randomNumberHashes[12],
|
||||||
|
timestamp: suite.timestamps[12],
|
||||||
|
heightSpan: types.DefaultMinBlockLock,
|
||||||
|
sender: suite.deputy,
|
||||||
|
recipient: suite.addrs[1],
|
||||||
|
senderOtherChain: TestSenderOtherChain,
|
||||||
|
recipientOtherChain: TestRecipientOtherChain,
|
||||||
|
coins: cs(c("inc", 50000000000)),
|
||||||
|
crossChain: true,
|
||||||
|
direction: types.Incoming,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"incoming swap over rate limit",
|
||||||
|
currentTmTime.Add(time.Minute * 10),
|
||||||
|
args{
|
||||||
|
randomNumberHash: suite.randomNumberHashes[13],
|
||||||
|
timestamp: suite.timestamps[13],
|
||||||
|
heightSpan: types.DefaultMinBlockLock,
|
||||||
|
sender: suite.deputy,
|
||||||
|
recipient: suite.addrs[1],
|
||||||
|
senderOtherChain: TestSenderOtherChain,
|
||||||
|
recipientOtherChain: TestRecipientOtherChain,
|
||||||
|
coins: cs(c("inc", 50000000001)),
|
||||||
|
crossChain: true,
|
||||||
|
direction: types.Incoming,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"outgoing swap",
|
"outgoing swap",
|
||||||
currentTmTime,
|
currentTmTime,
|
||||||
@ -396,14 +439,13 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
if tc.expectPass {
|
if tc.expectPass {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// Check coins moved
|
|
||||||
suite.Equal(senderBalancePre.Sub(tc.args.coins[0].Amount), senderBalancePost)
|
|
||||||
|
|
||||||
// Check incoming/outgoing asset supply increased
|
// Check incoming/outgoing asset supply increased
|
||||||
switch tc.args.direction {
|
switch tc.args.direction {
|
||||||
case types.Incoming:
|
case types.Incoming:
|
||||||
suite.Equal(assetSupplyPre.IncomingSupply.Add(tc.args.coins[0]), assetSupplyPost.IncomingSupply)
|
suite.Equal(assetSupplyPre.IncomingSupply.Add(tc.args.coins[0]), assetSupplyPost.IncomingSupply)
|
||||||
case types.Outgoing:
|
case types.Outgoing:
|
||||||
|
// Check coins moved
|
||||||
|
suite.Equal(senderBalancePre.Sub(tc.args.coins[0].Amount), senderBalancePost)
|
||||||
suite.Equal(assetSupplyPre.OutgoingSupply.Add(tc.args.coins[0]), assetSupplyPost.OutgoingSupply)
|
suite.Equal(assetSupplyPre.OutgoingSupply.Add(tc.args.coins[0]), assetSupplyPost.OutgoingSupply)
|
||||||
default:
|
default:
|
||||||
suite.Fail("should not have invalid direction")
|
suite.Fail("should not have invalid direction")
|
||||||
@ -460,8 +502,10 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
|
|
||||||
func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
||||||
suite.SetupTest()
|
suite.SetupTest()
|
||||||
|
currentTmTime := tmtime.Now()
|
||||||
invalidRandomNumber, _ := types.GenerateSecureRandomNumber()
|
invalidRandomNumber, _ := types.GenerateSecureRandomNumber()
|
||||||
type args struct {
|
type args struct {
|
||||||
|
coins sdk.Coins
|
||||||
swapID []byte
|
swapID []byte
|
||||||
randomNumber []byte
|
randomNumber []byte
|
||||||
direction types.SwapDirection
|
direction types.SwapDirection
|
||||||
@ -476,6 +520,18 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
|||||||
"normal incoming swap",
|
"normal incoming swap",
|
||||||
suite.ctx,
|
suite.ctx,
|
||||||
args{
|
args{
|
||||||
|
coins: cs(c(BNB_DENOM, 50000)),
|
||||||
|
swapID: []byte{},
|
||||||
|
randomNumber: []byte{},
|
||||||
|
direction: types.Incoming,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"normal incoming swap rate-limited",
|
||||||
|
suite.ctx.WithBlockTime(currentTmTime.Add(time.Minute * 10)),
|
||||||
|
args{
|
||||||
|
coins: cs(c(OTHER_DENOM, 50000)),
|
||||||
swapID: []byte{},
|
swapID: []byte{},
|
||||||
randomNumber: []byte{},
|
randomNumber: []byte{},
|
||||||
direction: types.Incoming,
|
direction: types.Incoming,
|
||||||
@ -486,6 +542,7 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
|||||||
"normal outgoing swap",
|
"normal outgoing swap",
|
||||||
suite.ctx,
|
suite.ctx,
|
||||||
args{
|
args{
|
||||||
|
coins: cs(c(BNB_DENOM, 50000)),
|
||||||
swapID: []byte{},
|
swapID: []byte{},
|
||||||
randomNumber: []byte{},
|
randomNumber: []byte{},
|
||||||
direction: types.Outgoing,
|
direction: types.Outgoing,
|
||||||
@ -496,6 +553,7 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
|||||||
"invalid random number",
|
"invalid random number",
|
||||||
suite.ctx,
|
suite.ctx,
|
||||||
args{
|
args{
|
||||||
|
coins: cs(c(BNB_DENOM, 50000)),
|
||||||
swapID: []byte{},
|
swapID: []byte{},
|
||||||
randomNumber: invalidRandomNumber[:],
|
randomNumber: invalidRandomNumber[:],
|
||||||
direction: types.Incoming,
|
direction: types.Incoming,
|
||||||
@ -506,6 +564,7 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
|||||||
"wrong swap ID",
|
"wrong swap ID",
|
||||||
suite.ctx,
|
suite.ctx,
|
||||||
args{
|
args{
|
||||||
|
coins: cs(c(BNB_DENOM, 50000)),
|
||||||
swapID: types.CalculateSwapID(suite.randomNumberHashes[3], suite.addrs[6], TestRecipientOtherChain),
|
swapID: types.CalculateSwapID(suite.randomNumberHashes[3], suite.addrs[6], TestRecipientOtherChain),
|
||||||
randomNumber: []byte{},
|
randomNumber: []byte{},
|
||||||
direction: types.Outgoing,
|
direction: types.Outgoing,
|
||||||
@ -516,6 +575,7 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
|||||||
"past expiration",
|
"past expiration",
|
||||||
suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 2000),
|
suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 2000),
|
||||||
args{
|
args{
|
||||||
|
coins: cs(c(BNB_DENOM, 50000)),
|
||||||
swapID: []byte{},
|
swapID: []byte{},
|
||||||
randomNumber: []byte{},
|
randomNumber: []byte{},
|
||||||
direction: types.Incoming,
|
direction: types.Incoming,
|
||||||
@ -528,21 +588,20 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
|||||||
suite.GenerateSwapDetails()
|
suite.GenerateSwapDetails()
|
||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
expectedRecipient := suite.addrs[5]
|
expectedRecipient := suite.addrs[5]
|
||||||
expectedClaimAmount := cs(c(BNB_DENOM, 50000))
|
|
||||||
sender := suite.deputy
|
sender := suite.deputy
|
||||||
|
|
||||||
// Set sender to other and increment current asset supply for outgoing swap
|
// Set sender to other and increment current asset supply for outgoing swap
|
||||||
if tc.args.direction == types.Outgoing {
|
if tc.args.direction == types.Outgoing {
|
||||||
sender = suite.addrs[6]
|
sender = suite.addrs[6]
|
||||||
expectedRecipient = suite.deputy
|
expectedRecipient = suite.deputy
|
||||||
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, expectedClaimAmount[0])
|
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, tc.args.coins[0])
|
||||||
suite.Nil(err)
|
suite.Nil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create atomic swap
|
// Create atomic swap
|
||||||
err := suite.keeper.CreateAtomicSwap(suite.ctx, suite.randomNumberHashes[i], suite.timestamps[i],
|
err := suite.keeper.CreateAtomicSwap(suite.ctx, suite.randomNumberHashes[i], suite.timestamps[i],
|
||||||
types.DefaultMinBlockLock, sender, expectedRecipient, TestSenderOtherChain, TestRecipientOtherChain,
|
types.DefaultMinBlockLock, sender, expectedRecipient, TestSenderOtherChain, TestRecipientOtherChain,
|
||||||
expectedClaimAmount, true)
|
tc.args.coins, true)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
realSwapID := types.CalculateSwapID(suite.randomNumberHashes[i], sender, TestSenderOtherChain)
|
realSwapID := types.CalculateSwapID(suite.randomNumberHashes[i], sender, TestSenderOtherChain)
|
||||||
@ -569,40 +628,40 @@ func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
|||||||
// Load expected recipient's account prior to claim attempt
|
// Load expected recipient's account prior to claim attempt
|
||||||
ak := suite.app.GetAccountKeeper()
|
ak := suite.app.GetAccountKeeper()
|
||||||
expectedRecipientAccPre := ak.GetAccount(tc.claimCtx, expectedRecipient)
|
expectedRecipientAccPre := ak.GetAccount(tc.claimCtx, expectedRecipient)
|
||||||
expectedRecipientBalancePre := expectedRecipientAccPre.GetCoins().AmountOf(expectedClaimAmount[0].Denom)
|
expectedRecipientBalancePre := expectedRecipientAccPre.GetCoins().AmountOf(tc.args.coins[0].Denom)
|
||||||
// Load asset supplies prior to claim attempt
|
// Load asset supplies prior to claim attempt
|
||||||
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.claimCtx, expectedClaimAmount[0].Denom)
|
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.claimCtx, tc.args.coins[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)
|
||||||
|
|
||||||
// Load expected recipient's account after the claim attempt
|
// Load expected recipient's account after the claim attempt
|
||||||
expectedRecipientAccPost := ak.GetAccount(tc.claimCtx, expectedRecipient)
|
expectedRecipientAccPost := ak.GetAccount(tc.claimCtx, expectedRecipient)
|
||||||
expectedRecipientBalancePost := expectedRecipientAccPost.GetCoins().AmountOf(expectedClaimAmount[0].Denom)
|
expectedRecipientBalancePost := expectedRecipientAccPost.GetCoins().AmountOf(tc.args.coins[0].Denom)
|
||||||
// Load asset supplies after the claim attempt
|
// Load asset supplies after the claim attempt
|
||||||
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.claimCtx, expectedClaimAmount[0].Denom)
|
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.claimCtx, tc.args.coins[0].Denom)
|
||||||
|
|
||||||
if tc.expectPass {
|
if tc.expectPass {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// Check coins moved
|
|
||||||
suite.Equal(expectedRecipientBalancePre.Add(expectedClaimAmount[0].Amount), expectedRecipientBalancePost)
|
|
||||||
|
|
||||||
// Check asset supply changes
|
// Check asset supply changes
|
||||||
switch tc.args.direction {
|
switch tc.args.direction {
|
||||||
case types.Incoming:
|
case types.Incoming:
|
||||||
|
// Check coins moved
|
||||||
|
suite.Equal(expectedRecipientBalancePre.Add(tc.args.coins[0].Amount), expectedRecipientBalancePost)
|
||||||
// Check incoming supply decreased
|
// Check incoming supply decreased
|
||||||
suite.True(assetSupplyPre.IncomingSupply.Amount.Sub(expectedClaimAmount[0].Amount).Equal(assetSupplyPost.IncomingSupply.Amount))
|
suite.True(assetSupplyPre.IncomingSupply.Amount.Sub(tc.args.coins[0].Amount).Equal(assetSupplyPost.IncomingSupply.Amount))
|
||||||
// Check current supply increased
|
// Check current supply increased
|
||||||
suite.Equal(assetSupplyPre.CurrentSupply.Add(expectedClaimAmount[0]), assetSupplyPost.CurrentSupply)
|
suite.Equal(assetSupplyPre.CurrentSupply.Add(tc.args.coins[0]), assetSupplyPost.CurrentSupply)
|
||||||
// Check outgoing supply not changed
|
// Check outgoing supply not changed
|
||||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||||
case types.Outgoing:
|
case types.Outgoing:
|
||||||
// Check incoming supply not changed
|
// Check incoming supply not changed
|
||||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||||
// Check current supply decreased
|
// Check current supply decreased
|
||||||
suite.Equal(assetSupplyPre.CurrentSupply.Sub(expectedClaimAmount[0]), assetSupplyPost.CurrentSupply)
|
suite.Equal(assetSupplyPre.CurrentSupply.Sub(tc.args.coins[0]), assetSupplyPost.CurrentSupply)
|
||||||
// Check outgoing supply decreased
|
// Check outgoing supply decreased
|
||||||
suite.True(assetSupplyPre.OutgoingSupply.Sub(expectedClaimAmount[0]).IsEqual(assetSupplyPost.OutgoingSupply))
|
suite.True(assetSupplyPre.OutgoingSupply.Sub(tc.args.coins[0]).IsEqual(assetSupplyPost.OutgoingSupply))
|
||||||
default:
|
default:
|
||||||
suite.Fail("should not have invalid direction")
|
suite.Fail("should not have invalid direction")
|
||||||
}
|
}
|
||||||
@ -732,8 +791,6 @@ func (suite *AtomicSwapTestSuite) TestRefundAtomicSwap() {
|
|||||||
|
|
||||||
if tc.expectPass {
|
if tc.expectPass {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// Check coins moved
|
|
||||||
suite.Equal(originalSenderBalancePre.Add(expectedRefundAmount[0].Amount), originalSenderBalancePost)
|
|
||||||
|
|
||||||
// Check asset supply changes
|
// Check asset supply changes
|
||||||
switch tc.args.direction {
|
switch tc.args.direction {
|
||||||
@ -744,6 +801,8 @@ func (suite *AtomicSwapTestSuite) TestRefundAtomicSwap() {
|
|||||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||||
case types.Outgoing:
|
case types.Outgoing:
|
||||||
|
// Check coins moved
|
||||||
|
suite.Equal(originalSenderBalancePre.Add(expectedRefundAmount[0].Amount), originalSenderBalancePost)
|
||||||
// Check incoming, current supply not changed
|
// Check incoming, current supply not changed
|
||||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||||
|
@ -1,14 +1,65 @@
|
|||||||
package v0_11
|
package v0_11
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
AddrByteCount = 20
|
||||||
|
RandomNumberHashLength = 32
|
||||||
|
RandomNumberLength = 32
|
||||||
|
DefaultPreviousBlockTime = tmtime.Canonical(time.Unix(0, 0))
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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"`
|
||||||
|
Supplies AssetSupplies `json:"supplies" yaml:"supplies"`
|
||||||
|
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates genesis inputs. It returns error if validation of any input fails.
|
||||||
|
func (gs GenesisState) Validate() error {
|
||||||
|
if err := gs.Params.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := map[string]bool{}
|
||||||
|
for _, swap := range gs.AtomicSwaps {
|
||||||
|
if ids[hex.EncodeToString(swap.GetSwapID())] {
|
||||||
|
return fmt.Errorf("found duplicate atomic swap ID %s", hex.EncodeToString(swap.GetSwapID()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := swap.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Params governance parameters for the bep3 module
|
// Params governance parameters for the bep3 module
|
||||||
@ -16,11 +67,83 @@ type Params struct {
|
|||||||
AssetParams AssetParams `json:"asset_params" yaml:"asset_params"`
|
AssetParams AssetParams `json:"asset_params" yaml:"asset_params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate ensure that params have valid values
|
||||||
|
func (p Params) Validate() error {
|
||||||
|
return validateAssetParams(p.AssetParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAssetParams(i interface{}) error {
|
||||||
|
assetParams, ok := i.(AssetParams)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
coinDenoms := make(map[string]bool)
|
||||||
|
for _, asset := range assetParams {
|
||||||
|
if err := sdk.ValidateDenom(asset.Denom); err != nil {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset denom invalid: %s", asset.Denom))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.CoinID < 0 {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s coin id must be a non negative integer", asset.Denom))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.Limit.IsNegative() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s has invalid (negative) supply limit: %s", asset.Denom, asset.SupplyLimit.Limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.TimeBasedLimit.IsNegative() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s has invalid (negative) supply time limit: %s", asset.Denom, asset.SupplyLimit.TimeBasedLimit))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.TimeBasedLimit.GT(asset.SupplyLimit.Limit) {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s cannot have supply time limit > supply limit: %s>%s", asset.Denom, asset.SupplyLimit.TimeBasedLimit, asset.SupplyLimit.Limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, found := coinDenoms[asset.Denom]
|
||||||
|
if found {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate denom", asset.Denom))
|
||||||
|
}
|
||||||
|
|
||||||
|
coinDenoms[asset.Denom] = true
|
||||||
|
|
||||||
|
if asset.DeputyAddress.Empty() {
|
||||||
|
return fmt.Errorf("deputy address cannot be empty for %s", asset.Denom)
|
||||||
|
}
|
||||||
|
|
||||||
|
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(fmt.Sprintf("asset %s must have a positive minimum swap amount, got %s", asset.Denom, asset.MinSwapAmount))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !asset.MaxSwapAmount.IsPositive() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("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
|
||||||
|
}
|
||||||
|
|
||||||
// AssetParam parameters that must be specified for each bep3 asset
|
// 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"` // SLIP-0044 registered coin type - see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
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
|
SupplyLimit SupplyLimit `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
|
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
|
FixedFee sdk.Int `json:"incoming_swap_fixed_fee" yaml:"incoming_swap_fixed_fee"` // the fixed fee charged by the relayer process for incoming swaps
|
||||||
@ -30,6 +153,14 @@ type AssetParam struct {
|
|||||||
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // Maximum swap block lock
|
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // Maximum swap block lock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupplyLimit parameters that control the absolute and time-based limits for an assets's supply
|
||||||
|
type SupplyLimit struct {
|
||||||
|
Limit sdk.Int `json:"limit" yaml:"limit"` // the absolute supply limit for an asset
|
||||||
|
TimeLimited bool `json:"time_limited" yaml:"time_limited"` // boolean for if the supply is also limited by time
|
||||||
|
TimePeriod time.Duration `json:"time_period" yaml:"time_period"` // the duration for which the supply time limit applies
|
||||||
|
TimeBasedLimit sdk.Int `json:"time_based_limit" yaml:"time_based_limit"` // the supply limit for an asset for each time period
|
||||||
|
}
|
||||||
|
|
||||||
// AssetParams array of AssetParam
|
// AssetParams array of AssetParam
|
||||||
type AssetParams []AssetParam
|
type AssetParams []AssetParam
|
||||||
|
|
||||||
@ -38,9 +169,77 @@ type AssetSupply struct {
|
|||||||
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"`
|
TimeLimitedCurrentSupply sdk.Coin `json:"time_limited_current_supply" yaml:"time_limited_current_supply"`
|
||||||
|
TimeElapsed time.Duration `json:"time_elapsed" yaml:"time_elapsed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewAssetSupply initializes a new AssetSupply
|
||||||
|
func NewAssetSupply(incomingSupply, outgoingSupply, currentSupply, timeLimitedSupply sdk.Coin, timeElapsed time.Duration) AssetSupply {
|
||||||
|
return AssetSupply{
|
||||||
|
IncomingSupply: incomingSupply,
|
||||||
|
OutgoingSupply: outgoingSupply,
|
||||||
|
CurrentSupply: currentSupply,
|
||||||
|
TimeLimitedCurrentSupply: timeLimitedSupply,
|
||||||
|
TimeElapsed: timeElapsed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic validation of an asset supply fields.
|
||||||
|
func (a AssetSupply) Validate() error {
|
||||||
|
if !a.IncomingSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "incoming supply %s", a.IncomingSupply)
|
||||||
|
}
|
||||||
|
if !a.OutgoingSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "outgoing supply %s", a.OutgoingSupply)
|
||||||
|
}
|
||||||
|
if !a.CurrentSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "current supply %s", a.CurrentSupply)
|
||||||
|
}
|
||||||
|
if !a.TimeLimitedCurrentSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "time-limited current supply %s", a.CurrentSupply)
|
||||||
|
}
|
||||||
|
denom := a.CurrentSupply.Denom
|
||||||
|
if (a.IncomingSupply.Denom != denom) ||
|
||||||
|
(a.OutgoingSupply.Denom != denom) ||
|
||||||
|
(a.TimeLimitedCurrentSupply.Denom != denom) {
|
||||||
|
return fmt.Errorf("asset supply denoms do not match %s %s %s %s", a.CurrentSupply.Denom, a.IncomingSupply.Denom, a.OutgoingSupply.Denom, a.TimeLimitedCurrentSupply.Denom)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns if two asset supplies are equal
|
||||||
|
func (a AssetSupply) Equal(b AssetSupply) bool {
|
||||||
|
if a.GetDenom() != b.GetDenom() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (a.IncomingSupply.IsEqual(b.IncomingSupply) &&
|
||||||
|
a.CurrentSupply.IsEqual(b.CurrentSupply) &&
|
||||||
|
a.OutgoingSupply.IsEqual(b.OutgoingSupply) &&
|
||||||
|
a.TimeLimitedCurrentSupply.IsEqual(b.TimeLimitedCurrentSupply) &&
|
||||||
|
a.TimeElapsed == b.TimeElapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements stringer
|
||||||
|
func (a AssetSupply) String() string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
asset supply:
|
||||||
|
Incoming supply: %s
|
||||||
|
Outgoing supply: %s
|
||||||
|
Current supply: %s
|
||||||
|
Time-limited current cupply: %s
|
||||||
|
Time elapsed: %s
|
||||||
|
`,
|
||||||
|
a.IncomingSupply, a.OutgoingSupply, a.CurrentSupply, a.TimeLimitedCurrentSupply, a.TimeElapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
type AssetSupplies []AssetSupply
|
||||||
|
|
||||||
// AtomicSwap contains the information for an atomic swap
|
// AtomicSwap contains the information for an atomic swap
|
||||||
type AtomicSwap struct {
|
type AtomicSwap struct {
|
||||||
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
||||||
@ -57,6 +256,73 @@ type AtomicSwap struct {
|
|||||||
Direction SwapDirection `json:"direction" yaml:"direction"`
|
Direction SwapDirection `json:"direction" yaml:"direction"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalculateSwapID calculates the hash of a RandomNumberHash, sdk.AccAddress, and string
|
||||||
|
func CalculateSwapID(randomNumberHash []byte, sender sdk.AccAddress, senderOtherChain string) []byte {
|
||||||
|
senderOtherChain = strings.ToLower(senderOtherChain)
|
||||||
|
data := randomNumberHash
|
||||||
|
data = append(data, sender.Bytes()...)
|
||||||
|
data = append(data, []byte(senderOtherChain)...)
|
||||||
|
return tmhash.Sum(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSwapID calculates the ID of an atomic swap
|
||||||
|
func (a AtomicSwap) GetSwapID() tmbytes.HexBytes {
|
||||||
|
return CalculateSwapID(a.RandomNumberHash, a.Sender, a.SenderOtherChain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoins returns the swap's amount as sdk.Coins
|
||||||
|
func (a AtomicSwap) GetCoins() sdk.Coins {
|
||||||
|
return sdk.NewCoins(a.Amount...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate performs a basic validation of an atomic swap fields.
|
||||||
|
func (a AtomicSwap) Validate() error {
|
||||||
|
if !a.Amount.IsValid() {
|
||||||
|
return fmt.Errorf("invalid amount: %s", a.Amount)
|
||||||
|
}
|
||||||
|
if !a.Amount.IsAllPositive() {
|
||||||
|
return fmt.Errorf("the swapped out coin must be positive: %s", a.Amount)
|
||||||
|
}
|
||||||
|
if len(a.RandomNumberHash) != RandomNumberHashLength {
|
||||||
|
return fmt.Errorf("the length of random number hash should be %d", RandomNumberHashLength)
|
||||||
|
}
|
||||||
|
if a.ExpireHeight == 0 {
|
||||||
|
return errors.New("expire height cannot be 0")
|
||||||
|
}
|
||||||
|
if a.Timestamp == 0 {
|
||||||
|
return errors.New("timestamp cannot be 0")
|
||||||
|
}
|
||||||
|
if a.Sender.Empty() {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender cannot be empty")
|
||||||
|
}
|
||||||
|
if a.Recipient.Empty() {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "recipient cannot be empty")
|
||||||
|
}
|
||||||
|
if len(a.Sender) != AddrByteCount {
|
||||||
|
return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(a.Sender))
|
||||||
|
}
|
||||||
|
if len(a.Recipient) != AddrByteCount {
|
||||||
|
return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(a.Recipient))
|
||||||
|
}
|
||||||
|
// NOTE: These adresses may not have a bech32 prefix.
|
||||||
|
if strings.TrimSpace(a.SenderOtherChain) == "" {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender other chain cannot be blank")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(a.RecipientOtherChain) == "" {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "recipient other chain cannot be blank")
|
||||||
|
}
|
||||||
|
if a.Status == Completed && a.ClosedBlock == 0 {
|
||||||
|
return errors.New("closed block cannot be 0")
|
||||||
|
}
|
||||||
|
if a.Status == NULL || a.Status > 3 {
|
||||||
|
return errors.New("invalid swap status")
|
||||||
|
}
|
||||||
|
if a.Direction == INVALID || a.Direction > 2 {
|
||||||
|
return errors.New("invalid swap direction")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AtomicSwaps is a slice of AtomicSwap
|
// AtomicSwaps is a slice of AtomicSwap
|
||||||
type AtomicSwaps []AtomicSwap
|
type AtomicSwaps []AtomicSwap
|
||||||
|
|
||||||
@ -71,6 +337,50 @@ const (
|
|||||||
Expired SwapStatus = 0x03
|
Expired SwapStatus = 0x03
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MarshalJSON marshals the SwapStatus
|
||||||
|
func (status SwapStatus) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(status.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals the SwapStatus
|
||||||
|
func (status *SwapStatus) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal(data, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*status = NewSwapStatusFromString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSwapStatusFromString converts string to SwapStatus type
|
||||||
|
func NewSwapStatusFromString(str string) SwapStatus {
|
||||||
|
switch str {
|
||||||
|
case "Open", "open":
|
||||||
|
return Open
|
||||||
|
case "Completed", "completed":
|
||||||
|
return Completed
|
||||||
|
case "Expired", "expired":
|
||||||
|
return Expired
|
||||||
|
default:
|
||||||
|
return NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of a SwapStatus
|
||||||
|
func (status SwapStatus) String() string {
|
||||||
|
switch status {
|
||||||
|
case Open:
|
||||||
|
return "Open"
|
||||||
|
case Completed:
|
||||||
|
return "Completed"
|
||||||
|
case Expired:
|
||||||
|
return "Expired"
|
||||||
|
default:
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SwapDirection is the direction of an AtomicSwap
|
// SwapDirection is the direction of an AtomicSwap
|
||||||
type SwapDirection byte
|
type SwapDirection byte
|
||||||
|
|
||||||
@ -79,3 +389,52 @@ const (
|
|||||||
Incoming SwapDirection = 0x01
|
Incoming SwapDirection = 0x01
|
||||||
Outgoing SwapDirection = 0x02
|
Outgoing SwapDirection = 0x02
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewSwapDirectionFromString converts string to SwapDirection type
|
||||||
|
func NewSwapDirectionFromString(str string) SwapDirection {
|
||||||
|
switch str {
|
||||||
|
case "Incoming", "incoming", "inc", "I", "i":
|
||||||
|
return Incoming
|
||||||
|
case "Outgoing", "outgoing", "out", "O", "o":
|
||||||
|
return Outgoing
|
||||||
|
default:
|
||||||
|
return INVALID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of a SwapDirection
|
||||||
|
func (direction SwapDirection) String() string {
|
||||||
|
switch direction {
|
||||||
|
case Incoming:
|
||||||
|
return "Incoming"
|
||||||
|
case Outgoing:
|
||||||
|
return "Outgoing"
|
||||||
|
default:
|
||||||
|
return "INVALID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON marshals the SwapDirection
|
||||||
|
func (direction SwapDirection) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(direction.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals the SwapDirection
|
||||||
|
func (direction *SwapDirection) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal(data, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*direction = NewSwapDirectionFromString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if the swap direction is valid and false otherwise.
|
||||||
|
func (direction SwapDirection) IsValid() bool {
|
||||||
|
if direction == Incoming ||
|
||||||
|
direction == Outgoing {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package v0_9
|
package v0_9
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||||
)
|
)
|
||||||
@ -141,6 +143,50 @@ const (
|
|||||||
Expired SwapStatus = 0x03
|
Expired SwapStatus = 0x03
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MarshalJSON marshals the SwapStatus
|
||||||
|
func (status SwapStatus) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(status.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals the SwapStatus
|
||||||
|
func (status *SwapStatus) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal(data, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*status = NewSwapStatusFromString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSwapStatusFromString converts string to SwapStatus type
|
||||||
|
func NewSwapStatusFromString(str string) SwapStatus {
|
||||||
|
switch str {
|
||||||
|
case "Open", "open":
|
||||||
|
return Open
|
||||||
|
case "Completed", "completed":
|
||||||
|
return Completed
|
||||||
|
case "Expired", "expired":
|
||||||
|
return Expired
|
||||||
|
default:
|
||||||
|
return NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of a SwapStatus
|
||||||
|
func (status SwapStatus) String() string {
|
||||||
|
switch status {
|
||||||
|
case Open:
|
||||||
|
return "Open"
|
||||||
|
case Completed:
|
||||||
|
return "Completed"
|
||||||
|
case Expired:
|
||||||
|
return "Expired"
|
||||||
|
default:
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SwapDirection is the direction of an AtomicSwap
|
// SwapDirection is the direction of an AtomicSwap
|
||||||
type SwapDirection byte
|
type SwapDirection byte
|
||||||
|
|
||||||
@ -149,3 +195,52 @@ const (
|
|||||||
Incoming SwapDirection = 0x01
|
Incoming SwapDirection = 0x01
|
||||||
Outgoing SwapDirection = 0x02
|
Outgoing SwapDirection = 0x02
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewSwapDirectionFromString converts string to SwapDirection type
|
||||||
|
func NewSwapDirectionFromString(str string) SwapDirection {
|
||||||
|
switch str {
|
||||||
|
case "Incoming", "incoming", "inc", "I", "i":
|
||||||
|
return Incoming
|
||||||
|
case "Outgoing", "outgoing", "out", "O", "o":
|
||||||
|
return Outgoing
|
||||||
|
default:
|
||||||
|
return INVALID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of a SwapDirection
|
||||||
|
func (direction SwapDirection) String() string {
|
||||||
|
switch direction {
|
||||||
|
case Incoming:
|
||||||
|
return "Incoming"
|
||||||
|
case Outgoing:
|
||||||
|
return "Outgoing"
|
||||||
|
default:
|
||||||
|
return "INVALID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON marshals the SwapDirection
|
||||||
|
func (direction SwapDirection) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(direction.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals the SwapDirection
|
||||||
|
func (direction *SwapDirection) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
err := json.Unmarshal(data, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*direction = NewSwapDirectionFromString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if the swap direction is valid and false otherwise.
|
||||||
|
func (direction SwapDirection) IsValid() bool {
|
||||||
|
if direction == Incoming ||
|
||||||
|
direction == Outgoing {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package simulation
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||||
"github.com/tendermint/tendermint/libs/kv"
|
"github.com/tendermint/tendermint/libs/kv"
|
||||||
@ -31,6 +32,11 @@ func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string {
|
|||||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA)
|
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA)
|
||||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB)
|
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB)
|
||||||
return fmt.Sprintf("%s\n%s", supplyA, supplyB)
|
return fmt.Sprintf("%s\n%s", supplyA, supplyB)
|
||||||
|
case bytes.Equal(kvA.Key[:1], types.PreviousBlockTimeKey):
|
||||||
|
var timeA, timeB time.Time
|
||||||
|
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &timeA)
|
||||||
|
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &timeB)
|
||||||
|
return fmt.Sprintf("%s\n%s", timeA, timeB)
|
||||||
|
|
||||||
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]))
|
||||||
|
@ -3,6 +3,7 @@ package simulation
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -22,12 +23,13 @@ func makeTestCodec() (cdc *codec.Codec) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeDistributionStore(t *testing.T) {
|
func TestDecodeBep3Store(t *testing.T) {
|
||||||
cdc := makeTestCodec()
|
cdc := makeTestCodec()
|
||||||
|
prevBlockTime := time.Now().UTC()
|
||||||
|
|
||||||
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{IncomingSupply: oneCoin, OutgoingSupply: oneCoin, CurrentSupply: oneCoin}
|
supply := types.AssetSupply{IncomingSupply: oneCoin, OutgoingSupply: oneCoin, CurrentSupply: oneCoin, TimeLimitedCurrentSupply: oneCoin, TimeElapsed: time.Duration(0)}
|
||||||
bz := tmbytes.HexBytes([]byte{1, 2})
|
bz := tmbytes.HexBytes([]byte{1, 2})
|
||||||
|
|
||||||
kvPairs := kv.Pairs{
|
kvPairs := kv.Pairs{
|
||||||
@ -35,6 +37,7 @@ func TestDecodeDistributionStore(t *testing.T) {
|
|||||||
kv.Pair{Key: types.AssetSupplyPrefix, 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: types.PreviousBlockTimeKey, Value: cdc.MustMarshalBinaryLengthPrefixed(prevBlockTime)},
|
||||||
kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}},
|
kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +49,7 @@ func TestDecodeDistributionStore(t *testing.T) {
|
|||||||
{"AssetSupply", fmt.Sprintf("%v\n%v", supply, supply)},
|
{"AssetSupply", fmt.Sprintf("%v\n%v", supply, supply)},
|
||||||
{"AtomicSwapByBlock", fmt.Sprintf("%s\n%s", bz, bz)},
|
{"AtomicSwapByBlock", fmt.Sprintf("%s\n%s", bz, bz)},
|
||||||
{"AtomicSwapLongtermStorage", fmt.Sprintf("%s\n%s", bz, bz)},
|
{"AtomicSwapLongtermStorage", fmt.Sprintf("%s\n%s", bz, bz)},
|
||||||
|
{"PreviousBlockTime", fmt.Sprintf("%s\n%s", prevBlockTime, prevBlockTime)},
|
||||||
{"other", ""},
|
{"other", ""},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
@ -61,7 +62,7 @@ func GenSupplyLimit(r *rand.Rand, max int) sdk.Int {
|
|||||||
func GenAssetSupply(r *rand.Rand, denom string) types.AssetSupply {
|
func GenAssetSupply(r *rand.Rand, denom string) types.AssetSupply {
|
||||||
return types.NewAssetSupply(
|
return types.NewAssetSupply(
|
||||||
sdk.NewCoin(denom, sdk.ZeroInt()), sdk.NewCoin(denom, sdk.ZeroInt()),
|
sdk.NewCoin(denom, sdk.ZeroInt()), sdk.NewCoin(denom, sdk.ZeroInt()),
|
||||||
sdk.NewCoin(denom, sdk.ZeroInt()))
|
sdk.NewCoin(denom, sdk.ZeroInt()), sdk.NewCoin(denom, sdk.ZeroInt()), time.Duration(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenMinBlockLock randomized MinBlockLock
|
// GenMinBlockLock randomized MinBlockLock
|
||||||
@ -97,10 +98,22 @@ func genSupportedAsset(r *rand.Rand, denom string) types.AssetParam {
|
|||||||
|
|
||||||
minSwapAmount := GenMinSwapAmount(r)
|
minSwapAmount := GenMinSwapAmount(r)
|
||||||
minBlockLock := GenMinBlockLock(r)
|
minBlockLock := GenMinBlockLock(r)
|
||||||
|
timeLimited := r.Float32() < 0.5
|
||||||
|
timeBasedLimit := sdk.ZeroInt()
|
||||||
|
if timeLimited {
|
||||||
|
// set time-based limit to between 10 and 25% of the total limit
|
||||||
|
min := int(limit.Quo(sdk.NewInt(10)).Int64())
|
||||||
|
max := int(limit.Quo(sdk.NewInt(4)).Int64())
|
||||||
|
timeBasedLimit = sdk.NewInt(int64(simulation.RandIntBetween(r, min, max)))
|
||||||
|
}
|
||||||
return types.AssetParam{
|
return types.AssetParam{
|
||||||
Denom: denom,
|
Denom: denom,
|
||||||
CoinID: int(coinID.Int64()),
|
CoinID: int(coinID.Int64()),
|
||||||
SupplyLimit: limit,
|
SupplyLimit: types.SupplyLimit{
|
||||||
|
Limit: limit,
|
||||||
|
TimeLimited: timeLimited,
|
||||||
|
TimePeriod: time.Hour * 24,
|
||||||
|
TimeBasedLimit: timeBasedLimit},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: GenRandBnbDeputy(r).Address,
|
DeputyAddress: GenRandBnbDeputy(r).Address,
|
||||||
FixedFee: GenRandFixedFee(r),
|
FixedFee: GenRandFixedFee(r),
|
||||||
@ -146,6 +159,7 @@ func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState
|
|||||||
AssetParams: supportedAssets,
|
AssetParams: supportedAssets,
|
||||||
},
|
},
|
||||||
Supplies: supplies,
|
Supplies: supplies,
|
||||||
|
PreviousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
return bep3Genesis
|
return bep3Genesis
|
||||||
@ -161,7 +175,7 @@ func loadAuthGenState(simState *module.SimulationState, bep3Genesis types.Genesi
|
|||||||
if !found {
|
if !found {
|
||||||
panic("deputy address not found in available accounts")
|
panic("deputy address not found in available accounts")
|
||||||
}
|
}
|
||||||
assetCoin := sdk.NewCoins(sdk.NewCoin(asset.Denom, asset.SupplyLimit))
|
assetCoin := sdk.NewCoins(sdk.NewCoin(asset.Denom, asset.SupplyLimit.Limit))
|
||||||
if err := deputy.SetCoins(deputy.GetCoins().Add(assetCoin...)); err != nil {
|
if err := deputy.SetCoins(deputy.GetCoins().Add(assetCoin...)); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -127,15 +127,27 @@ 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(asset.Denom)
|
maximumAmount := senderAcc.SpendableCoins(ctx.BlockTime()).Sub(fees).AmountOf(asset.Denom)
|
||||||
// The maximum amount for outgoing swaps is limited by the asset's current supply
|
|
||||||
if recipient.Address.Equals(asset.DeputyAddress) {
|
|
||||||
assetSupply, foundAssetSupply := k.GetAssetSupply(ctx, asset.Denom)
|
assetSupply, foundAssetSupply := k.GetAssetSupply(ctx, asset.Denom)
|
||||||
if !foundAssetSupply {
|
if !foundAssetSupply {
|
||||||
return noOpMsg, nil, fmt.Errorf("no asset supply found for %s", asset.Denom)
|
return noOpMsg, nil, fmt.Errorf("no asset supply found for %s", asset.Denom)
|
||||||
}
|
}
|
||||||
|
// The maximum amount for outgoing swaps is limited by the asset's current supply
|
||||||
|
if recipient.Address.Equals(asset.DeputyAddress) {
|
||||||
|
|
||||||
if maximumAmount.GT(assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)) {
|
if maximumAmount.GT(assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)) {
|
||||||
maximumAmount = assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)
|
maximumAmount = assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// the maximum amount for incoming swaps in limited by the asset's incoming supply + current supply (rate-limited if applicable) + swap amount being less than the supply limit
|
||||||
|
var currentRemainingSupply sdk.Int
|
||||||
|
if asset.SupplyLimit.TimeLimited {
|
||||||
|
currentRemainingSupply = asset.SupplyLimit.Limit.Sub(assetSupply.IncomingSupply.Amount).Sub(assetSupply.TimeLimitedCurrentSupply.Amount)
|
||||||
|
} else {
|
||||||
|
currentRemainingSupply = asset.SupplyLimit.Limit.Sub(assetSupply.IncomingSupply.Amount).Sub(assetSupply.CurrentSupply.Amount)
|
||||||
|
}
|
||||||
|
if currentRemainingSupply.LT(maximumAmount) {
|
||||||
|
maximumAmount = currentRemainingSupply
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -147,7 +159,7 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
|
|||||||
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)
|
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)", asset.Denom), "", false, nil), nil, nil
|
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (account funds exhausted for asset %s)", asset.Denom), "", false, nil), nil, nil
|
||||||
}
|
}
|
||||||
coins := sdk.NewCoins(sdk.NewCoin(asset.Denom, amount))
|
coins := sdk.NewCoins(sdk.NewCoin(asset.Denom, amount))
|
||||||
|
|
||||||
@ -242,7 +254,7 @@ func operationClaimAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID []
|
|||||||
if !found {
|
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
|
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)) {
|
if asset.SupplyLimit.Limit.LT(supply.CurrentSupply.Amount.Add(swap.Amount[0].Amount)) {
|
||||||
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,4 +43,6 @@ var (
|
|||||||
ErrInvalidAmount = sdkerrors.Register(ModuleName, 18, "amount is outside acceptable range")
|
ErrInvalidAmount = sdkerrors.Register(ModuleName, 18, "amount is outside acceptable range")
|
||||||
// ErrInvalidSwapAccount error for when a swap involves an invalid account
|
// ErrInvalidSwapAccount error for when a swap involves an invalid account
|
||||||
ErrInvalidSwapAccount = sdkerrors.Register(ModuleName, 19, "atomic swap has invalid account")
|
ErrInvalidSwapAccount = sdkerrors.Register(ModuleName, 19, "atomic swap has invalid account")
|
||||||
|
// ErrExceedsTimeBasedSupplyLimit error for when the proposed supply increase would put the supply above limit for the current time period
|
||||||
|
ErrExceedsTimeBasedSupplyLimit = sdkerrors.Register(ModuleName, 20, "asset supply over limit for current time period")
|
||||||
)
|
)
|
||||||
|
@ -13,6 +13,8 @@ type SupplyKeeper interface {
|
|||||||
|
|
||||||
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
|
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
|
||||||
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
|
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
|
||||||
|
BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error
|
||||||
|
MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccountKeeper defines the expected account keeper (noalias)
|
// AccountKeeper defines the expected account keeper (noalias)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenesisState - all bep3 state that must be provided at genesis
|
// GenesisState - all bep3 state that must be provided at genesis
|
||||||
@ -11,14 +12,16 @@ 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"`
|
||||||
Supplies AssetSupplies `json:"supplies" yaml:"supplies"`
|
Supplies AssetSupplies `json:"supplies" yaml:"supplies"`
|
||||||
|
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, previousBlockTime time.Time) GenesisState {
|
||||||
return GenesisState{
|
return GenesisState{
|
||||||
Params: params,
|
Params: params,
|
||||||
AtomicSwaps: swaps,
|
AtomicSwaps: swaps,
|
||||||
Supplies: supplies,
|
Supplies: supplies,
|
||||||
|
PreviousBlockTime: previousBlockTime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +31,7 @@ func DefaultGenesisState() GenesisState {
|
|||||||
DefaultParams(),
|
DefaultParams(),
|
||||||
AtomicSwaps{},
|
AtomicSwaps{},
|
||||||
AssetSupplies{},
|
AssetSupplies{},
|
||||||
|
DefaultPreviousBlockTime,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package types_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
@ -24,13 +25,15 @@ 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(coin, coin, coin)
|
supply := types.NewAssetSupply(coin, coin, coin, coin, time.Duration(0))
|
||||||
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
|
||||||
|
previousBlockTime time.Time
|
||||||
}
|
}
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
@ -41,6 +44,7 @@ func (suite *GenesisTestSuite) TestValidate() {
|
|||||||
"default",
|
"default",
|
||||||
args{
|
args{
|
||||||
swaps: types.AtomicSwaps{},
|
swaps: types.AtomicSwaps{},
|
||||||
|
previousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
@ -48,13 +52,33 @@ func (suite *GenesisTestSuite) TestValidate() {
|
|||||||
"with swaps",
|
"with swaps",
|
||||||
args{
|
args{
|
||||||
swaps: suite.swaps,
|
swaps: suite.swaps,
|
||||||
|
previousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"with supplies",
|
||||||
|
args{
|
||||||
|
swaps: types.AtomicSwaps{},
|
||||||
|
supplies: suite.supplies,
|
||||||
|
previousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"invalid supply",
|
||||||
|
args{
|
||||||
|
swaps: types.AtomicSwaps{},
|
||||||
|
supplies: types.AssetSupplies{types.AssetSupply{IncomingSupply: sdk.Coin{"Invalid", sdk.ZeroInt()}}},
|
||||||
|
previousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"duplicate swaps",
|
"duplicate swaps",
|
||||||
args{
|
args{
|
||||||
swaps: types.AtomicSwaps{suite.swaps[2], suite.swaps[2]},
|
swaps: types.AtomicSwaps{suite.swaps[2], suite.swaps[2]},
|
||||||
|
previousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
@ -62,9 +86,17 @@ func (suite *GenesisTestSuite) TestValidate() {
|
|||||||
"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)}}}},
|
||||||
|
previousBlockTime: types.DefaultPreviousBlockTime,
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"blocktime not set",
|
||||||
|
args{
|
||||||
|
swaps: types.AtomicSwaps{},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@ -73,7 +105,7 @@ func (suite *GenesisTestSuite) TestValidate() {
|
|||||||
if tc.name == "default" {
|
if tc.name == "default" {
|
||||||
gs = types.DefaultGenesisState()
|
gs = types.DefaultGenesisState()
|
||||||
} else {
|
} else {
|
||||||
gs = types.NewGenesisState(types.DefaultParams(), tc.args.swaps, suite.supplies)
|
gs = types.NewGenesisState(types.DefaultParams(), tc.args.swaps, tc.args.supplies, tc.args.previousBlockTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := gs.Validate()
|
err := gs.Validate()
|
||||||
|
@ -30,6 +30,7 @@ var (
|
|||||||
AtomicSwapByBlockPrefix = []byte{0x01} // prefix for keys of the AtomicSwapsByBlock index
|
AtomicSwapByBlockPrefix = []byte{0x01} // prefix for keys of the AtomicSwapsByBlock index
|
||||||
AtomicSwapLongtermStoragePrefix = []byte{0x02} // prefix for keys of the AtomicSwapLongtermStorage index
|
AtomicSwapLongtermStoragePrefix = []byte{0x02} // prefix for keys of the AtomicSwapLongtermStorage index
|
||||||
AssetSupplyPrefix = []byte{0x03}
|
AssetSupplyPrefix = []byte{0x03}
|
||||||
|
PreviousBlockTimeKey = []byte{0x04}
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetAtomicSwapByHeightKey is used by the AtomicSwapByBlock index and AtomicSwapLongtermStorage index
|
// GetAtomicSwapByHeightKey is used by the AtomicSwapByBlock index and AtomicSwapLongtermStorage index
|
||||||
|
@ -2,9 +2,16 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
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"
|
||||||
|
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bech32MainPrefix = "kava"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parameter keys
|
// Parameter keys
|
||||||
@ -16,6 +23,7 @@ var (
|
|||||||
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
|
||||||
|
DefaultPreviousBlockTime = tmtime.Canonical(time.Unix(0, 0))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Params governance parameters for bep3 module
|
// Params governance parameters for bep3 module
|
||||||
@ -47,7 +55,7 @@ func DefaultParams() Params {
|
|||||||
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"` // SLIP-0044 registered coin type - see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
CoinID int `json:"coin_id" yaml:"coin_id"` // SLIP-0044 registered coin type - see https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||||
SupplyLimit sdk.Int `json:"supply_limit" yaml:"supply_limit"` // asset supply limit
|
SupplyLimit SupplyLimit `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
|
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
|
FixedFee sdk.Int `json:"fixed_fee" yaml:"fixed_fee"` // the fixed fee charged by the relayer process for outgoing swaps
|
||||||
@ -59,7 +67,7 @@ type AssetParam struct {
|
|||||||
|
|
||||||
// NewAssetParam returns a new AssetParam
|
// NewAssetParam returns a new AssetParam
|
||||||
func NewAssetParam(
|
func NewAssetParam(
|
||||||
denom string, coinID int, limit sdk.Int, active bool,
|
denom string, coinID int, limit SupplyLimit, active bool,
|
||||||
deputyAddr sdk.AccAddress, fixedFee sdk.Int, minSwapAmount sdk.Int,
|
deputyAddr sdk.AccAddress, fixedFee sdk.Int, minSwapAmount sdk.Int,
|
||||||
maxSwapAmount sdk.Int, minBlockLock uint64, maxBlockLock uint64,
|
maxSwapAmount sdk.Int, minBlockLock uint64, maxBlockLock uint64,
|
||||||
) AssetParam {
|
) AssetParam {
|
||||||
@ -106,6 +114,28 @@ func (aps AssetParams) String() string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SupplyLimit parameters that control the absolute and time-based limits for an assets's supply
|
||||||
|
type SupplyLimit struct {
|
||||||
|
Limit sdk.Int `json:"limit" yaml:"limit"` // the absolute supply limit for an asset
|
||||||
|
TimeLimited bool `json:"time_limited" yaml:"time_limited"` // boolean for if the supply is also limited by time
|
||||||
|
TimePeriod time.Duration `json:"time_period" yaml:"time_period"` // the duration for which the supply time limit applies
|
||||||
|
TimeBasedLimit sdk.Int `json:"time_based_limit" yaml:"time_based_limit"` // the supply limit for an asset for each time period
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements fmt.Stringer
|
||||||
|
func (sl SupplyLimit) String() string {
|
||||||
|
return fmt.Sprintf(`%s
|
||||||
|
%t
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
`, sl.Limit, sl.TimeLimited, sl.TimePeriod, sl.TimeBasedLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if two supply limits are equal
|
||||||
|
func (sl SupplyLimit) Equals(sl2 SupplyLimit) bool {
|
||||||
|
return sl.Limit.Equal(sl2.Limit) && sl.TimeLimited == sl2.TimeLimited && sl.TimePeriod == sl2.TimePeriod && sl.TimeBasedLimit.Equal(sl2.TimeBasedLimit)
|
||||||
|
}
|
||||||
|
|
||||||
// ParamKeyTable Key declaration for parameters
|
// ParamKeyTable Key declaration for parameters
|
||||||
func ParamKeyTable() params.KeyTable {
|
func ParamKeyTable() params.KeyTable {
|
||||||
return params.NewKeyTable().RegisterParamSet(&Params{})
|
return params.NewKeyTable().RegisterParamSet(&Params{})
|
||||||
@ -134,20 +164,28 @@ func validateAssetParams(i interface{}) error {
|
|||||||
coinDenoms := make(map[string]bool)
|
coinDenoms := make(map[string]bool)
|
||||||
for _, asset := range assetParams {
|
for _, asset := range assetParams {
|
||||||
if err := sdk.ValidateDenom(asset.Denom); err != nil {
|
if err := sdk.ValidateDenom(asset.Denom); err != nil {
|
||||||
return fmt.Errorf("asset denom invalid: %s", asset.Denom)
|
return fmt.Errorf(fmt.Sprintf("asset denom invalid: %s", asset.Denom))
|
||||||
}
|
}
|
||||||
|
|
||||||
if asset.CoinID < 0 {
|
if asset.CoinID < 0 {
|
||||||
return fmt.Errorf("asset %s coin id must be a non negative integer", asset.Denom)
|
return fmt.Errorf(fmt.Sprintf("asset %s coin id must be a non negative integer", asset.Denom))
|
||||||
}
|
}
|
||||||
|
|
||||||
if asset.SupplyLimit.IsNegative() {
|
if asset.SupplyLimit.Limit.IsNegative() {
|
||||||
return fmt.Errorf("asset %s has invalid (negative) supply limit: %s", asset.Denom, asset.SupplyLimit)
|
return fmt.Errorf(fmt.Sprintf("asset %s has invalid (negative) supply limit: %s", asset.Denom, asset.SupplyLimit.Limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.TimeBasedLimit.IsNegative() {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s has invalid (negative) supply time limit: %s", asset.Denom, asset.SupplyLimit.TimeBasedLimit))
|
||||||
|
}
|
||||||
|
|
||||||
|
if asset.SupplyLimit.TimeBasedLimit.GT(asset.SupplyLimit.Limit) {
|
||||||
|
return fmt.Errorf(fmt.Sprintf("asset %s cannot have supply time limit > supply limit: %s>%s", asset.Denom, asset.SupplyLimit.TimeBasedLimit, asset.SupplyLimit.Limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, found := coinDenoms[asset.Denom]
|
_, found := coinDenoms[asset.Denom]
|
||||||
if found {
|
if found {
|
||||||
return fmt.Errorf("asset %s cannot have duplicate denom", asset.Denom)
|
return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate denom", asset.Denom))
|
||||||
}
|
}
|
||||||
|
|
||||||
coinDenoms[asset.Denom] = true
|
coinDenoms[asset.Denom] = true
|
||||||
@ -169,11 +207,11 @@ func validateAssetParams(i interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !asset.MinSwapAmount.IsPositive() {
|
if !asset.MinSwapAmount.IsPositive() {
|
||||||
return fmt.Errorf("asset %s must have a positive minimum swap amount, got %s", asset.Denom, asset.MinSwapAmount)
|
return fmt.Errorf(fmt.Sprintf("asset %s must have a positive minimum swap amount, got %s", asset.Denom, asset.MinSwapAmount))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !asset.MaxSwapAmount.IsPositive() {
|
if !asset.MaxSwapAmount.IsPositive() {
|
||||||
return fmt.Errorf("asset %s must have a positive maximum swap amount, got %s", asset.Denom, asset.MaxSwapAmount)
|
return fmt.Errorf(fmt.Sprintf("asset %s must have a positive maximum swap amount, got %s", asset.Denom, asset.MaxSwapAmount))
|
||||||
}
|
}
|
||||||
|
|
||||||
if asset.MinSwapAmount.GT(asset.MaxSwapAmount) {
|
if asset.MinSwapAmount.GT(asset.MaxSwapAmount) {
|
||||||
|
@ -2,6 +2,7 @@ package types_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ import (
|
|||||||
type ParamsTestSuite struct {
|
type ParamsTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
addr sdk.AccAddress
|
addr sdk.AccAddress
|
||||||
supply []sdk.Int
|
supply []types.SupplyLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ParamsTestSuite) SetupTest() {
|
func (suite *ParamsTestSuite) SetupTest() {
|
||||||
@ -22,7 +23,19 @@ 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))
|
supply1 := types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(10000000000000),
|
||||||
|
TimeLimited: false,
|
||||||
|
TimeBasedLimit: sdk.ZeroInt(),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
}
|
||||||
|
supply2 := types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(10000000000000),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(100000000000),
|
||||||
|
TimePeriod: time.Hour * 24,
|
||||||
|
}
|
||||||
|
suite.supply = append(suite.supply, supply1, supply2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +70,17 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
expectPass: true,
|
expectPass: true,
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "valid single asset time limited",
|
||||||
|
args: args{
|
||||||
|
assetParams: types.AssetParams{types.NewAssetParam(
|
||||||
|
"bnb", 714, suite.supply[1], true,
|
||||||
|
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
|
||||||
|
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
|
||||||
|
},
|
||||||
|
expectPass: true,
|
||||||
|
expectedErr: "",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "valid multi asset",
|
name: "valid multi asset",
|
||||||
args: args{
|
args: args{
|
||||||
@ -166,13 +190,38 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
args: args{
|
args: args{
|
||||||
assetParams: types.AssetParams{types.NewAssetParam(
|
assetParams: types.AssetParams{types.NewAssetParam(
|
||||||
"bnb", 714,
|
"bnb", 714,
|
||||||
sdk.NewInt(-10000000000000), true,
|
types.SupplyLimit{sdk.NewInt(-10000000000000), false, time.Hour, sdk.ZeroInt()}, true,
|
||||||
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
|
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
|
||||||
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
|
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
|
||||||
},
|
},
|
||||||
expectPass: false,
|
expectPass: false,
|
||||||
expectedErr: "invalid (negative) supply limit",
|
expectedErr: "invalid (negative) supply limit",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "negative asset time limit",
|
||||||
|
args: args{
|
||||||
|
assetParams: types.AssetParams{types.NewAssetParam(
|
||||||
|
"bnb", 714,
|
||||||
|
types.SupplyLimit{sdk.NewInt(10000000000000), false, time.Hour, sdk.NewInt(-10000000000000)}, true,
|
||||||
|
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
|
||||||
|
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
|
||||||
|
},
|
||||||
|
expectPass: false,
|
||||||
|
expectedErr: "invalid (negative) supply time limit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "asset time limit greater than overall limit",
|
||||||
|
args: args{
|
||||||
|
assetParams: types.AssetParams{types.NewAssetParam(
|
||||||
|
"bnb", 714,
|
||||||
|
types.SupplyLimit{sdk.NewInt(10000000000000), true, time.Hour, sdk.NewInt(100000000000000)},
|
||||||
|
true,
|
||||||
|
suite.addr, sdk.NewInt(1000), sdk.NewInt(100000000), sdk.NewInt(100000000000),
|
||||||
|
types.DefaultMinBlockLock, types.DefaultMaxBlockLock)},
|
||||||
|
},
|
||||||
|
expectPass: false,
|
||||||
|
expectedErr: "supply time limit > supply limit",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "duplicate denom",
|
name: "duplicate denom",
|
||||||
args: args{
|
args: args{
|
||||||
@ -201,7 +250,6 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
suite.Require().Error(err, tc.name)
|
suite.Require().Error(err, tc.name)
|
||||||
suite.Require().Contains(err.Error(), tc.expectedErr)
|
suite.Require().Contains(err.Error(), tc.expectedErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
@ -12,14 +13,18 @@ type AssetSupply struct {
|
|||||||
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"`
|
||||||
|
TimeLimitedCurrentSupply sdk.Coin `json:"time_limited_current_supply" yaml:"time_limited_current_supply"`
|
||||||
|
TimeElapsed time.Duration `json:"time_elapsed" yaml:"time_elapsed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAssetSupply initializes a new AssetSupply
|
// NewAssetSupply initializes a new AssetSupply
|
||||||
func NewAssetSupply(incomingSupply, outgoingSupply, currentSupply sdk.Coin) AssetSupply {
|
func NewAssetSupply(incomingSupply, outgoingSupply, currentSupply, timeLimitedSupply sdk.Coin, timeElapsed time.Duration) AssetSupply {
|
||||||
return AssetSupply{
|
return AssetSupply{
|
||||||
IncomingSupply: incomingSupply,
|
IncomingSupply: incomingSupply,
|
||||||
OutgoingSupply: outgoingSupply,
|
OutgoingSupply: outgoingSupply,
|
||||||
CurrentSupply: currentSupply,
|
CurrentSupply: currentSupply,
|
||||||
|
TimeLimitedCurrentSupply: timeLimitedSupply,
|
||||||
|
TimeElapsed: timeElapsed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,19 +39,28 @@ 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.TimeLimitedCurrentSupply.IsValid() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "time-limited current supply %s", a.CurrentSupply)
|
||||||
|
}
|
||||||
denom := a.CurrentSupply.Denom
|
denom := a.CurrentSupply.Denom
|
||||||
if (a.IncomingSupply.Denom != denom) ||
|
if (a.IncomingSupply.Denom != denom) ||
|
||||||
(a.OutgoingSupply.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)
|
(a.TimeLimitedCurrentSupply.Denom != denom) {
|
||||||
|
return fmt.Errorf("asset supply denoms do not match %s %s %s %s", a.CurrentSupply.Denom, a.IncomingSupply.Denom, a.OutgoingSupply.Denom, a.TimeLimitedCurrentSupply.Denom)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal returns if two asset supplies are equal
|
// Equal returns if two asset supplies are equal
|
||||||
func (a AssetSupply) Equal(b AssetSupply) bool {
|
func (a AssetSupply) Equal(b AssetSupply) bool {
|
||||||
|
if a.GetDenom() != b.GetDenom() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return (a.IncomingSupply.IsEqual(b.IncomingSupply) &&
|
return (a.IncomingSupply.IsEqual(b.IncomingSupply) &&
|
||||||
a.CurrentSupply.IsEqual(b.CurrentSupply) &&
|
a.CurrentSupply.IsEqual(b.CurrentSupply) &&
|
||||||
a.OutgoingSupply.IsEqual(b.OutgoingSupply))
|
a.OutgoingSupply.IsEqual(b.OutgoingSupply) &&
|
||||||
|
a.TimeLimitedCurrentSupply.IsEqual(b.TimeLimitedCurrentSupply) &&
|
||||||
|
a.TimeElapsed == b.TimeElapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements stringer
|
// String implements stringer
|
||||||
@ -56,8 +70,10 @@ func (a AssetSupply) String() string {
|
|||||||
Incoming supply: %s
|
Incoming supply: %s
|
||||||
Outgoing supply: %s
|
Outgoing supply: %s
|
||||||
Current supply: %s
|
Current supply: %s
|
||||||
|
Time-limited current cupply: %s
|
||||||
|
Time elapsed: %s
|
||||||
`,
|
`,
|
||||||
a.IncomingSupply, a.OutgoingSupply, a.CurrentSupply)
|
a.IncomingSupply, a.OutgoingSupply, a.CurrentSupply, a.TimeLimitedCurrentSupply, a.TimeElapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDenom getter method for the denom of the asset supply
|
// GetDenom getter method for the denom of the asset supply
|
||||||
|
@ -2,6 +2,7 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -17,7 +18,7 @@ func TestAssetSupplyValidate(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
msg: "valid asset",
|
msg: "valid asset",
|
||||||
asset: NewAssetSupply(coin, coin, coin),
|
asset: NewAssetSupply(coin, coin, coin, coin, time.Duration(0)),
|
||||||
expPass: true,
|
expPass: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -42,6 +43,27 @@ func TestAssetSupplyValidate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"invalid time limitedcurrent supply",
|
||||||
|
AssetSupply{
|
||||||
|
IncomingSupply: coin,
|
||||||
|
OutgoingSupply: coin,
|
||||||
|
CurrentSupply: coin,
|
||||||
|
TimeLimitedCurrentSupply: invalidCoin,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"non matching denoms",
|
||||||
|
AssetSupply{
|
||||||
|
IncomingSupply: coin,
|
||||||
|
OutgoingSupply: coin,
|
||||||
|
CurrentSupply: coin,
|
||||||
|
TimeLimitedCurrentSupply: sdk.NewCoin("lol", sdk.ZeroInt()),
|
||||||
|
TimeElapsed: time.Hour,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@ -53,3 +75,47 @@ func TestAssetSupplyValidate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAssetSupplyEquality(t *testing.T) {
|
||||||
|
coin := sdk.NewCoin("test", sdk.OneInt())
|
||||||
|
coin2 := sdk.NewCoin("other", sdk.OneInt())
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
asset1 AssetSupply
|
||||||
|
asset2 AssetSupply
|
||||||
|
expPass bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "equal",
|
||||||
|
asset1: NewAssetSupply(coin, coin, coin, coin, time.Duration(0)),
|
||||||
|
asset2: NewAssetSupply(coin, coin, coin, coin, time.Duration(0)),
|
||||||
|
expPass: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not equal duration",
|
||||||
|
asset1: NewAssetSupply(coin, coin, coin, coin, time.Duration(0)),
|
||||||
|
asset2: NewAssetSupply(coin, coin, coin, coin, time.Duration(1)),
|
||||||
|
expPass: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not equal coin amount",
|
||||||
|
asset1: NewAssetSupply(coin, coin, coin, coin, time.Duration(0)),
|
||||||
|
asset2: NewAssetSupply(sdk.NewCoin("test", sdk.ZeroInt()), coin, coin, coin, time.Duration(1)),
|
||||||
|
expPass: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not equal coin denom",
|
||||||
|
asset1: NewAssetSupply(coin, coin, coin, coin, time.Duration(0)),
|
||||||
|
asset2: NewAssetSupply(coin2, coin2, coin2, coin2, time.Duration(1)),
|
||||||
|
expPass: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
if tc.expPass {
|
||||||
|
require.True(t, tc.asset1.Equal(tc.asset2), tc.name)
|
||||||
|
} else {
|
||||||
|
require.False(t, tc.asset1.Equal(tc.asset2), tc.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package keeper_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
@ -85,7 +86,12 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
|
|||||||
bep3types.AssetParam{
|
bep3types.AssetParam{
|
||||||
Denom: "bnb",
|
Denom: "bnb",
|
||||||
CoinID: 714,
|
CoinID: 714,
|
||||||
SupplyLimit: sdk.NewInt(350000000000000),
|
SupplyLimit: bep3types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(350000000000000),
|
||||||
|
TimeLimited: false,
|
||||||
|
TimeBasedLimit: sdk.ZeroInt(),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: testDeputy,
|
DeputyAddress: testDeputy,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -97,7 +103,12 @@ func (suite *PermissionTestSuite) TestSubParamChangePermission_Allows() {
|
|||||||
bep3types.AssetParam{
|
bep3types.AssetParam{
|
||||||
Denom: "inc",
|
Denom: "inc",
|
||||||
CoinID: 9999,
|
CoinID: 9999,
|
||||||
SupplyLimit: sdk.NewInt(100),
|
SupplyLimit: bep3types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(100000000000000),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(50000000000),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: false,
|
Active: false,
|
||||||
DeputyAddress: testDeputy,
|
DeputyAddress: testDeputy,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
@ -58,7 +59,7 @@ func RandomizedGenState(simState *module.SimulationState) {
|
|||||||
[]types.Proposal{},
|
[]types.Proposal{},
|
||||||
[]types.Vote{},
|
[]types.Vote{},
|
||||||
)
|
)
|
||||||
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, []byte{})
|
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, genesisState))
|
||||||
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesisState)
|
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesisState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
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"
|
||||||
@ -161,7 +163,12 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
|
|||||||
bep3types.AssetParam{
|
bep3types.AssetParam{
|
||||||
Denom: "btc",
|
Denom: "btc",
|
||||||
CoinID: 0,
|
CoinID: 0,
|
||||||
SupplyLimit: sdk.NewInt(100),
|
SupplyLimit: bep3types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(100),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(50000000000),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: false,
|
Active: false,
|
||||||
DeputyAddress: deputyAddress,
|
DeputyAddress: deputyAddress,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -173,7 +180,12 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
|
|||||||
bep3types.AssetParam{
|
bep3types.AssetParam{
|
||||||
Denom: "bnb",
|
Denom: "bnb",
|
||||||
CoinID: 714,
|
CoinID: 714,
|
||||||
SupplyLimit: sdk.NewInt(350000000000000),
|
SupplyLimit: bep3types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(350000000000000),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(50000000000),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: deputyAddress,
|
DeputyAddress: deputyAddress,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -185,7 +197,12 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
|
|||||||
bep3types.AssetParam{
|
bep3types.AssetParam{
|
||||||
Denom: "xrp",
|
Denom: "xrp",
|
||||||
CoinID: 414,
|
CoinID: 414,
|
||||||
SupplyLimit: sdk.NewInt(350000000000000),
|
SupplyLimit: bep3types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(350000000000000),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(50000000000),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: deputyAddress,
|
DeputyAddress: deputyAddress,
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -200,10 +217,12 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
|
|||||||
updatedTestAPs[1] = testAPs[0]
|
updatedTestAPs[1] = testAPs[0]
|
||||||
updatedTestAPs[2] = testAPs[2]
|
updatedTestAPs[2] = testAPs[2]
|
||||||
|
|
||||||
updatedTestAPs[0].SupplyLimit = i(1000) // btc
|
updatedTestAPs[0].SupplyLimit.Limit = i(1000) // btc
|
||||||
updatedTestAPs[1].Active = false // bnb
|
updatedTestAPs[1].Active = false // bnb
|
||||||
updatedTestAPs[2].SupplyLimit = i(1000) // xrp
|
updatedTestAPs[2].SupplyLimit.Limit = i(1000) // xrp
|
||||||
updatedTestAPs[2].Active = false // xrp
|
updatedTestAPs[2].Active = false // xrp
|
||||||
|
updatedTestAPs[2].MinBlockLock = uint64(210) // xrp
|
||||||
|
updatedTestAPs[2].MaxSwapAmount = sdk.NewInt(10000000000000)
|
||||||
|
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
@ -228,6 +247,8 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
|
|||||||
CoinID: true,
|
CoinID: true,
|
||||||
Limit: true,
|
Limit: true,
|
||||||
Active: true,
|
Active: true,
|
||||||
|
MaxSwapAmount: true,
|
||||||
|
MinBlockLock: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
current: testAPs[:2],
|
current: testAPs[:2],
|
||||||
@ -268,6 +289,8 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParams_Allows() {
|
|||||||
Denom: "xrp",
|
Denom: "xrp",
|
||||||
Limit: true,
|
Limit: true,
|
||||||
Active: true,
|
Active: true,
|
||||||
|
MaxSwapAmount: true,
|
||||||
|
MinBlockLock: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
current: testAPs,
|
current: testAPs,
|
||||||
@ -598,7 +621,12 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParam_Allows() {
|
|||||||
testAP := bep3types.AssetParam{
|
testAP := bep3types.AssetParam{
|
||||||
Denom: "usdx",
|
Denom: "usdx",
|
||||||
CoinID: 999,
|
CoinID: 999,
|
||||||
SupplyLimit: sdk.NewInt(1000000000),
|
SupplyLimit: bep3types.SupplyLimit{
|
||||||
|
Limit: sdk.NewInt(350000000000000),
|
||||||
|
TimeLimited: true,
|
||||||
|
TimeBasedLimit: sdk.NewInt(50000000000),
|
||||||
|
TimePeriod: time.Hour,
|
||||||
|
},
|
||||||
Active: true,
|
Active: true,
|
||||||
DeputyAddress: sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1"))),
|
DeputyAddress: sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1"))),
|
||||||
FixedFee: sdk.NewInt(1000),
|
FixedFee: sdk.NewInt(1000),
|
||||||
@ -611,11 +639,11 @@ func (suite *PermissionsTestSuite) TestAllowedAssetParam_Allows() {
|
|||||||
newCoinidAP.CoinID = 0
|
newCoinidAP.CoinID = 0
|
||||||
|
|
||||||
newLimitAP := testAP
|
newLimitAP := testAP
|
||||||
newLimitAP.SupplyLimit = i(1000)
|
newLimitAP.SupplyLimit.Limit = i(1000)
|
||||||
|
|
||||||
newCoinidAndLimitAP := testAP
|
newCoinidAndLimitAP := testAP
|
||||||
newCoinidAndLimitAP.CoinID = 0
|
newCoinidAndLimitAP.CoinID = 0
|
||||||
newCoinidAndLimitAP.SupplyLimit = i(1000)
|
newCoinidAndLimitAP.SupplyLimit.Limit = i(1000)
|
||||||
|
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -465,19 +465,25 @@ func (aaps AllowedAssetParams) Allows(current, incoming bep3types.AssetParams) b
|
|||||||
return allAllowed
|
return allAllowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllowedAssetParam bep3 asset parameters that can be changed by committee
|
||||||
type AllowedAssetParam struct {
|
type AllowedAssetParam struct {
|
||||||
Denom string `json:"denom" yaml:"denom"`
|
Denom string `json:"denom" yaml:"denom"`
|
||||||
CoinID bool `json:"coin_id" yaml:"coin_id"`
|
CoinID bool `json:"coin_id" yaml:"coin_id"`
|
||||||
Limit bool `json:"limit" yaml:"limit"`
|
Limit bool `json:"limit" yaml:"limit"`
|
||||||
Active bool `json:"active" yaml:"active"`
|
Active bool `json:"active" yaml:"active"`
|
||||||
|
MaxSwapAmount bool `json:"max_swap_amount" yaml:"max_swap_amount"`
|
||||||
|
MinBlockLock bool `json:"min_block_lock" yaml:"min_block_lock"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allows bep3 AssetParam parameters than can be changed by committee
|
||||||
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.SupplyLimit.Equal(incoming.SupplyLimit) || aap.Limit) &&
|
(current.SupplyLimit.Equals(incoming.SupplyLimit) || aap.Limit) &&
|
||||||
((current.Active == incoming.Active) || aap.Active)
|
((current.Active == incoming.Active) || aap.Active) &&
|
||||||
|
((current.MaxSwapAmount.Equal(incoming.MaxSwapAmount)) || aap.MaxSwapAmount) &&
|
||||||
|
((current.MinBlockLock == incoming.MinBlockLock) || aap.MinBlockLock)
|
||||||
return allowed
|
return allowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user