mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10:05:18 +00:00
[R4R] Add BnbDeputyFixedFee param to BEP3 module (#511)
* generate length 32 random bytes * fix test * implement BnbDeputyFixedFee param * clean up for PR * update deputy address * remove impossible check * move comment
This commit is contained in:
parent
fa8ae9647a
commit
dd1d248be2
@ -47,7 +47,8 @@
|
|||||||
"assets_supplies": [],
|
"assets_supplies": [],
|
||||||
"atomic_swaps": [],
|
"atomic_swaps": [],
|
||||||
"params": {
|
"params": {
|
||||||
"bnb_deputy_address": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
|
"bnb_deputy_address": "kava1aphsdnz5hu2t5ty2au6znprug5kx3zpy6zwq29",
|
||||||
|
"bnb_deputy_fixed_fee": "1000",
|
||||||
"max_block_lock": "600",
|
"max_block_lock": "600",
|
||||||
"min_block_lock": "80",
|
"min_block_lock": "80",
|
||||||
"supported_assets": [
|
"supported_assets": [
|
||||||
|
@ -67,6 +67,15 @@ func (suite *GenesisTestSuite) TestGenesisState() {
|
|||||||
},
|
},
|
||||||
expectPass: true,
|
expectPass: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "0 deputy fees",
|
||||||
|
genState: func() app.GenesisState {
|
||||||
|
gs := baseGenState(suite.addrs[0])
|
||||||
|
gs.Params.BnbDeputyFixedFee = 0
|
||||||
|
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||||
|
},
|
||||||
|
expectPass: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "incoming supply doesn't match amount in incoming atomic swaps",
|
name: "incoming supply doesn't match amount in incoming atomic swaps",
|
||||||
genState: func() app.GenesisState {
|
genState: func() app.GenesisState {
|
||||||
|
@ -31,10 +31,11 @@ func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amo
|
|||||||
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
|
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
|
||||||
func ts(minOffset int) int64 { return tmtime.Now().Add(time.Duration(minOffset) * time.Minute).Unix() }
|
func ts(minOffset int) int64 { return tmtime.Now().Add(time.Duration(minOffset) * time.Minute).Unix() }
|
||||||
|
|
||||||
func NewBep3GenStateMulti(deputy sdk.AccAddress) app.GenesisState {
|
func NewBep3GenStateMulti(deputyAddress sdk.AccAddress) app.GenesisState {
|
||||||
bep3Genesis := types.GenesisState{
|
bep3Genesis := types.GenesisState{
|
||||||
Params: bep3.Params{
|
Params: bep3.Params{
|
||||||
BnbDeputyAddress: deputy,
|
BnbDeputyAddress: deputyAddress,
|
||||||
|
BnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee, // 1000
|
||||||
MinBlockLock: types.DefaultMinBlockLock, // 80
|
MinBlockLock: types.DefaultMinBlockLock, // 80
|
||||||
MaxBlockLock: types.DefaultMaxBlockLock, // 360
|
MaxBlockLock: types.DefaultMaxBlockLock, // 360
|
||||||
SupportedAssets: types.AssetParams{
|
SupportedAssets: types.AssetParams{
|
||||||
|
@ -24,6 +24,12 @@ func (k Keeper) GetBnbDeputyAddress(ctx sdk.Context) sdk.AccAddress {
|
|||||||
return params.BnbDeputyAddress
|
return params.BnbDeputyAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBnbDeputyFixedFee returns the deputy's fixed fee
|
||||||
|
func (k Keeper) GetBnbDeputyFixedFee(ctx sdk.Context) uint64 {
|
||||||
|
params := k.GetParams(ctx)
|
||||||
|
return params.BnbDeputyFixedFee
|
||||||
|
}
|
||||||
|
|
||||||
// GetMaxBlockLock returns the maximum block lock
|
// GetMaxBlockLock returns the maximum block lock
|
||||||
func (k Keeper) GetMaxBlockLock(ctx sdk.Context) uint64 {
|
func (k Keeper) GetMaxBlockLock(ctx sdk.Context) uint64 {
|
||||||
params := k.GetParams(ctx)
|
params := k.GetParams(ctx)
|
||||||
|
@ -46,6 +46,14 @@ func (suite *ParamsTestSuite) TestGetSetBnbDeputyAddress() {
|
|||||||
suite.Equal(suite.addrs[1], addr)
|
suite.Equal(suite.addrs[1], addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ParamsTestSuite) TestGetBnbDeputyFixedFee() {
|
||||||
|
params := suite.keeper.GetParams(suite.ctx)
|
||||||
|
bnbDeputyFixedFee := params.BnbDeputyFixedFee
|
||||||
|
|
||||||
|
res := suite.keeper.GetBnbDeputyFixedFee(suite.ctx)
|
||||||
|
suite.Equal(bnbDeputyFixedFee, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *ParamsTestSuite) TestGetMaxBlockLock() {
|
func (suite *ParamsTestSuite) TestGetMaxBlockLock() {
|
||||||
params := suite.keeper.GetParams(suite.ctx)
|
params := suite.keeper.GetParams(suite.ctx)
|
||||||
maxBlockLock := params.MaxBlockLock
|
maxBlockLock := params.MaxBlockLock
|
||||||
|
@ -62,13 +62,17 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times
|
|||||||
newAcc := k.accountKeeper.NewAccountWithAddress(ctx, recipient)
|
newAcc := k.accountKeeper.NewAccountWithAddress(ctx, recipient)
|
||||||
k.accountKeeper.SetAccount(ctx, newAcc)
|
k.accountKeeper.SetAccount(ctx, newAcc)
|
||||||
}
|
}
|
||||||
|
// Incoming swaps have already had their fees collected by the deputy during the relay process.
|
||||||
err = k.IncrementIncomingAssetSupply(ctx, amount[0])
|
err = k.IncrementIncomingAssetSupply(ctx, amount[0])
|
||||||
case types.Outgoing:
|
case types.Outgoing:
|
||||||
|
// Amount in outgoing swaps must be greater than the deputy's fixed fee.
|
||||||
|
if amount[0].Amount.Uint64() <= k.GetBnbDeputyFixedFee(ctx) {
|
||||||
|
return sdkerrors.Wrap(types.ErrInsufficientAmount, amount[0].String())
|
||||||
|
}
|
||||||
err = k.IncrementOutgoingAssetSupply(ctx, amount[0])
|
err = k.IncrementOutgoingAssetSupply(ctx, amount[0])
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("invalid swap direction: %s", direction.String())
|
err = fmt.Errorf("invalid swap direction: %s", direction.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -142,12 +142,31 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"unsupported asset",
|
|
||||||
|
"outgoing swap amount not greater than fixed fee",
|
||||||
currentTmTime,
|
currentTmTime,
|
||||||
args{
|
args{
|
||||||
randomNumberHash: suite.randomNumberHashes[1],
|
randomNumberHash: suite.randomNumberHashes[1],
|
||||||
timestamp: suite.timestamps[1],
|
timestamp: suite.timestamps[1],
|
||||||
heightSpan: uint64(360),
|
heightSpan: uint64(360),
|
||||||
|
sender: suite.addrs[1],
|
||||||
|
recipient: suite.addrs[2],
|
||||||
|
senderOtherChain: TestSenderOtherChain,
|
||||||
|
recipientOtherChain: TestRecipientOtherChain,
|
||||||
|
coins: cs(c(BNB_DENOM, int64(suite.keeper.GetBnbDeputyFixedFee(suite.ctx)))),
|
||||||
|
crossChain: true,
|
||||||
|
direction: types.Outgoing,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupported asset",
|
||||||
|
currentTmTime,
|
||||||
|
args{
|
||||||
|
randomNumberHash: suite.randomNumberHashes[2],
|
||||||
|
timestamp: suite.timestamps[2],
|
||||||
|
heightSpan: uint64(360),
|
||||||
sender: suite.deputy,
|
sender: suite.deputy,
|
||||||
recipient: suite.addrs[2],
|
recipient: suite.addrs[2],
|
||||||
senderOtherChain: TestSenderOtherChain,
|
senderOtherChain: TestSenderOtherChain,
|
||||||
@ -160,11 +179,11 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"past timestamp",
|
"outside timestamp range",
|
||||||
currentTmTime,
|
currentTmTime,
|
||||||
args{
|
args{
|
||||||
randomNumberHash: suite.randomNumberHashes[2],
|
randomNumberHash: suite.randomNumberHashes[3],
|
||||||
timestamp: suite.timestamps[2] - 2000,
|
timestamp: suite.timestamps[3] - 2000,
|
||||||
heightSpan: uint64(360),
|
heightSpan: uint64(360),
|
||||||
sender: suite.deputy,
|
sender: suite.deputy,
|
||||||
recipient: suite.addrs[3],
|
recipient: suite.addrs[3],
|
||||||
@ -181,8 +200,8 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
"future timestamp",
|
"future timestamp",
|
||||||
currentTmTime,
|
currentTmTime,
|
||||||
args{
|
args{
|
||||||
randomNumberHash: suite.randomNumberHashes[3],
|
randomNumberHash: suite.randomNumberHashes[4],
|
||||||
timestamp: suite.timestamps[3] + 5000,
|
timestamp: suite.timestamps[4] + 5000,
|
||||||
heightSpan: uint64(360),
|
heightSpan: uint64(360),
|
||||||
sender: suite.deputy,
|
sender: suite.deputy,
|
||||||
recipient: suite.addrs[4],
|
recipient: suite.addrs[4],
|
||||||
@ -199,8 +218,8 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
"small height span",
|
"small height span",
|
||||||
currentTmTime,
|
currentTmTime,
|
||||||
args{
|
args{
|
||||||
randomNumberHash: suite.randomNumberHashes[4],
|
randomNumberHash: suite.randomNumberHashes[5],
|
||||||
timestamp: suite.timestamps[4],
|
timestamp: suite.timestamps[5],
|
||||||
heightSpan: uint64(5),
|
heightSpan: uint64(5),
|
||||||
sender: suite.deputy,
|
sender: suite.deputy,
|
||||||
recipient: suite.addrs[5],
|
recipient: suite.addrs[5],
|
||||||
@ -217,8 +236,8 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
"big height span",
|
"big height span",
|
||||||
currentTmTime,
|
currentTmTime,
|
||||||
args{
|
args{
|
||||||
randomNumberHash: suite.randomNumberHashes[5],
|
randomNumberHash: suite.randomNumberHashes[6],
|
||||||
timestamp: suite.timestamps[5],
|
timestamp: suite.timestamps[6],
|
||||||
heightSpan: uint64(1000000),
|
heightSpan: uint64(1000000),
|
||||||
sender: suite.deputy,
|
sender: suite.deputy,
|
||||||
recipient: suite.addrs[6],
|
recipient: suite.addrs[6],
|
||||||
@ -235,8 +254,8 @@ func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
|||||||
"zero amount",
|
"zero amount",
|
||||||
currentTmTime,
|
currentTmTime,
|
||||||
args{
|
args{
|
||||||
randomNumberHash: suite.randomNumberHashes[6],
|
randomNumberHash: suite.randomNumberHashes[7],
|
||||||
timestamp: suite.timestamps[6],
|
timestamp: suite.timestamps[7],
|
||||||
heightSpan: uint64(360),
|
heightSpan: uint64(360),
|
||||||
sender: suite.deputy,
|
sender: suite.deputy,
|
||||||
recipient: suite.addrs[7],
|
recipient: suite.addrs[7],
|
||||||
|
@ -36,6 +36,13 @@ func GenRandBnbDeputy(r *rand.Rand) simulation.Account {
|
|||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenRandBnbDeputyFixedFee randomized BnbDeputyFixedFee in range [2, 10000]
|
||||||
|
func GenRandBnbDeputyFixedFee(r *rand.Rand) uint64 {
|
||||||
|
min := int(2)
|
||||||
|
max := int(10000)
|
||||||
|
return uint64(r.Intn(max-min) + min)
|
||||||
|
}
|
||||||
|
|
||||||
// GenMinBlockLock randomized MinBlockLock
|
// GenMinBlockLock randomized MinBlockLock
|
||||||
func GenMinBlockLock(r *rand.Rand) uint64 {
|
func GenMinBlockLock(r *rand.Rand) uint64 {
|
||||||
min := int(types.AbsoluteMinimumBlockLock)
|
min := int(types.AbsoluteMinimumBlockLock)
|
||||||
@ -100,6 +107,7 @@ func RandomizedGenState(simState *module.SimulationState) {
|
|||||||
|
|
||||||
func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState {
|
func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState {
|
||||||
bnbDeputy := GenRandBnbDeputy(simState.Rand)
|
bnbDeputy := GenRandBnbDeputy(simState.Rand)
|
||||||
|
bnbDeputyFixedFee := GenRandBnbDeputyFixedFee(simState.Rand)
|
||||||
|
|
||||||
// min/max block lock are hardcoded to 50/100 for expected -NumBlocks=100
|
// min/max block lock are hardcoded to 50/100 for expected -NumBlocks=100
|
||||||
minBlockLock := types.AbsoluteMinimumBlockLock
|
minBlockLock := types.AbsoluteMinimumBlockLock
|
||||||
@ -114,6 +122,7 @@ func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState
|
|||||||
bep3Genesis := types.GenesisState{
|
bep3Genesis := types.GenesisState{
|
||||||
Params: types.Params{
|
Params: types.Params{
|
||||||
BnbDeputyAddress: bnbDeputy.Address,
|
BnbDeputyAddress: bnbDeputy.Address,
|
||||||
|
BnbDeputyFixedFee: bnbDeputyFixedFee,
|
||||||
MinBlockLock: minBlockLock,
|
MinBlockLock: minBlockLock,
|
||||||
MaxBlockLock: maxBlockLock,
|
MaxBlockLock: maxBlockLock,
|
||||||
SupportedAssets: supportedAssets,
|
SupportedAssets: supportedAssets,
|
||||||
|
@ -65,10 +65,11 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Search for an account that holds coins received by an atomic swap
|
// Search for an account that holds coins received by an atomic swap
|
||||||
|
bnbDeputyFixedFee := k.GetBnbDeputyFixedFee(ctx)
|
||||||
senderOut, asset, found := findValidAccountAssetSupplyPair(accs, supplies, func(acc simulation.Account, asset types.AssetSupply) bool {
|
senderOut, asset, found := findValidAccountAssetSupplyPair(accs, supplies, func(acc simulation.Account, asset types.AssetSupply) bool {
|
||||||
if asset.CurrentSupply.Amount.IsPositive() {
|
if asset.CurrentSupply.Amount.IsPositive() {
|
||||||
authAcc := ak.GetAccount(ctx, acc.Address)
|
authAcc := ak.GetAccount(ctx, acc.Address)
|
||||||
if authAcc.SpendableCoins(ctx.BlockTime()).AmountOf(asset.Denom).IsPositive() {
|
if authAcc.SpendableCoins(ctx.BlockTime()).AmountOf(asset.Denom).GT(sdk.NewIntFromUint64(bnbDeputyFixedFee)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,7 +130,7 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula
|
|||||||
|
|
||||||
// Get an amount of coins between 0.1 and 2% of total coins
|
// Get an amount of coins between 0.1 and 2% of total coins
|
||||||
amount := maximumAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000))))
|
amount := maximumAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000))))
|
||||||
if amount.IsZero() {
|
if amount.LT(sdk.NewIntFromUint64(bnbDeputyFixedFee)) {
|
||||||
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", denom), "", false, nil), nil, nil
|
return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", denom), "", false, nil), nil, nil
|
||||||
}
|
}
|
||||||
coins := sdk.NewCoins(sdk.NewCoin(denom, amount))
|
coins := sdk.NewCoins(sdk.NewCoin(denom, amount))
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
keyBnbDeputyAddress = "BnbDeputyAddress"
|
keyBnbDeputyAddress = "BnbDeputyAddress"
|
||||||
|
keyBnbDeputyFixedFee = "BnbDeputyFixedFee"
|
||||||
keyMinBlockLock = "MinBlockLock"
|
keyMinBlockLock = "MinBlockLock"
|
||||||
keyMaxBlockLock = "MaxBlockLock"
|
keyMaxBlockLock = "MaxBlockLock"
|
||||||
keySupportedAssets = "SupportedAssets"
|
keySupportedAssets = "SupportedAssets"
|
||||||
@ -27,6 +28,11 @@ func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
|||||||
return fmt.Sprintf("\"%s\"", GenRandBnbDeputy(r).Address)
|
return fmt.Sprintf("\"%s\"", GenRandBnbDeputy(r).Address)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyFixedFee,
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%d\"", GenRandBnbDeputyFixedFee(r))
|
||||||
|
},
|
||||||
|
),
|
||||||
simulation.NewSimParamChange(types.ModuleName, keyMinBlockLock,
|
simulation.NewSimParamChange(types.ModuleName, keyMinBlockLock,
|
||||||
func(r *rand.Rand) string {
|
func(r *rand.Rand) string {
|
||||||
return fmt.Sprintf("\"%d\"", minBlockLockVal)
|
return fmt.Sprintf("\"%d\"", minBlockLockVal)
|
||||||
|
@ -8,8 +8,9 @@
|
|||||||
// Params governance parameters for bep3 module
|
// Params governance parameters for bep3 module
|
||||||
type Params struct {
|
type Params struct {
|
||||||
BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // deputy's address on Kava
|
BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // deputy's address on Kava
|
||||||
MinBlockLock int64 `json:"min_block_lock" yaml:"min_block_lock"` // minimum swap expire height
|
BnbDeputyFixedFee uint64 `json:"bnb_deputy_fixed_fee" yaml:"bnb_deputy_fixed_fee"` // deputy's fixed fee
|
||||||
MaxBlockLock int64 `json:"max_block_lock" yaml:"max_block_lock"` // maximum swap expire height
|
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // minimum swap expire height
|
||||||
|
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // maximum swap expire height
|
||||||
SupportedAssets AssetParams `json:"supported_assets" yaml:"supported_assets"` // array of supported asset
|
SupportedAssets AssetParams `json:"supported_assets" yaml:"supported_assets"` // array of supported asset
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +77,6 @@ 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"`
|
||||||
Limit sdk.Coin `json:"limit" yaml:"limit"`
|
SupplyLimit sdk.Coin `json:"supply_limit" yaml:"supply_limit"`
|
||||||
}
|
}
|
||||||
```
|
```
|
@ -5,8 +5,9 @@ The bep3 module contains the following parameters:
|
|||||||
| Key | Type | Example | Description |
|
| Key | Type | Example | Description |
|
||||||
|-------------------|-------------------------|-----------------------------------------------|-------------------------------|
|
|-------------------|-------------------------|-----------------------------------------------|-------------------------------|
|
||||||
| BnbDeputyAddress | string (sdk.AccAddress) | "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj" | deputy's Kava address |
|
| BnbDeputyAddress | string (sdk.AccAddress) | "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj" | deputy's Kava address |
|
||||||
| MinBlockLock | int64 | 80 | minimum swap expire height |
|
| BnbDeputyFixedFee | uint64 | 1000 | deputy's fixed bnb fee |
|
||||||
| MaxBlockLock | int64 | 600 | maximum swap expire height |
|
| MinBlockLock | uint64 | 80 | minimum swap expire height |
|
||||||
|
| MaxBlockLock | uint64 | 600 | maximum swap expire height |
|
||||||
| SupportedAssets | AssetParams | []AssetParam | array of supported assets |
|
| SupportedAssets | AssetParams | []AssetParam | array of supported assets |
|
||||||
|-------------------|-------------------------|-----------------------------------------------|-------------------------------|
|
|-------------------|-------------------------|-----------------------------------------------|-------------------------------|
|
||||||
| AssetParam | AssetParam | AssetParam{"bnb", 714, sdk.NewInt(100), true} | a supported asset |
|
| AssetParam | AssetParam | AssetParam{"bnb", 714, sdk.NewInt(100), true} | a supported asset |
|
||||||
|
@ -9,32 +9,34 @@ import (
|
|||||||
var (
|
var (
|
||||||
// ErrInvalidTimestamp error for when an timestamp is outside of bounds. Assumes block time of 10 seconds.
|
// ErrInvalidTimestamp error for when an timestamp is outside of bounds. Assumes block time of 10 seconds.
|
||||||
ErrInvalidTimestamp = sdkerrors.Register(ModuleName, 2, "timestamp can neither be 15 minutes ahead of the current time, nor 30 minutes later")
|
ErrInvalidTimestamp = sdkerrors.Register(ModuleName, 2, "timestamp can neither be 15 minutes ahead of the current time, nor 30 minutes later")
|
||||||
// ErrInvalidHeightSpan error a proposed height span is outside of lock time range
|
// ErrInvalidHeightSpan error for when a proposed height span is outside of lock time range
|
||||||
ErrInvalidHeightSpan = sdkerrors.Register(ModuleName, 3, "height span is outside acceptable range")
|
ErrInvalidHeightSpan = sdkerrors.Register(ModuleName, 3, "height span is outside acceptable range")
|
||||||
|
// ErrInsufficientAmount error for when a swap's amount is less than the deputy fixed fee
|
||||||
|
ErrInsufficientAmount = sdkerrors.Register(ModuleName, 4, "amount must be greater than the deputy fixed fee")
|
||||||
// ErrAssetNotSupported error for when an asset is not supported
|
// ErrAssetNotSupported error for when an asset is not supported
|
||||||
ErrAssetNotSupported = sdkerrors.Register(ModuleName, 4, "asset not on the list of supported assets")
|
ErrAssetNotSupported = sdkerrors.Register(ModuleName, 5, "asset not on the list of supported assets")
|
||||||
// ErrAssetNotActive error for when an asset is currently inactive
|
// ErrAssetNotActive error for when an asset is currently inactive
|
||||||
ErrAssetNotActive = sdkerrors.Register(ModuleName, 5, "asset is currently inactive")
|
ErrAssetNotActive = sdkerrors.Register(ModuleName, 6, "asset is currently inactive")
|
||||||
// ErrAssetSupplyNotFound error for when an asset's supply is not found in the store
|
// ErrAssetSupplyNotFound error for when an asset's supply is not found in the store
|
||||||
ErrAssetSupplyNotFound = sdkerrors.Register(ModuleName, 6, "asset supply not found in store")
|
ErrAssetSupplyNotFound = sdkerrors.Register(ModuleName, 7, "asset supply not found in store")
|
||||||
// ErrExceedsSupplyLimit error for when the proposed supply increase would put the supply above limit
|
// ErrExceedsSupplyLimit error for when the proposed supply increase would put the supply above limit
|
||||||
ErrExceedsSupplyLimit = sdkerrors.Register(ModuleName, 7, "asset supply over limit")
|
ErrExceedsSupplyLimit = sdkerrors.Register(ModuleName, 8, "asset supply over limit")
|
||||||
// ErrExceedsAvailableSupply error for when the proposed outgoing amount exceeds the total available supply
|
// ErrExceedsAvailableSupply error for when the proposed outgoing amount exceeds the total available supply
|
||||||
ErrExceedsAvailableSupply = sdkerrors.Register(ModuleName, 8, "outgoing swap exceeds total available supply")
|
ErrExceedsAvailableSupply = sdkerrors.Register(ModuleName, 9, "outgoing swap exceeds total available supply")
|
||||||
// ErrInvalidCurrentSupply error for when the proposed decrease would result in a negative current supplyx
|
// ErrInvalidCurrentSupply error for when the proposed decrease would result in a negative current supplyx
|
||||||
ErrInvalidCurrentSupply = sdkerrors.Register(ModuleName, 9, "supply decrease puts current asset supply below 0")
|
ErrInvalidCurrentSupply = sdkerrors.Register(ModuleName, 10, "supply decrease puts current asset supply below 0")
|
||||||
// ErrInvalidIncomingSupply error for when the proposed decrease would result in a negative incoming supply
|
// ErrInvalidIncomingSupply error for when the proposed decrease would result in a negative incoming supply
|
||||||
ErrInvalidIncomingSupply = sdkerrors.Register(ModuleName, 10, "supply decrease puts incoming asset supply below 0")
|
ErrInvalidIncomingSupply = sdkerrors.Register(ModuleName, 11, "supply decrease puts incoming asset supply below 0")
|
||||||
// ErrInvalidOutgoingSupply error for when the proposed decrease would result in a negative outgoing supply
|
// ErrInvalidOutgoingSupply error for when the proposed decrease would result in a negative outgoing supply
|
||||||
ErrInvalidOutgoingSupply = sdkerrors.Register(ModuleName, 11, "supply decrease puts outgoing asset supply below 0")
|
ErrInvalidOutgoingSupply = sdkerrors.Register(ModuleName, 12, "supply decrease puts outgoing asset supply below 0")
|
||||||
// ErrInvalidClaimSecret error when a submitted secret doesn't match an AtomicSwap's swapID
|
// ErrInvalidClaimSecret error when a submitted secret doesn't match an AtomicSwap's swapID
|
||||||
ErrInvalidClaimSecret = sdkerrors.Register(ModuleName, 12, "hashed claim attempt does not match")
|
ErrInvalidClaimSecret = sdkerrors.Register(ModuleName, 13, "hashed claim attempt does not match")
|
||||||
// ErrAtomicSwapAlreadyExists error for when an AtomicSwap with this swapID already exists
|
// ErrAtomicSwapAlreadyExists error for when an AtomicSwap with this swapID already exists
|
||||||
ErrAtomicSwapAlreadyExists = sdkerrors.Register(ModuleName, 13, "atomic swap already exists")
|
ErrAtomicSwapAlreadyExists = sdkerrors.Register(ModuleName, 14, "atomic swap already exists")
|
||||||
// ErrAtomicSwapNotFound error for when an atomic swap is not found
|
// ErrAtomicSwapNotFound error for when an atomic swap is not found
|
||||||
ErrAtomicSwapNotFound = sdkerrors.Register(ModuleName, 14, "atomic swap not found")
|
ErrAtomicSwapNotFound = sdkerrors.Register(ModuleName, 15, "atomic swap not found")
|
||||||
// ErrSwapNotRefundable error for when an AtomicSwap has not expired and cannot be refunded
|
// ErrSwapNotRefundable error for when an AtomicSwap has not expired and cannot be refunded
|
||||||
ErrSwapNotRefundable = sdkerrors.Register(ModuleName, 15, "atomic swap is still active and cannot be refunded")
|
ErrSwapNotRefundable = sdkerrors.Register(ModuleName, 16, "atomic swap is still active and cannot be refunded")
|
||||||
// ErrSwapNotClaimable error for when an atomic swap is not open and cannot be claimed
|
// ErrSwapNotClaimable error for when an atomic swap is not open and cannot be claimed
|
||||||
ErrSwapNotClaimable = sdkerrors.Register(ModuleName, 16, "atomic swap is not claimable")
|
ErrSwapNotClaimable = sdkerrors.Register(ModuleName, 17, "atomic swap is not claimable")
|
||||||
)
|
)
|
||||||
|
@ -16,10 +16,12 @@ const (
|
|||||||
// Parameter keys
|
// Parameter keys
|
||||||
var (
|
var (
|
||||||
KeyBnbDeputyAddress = []byte("BnbDeputyAddress")
|
KeyBnbDeputyAddress = []byte("BnbDeputyAddress")
|
||||||
|
KeyBnbDeputyFixedFee = []byte("BnbDeputyFixedFee")
|
||||||
KeyMinBlockLock = []byte("MinBlockLock")
|
KeyMinBlockLock = []byte("MinBlockLock")
|
||||||
KeyMaxBlockLock = []byte("MaxBlockLock")
|
KeyMaxBlockLock = []byte("MaxBlockLock")
|
||||||
KeySupportedAssets = []byte("SupportedAssets")
|
KeySupportedAssets = []byte("SupportedAssets")
|
||||||
|
|
||||||
|
DefaultBnbDeputyFixedFee uint64 = 1000
|
||||||
AbsoluteMaximumBlockLock uint64 = 10000
|
AbsoluteMaximumBlockLock uint64 = 10000
|
||||||
AbsoluteMinimumBlockLock uint64 = 50
|
AbsoluteMinimumBlockLock uint64 = 50
|
||||||
DefaultMinBlockLock uint64 = 80
|
DefaultMinBlockLock uint64 = 80
|
||||||
@ -37,6 +39,7 @@ var (
|
|||||||
// Params governance parameters for bep3 module
|
// Params governance parameters for bep3 module
|
||||||
type Params struct {
|
type Params struct {
|
||||||
BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // Bnbchain deputy address
|
BnbDeputyAddress sdk.AccAddress `json:"bnb_deputy_address" yaml:"bnb_deputy_address"` // Bnbchain deputy address
|
||||||
|
BnbDeputyFixedFee uint64 `json:"bnb_deputy_fixed_fee" yaml:"bnb_deputy_fixed_fee"` // Deputy fixed fee in BNB
|
||||||
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // AtomicSwap minimum block lock
|
MinBlockLock uint64 `json:"min_block_lock" yaml:"min_block_lock"` // AtomicSwap minimum block lock
|
||||||
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // AtomicSwap maximum block lock
|
MaxBlockLock uint64 `json:"max_block_lock" yaml:"max_block_lock"` // AtomicSwap maximum block lock
|
||||||
SupportedAssets AssetParams `json:"supported_assets" yaml:"supported_assets"` // Supported assets
|
SupportedAssets AssetParams `json:"supported_assets" yaml:"supported_assets"` // Supported assets
|
||||||
@ -46,17 +49,19 @@ type Params struct {
|
|||||||
func (p Params) String() string {
|
func (p Params) String() string {
|
||||||
return fmt.Sprintf(`Params:
|
return fmt.Sprintf(`Params:
|
||||||
Bnbchain deputy address: %s,
|
Bnbchain deputy address: %s,
|
||||||
|
Deputy fixed fee (BNB): %d,
|
||||||
Min block lock: %d,
|
Min block lock: %d,
|
||||||
Max block lock: %d,
|
Max block lock: %d,
|
||||||
Supported assets: %s`,
|
Supported assets: %s`,
|
||||||
p.BnbDeputyAddress.String(), p.MinBlockLock, p.MaxBlockLock, p.SupportedAssets)
|
p.BnbDeputyAddress.String(), p.BnbDeputyFixedFee, p.MinBlockLock, p.MaxBlockLock, p.SupportedAssets)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewParams returns a new params object
|
// NewParams returns a new params object
|
||||||
func NewParams(bnbDeputyAddress sdk.AccAddress, minBlockLock, maxBlockLock uint64, supportedAssets AssetParams,
|
func NewParams(bnbDeputyAddress sdk.AccAddress, bnbDeputyFixedFee, minBlockLock, maxBlockLock uint64, supportedAssets AssetParams,
|
||||||
) Params {
|
) Params {
|
||||||
return Params{
|
return Params{
|
||||||
BnbDeputyAddress: bnbDeputyAddress,
|
BnbDeputyAddress: bnbDeputyAddress,
|
||||||
|
BnbDeputyFixedFee: bnbDeputyFixedFee,
|
||||||
MinBlockLock: minBlockLock,
|
MinBlockLock: minBlockLock,
|
||||||
MaxBlockLock: maxBlockLock,
|
MaxBlockLock: maxBlockLock,
|
||||||
SupportedAssets: supportedAssets,
|
SupportedAssets: supportedAssets,
|
||||||
@ -70,7 +75,8 @@ func DefaultParams() Params {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewParams(defaultBnbDeputyAddress, DefaultMinBlockLock, DefaultMaxBlockLock, DefaultSupportedAssets)
|
return NewParams(defaultBnbDeputyAddress, DefaultBnbDeputyFixedFee,
|
||||||
|
DefaultMinBlockLock, DefaultMaxBlockLock, DefaultSupportedAssets)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetParam governance parameters for each asset within a supported chain
|
// AssetParam governance parameters for each asset within a supported chain
|
||||||
@ -114,6 +120,7 @@ func ParamKeyTable() params.KeyTable {
|
|||||||
func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||||
return params.ParamSetPairs{
|
return params.ParamSetPairs{
|
||||||
params.NewParamSetPair(KeyBnbDeputyAddress, &p.BnbDeputyAddress, validateBnbDeputyAddressParam),
|
params.NewParamSetPair(KeyBnbDeputyAddress, &p.BnbDeputyAddress, validateBnbDeputyAddressParam),
|
||||||
|
params.NewParamSetPair(KeyBnbDeputyFixedFee, &p.BnbDeputyFixedFee, validateBnbDeputyFixedFeeParam),
|
||||||
params.NewParamSetPair(KeyMinBlockLock, &p.MinBlockLock, validateMinBlockLockParam),
|
params.NewParamSetPair(KeyMinBlockLock, &p.MinBlockLock, validateMinBlockLockParam),
|
||||||
params.NewParamSetPair(KeyMaxBlockLock, &p.MaxBlockLock, validateMaxBlockLockParam),
|
params.NewParamSetPair(KeyMaxBlockLock, &p.MaxBlockLock, validateMaxBlockLockParam),
|
||||||
params.NewParamSetPair(KeySupportedAssets, &p.SupportedAssets, validateSupportedAssetsParams),
|
params.NewParamSetPair(KeySupportedAssets, &p.SupportedAssets, validateSupportedAssetsParams),
|
||||||
@ -126,6 +133,10 @@ func (p Params) Validate() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validateBnbDeputyFixedFeeParam(p.BnbDeputyFixedFee); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := validateMinBlockLockParam(p.MinBlockLock); err != nil {
|
if err := validateMinBlockLockParam(p.MinBlockLock); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -158,6 +169,15 @@ func validateBnbDeputyAddressParam(i interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateBnbDeputyFixedFeeParam(i interface{}) error {
|
||||||
|
_, ok := i.(uint64)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func validateMinBlockLockParam(i interface{}) error {
|
func validateMinBlockLockParam(i interface{}) error {
|
||||||
minBlockLock, ok := i.(uint64)
|
minBlockLock, ok := i.(uint64)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -29,6 +29,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
bnbDeputyAddress sdk.AccAddress
|
bnbDeputyAddress sdk.AccAddress
|
||||||
|
bnbDeputyFixedFee uint64
|
||||||
minBlockLock uint64
|
minBlockLock uint64
|
||||||
maxBlockLock uint64
|
maxBlockLock uint64
|
||||||
supportedAssets types.AssetParams
|
supportedAssets types.AssetParams
|
||||||
@ -44,6 +45,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "default",
|
name: "default",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.DefaultSupportedAssets,
|
supportedAssets: types.DefaultSupportedAssets,
|
||||||
@ -55,6 +57,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "minimum block lock below limit",
|
name: "minimum block lock below limit",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: 1,
|
minBlockLock: 1,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.DefaultSupportedAssets,
|
supportedAssets: types.DefaultSupportedAssets,
|
||||||
@ -66,6 +69,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "minimum block lock above limit",
|
name: "minimum block lock above limit",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: 500000,
|
minBlockLock: 500000,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.DefaultSupportedAssets,
|
supportedAssets: types.DefaultSupportedAssets,
|
||||||
@ -77,6 +81,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "maximum block lock below limit",
|
name: "maximum block lock below limit",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: 1,
|
maxBlockLock: 1,
|
||||||
supportedAssets: types.DefaultSupportedAssets,
|
supportedAssets: types.DefaultSupportedAssets,
|
||||||
@ -88,6 +93,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "maximum block lock above limit",
|
name: "maximum block lock above limit",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: 100000000,
|
maxBlockLock: 100000000,
|
||||||
supportedAssets: types.DefaultSupportedAssets,
|
supportedAssets: types.DefaultSupportedAssets,
|
||||||
@ -99,6 +105,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "empty asset denom",
|
name: "empty asset denom",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.AssetParams{
|
supportedAssets: types.AssetParams{
|
||||||
@ -117,6 +124,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "negative asset coin ID",
|
name: "negative asset coin ID",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.AssetParams{
|
supportedAssets: types.AssetParams{
|
||||||
@ -135,6 +143,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "negative asset limit",
|
name: "negative asset limit",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.AssetParams{
|
supportedAssets: types.AssetParams{
|
||||||
@ -153,6 +162,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "duplicate asset denom",
|
name: "duplicate asset denom",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.AssetParams{
|
supportedAssets: types.AssetParams{
|
||||||
@ -177,6 +187,7 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
name: "duplicate asset coin ID",
|
name: "duplicate asset coin ID",
|
||||||
args: args{
|
args: args{
|
||||||
bnbDeputyAddress: suite.addr,
|
bnbDeputyAddress: suite.addr,
|
||||||
|
bnbDeputyFixedFee: types.DefaultBnbDeputyFixedFee,
|
||||||
minBlockLock: types.DefaultMinBlockLock,
|
minBlockLock: types.DefaultMinBlockLock,
|
||||||
maxBlockLock: types.DefaultMaxBlockLock,
|
maxBlockLock: types.DefaultMaxBlockLock,
|
||||||
supportedAssets: types.AssetParams{
|
supportedAssets: types.AssetParams{
|
||||||
@ -200,8 +211,8 @@ func (suite *ParamsTestSuite) TestParamValidation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
params := types.NewParams(tc.args.bnbDeputyAddress, tc.args.minBlockLock,
|
params := types.NewParams(tc.args.bnbDeputyAddress, tc.args.bnbDeputyFixedFee,
|
||||||
tc.args.maxBlockLock, tc.args.supportedAssets)
|
tc.args.minBlockLock, tc.args.maxBlockLock, tc.args.supportedAssets)
|
||||||
|
|
||||||
err := params.Validate()
|
err := params.Validate()
|
||||||
if tc.expectPass {
|
if tc.expectPass {
|
||||||
|
Loading…
Reference in New Issue
Block a user