Skip to content

Commit b65fb6c

Browse files
p0mvnczarcas7ic
andauthored
feat!: initial deposit requirement for proposals (backport #296) (#297)
* feat!: initial deposit requirement for proposals (backport #296) * Update x/gov/legacy/v3/store_test.go Co-authored-by: Adam Tucker <adam@osmosis.team> * Update x/gov/simulation/genesis.go Co-authored-by: Adam Tucker <adam@osmosis.team> Co-authored-by: Adam Tucker <adam@osmosis.team>
1 parent f37e34d commit b65fb6c

File tree

22 files changed

+518
-122
lines changed

22 files changed

+518
-122
lines changed

docs/core/proto-docs.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5027,6 +5027,7 @@ DepositParams defines the params for deposits on governance proposals.
50275027
| ----- | ---- | ----- | ----------- |
50285028
| `min_deposit` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | Minimum deposit for a proposal to enter voting period. |
50295029
| `max_deposit_period` | [google.protobuf.Duration](#google.protobuf.Duration) | | Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months. |
5030+
| `min_initial_deposit_ratio` | [string](#string) | | The ratio representing the proportion of the deposit value that must be paid at proposal submission. |
50305031

50315032

50325033

proto/cosmos/gov/v1beta1/gov.proto

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ message DepositParams {
159159
(gogoproto.jsontag) = "max_deposit_period,omitempty",
160160
(gogoproto.moretags) = "yaml:\"max_deposit_period\""
161161
];
162+
// The ratio representing the proportion of the deposit value that must be paid at proposal submission.
163+
string min_initial_deposit_ratio = 3 [
164+
(gogoproto.nullable) = false,
165+
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
166+
(gogoproto.jsontag) = "min_initial_deposit_ratio,omitempty"
167+
];
162168
}
163169

164170
// VotingParams defines the params for voting on governance proposals.

x/gov/client/testutil/suite.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func (s *IntegrationTestSuite) TestCmdParams() {
8888
{
8989
"json output",
9090
[]string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
91-
`{"voting_params":{"voting_period":"172800000000000"},"tally_params":{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"},"deposit_params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}}`,
91+
`{"voting_params":{"voting_period":"172800000000000"},"tally_params":{"quorum":"0.334000000000000000","threshold":"0.500000000000000000","veto_threshold":"0.334000000000000000"},"deposit_params":{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000","min_initial_deposit_ratio":"0.000000000000000000"}}`,
9292
},
9393
{
9494
"text output",
@@ -99,6 +99,7 @@ deposit_params:
9999
min_deposit:
100100
- amount: "10000000"
101101
denom: stake
102+
min_initial_deposit_ratio: "0.000000000000000000"
102103
tally_params:
103104
quorum: "0.334000000000000000"
104105
threshold: "0.500000000000000000"
@@ -153,7 +154,7 @@ func (s *IntegrationTestSuite) TestCmdParam() {
153154
"deposit",
154155
fmt.Sprintf("--%s=json", tmcli.OutputFlag),
155156
},
156-
`{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000"}`,
157+
`{"min_deposit":[{"denom":"stake","amount":"10000000"}],"max_deposit_period":"172800000000000","min_initial_deposit_ratio":"0.000000000000000000"}`,
157158
},
158159
}
159160

x/gov/keeper/deposit.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,21 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) {
185185
return false
186186
})
187187
}
188+
189+
// validateInitialDeposit validates if initial deposit is greater than or equal to the minimum
190+
// required at the time of proposal submission. This threshold amount is determined by
191+
// the deposit parameters. Returns nil on success, error otherwise.
192+
func (keeper Keeper) validateInitialDeposit(ctx sdk.Context, initialDeposit sdk.Coins) error {
193+
depositParams := keeper.GetDepositParams(ctx)
194+
if depositParams.MinInitialDepositRatio.IsNil() || depositParams.MinInitialDepositRatio.IsZero() {
195+
return nil
196+
}
197+
minDepositCoins := depositParams.MinDeposit
198+
for i := range minDepositCoins {
199+
minDepositCoins[i].Amount = minDepositCoins[i].Amount.ToDec().Mul(depositParams.MinInitialDepositRatio).RoundInt()
200+
}
201+
if !initialDeposit.IsAllGTE(minDepositCoins) {
202+
return sdkerrors.Wrapf(types.ErrMinDepositTooSmall, "was (%s), need (%s)", initialDeposit, minDepositCoins)
203+
}
204+
return nil
205+
}

x/gov/keeper/deposit_test.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import (
99

1010
"github.com/cosmos/cosmos-sdk/simapp"
1111
sdk "github.com/cosmos/cosmos-sdk/types"
12+
"github.com/cosmos/cosmos-sdk/x/gov/types"
13+
)
14+
15+
const (
16+
baseDepositTestAmount = 100
17+
baseDepositTestPercent = 25
1218
)
1319

