Fix treasury voting power calculation (#214)

* Fix treasury voting power calculation

* Update changelog
This commit is contained in:
mzhu25 2021-04-28 10:22:52 -07:00 committed by GitHub
parent c03a014740
commit fe0c26387c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 16 deletions

View File

@ -1,4 +1,13 @@
[ [
{
"version": "1.1.5",
"changes": [
{
"note": "Patched votingPower logic",
"pr": 214
}
]
},
{ {
"timestamp": 1619596077, "timestamp": 1619596077,
"version": "1.1.4", "version": "1.1.4",

View File

@ -30,6 +30,7 @@ interface IZrxTreasury {
uint256 votingPeriod; uint256 votingPeriod;
uint256 proposalThreshold; uint256 proposalThreshold;
uint256 quorumThreshold; uint256 quorumThreshold;
bytes32 defaultPoolId;
} }
struct ProposedAction { struct ProposedAction {

View File

@ -20,8 +20,6 @@
pragma solidity ^0.6.12; pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol"; import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
@ -32,7 +30,6 @@ import "./IZrxTreasury.sol";
contract ZrxTreasury is contract ZrxTreasury is
IZrxTreasury IZrxTreasury
{ {
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256; using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes; using LibRichErrorsV06 for bytes;
using LibBytesV06 for bytes; using LibBytesV06 for bytes;
@ -52,11 +49,9 @@ contract ZrxTreasury is
/// @dev Initializes the ZRX treasury and creates the default /// @dev Initializes the ZRX treasury and creates the default
/// staking pool. /// staking pool.
/// @param stakingProxy_ The 0x staking proxy contract. /// @param stakingProxy_ The 0x staking proxy contract.
/// @param weth_ The WETH token contract.
/// @param params Immutable treasury parameters. /// @param params Immutable treasury parameters.
constructor( constructor(
IStaking stakingProxy_, IStaking stakingProxy_,
IERC20TokenV06 weth_,
TreasuryParameters memory params TreasuryParameters memory params
) )
public public
@ -66,15 +61,12 @@ contract ZrxTreasury is
"VOTING_PERIOD_TOO_LONG" "VOTING_PERIOD_TOO_LONG"
); );
stakingProxy = stakingProxy_; stakingProxy = stakingProxy_;
DefaultPoolOperator defaultPoolOperator_ = new DefaultPoolOperator(
stakingProxy_,
weth_
);
defaultPoolOperator = defaultPoolOperator_;
defaultPoolId = defaultPoolOperator_.poolId();
votingPeriod = params.votingPeriod; votingPeriod = params.votingPeriod;
proposalThreshold = params.proposalThreshold; proposalThreshold = params.proposalThreshold;
quorumThreshold = params.quorumThreshold; quorumThreshold = params.quorumThreshold;
defaultPoolId = params.defaultPoolId;
IStaking.Pool memory defaultPool = stakingProxy_.getStakingPool(params.defaultPoolId);
defaultPoolOperator = DefaultPoolOperator(defaultPool.operator);
} }
// solhint-disable // solhint-disable
@ -286,6 +278,12 @@ contract ZrxTreasury is
// Add voting power for operated staking pools. // Add voting power for operated staking pools.
for (uint256 i = 0; i != operatedPoolIds.length; i++) { for (uint256 i = 0; i != operatedPoolIds.length; i++) {
for (uint256 j = 0; j != i; j++) {
require(
operatedPoolIds[i] != operatedPoolIds[j],
"getVotingPower/DUPLICATE_POOL_ID"
);
}
IStaking.Pool memory pool = stakingProxy.getStakingPool(operatedPoolIds[i]); IStaking.Pool memory pool = stakingProxy.getStakingPool(operatedPoolIds[i]);
require( require(
pool.operator == account, pool.operator == account,

View File

@ -28,6 +28,7 @@ blockchainTests.resets('Treasury governance', env => {
votingPeriod: new BigNumber(3).times(stakingConstants.ONE_DAY_IN_SECONDS), votingPeriod: new BigNumber(3).times(stakingConstants.ONE_DAY_IN_SECONDS),
proposalThreshold: new BigNumber(100), proposalThreshold: new BigNumber(100),
quorumThreshold: new BigNumber(1000), quorumThreshold: new BigNumber(1000),
defaultPoolId: stakingConstants.INITIAL_POOL_ID,
}; };
const PROPOSAL_DESCRIPTION = 'A very compelling proposal!'; const PROPOSAL_DESCRIPTION = 'A very compelling proposal!';
const TREASURY_BALANCE = constants.INITIAL_ERC20_BALANCE; const TREASURY_BALANCE = constants.INITIAL_ERC20_BALANCE;
@ -135,6 +136,16 @@ blockchainTests.resets('Treasury governance', env => {
.approve(erc20ProxyContract.address, constants.INITIAL_ERC20_ALLOWANCE) .approve(erc20ProxyContract.address, constants.INITIAL_ERC20_ALLOWANCE)
.awaitTransactionSuccessAsync({ from: delegator }); .awaitTransactionSuccessAsync({ from: delegator });
defaultPoolOperator = await DefaultPoolOperatorContract.deployFrom0xArtifactAsync(
artifacts.DefaultPoolOperator,
env.provider,
env.txDefaults,
{ ...artifacts, ...erc20Artifacts },
staking.address,
weth.address,
);
defaultPoolId = stakingConstants.INITIAL_POOL_ID;
const createStakingPoolTx = staking.createStakingPool(stakingConstants.PPM, false); const createStakingPoolTx = staking.createStakingPool(stakingConstants.PPM, false);
nonDefaultPoolId = await createStakingPoolTx.callAsync({ from: poolOperator }); nonDefaultPoolId = await createStakingPoolTx.callAsync({ from: poolOperator });
await createStakingPoolTx.awaitTransactionSuccessAsync({ from: poolOperator }); await createStakingPoolTx.awaitTransactionSuccessAsync({ from: poolOperator });
@ -145,9 +156,9 @@ blockchainTests.resets('Treasury governance', env => {
env.txDefaults, env.txDefaults,
{ ...artifacts, ...erc20Artifacts }, { ...artifacts, ...erc20Artifacts },
staking.address, staking.address,
weth.address,
TREASURY_PARAMS, TREASURY_PARAMS,
); );
await zrx.mint(TREASURY_BALANCE).awaitTransactionSuccessAsync(); await zrx.mint(TREASURY_BALANCE).awaitTransactionSuccessAsync();
await zrx.transfer(treasury.address, TREASURY_BALANCE).awaitTransactionSuccessAsync(); await zrx.transfer(treasury.address, TREASURY_BALANCE).awaitTransactionSuccessAsync();
actions = [ actions = [
@ -166,10 +177,6 @@ blockchainTests.resets('Treasury governance', env => {
value: constants.ZERO_AMOUNT, value: constants.ZERO_AMOUNT,
}, },
]; ];
defaultPoolId = await treasury.defaultPoolId().callAsync();
const defaultPoolOperatorAddress = await treasury.defaultPoolOperator().callAsync();
defaultPoolOperator = new DefaultPoolOperatorContract(defaultPoolOperatorAddress, env.provider, env.txDefaults);
}); });
describe('getVotingPower()', () => { describe('getVotingPower()', () => {
it('Unstaked ZRX has no voting power', async () => { it('Unstaked ZRX has no voting power', async () => {
@ -222,6 +229,19 @@ blockchainTests.resets('Treasury governance', env => {
const operatorVotingPower = await treasury.getVotingPower(poolOperator, [nonDefaultPoolId]).callAsync(); const operatorVotingPower = await treasury.getVotingPower(poolOperator, [nonDefaultPoolId]).callAsync();
expect(operatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.dividedBy(2)); expect(operatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.dividedBy(2));
}); });
it('Reverts if given duplicate pool IDs', async () => {
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, nonDefaultPoolId),
TREASURY_PARAMS.proposalThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const tx = treasury.getVotingPower(poolOperator, [nonDefaultPoolId, nonDefaultPoolId]).callAsync();
return expect(tx).to.revertWith('getVotingPower/DUPLICATE_POOL_ID');
});
it('Correctly sums voting power delegated to multiple pools', async () => { it('Correctly sums voting power delegated to multiple pools', async () => {
await staking await staking
.stake(TREASURY_PARAMS.proposalThreshold.times(2)) .stake(TREASURY_PARAMS.proposalThreshold.times(2))