Skip to content

Commit

Permalink
Merge branch 'master' into reserved-stakes
Browse files Browse the repository at this point in the history
  • Loading branch information
darcys22 authored Sep 17, 2024
2 parents 90329e0 + 9c4c819 commit 6d63ea4
Show file tree
Hide file tree
Showing 34 changed files with 1,368 additions and 522 deletions.
518 changes: 506 additions & 12 deletions .openzeppelin/unknown-421614.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/RewardRatePool.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
Expand Down
2 changes: 1 addition & 1 deletion contracts/SENT.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
Expand Down
46 changes: 45 additions & 1 deletion contracts/ServiceNodeContribution.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "./libraries/Shared.sol";
import "./interfaces/IServiceNodeRewards.sol";
Expand Down Expand Up @@ -200,13 +200,16 @@ contract ServiceNodeContribution is Shared {

// NOTE: Check contract status and collateral
require(totalContribution() + totalReservedContribution() + amount <= stakingRequirement, "Contribution exceeds the funding goal.");

require(!finalized, "Node has already been finalized.");
require(!cancelled, "Node has been cancelled.");

// NOTE: Add the contributor to the contract
if (contributions[msg.sender] == 0) {
require(amount >= minimumContribution(), "Contribution is below the minimum requirement.");
contributorAddresses.push(msg.sender);
}
// else this is an existing contributor topping up an already-valid contribution

// NOTE: Update the amount contributed and transfer the tokens
contributions[msg.sender] += amount;
Expand Down Expand Up @@ -314,6 +317,32 @@ contract ServiceNodeContribution is Shared {
blsPubkey = newBlsPubkey;
}

/**
* @notice Allows the operator to update the serviceNodeParams.
* @dev This function can only be called by the operator, before the contract is finalized,
* and when there are no other contributors besides the operator.
* @param newParams The new ServiceNodeParams to set.
*/
function updateServiceNodeParams(IServiceNodeRewards.ServiceNodeParams memory newParams) public onlyOperator {
require(!finalized, "Cannot update params: Node has already been finalized.");
require(contributorAddresses.length == 1, "Cannot update params: Other contributors have already joined.");

serviceNodeParams = newParams;
}

/**
* @notice Allows the operator to update the blsPubkey.
* @dev This function can only be called by the operator, before the contract is finalized,
* and when there are no other contributors besides the operator.
* @param newBlsPubkey The new BLS Pubkey to set.
*/
function updateBLSPubkey(BN256G1.G1Point memory newBlsPubkey) public onlyOperator {
require(!finalized, "Cannot update pubkey: Node has already been finalized.");
require(contributorAddresses.length == 1, "Cannot update pubkey: Other contributors have already joined.");

blsPubkey = newBlsPubkey;
}

/**
* @notice Function to allow owner to rescue any ERC20 tokens sent to the
* contract after it has been finalized.
Expand Down Expand Up @@ -512,6 +541,21 @@ contract ServiceNodeContribution is Shared {
return result;
}

/**
* @notice Access the list of contributor addresses and corresponding contributions. The
* first returned address (if any) is also the operator address.
*/
function getContributions() public view returns (address[] memory addrs, uint256[] memory contribs) {
uint256 size = contributorAddresses.length;
addrs = new address[](size);
contribs = new uint256[](size);
for (uint256 i = 0; i < size; i++) {
addrs[i] = contributorAddresses[i];
contribs[i] = contributions[addrs[i]];
}
return (addrs, contribs);
}

/**
* @notice Sum up all the contributions recorded in the contributors list
*/
Expand Down
2 changes: 1 addition & 1 deletion contracts/ServiceNodeContributionFactory.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "./ServiceNodeContribution.sol";
import "./interfaces/IServiceNodeRewards.sol";
Expand Down
68 changes: 56 additions & 12 deletions contracts/ServiceNodeRewards.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "./interfaces/IServiceNodeRewards.sol";

Expand All @@ -23,7 +23,7 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU

uint64 public constant LIST_SENTINEL = 0;
uint256 public constant MAX_SERVICE_NODE_REMOVAL_WAIT_TIME = 30 days;
uint256 public constant MAX_PERMITTED_PUBKEY_AGGREGATIONS_LOWER_BOUND = 5;
uint256 public constant MAX_PERMITTED_PUBKEY_AGGREGATIONS_LOWER_BOUND = 20;
// A small contributor is one who contributes less than 1/DIVISOR of the total; such a
// contributor may not initiate a leave request within the initial LEAVE_DELAY:
uint256 public constant SMALL_CONTRIBUTOR_LEAVE_DELAY = 30 days;
Expand Down Expand Up @@ -65,6 +65,7 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
uint256 recipientRatio_
) public initializer {
if (recipientRatio_ < 1) revert RecipientRewardsTooLow();
if (liquidatorRewardRatio_< 1) revert LiquidatorRewardsTooLow();
isStarted = false;
totalNodes = 0;
blsNonSignerThreshold = 0;
Expand All @@ -75,6 +76,10 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
liquidateTag = buildTag("BLS_SIG_TRYANDINCREMENT_LIQUIDATE");
hashToG2Tag = buildTag("BLS_SIG_HASH_TO_FIELD_TAG");
signatureExpiry = 10 minutes;
claimThreshold = 1_000_000 * 1e9;
claimCycle = 12 hours;
periodicClaims = 0;
epochDay = 0;

designatedToken = IERC20(token_);
foundationPool = IERC20(foundationPool_);
Expand Down Expand Up @@ -123,6 +128,11 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
_;
}