1420
func TestDeposits(t *testing.T) {
@@ -108,3 +114,102 @@ func TestDeposits(t *testing.T) {
108114
deposits = app.GovKeeper.GetDeposits(ctx, proposalID)
109115
require.Len(t, deposits, 0)
110116
}
117+
118+
func TestValidateInitialDeposit(t *testing.T) {
119+
testcases := map[string]struct {
120+
minDeposit sdk.Coins
121+
minInitialDepositPercent int64
122+
initialDeposit sdk.Coins
123+
124+
expectError bool
125+
}{
126+
"min deposit * initial percent == initial deposit: success": {
127+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
128+
minInitialDepositPercent: baseDepositTestPercent,
129+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100))),
130+
},
131+
"min deposit * initial percent < initial deposit: success": {
132+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
133+
minInitialDepositPercent: baseDepositTestPercent,
134+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100+1))),
135+
},
136+
"min deposit * initial percent > initial deposit: error": {
137+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
138+
minInitialDepositPercent: baseDepositTestPercent,
139+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100-1))),
140+
141+
expectError: true,
142+
},
143+
"min deposit * initial percent == initial deposit (non-base values and denom): success": {
144+
minDeposit: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(56912))),
145+
minInitialDepositPercent: 50,
146+
initialDeposit: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(56912/2+10))),
147+
},
148+
"min deposit * initial percent == initial deposit but different denoms: error": {
149+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
150+
minInitialDepositPercent: baseDepositTestPercent,
151+
initialDeposit: sdk.NewCoins(sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100))),
152+
153+
expectError: true,
154+
},
155+
"min deposit * initial percent == initial deposit (multiple coins): success": {
156+
minDeposit: sdk.NewCoins(
157+
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount)),
158+
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2))),
159+
minInitialDepositPercent: baseDepositTestPercent,
160+
initialDeposit: sdk.NewCoins(
161+
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100)),
162+
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2*baseDepositTestPercent/100)),
163+
),
164+
},
165+
"min deposit * initial percent > initial deposit (multiple coins): error": {
166+
minDeposit: sdk.NewCoins(
167+
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount)),
168+
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2))),
169+
minInitialDepositPercent: baseDepositTestPercent,
170+
initialDeposit: sdk.NewCoins(
171+
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100)),
172+
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*2*baseDepositTestPercent/100-1)),
173+
),
174+
175+
expectError: true,
176+
},
177+
"min deposit * initial percent < initial deposit (multiple coins - coin not required by min deposit): success": {
178+
minDeposit: sdk.NewCoins(
179+
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
180+
minInitialDepositPercent: baseDepositTestPercent,
181+
initialDeposit: sdk.NewCoins(
182+
sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100)),
183+
sdk.NewCoin("uosmo", sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100-1)),
184+
),
185+
},
186+
"0 initial percent: success": {
187+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
188+
minInitialDepositPercent: 0,
189+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount*baseDepositTestPercent/100))),
190+
},
191+
}
192+
193+
for name, tc := range testcases {
194+
t.Run(name, func(t *testing.T) {
195+
app := simapp.Setup(false)
196+
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
197+
198+
govKeeper := app.GovKeeper
199+
200+
params := types.DefaultDepositParams()
201+
params.MinDeposit = tc.minDeposit
202+
params.MinInitialDepositRatio = sdk.NewDec(tc.minInitialDepositPercent).Quo(sdk.NewDec(100))
203+
204+
govKeeper.SetDepositParams(ctx, params)
205+
206+
err := govKeeper.ValidateInitialDeposit(ctx, tc.initialDeposit)
207+
208+
if tc.expectError {
209+
require.Error(t, err)
210+
return
211+
}
212+
require.NoError(t, err)
213+
})
214+
}
215+
}

x/gov/keeper/export_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package keeper
2+
3+
import sdk "github.com/cosmos/cosmos-sdk/types"
4+
5+
func (k Keeper) ValidateInitialDeposit(ctx sdk.Context, initialDeposit sdk.Coins) error {
6+
return k.validateInitialDeposit(ctx, initialDeposit)
7+
}

