Skip to content

Commit

Permalink
Merge pull request #538 from lidofinance/feature/lido-locator-tests
Browse files Browse the repository at this point in the history
lidoLocator update tests
  • Loading branch information
TheDZhon authored Feb 4, 2023
2 parents afdf94a + 0908f2c commit 52b5efa
Show file tree
Hide file tree
Showing 26 changed files with 1,855 additions and 1,702 deletions.
14 changes: 8 additions & 6 deletions contracts/0.4.24/Lido.sol
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ contract Lido is StETHPermit, AragonApp, Versioned {
* are treated as a user deposit
*/
function receiveELRewards() external payable {
require(msg.sender == getLidoLocator().elRewardsVault());

require(msg.sender == getLidoLocator().elRewardsVault(), "EXECUTION_LAYER_REAWARDS_VAULT_ONLY");

TOTAL_EL_REWARDS_COLLECTED_POSITION.setStorageUint256(getTotalELRewardsCollected().add(msg.value));

Expand Down Expand Up @@ -505,7 +506,8 @@ contract Lido is StETHPermit, AragonApp, Versioned {
uint256 _elRewardsVaultBalance,
// Decision about withdrawals processing
uint256 _requestIdToFinalizeUpTo,
uint256 _finalizationShareRate
uint256 _finalizationShareRate,
bool _isBunkerMode
) external returns (
uint256 totalPooledEther,
uint256 totalShares,
Expand Down Expand Up @@ -625,9 +627,6 @@ contract Lido is StETHPermit, AragonApp, Versioned {
uint256 preClValidators = CL_VALIDATORS_POSITION.getStorageUint256();
require(_postClValidators >= preClValidators, "REPORTED_LESS_VALIDATORS");

// Save the current CL balance and validators to
// calculate rewards on the next push
CL_BALANCE_POSITION.setStorageUint256(_postClBalance);

if (_postClValidators > preClValidators) {
CL_VALIDATORS_POSITION.setStorageUint256(_postClValidators);
Expand All @@ -637,6 +636,10 @@ contract Lido is StETHPermit, AragonApp, Versioned {
uint256 preCLBalance = CL_BALANCE_POSITION.getStorageUint256();
uint256 rewardsBase = appearedValidators.mul(DEPOSIT_SIZE).add(preCLBalance);

// Save the current CL balance and validators to
// calculate rewards on the next push
CL_BALANCE_POSITION.setStorageUint256(_postClBalance);

return _signedSub(int256(_postClBalance), int256(rewardsBase));
}

Expand Down Expand Up @@ -1026,7 +1029,6 @@ contract Lido is StETHPermit, AragonApp, Versioned {

// distribute rewards to Lido and Node Operators
uint256 sharesMintedAsFees = _processRewards(clBalanceDiff, withdrawals, elRewards);

_applyCoverage(tokenRebaseLimiter);

(
Expand Down
68 changes: 68 additions & 0 deletions contracts/0.8.9/test_helpers/LidoLocatorMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: 2023 Lido <[email protected]>

// SPDX-License-Identifier: GPL-3.0

// See contracts/COMPILERS.md
pragma solidity 0.8.9;

contract LidoLocatorMock {
struct ContractAddresses {
address lido;
address depositSecurityModule;
address elRewardsVault;
address accountingOracle;
address legacyOracle;
address safetyNetsRegistry;
address selfOwnedStEthBurner;
address validatorExitBus;
address stakingRouter;
address treasury;
address withdrawalQueue;
address withdrawalVault;
address rebaseReceiver;
}

address public immutable lido;
address public immutable depositSecurityModule;
address public immutable elRewardsVault;
address public immutable accountingOracle;
address public immutable legacyOracle;
address public immutable safetyNetsRegistry;
address public immutable selfOwnedStEthBurner;
address public immutable validatorExitBus;
address public immutable stakingRouter;
address public immutable treasury;
address public immutable withdrawalQueue;
address public immutable withdrawalVault;
address public immutable rebaseReceiver;

constructor (
ContractAddresses memory addrs
) {
lido = addrs.lido;
depositSecurityModule = addrs.depositSecurityModule;
elRewardsVault = addrs.elRewardsVault;
accountingOracle = addrs.accountingOracle;
legacyOracle = addrs.legacyOracle;
safetyNetsRegistry = addrs.safetyNetsRegistry;
selfOwnedStEthBurner = addrs.selfOwnedStEthBurner;
validatorExitBus = addrs.validatorExitBus;
stakingRouter = addrs.stakingRouter;
treasury = addrs.treasury;
withdrawalQueue = addrs.withdrawalQueue;
withdrawalVault = addrs.withdrawalVault;
rebaseReceiver = addrs.rebaseReceiver;
}

function coreComponents() external view returns(address,address,address,address,address,address) {
return (
elRewardsVault,
address(0),
address(0),
address(0),
withdrawalQueue,
withdrawalVault
);
}

}
197 changes: 51 additions & 146 deletions test/0.4.24/lido.rewards-distribution.test.js
Original file line number Diff line number Diff line change
@@ -1,162 +1,67 @@
const { assert } = require('chai')
const { newDao, newApp } = require('./helpers/dao')
const { assertBn } = require('@aragon/contract-helpers-test/src/asserts')
const hre = require('hardhat')

const { ZERO_ADDRESS, bn } = require('@aragon/contract-helpers-test')

const NodeOperatorsRegistry = artifacts.require('NodeOperatorsRegistryMock')
const { EvmSnapshot } = require('../helpers/blockchain')
const { setupNodeOperatorsRegistry } = require('../helpers/staking-modules')
const { deployProtocol } = require('../helpers/protocol')
const { assert } = require('../helpers/assert')
const { pushOracleReport } = require('../helpers/oracle')

const Lido = artifacts.require('LidoMock.sol')
const DepositContractMock = artifacts.require('DepositContractMock.sol')
const StakingRouter = artifacts.require('StakingRouterMock.sol')
const ModuleSolo = artifacts.require('ModuleSolo.sol')
const EIP712StETH = artifacts.require('EIP712StETH')
const ELRewardsVault = artifacts.require('LidoExecutionLayerRewardsVault.sol')

const ETH = (value) => web3.utils.toWei(value + '', 'ether')

const cfgCurated = {
moduleFee: 500,
treasuryFee: 500,
targetShare: 10000
}

const cfgCommunity = {
moduleFee: 566,
treasuryFee: 123,
targetShare: 5000
}

contract('Lido: staking router reward distribution', ([appManager, voting, treasury, oracle, depositor, user2]) => {
let appBase, nodeOperatorsRegistryBase, app, depositContract, curatedModule, stakingRouter, soloModule
let dao, acl
let elRewardsVault

before('deploy base app', async () => {
// Deploy the app's base contract.
appBase = await Lido.new()
depositContract = await DepositContractMock.new()
nodeOperatorsRegistryBase = await NodeOperatorsRegistry.new()
})

const pushOracleReport = async (epochId, clValidators, clBalance) => {
const elRewardsVaultBalance = await web3.eth.getBalance(elRewardsVault.address)
return await app.handleOracleReport(
clValidators,
clBalance,
0,
elRewardsVaultBalance,
0,
0,
{from: oracle}
)
}

beforeEach('deploy dao and app', async () => {
; ({ dao, acl } = await newDao(appManager))

// Instantiate a proxy for the app, using the base contract as its logic implementation.
let proxyAddress = await newApp(dao, 'lido', appBase.address, appManager)
app = await Lido.at(proxyAddress)

// NodeOperatorsRegistry
proxyAddress = await newApp(dao, 'node-operators-registry', nodeOperatorsRegistryBase.address, appManager)
curatedModule = await NodeOperatorsRegistry.at(proxyAddress)
await curatedModule.initialize(app.address, '0x01')

// Set up the app's permissions.
await acl.createPermission(voting, app.address, await app.PAUSE_ROLE(), appManager, { from: appManager })
await acl.createPermission(voting, app.address, await app.RESUME_ROLE(), appManager, { from: appManager })
await acl.createPermission(voting, app.address, await app.BURN_ROLE(), appManager, { from: appManager })
await acl.createPermission(voting, app.address, await app.MANAGE_PROTOCOL_CONTRACTS_ROLE(), appManager, { from: appManager })
await acl.createPermission(voting, app.address, await app.MANAGE_MAX_POSITIVE_TOKEN_REBASE_ROLE(), appManager, {
from: appManager
})
await acl.createPermission(voting, app.address, await app.STAKING_PAUSE_ROLE(), appManager, { from: appManager })
await acl.createPermission(voting, app.address, await app.STAKING_CONTROL_ROLE(), appManager, { from: appManager })

await acl.createPermission(voting, curatedModule.address, await curatedModule.MANAGE_SIGNING_KEYS(), appManager, { from: appManager })
await acl.createPermission(voting, curatedModule.address, await curatedModule.ADD_NODE_OPERATOR_ROLE(), appManager, {
from: appManager
})

await acl.createPermission(voting, curatedModule.address, await curatedModule.SET_NODE_OPERATOR_NAME_ROLE(), appManager, {
from: appManager
})
await acl.createPermission(voting, curatedModule.address, await curatedModule.SET_NODE_OPERATOR_ADDRESS_ROLE(), appManager, {
from: appManager
})
await acl.createPermission(voting, curatedModule.address, await curatedModule.SET_NODE_OPERATOR_LIMIT_ROLE(), appManager, {
from: appManager
})

const eip712StETH = await EIP712StETH.new()
elRewardsVault = await ELRewardsVault.new(app.address, treasury)

stakingRouter = await StakingRouter.new(depositContract.address)
// initialize
const wc = '0x'.padEnd(66, '1234')
await stakingRouter.initialize(appManager, app.address, wc)

// Set up the staking router permissions.
const STAKING_MODULE_MANAGE_ROLE = await stakingRouter.STAKING_MODULE_MANAGE_ROLE()
const REPORT_REWARDS_MINTED_ROLE = await stakingRouter.REPORT_REWARDS_MINTED_ROLE()

await stakingRouter.grantRole(REPORT_REWARDS_MINTED_ROLE, app.address, { from: appManager })
await stakingRouter.grantRole(STAKING_MODULE_MANAGE_ROLE, voting, { from: appManager })

await acl.createPermission(stakingRouter.address, curatedModule.address, await curatedModule.STAKING_ROUTER_ROLE(), appManager, {
from: appManager
contract('Lido: staking router reward distribution', ([depositor, user2]) => {
let app, oracle, curatedModule, stakingRouter, soloModule, snapshot, appManager, consensus, treasury


before(async () => {
const deployed = await deployProtocol({
stakingModulesFactory: async (protocol) => {
const curatedModule = await setupNodeOperatorsRegistry(protocol, true)
const soloModule = await ModuleSolo.new(protocol.pool.address, { from: protocol.appManager.address })
return [
{
module: curatedModule,
name: 'Curated',
targetShares: 10000,
moduleFee: 500,
treasuryFee: 500
},
{
module: soloModule,
name: 'Curated',
targetShares: 5000,
moduleFee: 566,
treasuryFee: 123
}
]
}
})

soloModule = await ModuleSolo.new(app.address, { from: appManager })

await stakingRouter.addStakingModule(
'Curated',
curatedModule.address,
cfgCurated.targetShare,
cfgCurated.moduleFee,
cfgCurated.treasuryFee,
{
from: voting
}
)
app = deployed.pool
stakingRouter = deployed.stakingRouter
curatedModule = deployed.stakingModules[0]
soloModule = deployed.stakingModules[1]
consensus = deployed.consensusContract
oracle = deployed.oracle
appManager = deployed.appManager.address
treasury = deployed.treasury.address

await curatedModule.increaseTotalSigningKeysCount(500_000, { from: appManager })
await curatedModule.increaseDepositedSigningKeysCount(499_950, { from: appManager })
await curatedModule.increaseVettedSigningKeysCount(499_950, { from: appManager })

await stakingRouter.addStakingModule(
'Solo',
soloModule.address,
cfgCommunity.targetShare,
cfgCommunity.moduleFee,
cfgCommunity.treasuryFee,
{
from: voting
}
)
await soloModule.setTotalKeys(100, { from: appManager })
await soloModule.setTotalUsedKeys(10, { from: appManager })
await soloModule.setTotalStoppedKeys(0, { from: appManager })

// Initialize the app's proxy.
await app.initialize(
oracle,
treasury,
stakingRouter.address,
depositor,
elRewardsVault.address,
ZERO_ADDRESS,
eip712StETH.address,
)

assert((await app.isStakingPaused()) === true)
assert((await app.isStopped()) === true)
await app.resumeProtocolAndStaking({ from: voting })
assert((await app.isStakingPaused()) === false)
assert((await app.isStopped()) === false)

await depositContract.reset()
snapshot = new EvmSnapshot(hre.ethers.provider)
await snapshot.make()
})

afterEach(async () => {
await snapshot.rollback()
})

it('Rewards distribution fills treasury', async () => {
Expand All @@ -167,11 +72,11 @@ contract('Lido: staking router reward distribution', ([appManager, voting, treas
await app.submit(ZERO_ADDRESS, { from: user2, value: ETH(32) })

const treasuryBalanceBefore = await app.balanceOf(treasury)
await pushOracleReport(100, 0, beaconBalance, { from: appManager })
await pushOracleReport(consensus, oracle, 0, beaconBalance)

const treasuryBalanceAfter = await app.balanceOf(treasury)
assert(treasuryBalanceAfter.gt(treasuryBalanceBefore))
assertBn(fixRound(treasuryBalanceBefore.add(treasuryRewards)), fixRound(treasuryBalanceAfter))
assert.equals(fixRound(treasuryBalanceBefore.add(treasuryRewards)), fixRound(treasuryBalanceAfter))
})

it('Rewards distribution fills modules', async () => {
Expand All @@ -185,13 +90,13 @@ contract('Lido: staking router reward distribution', ([appManager, voting, treas
moduleBalanceBefore.push(await app.balanceOf(recipients[i]))
}

await pushOracleReport(100, 0, beaconBalance)
await pushOracleReport(consensus, oracle, 0, beaconBalance)

for (let i = 0; i < recipients.length; i++) {
const moduleBalanceAfter = await app.balanceOf(recipients[i])
const moduleRewards = bn(beaconBalance).mul(stakingModuleFees[i]).div(precisionPoints)
assert(moduleBalanceAfter.gt(moduleBalanceBefore[i]))
assertBn(fixRound(moduleBalanceBefore[i].add(moduleRewards)), fixRound(moduleBalanceAfter))
assert.equals(fixRound(moduleBalanceBefore[i].add(moduleRewards)), fixRound(moduleBalanceAfter))
}
})
})
Expand Down
Loading

0 comments on commit 52b5efa

Please sign in to comment.