Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion bindings/bin/distribute_deployed.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bindings/bin/l1staking_deployed.hex

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bindings/bindings/distribute.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bindings/bindings/distribute_more.go

Large diffs are not rendered by default.

160 changes: 158 additions & 2 deletions bindings/bindings/l1staking.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bindings/bindings/l1staking_more.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions contracts/contracts/l1/staking/IL1Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ interface IL1Staking {
/// @param remove addresses removed
event WhitelistUpdated(address[] add, address[] remove);

/// @notice staking value updated
/// @param oldStakingValue old staking value
/// @param newStakingValue new staking value
event StakingValueUpdated(uint256 oldStakingValue, uint256 newStakingValue);

/// @notice gas limit add staker updated
/// @param oldGasLimit old gas limit
/// @param newGasLimit new gas limit
Expand Down
11 changes: 10 additions & 1 deletion contracts/contracts/l1/staking/L1Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ contract L1Staking is IL1Staking, Staking, OwnableUpgradeable, ReentrancyGuardUp
/// @notice rollup Contract
address public rollupContract;

/// @notice staking value, immutable
/// @notice staking value
uint256 public stakingValue;

/// @notice exit lock blocks
Expand Down Expand Up @@ -254,6 +254,15 @@ contract L1Staking is IL1Staking, Staking, OwnableUpgradeable, ReentrancyGuardUp
emit SlashRemainingClaimed(receiver, _slashRemaining);
}

/// @notice update staking value
/// @param _stakingValue staking value
function updateStakingValue(uint256 _stakingValue) external onlyOwner {
require(_stakingValue > 0 && _stakingValue != stakingValue, "invalid staking value");
uint256 _oldStakingValue = stakingValue;
stakingValue = _stakingValue;
emit StakingValueUpdated(_oldStakingValue, stakingValue);
}