x/gov/keeper/grpc_query_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,9 @@ func (suite *KeeperTestSuite) TestGRPCQueryParams() {
470470
func() {
471471
req = &types.QueryParamsRequest{ParamsType: types.ParamVoting}
472472
expRes = &types.QueryParamsResponse{
473-
VotingParams: types.DefaultVotingParams(),
474-
TallyParams: types.NewTallyParams(sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)),
473+
VotingParams: types.DefaultVotingParams(),
474+
DepositParams: types.DepositParams{MinInitialDepositRatio: sdk.ZeroDec()},
475+
TallyParams: types.NewTallyParams(sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0)),
475476
}
476477
},
477478
true,
@@ -481,7 +482,8 @@ func (suite *KeeperTestSuite) TestGRPCQueryParams() {
481482
func() {
482483
req = &types.QueryParamsRequest{ParamsType: types.ParamTallying}
483484
expRes = &types.QueryParamsResponse{
484-
TallyParams: types.DefaultTallyParams(),
485+
DepositParams: types.DepositParams{MinInitialDepositRatio: sdk.ZeroDec()},
486+
TallyParams: types.DefaultTallyParams(),
485487
}
486488
},
487489
true,

x/gov/keeper/msg_server.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ var _ types.MsgServer = msgServer{}
2626

2727
func (k msgServer) SubmitProposal(goCtx context.Context, msg *types.MsgSubmitProposal) (*types.MsgSubmitProposalResponse, error) {
2828
ctx := sdk.UnwrapSDKContext(goCtx)
29+
initialDeposit := msg.GetInitialDeposit()
30+
31+
if err := k.validateInitialDeposit(ctx, initialDeposit); err != nil {
32+
return nil, err
33+
}
34+
2935
proposal, err := k.Keeper.SubmitProposal(ctx, msg.GetContent())
3036
if err != nil {
3137
return nil, err

x/gov/keeper/msg_server_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package keeper_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/cosmos/cosmos-sdk/simapp"
7+
sdk "github.com/cosmos/cosmos-sdk/types"
8+
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
9+
"github.com/cosmos/cosmos-sdk/x/gov/types"
10+
"github.com/stretchr/testify/require"
11+
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
12+
)
13+
14+
func TestSubmitProposal_InitialDeposit(t *testing.T) {
15+
const meetsDepositValue = baseDepositTestAmount * baseDepositTestPercent / 100
16+
var baseDepositRatioDec = sdk.NewDec(baseDepositTestPercent).Quo(sdk.NewDec(100))
17+
18+
testcases := map[string]struct {
19+
minDeposit sdk.Coins
20+
minInitialDepositRatio sdk.Dec
21+
initialDeposit sdk.Coins
22+
accountBalance sdk.Coins
23+
24+
expectError bool
25+
}{
26+
"meets initial deposit, enough balance - success": {
27+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
28+
minInitialDepositRatio: baseDepositRatioDec,
29+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),
30+
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),
31+
},
32+
"does not meet initial deposit, enough balance - error": {
33+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
34+
minInitialDepositRatio: baseDepositRatioDec,
35+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),
36+
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),
37+
38+
expectError: true,
39+
},
40+
"meets initial deposit, not enough balance - error": {
41+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
42+
minInitialDepositRatio: baseDepositRatioDec,
43+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue))),
44+
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),
45+
46+
expectError: true,
47+
},
48+
"does not meet initial deposit and not enough balance - error": {
49+
minDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(baseDepositTestAmount))),
50+
minInitialDepositRatio: baseDepositRatioDec,
51+
initialDeposit: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),
52+
accountBalance: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(meetsDepositValue-1))),
53+
54+
expectError: true,
55+
},
56+
}
57+
58+
for name, tc := range testcases {
59+
t.Run(name, func(t *testing.T) {
60+
// Setup
61+
app := simapp.Setup(false)
62+
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
63+
govKeeper := app.GovKeeper
64+
msgServer := keeper.NewMsgServerImpl(govKeeper)
65+
66+
params := types.DefaultDepositParams()
67+
params.MinDeposit = tc.minDeposit
68+
params.MinInitialDepositRatio = tc.minInitialDepositRatio
69+
govKeeper.SetDepositParams(ctx, params)
70+
71+
address := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(0))[0]
72+
simapp.FundAccount(app.BankKeeper, ctx, address, tc.accountBalance)
73+
74+
msg, err := types.NewMsgSubmitProposal(TestProposal, tc.initialDeposit, address)
75+
require.NoError(t, err)
76+
77+
// System under test
78+
_, err = msgServer.SubmitProposal(sdk.WrapSDKContext(ctx), msg)
79+
80+
// Assertions
81+
if tc.expectError {
82+
require.Error(t, err)
83+
return
84+
}
85+
require.NoError(t, err)
86+
})
87+
}
88+
}

x/gov/legacy/v040/migrate_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ func TestMigrate(t *testing.T) {
9797
expected := `{
9898
"deposit_params": {
9999
"max_deposit_period": "0s",
100-
"min_deposit": []
100+
"min_deposit": [],
101+
"min_initial_deposit_ratio": "0"
101102
},
102103
"deposits": [],
103104
"proposals": [

0 commit comments

Comments
 (0)