Skip to content

Commit

Permalink
issue ERC20 staking rewards (native rewards TBD)
Browse files Browse the repository at this point in the history
  • Loading branch information
feuGeneA committed Sep 9, 2024
1 parent 2e71612 commit 19d26be
Show file tree
Hide file tree
Showing 16 changed files with 397 additions and 1,212 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions contracts/staking/ERC20TokenStakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,9 @@ contract ERC20TokenStakingManager is
function _unlock(uint256 value, address to) internal virtual override {
_getERC20StakingManagerStorage()._token.safeTransfer(to, value);
}

function _reward(address account, uint256 amount) internal virtual override {
ERC20TokenStakingManagerStorage storage $ = _getERC20StakingManagerStorage();
$._token.mint(account, amount);
}
}
5 changes: 5 additions & 0 deletions contracts/staking/NativeTokenStakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,9 @@ contract NativeTokenStakingManager is
function _unlock(uint256 value, address to) internal virtual override {
payable(to).sendValue(value);
}

// solhint-disable-next-line no-empty-blocks
function _reward(address account, uint256 amount) internal virtual override {
// TODO: call the native minter precompile to mint `amount` for `account`
}
}
10 changes: 10 additions & 0 deletions contracts/staking/PoSValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ abstract contract PoSValidatorManager is IPoSValidatorManager, ValidatorManager

function completeEndValidation(uint32 messageIndex) external {
Validator memory validator = _completeEndValidation(messageIndex);

PoSValidatorManagerStorage storage $ = _getPoSValidatorManagerStorage();
uint256 rewardAmount = $._rewardCalculator.calculateReward(
validator.weight, validator.startedAt, validator.endedAt, 0, 0
);

_reward(validator.owner, rewardAmount);

_unlock(validator.startingWeight, validator.owner);
}

Expand Down Expand Up @@ -376,4 +384,6 @@ abstract contract PoSValidatorManager is IPoSValidatorManager, ValidatorManager
"PoSValidatorManager: delegation registration not pending"
);
}

function _reward(address account, uint256 amount) internal virtual;
}
393 changes: 0 additions & 393 deletions contracts/staking/PoSValidatorManager.sol.orig

This file was deleted.

