Skip to content

Commit

Permalink
Soloseng/update-celo-L2-distribution-logic (#11045)
Browse files Browse the repository at this point in the history
* modified L2 CELO distribution logic

* rename functions

* ++ more name changes

* change more variable names

* ∆ gold to celo in contract

* contract rename

* forgotten names
  • Loading branch information
soloseng authored Jun 21, 2024
1 parent 0f7753e commit 1d553de
Show file tree
Hide file tree
Showing 16 changed files with 930 additions and 862 deletions.
4 changes: 2 additions & 2 deletions packages/protocol/contractPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ export const SOLIDITY_08_PACKAGE = {
proxiesPath: '/', // Proxies are still with 0.5 contracts
// Proxies shouldn't have to be added to a list manually
// https://github.com/celo-org/celo-monorepo/issues/10555
contracts: ['GasPriceMinimum', 'FeeCurrencyDirectory', 'MintGoldSchedule'],
contracts: ['GasPriceMinimum', 'FeeCurrencyDirectory', 'CeloDistributionSchedule'],
proxyContracts: [
'GasPriceMinimumProxy',
'FeeCurrencyDirectoryProxy',
'MentoFeeCurrencyAdapterV1',
'MintGoldScheduleProxy',
'CeloDistributionScheduleProxy',
],
truffleConfig: 'truffle-config0.8.js',
} satisfies ContractPackage
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import "../../contracts/common/Initializable.sol";
import "../../contracts-0.8/common/interfaces/IGoldToken.sol";

/**
* @title Contract for minting new CELO token based on a schedule.
* @title Contract for distributing CELO token based on a schedule.
*/
contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2Check {
contract CeloDistributionSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2Check {
using FixidityLib for FixidityLib.Fraction;

uint256 constant GENESIS_GOLD_SUPPLY = 600000000 ether; // 600 million Gold
uint256 constant GOLD_SUPPLY_CAP = 1000000000 ether; // 1 billion Gold
uint256 constant GENESIS_CELO_SUPPLY = 600000000 ether; // 600 million Celo
uint256 constant CELO_SUPPLY_CAP = 1000000000 ether; // 1 billion Celo
uint256 constant YEARS_LINEAR = 15;
uint256 constant SECONDS_LINEAR = YEARS_LINEAR * 365 * 1 days;

Expand All @@ -27,7 +27,7 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
uint256 public l2StartTime;
uint256 public totalSupplyAtL2Start;

uint256 public totalMintedBySchedule;
uint256 public totalDistributedBySchedule;
address public communityRewardFund;
address public carbonOffsettingPartner;

Expand All @@ -38,7 +38,7 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
event CarbonOffsettingFundSet(address indexed partner, uint256 fraction);

modifier whenActivated() {
require(areDependenciesSet, "Minting schedule has not been activated.");
require(areDependenciesSet, "Distribution schedule has not been activated.");
_;
}

Expand All @@ -49,14 +49,14 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
constructor(bool test) public Initializable(test) {}

/**
* @notice A constructor for initialising a new instance of a MintGoldSchedule contract.
* @notice A constructor for initialising a new instance of a CeloDistributionSchedule contract.
*/
function initialize() external initializer {
_transferOwnership(msg.sender);
}

/**
* @notice Sets the minting schedule dependencies during L2 transition.
* @notice Sets the distribution schedule dependencies during L2 transition.
* @param _l2StartTime The timestamp of L1 to L2 transition.
* @param _communityRewardFraction The percentage of rewards that go the community funds.
* @param _carbonOffsettingPartner The address of the carbon offsetting partner.
Expand All @@ -70,6 +70,7 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
uint256 _carbonOffsettingFraction,
address registryAddress
) external onlyOwner onlyL2 {
require(address(this).balance > 0, "Contract does not have CELO balance.");
require(!areDependenciesSet, "Contract has already been activated.");
require(registryAddress != address(0), "The registry address cannot be the zero address");
require(block.timestamp > _l2StartTime, "L2 start time cannot be set to a future date.");
Expand All @@ -83,32 +84,38 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
}

/**
* @notice Mints CELO to the community and carbon offsetting funds according to the predefined schedule.
* @notice Distributes CELO to the community and carbon offsetting funds according to the predefined schedule.
*/
function mintAccordingToSchedule() external nonReentrant onlyL2 returns (bool) {
function distributeAccordingToSchedule() external nonReentrant onlyL2 returns (bool) {
(
uint256 targetGoldTotalSupply,
uint256 communityRewardFundMintAmount,
uint256 carbonOffsettingPartnerMintAmount
) = getTargetGoldTotalSupply();

uint256 mintableAmount = Math.min(
getRemainingBalanceToMint(),
targetGoldTotalSupply - getGoldToken().totalSupply()
uint256 targetCeloTotalSupply,
uint256 communityRewardFundDistributionAmount,
uint256 carbonOffsettingPartnerDistributionAmount
) = getTargetCeloTotalSupply();

uint256 distributableAmount = Math.min(
getRemainingBalanceToDistribute(),
targetCeloTotalSupply - getGoldToken().totalSupply()
);

require(mintableAmount > 0, "Mintable amount must be greater than zero");
totalMintedBySchedule += mintableAmount;
require(distributableAmount > 0, "Distributable amount must be greater than zero.");
require(address(this).balance > distributableAmount, "Contract balance is insufficient.");

IGoldToken goldToken = IGoldToken(address(getGoldToken()));
totalDistributedBySchedule += distributableAmount;

IGoldToken celoToken = IGoldToken(address(getGoldToken()));

celoToken.increaseSupply(
communityRewardFundDistributionAmount + carbonOffsettingPartnerDistributionAmount
);
require(
goldToken.mint(communityRewardFund, communityRewardFundMintAmount),
"Failed to mint to community partner."
celoToken.transfer(communityRewardFund, communityRewardFundDistributionAmount),
"Failed to transfer to community partner."
);

require(
goldToken.mint(carbonOffsettingPartner, carbonOffsettingPartnerMintAmount),
"Failed to mint to carbon offsetting partner."
celoToken.transfer(carbonOffsettingPartner, carbonOffsettingPartnerDistributionAmount),
"Failed to transfer to carbon offsetting partner."
);
return true;
}
Expand All @@ -129,6 +136,13 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
return carbonOffsettingFraction.unwrap();
}

/**
* @return The total balance distributed by the CeloDistributionSchedule contract.
*/
function getTotalDistributedBySchedule() external view returns (uint256) {
return totalDistributedBySchedule;
}

/**
* @notice Returns the storage, major, minor, and patch version of the contract.
* @return Storage version of the contract.
Expand Down Expand Up @@ -200,40 +214,33 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
}

/**
* @notice Calculates remaining CELO balance to mint.
* @return The remaining CELO balance to mint.
*/
function getRemainingBalanceToMint() public view returns (uint256) {
return GOLD_SUPPLY_CAP - getGoldToken().totalSupply();
}

/**
* @return The total balance minted by the MintGoldSchedule contract.
* @notice Calculates remaining CELO balance to distribute.
* @return The remaining CELO balance to distribute.
*/
function getTotalMintedBySchedule() public view returns (uint256) {
return totalMintedBySchedule;
function getRemainingBalanceToDistribute() public view returns (uint256) {
return CELO_SUPPLY_CAP - getGoldToken().totalSupply();
}

/**
* @return The currently mintable amount.
* @return The currently distributable amount.
*/
function getMintableAmount() public view returns (uint256) {
(uint256 targetGoldTotalSupply, , ) = getTargetGoldTotalSupply();
return targetGoldTotalSupply - getGoldToken().totalSupply();
function getDistributableAmount() public view returns (uint256) {
(uint256 targetCeloTotalSupply, , ) = getTargetCeloTotalSupply();
return targetCeloTotalSupply - getGoldToken().totalSupply();
}

/**
* @notice Returns the target CELO supply according to the target schedule.
* @return targetGoldTotalSupply The target total CELO supply according to the target schedule.
* @return communityTargetRewards The community reward that can be minted according to the target schedule.
* @return carbonFundTargetRewards The carbon offsetting reward that can be minted according to the target schedule.
* @return targetCeloTotalSupply The target total CELO supply according to the target schedule.
* @return communityTargetRewards The community reward that can be distributed according to the target schedule.
* @return carbonFundTargetRewards The carbon offsetting reward that can be distributed according to the target schedule.
*/
function getTargetGoldTotalSupply()
function getTargetCeloTotalSupply()
public
view
whenActivated
returns (
uint256 targetGoldTotalSupply,
uint256 targetCeloTotalSupply,
uint256 communityTargetRewards,
uint256 carbonFundTargetRewards
)
Expand All @@ -243,20 +250,20 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2

uint256 timeSinceL2Start = block.timestamp - l2StartTime;
uint256 totalL2LinearSecondsAvailable = SECONDS_LINEAR - (l2StartTime - GENESIS_START_TIME);
uint256 mintedOnL1 = totalSupplyAtL2Start - GENESIS_GOLD_SUPPLY;
uint256 mintedOnL1 = totalSupplyAtL2Start - GENESIS_CELO_SUPPLY;

bool isLinearDistribution = timeSinceL2Start < totalL2LinearSecondsAvailable;
if (isLinearDistribution) {
(
targetGoldTotalSupply,
targetCeloTotalSupply,
communityTargetRewards,
carbonFundTargetRewards
) = _calculateTargetReward(timeSinceL2Start, totalL2LinearSecondsAvailable, mintedOnL1);

return (targetGoldTotalSupply, communityTargetRewards, carbonFundTargetRewards);
return (targetCeloTotalSupply, communityTargetRewards, carbonFundTargetRewards);
} else {
(
targetGoldTotalSupply,
targetCeloTotalSupply,
communityTargetRewards,
carbonFundTargetRewards
) = _calculateTargetReward(
Expand All @@ -265,13 +272,13 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
mintedOnL1
);

bool hasNotYetMintedAllLinearRewards = totalMintedBySchedule +
GENESIS_GOLD_SUPPLY +
bool hasNotYetDistributedAllLinearRewards = totalDistributedBySchedule +
GENESIS_CELO_SUPPLY +
mintedOnL1 <
targetGoldTotalSupply;
targetCeloTotalSupply;

if (hasNotYetMintedAllLinearRewards) {
return (targetGoldTotalSupply, communityTargetRewards, carbonFundTargetRewards);
if (hasNotYetDistributedAllLinearRewards) {
return (targetCeloTotalSupply, communityTargetRewards, carbonFundTargetRewards);
}
revert("Block reward calculation for years 15-30 unimplemented");
return (0, 0, 0);
Expand All @@ -286,7 +293,7 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
internal
view
returns (
uint256 targetGoldTotalSupply,
uint256 targetCeloTotalSupply,
uint256 communityTargetRewards,
uint256 carbonFundTargetRewards
)
Expand All @@ -296,7 +303,7 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
_totalL2LinearSecondsAvailable
);
// Pay out half of all block rewards linearly.
uint256 totalLinearRewards = (GOLD_SUPPLY_CAP - GENESIS_GOLD_SUPPLY) / 2; //(200 million) includes validator rewards.
uint256 totalLinearRewards = (CELO_SUPPLY_CAP - GENESIS_CELO_SUPPLY) / 2; //(200 million) includes validator rewards.

FixidityLib.Fraction memory l2LinearRewards = FixidityLib.newFixed(
totalLinearRewards - _mintedOnL1
Expand All @@ -321,10 +328,10 @@ contract MintGoldSchedule is UsingRegistry, ReentrancyGuard, Initializable, IsL2
.divide(totalL2LinearSecondsAvailableFraction)
.fromFixed();

targetGoldTotalSupply =
targetCeloTotalSupply =
communityTargetRewards +
carbonFundTargetRewards +
GENESIS_GOLD_SUPPLY +
GENESIS_CELO_SUPPLY +
_mintedOnL1;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IMintGoldScheduleInitializer {
interface ICeloDistributionScheduleInitializer {
function initialize() external;
}
14 changes: 11 additions & 3 deletions packages/protocol/contracts-0.8/common/interfaces/IGoldToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@ interface IGoldToken is IERC20 {
function setRegistry(address registryAddress) external;

/**
* @notice Used set the address of the MintGoldSchedule contract.
* @param goldTokenMintingScheduleAddress The address of the MintGoldSchedule contract.
* @notice Used set the address of the CeloDistributionSchedule contract.
* @param celoTokenDistributionScheduleAddress The address of the CeloDistributionSchedule contract.
*/
function setGoldTokenMintingScheduleAddress(address goldTokenMintingScheduleAddress) external;
function setCeloTokenDistributionScheduleAddress(
address celoTokenDistributionScheduleAddress
) external;

/**
* @dev Mints a new token.
* @param to The address that will own the minted token.
* @param value The amount of token to be minted.
*/
function mint(address to, uint256 value) external returns (bool);

/**
* @notice Increases the variable for total amount of CELO in existence.
* @param amount The amount to increase counter by.
*/
function increaseSupply(uint256 amount) external;
}
32 changes: 18 additions & 14 deletions packages/protocol/contracts/common/GoldToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "./CalledByVm.sol";
import "./Initializable.sol";
import "./interfaces/ICeloToken.sol";
import "./interfaces/ICeloVersionedContract.sol";
import "./interfaces/IMintGoldSchedule.sol";
import "./interfaces/ICeloDistributionSchedule.sol";
import "../../contracts-0.8/common/IsL2Check.sol";

contract GoldToken is
Expand Down Expand Up @@ -37,19 +37,22 @@ contract GoldToken is
// Burn address is 0xdEaD because truffle is having buggy behaviour with the zero address
address constant BURN_ADDRESS = address(0x000000000000000000000000000000000000dEaD);

IMintGoldSchedule public goldTokenMintingSchedule;
ICeloDistributionSchedule public celoTokenDistributionSchedule;

event Transfer(address indexed from, address indexed to, uint256 value);

event TransferComment(string comment);

event Approval(address indexed owner, address indexed spender, uint256 value);

event SetGoldTokenMintingScheduleAddress(address indexed newScheduleAddress);
event SetCeloTokenDistributionScheduleAddress(address indexed newScheduleAddress);

modifier onlySchedule() {
if (isL2()) {
require(msg.sender == address(goldTokenMintingSchedule), "Only MintGoldSchedule can call.");
require(
msg.sender == address(celoTokenDistributionSchedule),
"Only CeloDistributionSchedule can call."
);
} else {
require(msg.sender == address(0), "Only VM can call.");
}
Expand All @@ -73,20 +76,20 @@ contract GoldToken is
}

/**
* @notice Used set the address of the MintGoldSchedule contract.
* @param goldTokenMintingScheduleAddress The address of the MintGoldSchedule contract.
* @notice Used set the address of the CeloDistributionSchedule contract.
* @param celoTokenDistributionScheduleAddress The address of the CeloDistributionSchedule contract.
*/
function setGoldTokenMintingScheduleAddress(
address goldTokenMintingScheduleAddress
function setCeloTokenDistributionScheduleAddress(
address celoTokenDistributionScheduleAddress
) external onlyOwner {
require(
goldTokenMintingScheduleAddress != address(0) ||
goldTokenMintingScheduleAddress != address(goldTokenMintingSchedule),
celoTokenDistributionScheduleAddress != address(0) ||
celoTokenDistributionScheduleAddress != address(celoTokenDistributionSchedule),
"Invalid address."
);
goldTokenMintingSchedule = IMintGoldSchedule(goldTokenMintingScheduleAddress);
celoTokenDistributionSchedule = ICeloDistributionSchedule(celoTokenDistributionScheduleAddress);

emit SetGoldTokenMintingScheduleAddress(goldTokenMintingScheduleAddress);
emit SetCeloTokenDistributionScheduleAddress(celoTokenDistributionScheduleAddress);
}

/**
Expand Down Expand Up @@ -198,8 +201,9 @@ contract GoldToken is
* @notice Mints new CELO and gives it to 'to'.
* @param to The account for which to mint tokens.
* @param value The amount of CELO to mint.
* @dev This function will be deprecated in L2.
*/
function mint(address to, uint256 value) external onlySchedule returns (bool) {
function mint(address to, uint256 value) external onlyL1 onlyVm returns (bool) {
if (value == 0) {
return true;
}
Expand All @@ -221,7 +225,7 @@ contract GoldToken is
* @dev This function will be deprecated in L2. The onlyway to increase
* the supply is with the mint function.
*/
function increaseSupply(uint256 amount) external onlyL1 onlyVm {
function increaseSupply(uint256 amount) external onlySchedule {
totalSupply_ = totalSupply_.add(amount);
}

Expand Down
Loading

0 comments on commit 1d553de

Please sign in to comment.