Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staking rewards #534

Merged
merged 42 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c6c290d
`IERC20Mintable`, not `IERC20`
feuGeneA Sep 6, 2024
318c277
issue ERC20 staking rewards (native rewards TBD)
feuGeneA Sep 6, 2024
f45cce7
mint(addr,amount): respect _MAX_MINT
feuGeneA Sep 10, 2024
57a519e
incorporate uptime in rewards
feuGeneA Sep 11, 2024
f26ecbe
increment validator rewards, not overwrite them
feuGeneA Sep 11, 2024
fd0aa94
Merge branch 'staking-contract' into staking-rewards
geoff-vball Sep 16, 2024
6ba67b6
Fixup
geoff-vball Sep 16, 2024
56304eb
Fix tests
geoff-vball Sep 16, 2024
08d95b8
Fix rewards calculator
geoff-vball Sep 16, 2024
f2c13d5
Fixups
geoff-vball Sep 16, 2024
267d92e
Fixups
geoff-vball Sep 16, 2024
dabde59
Fixups
geoff-vball Sep 16, 2024
d480b4c
Fixups
geoff-vball Sep 16, 2024
2ecf2bf
lint
geoff-vball Sep 16, 2024
e29827c
Native staking rewards
feuGeneA Sep 11, 2024
29259c5
Merge pull request #552 from ava-labs/gstuart/native-staking-rewards
geoff-vball Sep 17, 2024
44d8097
Updates and fixes
geoff-vball Sep 18, 2024
9019085
Remove unneccessary comment
geoff-vball Sep 18, 2024
2109a68
Add comments
geoff-vball Sep 18, 2024
69242fd
Merge branch 'validation-ends-first' into staking-rewards
geoff-vball Sep 18, 2024
8d2969a
Update contracts/staking/PoSValidatorManager.sol
geoff-vball Sep 18, 2024
b465fba
Update contracts/staking/PoSValidatorManager.sol
geoff-vball Sep 18, 2024
c215b3c
Update contracts/staking/PoSValidatorManager.sol
geoff-vball Sep 18, 2024
d9ae361
fix comment
geoff-vball Sep 18, 2024
9bbfde2
review fixes
geoff-vball Sep 18, 2024
7d96776
Merge branch 'validation-ends-first' into staking-rewards
geoff-vball Sep 18, 2024
bda2341
Merge branch 'validation-ends-first' into staking-rewards
geoff-vball Sep 18, 2024
ebe122e
lint
geoff-vball Sep 18, 2024
8a791c1
Merge branch 'staking-contract' into staking-rewards
geoff-vball Sep 18, 2024
13e210b
Review fixes
geoff-vball Sep 18, 2024
061c125
Review fixes
geoff-vball Sep 18, 2024
19e28b3
Fix unit tests
geoff-vball Sep 18, 2024
d5e5e26
Function for withdrawing delegation fees
geoff-vball Sep 18, 2024
c9b1556
Hook up native minter precompile to PoS tests
geoff-vball Sep 18, 2024
bc7ddc7
wait for success in AddNativeMinterAdmin
iansuvak Sep 18, 2024
9e80079
lint
iansuvak Sep 18, 2024
015d92e
Review fixes
geoff-vball Sep 19, 2024
daad525
Check for owner when initializing validator completion
geoff-vball Sep 19, 2024
3286116
Small fix
geoff-vball Sep 19, 2024
6b1e9c0
Small fix
geoff-vball Sep 19, 2024
db44dab
Emit rewards and fees on delegator exit
geoff-vball Sep 19, 2024
099bf13
add test
geoff-vball Sep 19, 2024
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
634 changes: 634 additions & 0 deletions abi-bindings/go/INativeMinter/INativeMinter.go

Large diffs are not rendered by default.

43 changes: 32 additions & 11 deletions abi-bindings/go/mocks/ExampleERC20/ExampleERC20.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions contracts/mocks/ExampleERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import {
ERC20Burnable,
ERC20
} from "@openzeppelin/contracts@5.0.2/token/ERC20/extensions/ERC20Burnable.sol";
import {IERC20Mintable} from "../staking/interfaces/IERC20Mintable.sol";

