Skip to content

Commit ae3d765

Browse files
committed
added gas limit tests that tests some specific high gas usage delegations scenarios
1 parent f381491 commit ae3d765

File tree

4 files changed

+235
-4
lines changed

4 files changed

+235
-4
lines changed

contracts/delegation/dao-vote-delegation/src/contract.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ pub(crate) const CONTRACT_NAME: &str = "crates.io:dao-vote-delegation";
4040
pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
4141

4242
pub const DEFAULT_LIMIT: u32 = 10;
43-
pub const MAX_LIMIT: u32 = 50;
4443

4544
/// in tests on Neutron, with a block max gas of 30M (which is one of the lowest
4645
/// gas limits on any chain), we found that 50 delegations is a safe upper
@@ -573,7 +572,7 @@ fn query_delegates(
573572
start_after: Option<String>,
574573
limit: Option<u32>,
575574
) -> StdResult<DelegatesResponse> {
576-
let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
575+
let limit = limit.unwrap_or(DEFAULT_LIMIT) as usize;
577576

578577
let start = maybe_addr(deps.api, start_after)?.map(Bound::exclusive);
579578

contracts/delegation/dao-vote-delegation/src/testing/suite/base.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ impl DaoVoteDelegationTestingSuiteBase {
379379

380380
/// assert that there are N delegates
381381
pub fn assert_delegates_count(&self, count: u32) {
382-
let delegates = self.delegates(None, None);
382+
let delegates = self.delegates(None, Some(count));
383383
assert_eq!(delegates.len() as u32, count);
384384
}
385385

contracts/delegation/dao-vote-delegation/src/testing/suite/token.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::ops::{Deref, DerefMut};
22

3-
use cosmwasm_std::Decimal;
3+
use cosmwasm_std::{coins, Decimal, Uint128};
4+
use cw_multi_test::{BankSudo, SudoMsg};
45
use dao_interface::token::InitialBalance;
56
use dao_testing::{DaoTestingSuite, TokenTestDao};
67

@@ -112,4 +113,42 @@ impl TokenDaoVoteDelegationTestingSuite {
112113
// set the delegation module for all proposal modules
113114
self.set_delegation_module(&dao, &delegation_addr);
114115
}
116+
117+
/// mint tokens
118+
pub fn mint(&mut self, recipient: impl Into<String>, amount: impl Into<u128>) {
119+
let denom = self.dao.x.denom.clone();
120+
self.app
121+
.sudo(SudoMsg::Bank({
122+
BankSudo::Mint {
123+
to_address: recipient.into(),
124+
amount: coins(amount.into(), denom),
125+
}
126+
}))
127+
.unwrap();
128+
}
129+
130+
/// stake tokens
131+
pub fn stake(&mut self, staker: impl Into<String>, amount: impl Into<u128>) {
132+
let voting_module_addr = self.dao.voting_module_addr.clone();
133+
let denom = self.dao.x.denom.clone();
134+
self.execute_smart_ok(
135+
staker,
136+
voting_module_addr,
137+
&dao_voting_token_staked::msg::ExecuteMsg::Stake {},
138+
&coins(amount.into(), denom),
139+
);
140+
}
141+
142+
/// unstake tokens
143+
pub fn unstake(&mut self, staker: impl Into<String>, amount: impl Into<Uint128>) {
144+
let voting_module_addr = self.dao.voting_module_addr.clone();
145+
self.execute_smart_ok(
146+
staker,
147+
&voting_module_addr,
148+
&dao_voting_token_staked::msg::ExecuteMsg::Unstake {
149+
amount: amount.into(),
150+
},
151+
&[],
152+
);
153+
}
115154
}

contracts/delegation/dao-vote-delegation/src/testing/tests.rs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,10 @@ fn test_migrate() {
10711071
assert_eq!(version.version, CONTRACT_VERSION);
10721072
}
10731073

1074+
/// this test does not actually test gas limits, since cw-multi-test does not
1075+
/// run a real chain, but it is demonstrative of what behaviors may lead to high
1076+
/// gas usage. this test is replicated in the DAO DAO UI codebase using an
1077+
/// actual chain with gas limits.
10741078
#[test]
10751079
fn test_vp_cap_update_token_dao() {
10761080
let mut suite = TokenDaoVoteDelegationTestingSuite::new()
@@ -1254,3 +1258,192 @@ fn test_vp_cap_update_token_dao() {
12541258
total_vp_except_addr0,
12551259
);
12561260
}
1261+
1262+
#[test]
1263+
fn test_gas_limits() {
1264+
let mut suite = TokenDaoVoteDelegationTestingSuite::new()
1265+
.with_max_delegations(100)
1266+
.build();
1267+
let dao = suite.dao.clone();
1268+
1269+
// unstake all tokens for initial members
1270+
for member in suite.members.clone() {
1271+
suite.unstake(member.address, member.amount);
1272+
}
1273+
1274+
// mint 2,000 tokens and stake half for each of 1,000 members
1275+
let members = 1_000u128;
1276+
let initial_balance = 2_000u128;
1277+
let initial_staked = initial_balance / 2;
1278+
for i in 0..members {
1279+
suite.mint(format!("member_{}", i), initial_balance);
1280+
suite.stake(format!("member_{}", i), initial_staked);
1281+
}
1282+
1283+
// staking takes effect at the next block
1284+
suite.advance_block();
1285+
1286+
let total_vp: dao_interface::voting::TotalPowerAtHeightResponse = suite
1287+
.querier()
1288+
.query_wasm_smart(
1289+
&suite.dao.core_addr,
1290+
&dao_voting_token_staked::msg::QueryMsg::TotalPowerAtHeight { height: None },
1291+
)
1292+
.unwrap();
1293+
assert_eq!(total_vp.power, Uint128::from(initial_staked * members));
1294+
1295+
// register first 100 members as delegates, and make delegator the first
1296+
// non-delegate
1297+
let delegates = 100u128;
1298+
let delegator = format!("member_{}", delegates);
1299+
for i in 0..delegates {
1300+
suite.register(format!("member_{}", i));
1301+
}
1302+
1303+
// delegations take effect on the next block
1304+
suite.advance_block();
1305+
1306+
// check that the delegations are registered
1307+
suite.assert_delegates_count(100);
1308+
1309+
// TEST 1: Update voting power for a delegator, which loops through all
1310+
// delegates and updates their delegated voting power. This should cause a
1311+
// gas error if there are too many delegates to update.
1312+
1313+
// delegate to each of the delegates, rounding to 5 decimal places to avoid
1314+
// infinitely repeating decimals
1315+
let percent_delegated = Decimal::from_ratio(100_000u128 / delegates / 3, 100_000u128);
1316+
for i in 0..delegates {
1317+
suite.delegate(&delegator, format!("member_{}", i), percent_delegated);
1318+
}
1319+
1320+
// delegations take effect on the next block
1321+
suite.advance_block();
1322+
1323+
// check that the voting power is distributed correctly
1324+
for delegate in suite.delegates(None, None) {
1325+
assert_eq!(
1326+
delegate.power,
1327+
Uint128::from(initial_staked).mul_floor(percent_delegated)
1328+
);
1329+
}
1330+
1331+
// stake the other half of the tokens for the delegator, which should loop
1332+
// through and update all delegations
1333+
suite.stake(&delegator, initial_balance - initial_staked);
1334+
1335+
// delegations take effect on the next block
1336+
suite.advance_block();
1337+
1338+
// check that the voting power is distributed correctly
1339+
for delegate in suite.delegates(None, None) {
1340+
assert_eq!(
1341+
delegate.power,
1342+
Uint128::from(initial_balance).mul_floor(percent_delegated)
1343+
);
1344+
}
1345+
1346+
// undo the half stake so that all members have the same voting power again
1347+
suite.unstake(&delegator, initial_balance - initial_staked);
1348+
1349+
// delegations take effect on the next block
1350+
suite.advance_block();
1351+
1352+
// TEST 2: Override all delegates' votes, which loops through all delegates
1353+
// and updates both their ballots and unvoted delegated voting power on that
1354+
// proposal. This should cause a gas error if there are too many delegates
1355+
// to update.
1356+
1357+
let (proposal_module, proposal_id, proposal) =
1358+
suite.propose_single_choice(&dao, "member_0", "test proposal", vec![]);
1359+
1360+
// ensure that the unvoted delegated voting power is equal to the total
1361+
// delegated voting power, since the delegator has not voted yet
1362+
for i in 0..delegates {
1363+
let vp = Uint128::from(initial_staked).mul_floor(percent_delegated);
1364+
suite.assert_effective_udvp(
1365+
format!("member_{}", i),
1366+
&proposal_module,
1367+
proposal_id,
1368+
proposal.start_height,
1369+
vp,
1370+
);
1371+
suite.assert_total_udvp(
1372+
format!("member_{}", i),
1373+
&proposal_module,
1374+
proposal_id,
1375+
proposal.start_height,
1376+
vp,
1377+
);
1378+
}
1379+
1380+
// all delegates vote on the proposal
1381+
for i in 0..delegates {
1382+
suite.vote_single_choice(
1383+
&dao,
1384+
format!("member_{}", i),
1385+
proposal_id,
1386+
dao_voting::voting::Vote::Yes,
1387+
);
1388+
}
1389+
1390+
// verify votes tallied with the delegates' personal voting power and
1391+
// delegated voting power
1392+
suite.assert_single_choice_votes_count(
1393+
&proposal_module,
1394+
proposal_id,
1395+
dao_voting::voting::Vote::Yes,
1396+
// compute delegated voting power
1397+
Uint128::from(initial_staked)
1398+
.mul_floor(percent_delegated)
1399+
// add personal voting power
1400+
.checked_add(Uint128::from(initial_staked))
1401+
.unwrap()
1402+
// multiply by number of delegates
1403+
.checked_mul(Uint128::from(delegates))
1404+
.unwrap(),
1405+
);
1406+
1407+
// delegator overrides all delegates' votes, which should update all
1408+
// delegate's ballots and unvoted delegated voting power on the proposal
1409+
suite.vote_single_choice(&dao, delegator, proposal_id, dao_voting::voting::Vote::No);
1410+
1411+
// verify vote tallies have been updated with the delegator's vote, removing
1412+
// the delegator's delegated voting power from the delegates' yes votes and
1413+
// adding the delegator's full voting power to the no votes
1414+
suite.assert_single_choice_votes_count(
1415+
&proposal_module,
1416+
proposal_id,
1417+
dao_voting::voting::Vote::Yes,
1418+
// add personal voting power
1419+
Uint128::from(initial_staked)
1420+
// multiply by number of delegates
1421+
.checked_mul(Uint128::from(delegates))
1422+
.unwrap(),
1423+
);
1424+
suite.assert_single_choice_votes_count(
1425+
&proposal_module,
1426+
proposal_id,
1427+
dao_voting::voting::Vote::No,
1428+
Uint128::from(initial_staked),
1429+
);
1430+
1431+
// verify that the unvoted delegated voting power is 0, since the delegator
1432+
// voted
1433+
for i in 0..delegates {
1434+
suite.assert_effective_udvp(
1435+
format!("member_{}", i),
1436+
&proposal_module,
1437+
proposal_id,
1438+
proposal.start_height,
1439+
0u128,
1440+
);
1441+
suite.assert_total_udvp(
1442+
format!("member_{}", i),
1443+
&proposal_module,
1444+
proposal_id,
1445+
proposal.start_height,
1446+
0u128,
1447+
);
1448+
}
1449+
}

0 commit comments

Comments
 (0)