Skip to content

Commit

Permalink
feat: make dust threshold configurable per chain (#1256)
Browse files Browse the repository at this point in the history
* feat: add dust threshold into zone

* refactor: change hardcode into config

* refactor: add dust threshold into proposals struct

* test: update test

* feat: add v1_6 upgrades logic

* linting

* threshold upgradable per chain

* Update app/upgrades/v1_6.go

* rebase main

* change dust threshold data type

* fix lint

---------

Co-authored-by: Jacob Gadikian <[email protected]>
  • Loading branch information
minhngoc274 and faddat authored Apr 11, 2024
1 parent 1205895 commit a9ddc55
Show file tree
Hide file tree
Showing 15 changed files with 649 additions and 222 deletions.
23 changes: 23 additions & 0 deletions app/upgrades/v1_6.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package upgrades

import (
"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
Expand All @@ -27,6 +29,27 @@ func V010600UpgradeHandler(
panic(err)
}

// Update dust threshold configuration for all zones
thresholds := map[string]math.Int{
"osmosis-1": math.NewInt(1_000_000),
"cosmoshub-4": math.NewInt(1_000_000),
"stargaze-1": math.NewInt(5_000_000),
"juno-1": math.NewInt(2_000_000),
"sommelier-3": math.NewInt(5_000_000),
"regen-1": math.NewInt(5_000_000),
"dydx-mainnet-1": math.NewInt(500_000_000_000_000_000),
}
appKeepers.InterchainstakingKeeper.IterateZones(ctx, func(index int64, zone *icstypes.Zone) (stop bool) {
threshold, ok := thresholds[zone.ChainId]
// if threshold not exist => get default value
if !ok {
threshold = math.NewInt(1_000_000)
}
zone.DustThreshold = threshold
appKeepers.InterchainstakingKeeper.SetZone(ctx, zone)
return false
})

return mm.RunMigrations(ctx, configurator, fromVM)
}
}
Expand Down
136 changes: 136 additions & 0 deletions app/upgrades_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -925,3 +925,139 @@ func (s *AppTestSuite) UncheckedSetWithdrawalRecord(ctx sdk.Context, app *Quicks
bz := app.InterchainstakingKeeper.GetCodec().MustMarshal(&record)
store.Set(key, bz)
}

