Skip to content

Commit

Permalink
hub tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hujw77 committed Jun 13, 2024
1 parent 588e711 commit 4390e8f
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 32 deletions.
27 changes: 11 additions & 16 deletions src/collator/CollatorStakingHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ pragma solidity 0.8.20;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "./interfaces/ICollatorStakingPool.sol";
import "./interfaces/INominationPool.sol";
import "./interfaces/IGRING.sol";
import "../deposit/interfaces/IDeposit.sol";
import "./NominationPool.sol";
Expand All @@ -27,7 +26,7 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {

event Staked(address indexed pool, address collator, address account, uint256 assets);
event Unstaked(address indexed pool, address collator, address account, uint256 assets);
event CollatorStakingPoolCreated(address indexed stRING, address collator, address prev);
event NominationPoolCreated(address indexed stRING, address collator, address prev);
event CommissionUpdated(address indexed collator, uint256 commission);

modifier onlySystem() {
Expand All @@ -47,11 +46,7 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
_disableInitializers();
}

function exist(address pool) public view returns (bool) {
return collatorOf[pool] != address(0);
}

function createCollatorStakingPool(address prev, uint256 commission) public returns (address pool) {
function createNominationPool(address prev, uint256 commission) public returns (address pool) {
address collator = msg.sender;
require(poolOf[collator] == address(0), "created");

Expand All @@ -62,20 +57,18 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
pool := create2(0, add(initCode, 32), mload(initCode), 0)
}
require(pool != address(0), "!create2");
require(collatorOf[pool] == address(0), "duplicate");

poolOf[collator] = pool;
collatorOf[pool] = collator;
_addCollator(collator, 0, prev);
_collect(collator, commission);
emit CollatorStakingPoolCreated(pool, collator, prev);
emit NominationPoolCreated(pool, collator, prev);
}

function _stake(address collator, address account, uint256 assets) internal {
stakingLocks[collator][account] = LOCK_PERIOD + block.timestamp;
address pool = poolOf[collator];
require(pool != address(0), "!collator");
ICollatorStakingPool(pool).stake(account, assets);
INominationPool(pool).stake(account, assets);
IGRING(gRING).mint(account, assets);
emit Staked(pool, collator, account, assets);
}
Expand All @@ -85,14 +78,14 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
address pool = poolOf[collator];
require(pool != address(0), "!collator");
IGRING(gRING).burn(account, assets);
ICollatorStakingPool(pool).withdraw(account, assets);
INominationPool(pool).withdraw(account, assets);
emit Unstaked(pool, collator, account, assets);
}

function claim(address collator) public nonReentrant {
address pool = poolOf[collator];
require(pool != address(0), "!collator");
ICollatorStakingPool(pool).getReward(msg.sender);
INominationPool(pool).getReward(msg.sender);
}

function stakeRING(address collator, address oldPrev, address newPrev) public payable nonReentrant {
Expand Down Expand Up @@ -149,11 +142,13 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
uint256 rewards = msg.value;
uint256 commission_ = rewards * commissionOf[collator] / COMMISSION_BASE;
payable(collator).sendValue(commission_);
ICollatorStakingPool(pool).notifyRewardAmount{value: rewards - commission_}();
INominationPool(pool).notifyRewardAmount{value: rewards - commission_}();
}

function stakedOf(address collator) public view returns (uint256) {
return IERC20(collator).totalSupply();
address pool = poolOf[collator];
require(pool != address(0), "!collator");
return INominationPool(pool).totalSupply();
}