/// @notice update gas limit of add staker
/// @param _gasLimitAdd cross-chain gas limit add staker
function updateGasLimitAddStaker(uint256 _gasLimitAdd) external onlyOwner {
Expand Down
5 changes: 3 additions & 2 deletions contracts/contracts/l2/staking/Distribute.sol
Original file line number Diff line number Diff line change
Expand Up @@ -272,17 +272,18 @@ contract Distribute is IDistribute, OwnableUpgradeable {
function queryUnclaimed(address delegatee, address delegator) external view returns (uint256 reward) {
require(unclaimed[delegator].delegatees.length() != 0, "invalid delegator or no remaining reward");
require(unclaimed[delegator].delegatees.contains(delegatee), "no remaining reward of the delegatee");

uint256 delegateeAmount;
uint256 delegatorAmount;
uint256 start = unclaimed[delegator].unclaimedStart[delegatee];
for (uint256 i = start; i < mintedEpochCount; i++) {
for (uint256 i = unclaimed[delegator].unclaimedStart[delegatee]; i < mintedEpochCount; i++) {
if (distributions[delegatee][i].amounts[delegator] > 0) {
delegatorAmount = distributions[delegatee][i].amounts[delegator];
}
if (distributions[delegatee][i].delegationAmount > 0) {
delegateeAmount = distributions[delegatee][i].delegationAmount;
}
reward += (distributions[delegatee][i].delegatorRewardAmount * delegatorAmount) / delegateeAmount;

if (unclaimed[delegator].undelegated[delegatee] && unclaimed[delegator].unclaimedEnd[delegatee] == i) {
break;
}
Expand Down
200 changes: 199 additions & 1 deletion contracts/contracts/test/L2Staking.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {ICrossDomainMessenger} from "../libraries/ICrossDomainMessenger.sol";
import {IDistribute} from "../l2/staking/IDistribute.sol";
import {console} from "forge-std/Test.sol";

// import "forge-std/console.sol";
import "forge-std/console.sol";

contract L2StakingTest is L2StakingBaseTest {
uint256 public morphBalance = 20 ether;
Expand Down Expand Up @@ -1470,6 +1470,204 @@ contract L2StakingTest is L2StakingBaseTest {
hevm.stopPrank();
}

/**
* @notice staking -> distribute -> claim -> undelegate -> distribute -> claim
*/
function test_delegatorClaimAllAfterUndelegate_succeeds() public {
hevm.startPrank(alice);
morphToken.approve(address(l2Staking), type(uint256).max);
l2Staking.delegateStake(firstStaker, 5 ether);
l2Staking.delegateStake(secondStaker, 5 ether);
l2Staking.delegateStake(thirdStaker, 5 ether);
hevm.stopPrank();

uint256 time = REWARD_EPOCH;
hevm.warp(time);

// reward starting
// rewardStartTime = 86400
// block.timeStamp >= rewardStartTime
// candidateNumber > 0
hevm.prank(multisig);
l2Staking.startReward();

// staker set commission
hevm.prank(firstStaker);
l2Staking.setCommissionRate(1);
hevm.prank(secondStaker);
l2Staking.setCommissionRate(1);
hevm.prank(thirdStaker);
l2Staking.setCommissionRate(1);

// *************** epoch = 1 ******************** //
time = REWARD_EPOCH * 2;
hevm.warp(time);

uint256 blocksCountOfEpoch = REWARD_EPOCH / 3;
hevm.roll(blocksCountOfEpoch * 2);
hevm.prank(oracleAddress);
record.setLatestRewardEpochBlock(blocksCountOfEpoch);
_updateDistribute(0);

// effectiveEpoch = 2
hevm.startPrank(bob);
morphToken.approve(address(l2Staking), type(uint256).max);
l2Staking.delegateStake(secondStaker, morphBalance - 5 ether);
hevm.stopPrank();

// ranking changed by delegate amount
uint256 secondRanking = l2Staking.stakerRankings(secondStaker);
assertEq(secondRanking, 0 + 1);

// *********************************** //
time = REWARD_EPOCH * (3);
hevm.roll(blocksCountOfEpoch * (3));
hevm.warp(time);
_updateDistribute(1);
// console.logUint(morphToken.inflationMintedEpochs());
// *********************************** //

uint256 aliceReward1 = distribute.queryUnclaimed(firstStaker, alice);
uint256 aliceReward2 = distribute.queryUnclaimed(secondStaker, alice);
uint256 aliceReward3 = distribute.queryUnclaimed(thirdStaker, alice);
hevm.startPrank(alice);
uint256 balanceBefore = morphToken.balanceOf(alice);
// console.logString("......................");
// console.logUint(balanceBefore);
// console.logUint(aliceReward1);
// console.logUint(aliceReward2);
// console.logUint(aliceReward3);

l2Staking.claimReward(address(0), 0);
hevm.expectRevert("all reward claimed");
l2Staking.claimReward(firstStaker, 0);
hevm.expectRevert("all reward claimed");
l2Staking.claimReward(secondStaker, 0);
hevm.expectRevert("all reward claimed");
l2Staking.claimReward(thirdStaker, 0);
uint256 balanceAfter = morphToken.balanceOf(alice);

// console.logUint(balanceAfter);
// console.logString("......................");
assertEq(balanceAfter, balanceBefore + aliceReward1 + aliceReward2 + aliceReward3);
hevm.stopPrank();

// *********************************** //
time = REWARD_EPOCH * (4);
hevm.roll(blocksCountOfEpoch * (4));
hevm.warp(time);
_updateDistribute(2);
// console.logUint(morphToken.inflationMintedEpochs());
// *********************************** //

hevm.startPrank(alice);
l2Staking.undelegateStake(firstStaker);
l2Staking.undelegateStake(secondStaker);
l2Staking.undelegateStake(thirdStaker);
hevm.stopPrank();

// console.logString("......................");
// console.logString("......................");
aliceReward1 = distribute.queryUnclaimed(firstStaker, alice);
aliceReward2 = distribute.queryUnclaimed(secondStaker, alice);
aliceReward3 = distribute.queryUnclaimed(thirdStaker, alice);
// console.logUint(aliceReward1);
// console.logUint(aliceReward2);
// console.logUint(aliceReward3);
// console.logString("......................");
// console.logString("......................");

// *********************************** //
time = REWARD_EPOCH * (5);
hevm.roll(blocksCountOfEpoch * (5));
hevm.warp(time);
_updateDistribute(3);
// console.logUint(morphToken.inflationMintedEpochs());
// *********************************** //

// *********************************** //
time = REWARD_EPOCH * (6);
hevm.roll(blocksCountOfEpoch * (6));
hevm.warp(time);
_updateDistribute(4);
// console.logUint(morphToken.inflationMintedEpochs());
// *********************************** //

// *********************************** //
time = REWARD_EPOCH * (7);
hevm.roll(blocksCountOfEpoch * (7));
hevm.warp(time);
_updateDistribute(5);
// console.logUint(morphToken.inflationMintedEpochs());
// *********************************** //

// *********************************** //
time = REWARD_EPOCH * (8);
hevm.roll(blocksCountOfEpoch * (8));
hevm.warp(time);
_updateDistribute(6);
// console.logUint(morphToken.inflationMintedEpochs());
// *********************************** //

uint256 aliceRewardNew1 = distribute.queryUnclaimed(firstStaker, alice);
uint256 aliceRewardNew2 = distribute.queryUnclaimed(secondStaker, alice);
uint256 aliceRewardNew3 = distribute.queryUnclaimed(thirdStaker, alice);

hevm.startPrank(alice);
// console.logString("......................");
balanceBefore = morphToken.balanceOf(alice);
uint256 balanceBeforeTmp = balanceBefore;
// console.logUint(balanceBefore);
// console.logString("......................");

// l2Staking.claimReward(address(0), 0);

// console.logString("......................");

// console.logUint(aliceRewardNew1);
balanceBeforeTmp = morphToken.balanceOf(alice);
l2Staking.claimReward(firstStaker, 0);
hevm.expectRevert("no remaining reward");
l2Staking.claimReward(firstStaker, 0);
balanceAfter = morphToken.balanceOf(alice);
// console.logUint(balanceAfter);
assertEq(balanceAfter, balanceBeforeTmp + aliceRewardNew1);

// console.logString("......................");

// console.logUint(aliceRewardNew2);
balanceBeforeTmp = morphToken.balanceOf(alice);
l2Staking.claimReward(secondStaker, 0);
hevm.expectRevert("no remaining reward");
l2Staking.claimReward(secondStaker, 0);
balanceAfter = morphToken.balanceOf(alice);
// console.logUint(balanceAfter);
assertEq(balanceAfter, balanceBeforeTmp + aliceRewardNew2);

// console.logString("......................");

// console.logUint(aliceRewardNew3);
balanceBeforeTmp = morphToken.balanceOf(alice);
l2Staking.claimReward(thirdStaker, 0);
hevm.expectRevert("no remaining reward");
l2Staking.claimReward(thirdStaker, 0);
balanceAfter = morphToken.balanceOf(alice);
// console.logUint(balanceAfter);
assertEq(balanceAfter, balanceBeforeTmp + aliceRewardNew3);

// console.logString("......................");

assertEq(balanceAfter, balanceBefore + aliceRewardNew1 + aliceRewardNew2 + aliceRewardNew3);

hevm.expectRevert("invalid delegator or no remaining reward");
distribute.queryUnclaimed(firstStaker, alice);
hevm.expectRevert("invalid delegator or no remaining reward");
distribute.queryUnclaimed(secondStaker, alice);
hevm.expectRevert("invalid delegator or no remaining reward");
distribute.queryUnclaimed(thirdStaker, alice);
hevm.stopPrank();
}

/**
* @notice staking -> distribute -> claim
*/
Expand Down
3 changes: 2 additions & 1 deletion contracts/contracts/test/base/CommonTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ contract CommonTest is DSTestPlus, MockTree {
address public bob3 = address(259);
address public bob4 = address(260);
address public bob5 = address(261);

address public multisig = address(512);

FFIInterface public ffi;
Expand All @@ -41,6 +41,7 @@ contract CommonTest is DSTestPlus, MockTree {

uint256 public finalizationPeriodSeconds = 2;
uint256 public proofRewardPercent = 70;

function setUp() public virtual {
// Give alice and bob some ETH
hevm.deal(alice, 1 << 16);
Expand Down