Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

startNextEpochProcess unit & integration test #11191

Merged
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f9979ce
unit test with mocks
soloseng Aug 19, 2024
75bf938
++ integration tests
soloseng Aug 20, 2024
cb47ec9
clean up
soloseng Aug 20, 2024
fbdc5fd
Merge branch 'feat/l2-epoch-system' into soloseng/startNextEpochProce…
soloseng Aug 20, 2024
b8f5584
-- logging
soloseng Aug 20, 2024
282ef91
removed duplicate interface
soloseng Aug 20, 2024
40711cb
using `MockCeloToken` to get test to pass.
soloseng Aug 20, 2024
654b279
removed endEpochTimestamp
soloseng Aug 20, 2024
2072107
moved IEpochManager to 0.5 folder
soloseng Aug 20, 2024
9a6c01f
added L2 conditions for EpochRewards functions using precompiles
soloseng Aug 20, 2024
c48b8e5
renamed EpochManagerInitializer due to name conflict
soloseng Aug 21, 2024
47f93af
++ more unit test
soloseng Aug 21, 2024
d3b1db4
setup anvil migration
soloseng Aug 22, 2024
a6e2844
Merge branch 'feat/l2-epoch-system' into soloseng/startNextEpochProce…
soloseng Aug 26, 2024
2ba9360
compiles
soloseng Aug 26, 2024
35b637a
++ require fund in unreleased treasury
soloseng Aug 27, 2024
c02c0a4
Updated regex
soloseng Aug 27, 2024
92ac7cf
++ registry 0.8 for testing only
soloseng Aug 27, 2024
f50ac8b
clean up
soloseng Aug 27, 2024
3aa816c
++ unit test
soloseng Aug 27, 2024
180162b
initial integration test using L1 devchain
soloseng Aug 27, 2024
22946ff
++ comment
soloseng Aug 27, 2024
0ed7e93
-- forge based integration test
soloseng Aug 27, 2024
7fcc9a8
Merge branch 'feat/l2-epoch-system' into soloseng/startNextEpochProce…
soloseng Aug 27, 2024
8f0ea64
++ to const
soloseng Aug 27, 2024
cfa7cdb
happy linter
soloseng Aug 28, 2024
cdf7aea
update contract name
soloseng Aug 28, 2024
b49f929
++ PR feedback
soloseng Aug 29, 2024
1b616ca
++ checks
soloseng Sep 10, 2024
b47dba3
updated carbon address
soloseng Sep 10, 2024
48db871
proxy stableToken mint call via Validators contract
soloseng Sep 11, 2024
8a8f4d9
Merge branch 'feat/l2-epoch-system' into soloseng/startNextEpochProce…
soloseng Sep 11, 2024
bbb239f
-- duplicate imports
soloseng Sep 11, 2024
2ffeed2
removed registry08. replaced with vm call
soloseng Sep 11, 2024
b195ebb
PR feedback
soloseng Sep 11, 2024
3c2ebfc
-- coment
soloseng Sep 12, 2024
83bd5aa
passing unit tests
soloseng Sep 12, 2024
1d4eaa4
clean up
soloseng Sep 12, 2024
43ae993
++ mintStable test
soloseng Sep 12, 2024
de06b4e
-- TODO; compiles test when filtering
soloseng Sep 12, 2024
0dc7c92
PR feedback
soloseng Sep 16, 2024
4d5360d
updated migration script to add more validators
soloseng Sep 16, 2024
729008f
passing integration test
soloseng Sep 16, 2024
20f8ae4
removed test for zero amount
soloseng Sep 16, 2024
6f986bd
yarn build fix
soloseng Sep 16, 2024
b09a3bb
clean up comments && TODO
soloseng Sep 16, 2024
60e3e70
revert change as out of scope
soloseng Sep 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -8,9 +8,7 @@ import "./UsingRegistry.sol";
import "../common/IsL2Check.sol";

import "../../contracts/common/Initializable.sol";
import "../../contracts/common/interfaces/ICeloToken.sol";
import "./interfaces/ICeloUnreleasedTreasureInitializer.sol";
import "@openzeppelin/contracts8/token/ERC20/IERC20.sol";

/**
* @title Contract for unreleased Celo tokens.
@@ -49,8 +47,7 @@ contract CeloUnreleasedTreasure is UsingRegistry, ReentrancyGuard, Initializable
*/
function release(address to, uint256 amount) external onlyEpochManager {
require(address(this).balance >= amount, "Insufficient balance.");
IERC20 celoToken = IERC20(address(getCeloToken()));
celoToken.transfer(to, amount);
require(getCeloToken().transfer(to, amount), "CELO transfer failed.");
soloseng marked this conversation as resolved.
Show resolved Hide resolved
emit Released(to, amount);
}

