diff --git a/eth-contracts/contracts/DelegateManager.sol b/eth-contracts/contracts/DelegateManager.sol index 989ff19ee32..ce9d22c2623 100644 --- a/eth-contracts/contracts/DelegateManager.sol +++ b/eth-contracts/contracts/DelegateManager.sol @@ -22,6 +22,12 @@ contract DelegateManager is InitializableV2 { string private constant ERROR_MINIMUM_DELEGATION = ( "DelegateManager: Minimum delegation amount required" ); + string private constant ERROR_ONLY_SP_GOVERNANCE = ( + "DelegateManager: Only callable by target SP or governance" + ); + string private constant ERROR_DELEGATOR_STAKE = ( + "DelegateManager: Delegator must be staked for SP" + ); address private governanceAddress; address private stakingAddress; @@ -30,6 +36,8 @@ contract DelegateManager is InitializableV2 { /** * Number of blocks an undelegate operation has to wait + * The undelegate operation speed bump is to prevent a delegator from + * attempting to remove their delegation in anticipation of a slash. * @notice must be >= Governance.votingPeriod */ uint256 private undelegateLockupDuration; @@ -40,6 +48,20 @@ contract DelegateManager is InitializableV2 { // Minimum amount of delegation allowed uint256 private minDelegationAmount; + /** + * Lockup duration for a remove delegator request. + * The remove delegator speed bump is to prevent a service provider from maliciously + * removing a delegator prior to the evaluation of a proposal. + * @notice must be >= Governance.votingPeriod + */ + uint256 private removeDelegatorLockupDuration; + + /** + * Evaluation period for a remove delegator request + * @notice added to expiry block calculated for removeDelegatorLockupDuration + */ + uint256 private removeDelegatorEvalDuration; + // Staking contract ref ERC20Mintable private audiusToken; @@ -67,28 +89,38 @@ contract DelegateManager is InitializableV2 { // Requester to pending undelegate request mapping (address => UndelegateStakeRequest) private undelegateRequests; + // Pending remove delegator requests + // service provider -> (delegator -> lockupExpiryBlock) + mapping (address => mapping (address => uint256)) private removeDelegatorRequests; + event IncreaseDelegatedStake( - address indexed _delegator, - address indexed _serviceProvider, - uint256 indexed _increaseAmount + address indexed _delegator, + address indexed _serviceProvider, + uint256 indexed _increaseAmount ); event DecreaseDelegatedStake( - address indexed _delegator, - address indexed _serviceProvider, - uint256 indexed _decreaseAmount + address indexed _delegator, + address indexed _serviceProvider, + uint256 indexed _decreaseAmount ); event Claim( - address indexed _claimer, - uint256 indexed _rewards, - uint256 indexed _newTotal + address indexed _claimer, + uint256 indexed _rewards, + uint256 indexed _newTotal ); event Slash( - address indexed _target, - uint256 indexed _amount, - uint256 indexed _newTotal + address indexed _target, + uint256 indexed _amount, + uint256 indexed _newTotal + ); + + event DelegatorRemoved( + address indexed _serviceProvider, + address indexed _delegator, + uint256 indexed _unstakedAmount ); event MaxDelegatorsUpdated(uint256 indexed _maxDelegators); @@ -98,6 +130,8 @@ contract DelegateManager is InitializableV2 { event StakingAddressUpdated(address indexed _newStakingAddress); event ServiceProviderFactoryAddressUpdated(address indexed _newServiceProviderFactoryAddress); event ClaimsManagerAddressUpdated(address indexed _newClaimsManagerAddress); + event RemoveDelegatorLockupDurationUpdated(uint256 indexed _removeDelegatorLockupDuration); + event RemoveDelegatorEvalDurationUpdated(uint256 indexed _removeDelegatorEvalDuration); /** * @notice Function to initialize the contract @@ -115,11 +149,18 @@ contract DelegateManager is InitializableV2 { { _updateGovernanceAddress(_governanceAddress); audiusToken = ERC20Mintable(_tokenAddress); - undelegateLockupDuration = _undelegateLockupDuration; maxDelegators = 175; // Default minimum delegation amount set to 100AUD minDelegationAmount = 100 * 10**uint256(18); InitializableV2.initialize(); + + _updateUndelegateLockupDuration(_undelegateLockupDuration); + + // 1 week = 168hrs * 60 min/hr * 60 sec/min / ~13 sec/block = 46523 blocks + _updateRemoveDelegatorLockupDuration(46523); + + // 24hr * 60min/hr * 60sec/min / ~13 sec/block = 6646 blocks + removeDelegatorEvalDuration = 6646; } /** @@ -218,7 +259,7 @@ contract DelegateManager is InitializableV2 { address delegator = msg.sender; require( _delegatorExistsForSP(delegator, _target), - "DelegateManager: Delegator must be staked for SP" + ERROR_DELEGATOR_STAKE ); // Confirm no pending delegation request @@ -514,7 +555,55 @@ contract DelegateManager is InitializableV2 { } /** - * @notice Allow a service provider to forcibly remove a delegator + * @notice Initiate forcible removal of a delegator + * @param _serviceProvider - address of service provider + * @param _delegator - address of delegator + */ + function requestRemoveDelegator(address _serviceProvider, address _delegator) external { + _requireIsInitialized(); + + require( + msg.sender == _serviceProvider || msg.sender == governanceAddress, + ERROR_ONLY_SP_GOVERNANCE + ); + + require( + removeDelegatorRequests[_serviceProvider][_delegator] == 0, + "DelegateManager: Pending remove delegator request" + ); + + require( + _delegatorExistsForSP(_delegator, _serviceProvider), + ERROR_DELEGATOR_STAKE + ); + + // Update lockup + removeDelegatorRequests[_serviceProvider][_delegator] = ( + block.number + removeDelegatorLockupDuration + ); + } + + /** + * @notice Cancel pending removeDelegator request + * @param _serviceProvider - address of service provider + * @param _delegator - address of delegator + */ + function cancelRemoveDelegator(address _serviceProvider, address _delegator) external { + require( + msg.sender == _serviceProvider || msg.sender == governanceAddress, + ERROR_ONLY_SP_GOVERNANCE + ); + require( + removeDelegatorRequests[_serviceProvider][_delegator] != 0, + "DelegateManager: No pending request" + ); + + // Reset lockup expiry + removeDelegatorRequests[_serviceProvider][_delegator] = 0; + } + + /** + * @notice Evaluate removeDelegator request * @param _serviceProvider - address of service provider * @param _delegator - address of delegator * @return Updated total amount delegated to the service provider by delegator @@ -525,8 +614,26 @@ contract DelegateManager is InitializableV2 { require( msg.sender == _serviceProvider || msg.sender == governanceAddress, - "DelegateManager: Only callable by target SP or governance" + ERROR_ONLY_SP_GOVERNANCE + ); + + require( + removeDelegatorRequests[_serviceProvider][_delegator] != 0, + "DelegateManager: No pending request" + ); + + // Enforce lockup expiry block + require( + block.number >= removeDelegatorRequests[_serviceProvider][_delegator], + "DelegateManager: Lockup must be expired" + ); + + // Enforce evaluation window for request + require( + block.number < removeDelegatorRequests[_serviceProvider][_delegator] + removeDelegatorEvalDuration, + "DelegateManager: RemoveDelegator evaluation window expired" ); + uint256 unstakeAmount = delegateInfo[_delegator][_serviceProvider]; // Unstake on behalf of target service provider Staking(stakingAddress).undelegateStakeFor( @@ -558,6 +665,10 @@ contract DelegateManager is InitializableV2 { // Remove from list of delegators _removeFromDelegatorsList(_serviceProvider, _delegator); + + // Reset lockup expiry + removeDelegatorRequests[_serviceProvider][_delegator] = 0; + emit DelegatorRemoved(_serviceProvider, _delegator, unstakeAmount); } /** @@ -569,7 +680,7 @@ contract DelegateManager is InitializableV2 { require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE); - undelegateLockupDuration = _duration; + _updateUndelegateLockupDuration(_duration); emit UndelegateLockupDurationUpdated(_duration); } @@ -599,6 +710,32 @@ contract DelegateManager is InitializableV2 { emit MinDelegationUpdated(_minDelegationAmount); } + /** + * @notice Update remove delegator lockup duration + * @param _duration - new lockup duration + */ + function updateRemoveDelegatorLockupDuration(uint256 _duration) external { + _requireIsInitialized(); + + require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE); + + _updateRemoveDelegatorLockupDuration(_duration); + emit RemoveDelegatorLockupDurationUpdated(_duration); + } + + /** + * @notice Update remove delegator evaluation window duration + * @param _duration - new window duration + */ + function updateRemoveDelegatorEvalDuration(uint256 _duration) external { + _requireIsInitialized(); + + require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE); + + removeDelegatorEvalDuration = _duration; + emit RemoveDelegatorEvalDurationUpdated(_duration); + } + /** * @notice Set the Governance address * @dev Only callable by Governance address @@ -608,6 +745,7 @@ contract DelegateManager is InitializableV2 { _requireIsInitialized(); require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE); + _updateGovernanceAddress(_governanceAddress); governanceAddress = _governanceAddress; emit GovernanceAddressUpdated(_governanceAddress); @@ -706,6 +844,22 @@ contract DelegateManager is InitializableV2 { return (req.serviceProvider, req.amount, req.lockupExpiryBlock); } + /** + * @notice Get status of pending remove delegator request for a given address + * @param _serviceProvider - address of the service provider + * @param _delegator - address of the delegator + * @return - current lockup expiry block for remove delegator request + */ + function getPendingRemoveDelegatorRequest( + address _serviceProvider, + address _delegator + ) external view returns (uint256) + { + _requireIsInitialized(); + + return removeDelegatorRequests[_serviceProvider][_delegator]; + } + /// @notice Get current undelegate lockup duration function getUndelegateLockupDuration() external view returns (uint256) @@ -733,6 +887,24 @@ contract DelegateManager is InitializableV2 { return minDelegationAmount; } + /// @notice Get the duration for remove delegator request lockup + function getRemoveDelegatorLockupDuration() + external view returns (uint256) + { + _requireIsInitialized(); + + return removeDelegatorLockupDuration; + } + + /// @notice Get the duration for evaluation of remove delegator operations + function getRemoveDelegatorEvalDuration() + external view returns (uint256) + { + _requireIsInitialized(); + + return removeDelegatorEvalDuration; + } + /// @notice Get the Governance address function getGovernanceAddress() external view returns (address) { _requireIsInitialized(); @@ -975,6 +1147,32 @@ contract DelegateManager is InitializableV2 { governanceAddress = _governanceAddress; } + /** + * @notice Set the remove delegator lockup duration after validating against governance + * @param _duration - Incoming remove delegator duration value + */ + function _updateRemoveDelegatorLockupDuration(uint256 _duration) internal { + Governance governance = Governance(governanceAddress); + require( + _duration > governance.getVotingPeriod() + governance.getExecutionDelay(), + "DelegateManager: removeDelegatorLockupDuration duration must be greater than governance votingPeriod + executionDelay" + ); + removeDelegatorLockupDuration = _duration; + } + + /** + * @notice Set the undelegate lockup duration after validating against governance + * @param _duration - Incoming undelegate lockup duration value + */ + function _updateUndelegateLockupDuration(uint256 _duration) internal { + Governance governance = Governance(governanceAddress); + require( + _duration > governance.getVotingPeriod() + governance.getExecutionDelay(), + "DelegateManager: undelegateLockupDuration duration must be greater than governance votingPeriod + executionDelay" + ); + undelegateLockupDuration = _duration; + } + /** * @notice Returns if delegator has delegated to a service provider * @param _delegator - address of delegator diff --git a/eth-contracts/migrations/8_delegate_manager_migration.js b/eth-contracts/migrations/8_delegate_manager_migration.js index eec2106f3aa..a2a0ac04876 100644 --- a/eth-contracts/migrations/8_delegate_manager_migration.js +++ b/eth-contracts/migrations/8_delegate_manager_migration.js @@ -16,9 +16,9 @@ const serviceProviderFactoryKey = web3.utils.utf8ToHex('ServiceProviderFactory') const governanceKey = web3.utils.utf8ToHex('Governance') const delegateManagerKey = web3.utils.utf8ToHex('DelegateManager') -// stake lockup duration = 1 wk in blocks +// undelegate lockup duration = 1 wk in blocks // - 1/13 block/s * 604800 s/wk ~= 46523 block/wk -const decreaseStakeLockupDuration = 46523 +const undelegateLockupDuration = 46523 module.exports = (deployer, network, accounts) => { deployer.then(async () => { @@ -43,7 +43,7 @@ module.exports = (deployer, network, accounts) => { const initializeCallData = _lib.encodeCall( 'initialize', ['address', 'address', 'uint256'], - [token.address, governanceAddress, decreaseStakeLockupDuration] + [token.address, governanceAddress, undelegateLockupDuration] ) const delegateManagerProxy = await deployer.deploy( AudiusAdminUpgradeabilityProxy, diff --git a/eth-contracts/test/delegateManager.test.js b/eth-contracts/test/delegateManager.test.js index 8b11fb84054..fe2cfefba72 100644 --- a/eth-contracts/test/delegateManager.test.js +++ b/eth-contracts/test/delegateManager.test.js @@ -30,6 +30,7 @@ const VOTING_PERIOD = 10 const EXECUTION_DELAY = VOTING_PERIOD const VOTING_QUORUM_PERCENT = 10 const DECREASE_STAKE_LOCKUP_DURATION = 10 +const UNDELEGATE_LOCKUP_DURATION = 21 const callValue0 = _lib.toBN(0) @@ -161,7 +162,7 @@ contract('DelegateManager', async (accounts) => { const delegateManagerInitializeData = _lib.encodeCall( 'initialize', ['address', 'address', 'uint256'], - [token.address, governance.address, 10] + [token.address, governance.address, UNDELEGATE_LOCKUP_DURATION] ) let delegateManager0 = await DelegateManager.new({ from: proxyDeployerAddress }) let delegateManagerProxy = await AudiusAdminUpgradeabilityProxy.new( @@ -174,14 +175,6 @@ contract('DelegateManager', async (accounts) => { delegateManager = await DelegateManager.at(delegateManagerProxy.address) await registry.addContract(delegateManagerKey, delegateManagerProxy.address, { from: proxyDeployerAddress }) - // Clear min delegation amount for testing - await governance.guardianExecuteTransaction( - delegateManagerKey, - _lib.toBN(0), - 'updateMinDelegationAmount(uint256)', - _lib.abiEncode(['uint256'], [0]), - { from: guardianAddress } - ) // ---- Configuring addresses await _lib.configureGovernanceStakingAddress( governance, @@ -259,6 +252,58 @@ contract('DelegateManager', async (accounts) => { claimsManagerProxy.address, delegateManagerProxy.address ) + + // Clear min delegation amount for testing + let updateTx = await governance.guardianExecuteTransaction( + delegateManagerKey, + _lib.toBN(0), + 'updateMinDelegationAmount(uint256)', + _lib.abiEncode(['uint256'], [0]), + { from: guardianAddress } + ) + await expectEvent.inTransaction( + updateTx.tx, + DelegateManager, + 'MinDelegationUpdated', + { _minDelegationAmount: '0' } + ) + // Expect revert for 8 since it is below votingPeriod + votingDelay + await _lib.assertRevert( + governance.guardianExecuteTransaction( + delegateManagerKey, + _lib.toBN(0), + 'updateRemoveDelegatorLockupDuration(uint256)', + _lib.abiEncode(['uint256'], [8]), + { from: guardianAddress } + ) + ) + // Reset lockup and eval duration for testing + updateTx = await governance.guardianExecuteTransaction( + delegateManagerKey, + _lib.toBN(0), + 'updateRemoveDelegatorLockupDuration(uint256)', + _lib.abiEncode(['uint256'], [100]), + { from: guardianAddress } + ) + await expectEvent.inTransaction( + updateTx.tx, + DelegateManager, + 'RemoveDelegatorLockupDurationUpdated', + { _removeDelegatorLockupDuration: '100' } + ) + updateTx = await governance.guardianExecuteTransaction( + delegateManagerKey, + _lib.toBN(0), + 'updateRemoveDelegatorEvalDuration(uint256)', + _lib.abiEncode(['uint256'], [10]), + { from: guardianAddress } + ) + await expectEvent.inTransaction( + updateTx.tx, + DelegateManager, + 'RemoveDelegatorEvalDurationUpdated', + { _removeDelegatorEvalDuration: '10' } + ) }) /* Helper functions */ @@ -1629,6 +1674,14 @@ contract('DelegateManager', async (accounts) => { delegateManager.slash(10, slasherAccount), "Only callable by Governance contract" ) + await _lib.assertRevert( + delegateManager.updateRemoveDelegatorLockupDuration(10, { from: accounts[3] }), + "Only callable by Governance contract" + ) + await _lib.assertRevert( + delegateManager.updateRemoveDelegatorEvalDuration(10, { from: accounts[3] }), + "Only callable by Governance contract" + ) }) it('Fail to set service addresses from non-governance contract', async () => { @@ -1712,14 +1765,27 @@ contract('DelegateManager', async (accounts) => { 'Expect pending request' ) - // fail to removeDelegator from not a SP or governance + // Fail to call removeDelegator from not a SP or governance await _lib.assertRevert( delegateManager.removeDelegator(stakerAccount, delegatorAccount1, { from: delegatorAccount1 }), "Only callable by target SP or governance" ) - // Forcibly remove the delegator from service provider account + // Confirm failure without a pending request + await _lib.assertRevert( + delegateManager.removeDelegator(stakerAccount, delegatorAccount1, { from: stakerAccount }), + "No pending request" + ) + + // Remove delegator + await delegateManager.requestRemoveDelegator(stakerAccount, delegatorAccount1, { from: stakerAccount }) + + let requestTargetBlock = await delegateManager.getPendingRemoveDelegatorRequest(stakerAccount, delegatorAccount1) + + // Move to valid block and actually perform remove + await time.advanceBlockTo(requestTargetBlock) await delegateManager.removeDelegator(stakerAccount, delegatorAccount1, { from: stakerAccount }) + let stakeAfterRemoval = await delegateManager.getDelegatorStakeForServiceProvider(delegatorAccount1, stakerAccount) let delegatorsList = await delegateManager.getDelegatorsList(stakerAccount) pendingUndelegateRequest = await delegateManager.getPendingUndelegateRequest(delegatorAccount1) @@ -1768,14 +1834,19 @@ contract('DelegateManager', async (accounts) => { ) }) - it('Deregister delegator', async () => { + // Validate behavior around removeDelegator + // - expiry block calculated correctly (done) + // - cancelRemoveDelegator behavior resets request (done) + // - evaluation window enforced (done) + // - invalid delegator for this sp during call to removeDelegator (done) + // - Pending request before call to requestRemoveDelegator + it('removeDelegator validation', async () => { const delegationAmount = _lib.toBN(100) + const delegatorAccount2 = accounts[5] // Transfer tokens to delegator await token.transfer(delegatorAccount1, delegationAmount, { from: proxyDeployerAddress }) - await token.approve( - stakingAddress, - delegationAmount, - { from: delegatorAccount1 }) + await token.transfer(delegatorAccount2, delegationAmount, { from: proxyDeployerAddress }) + await token.approve(stakingAddress, delegationAmount, { from: delegatorAccount1 }) await delegateManager.delegateStake( stakerAccount, delegationAmount, @@ -1788,9 +1859,32 @@ contract('DelegateManager', async (accounts) => { delegateManager.removeDelegator(stakerAccount, delegatorAccount1, { from: delegatorAccount1 }), "Only callable by target SP or governance" ) - + + let removeReqDuration = await delegateManager.getRemoveDelegatorLockupDuration() + let removeReqEvalDuration = await delegateManager.getRemoveDelegatorEvalDuration() + + // Remove delegator + let tx = await delegateManager.requestRemoveDelegator(stakerAccount, delegatorAccount1, { from: stakerAccount }) + let blocknumber = _lib.toBN(tx.receipt.blockNumber) + let expectedTarget = blocknumber.add(removeReqDuration) + + let requestTargetBlock = await delegateManager.getPendingRemoveDelegatorRequest(stakerAccount, delegatorAccount1) + assert.isTrue(requestTargetBlock.eq(expectedTarget), 'Target unexpected') + + // Move to valid block and actually perform remove + await time.advanceBlockTo(requestTargetBlock) + tx = await delegateManager.removeDelegator(stakerAccount, delegatorAccount1, { from: stakerAccount }) + await expectEvent.inTransaction( + tx.tx, + DelegateManager, + 'DelegatorRemoved', + { _serviceProvider: stakerAccount, _delegator: delegatorAccount1, _unstakedAmount: delegationAmount } + ) + + requestTargetBlock = await delegateManager.getPendingRemoveDelegatorRequest(stakerAccount, delegatorAccount1) + assert.isTrue(requestTargetBlock.eq(_lib.toBN(0)), 'Reset expected') + // Forcibly remove the delegator from service provider account - await delegateManager.removeDelegator(stakerAccount, delegatorAccount1, { from: stakerAccount }) let stakeAfterRemoval = await delegateManager.getDelegatorStakeForServiceProvider(delegatorAccount1, stakerAccount) let delegatorsList = await delegateManager.getDelegatorsList(stakerAccount) assert.isTrue(stakeAfterRemoval.eq(_lib.toBN(0)), 'Expect 0 delegated stake') @@ -1799,6 +1893,61 @@ contract('DelegateManager', async (accounts) => { let delegatorTokenBalance2 = await token.balanceOf(delegatorAccount1) let diff = delegatorTokenBalance2.sub(delegatorTokenBalance) assert.isTrue(diff.eq(delegationAmount), 'Expect full delegation amount to be refunded') + + // Try to remove a delegator that does not yet exist, confirm failure + await _lib.assertRevert( + delegateManager.requestRemoveDelegator(stakerAccount, delegatorAccount2, { from: stakerAccount }), + 'Delegator must be staked for SP' + ) + + // Delegate from a new account + await token.approve( + stakingAddress, + delegationAmount, + { from: delegatorAccount2 }) + await delegateManager.delegateStake( + stakerAccount, + delegationAmount, + { from: delegatorAccount2 }) + + // Request removal + tx = await delegateManager.requestRemoveDelegator(stakerAccount, delegatorAccount2, { from: stakerAccount }) + blocknumber = _lib.toBN(tx.receipt.blockNumber) + expectedTarget = blocknumber.add(removeReqDuration) + + requestTargetBlock = await delegateManager.getPendingRemoveDelegatorRequest(stakerAccount, delegatorAccount2) + assert.isTrue(requestTargetBlock.eq(expectedTarget), 'Target block unexpected') + + // Call from wrong account + await _lib.assertRevert( + delegateManager.cancelRemoveDelegator(stakerAccount, delegatorAccount2), + 'Only callable by target SP' + ) + + // Cancel and validate request + await delegateManager.cancelRemoveDelegator(stakerAccount, delegatorAccount2, { from: stakerAccount }) + let requestTargetBlockAfterCancel = await delegateManager.getPendingRemoveDelegatorRequest(stakerAccount, delegatorAccount2) + assert.isTrue(requestTargetBlockAfterCancel.eq(_lib.toBN(0)), 'Expect reset') + + // Reissue request + await delegateManager.requestRemoveDelegator(stakerAccount, delegatorAccount2, { from: stakerAccount }) + requestTargetBlock = await delegateManager.getPendingRemoveDelegatorRequest(stakerAccount, delegatorAccount2) + let evalBlock = requestTargetBlock.add(removeReqEvalDuration) + + // Progress to the evaluation block + await time.advanceBlockTo(evalBlock) + + // Confirm rejection after window + await _lib.assertRevert( + delegateManager.removeDelegator(stakerAccount, delegatorAccount2, { from: stakerAccount }), + 'RemoveDelegator evaluation window expired' + ) + + // Retry should fail here as the request has not been cancelled yet, but the window has expired + await _lib.assertRevert( + delegateManager.requestRemoveDelegator(stakerAccount, delegatorAccount2, { from: stakerAccount }), + 'Pending remove delegator request' + ) }) describe('Service provider decrease stake behavior', async () => { diff --git a/eth-contracts/test/governance.test.js b/eth-contracts/test/governance.test.js index c5908dbe101..700a4977f14 100644 --- a/eth-contracts/test/governance.test.js +++ b/eth-contracts/test/governance.test.js @@ -53,6 +53,7 @@ contract('Governance.sol', async (accounts) => { const maxInProgressProposals = 20 const maxDescriptionLength = 250 const executionDelay = votingPeriod + const undelegateLockupDuration = 21 // intentionally not using acct0 to make sure no TX accidentally succeeds without specifying sender const [, proxyAdminAddress, proxyDeployerAddress, newUpdateAddress] = accounts @@ -205,7 +206,7 @@ contract('Governance.sol', async (accounts) => { const delegateManagerInitializeData = _lib.encodeCall( 'initialize', ['address', 'address', 'uint256'], - [token.address, governance.address, 10] + [token.address, governance.address, undelegateLockupDuration] ) let delegateManager0 = await DelegateManager.new({ from: proxyDeployerAddress }) let delegateManagerProxy = await AudiusAdminUpgradeabilityProxy.new(