@@ -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]
10751079fn 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