24 changes: 19 additions & 5 deletions contracts/staking/tests/ERC20TokenStakingManagerTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {PoSValidatorManagerTest} from "./PoSValidatorManagerTests.t.sol";
import {ERC20TokenStakingManager} from "../ERC20TokenStakingManager.sol";
import {ValidatorManagerSettings} from "../interfaces/IValidatorManager.sol";
import {PoSValidatorManagerSettings} from "../interfaces/IPoSValidatorManager.sol";
import {IRewardCalculator} from "../interfaces/IRewardCalculator.sol";
import {ExampleRewardCalculator} from "../ExampleRewardCalculator.sol";
import {ICMInitializable} from "../../utilities/ICMInitializable.sol";
import {ExampleERC20} from "@mocks/ExampleERC20.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
Expand All @@ -23,11 +23,13 @@ contract ERC20TokenStakingManagerTest is PoSValidatorManagerTest {

ERC20TokenStakingManager public app;
IERC20Mintable public token;
ExampleRewardCalculator public rewardCalculator;

function setUp() public virtual {
// Construct the object under test
app = new ERC20TokenStakingManager(ICMInitializable.Allowed);
token = new ExampleERC20();
rewardCalculator = new ExampleRewardCalculator(DEFAULT_REWARD_RATE);
app.initialize(
PoSValidatorManagerSettings({
baseSettings: ValidatorManagerSettings({
Expand All @@ -38,7 +40,7 @@ contract ERC20TokenStakingManagerTest is PoSValidatorManagerTest {
minimumStakeAmount: DEFAULT_MINIMUM_STAKE,
maximumStakeAmount: DEFAULT_MAXIMUM_STAKE,
minimumStakeDuration: DEFAULT_MINIMUM_STAKE_DURATION,
rewardCalculator: IRewardCalculator(address(0))
rewardCalculator: rewardCalculator
}),
token
);
Expand All @@ -47,26 +49,38 @@ contract ERC20TokenStakingManagerTest is PoSValidatorManagerTest {
}

function testCompleteEndValidation() public override {
uint256 registrationDuration = 1000 * 60 * 60 * 24; // 1 day

uint256 registrationExpiry = vm.getBlockTimestamp() + registrationDuration;

bytes32 validationID = _setUpInitializeEndValidation({
nodeID: DEFAULT_NODE_ID,
subnetID: DEFAULT_SUBNET_ID,
weight: DEFAULT_WEIGHT,
registrationExpiry: DEFAULT_EXPIRY,
registrationExpiry: uint64(registrationExpiry),
blsPublicKey: DEFAULT_BLS_PUBLIC_KEY,
registrationTimestamp: DEFAULT_REGISTRATION_TIMESTAMP,
completionTimestamp: DEFAULT_COMPLETION_TIMESTAMP
completionTimestamp: uint64(registrationExpiry)
});

address validator = address(this);

uint256 balanceBefore = token.balanceOf(validator);

uint256 expectedReward = DEFAULT_WEIGHT * DEFAULT_REWARD_RATE / 365 - 1;

vm.expectCall(address(token), abi.encodeCall(IERC20.transfer, (validator, DEFAULT_WEIGHT)));
vm.expectCall(
address(token), abi.encodeCall(IERC20Mintable.mint, (validator, expectedReward))
);

_testCompleteEndValidation(validationID);

uint256 balanceChange = token.balanceOf(validator) - balanceBefore;
require(balanceChange == DEFAULT_WEIGHT, "validator should have received their stake back");
require(
balanceChange == DEFAULT_WEIGHT + expectedReward,
"validator should have received their stake back and gotten rewarded"
);
}

function _initializeValidatorRegistration(
Expand Down
113 changes: 0 additions & 113 deletions contracts/staking/tests/ERC20TokenStakingManagerTests.t.sol.orig

This file was deleted.

8 changes: 6 additions & 2 deletions contracts/staking/tests/NativeTokenStakingManagerTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {NativeTokenStakingManager} from "../NativeTokenStakingManager.sol";
import {ValidatorManagerSettings} from "../interfaces/IValidatorManager.sol";
import {PoSValidatorManagerSettings} from "../interfaces/IPoSValidatorManager.sol";
import {IRewardCalculator} from "../interfaces/IRewardCalculator.sol";
import {ExampleRewardCalculator} from "../ExampleRewardCalculator.sol";
import {ICMInitializable} from "../../utilities/ICMInitializable.sol";

// TODO: Remove this once all unit tests implemented
Expand All @@ -30,7 +31,7 @@ contract NativeTokenStakingManagerTest is PoSValidatorManagerTest {
minimumStakeAmount: DEFAULT_MINIMUM_STAKE,
maximumStakeAmount: DEFAULT_MAXIMUM_STAKE,
minimumStakeDuration: DEFAULT_MINIMUM_STAKE_DURATION,
rewardCalculator: IRewardCalculator(address(0))
rewardCalculator: IRewardCalculator(new ExampleRewardCalculator(DEFAULT_REWARD_RATE))
})
);
validatorManager = app;
Expand All @@ -56,7 +57,10 @@ contract NativeTokenStakingManagerTest is PoSValidatorManagerTest {
_testCompleteEndValidation(validationID);

uint256 balanceChange = address(this).balance - balanceBefore;
require(balanceChange == DEFAULT_WEIGHT, "validator should have received their stake back");
require(
balanceChange >= DEFAULT_WEIGHT, // >= so we account for rewards
"validator should have received their stake back"
);
}

// Helpers
Expand Down
1 change: 1 addition & 0 deletions contracts/staking/tests/PoSValidatorManagerTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ abstract contract PoSValidatorManagerTest is ValidatorManagerTest {
uint64 public constant DEFAULT_DELEGATOR_END_DELEGATION_TIMESTAMP = uint64(4000);
address public constant DEFAULT_DELEGATOR_ADDRESS =
address(0x1234123412341234123412341234123412341234);
uint64 public constant DEFAULT_REWARD_RATE = uint64(10);

PoSValidatorManager public posValidatorManager;

Expand Down
Loading

0 comments on commit 19d26be

Please sign in to comment.