uint256 public claimThreshold;
uint256 public claimCycle;
uint256 public periodicClaims;
uint256 public epochDay;

// EVENTS
event NewSeededServiceNode(uint64 indexed serviceNodeID, BN256G1.G1Point pubkey);
event NewServiceNode(
Expand All @@ -135,6 +145,7 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
event RewardsBalanceUpdated(address indexed recipientAddress, uint256 amount, uint256 previousBalance);
event RewardsClaimed(address indexed recipientAddress, uint256 amount);
event BLSNonSignerThresholdMaxUpdated(uint256 newMax);
event ClaimThresholdUpdated(uint256 newThreshold);
event ServiceNodeLiquidated(uint64 indexed serviceNodeID, address operator, BN256G1.G1Point pubkey);
event ServiceNodeRemoval(
uint64 indexed serviceNodeID,
Expand All @@ -150,6 +161,7 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
error BLSPubkeyAlreadyExists(uint64 serviceNodeID);
error BLSPubkeyDoesNotMatch(uint64 serviceNodeID, BN256G1.G1Point pubkey);
error CallerNotContributor(uint64 serviceNodeID, address contributor);
error ClaimThresholdExceeded();
error ContractAlreadyStarted();
error ContractNotStarted();
error ContributionTotalMismatch(uint256 required, uint256 provided);
Expand All @@ -162,7 +174,9 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
error InvalidBLSSignature();
error InvalidBLSProofOfPossession();
error LeaveRequestTooEarly(uint64 serviceNodeID, uint256 timestamp, uint256 currenttime);
error LiquidatorRewardsTooLow();
error MaxContributorsExceeded();
error MaxClaimExceeded();
error MaxPubkeyAggregationsExceeded();
error NullPublicKey();
error NullAddress();
Expand Down Expand Up @@ -229,22 +243,41 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
}

/// @dev Internal function to handle reward claims. Will transfer the
/// available rewards worth of our token to claimingAddress
/// requested amount of our token to claimingAddress, up to the available rewards
/// @param claimingAddress The address claiming the rewards.
function _claimRewards(address claimingAddress) internal {
/// @param amount The amount of rewards to claim.
function _claimRewards(address claimingAddress, uint256 amount) internal {
uint256 claimedRewards = recipients[claimingAddress].claimed;
uint256 totalRewards = recipients[claimingAddress].rewards;
uint256 amountToRedeem = totalRewards - claimedRewards;

recipients[claimingAddress].claimed = totalRewards;
emit RewardsClaimed(claimingAddress, amountToRedeem);
uint256 maxAmount = totalRewards - claimedRewards;
if (amount > maxAmount)
revert MaxClaimExceeded();

uint256 _epochDay = block.timestamp / claimCycle;
if (_epochDay > epochDay) {
epochDay = _epochDay;
periodicClaims = 0;
}
periodicClaims += amount;
if (periodicClaims > claimThreshold) revert ClaimThresholdExceeded();

SafeERC20.safeTransfer(designatedToken, claimingAddress, amountToRedeem);
recipients[claimingAddress].claimed += amount;
emit RewardsClaimed(claimingAddress, amount);
SafeERC20.safeTransfer(designatedToken, claimingAddress, amount);
}

/// @notice Claim the rewards due for the active wallet invoking the claim.
/// @notice Claim all available rewards for the active wallet invoking the claim.
function claimRewards() public {
_claimRewards(msg.sender);
uint256 claimedRewards = recipients[msg.sender].claimed;
uint256 totalRewards = recipients[msg.sender].rewards;
uint256 amountToRedeem = totalRewards - claimedRewards;
_claimRewards(msg.sender, amountToRedeem);
}

/// @notice Claim a specific amount of rewards for the active wallet invoking the claim.
/// @param amount The amount of rewards to claim.
function claimRewards(uint256 amount) public {
_claimRewards(msg.sender, amount);
}

/// MANAGING BLS PUBLIC KEY LIST
Expand Down Expand Up @@ -768,6 +801,17 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
emit BLSNonSignerThresholdMaxUpdated(newMax);
}

/// @notice Max Claim amount to use in the check
/// before allowing the user to claim. If the claimed amount over 24 hours
/// exceeds this then the claim will fail
/// @param newMax The new maximum claim threshold
function setClaimThreshold(uint256 newMax) public onlyOwner {
if (newMax <= 0)
revert PositiveNumberRequirement();
claimThreshold = newMax;
emit ClaimThresholdUpdated(newMax);
}

//////////////////////////////////////////////////////////////
// //
// Non-state-changing functions //
Expand Down Expand Up @@ -828,7 +872,7 @@ contract ServiceNodeRewards is Initializable, Ownable2StepUpgradeable, PausableU
/// @notice The maximum number of pubkey aggregations permitted for the
/// current block height.
///
/// @dev This is currently defined as max(5, 2 percent of the network).
/// @dev This is currently defined as max(20, 2 percent of the network).
///
/// This value is used in tandem with `_numPubkeyAggregationsForHeight`
/// which tracks the current number of aggregations thus far for the current
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IServiceNodeRewards.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/BN256G1.sol";
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/ITokenVestingNoStaking.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/ITokenVestingStaking.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IServiceNodeRewards.sol";
Expand Down
2 changes: 1 addition & 1 deletion contracts/libraries/BN256G1.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

library BN256G1 {
uint256 internal constant FIELD_MODULUS = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
Expand Down
2 changes: 1 addition & 1 deletion contracts/libraries/BN256G2.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

/**
* @title Elliptic curve operations on twist points for alt_bn128
Expand Down
4 changes: 2 additions & 2 deletions contracts/libraries/Pairing.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "./BN256G1.sol";
import "./BN256G2.sol";
Expand Down Expand Up @@ -29,7 +29,7 @@ library Pairing {
assembly {
success := call(sub(gas(), 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
}
require(success, "Call to precompiled contract for pairing failed");
require(success, "Invalid Signature");
return out[0] != 0;
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/libraries/Shared.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

/**
* @title Shared contract
Expand Down
4 changes: 2 additions & 2 deletions contracts/test/BN256G2EchidnaTest.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "../libraries/BN256G2.sol";

Expand Down Expand Up @@ -44,7 +44,7 @@ contract BN256G2EchidnaTest {
Hm = BN256G2.hashToG2(message, dummyTag);
}

function echidna_always_hashable() public returns (bool) {
function echidna_always_hashable() public view returns (bool) {
return BN256G2.IsOnCurve(Hm.X[1], Hm.X[0], Hm.Y[1], Hm.Y[0]);
}
}
2 changes: 1 addition & 1 deletion contracts/test/MockERC20.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

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

Expand Down
2 changes: 1 addition & 1 deletion contracts/test/MockServiceNodeRewards.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "../interfaces/IServiceNodeRewards.sol";

Expand Down
2 changes: 1 addition & 1 deletion contracts/test/ServiceNodeContributionEchidnaTest.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "../ServiceNodeContribution.sol";
import "./MockERC20.sol";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

interface IServiceNodeContribution {
function cancelNode() external;
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/TestnetRewardRatePool.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "../RewardRatePool.sol";

Expand Down
11 changes: 10 additions & 1 deletion contracts/test/TestnetServiceNodeRewards.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "../ServiceNodeRewards.sol";

contract TestnetServiceNodeRewards is ServiceNodeRewards {
// NOTE: Admin function to remove node by ID for stagenet debugging
function requestRemoveNodeBySNID(uint64[] calldata ids) external onlyOwner {
for (uint256 i = 0; i < ids.length; i++) {
uint64 serviceNodeID = ids[i];
IServiceNodeRewards.ServiceNode memory node = this.serviceNodes(serviceNodeID);
require(node.operator != address(0));
_initiateRemoveBLSPublicKey(serviceNodeID, node.operator);
}
}

function removeNodeBySNID(uint64[] calldata ids) external onlyOwner {
for (uint256 i = 0; i < ids.length; i++) {
uint64 serviceNodeID = ids[i];
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/TokenConverter.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/TokenVestingNoStaking.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;
pragma solidity ^0.8.26;

import "../libraries/Shared.sol";
import "../interfaces/ITokenVestingNoStaking.sol";
Expand Down
Loading

0 comments on commit 6d63ea4

Please sign in to comment.