func (s *AppTestSuite) InitV160TestZones() {
cosmosWithdrawal := addressutils.GenerateAddressForTestWithPrefix("cosmos")
cosmosPerformance := addressutils.GenerateAddressForTestWithPrefix("cosmos")
cosmosDeposit := addressutils.GenerateAddressForTestWithPrefix("cosmos")
cosmosDelegate := addressutils.GenerateAddressForTestWithPrefix("cosmos")
// cosmos zone
zone := icstypes.Zone{
ConnectionId: "connection-77001",
ChainId: "cosmoshub-4",
AccountPrefix: "cosmos",
LocalDenom: "uqatom",
BaseDenom: "uatom",
MultiSend: false,
LiquidityModule: false,
WithdrawalAddress: &icstypes.ICAAccount{
Address: cosmosWithdrawal,
PortName: "icacontroller-cosmoshub-4.withdrawal",
WithdrawalAddress: cosmosWithdrawal,
},
DelegationAddress: &icstypes.ICAAccount{
Address: cosmosDelegate,
PortName: "icacontroller-cosmoshub-4.delegate",
WithdrawalAddress: cosmosWithdrawal,
},
DepositAddress: &icstypes.ICAAccount{
Address: cosmosDeposit,
PortName: "icacontroller-cosmoshub-4.deposit",
WithdrawalAddress: cosmosWithdrawal,
},
PerformanceAddress: &icstypes.ICAAccount{
Address: cosmosPerformance,
PortName: "icacontroller-cosmoshub-4.performance",
WithdrawalAddress: cosmosWithdrawal,
},
}
s.GetQuicksilverApp(s.chainA).InterchainstakingKeeper.SetZone(s.chainA.GetContext(), &zone)

osmoWithdrawal := addressutils.GenerateAddressForTestWithPrefix("osmo")
osmoPerformance := addressutils.GenerateAddressForTestWithPrefix("osmo")
osmoDeposit := addressutils.GenerateAddressForTestWithPrefix("osmo")
osmoDelegate := addressutils.GenerateAddressForTestWithPrefix("osmo")
// osmosis zone
zone = icstypes.Zone{
ConnectionId: "connection-77002",
ChainId: "osmosis-1",
AccountPrefix: "osmo",
LocalDenom: "uqosmo",
BaseDenom: "uosmo",
MultiSend: false,
LiquidityModule: false,
WithdrawalAddress: &icstypes.ICAAccount{
Address: osmoWithdrawal,
PortName: "icacontroller-osmosis-1.withdrawal",
WithdrawalAddress: osmoWithdrawal,
},
DelegationAddress: &icstypes.ICAAccount{
Address: osmoDelegate,
PortName: "icacontroller-osmosis-1.delegate",
WithdrawalAddress: osmoWithdrawal,
},
DepositAddress: &icstypes.ICAAccount{
Address: osmoDeposit,
PortName: "icacontroller-osmosis-1.deposit",
WithdrawalAddress: osmoWithdrawal,
},
PerformanceAddress: &icstypes.ICAAccount{
Address: osmoPerformance,
PortName: "icacontroller-osmosis-1.performance",
WithdrawalAddress: osmoWithdrawal,
},
}
s.GetQuicksilverApp(s.chainA).InterchainstakingKeeper.SetZone(s.chainA.GetContext(), &zone)
// uni-5 zone

junoWithdrawal := addressutils.GenerateAddressForTestWithPrefix("juno")
junoPerformance := addressutils.GenerateAddressForTestWithPrefix("juno")
junoDeposit := addressutils.GenerateAddressForTestWithPrefix("juno")
junoDelegate := addressutils.GenerateAddressForTestWithPrefix("juno")

zone = icstypes.Zone{
ConnectionId: "connection-77003",
ChainId: "juno-1",
AccountPrefix: "juno",
LocalDenom: "uqjuno",
BaseDenom: "ujuno",
MultiSend: false,
LiquidityModule: false,
WithdrawalAddress: &icstypes.ICAAccount{
Address: junoWithdrawal,
PortName: "icacontroller-juno-1.withdrawal",
WithdrawalAddress: junoWithdrawal,
},
DelegationAddress: &icstypes.ICAAccount{
Address: junoDelegate,
PortName: "icacontroller-juno-1.delegate",
WithdrawalAddress: junoWithdrawal,
},
DepositAddress: &icstypes.ICAAccount{
Address: junoDeposit,
PortName: "icacontroller-juno-1.deposit",
WithdrawalAddress: junoWithdrawal,
},
PerformanceAddress: &icstypes.ICAAccount{
Address: junoPerformance,
PortName: "icacontroller-juno-1.performance",
WithdrawalAddress: junoWithdrawal,
},
}
s.GetQuicksilverApp(s.chainA).InterchainstakingKeeper.SetZone(s.chainA.GetContext(), &zone)
addVestingAccount(s.chainA.GetContext(), &s.GetQuicksilverApp(s.chainA).AccountKeeper, "quick1qfyntnmlvznvrkk9xqppmcxqcluv7wd74nmyus", 10, 864000, 5000000000)
}

