Skip to content

Commit

Permalink
test
Browse files Browse the repository at this point in the history
  • Loading branch information
mmsqe committed May 7, 2024
1 parent 93d6d16 commit 5e9415e
Show file tree
Hide file tree
Showing 6 changed files with 343 additions and 0 deletions.
7 changes: 7 additions & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ var ContractMigrations = map[string][]contractMigration{
Value: common.HexToHash("0x000000000000000000000000730CbB94480d50788481373B43d83133e171367e"),
},
},
"cronos_777-1": {
{
Contract: common.HexToAddress("0x28838c2e6db87977e0cae0e218f1929e440d1598"),
Slot: common.BigToHash(big.NewInt(0)),
Value: common.HexToHash("0x730CbB94480d50788481373B43d83133e171367e"),
},
},
}

func (app *App) RegisterUpgradeHandlers(cdc codec.BinaryCodec, clientKeeper clientkeeper.Keeper) {
Expand Down
10 changes: 10 additions & 0 deletions integration_tests/contracts/contracts/IMintedToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IMintedToken is IERC20 {
function SUPPLY_CAP() external view returns (uint256);

function mint(address account, uint256 amount) external returns (bool);
}
276 changes: 276 additions & 0 deletions integration_tests/contracts/contracts/TokenDistributor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./IMintedToken.sol";

import "@openzeppelin/contracts/access/Ownable.sol";

/**
* @title TokenDistributor
* @notice It handles the distribution of MTD token.
* It auto-adjusts block rewards over a set number of periods.
*/
contract TokenDistributor is Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
using SafeERC20 for IMintedToken;
uint256 public constant PRECISION_FACTOR = 10**18;
struct StakingPeriod {
uint256 rewardPerBlockForStaking;
uint256 rewardPerBlockForOthers;
uint256 periodLengthInBlock;
}

IMintedToken public immutable mintedToken;

address public immutable tokenSplitter;
address public stakingAddr;

// Number of reward periods
uint256 public immutable NUMBER_PERIODS;

// Block number when rewards start
uint256 public immutable START_BLOCK;

// Current phase for rewards
uint256 public currentPhase;

// Block number when rewards end
uint256 public endBlock;

// Block number of the last update
uint256 public lastRewardBlock;

// Tokens distributed per block for other purposes (team + treasury + trading rewards)
uint256 public rewardPerBlockForOthers;

// Tokens distributed per block for staking
uint256 public rewardPerBlockForStaking;

mapping(uint256 => StakingPeriod) public stakingPeriod;

event NewRewardsPerBlock(
uint256 indexed currentPhase,
uint256 startBlock,
uint256 rewardPerBlockForStaking,
uint256 rewardPerBlockForOthers
);
event SetupStakingAddress(address stakingAddr);

/**
* @notice Constructor
* @param _mintedToken MTD token address
* @param _tokenSplitter token splitter contract address (for team and trading rewards)
* @param _startBlock start block for reward program
* @param _rewardsPerBlockForStaking array of rewards per block for staking
* @param _rewardsPerBlockForOthers array of rewards per block for other purposes (team + treasury + trading rewards)
* @param _periodLengthesInBlocks array of period lengthes
* @param _numberPeriods number of periods with different rewards/lengthes (e.g., if 3 changes --> 4 periods)
*/
constructor(
address _mintedToken,
address _tokenSplitter,
uint256 _startBlock,
uint256[] memory _rewardsPerBlockForStaking,
uint256[] memory _rewardsPerBlockForOthers,
uint256[] memory _periodLengthesInBlocks,
uint256 _numberPeriods,
uint256 slippage
) {
require(_mintedToken != address(0), "Distributor: mintedToken must not be address(0)");
require(
(_periodLengthesInBlocks.length == _numberPeriods) &&
(_rewardsPerBlockForStaking.length == _numberPeriods) &&
(_rewardsPerBlockForOthers.length == _numberPeriods),
"Distributor: lengths must match numberPeriods"
);
require(_tokenSplitter != address(0), "Distributor: tokenSplitter must not be address(0)");

// 1. Operational checks for supply
uint256 nonCirculatingSupply = IMintedToken(_mintedToken).SUPPLY_CAP() -
IMintedToken(_mintedToken).totalSupply();

uint256 amountTokensToBeMinted;

for (uint256 i = 0; i < _numberPeriods; i++) {
amountTokensToBeMinted +=
(_rewardsPerBlockForStaking[i] * _periodLengthesInBlocks[i]) +
(_rewardsPerBlockForOthers[i] * _periodLengthesInBlocks[i]);

stakingPeriod[i] = StakingPeriod({
rewardPerBlockForStaking: _rewardsPerBlockForStaking[i],
rewardPerBlockForOthers: _rewardsPerBlockForOthers[i],
periodLengthInBlock: _periodLengthesInBlocks[i]
});
}
// require(amountTokensToBeMinted <= nonCirculatingSupply, "Distributor: rewards exceeds supply");
// uint256 residueAmt = nonCirculatingSupply - amountTokensToBeMinted;
// uint256 rewardSlippage = (residueAmt * 100 * PRECISION_FACTOR) / nonCirculatingSupply;
// require(rewardSlippage <= slippage, "Distributor: slippage exceeds");
// // 2. Store values
// mintedToken = IMintedToken(_mintedToken);
// tokenSplitter = _tokenSplitter;
// rewardPerBlockForStaking = _rewardsPerBlockForStaking[0];
// rewardPerBlockForOthers = _rewardsPerBlockForOthers[0];

// START_BLOCK = _startBlock;
// endBlock = _startBlock + _periodLengthesInBlocks[0];

// NUMBER_PERIODS = _numberPeriods;

// // Set the lastRewardBlock as the startBlock
// lastRewardBlock = _startBlock;
}

/**
* @dev updates the staking adddress as a mintedBoost contract address once it is deployed.
*/

function setupStakingAddress(address _stakingAddr) external onlyOwner {
require(_stakingAddr != address(0), "invalid address");
stakingAddr = _stakingAddr;
emit SetupStakingAddress(stakingAddr);
}

/**
* @notice Update pool rewards
*/
function updatePool() external nonReentrant {
_updatePool();
}

/**
* @notice Update reward variables of the pool
*/
function _updatePool() internal {
require(stakingAddr != address(0), "staking address not setup");
if (block.number <= lastRewardBlock) {
return;
}
(uint256 tokenRewardForStaking, uint256 tokenRewardForOthers) = _calculatePendingRewards();
// mint tokens only if token rewards for staking are not null
if (tokenRewardForStaking > 0) {
// It allows protection against potential issues to prevent funds from being locked
mintedToken.mint(stakingAddr, tokenRewardForStaking);
mintedToken.mint(tokenSplitter, tokenRewardForOthers);
}

// Update last reward block only if it wasn't updated after or at the end block
if (lastRewardBlock <= endBlock) {
lastRewardBlock = block.number;
}
}

function _calculatePendingRewards() internal returns (uint256, uint256) {
if (block.number <= lastRewardBlock) {
return (0, 0);
}
// Calculate multiplier
uint256 multiplier = _getMultiplier(lastRewardBlock, block.number, endBlock);
// Calculate rewards for staking and others
uint256 tokenRewardForStaking = multiplier * rewardPerBlockForStaking;
uint256 tokenRewardForOthers = multiplier * rewardPerBlockForOthers;

// Check whether to adjust multipliers and reward per block
while ((block.number > endBlock) && (currentPhase < (NUMBER_PERIODS - 1))) {
// Update rewards per block
_updateRewardsPerBlock(endBlock);

uint256 previousEndBlock = endBlock;

// Adjust the end block
endBlock += stakingPeriod[currentPhase].periodLengthInBlock;

// Adjust multiplier to cover the missing periods with other lower inflation schedule
uint256 newMultiplier = _getMultiplier(previousEndBlock, block.number, endBlock);

// Adjust token rewards
tokenRewardForStaking += (newMultiplier * rewardPerBlockForStaking);
tokenRewardForOthers += (newMultiplier * rewardPerBlockForOthers);
}
return (tokenRewardForStaking, tokenRewardForOthers);
}

function getPendingRewards() external view returns (uint256, uint256) {
if (block.number <= lastRewardBlock) {
return (0, 0);
}
// shadow state vars to avoid updates
uint256 tEndBlock = endBlock;
uint256 tCurrentPhase = currentPhase;
uint256 tRewardPerBlockForStaking = rewardPerBlockForStaking;
uint256 tRewardPerBlockForOthers = rewardPerBlockForOthers;
// Calculate multiplier
uint256 multiplier = _getMultiplier(lastRewardBlock, block.number, tEndBlock);
// Calculate rewards for staking and others
uint256 tokenRewardForStaking = multiplier * tRewardPerBlockForStaking;
uint256 tokenRewardForOthers = multiplier * tRewardPerBlockForOthers;
// Check whether to adjust multipliers and reward per block
while ((block.number > tEndBlock) && (tCurrentPhase < (NUMBER_PERIODS - 1))) {
// Update rewards per block
tCurrentPhase++;
tRewardPerBlockForStaking = stakingPeriod[tCurrentPhase].rewardPerBlockForStaking;
tRewardPerBlockForOthers = stakingPeriod[tCurrentPhase].rewardPerBlockForOthers;
uint256 previousEndBlock = tEndBlock;

// Adjust the end block
tEndBlock += stakingPeriod[tCurrentPhase].periodLengthInBlock;

// Adjust multiplier to cover the missing periods with other lower inflation schedule
uint256 newMultiplier = _getMultiplier(previousEndBlock, block.number, tEndBlock);

// Adjust token rewards
tokenRewardForStaking += (newMultiplier * tRewardPerBlockForStaking);
tokenRewardForOthers += (newMultiplier * tRewardPerBlockForOthers);
}
return (tokenRewardForStaking, tokenRewardForOthers);
}

function getPendingStakingRewards() external view returns (uint256) {
if (block.number <= lastRewardBlock) {
return 0;
}
uint256 multiplier = block.number - lastRewardBlock;
return multiplier * rewardPerBlockForStaking;
}

/**
* @notice Update rewards per block
* @dev Rewards are halved by 2 (for staking + others)
*/
function _updateRewardsPerBlock(uint256 _newStartBlock) internal {
// Update current phase
currentPhase++;

// Update rewards per block
rewardPerBlockForStaking = stakingPeriod[currentPhase].rewardPerBlockForStaking;
rewardPerBlockForOthers = stakingPeriod[currentPhase].rewardPerBlockForOthers;

emit NewRewardsPerBlock(
currentPhase,
_newStartBlock,
rewardPerBlockForStaking,
rewardPerBlockForOthers
);
}

/**
* @notice Return reward multiplier over the given "from" to "to" block.
* @param from block to start calculating reward
* @param to block to finish calculating reward
* @return the multiplier for the period
*/
function _getMultiplier(
uint256 from,
uint256 to,
uint256 tEndBlock
) internal pure returns (uint256) {
if (to <= tEndBlock) {
return to - from;
} else if (from >= tEndBlock) {
return 0;
} else {
return tEndBlock - from;
}
}
}
19 changes: 19 additions & 0 deletions integration_tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
contract_address,
contract_path,
deploy_contract,
derive_new_account,
fund_acc,
get_receipts_by_block,
modify_command_in_supervisor_config,
send_transaction,
Expand Down Expand Up @@ -79,6 +81,23 @@ def approve_proposal_legacy(node, rsp):
return amount