64 changes: 39 additions & 25 deletions packages/protocol/contracts-0.8/common/EpochManager.sol
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import "../common/UsingRegistry.sol";
import "../../contracts/common/Initializable.sol";
import "../../contracts/common/interfaces/IEpochManager.sol";
import "../../contracts/common/interfaces/ICeloVersionedContract.sol";
import "../../contracts/common/interfaces/IEpochManager.sol";

contract EpochManager is
Initializable,
@@ -23,7 +24,6 @@ contract EpochManager is
uint256 firstBlock;
uint256 lastBlock;
uint256 startTimestamp;
uint256 endTimestamp;
uint256 rewardsBlock;
}

@@ -33,7 +33,7 @@ contract EpochManager is
}

struct EpochProcessState {
EpochProcessStatus status; // TODO maybe a enum for future updates
EpochProcessStatus status;
uint256 perValidatorReward; // The per validator epoch reward.
uint256 totalRewardsVoter; // The total rewards to voters.
uint256 totalRewardsCommunity; // The total community reward.
@@ -59,7 +59,7 @@ contract EpochManager is
mapping(address => uint256) public validatorPendingPayments;

address public carbonOffsettingPartner;
address public epochManagerInitializer;
address public epochManagerEnabler;

/**
* @notice Event emited when epochProcessing has begun.
@@ -73,8 +73,8 @@ contract EpochManager is
*/
event EpochProcessingEnded(uint256 indexed epochNumber);

modifier onlyEpochManagerInitializer() {
require(msg.sender == epochManagerInitializer, "msg.sender is not Initializer");
modifier onlyEpochManagerEnabler() {
require(msg.sender == epochManagerEnabler, "msg.sender is not Initializer");
_;
}

@@ -93,14 +93,15 @@ contract EpochManager is
address registryAddress,
uint256 newEpochDuration,
address _carbonOffsettingPartner,
address _epochManagerInitializer
address _epochManagerEnabler
) external initializer {
require(_epochManagerInitializer != address(0), "EpochManagerInitializer address is required");
require(_carbonOffsettingPartner != address(0), "carbonOffsettingPartner address is required");
require(_epochManagerEnabler != address(0), "EpochManagerEnabler address is required");
_transferOwnership(msg.sender);
setRegistry(registryAddress);
setEpochDuration(newEpochDuration);
carbonOffsettingPartner = _carbonOffsettingPartner;
epochManagerInitializer = _epochManagerInitializer;
epochManagerEnabler = _epochManagerEnabler;
}

// DESIGNDESICION(XXX): we assume that the first epoch on the L2 starts as soon as the system is initialized
@@ -110,7 +111,16 @@ contract EpochManager is
uint256 firstEpochNumber,
uint256 firstEpochBlock,
address[] memory firstElected
) external onlyEpochManagerInitializer {
) external onlyEpochManagerEnabler {
require(
address(registry.getAddressForOrDie(CELO_UNRELEASED_TREASURE_REGISTRY_ID)).balance > 0,
"CeloUnreleasedTreasury not yet funded."
);
require(
getCeloToken().balanceOf(registry.getAddressForOrDie(CELO_UNRELEASED_TREASURE_REGISTRY_ID)) >
0,
"CeloUnreleasedTreasury not yet funded."
);
require(!systemAlreadyInitialized(), "Epoch system already initialized");
require(firstEpochNumber > 0, "First epoch number must be greater than 0");
require(firstEpochBlock > 0, "First epoch block must be greater than 0");
@@ -127,7 +137,7 @@ contract EpochManager is
_currentEpoch.startTimestamp = block.timestamp;

elected = firstElected;
epochManagerInitializer = address(0);
epochManagerEnabler = address(0);
soloseng marked this conversation as resolved.
Show resolved Hide resolved
}

// TODO maybe "freezeEpochRewards" "prepareForNextEpoch"
@@ -171,8 +181,6 @@ contract EpochManager is
// TODO complete this function
require(isOnEpochProcess(), "Epoch process is not started");
// finalize epoch
// TODO last block should be the block before and timestamp from previous block
epochs[currentEpochNumber].endTimestamp = block.timestamp;
epochs[currentEpochNumber].lastBlock = block.number - 1;
// start new epoch
currentEpochNumber++;
@@ -218,16 +226,9 @@ contract EpochManager is
}
soloseng marked this conversation as resolved.
Show resolved Hide resolved