func (s *AppTestSuite) TestV010600UpgradeHandler() {
s.InitV160TestZones()
app := s.GetQuicksilverApp(s.chainA)

handler := upgrades.V010600UpgradeHandler(app.mm,
app.configurator, &app.AppKeepers)
ctx := s.chainA.GetContext()

_, err := handler(ctx, types.Plan{}, app.mm.GetVersionMap())
s.NoError(err)

osmoZone, ok := app.InterchainstakingKeeper.GetZone(ctx, "osmosis-1")
s.True(ok)
s.Equal(math.NewInt(1_000_000), osmoZone.DustThreshold)

cosmosZone, ok := app.InterchainstakingKeeper.GetZone(ctx, "cosmoshub-4")
s.True(ok)
s.Equal(math.NewInt(1_000_000), cosmosZone.DustThreshold)

junoZone, ok := app.InterchainstakingKeeper.GetZone(ctx, "juno-1")
s.True(ok)
s.Equal(math.NewInt(2_000_000), junoZone.DustThreshold)
}
4 changes: 4 additions & 0 deletions docs/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3204,6 +3204,8 @@ paths:
type: string
base_chainID:
type: string
dust_threshold:
type: string
stats:
type: object
properties:
Expand Down Expand Up @@ -3541,6 +3543,8 @@ paths:
type: string
base_chainID:
type: string
dust_threshold:
type: string
stats:
type: array
items:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ message Zone {
bool return_to_sender = 27;
bool is_118 = 28;
SubzoneInfo subzoneInfo = 29;
string dust_threshold = 30 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}