Check failure on line 84 in integration_tests/test_basic.py

View workflow job for this annotation

GitHub Actions / Lint python

./integration_tests/test_basic.py:84:1: BLK100 Black would make changes.
def test_sc(cronos):

Check failure on line 85 in integration_tests/test_basic.py

View workflow job for this annotation

GitHub Actions / Lint python

./integration_tests/test_basic.py:85:1: E303 too many blank lines (3)
w3 = cronos.w3
acc = derive_new_account()
fund_acc(w3, acc)
addr = "0xa16226396d79dc7B3Bc70DE0daCa4Eef11742a9E"
sc = deploy_contract(

Check failure on line 90 in integration_tests/test_basic.py

View workflow job for this annotation

GitHub Actions / integration_tests (unmarked)

test_sc[True] web3.exceptions.ContractLogicError: execution reverted
w3,
CONTRACTS["TokenDistributor"],
(addr, addr, 1, [0], [0], [0], 1, 1),
key=acc.key,
)
owner = sc.functions.owner().call()
# 0xeBF80fF512D5aF394c2F86B39Aa92670d6D3B15f 0x28838c2E6Db87977e0CaE0E218f1929e440d1598

Check failure on line 97 in integration_tests/test_basic.py

View workflow job for this annotation

GitHub Actions / Lint python