/// returns the current epoch Info
function getCurrentEpoch() external view returns (uint256, uint256, uint256, uint256, uint256) {
function getCurrentEpoch() external view returns (uint256, uint256, uint256, uint256) {
Epoch storage _epoch = epochs[currentEpochNumber];

return (
_epoch.firstBlock,
_epoch.lastBlock,
_epoch.startTimestamp,
_epoch.endTimestamp,
_epoch.rewardsBlock
);
return (_epoch.firstBlock, _epoch.lastBlock, _epoch.startTimestamp, _epoch.rewardsBlock);
martinvol marked this conversation as resolved.
Show resolved Hide resolved
}

/// returns the current epoch number.
@@ -236,6 +237,21 @@ contract EpochManager is
return currentEpochNumber;
}

/// returns epoch processing state
function getEpochProcessingState()
external
view
returns (uint256, uint256, uint256, uint256, uint256)
{
return (
uint256(epochProcessing.status),
epochProcessing.perValidatorReward,
epochProcessing.totalRewardsVoter,
epochProcessing.totalRewardsCommunity,
epochProcessing.totalRewardsCarbonFund
);
}

function getElected() external view returns (address[] memory) {
return elected;
}
@@ -285,11 +301,10 @@ contract EpochManager is
}

function systemAlreadyInitialized() public view returns (bool) {
return initialized && epochManagerInitializer == address(0);
return initialized && epochManagerEnabler == address(0);
}

function allocateValidatorsRewards() internal {
// TODO complete this function
uint256 totalRewards = 0;
IScoreReader scoreReader = getScoreReader();
IValidators validators = getValidators();
@@ -305,15 +320,14 @@ contract EpochManager is
totalRewards += validatorReward;
}
// Mint all cUSD required for payment and the corresponding CELO
IStableToken(getStableToken()).mint(address(this), totalRewards);
validators.mintStableToEpochManager(totalRewards);
// this should have a setter for the oracle.

(uint256 numerator, uint256 denominator) = IOracle(address(getSortedOracles())).getExchangeRate(
address(getStableToken())
);