contract ExampleERC20 is ERC20Burnable {
contract ExampleERC20 is ERC20Burnable, IERC20Mintable {
string private constant _TOKEN_NAME = "Mock Token";
string private constant _TOKEN_SYMBOL = "EXMP";

uint256 private constant _MAX_MINT = 1e16;
uint256 private constant _MAX_MINT = 1e19;

constructor() ERC20(_TOKEN_NAME, _TOKEN_SYMBOL) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thinking if this example ERC20 should only give access to mint to an ownable address. Even for an example seems like a pretty common check that would exist.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure what you mean

_mint(msg.sender, 1e28);
Expand All @@ -30,4 +31,11 @@ contract ExampleERC20 is ERC20Burnable {

_mint(msg.sender, amount);
}

function mint(address account, uint256 amount) external {
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
// Can only mint 10 at a time.
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
require(amount <= _MAX_MINT, "ExampleERC20: max mint exceeded");

_mint(account, amount);
}
}
24 changes: 16 additions & 8 deletions contracts/staking/ERC20TokenStakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pragma solidity 0.8.25;
import {IERC20TokenStakingManager} from "./interfaces/IERC20TokenStakingManager.sol";
import {Initializable} from
"@openzeppelin/contracts-upgradeable@5.0.2/proxy/utils/Initializable.sol";
import {IERC20} from "@openzeppelin/contracts@5.0.2/token/ERC20/IERC20.sol";
import {IERC20Mintable} from "./interfaces/IERC20Mintable.sol";
import {SafeERC20TransferFrom} from "@utilities/SafeERC20TransferFrom.sol";
import {SafeERC20} from "@openzeppelin/contracts@5.0.2/token/ERC20/utils/SafeERC20.sol";
import {ICMInitializable} from "../utilities/ICMInitializable.sol";
Expand All @@ -21,13 +21,13 @@ contract ERC20TokenStakingManager is
PoSValidatorManager,
IERC20TokenStakingManager
{
using SafeERC20 for IERC20;
using SafeERC20TransferFrom for IERC20;
using SafeERC20 for IERC20Mintable;
using SafeERC20TransferFrom for IERC20Mintable;

// solhint-disable private-vars-leading-underscore
/// @custom:storage-location erc7201:avalanche-icm.storage.ERC20TokenStakingManager
struct ERC20TokenStakingManagerStorage {
IERC20 _token;
IERC20Mintable _token;
uint8 _tokenDecimals;
}
// solhint-enable private-vars-leading-underscore
Expand Down Expand Up @@ -63,22 +63,25 @@ contract ERC20TokenStakingManager is
*/
function initialize(
PoSValidatorManagerSettings calldata settings,
IERC20 token
IERC20Mintable token
) external reinitializer(2) {
__ERC20TokenStakingManager_init(settings, token);
}

// solhint-disable func-name-mixedcase
function __ERC20TokenStakingManager_init(
PoSValidatorManagerSettings calldata settings,
IERC20 token
IERC20Mintable token
) internal onlyInitializing {
__POS_Validator_Manager_init(settings);
__ERC20TokenStakingManager_init_unchained(token);
}

// solhint-disable func-name-mixedcase
function __ERC20TokenStakingManager_init_unchained(IERC20 token) internal onlyInitializing {
function __ERC20TokenStakingManager_init_unchained(IERC20Mintable token)
internal
onlyInitializing
{
ERC20TokenStakingManagerStorage storage $ = _getERC20StakingManagerStorage();
require(address(token) != address(0), "ERC20TokenStakingManager: zero token address");
$._token = token;
Expand Down Expand Up @@ -116,7 +119,12 @@ contract ERC20TokenStakingManager is
return _getERC20StakingManagerStorage()._token.safeTransferFrom(value);
}

function _unlock(uint256 value, address to) internal virtual override {
function _unlock(address to, uint256 value) internal virtual override {
_getERC20StakingManagerStorage()._token.safeTransfer(to, value);
}

function _reward(address account, uint256 amount) internal virtual override {
ERC20TokenStakingManagerStorage storage $ = _getERC20StakingManagerStorage();
$._token.mint(account, amount);
}
}
20 changes: 17 additions & 3 deletions contracts/staking/ExampleRewardCalculator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {IRewardCalculator} from "./interfaces/IRewardCalculator.sol";
contract ExampleRewardCalculator is IRewardCalculator {
uint256 public constant SECONDS_IN_YEAR = 31536000;

uint8 public constant UPTIME_REWARDS_THRESHOLD_PERCENTAGE = 80;

uint64 public immutable rewardBasisPoints;

constructor(uint64 rewardBasisPoints_) {
Expand All @@ -21,11 +23,23 @@ contract ExampleRewardCalculator is IRewardCalculator {
*/
function calculateReward(
uint256 stakeAmount,
uint64 startTime,
uint64 endTime,
uint64 validatorStartTime,
uint64 stakingStartTime,
uint64 stakingEndTime,
uint64 uptimeSeconds,
uint256, // initialSupply
uint256 // endSupply
) external view returns (uint256) {
return (stakeAmount * rewardBasisPoints * (endTime - startTime)) / SECONDS_IN_YEAR / 1000;
// Equivalent to uptimeSeconds/(validator.endedAt - validator.startedAt) < UPTIME_REWARDS_THRESHOLD_PERCENTAGE/100
// Rearranged to prevent integer division truncation.
if (
uptimeSeconds * 100
< (stakingEndTime - validatorStartTime) * UPTIME_REWARDS_THRESHOLD_PERCENTAGE
) {
return 0;
}

return (stakeAmount * rewardBasisPoints * (stakingEndTime - stakingStartTime))
/ SECONDS_IN_YEAR / 10000;
}
}
11 changes: 10 additions & 1 deletion contracts/staking/NativeTokenStakingManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
pragma solidity 0.8.25;

import {INativeTokenStakingManager} from "./interfaces/INativeTokenStakingManager.sol";
import {INativeMinter} from
"@avalabs/subnet-evm-contracts@1.2.0/contracts/interfaces/INativeMinter.sol";
import {Address} from "@openzeppelin/contracts@5.0.2/utils/Address.sol";
import {Initializable} from
"@openzeppelin/contracts-upgradeable@5.0.2/proxy/utils/Initializable.sol";
Expand All @@ -21,6 +23,9 @@ contract NativeTokenStakingManager is
{
using Address for address payable;

INativeMinter public constant NATIVE_MINTER =
INativeMinter(0x0200000000000000000000000000000000000001);

constructor(ICMInitializable init) {
if (init == ICMInitializable.Disallowed) {
_disableInitializers();
Expand Down Expand Up @@ -80,7 +85,11 @@ contract NativeTokenStakingManager is
return value;
}

function _unlock(uint256 value, address to) internal virtual override {
function _unlock(address to, uint256 value) internal virtual override {
payable(to).sendValue(value);
}

function _reward(address account, uint256 amount) internal virtual override {
NATIVE_MINTER.mintNativeCoin(account, amount);
}
}
Loading