function _collect(address collator, uint256 commission) internal {
Expand Down
2 changes: 0 additions & 2 deletions src/collator/CollatorStakingHubStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ contract CollatorStakingHubStorage {
address public gRING;
// Deposit NFT.
address public DEPOSIT;
// CollatorStakingPool symbol subfix
string public SYMBOL;
// collator => stakingPool
mapping(address => address) public poolOf;
// stakingPool => collator
Expand Down
4 changes: 2 additions & 2 deletions src/collator/interfaces/IGRING.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ pragma solidity >=0.4.24;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IGRING is IERC20 {
function burn(address from, uint256 amount) external returns (bool);
function mint(address to, uint256 amount) external returns (bool);
function burn(address from, uint256 amount) external;
function mint(address to, uint256 amount) external;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity >=0.4.24;

interface ICollatorStakingPool {
interface INominationPool {
// Views
function operator() external view returns (address);

Expand Down
1 change: 1 addition & 0 deletions src/deposit/Deposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ contract Deposit is
depositOf[id] = DepositInfo({months: months, startAt: uint64(block.timestamp), value: uint128(value)});

uint256 interest = computeInterest(value, months);
require(interest > 0, "!interest");
require(KTON.mint(account, interest), "!mint");
_safeMint(account, id);

Expand Down
8 changes: 4 additions & 4 deletions test/collator/CollatorSet.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ contract CollatorSetTest is Test, CollatorSet {
__CollatorSet_init();
}

function invariant_check() public view {
assertTrue(collators[HEAD] != address(0));
assertTrue(collators[TAIL] == address(0));
}
// function invariant_check() public view {
// assertTrue(collators[HEAD] != address(0));
// assertTrue(collators[TAIL] == address(0));
// }

function test_init() public view {
assertEq(collators[HEAD], TAIL);
Expand Down
180 changes: 173 additions & 7 deletions test/collator/CollatorStakingHub.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,189 @@ import "forge-std/Test.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import "../deposit/Deposit.t.sol";
import "../../src/collator/CollatorStakingHub.sol";
import "../../src/deposit/Deposit.sol";

contract CollatorStakingHubTest is Test {
address hub;
address gring;
address deposit;
CollatorStakingHub hub;

address HEAD = address(0x1);
address TAIL = address(0x2);
address alith = address(0x0a);
address baltathar = address(0x0b);
address charleth = address(0xc);
address alice = address(0xaa);
address bob = address(0xbb);

uint256 REWARDS_DURATION = 7 days;

function setUp() public {
address gring = address(new GRINGMock());
address deposit = Upgrades.deployTransparentProxy(
gring = address(new GRINGMock());
deposit = Upgrades.deployTransparentProxy(
"Deposit.sol:Deposit", msg.sender, abi.encodeCall(Deposit.initialize, ("RING Deposit", "RDPS"))
);
hub = Upgrades.deployTransparentProxy(
"CollatorStakingHub.sol:CollatorStakingHub",
msg.sender,
abi.encodeCall(CollatorStakingHub.initialize, (gring, deposit))

vm.etch(0x0000000000000000000000000000000000000402, type(KTONMock).runtimeCode);
hub = CollatorStakingHub(
Upgrades.deployTransparentProxy(
"CollatorStakingHub.sol:CollatorStakingHub",
msg.sender,
abi.encodeCall(CollatorStakingHub.initialize, (gring, deposit))
)
);
}

function test_initialize() public view {
assertEq(gring, hub.gRING());
assertEq(deposit, hub.DEPOSIT());
}

function test_createNominationPool() public {
vm.prank(alith);
address a = hub.createNominationPool(HEAD, 1);
assertEq(NominationPool(a).id(), 0);
assertEq(NominationPool(a).hub(), address(hub));
assertEq(NominationPool(a).collator(), alith);
vm.prank(baltathar);
address b = hub.createNominationPool(HEAD, 2);
assertEq(NominationPool(b).id(), 1);
assertEq(NominationPool(b).hub(), address(hub));
assertEq(NominationPool(b).collator(), baltathar);
vm.prank(charleth);
address c = hub.createNominationPool(baltathar, 3);
assertEq(NominationPool(c).id(), 2);
assertEq(NominationPool(c).hub(), address(hub));
assertEq(NominationPool(c).collator(), charleth);
assertEq(hub.collators(HEAD), baltathar);
assertEq(hub.collators(baltathar), charleth);
assertEq(hub.collators(charleth), alith);
assertEq(hub.collators(alith), TAIL);
assertEq(hub.stakedOf(alith), 0);
assertEq(hub.stakedOf(baltathar), 0);
assertEq(hub.stakedOf(charleth), 0);
}

function test_stakeRING() public {
uint256 stake = 1 ether;
vm.prank(alith);
uint256 commissoin = 1;
address a = hub.createNominationPool(HEAD, commissoin);
vm.deal(alice, stake);
vm.prank(alice);
hub.stakeRING{value: stake}(alith, HEAD, HEAD);
assertEq(hub.stakedRINGOf(alice), stake);
assertEq(NominationPool(a).balanceOf(alice), stake);
assertEq(hub.votesOf(alith), stake * (100 - commissoin) / 100);
assertEq(hub.stakedOf(alith), stake);
}

function test_claim() public {
uint256 stake = 1 ether;
uint256 commissoin = 1;
vm.prank(alith);
address a = hub.createNominationPool(HEAD, commissoin);

vm.deal(alice, stake);
vm.prank(alice);
hub.stakeRING{value: stake}(alith, HEAD, HEAD);

uint256 reward = 100 ether;
vm.deal(hub.STAKING_PALLET(), reward);
vm.prank(hub.STAKING_PALLET());
hub.distributeReward{value: reward}(alith);

vm.warp(NominationPool(a).periodFinish() + 1);

vm.prank(alice);
hub.claim(alith);
assertEq(alice.balance, reward * (100 - commissoin) / 100 / REWARDS_DURATION * REWARDS_DURATION);
assertEq(alith.balance, reward * commissoin / 100);
}

function test_unstakeRING() public {
uint256 stake = 1 ether;
uint256 commissoin = 1;
vm.prank(alith);
address a = hub.createNominationPool(HEAD, commissoin);

vm.deal(alice, stake);
vm.prank(alice);
hub.stakeRING{value: stake}(alith, HEAD, HEAD);
assertEq(hub.stakingLocks(alith, alice), hub.LOCK_PERIOD() + block.timestamp);

vm.warp(block.timestamp + hub.LOCK_PERIOD() + 1);

vm.prank(alice);
hub.unstakeRING(alith, stake, HEAD, HEAD);
assertEq(alice.balance, stake);
assertEq(hub.stakedOf(alith), 0);
}

function test_stakeNFT() public {
uint256 stake = 1 ether;
vm.deal(alice, stake);
vm.prank(alice);
uint256 id = Deposit(deposit).deposit{value: stake}(1);

uint256 commissoin = 1;
vm.prank(alith);
address a = hub.createNominationPool(HEAD, commissoin);

vm.prank(alice);
Deposit(deposit).approve(address(hub), id);
vm.prank(alice);
hub.stakeNFT(alith, id, HEAD, HEAD);
assertEq(hub.stakingLocks(alith, alice), hub.LOCK_PERIOD() + block.timestamp);
(address account, uint256 assets, address collator) = hub.depositInfos(id);
assertEq(account, alice);
assertEq(assets, stake);
assertEq(collator, alith);
assertEq(hub.stakedDepositsLength(alice), 1);
uint256[] memory deposits = hub.stakedDepositsOf(alice);
assertEq(deposits.length, 1);
assertEq(deposits[0], id);
assertEq(hub.stakedDepositsAt(alice, 0), id);
assertTrue(hub.stakedDepositsContains(alice, id));
assertEq(NominationPool(a).balanceOf(alice), stake);
assertEq(hub.votesOf(alith), stake * (100 - commissoin) / 100);
assertEq(hub.stakedOf(alith), stake);
}

function test_unstakeNFT() public {
uint256 stake = 1 ether;
vm.deal(alice, stake);
vm.prank(alice);
uint256 id = Deposit(deposit).deposit{value: stake}(1);

uint256 commissoin = 1;
vm.prank(alith);
address a = hub.createNominationPool(HEAD, commissoin);

vm.prank(alice);
Deposit(deposit).approve(address(hub), id);
vm.prank(alice);
hub.stakeNFT(alith, id, HEAD, HEAD);

vm.warp(block.timestamp + hub.LOCK_PERIOD() + 1);

vm.prank(alice);
hub.unstakeNFT(id, HEAD, HEAD);

(address account, uint256 assets, address collator) = hub.depositInfos(id);
assertEq(account, address(0));
assertEq(assets, 0);
assertEq(collator, address(0));
assertEq(hub.stakedDepositsLength(alice), 0);
uint256[] memory deposits = hub.stakedDepositsOf(alice);
assertEq(deposits.length, 0);
assertTrue(!hub.stakedDepositsContains(alice, id));
assertEq(NominationPool(a).balanceOf(alice), 0);
assertEq(hub.votesOf(alith), 0);
assertEq(hub.stakedOf(alith), 0);
}
}

contract GRINGMock is ERC20 {
Expand Down
3 changes: 3 additions & 0 deletions test/collator/NominationPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ contract NominationPoolTest is Test {

function test_constructor() public view {
assertEq(pool.totalSupply(), 0);
assertEq(pool.id(), 0);
assertEq(pool.hub(), self);
assertEq(pool.collator(), self);
}

function test_notifyRewardAmount_full() public {
Expand Down

0 comments on commit 4390e8f

Please sign in to comment.