uint256 CELOequivalent = (numerator * totalRewards) / denominator;
// this is not a mint anymore
getCeloUnreleasedTreasure().release(
registry.getAddressForOrDie(RESERVE_REGISTRY_ID),
CELOequivalent
Original file line number Diff line number Diff line change
@@ -6,9 +6,10 @@ import "../common/UsingPrecompiles.sol";

import "../../contracts/common/Initializable.sol";
import "../../contracts/common/interfaces/ICeloVersionedContract.sol";
import "../../contracts/common/interfaces/IEpochManagerEnabler.sol";
import "../../contracts/governance/interfaces/IEpochRewards.sol";

contract EpochManagerInitializer is Initializable, UsingPrecompiles, UsingRegistry {
contract EpochManagerEnabler is Initializable, UsingPrecompiles, UsingRegistry {
uint256 public lastKnownEpochNumber;
address[] public lastKnownElectedAccounts;

4 changes: 1 addition & 3 deletions packages/protocol/contracts-0.8/common/ScoreManager.sol
Original file line number Diff line number Diff line change
@@ -16,10 +16,8 @@ contract ScoreManager is Initializable, Ownable {

/**
* @notice Used in place of the constructor to allow the contract to be upgradable via proxy.
* @param registryAddress The address of the registry core smart contract.
* @param newEpochDuration The duration of an epoch in seconds.
*/
function initialize(address registryAddress, uint256 newEpochDuration) external initializer {
function initialize() external initializer {
_transferOwnership(msg.sender);
}

9 changes: 4 additions & 5 deletions packages/protocol/contracts-0.8/common/UsingRegistry.sol
Original file line number Diff line number Diff line change
@@ -12,19 +12,18 @@ import "../../contracts/common/interfaces/IAccounts.sol";
import "../../contracts/common/interfaces/IEpochManager.sol";
import "../../contracts/common/interfaces/IFreezer.sol";
import "../../contracts/common/interfaces/ICeloUnreleasedTreasure.sol";
import "../../contracts/common/interfaces/IFeeCurrencyWhitelist.sol";
import "../../contracts/common/interfaces/IFeeHandlerSeller.sol";
import "../../contracts/common/interfaces/IEpochManager.sol";
import "../../contracts/governance/interfaces/IGovernance.sol";
import "../../contracts/governance/interfaces/ILockedGold.sol";
import "../../contracts/governance/interfaces/ILockedCelo.sol";
import "../../contracts/governance/interfaces/IValidators.sol";
import "../../contracts/governance/interfaces/IElection.sol";
import "../../contracts/governance/interfaces/IEpochRewards.sol";
import "../../contracts/stability/interfaces/ISortedOracles.sol";
import "../../contracts/common/interfaces/IFeeCurrencyWhitelist.sol";
import "./interfaces/IScoreReader.sol";

import "../../contracts/governance/interfaces/IElection.sol";
import "../../contracts/common/interfaces/IFeeHandlerSeller.sol";
import "../../contracts/governance/interfaces/IEpochRewards.sol";
import "./interfaces/IScoreReader.sol";

contract UsingRegistry is Ownable {
// solhint-disable state-visibility
34 changes: 0 additions & 34 deletions packages/protocol/contracts-0.8/common/interfaces/ICeloToken.sol

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IEpochManagerEnablerInitializer {
function initialize(address registryAddress) external;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IEpochManagerInitializer {
function initialize(
address registryAddress,
uint256 newEpochDuration,
address _carbonOffsettingPartner,
address _epochManagerEnabler
) external;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IScoreManagerInitializer {
function initialize() external;
}
60 changes: 60 additions & 0 deletions packages/protocol/contracts-0.8/common/test/MockCeloToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
pragma solidity >=0.8.0 <0.9.0;
soloseng marked this conversation as resolved.
Show resolved Hide resolved
// solhint-disable no-unused-vars

/**
* @title A mock StableToken for testing. This contract can be deprecated once GoldToken gets migrated to 0.8
*/
contract MockCeloToken08 {
uint256 public totalSupply_;
uint8 public constant decimals = 18;
mapping(address => uint256) balances;

uint256 constant L1_MINTED_CELO_SUPPLY = 692702432463315819704447326; // as of May 15 2024

uint256 constant CELO_SUPPLY_CAP = 1000000000 ether; // 1 billion Celo
uint256 constant GENESIS_CELO_SUPPLY = 600000000 ether; // 600 million Celo

uint256 constant FIFTEEN_YEAR_LINEAR_REWARD = (CELO_SUPPLY_CAP - GENESIS_CELO_SUPPLY) / 2; // 200 million Celo

uint256 constant FIFTEEN_YEAR_CELO_SUPPLY = GENESIS_CELO_SUPPLY + FIFTEEN_YEAR_LINEAR_REWARD; // 800 million Celo (includes GENESIS_CELO_SUPPLY)

uint256 constant MAX_L2_DISTRIBUTION = FIFTEEN_YEAR_CELO_SUPPLY - L1_MINTED_CELO_SUPPLY; // 107.2 million Celo

uint256 constant L2_INITIAL_STASH_BALANCE = FIFTEEN_YEAR_LINEAR_REWARD + MAX_L2_DISTRIBUTION; // leftover from L1 target supply plus the 2nd 15 year term.

function setTotalSupply(uint256 value) external {
totalSupply_ = value;
}

function transfer(address to, uint256 amount) external returns (bool) {
return _transfer(msg.sender, to, amount);
}

function transferFrom(address from, address to, uint256 amount) external returns (bool) {
return _transfer(from, to, amount);
}

function _transfer(address from, address to, uint256 amount) internal returns (bool) {
if (balances[from] < amount) {
return false;
}
balances[from] -= amount;
balances[to] += amount;
return true;
}

function setBalanceOf(address a, uint256 value) external {
balances[a] = value;
}

function balanceOf(address a) public view returns (uint256) {
return balances[a];
}

function totalSupply() public view returns (uint256) {
return totalSupply_;
}
function allocatedSupply() public view returns (uint256) {
return CELO_SUPPLY_CAP - L2_INITIAL_STASH_BALANCE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity >=0.8.0 <0.9.0;
// solhint-disable no-unused-vars

import "../../../contracts/common/interfaces/ICeloUnreleasedTreasure.sol";
import "../UsingRegistry.sol";

/**
* @title A mock CeloUnreleasedTreasure for testing.
*/
contract MockCeloUnreleasedTreasure is ICeloUnreleasedTreasure, UsingRegistry {
function release(address to, uint256 amount) external {
require(address(this).balance >= amount, "Insufficient balance.");
require(getCeloToken().transfer(to, amount), "CELO transfer failed.");
}
}
Loading
Loading