./integration_tests/test_basic.py:97:89: E501 line too long (91 > 88 characters)
print("mm-owner", owner, sc.address)


def test_ica_enabled(cronos):
cli = cronos.cosmos_cli()
p = cli.query_icacontroller_params()
Expand Down
15 changes: 15 additions & 0 deletions integration_tests/test_upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
KEYS,
approve_proposal,
deploy_contract,
derive_new_account,
edit_ini_sections,
fund_acc,
get_consensus_params,
get_send_enable,
send_transaction,
Expand Down Expand Up @@ -229,6 +231,17 @@ def do_upgrade(plan_name, target, mode=None, method="submit-legacy-proposal"):
)
print("old values", old_height, old_balance, old_base_fee)

acc = derive_new_account()
fund_acc(w3, acc)
addr = "0xa16226396d79dc7B3Bc70DE0daCa4Eef11742a9E"
sc = deploy_contract(
w3,
CONTRACTS["TokenDistributor"],
(addr, addr, 1, [0], [0], [0], 1, 1),
key=acc.key,
)
owner = sc.functions.owner().call()
print("mm-owner-bf", owner, sc.address)
do_upgrade("v1.3", target_height1)
cli = c.cosmos_cli()

Expand All @@ -244,6 +257,8 @@ def do_upgrade(plan_name, target, mode=None, method="submit-legacy-proposal"):
},
)
assert receipt.status == 1
owner = sc.functions.owner().call()
print("mm-owner-af", owner, sc.address)

# deploy contract should still work
deploy_contract(w3, CONTRACTS["Greeter"])
Expand Down
Loading

0 comments on commit 5e9415e

Please sign in to comment.