message SubzoneInfo {
Expand Down
4 changes: 4 additions & 0 deletions proto/quicksilver/interchainstaking/v1/proposals.proto
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ message RegisterZoneProposal {
bool unbonding_enabled = 12;
int64 decimals = 13;
bool is_118 = 14;
string dustThreshold = 15 [(gogoproto.moretags) = "yaml:\"dust_threshold\""];
}

message RegisterZoneProposalWithDeposit {
Expand All @@ -48,6 +49,7 @@ message RegisterZoneProposalWithDeposit {
bool unbonding_enabled = 13 [(gogoproto.moretags) = "yaml:\"deposits_enabled\""];
int64 decimals = 14 [(gogoproto.moretags) = "yaml:\"decimals\""];
bool is_118 = 15;
string dustThreshold = 16 [(gogoproto.moretags) = "yaml:\"dust_threshold\""];
}

message UpdateZoneProposal {
Expand All @@ -60,6 +62,7 @@ message UpdateZoneProposal {
string chain_id = 3 [(gogoproto.moretags) = "yaml:\"chain_id\""];

repeated UpdateZoneValue changes = 4 [(gogoproto.moretags) = "yaml:\"changes\""];
string dustThreshold = 5 [(gogoproto.moretags) = "yaml:\"dust_threshold\""];
}

message UpdateZoneProposalWithDeposit {
Expand All @@ -72,6 +75,7 @@ message UpdateZoneProposalWithDeposit {

repeated UpdateZoneValue changes = 4 [(gogoproto.moretags) = "yaml:\"changes\""];
string deposit = 5 [(gogoproto.moretags) = "yaml:\"deposit\""];
string dustThreshold = 6 [(gogoproto.moretags) = "yaml:\"dust_threshold\""];
}

// UpdateZoneValue defines an individual parameter change, for use in
Expand Down
4 changes: 4 additions & 0 deletions x/interchainstaking/client/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/stretchr/testify/suite"
tmcli "github.com/tendermint/tendermint/libs/cli"

"cosmossdk.io/math"

"github.com/cosmos/cosmos-sdk/client/flags"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
Expand Down Expand Up @@ -70,6 +72,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
UnbondingPeriod: 0,
MessagesPerTx: 0,
Is_118: true,
DustThreshold: math.NewInt(1000000),
}

zone.Validators = append(zone.Validators,
Expand Down Expand Up @@ -189,6 +192,7 @@ func (s *IntegrationTestSuite) ZonesEqual(zoneA, zoneB types.Zone) bool {
s.Require().Equal(zoneA.LastEpochHeight, zoneB.LastEpochHeight)
s.Require().Equal(zoneA.LiquidityModule, zoneB.LiquidityModule)
s.Require().Equal(zoneA.LocalDenom, zoneB.LocalDenom)
s.Require().Equal(zoneA.DustThreshold, zoneB.DustThreshold)
for i := range zoneA.Validators {
s.Require().Equal(zoneA.Validators[i], zoneB.Validators[i])
}
Expand Down
8 changes: 5 additions & 3 deletions x/interchainstaking/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ Where proposal.json contains:
"account_prefix": "cosmos",
"multi_send": true,
"liquidity_module": false,
"deposit": "512000000uqck"
"deposit": "512000000uqck",
"dust_threshold": "1000000",
}
`),
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -175,7 +176,7 @@ Where proposal.json contains:
from := clientCtx.GetFromAddress()

content := types.NewRegisterZoneProposal(proposal.Title, proposal.Description, proposal.ConnectionId, proposal.BaseDenom,
proposal.LocalDenom, proposal.AccountPrefix, proposal.ReturnToSender, proposal.UnbondingEnabled, proposal.DepositsEnabled, proposal.LiquidityModule, proposal.Decimals, proposal.MessagesPerTx, proposal.Is_118)
proposal.LocalDenom, proposal.AccountPrefix, proposal.ReturnToSender, proposal.UnbondingEnabled, proposal.DepositsEnabled, proposal.LiquidityModule, proposal.Decimals, proposal.MessagesPerTx, proposal.Is_118, proposal.DustThreshold)

msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from)
if err != nil {
Expand Down Expand Up @@ -229,6 +230,7 @@ Where proposal.json contains:
"value": "true",
}],
"deposit": "512000000uqck"
"dust_threshold": "1000000"
}
`),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -248,7 +250,7 @@ Where proposal.json contains:

from := clientCtx.GetFromAddress()

content := types.NewUpdateZoneProposal(proposal.Title, proposal.Description, proposal.ChainId, proposal.Changes)
content := types.NewUpdateZoneProposal(proposal.Title, proposal.Description, proposal.ChainId, proposal.Changes, proposal.DustThreshold)

msg, err := govv1beta1.NewMsgSubmitProposal(content, deposit, from)
if err != nil {
Expand Down
3 changes: 1 addition & 2 deletions x/interchainstaking/keeper/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,7 @@ func (*Keeper) PrepareDelegationMessagesForCoins(zone *types.Zone, allocations m
var msgs []sdk.Msg
for _, valoper := range utils.Keys(allocations) {
if allocations[valoper].IsPositive() {
if allocations[valoper].GTE(sdk.NewInt(1_000_000)) || isFlush {
// don't delegate tiny amounts. TODO: make configurable per zone.
if allocations[valoper].GTE(zone.DustThreshold) || isFlush {
msgs = append(msgs, &stakingtypes.MsgDelegate{DelegatorAddress: zone.DelegationAddress.Address, ValidatorAddress: valoper, Amount: sdk.NewCoin(zone.BaseDenom, allocations[valoper])})
}
}
Expand Down
3 changes: 1 addition & 2 deletions x/interchainstaking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,8 +784,7 @@ func (k *Keeper) Rebalance(ctx sdk.Context, zone *types.Zone, epochNumber int64)
rebalances := types.DetermineAllocationsForRebalancing(currentAllocations, currentLocked, currentSum, lockedSum, targetAllocations, maxCanAllocate, k.Logger(ctx)).RemoveDuplicates()
msgs := make([]sdk.Msg, 0)
for _, rebalance := range rebalances {
if rebalance.Amount.GTE(sdk.NewInt(1_000_000)) {
// don't redelegate dust; TODO: config per zone
if rebalance.Amount.GTE(zone.DustThreshold) {
if !rebalance.Amount.IsInt64() {
k.Logger(ctx).Error("Rebalance amount out of bound Int64", "amount", rebalance.Amount.String())
// Ignore this
Expand Down
1 change: 1 addition & 0 deletions x/interchainstaking/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (suite *KeeperTestSuite) setupTestZones() {
DepositsEnabled: true,
Decimals: 6,
Is_118: true,
DustThreshold: "1000000",
}

quicksilver := suite.GetQuicksilverApp(suite.chainA)
Expand Down
Loading

0 comments on commit a9ddc55

Please sign in to comment.