├── .gitattributes ├── contracts ├── .gitattributes ├── interface │ ├── dao │ │ ├── protocol │ │ │ ├── RocketDAOProtocolActionsInterface.sol │ │ │ ├── settings │ │ │ │ ├── RocketDAOProtocolSettingsInflationInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsSecurityInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsAuctionInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsDepositInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsNodeInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsProposalsInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsRewardsInterface.sol │ │ │ │ ├── RocketDAOProtocolSettingsNetworkInterface.sol │ │ │ │ └── RocketDAOProtocolSettingsMinipoolInterface.sol │ │ │ ├── RocketDAOProtocolProposalsInterface.sol │ │ │ ├── RocketDAOProtocolInterface.sol │ │ │ ├── RocketDAOProtocolVerifierInterface.sol │ │ │ └── RocketDAOProtocolProposalInterface.sol │ │ ├── node │ │ │ ├── settings │ │ │ │ ├── RocketDAONodeTrustedSettingsRewardsInterface.sol │ │ │ │ ├── RocketDAONodeTrustedSettingsProposalsInterface.sol │ │ │ │ ├── RocketDAONodeTrustedSettingsInterface.sol │ │ │ │ ├── RocketDAONodeTrustedSettingsMembersInterface.sol │ │ │ │ └── RocketDAONodeTrustedSettingsMinipoolInterface.sol │ │ │ ├── RocketDAONodeTrustedUpgradeInterface.sol │ │ │ ├── RocketDAONodeTrustedActionsInterface.sol │ │ │ ├── RocketDAONodeTrustedProposalsInterface.sol │ │ │ └── RocketDAONodeTrustedInterface.sol │ │ ├── security │ │ │ ├── RocketDAOSecurityActionsInterface.sol │ │ │ ├── RocketDAOSecurityInterface.sol │ │ │ └── RocketDAOSecurityProposalsInterface.sol │ │ └── RocketDAOProposalInterface.sol │ ├── RocketVaultWithdrawerInterface.sol │ ├── rewards │ │ ├── RocketSmoothingPoolInterface.sol │ │ ├── RocketMerkleDistributorMainnetInterface.sol │ │ ├── claims │ │ │ ├── RocketClaimNodeInterface.sol │ │ │ ├── RocketClaimTrustedNodeInterface.sol │ │ │ └── RocketClaimDAOInterface.sol │ │ ├── RocketRewardsRelayInterface.sol │ │ └── RocketRewardsPoolInterface.sol │ ├── casper │ │ └── DepositInterface.sol │ ├── node │ │ ├── RocketNodeDistributorInterface.sol │ │ ├── RocketNodeDistributorFactoryInterface.sol │ │ ├── RocketNodeDepositInterface.sol │ │ ├── RocketNodeStakingInterface.sol │ │ └── RocketNodeManagerInterface.sol │ ├── util │ │ ├── IERC20Burnable.sol │ │ ├── AddressSetStorageInterface.sol │ │ ├── AddressQueueStorageInterface.sol │ │ └── IERC20.sol │ ├── network │ │ ├── RocketNetworkFeesInterface.sol │ │ ├── RocketNetworkPenaltiesInterface.sol │ │ ├── RocketNetworkPricesInterface.sol │ │ ├── RocketNetworkBalancesInterface.sol │ │ ├── RocketNetworkVotingInterface.sol │ │ └── RocketNetworkSnapshotsInterface.sol │ ├── minipool │ │ ├── RocketMinipoolFactoryInterface.sol │ │ ├── RocketMinipoolPenaltyInterface.sol │ │ ├── RocketMinipoolBaseInterface.sol │ │ ├── RocketMinipoolBondReducerInterface.sol │ │ ├── RocketMinipoolQueueInterface.sol │ │ ├── RocketMinipoolInterface.sol │ │ └── RocketMinipoolManagerInterface.sol │ ├── token │ │ ├── RocketTokenRETHInterface.sol │ │ └── RocketTokenRPLInterface.sol │ ├── RocketVaultInterface.sol │ ├── deposit │ │ └── RocketDepositPoolInterface.sol │ ├── auction │ │ └── RocketAuctionManagerInterface.sol │ └── RocketStorageInterface.sol ├── types │ ├── SettingType.sol │ ├── RewardSubmission.sol │ ├── MinipoolStatus.sol │ ├── MinipoolDeposit.sol │ ├── MinipoolDetails.sol │ └── NodeDetails.sol └── contract │ ├── node │ ├── RocketNodeDistributorStorageLayout.sol │ ├── RocketNodeDistributor.sol │ └── RocketNodeDistributorFactory.sol │ ├── helper │ ├── RevertOnTransfer.sol │ ├── PenaltyTest.sol │ └── SnapshotTest.sol │ ├── util │ ├── Context.sol │ ├── ERC20Burnable.sol │ └── AddressSetStorage.sol │ ├── dao │ ├── protocol │ │ ├── RocketDAOProtocolActions.sol │ │ └── settings │ │ │ └── RocketDAOProtocolSettings.sol │ └── node │ │ └── settings │ │ ├── RocketDAONodeTrustedSettingsRewards.sol │ │ ├── RocketDAONodeTrustedSettings.sol │ │ └── RocketDAONodeTrustedSettingsProposals.sol │ ├── casper │ └── compiled │ │ └── Deposit.abi │ ├── rewards │ └── RocketSmoothingPool.sol │ ├── token │ └── temp │ │ └── RocketTokenDummyRPL.sol │ └── minipool │ ├── RocketMinipoolPenalty.sol │ ├── RocketMinipoolFactory.sol │ └── RocketMinipoolStorageLayout.sol ├── test ├── _utils │ ├── upgrade.js │ ├── formatting.js │ ├── snapshotting.js │ ├── beacon.js │ ├── testing.js │ ├── contract.js │ └── evm.js ├── node │ ├── scenario-register-smoothing-pool.js │ ├── scenario-set-timezone.js │ ├── scenario-register.js │ └── scenario-set-withdrawal-address.js ├── _helpers │ ├── bigmath.js │ ├── deposit.js │ ├── console.js │ ├── deployment.js │ ├── auction.js │ ├── settings.js │ ├── bn.js │ ├── tokens.js │ ├── defaults.js │ └── network.js ├── minipool │ ├── scenario-dissolve.js │ ├── scenario-close.js │ ├── scenario-refund.js │ ├── scenario-stake.js │ ├── scenario-reduce-bond.js │ └── scenario-skim-rewards.js ├── rewards │ ├── scenario-rewards-claim.js │ ├── scenario-claim-rewards.js │ └── scenario-claim-and-stake-rewards.js ├── token │ ├── scenario-rpl-allow-fixed.js │ ├── scenario-reth-transfer.js │ ├── scenario-rpl-mint-fixed.js │ ├── scenario-reth-burn.js │ └── scenario-rpl-burn-fixed.js ├── auction │ ├── scenario-recover-rpl.js │ └── scenario-claim-bid.js ├── deposit │ └── scenario-deposit.js ├── dao │ ├── scenario-dao-proposal.js │ └── scenario-dao-protocol-treasury.js ├── network │ ├── network-voting-tests.js │ ├── network-snapshots-tests.js │ └── network-fees-tests.js └── rocket-pool-tests.js ├── .mocharc.json ├── images ├── logo.png └── rocket-pool-atlas-test.png ├── .gitignore ├── remapping.json ├── .github └── workflows │ └── CI.yml ├── hardhat-fork.config.js ├── test-fork └── rocket-fork-tests.js ├── scripts ├── preamble.sol └── etherscan-verify.js ├── hardhat.config.js ├── hardhat-common.config.js ├── package.json └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /contracts/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /test/_utils/upgrade.js: -------------------------------------------------------------------------------- 1 | import { artifacts } from './artifacts'; 2 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": "hardhat/register", 3 | "timeout": 0 4 | } -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rocket-pool/rocketpool/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/rocket-pool-atlas-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rocket-pool/rocketpool/HEAD/images/rocket-pool-atlas-test.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode 3 | .idea/ 4 | cache/ 5 | artifacts/ 6 | .env 7 | coverage/ 8 | coverage.json 9 | deployments/ -------------------------------------------------------------------------------- /test/_utils/formatting.js: -------------------------------------------------------------------------------- 1 | // Print pretty test title 2 | export function printTitle(user, desc) { 3 | return '\x1b[33m' + user + '\u001b[00m: \u001b[01;34m' + desc; 4 | } 5 | -------------------------------------------------------------------------------- /remapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "remappings": [ 3 | "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/", 4 | "@openzeppelin4/contracts/=node_modules/@openzeppelin4/contracts/" 5 | ] 6 | } -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/RocketDAOProtocolActionsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolActionsInterface { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interface/RocketVaultWithdrawerInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketVaultWithdrawerInterface { 6 | function receiveVaultWithdrawalETH() external payable; 7 | } 8 | -------------------------------------------------------------------------------- /contracts/types/SettingType.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | enum SettingType { 6 | UINT256, 7 | BOOL, 8 | ADDRESS, 9 | STRING, 10 | BYTES, 11 | BYTES32, 12 | INT256 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interface/rewards/RocketSmoothingPoolInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | interface RocketSmoothingPoolInterface { 6 | function withdrawEther(address _to, uint256 _amount) external; 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/settings/RocketDAONodeTrustedSettingsRewardsInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | interface RocketDAONodeTrustedSettingsRewardsInterface { 5 | function getNetworkEnabled(uint256 _network) external view returns (bool); 6 | } 7 | -------------------------------------------------------------------------------- /contracts/interface/casper/DepositInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface DepositInterface { 6 | function deposit(bytes calldata _pubkey, bytes calldata _withdrawalCredentials, bytes calldata _signature, bytes32 _depositDataRoot) external payable; 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/RocketDAONodeTrustedUpgradeInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedUpgradeInterface { 6 | function upgrade(string memory _type, string memory _name, string memory _contractAbi, address _contractAddress) external; 7 | } 8 | -------------------------------------------------------------------------------- /contracts/interface/node/RocketNodeDistributorInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketNodeDistributorInterface { 6 | function getNodeShare() external view returns (uint256); 7 | function getUserShare() external view returns (uint256); 8 | function distribute() external; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/contract/node/RocketNodeDistributorStorageLayout.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | import "../../interface/RocketStorageInterface.sol"; 4 | 5 | // SPDX-License-Identifier: GPL-3.0-only 6 | 7 | abstract contract RocketNodeDistributorStorageLayout { 8 | RocketStorageInterface rocketStorage; 9 | address nodeAddress; 10 | uint256 lock; // Reentrancy guard 11 | } -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsInflationInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolSettingsInflationInterface { 6 | function getInflationIntervalRate() external view returns (uint256); 7 | function getInflationIntervalStartTime() external view returns (uint256); 8 | } 9 | -------------------------------------------------------------------------------- /contracts/interface/util/IERC20Burnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) 3 | 4 | import "./IERC20.sol"; 5 | 6 | pragma solidity >0.5.0 <0.9.0; 7 | 8 | interface IERC20Burnable is IERC20 { 9 | function burn(uint256 amount) external; 10 | function burnFrom(address account, uint256 amount) external; 11 | } 12 | -------------------------------------------------------------------------------- /contracts/interface/network/RocketNetworkFeesInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketNetworkFeesInterface { 6 | function getNodeDemand() external view returns (int256); 7 | function getNodeFee() external view returns (uint256); 8 | function getNodeFeeByDemand(int256 _nodeDemand) external view returns (uint256); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interface/rewards/RocketMerkleDistributorMainnetInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | import "./RocketRewardsRelayInterface.sol"; 5 | 6 | interface RocketMerkleDistributorMainnetInterface is RocketRewardsRelayInterface { 7 | function claimOutstandingEth() external; 8 | function getOutstandingEth(address _address) external view returns (uint256); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interface/node/RocketNodeDistributorFactoryInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketNodeDistributorFactoryInterface { 6 | function getProxyBytecode() external pure returns (bytes memory); 7 | function getProxyAddress(address _nodeAddress) external view returns(address); 8 | function createProxy(address _nodeAddress) external; 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | 4 | jobs: 5 | run-ci: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/setup-node@v3 9 | with: 10 | node-version: 18 11 | - uses: actions/checkout@v4 12 | with: 13 | submodules: recursive 14 | - name: Install Components 15 | run: npm install 16 | 17 | - name: Run Rocket Pool tests 18 | run: npm test 19 | -------------------------------------------------------------------------------- /contracts/interface/minipool/RocketMinipoolFactoryInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../types/MinipoolDeposit.sol"; 6 | 7 | interface RocketMinipoolFactoryInterface { 8 | function getExpectedAddress(address _nodeAddress, uint256 _salt) external view returns (address); 9 | function deployContract(address _nodeAddress, uint256 _salt) external returns (address); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interface/network/RocketNetworkPenaltiesInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketNetworkPenaltiesInterface { 6 | function submitPenalty(address _minipoolAddress, uint256 _block) external; 7 | function executeUpdatePenalty(address _minipoolAddress, uint256 _block) external; 8 | function getPenaltyCount(address _minipoolAddress) external view returns (uint256); 9 | } 10 | -------------------------------------------------------------------------------- /contracts/interface/dao/security/RocketDAOSecurityActionsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | // SPDX-License-Identifier: GPL-3.0-only 3 | 4 | interface RocketDAOSecurityActionsInterface { 5 | function actionKick(address _nodeAddress) external; 6 | function actionKickMulti(address[] calldata _nodeAddresses) external; 7 | function actionJoin() external; 8 | function actionRequestLeave() external; 9 | function actionLeave() external; 10 | } 11 | -------------------------------------------------------------------------------- /hardhat-fork.config.js: -------------------------------------------------------------------------------- 1 | let common = require('./hardhat-common.config.js'); 2 | 3 | // Config from environment 4 | const mainnetProviderUrl = process.env.MAINNET_PROVIDER_URL || 'http://localhost:8545'; 5 | 6 | module.exports = Object.assign(common, { 7 | networks: { 8 | hardhat: { 9 | forking: { 10 | url: mainnetProviderUrl, 11 | }, 12 | }, 13 | }, 14 | paths: { 15 | tests: './test-fork', 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /contracts/types/RewardSubmission.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | struct RewardSubmission { 6 | uint256 rewardIndex; 7 | uint256 executionBlock; 8 | uint256 consensusBlock; 9 | bytes32 merkleRoot; 10 | string merkleTreeCID; 11 | uint256 intervalsPassed; 12 | uint256 treasuryRPL; 13 | uint256[] trustedNodeRPL; 14 | uint256[] nodeRPL; 15 | uint256[] nodeETH; 16 | uint256 userETH; 17 | } 18 | -------------------------------------------------------------------------------- /contracts/interface/network/RocketNetworkPricesInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | interface RocketNetworkPricesInterface { 5 | function getPricesBlock() external view returns (uint256); 6 | function getRPLPrice() external view returns (uint256); 7 | function submitPrices(uint256 _block, uint256 _slotTimestamp, uint256 _rplPrice) external; 8 | function executeUpdatePrices(uint256 _block, uint256 _slotTimestamp, uint256 _rplPrice) external; 9 | } 10 | -------------------------------------------------------------------------------- /test/_utils/snapshotting.js: -------------------------------------------------------------------------------- 1 | const helpers = require('@nomicfoundation/hardhat-network-helpers'); 2 | 3 | let globalSnapshot, snapshot; 4 | 5 | export async function startSnapShot() { 6 | snapshot = await helpers.takeSnapshot(); 7 | } 8 | 9 | export async function endSnapShot() { 10 | await snapshot.restore(); 11 | } 12 | 13 | export async function globalSnapShot() { 14 | if (globalSnapshot) { 15 | await globalSnapshot.restore(); 16 | } 17 | globalSnapshot = await helpers.takeSnapshot(); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsSecurityInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | interface RocketDAOProtocolSettingsSecurityInterface { 5 | function getQuorum() external view returns (uint256); 6 | function getLeaveTime() external view returns (uint256); 7 | function getVoteTime() external view returns(uint256); 8 | function getExecuteTime() external view returns(uint256); 9 | function getActionTime() external view returns (uint256); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interface/minipool/RocketMinipoolPenaltyInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketMinipoolPenaltyInterface { 6 | // Max penalty rate 7 | function setMaxPenaltyRate(uint256 _rate) external; 8 | function getMaxPenaltyRate() external view returns (uint256); 9 | 10 | // Penalty rate 11 | function setPenaltyRate(address _minipoolAddress, uint256 _rate) external; 12 | function getPenaltyRate(address _minipoolAddress) external view returns(uint256); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interface/util/AddressSetStorageInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface AddressSetStorageInterface { 6 | function getCount(bytes32 _key) external view returns (uint); 7 | function getItem(bytes32 _key, uint _index) external view returns (address); 8 | function getIndexOf(bytes32 _key, address _value) external view returns (int); 9 | function addItem(bytes32 _key, address _value) external; 10 | function removeItem(bytes32 _key, address _value) external; 11 | } 12 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/settings/RocketDAONodeTrustedSettingsProposalsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedSettingsProposalsInterface { 6 | function getCooldownTime() external view returns(uint256); 7 | function getVoteTime() external view returns(uint256); 8 | function getVoteDelayTime() external view returns(uint256); 9 | function getExecuteTime() external view returns(uint256); 10 | function getActionTime() external view returns(uint256); 11 | } 12 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/settings/RocketDAONodeTrustedSettingsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedSettingsInterface { 6 | function getSettingUint(string memory _settingPath) external view returns (uint256); 7 | function setSettingUint(string memory _settingPath, uint256 _value) external; 8 | function getSettingBool(string memory _settingPath) external view returns (bool); 9 | function setSettingBool(string memory _settingPath, bool _value) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/RocketDAONodeTrustedActionsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedActionsInterface { 6 | function actionJoin() external; 7 | function actionJoinRequired(address _nodeAddress) external; 8 | function actionLeave(address _rplBondRefundAddress) external; 9 | function actionKick(address _nodeAddress, uint256 _rplFine) external; 10 | function actionChallengeMake(address _nodeAddress) external payable; 11 | function actionChallengeDecide(address _nodeAddress) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interface/rewards/claims/RocketClaimNodeInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketClaimNodeInterface { 6 | function getEnabled() external view returns (bool); 7 | function getClaimPossible(address _nodeAddress) external view returns (bool); 8 | function getClaimRewardsPerc(address _nodeAddress) external view returns (uint256); 9 | function getClaimRewardsAmount(address _nodeAddress) external view returns (uint256); 10 | function register(address _nodeAddress, bool _enable) external; 11 | function claim() external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interface/util/AddressQueueStorageInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface AddressQueueStorageInterface { 6 | function getLength(bytes32 _key) external view returns (uint); 7 | function getItem(bytes32 _key, uint _index) external view returns (address); 8 | function getIndexOf(bytes32 _key, address _value) external view returns (int); 9 | function enqueueItem(bytes32 _key, address _value) external; 10 | function dequeueItem(bytes32 _key) external returns (address); 11 | function removeItem(bytes32 _key, address _value) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/contract/helper/RevertOnTransfer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | // Helper contract to simulate malicious node withdrawal address or withdrawal address 6 | contract RevertOnTransfer { 7 | bool public enabled = true; 8 | 9 | function setEnabled(bool _enabled) external { 10 | enabled = _enabled; 11 | } 12 | 13 | fallback() external payable { 14 | require(!enabled); 15 | } 16 | 17 | function call(address _address, bytes calldata _payload) external payable { 18 | _address.call{value: msg.value}(_payload); 19 | } 20 | } -------------------------------------------------------------------------------- /contracts/types/MinipoolStatus.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | // Represents a minipool's status within the network 6 | 7 | enum MinipoolStatus { 8 | Initialised, // The minipool has been initialised and is awaiting a deposit of user ETH 9 | Prelaunch, // The minipool has enough ETH to begin staking and is awaiting launch by the node operator 10 | Staking, // The minipool is currently staking 11 | Withdrawable, // NO LONGER USED 12 | Dissolved // The minipool has been dissolved and its user deposited ETH has been returned to the deposit pool 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interface/rewards/claims/RocketClaimTrustedNodeInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketClaimTrustedNodeInterface { 6 | function getEnabled() external view returns (bool); 7 | function getClaimPossible(address _trustedNodeAddress) external view returns (bool); 8 | function getClaimRewardsPerc(address _trustedNodeAddress) external view returns (uint256); 9 | function getClaimRewardsAmount(address _trustedNodeAddress) external view returns (uint256); 10 | function register(address _trustedNode, bool _enable) external; 11 | function claim() external; 12 | } 13 | -------------------------------------------------------------------------------- /test-fork/rocket-fork-tests.js: -------------------------------------------------------------------------------- 1 | import { afterEach, beforeEach, describe } from 'mocha'; 2 | import { injectBNHelpers } from '../test/_helpers/bn'; 3 | import pako from 'pako'; 4 | import { endSnapShot, startSnapShot } from '../test/_utils/snapshotting'; 5 | 6 | const hre = require('hardhat'); 7 | 8 | const rocketStorageAddress = process.env.MAINNET_ROCKET_STORAGE || '0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46'; 9 | 10 | injectBNHelpers(); 11 | beforeEach(startSnapShot); 12 | afterEach(endSnapShot); 13 | 14 | function compressABI(abi) { 15 | return Buffer.from(pako.deflate(JSON.stringify(abi))).toString('base64'); 16 | } 17 | 18 | describe('Fork Mainnet', () => { 19 | }); -------------------------------------------------------------------------------- /contracts/interface/minipool/RocketMinipoolBaseInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketMinipoolBaseInterface { 6 | function initialise(address _rocketStorage, address _nodeAddress) external; 7 | function delegateUpgrade() external; 8 | function delegateRollback() external; 9 | function setUseLatestDelegate(bool _setting) external; 10 | function getUseLatestDelegate() external view returns (bool); 11 | function getDelegate() external view returns (address); 12 | function getPreviousDelegate() external view returns (address); 13 | function getEffectiveDelegate() external view returns (address); 14 | } 15 | -------------------------------------------------------------------------------- /test/node/scenario-register-smoothing-pool.js: -------------------------------------------------------------------------------- 1 | import { RocketNodeManager } from '../_utils/artifacts'; 2 | import * as assert from 'assert'; 3 | 4 | // Register a node 5 | export async function setSmoothingPoolRegistrationState(state, txOptions) { 6 | // Load contracts 7 | const rocketNodeManager = await RocketNodeManager.deployed(); 8 | 9 | // Register 10 | await rocketNodeManager.connect(txOptions.from).setSmoothingPoolRegistrationState(state, txOptions); 11 | 12 | // Check details 13 | const newState = await rocketNodeManager.getSmoothingPoolRegistrationState(txOptions.from.address); 14 | assert.strictEqual(newState, state, 'Incorrect smoothing pool registration state'); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/settings/RocketDAONodeTrustedSettingsMembersInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedSettingsMembersInterface { 6 | function getQuorum() external view returns (uint256); 7 | function getRPLBond() external view returns(uint256); 8 | function getMinipoolUnbondedMax() external view returns(uint256); 9 | function getMinipoolUnbondedMinFee() external view returns(uint256); 10 | function getChallengeCooldown() external view returns(uint256); 11 | function getChallengeWindow() external view returns(uint256); 12 | function getChallengeCost() external view returns(uint256); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsAuctionInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolSettingsAuctionInterface { 6 | function getCreateLotEnabled() external view returns (bool); 7 | function getBidOnLotEnabled() external view returns (bool); 8 | function getLotMinimumEthValue() external view returns (uint256); 9 | function getLotMaximumEthValue() external view returns (uint256); 10 | function getLotDuration() external view returns (uint256); 11 | function getStartingPriceRatio() external view returns (uint256); 12 | function getReservePriceRatio() external view returns (uint256); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolSettingsInterface { 6 | function getSettingUint(string memory _settingPath) external view returns (uint256); 7 | function setSettingUint(string memory _settingPath, uint256 _value) external; 8 | function getSettingBool(string memory _settingPath) external view returns (bool); 9 | function setSettingBool(string memory _settingPath, bool _value) external; 10 | function getSettingAddress(string memory _settingPath) external view returns (address); 11 | function setSettingAddress(string memory _settingPath, address _value) external; 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsDepositInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolSettingsDepositInterface { 6 | function getDepositEnabled() external view returns (bool); 7 | function getAssignDepositsEnabled() external view returns (bool); 8 | function getMinimumDeposit() external view returns (uint256); 9 | function getMaximumDepositPoolSize() external view returns (uint256); 10 | function getMaximumDepositAssignments() external view returns (uint256); 11 | function getMaximumDepositSocialisedAssignments() external view returns (uint256); 12 | function getDepositFee() external view returns (uint256); 13 | } 14 | -------------------------------------------------------------------------------- /test/_helpers/bigmath.js: -------------------------------------------------------------------------------- 1 | export function BigMin(...args) { 2 | let smallest = args[0]; 3 | 4 | for (let i = 1; i < args.length; i++) { 5 | if (args[i] < smallest) { 6 | smallest = args[i]; 7 | } 8 | } 9 | 10 | return smallest; 11 | } 12 | 13 | export function BigSqrt(value) { 14 | if (value < 0n) { 15 | throw 'negative number'; 16 | } 17 | 18 | if (value < 2n) { 19 | return value; 20 | } 21 | 22 | function newtonIteration(n, x0) { 23 | const x1 = ((n / x0) + x0) >> 1n; 24 | if (x0 === x1 || x0 === (x1 - 1n)) { 25 | return x0; 26 | } 27 | return newtonIteration(n, x1); 28 | } 29 | 30 | return newtonIteration(value, 1n); 31 | } -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsNodeInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolSettingsNodeInterface { 6 | function getRegistrationEnabled() external view returns (bool); 7 | function getSmoothingPoolRegistrationEnabled() external view returns (bool); 8 | function getDepositEnabled() external view returns (bool); 9 | function getVacantMinipoolsEnabled() external view returns (bool); 10 | function getMinimumPerMinipoolStake() external view returns (uint256); 11 | function getMaximumPerMinipoolStake() external view returns (uint256); 12 | function getMaximumStakeForVotingPower() external view returns (uint256); 13 | } 14 | -------------------------------------------------------------------------------- /contracts/types/MinipoolDeposit.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | // Represents the type of deposits required by a minipool 6 | 7 | enum MinipoolDeposit { 8 | None, // Marks an invalid deposit type 9 | Full, // The minipool requires 32 ETH from the node operator, 16 ETH of which will be refinanced from user deposits 10 | Half, // The minipool required 16 ETH from the node operator to be matched with 16 ETH from user deposits 11 | Empty, // The minipool requires 0 ETH from the node operator to be matched with 32 ETH from user deposits (trusted nodes only) 12 | Variable // Indicates this minipool is of the new generation that supports a variable deposit amount 13 | } 14 | -------------------------------------------------------------------------------- /test/_helpers/deposit.js: -------------------------------------------------------------------------------- 1 | import { RocketDepositPool } from '../_utils/artifacts'; 2 | 3 | // Get the deposit pool excess ETH balance 4 | export async function getDepositExcessBalance() { 5 | const rocketDepositPool = await RocketDepositPool.deployed(); 6 | return rocketDepositPool.getExcessBalance.call(); 7 | } 8 | 9 | // Make a deposit 10 | export async function userDeposit(txOptions) { 11 | const rocketDepositPool = await RocketDepositPool.deployed(); 12 | await rocketDepositPool.connect(txOptions.from).deposit(txOptions); 13 | } 14 | 15 | // Assign deposits 16 | export async function assignDeposits(txOptions) { 17 | const rocketDepositPool = await RocketDepositPool.deployed(); 18 | await rocketDepositPool.assignDeposits(txOptions); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /test/node/scenario-set-timezone.js: -------------------------------------------------------------------------------- 1 | import { RocketNodeManager } from '../../test/_utils/artifacts'; 2 | import * as assert from 'assert'; 3 | 4 | // Set a node's timezone location 5 | export async function setTimezoneLocation(timezoneLocation, txOptions) { 6 | // Load contracts 7 | const rocketNodeManager = await RocketNodeManager.deployed(); 8 | 9 | // Set timezone location 10 | await rocketNodeManager.connect(txOptions.from).setTimezoneLocation(timezoneLocation, txOptions); 11 | 12 | // Get timezone location 13 | let nodeTimezoneLocation = await rocketNodeManager.getNodeTimezoneLocation(txOptions.from.address); 14 | 15 | // Check 16 | assert.strictEqual(nodeTimezoneLocation, timezoneLocation, 'Incorrect updated timezone location'); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/interface/dao/security/RocketDAOSecurityInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | interface RocketDAOSecurityInterface { 5 | function getMemberQuorumVotesRequired() external view returns (uint256); 6 | function getMemberIsValid(address _nodeAddress) external view returns (bool); 7 | function getMemberAt(uint256 _index) external view returns (address); 8 | function getMemberCount() external view returns (uint256); 9 | function getMemberID(address _nodeAddress) external view returns (string memory); 10 | function getMemberJoinedTime(address _nodeAddress) external view returns (uint256); 11 | function getMemberProposalExecutedTime(string memory _proposalType, address _nodeAddress) external view returns (uint256); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interface/token/RocketTokenRETHInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../util/IERC20.sol"; 6 | 7 | interface RocketTokenRETHInterface is IERC20 { 8 | function getEthValue(uint256 _rethAmount) external view returns (uint256); 9 | function getRethValue(uint256 _ethAmount) external view returns (uint256); 10 | function getExchangeRate() external view returns (uint256); 11 | function getTotalCollateral() external view returns (uint256); 12 | function getCollateralRate() external view returns (uint256); 13 | function depositExcess() external payable; 14 | function depositExcessCollateral() external; 15 | function mint(uint256 _ethAmount, address _to) external; 16 | function burn(uint256 _rethAmount) external; 17 | } 18 | -------------------------------------------------------------------------------- /contracts/interface/rewards/RocketRewardsRelayInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | interface RocketRewardsRelayInterface { 6 | function relayRewards(uint256 _intervalIndex, bytes32 _merkleRoot, uint256 _rewardsRPL, uint256 _rewardsETH) external; 7 | function claim(address _nodeAddress, uint256[] calldata _intervalIndex, uint256[] calldata _amountRPL, uint256[] calldata _amountETH, bytes32[][] calldata _merkleProof) external; 8 | function claimAndStake(address _nodeAddress, uint256[] calldata _intervalIndex, uint256[] calldata _amountRPL, uint256[] calldata _amountETH, bytes32[][] calldata _merkleProof, uint256 _stakeAmount) external; 9 | function isClaimed(uint256 _intervalIndex, address _claimer) external view returns (bool); 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/settings/RocketDAONodeTrustedSettingsMinipoolInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedSettingsMinipoolInterface { 6 | function getScrubPeriod() external view returns(uint256); 7 | function getPromotionScrubPeriod() external view returns(uint256); 8 | function getScrubQuorum() external view returns(uint256); 9 | function getCancelBondReductionQuorum() external view returns(uint256); 10 | function getScrubPenaltyEnabled() external view returns(bool); 11 | function isWithinBondReductionWindow(uint256 _time) external view returns (bool); 12 | function getBondReductionWindowStart() external view returns (uint256); 13 | function getBondReductionWindowLength() external view returns (uint256); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/interface/network/RocketNetworkBalancesInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | pragma abicoder v2; 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketNetworkBalancesInterface { 6 | function getBalancesBlock() external view returns (uint256); 7 | function getTotalETHBalance() external view returns (uint256); 8 | function getStakingETHBalance() external view returns (uint256); 9 | function getTotalRETHSupply() external view returns (uint256); 10 | function getETHUtilizationRate() external view returns (uint256); 11 | function submitBalances(uint256 _block, uint256 _slotTimestamp, uint256 _total, uint256 _staking, uint256 _rethSupply) external; 12 | function executeUpdateBalances(uint256 _block, uint256 _slotTimestamp, uint256 _totalEth, uint256 _stakingEth, uint256 _rethSupply) external; 13 | } 14 | -------------------------------------------------------------------------------- /contracts/interface/network/RocketNetworkVotingInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | interface RocketNetworkVotingInterface { 5 | function initialiseVotingFor(address _nodeAddress) external; 6 | function initialiseVoting() external; 7 | function initialiseVotingWithDelegate(address _delegate) external; 8 | function getVotingInitialised(address _nodeAddress) external view returns (bool); 9 | function getNodeCount(uint32 _block) external view returns (uint256); 10 | function getVotingPower(address _nodeAddress, uint32 _block) external view returns (uint256); 11 | function setDelegate(address _newDelegate) external; 12 | function getDelegate(address _nodeAddress, uint32 _block) external view returns (address); 13 | function getCurrentDelegate(address _nodeAddress) external view returns (address); 14 | } -------------------------------------------------------------------------------- /contracts/interface/token/RocketTokenRPLInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../util/IERC20.sol"; 6 | 7 | interface RocketTokenRPLInterface is IERC20 { 8 | function getInflationCalcTime() external view returns(uint256); 9 | function getInflationIntervalTime() external view returns(uint256); 10 | function getInflationIntervalRate() external view returns(uint256); 11 | function getInflationIntervalsPassed() external view returns(uint256); 12 | function getInflationIntervalStartTime() external view returns(uint256); 13 | function getInflationRewardsContractAddress() external view returns(address); 14 | function inflationCalculate() external view returns (uint256); 15 | function inflationMintTokens() external returns (uint256); 16 | function swapTokens(uint256 _amount) external; 17 | } 18 | -------------------------------------------------------------------------------- /contracts/interface/network/RocketNetworkSnapshotsInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | struct Checkpoint224 { 5 | uint32 _block; 6 | uint224 _value; 7 | } 8 | 9 | /// @notice Accounting for snapshotting of values based on block numbers 10 | interface RocketNetworkSnapshotsInterface { 11 | function push(bytes32 _key, uint224 _value) external; 12 | function length(bytes32 _key) external view returns (uint256); 13 | function latest(bytes32 _key) external view returns (bool, uint32, uint224); 14 | function latestBlock(bytes32 _key) external view returns (uint32); 15 | function latestValue(bytes32 _key) external view returns (uint224); 16 | function lookup(bytes32 _key, uint32 _block) external view returns (uint224); 17 | function lookupRecent(bytes32 _key, uint32 _block, uint256 _recency) external view returns (uint224); 18 | } 19 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsProposalsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolSettingsProposalsInterface { 6 | function getVotePhase1Time() external view returns(uint256); 7 | function getVotePhase2Time() external view returns(uint256); 8 | function getVoteDelayTime() external view returns(uint256); 9 | function getExecuteTime() external view returns(uint256); 10 | function getProposalBond() external view returns(uint256); 11 | function getChallengeBond() external view returns(uint256); 12 | function getChallengePeriod() external view returns(uint256); 13 | function getProposalQuorum() external view returns (uint256); 14 | function getProposalVetoQuorum() external view returns (uint256); 15 | function getProposalMaxBlockAge() external view returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /test/_helpers/console.js: -------------------------------------------------------------------------------- 1 | // Calls func and suppresses all output to stdout and stderr unless and error occurs 2 | export async function suppressLog(func) { 3 | let stdout = process.stdout.write; 4 | let stderr = process.stderr.write; 5 | 6 | let logs = []; 7 | 8 | process.stdout.write = function() { 9 | logs.push(['stdout', arguments]); 10 | }; 11 | process.stderr.write = function() { 12 | logs.push(['stderr', arguments]); 13 | }; 14 | 15 | let result; 16 | try { 17 | result = await func(); 18 | } catch (e) { 19 | process.stdout.write = stdout; 20 | process.stderr.write = stderr; 21 | 22 | for (const log of logs) { 23 | process[log[0]].write.apply(process[log[0]], log[1]); 24 | } 25 | throw e; 26 | } 27 | 28 | process.stdout.write = stdout; 29 | process.stderr.write = stderr; 30 | 31 | return result; 32 | } -------------------------------------------------------------------------------- /test/_helpers/deployment.js: -------------------------------------------------------------------------------- 1 | /*** Dependencies ********************/ 2 | import { artifacts } from '../_utils/artifacts'; 3 | import { RocketPoolDeployer } from './deployer'; 4 | 5 | const hre = require('hardhat'); 6 | const ethers = hre.ethers; 7 | 8 | // Development helper contracts 9 | const revertOnTransfer = artifacts.require('RevertOnTransfer'); 10 | const rocketNodeDepositLEB4 = artifacts.require('RocketNodeDepositLEB4'); 11 | 12 | // Deploy Rocket Pool 13 | export async function deployRocketPool() { 14 | const [signer] = await ethers.getSigners(); 15 | const deployer = new RocketPoolDeployer(signer, { logging: false }); 16 | await deployer.deploy(); 17 | 18 | let instance = await revertOnTransfer.new(); 19 | revertOnTransfer.setAsDeployed(instance); 20 | 21 | instance = await rocketNodeDepositLEB4.new(deployer.rocketStorageInstance.target); 22 | rocketNodeDepositLEB4.setAsDeployed(instance); 23 | } 24 | -------------------------------------------------------------------------------- /contracts/interface/RocketVaultInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | import "./util/IERC20Burnable.sol"; 5 | 6 | interface RocketVaultInterface { 7 | function balanceOf(string memory _networkContractName) external view returns (uint256); 8 | function depositEther() external payable; 9 | function withdrawEther(uint256 _amount) external; 10 | function depositToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external; 11 | function withdrawToken(address _withdrawalAddress, IERC20 _tokenAddress, uint256 _amount) external; 12 | function balanceOfToken(string memory _networkContractName, IERC20 _tokenAddress) external view returns (uint256); 13 | function transferToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external; 14 | function burnToken(IERC20Burnable _tokenAddress, uint256 _amount) external; 15 | } 16 | -------------------------------------------------------------------------------- /contracts/types/MinipoolDetails.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "./MinipoolDeposit.sol"; 6 | import "./MinipoolStatus.sol"; 7 | 8 | // A struct containing all the information on-chain about a specific minipool 9 | 10 | struct MinipoolDetails { 11 | bool exists; 12 | address minipoolAddress; 13 | bytes pubkey; 14 | MinipoolStatus status; 15 | uint256 statusBlock; 16 | uint256 statusTime; 17 | bool finalised; 18 | MinipoolDeposit depositType; 19 | uint256 nodeFee; 20 | uint256 nodeDepositBalance; 21 | bool nodeDepositAssigned; 22 | uint256 userDepositBalance; 23 | bool userDepositAssigned; 24 | uint256 userDepositAssignedTime; 25 | bool useLatestDelegate; 26 | address delegate; 27 | address previousDelegate; 28 | address effectiveDelegate; 29 | uint256 penaltyCount; 30 | uint256 penaltyRate; 31 | address nodeAddress; 32 | } 33 | -------------------------------------------------------------------------------- /contracts/contract/helper/PenaltyTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../RocketBase.sol"; 6 | import "../../interface/minipool/RocketMinipoolPenaltyInterface.sol"; 7 | 8 | // THIS CONTRACT IS NOT DEPLOYED TO MAINNET 9 | 10 | // Helper contract used in unit tests that can set the penalty rate on a minipool (a feature that will be implemented at a later time) 11 | contract PenaltyTest is RocketBase { 12 | // Construct 13 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 14 | } 15 | 16 | // Sets the penalty rate for the given minipool 17 | function setPenaltyRate(address _minipoolAddress, uint256 _rate) external { 18 | RocketMinipoolPenaltyInterface rocketMinipoolPenalty = RocketMinipoolPenaltyInterface(getContractAddress("rocketMinipoolPenalty")); 19 | rocketMinipoolPenalty.setPenaltyRate(_minipoolAddress, _rate); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/_utils/beacon.js: -------------------------------------------------------------------------------- 1 | const ssz = require('@chainsafe/ssz'); 2 | const types = require('@chainsafe/lodestar-types/lib/ssz/presets/mainnet').types; 3 | 4 | 5 | // Current pubkey index 6 | let pubkeyIndex = 0; 7 | 8 | 9 | // Create a new validator pubkey 10 | export function getValidatorPubkey() { 11 | let index = ++pubkeyIndex; 12 | return Buffer.from(index.toString(16).padStart(96, '0'), 'hex'); 13 | } 14 | 15 | 16 | // Create a validator signature 17 | // TODO: implement correctly once BLS library found 18 | export function getValidatorSignature() { 19 | return Buffer.from( 20 | '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' + 21 | '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' + 22 | '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', 23 | 'hex'); 24 | } 25 | 26 | 27 | // Create validator deposit data root 28 | export function getDepositDataRoot(depositData) { 29 | return types.DepositData.hashTreeRoot(depositData); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /contracts/interface/deposit/RocketDepositPoolInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDepositPoolInterface { 6 | function getBalance() external view returns (uint256); 7 | function getNodeBalance() external view returns (uint256); 8 | function getUserBalance() external view returns (int256); 9 | function getExcessBalance() external view returns (uint256); 10 | function deposit() external payable; 11 | function getMaximumDepositAmount() external view returns (uint256); 12 | function nodeDeposit(uint256 _totalAmount) external payable; 13 | function nodeCreditWithdrawal(uint256 _amount) external; 14 | function recycleDissolvedDeposit() external payable; 15 | function recycleExcessCollateral() external payable; 16 | function recycleLiquidatedStake() external payable; 17 | function assignDeposits() external; 18 | function maybeAssignDeposits() external returns (bool); 19 | function withdrawExcessBalance(uint256 _amount) external; 20 | } 21 | -------------------------------------------------------------------------------- /contracts/contract/util/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >0.5.0 <0.9.0; 4 | 5 | /* 6 | * @dev Provides information about the current execution context, including the 7 | * sender of the transaction and its data. While these are generally available 8 | * via msg.sender and msg.data, they should not be accessed in such a direct 9 | * manner, since when dealing with GSN meta-transactions the account sending and 10 | * paying for execution may not be the actual sender (as far as an application 11 | * is concerned). 12 | * 13 | * This contract is only required for intermediate, library-like contracts. 14 | */ 15 | abstract contract Context { 16 | function _msgSender() internal view virtual returns (address payable) { 17 | return payable(msg.sender); 18 | } 19 | 20 | function _msgData() internal view virtual returns (bytes memory) { 21 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 22 | return msg.data; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/contract/dao/protocol/RocketDAOProtocolActions.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../RocketBase.sol"; 6 | import "../../../interface/RocketVaultInterface.sol"; 7 | import "../../../interface/dao/protocol/RocketDAOProtocolActionsInterface.sol"; 8 | import "../../../interface/util/IERC20Burnable.sol"; 9 | 10 | import "@openzeppelin/contracts/math/SafeMath.sol"; 11 | 12 | 13 | // The Rocket Pool Network DAO Actions - This is a placeholder for the network DAO to come 14 | contract RocketDAOProtocolActions is RocketBase, RocketDAOProtocolActionsInterface { 15 | 16 | using SafeMath for uint; 17 | 18 | // The namespace for any data stored in the network DAO (do not change) 19 | string constant daoNameSpace = "dao.protocol."; 20 | 21 | 22 | // Construct 23 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 24 | // Version 25 | version = 1; 26 | } 27 | 28 | 29 | /*** Action Methods ************************/ 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsRewardsInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | interface RocketDAOProtocolSettingsRewardsInterface { 5 | function setSettingRewardsClaimers(uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent) external; 6 | function getRewardsClaimerPerc(string memory _contractName) external view returns (uint256); 7 | function getRewardsClaimersPerc() external view returns (uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent); 8 | function getRewardsClaimersTrustedNodePerc() external view returns (uint256); 9 | function getRewardsClaimersProtocolPerc() external view returns (uint256); 10 | function getRewardsClaimersNodePerc() external view returns (uint256); 11 | function getRewardsClaimersTimeUpdated() external view returns (uint256); 12 | function getRewardsClaimIntervalPeriods() external view returns (uint256); 13 | function getRewardsClaimIntervalTime() external view returns (uint256); 14 | } 15 | -------------------------------------------------------------------------------- /contracts/types/NodeDetails.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | // A struct containing all the information on-chain about a specific node 6 | 7 | struct NodeDetails { 8 | bool exists; 9 | uint256 registrationTime; 10 | string timezoneLocation; 11 | bool feeDistributorInitialised; 12 | address feeDistributorAddress; 13 | uint256 rewardNetwork; 14 | uint256 rplStake; 15 | uint256 effectiveRPLStake; 16 | uint256 minimumRPLStake; 17 | uint256 maximumRPLStake; 18 | uint256 ethMatched; 19 | uint256 ethMatchedLimit; 20 | uint256 minipoolCount; 21 | uint256 balanceETH; 22 | uint256 balanceRETH; 23 | uint256 balanceRPL; 24 | uint256 balanceOldRPL; 25 | uint256 depositCreditBalance; 26 | uint256 distributorBalanceUserETH; 27 | uint256 distributorBalanceNodeETH; 28 | address withdrawalAddress; 29 | address pendingWithdrawalAddress; 30 | bool smoothingPoolRegistrationState; 31 | uint256 smoothingPoolRegistrationChanged; 32 | address nodeAddress; 33 | } 34 | -------------------------------------------------------------------------------- /contracts/contract/casper/compiled/Deposit.abi: -------------------------------------------------------------------------------- 1 | [{"name": "DepositEvent", "inputs": [{"type": "bytes", "name": "pubkey", "indexed": false}, {"type": "bytes", "name": "withdrawal_credentials", "indexed": false}, {"type": "bytes", "name": "amount", "indexed": false}, {"type": "bytes", "name": "signature", "indexed": false}, {"type": "bytes", "name": "index", "indexed": false}], "anonymous": false, "type": "event"}, {"outputs": [], "inputs": [], "constant": false, "payable": false, "type": "constructor"}, {"name": "get_deposit_root", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 91674}, {"name": "get_deposit_count", "outputs": [{"type": "bytes", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 10433}, {"name": "deposit", "outputs": [], "inputs": [{"type": "bytes", "name": "pubkey"}, {"type": "bytes", "name": "withdrawal_credentials"}, {"type": "bytes", "name": "signature"}, {"type": "bytes32", "name": "deposit_data_root"}], "constant": false, "payable": true, "type": "function", "gas": 1334547}] -------------------------------------------------------------------------------- /test/minipool/scenario-dissolve.js: -------------------------------------------------------------------------------- 1 | // Dissolve a minipool 2 | import { minipoolStates } from '../_helpers/minipool'; 3 | import * as assert from 'assert'; 4 | 5 | export async function dissolve(minipool, txOptions) { 6 | // Get minipool details 7 | function getMinipoolDetails() { 8 | return Promise.all([ 9 | minipool.getStatus(), 10 | minipool.getUserDepositBalance(), 11 | ]).then( 12 | ([status, userDepositBalance]) => 13 | ({ status: Number(status), userDepositBalance }), 14 | ); 15 | } 16 | 17 | // Get initial minipool details 18 | let details1 = await getMinipoolDetails(); 19 | 20 | // Dissolve 21 | await minipool.connect(txOptions.from).dissolve(txOptions); 22 | 23 | // Get updated minipool details 24 | let details2 = await getMinipoolDetails(); 25 | 26 | // Check minipool details 27 | assert.notEqual(details1.status, minipoolStates.Dissolved, 'Incorrect initial minipool status'); 28 | assert.equal(details2.status, minipoolStates.Dissolved, 'Incorrect updated minipool status'); 29 | } 30 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/RocketDAONodeTrustedProposalsInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedProposalsInterface { 6 | function propose(string memory _proposalMessage, bytes memory _payload) external returns (uint256); 7 | function vote(uint256 _proposalID, bool _support) external; 8 | function cancel(uint256 _proposalID) external; 9 | function execute(uint256 _proposalID) external; 10 | function proposalInvite(string memory _id, string memory _url, address _nodeAddress) external; 11 | function proposalLeave(address _nodeAddress) external; 12 | function proposalKick(address _nodeAddress, uint256 _rplFine) external; 13 | function proposalSettingUint(string memory _settingContractName, string memory _settingPath, uint256 _value) external; 14 | function proposalSettingBool(string memory _settingContractName, string memory _settingPath, bool _value) external; 15 | function proposalUpgrade(string memory _type, string memory _name, string memory _contractAbi, address _contractAddress) external; 16 | } 17 | -------------------------------------------------------------------------------- /contracts/interface/minipool/RocketMinipoolBondReducerInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | interface RocketMinipoolBondReducerInterface { 6 | function beginReduceBondAmount(address _minipoolAddress, uint256 _newBondAmount) external; 7 | function getReduceBondTime(address _minipoolAddress) external view returns (uint256); 8 | function getReduceBondValue(address _minipoolAddress) external view returns (uint256); 9 | function getReduceBondCancelled(address _minipoolAddress) external view returns (bool); 10 | function canReduceBondAmount(address _minipoolAddress) external view returns (bool); 11 | function voteCancelReduction(address _minipoolAddress) external; 12 | function reduceBondAmount() external returns (uint256); 13 | function getLastBondReductionTime(address _minipoolAddress) external view returns (uint256); 14 | function getLastBondReductionPrevValue(address _minipoolAddress) external view returns (uint256); 15 | function getLastBondReductionPrevNodeFee(address _minipoolAddress) external view returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /test/rewards/scenario-rewards-claim.js: -------------------------------------------------------------------------------- 1 | import { RocketDAOProtocolSettingsRewards, RocketRewardsPool } from '../../test/_utils/artifacts'; 2 | 3 | 4 | // Get the current rewards claim period in blocks 5 | export async function rewardsClaimIntervalTimeGet(txOptions) { 6 | // Load contracts 7 | const rocketDAOProtocolSettingsRewards = await RocketDAOProtocolSettingsRewards.deployed(); 8 | return await rocketDAOProtocolSettingsRewards.getClaimIntervalTime.call(); 9 | } 10 | 11 | 12 | // Get the current rewards claimers total 13 | export async function rewardsClaimersPercTotalGet(txOptions) { 14 | // Load contracts 15 | const rocketDAOProtocolSettingsRewards = await RocketDAOProtocolSettingsRewards.deployed(); 16 | return await rocketDAOProtocolSettingsRewards.getRewardsClaimersPercTotal.call(); 17 | } 18 | 19 | 20 | // Get how many seconds needed until the next claim interval 21 | export async function rewardsClaimIntervalsPassedGet(txOptions) { 22 | // Load contracts 23 | const rocketRewardsPool = await RocketRewardsPool.deployed(); 24 | return await rocketRewardsPool.getClaimIntervalsPassed.call(); 25 | } 26 | -------------------------------------------------------------------------------- /test/token/scenario-rpl-allow-fixed.js: -------------------------------------------------------------------------------- 1 | import { RocketTokenDummyRPL } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | // Allow RPL from the fixed contract to be spent 5 | export async function allowDummyRPL(to, amount, txOptions) { 6 | // Load contracts 7 | const rocketTokenDummyRPL = await RocketTokenDummyRPL.deployed(); 8 | 9 | // Get balances 10 | function getBalances() { 11 | return Promise.all([ 12 | rocketTokenDummyRPL.allowance(txOptions.from.address, to), 13 | ]).then( 14 | ([tokenAllowance]) => 15 | ({ tokenAllowance }), 16 | ); 17 | } 18 | 19 | // Get initial balances 20 | let balances1 = await getBalances(); 21 | 22 | // Mint tokens 23 | await rocketTokenDummyRPL.connect(txOptions.from).approve(to, amount, txOptions); 24 | 25 | // Get updated balances 26 | let balances2 = await getBalances(); 27 | 28 | // Calculate values 29 | let allowanceAmount = BigInt(amount); 30 | 31 | // Check balances 32 | assertBN.equal(balances2.tokenAllowance, balances1.tokenAllowance + allowanceAmount, 'Incorrect allowance for token'); 33 | } 34 | -------------------------------------------------------------------------------- /test/_utils/testing.js: -------------------------------------------------------------------------------- 1 | // Assert that a transaction reverts 2 | import * as assert from 'assert'; 3 | 4 | export async function shouldRevert(txPromise, message, expectedErrorMessage = null) { 5 | let txSuccess = false; 6 | try { 7 | await txPromise; 8 | txSuccess = true; 9 | } catch (e) { 10 | // If we don't need to match a specific error message 11 | if (!expectedErrorMessage && e.message.indexOf('VM Exception') == -1) throw e; 12 | // If we do 13 | if (expectedErrorMessage && e.message.indexOf(expectedErrorMessage) == -1) assert.fail('Expected error message not found, error received: '+e.message.replace('Returned error:', '')); 14 | } finally { 15 | if (txSuccess) assert.fail(message); 16 | } 17 | } 18 | 19 | // Allows async describe functions 20 | export default async function asyncDescribe(desc, run) { 21 | const its = {} 22 | 23 | return run((testName, func) => { 24 | its[testName] = func 25 | }).then(() => { 26 | describe(desc, () => { 27 | for (const [testName, runFunction] of Object.entries(its)) { 28 | it(testName, runFunction) 29 | } 30 | }) 31 | }) 32 | } -------------------------------------------------------------------------------- /scripts/preamble.sol: -------------------------------------------------------------------------------- 1 | /** 2 | * . 3 | * / \ 4 | * |.'.| 5 | * |'.'| 6 | * ,'| |'. 7 | * |,-'-|-'-.| 8 | * __|_| | _ _ _____ _ 9 | * | ___ \| | | | | | ___ \ | | 10 | * | |_/ /|__ ___| | _____| |_ | |_/ /__ ___ | | 11 | * | // _ \ / __| |/ / _ \ __| | __/ _ \ / _ \| | 12 | * | |\ \ (_) | (__| < __/ |_ | | | (_) | (_) | | 13 | * \_| \_\___/ \___|_|\_\___|\__| \_| \___/ \___/|_| 14 | * +---------------------------------------------------+ 15 | * | DECENTRALISED STAKING PROTOCOL FOR ETHEREUM | 16 | * +---------------------------------------------------+ 17 | * 18 | * Rocket Pool is a first-of-its-kind Ethereum staking pool protocol, designed to 19 | * be community-owned, decentralised, permissionless, & trustless. 20 | * 21 | * For more information about Rocket Pool, visit https://rocketpool.net 22 | * 23 | * Authored by the Rocket Pool Core Team 24 | * Contributors: https://github.com/rocket-pool/rocketpool/graphs/contributors 25 | * A special thanks to the Rocket Pool community for all their contributions. 26 | * 27 | */ 28 | 29 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsNetworkInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProtocolSettingsNetworkInterface { 6 | function getNodeConsensusThreshold() external view returns (uint256); 7 | function getNodePenaltyThreshold() external view returns (uint256); 8 | function getPerPenaltyRate() external view returns (uint256); 9 | function getSubmitBalancesEnabled() external view returns (bool); 10 | function getSubmitBalancesFrequency() external view returns (uint256); 11 | function getSubmitPricesEnabled() external view returns (bool); 12 | function getSubmitPricesFrequency() external view returns (uint256); 13 | function getMinimumNodeFee() external view returns (uint256); 14 | function getTargetNodeFee() external view returns (uint256); 15 | function getMaximumNodeFee() external view returns (uint256); 16 | function getNodeFeeDemandRange() external view returns (uint256); 17 | function getTargetRethCollateralRate() external view returns (uint256); 18 | function getRethDepositDelay() external view returns (uint256); 19 | function getSubmitRewardsEnabled() external view returns (bool); 20 | } 21 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('hardhat-gas-reporter'); 2 | require('solidity-coverage'); 3 | 4 | let common = require('./hardhat-common.config.js'); 5 | 6 | // Config from environment 7 | const mnemonicPhrase = process.env.MNEMONIC || 'test test test test test test test test test test test junk'; 8 | const mnemonicPassword = process.env.MNEMONIC_PASSWORD; 9 | const providerUrl = process.env.PROVIDER_URL || 'http://localhost:8545'; 10 | 11 | module.exports = Object.assign(common, { 12 | networks: { 13 | hardhat: { 14 | accounts: { 15 | count: 50, 16 | accountsBalance: '10000000000000000000000000', 17 | }, 18 | }, 19 | localhost: { 20 | host: '127.0.0.1', 21 | port: 8545, 22 | network_id: '*', 23 | }, 24 | custom: { 25 | url: `${providerUrl}`, 26 | accounts: { 27 | mnemonic: mnemonicPhrase, 28 | path: 'm/44\'/60\'/0\'/0', 29 | initialIndex: 0, 30 | count: 1, 31 | passphrase: mnemonicPassword, 32 | }, 33 | network_id: '*', 34 | }, 35 | }, 36 | gasReporter: { 37 | enabled: !!process.env.REPORT_GAS, 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /test/token/scenario-reth-transfer.js: -------------------------------------------------------------------------------- 1 | import { RocketTokenRETH } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | // Transfer rETH between accounts 5 | export async function transferReth(to, amount, txOptions) { 6 | // Load contracts 7 | const rocketTokenRETH = await RocketTokenRETH.deployed(); 8 | 9 | // Get balances 10 | function getBalances() { 11 | return Promise.all([ 12 | rocketTokenRETH.balanceOf(txOptions.from), 13 | rocketTokenRETH.balanceOf(to), 14 | ]).then( 15 | ([userFromTokenBalance, userToTokenBalance]) => 16 | ({ userFromTokenBalance, userToTokenBalance }), 17 | ); 18 | } 19 | 20 | // Get initial balances 21 | let balances1 = await getBalances(); 22 | 23 | // Transfer tokens 24 | await rocketTokenRETH.connect(txOptions.from).transfer(to, amount, txOptions); 25 | 26 | // Get updated balances 27 | let balances2 = await getBalances(); 28 | 29 | // Check balances 30 | assertBN.equal(balances2.userFromTokenBalance, balances1.userFromTokenBalance - amount, 'Incorrect updated user token balance'); 31 | assertBN.equal(balances2.userToTokenBalance, balances1.userToTokenBalance + amount, 'Incorrect updated user token balance'); 32 | } 33 | -------------------------------------------------------------------------------- /contracts/interface/dao/security/RocketDAOSecurityProposalsInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | import "../../../types/SettingType.sol"; 6 | 7 | interface RocketDAOSecurityProposalsInterface { 8 | function propose(string memory _proposalMessage, bytes memory _payload) external returns (uint256); 9 | function vote(uint256 _proposalID, bool _support) external; 10 | function cancel(uint256 _proposalID) external; 11 | function execute(uint256 _proposalID) external; 12 | 13 | function proposalSettingUint(string memory _settingContractName, string memory _settingPath, uint256 _value) external; 14 | function proposalSettingBool(string memory _settingContractName, string memory _settingPath, bool _value) external; 15 | function proposalSettingAddress(string memory _settingContractName, string memory _settingPath, address _value) external; 16 | 17 | function proposalInvite(string memory _id, address _memberAddress) external; 18 | function proposalKick(address _memberAddress) external; 19 | function proposalKickMulti(address[] calldata _memberAddresses) external; 20 | function proposalReplace(address _existingMemberAddress, string calldata _newMemberId, address _newMemberAddress) external; 21 | } 22 | -------------------------------------------------------------------------------- /test/_helpers/auction.js: -------------------------------------------------------------------------------- 1 | import { RocketAuctionManager } from '../_utils/artifacts'; 2 | 3 | // Get lot start/end blocks 4 | export async function getLotStartBlock(lotIndex) { 5 | const rocketAuctionManager = await RocketAuctionManager.deployed(); 6 | return rocketAuctionManager.getLotStartBlock(lotIndex); 7 | } 8 | 9 | export async function getLotEndBlock(lotIndex) { 10 | const rocketAuctionManager = await RocketAuctionManager.deployed(); 11 | return rocketAuctionManager.getLotEndBlock(lotIndex); 12 | } 13 | 14 | // Get lot price at a block 15 | export async function getLotPriceAtBlock(lotIndex, block) { 16 | const rocketAuctionManager = await RocketAuctionManager.deployed(); 17 | return rocketAuctionManager.getLotPriceAtBlock(lotIndex, block); 18 | } 19 | 20 | // Create a new lot for auction 21 | export async function auctionCreateLot(txOptions) { 22 | const rocketAuctionManager = await RocketAuctionManager.deployed(); 23 | await rocketAuctionManager.connect(txOptions.from).createLot(txOptions); 24 | } 25 | 26 | // Place a bid on a lot 27 | export async function auctionPlaceBid(lotIndex, txOptions) { 28 | const rocketAuctionManager = await RocketAuctionManager.deployed(); 29 | await rocketAuctionManager.connect(txOptions.from).placeBid(lotIndex, txOptions); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /contracts/interface/minipool/RocketMinipoolQueueInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../types/MinipoolDeposit.sol"; 6 | 7 | interface RocketMinipoolQueueInterface { 8 | function getTotalLength() external view returns (uint256); 9 | function getContainsLegacy() external view returns (bool); 10 | function getLengthLegacy(MinipoolDeposit _depositType) external view returns (uint256); 11 | function getLength() external view returns (uint256); 12 | function getTotalCapacity() external view returns (uint256); 13 | function getEffectiveCapacity() external view returns (uint256); 14 | function getNextCapacityLegacy() external view returns (uint256); 15 | function getNextDepositLegacy() external view returns (MinipoolDeposit, uint256); 16 | function enqueueMinipool(address _minipool) external; 17 | function dequeueMinipoolByDepositLegacy(MinipoolDeposit _depositType) external returns (address minipoolAddress); 18 | function dequeueMinipools(uint256 _maxToDequeue) external returns (address[] memory minipoolAddress); 19 | function removeMinipool(MinipoolDeposit _depositType) external; 20 | function getMinipoolAt(uint256 _index) external view returns(address); 21 | function getMinipoolPosition(address _minipool) external view returns (int256); 22 | } 23 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/settings/RocketDAOProtocolSettingsMinipoolInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../../../types/MinipoolDeposit.sol"; 6 | 7 | interface RocketDAOProtocolSettingsMinipoolInterface { 8 | function getLaunchBalance() external view returns (uint256); 9 | function getPreLaunchValue() external pure returns (uint256); 10 | function getDepositUserAmount(MinipoolDeposit _depositType) external view returns (uint256); 11 | function getFullDepositUserAmount() external view returns (uint256); 12 | function getHalfDepositUserAmount() external view returns (uint256); 13 | function getVariableDepositAmount() external view returns (uint256); 14 | function getSubmitWithdrawableEnabled() external view returns (bool); 15 | function getBondReductionEnabled() external view returns (bool); 16 | function getLaunchTimeout() external view returns (uint256); 17 | function getMaximumCount() external view returns (uint256); 18 | function isWithinUserDistributeWindow(uint256 _time) external view returns (bool); 19 | function hasUserDistributeWindowPassed(uint256 _time) external view returns (bool); 20 | function getUserDistributeWindowStart() external view returns (uint256); 21 | function getUserDistributeWindowLength() external view returns (uint256); 22 | } 23 | -------------------------------------------------------------------------------- /test/token/scenario-rpl-mint-fixed.js: -------------------------------------------------------------------------------- 1 | import { RocketTokenDummyRPL } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | // Mint RPL from the dummy RPL contract to simulate a user having existing fixed supply RPL 5 | export async function mintDummyRPL(to, amount, txOptions) { 6 | // Load contracts 7 | const rocketTokenDummyRPL = await RocketTokenDummyRPL.deployed(); 8 | 9 | // Get balances 10 | function getBalances() { 11 | return Promise.all([ 12 | rocketTokenDummyRPL.totalSupply(), 13 | rocketTokenDummyRPL.balanceOf(to), 14 | ]).then( 15 | ([tokenSupply, userTokenBalance]) => 16 | ({ tokenSupply, userTokenBalance }), 17 | ); 18 | } 19 | 20 | // Get initial balances 21 | let balances1 = await getBalances(); 22 | 23 | // Mint tokens 24 | await rocketTokenDummyRPL.connect(txOptions.from).mint(to, amount, txOptions); 25 | 26 | // Get updated balances 27 | let balances2 = await getBalances(); 28 | 29 | // Calculate values 30 | let mintAmount = BigInt(amount); 31 | 32 | // Check balances 33 | assertBN.equal(balances2.tokenSupply, balances1.tokenSupply + mintAmount, 'Incorrect updated token supply'); 34 | assertBN.equal(balances2.userTokenBalance, balances1.userTokenBalance + mintAmount, 'Incorrect updated user token balance'); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/contract/util/ERC20Burnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >0.5.0 <0.9.0; 4 | 5 | import "./ERC20.sol"; 6 | import "./Context.sol"; 7 | 8 | /** 9 | * @dev Extension of {ERC20} that allows token holders to destroy both their own 10 | * tokens and those that they have an allowance for, in a way that can be 11 | * recognized off-chain (via event analysis). 12 | */ 13 | abstract contract ERC20Burnable is Context, ERC20 { 14 | using SafeMath for uint256; 15 | 16 | /** 17 | * @dev Destroys `amount` tokens from the caller. 18 | * 19 | * See {ERC20-_burn}. 20 | */ 21 | function burn(uint256 amount) public virtual { 22 | _burn(_msgSender(), amount); 23 | } 24 | 25 | /** 26 | * @dev Destroys `amount` tokens from `account`, deducting from the caller's 27 | * allowance. 28 | * 29 | * See {ERC20-_burn} and {ERC20-allowance}. 30 | * 31 | * Requirements: 32 | * 33 | * - the caller must have allowance for ``accounts``'s tokens of at least 34 | * `amount`. 35 | */ 36 | function burnFrom(address account, uint256 amount) public virtual { 37 | uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); 38 | 39 | _approve(account, _msgSender(), decreasedAllowance); 40 | _burn(account, amount); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /hardhat-common.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | require('@nomicfoundation/hardhat-ethers'); 3 | 4 | // Importing babel to be able to use ES6 imports 5 | require('@babel/register')({ 6 | presets: [ 7 | ['@babel/preset-env', { 8 | 'targets': { 9 | 'node': '16', 10 | }, 11 | }], 12 | ], 13 | only: [/test|scripts/], 14 | retainLines: true, 15 | }); 16 | require('@babel/polyfill'); 17 | 18 | /** @type import('hardhat/config').HardhatUserConfig */ 19 | module.exports = { 20 | solidity: { 21 | compilers: [ 22 | { 23 | version: '0.7.6', 24 | settings: { 25 | optimizer: { 26 | enabled: true, 27 | runs: 15000, 28 | }, 29 | }, 30 | }, 31 | { 32 | version: '0.8.18', 33 | settings: { 34 | optimizer: { 35 | enabled: true, 36 | runs: 15000, 37 | }, 38 | }, 39 | }, 40 | ], 41 | }, 42 | networks: {}, 43 | paths: { 44 | sources: './contracts', 45 | tests: './test', 46 | cache: './cache', 47 | artifacts: './artifacts', 48 | }, 49 | mocha: { 50 | timeout: 0, 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /contracts/contract/node/RocketNodeDistributor.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../RocketBase.sol"; 6 | import "./RocketNodeDistributorStorageLayout.sol"; 7 | 8 | contract RocketNodeDistributor is RocketNodeDistributorStorageLayout { 9 | bytes32 immutable distributorStorageKey; 10 | 11 | constructor(address _nodeAddress, address _rocketStorage) { 12 | rocketStorage = RocketStorageInterface(_rocketStorage); 13 | nodeAddress = _nodeAddress; 14 | 15 | // Precompute storage key for rocketNodeDistributorDelegate 16 | distributorStorageKey = keccak256(abi.encodePacked("contract.address", "rocketNodeDistributorDelegate")); 17 | } 18 | 19 | // Allow contract to receive ETH without making a delegated call 20 | receive() external payable {} 21 | 22 | // Delegates all transactions to the target supplied during creation 23 | fallback() external payable { 24 | address _target = rocketStorage.getAddress(distributorStorageKey); 25 | assembly { 26 | calldatacopy(0x0, 0x0, calldatasize()) 27 | let result := delegatecall(gas(), _target, 0x0, calldatasize(), 0x0, 0) 28 | returndatacopy(0x0, 0x0, returndatasize()) 29 | switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/interface/rewards/claims/RocketClaimDAOInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | interface RocketClaimDAOInterface { 6 | // Struct for returning data about a payment contract 7 | struct PaymentContract { 8 | address recipient; 9 | uint256 amountPerPeriod; 10 | uint256 periodLength; 11 | uint256 lastPaymentTime; 12 | uint256 numPeriods; 13 | uint256 periodsPaid; 14 | } 15 | 16 | function getContractExists(string calldata _contractName) external view returns (bool); 17 | function getContract(string calldata _contractName) external view returns (PaymentContract memory); 18 | function getBalance(address _recipientAddress) external view returns (uint256); 19 | function spend(string memory _invoiceID, address _recipientAddress, uint256 _amount) external; 20 | function newContract(string memory _contractName, address _recipientAddress, uint256 _amountPerPeriod, uint256 _periodLength, uint256 _startTime, uint256 _numPeriods) external; 21 | function updateContract(string memory _contractName, address _recipientAddress, uint256 _amountPerPeriod, uint256 _periodLength, uint256 _numPeriods) external; 22 | function withdrawBalance(address _recipientAddress) external; 23 | function payOutContracts(string[] calldata _contractNames) external; 24 | function payOutContractsAndWithdraw(string[] calldata _contractNames) external; 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rocketpool", 3 | "version": "3.0.0", 4 | "description": "A next generation decentralised Ethereum 2.0 proof of stake protocol", 5 | "repository": "git@github.com:rocket-pool/rocketpool.git", 6 | "author": "David Rugendyke ", 7 | "scripts": { 8 | "compile": "hardhat compile", 9 | "deploy": "hardhat run scripts/deploy.js", 10 | "test": "hardhat test --bail", 11 | "test-gas": "REPORT_GAS=1 hardhat test --bail", 12 | "test-fork": "hardhat test --config hardhat-fork.config.js --bail", 13 | "coverage": "SOLIDITY_COVERAGE=true hardhat coverage", 14 | "slither": "slither --filter-paths node_modules,old --exclude conformance-to-solidity-naming-conventions ." 15 | }, 16 | "devDependencies": { 17 | "@babel/polyfill": "^7.12.1", 18 | "@babel/preset-env": "^7.20.2", 19 | "@babel/register": "^7.18.9", 20 | "@babel/runtime": "^7.20.1", 21 | "@chainsafe/lodestar-types": "^0.5.0", 22 | "@chainsafe/ssz": "^0.6.1", 23 | "@nomicfoundation/hardhat-ethers": "^3.0.8", 24 | "@nomicfoundation/hardhat-network-helpers": "^1.0.12", 25 | "@openzeppelin/contracts": "^3.4.0", 26 | "@openzeppelin4/contracts": "npm:@openzeppelin/contracts@^4.9.2", 27 | "axios": "1.8.2", 28 | "dotenv": "^16.0.3", 29 | "ethers": "^6.13.3", 30 | "hardhat": "2.22.12", 31 | "hardhat-gas-reporter": "^2.2.1", 32 | "mocha": "^10.1.0", 33 | "pako": "^1.0.6", 34 | "solidity-coverage": "^0.8.13" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/_helpers/settings.js: -------------------------------------------------------------------------------- 1 | import { 2 | RocketDAOProtocolSettingsAuction, 3 | RocketDAOProtocolSettingsDeposit, 4 | RocketDAOProtocolSettingsMinipool, 5 | RocketDAOProtocolSettingsNetwork, 6 | RocketDAOProtocolSettingsNode, 7 | } from '../_utils/artifacts'; 8 | 9 | // Auction settings 10 | export async function getAuctionSetting(setting) { 11 | const rocketAuctionSettings = await RocketDAOProtocolSettingsAuction.deployed(); 12 | return rocketAuctionSettings['get' + setting](); 13 | } 14 | 15 | // Deposit settings 16 | export async function getDepositSetting(setting) { 17 | const rocketDAOProtocolSettingsDeposit = await RocketDAOProtocolSettingsDeposit.deployed(); 18 | return rocketDAOProtocolSettingsDeposit['get' + setting](); 19 | } 20 | 21 | // Minipool settings 22 | export async function getMinipoolSetting(setting) { 23 | const rocketDAOProtocolSettingsMinipool = await RocketDAOProtocolSettingsMinipool.deployed(); 24 | return rocketDAOProtocolSettingsMinipool['get' + setting](); 25 | } 26 | 27 | // Network settings 28 | export async function getNetworkSetting(setting) { 29 | const rocketDAOProtocolSettingsNetwork = await RocketDAOProtocolSettingsNetwork.deployed(); 30 | return rocketDAOProtocolSettingsNetwork['get' + setting](); 31 | } 32 | 33 | // Node settings 34 | export async function getNodeSetting(setting) { 35 | const rocketDAOProtocolSettingsNode = await RocketDAOProtocolSettingsNode.deployed(); 36 | return rocketDAOProtocolSettingsNode['get' + setting](); 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /test/_utils/contract.js: -------------------------------------------------------------------------------- 1 | // Dependencies 2 | const pako = require('pako'); 3 | 4 | 5 | // Get arbitrary contract events from a transaction result 6 | // txReceipt is the receipt returned from the transaction call 7 | // contractAddress is the address of the contract to retrieve events for 8 | // eventName is the name of the event to retrieve 9 | // eventParams is an array of objects with string 'type' and 'name' keys and an optional boolean 'indexed' key 10 | export function getTxContractEvents(txReceipt, contractAddress, eventName, eventParams) { 11 | return txReceipt.receipt.rawLogs 12 | .filter(log => (log.address.toLowerCase() == contractAddress.toLowerCase())) 13 | .filter(log => (log.topics[0] == web3.utils.soliditySha3(eventName + '(' + eventParams.map(param => param.type).join(',') + ')'))) 14 | .map(log => web3.eth.abi.decodeLog(eventParams.map(param => { 15 | let decodeParam = Object.assign({}, param); 16 | if (decodeParam.indexed && (decodeParam.type == 'string' || decodeParam.type == 'bytes')) decodeParam.type = 'bytes32'; // Issues decoding indexed string and bytes parameters 17 | return decodeParam; 18 | }), log.data, log.topics.slice(1))); 19 | } 20 | 21 | 22 | // Compress / decompress contract ABIs 23 | export function compressABI(abi) { 24 | return Buffer.from(pako.deflate(JSON.stringify(abi))).toString('base64'); 25 | } 26 | export function decompressABI(abi) { 27 | return JSON.parse(pako.inflate(Buffer.from(abi, 'base64'), {to: 'string'})); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /test/node/scenario-register.js: -------------------------------------------------------------------------------- 1 | import { RocketNodeManager } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | import * as assert from 'assert'; 4 | 5 | // Register a node 6 | export async function register(timezoneLocation, txOptions) { 7 | // Load contracts 8 | const rocketNodeManager = await RocketNodeManager.deployed(); 9 | 10 | // Get node details 11 | function getNodeDetails(nodeAddress) { 12 | return Promise.all([ 13 | rocketNodeManager.getNodeExists(nodeAddress), 14 | rocketNodeManager.getNodeTimezoneLocation(nodeAddress), 15 | ]).then( 16 | ([exists, timezoneLocation]) => 17 | ({ exists, timezoneLocation }), 18 | ); 19 | } 20 | 21 | // Get initial node index 22 | let nodeCount1 = await rocketNodeManager.getNodeCount(); 23 | 24 | // Register 25 | await rocketNodeManager.connect(txOptions.from).registerNode(timezoneLocation, txOptions); 26 | 27 | // Get updated node index & node details 28 | let nodeCount2 = await rocketNodeManager.getNodeCount(); 29 | let [lastNodeAddress, details] = await Promise.all([ 30 | rocketNodeManager.getNodeAt(nodeCount2 - 1n), 31 | getNodeDetails(txOptions.from.address), 32 | ]); 33 | 34 | // Check details 35 | assertBN.equal(nodeCount2, nodeCount1 + 1n, 'Incorrect updated node count'); 36 | assert.strictEqual(lastNodeAddress, txOptions.from.address, 'Incorrect updated node index'); 37 | assert.equal(details.exists, true, 'Incorrect node exists flag'); 38 | assert.strictEqual(details.timezoneLocation, timezoneLocation, 'Incorrect node timezone location'); 39 | } 40 | -------------------------------------------------------------------------------- /scripts/etherscan-verify.js: -------------------------------------------------------------------------------- 1 | import { EtherscanVerifier } from '../test/_helpers/verify'; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | 5 | const preamble = process.env.PREAMBLE || null; 6 | const etherscanApiKey = process.env.ETHERSCAN_API_KEY || null; 7 | const deployment = process.env.DEPLOYMENT || 'latest'; 8 | 9 | async function verify() { 10 | const deploymentData = JSON.parse(fs.readFileSync('deployments' + path.sep + deployment + '.json').toString('utf-8')); 11 | const chain = deploymentData.chain; 12 | 13 | console.log(`Chain: ${chain}`); 14 | console.log('\n'); 15 | 16 | // Verify all deployed contracts 17 | const verifierOpts = { 18 | chain: chain, 19 | preamble: preamble !== null ? fs.readFileSync(process.cwd() + path.sep + preamble, 'utf8') : '', 20 | apiKey: etherscanApiKey, 21 | }; 22 | const verifier = new EtherscanVerifier(deploymentData.buildInfos, verifierOpts); 23 | const verificationResults = await verifier.verifyAll(deploymentData.verification); 24 | 25 | console.log(); 26 | console.log('# Verification results'); 27 | console.log(); 28 | 29 | for (const contract in verificationResults) { 30 | const guid = verificationResults[contract]; 31 | if (guid === null) { 32 | console.log(` - ${contract}: Failed to submit`); 33 | } else if (guid === undefined) { 34 | console.log(` - ${contract}: Already verified`); 35 | } else { 36 | const status = await verifier.getVerificationStatus(verificationResults[contract]); 37 | console.log(` - ${contract}: ${status.result}`); 38 | } 39 | } 40 | } 41 | 42 | verify().then(() => process.exit()); 43 | 44 | -------------------------------------------------------------------------------- /contracts/interface/rewards/RocketRewardsPoolInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | pragma abicoder v2; 3 | 4 | import "../../types/RewardSubmission.sol"; 5 | 6 | // SPDX-License-Identifier: GPL-3.0-only 7 | 8 | interface RocketRewardsPoolInterface { 9 | function getRewardIndex() external view returns(uint256); 10 | function getRPLBalance() external view returns(uint256); 11 | function getPendingRPLRewards() external view returns (uint256); 12 | function getPendingETHRewards() external view returns (uint256); 13 | function getClaimIntervalTimeStart() external view returns(uint256); 14 | function getClaimIntervalTime() external view returns(uint256); 15 | function getClaimIntervalsPassed() external view returns(uint256); 16 | function getClaimIntervalExecutionBlock(uint256 _interval) external view returns(uint256); 17 | function getClaimIntervalExecutionAddress(uint256 _interval) external view returns(address); 18 | function getClaimingContractPerc(string memory _claimingContract) external view returns(uint256); 19 | function getClaimingContractsPerc(string[] memory _claimingContracts) external view returns (uint256[] memory); 20 | function getTrustedNodeSubmitted(address _trustedNodeAddress, uint256 _rewardIndex) external view returns (bool); 21 | function getSubmissionFromNodeExists(address _trustedNodeAddress, RewardSubmission calldata _submission) external view returns (bool); 22 | function getSubmissionCount(RewardSubmission calldata _submission) external view returns (uint256); 23 | function submitRewardSnapshot(RewardSubmission calldata _submission) external; 24 | function executeRewardSnapshot(RewardSubmission calldata _submission) external; 25 | } 26 | -------------------------------------------------------------------------------- /test/token/scenario-reth-burn.js: -------------------------------------------------------------------------------- 1 | import { RocketTokenRETH } from '../../test/_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | // Burn rETH for ETH 5 | export async function burnReth(amount, txOptions) { 6 | // Load contracts 7 | const rocketTokenRETH = await RocketTokenRETH.deployed(); 8 | 9 | // Get balances 10 | function getBalances() { 11 | return Promise.all([ 12 | rocketTokenRETH.totalSupply(), 13 | rocketTokenRETH.balanceOf(txOptions.from), 14 | ethers.provider.getBalance(txOptions.from), 15 | ]).then( 16 | ([tokenSupply, userTokenBalance, userEthBalance]) => 17 | ({ tokenSupply, userTokenBalance, userEthBalance }), 18 | ); 19 | } 20 | 21 | // Get initial balances 22 | let balances1 = await getBalances(); 23 | 24 | // Set gas price 25 | let gasPrice = '20'.gwei; 26 | txOptions.gasPrice = gasPrice; 27 | 28 | // Burn tokens & get tx fee 29 | let tx = await rocketTokenRETH.connect(txOptions.from).burn(amount, txOptions); 30 | const txReceipt = await tx.wait(); 31 | let txFee = gasPrice * txReceipt.gasUsed; 32 | 33 | // Get updated balances 34 | let balances2 = await getBalances(); 35 | 36 | // Calculate values 37 | let burnAmount = amount; 38 | let expectedEthTransferred = await rocketTokenRETH.getEthValue(burnAmount); 39 | 40 | // Check balances 41 | assertBN.equal(balances2.tokenSupply, balances1.tokenSupply - burnAmount, 'Incorrect updated token supply'); 42 | assertBN.equal(balances2.userTokenBalance, balances1.userTokenBalance - burnAmount, 'Incorrect updated user token balance'); 43 | assertBN.equal(balances2.userEthBalance, balances1.userEthBalance + expectedEthTransferred - txFee, 'Incorrect updated user ETH balance'); 44 | } 45 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/RocketDAOProtocolProposalsInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | import "../../../types/SettingType.sol"; 6 | 7 | interface RocketDAOProtocolProposalsInterface { 8 | function proposalSettingMulti(string[] memory _settingContractNames, string[] memory _settingPaths, SettingType[] memory _types, bytes[] memory _data) external; 9 | function proposalSettingUint(string memory _settingContractName, string memory _settingPath, uint256 _value) external; 10 | function proposalSettingBool(string memory _settingContractName, string memory _settingPath, bool _value) external; 11 | function proposalSettingAddress(string memory _settingContractName, string memory _settingPath, address _value) external; 12 | function proposalSettingRewardsClaimers(uint256 _trustedNodePercent, uint256 _protocolPercent, uint256 _nodePercent) external; 13 | 14 | function proposalTreasuryOneTimeSpend(string memory _invoiceID, address _recipientAddress, uint256 _amount) external; 15 | function proposalTreasuryNewContract(string memory _contractName, address _recipientAddress, uint256 _amountPerPeriod, uint256 _periodLength, uint256 _startTime, uint256 _numPeriods) external; 16 | function proposalTreasuryUpdateContract(string memory _contractName, address _recipientAddress, uint256 _amountPerPeriod, uint256 _periodLength, uint256 _numPeriods) external; 17 | 18 | function proposalSecurityInvite(string memory _id, address _memberAddress) external; 19 | function proposalSecurityKick(address _memberAddress) external; 20 | function proposalSecurityKickMulti(address[] calldata _memberAddresses) external; 21 | function proposalSecurityReplace(address _existingMemberAddress, string calldata _id, address _newMemberAddress) external; 22 | } 23 | -------------------------------------------------------------------------------- /contracts/interface/node/RocketNodeDepositInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../types/MinipoolDeposit.sol"; 6 | 7 | interface RocketNodeDepositInterface { 8 | function getNodeDepositCredit(address _nodeAddress) external view returns (uint256); 9 | function getNodeEthBalance(address _nodeAddress) external view returns (uint256); 10 | function getNodeCreditAndBalance(address _nodeAddress) external view returns (uint256); 11 | function getNodeUsableCreditAndBalance(address _nodeAddress) external view returns (uint256); 12 | function getNodeUsableCredit(address _nodeAddress) external view returns (uint256); 13 | function increaseDepositCreditBalance(address _nodeOperator, uint256 _amount) external; 14 | function depositEthFor(address _nodeAddress) external payable; 15 | function withdrawEth(address _nodeAddress, uint256 _amount) external; 16 | function deposit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable; 17 | function depositWithCredit(uint256 _depositAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot, uint256 _salt, address _expectedMinipoolAddress) external payable; 18 | function isValidDepositAmount(uint256 _amount) external pure returns (bool); 19 | function getDepositAmounts() external pure returns (uint256[] memory); 20 | function createVacantMinipool(uint256 _bondAmount, uint256 _minimumNodeFee, bytes calldata _validatorPubkey, uint256 _salt, address _expectedMinipoolAddress, uint256 _currentBalance) external; 21 | function increaseEthMatched(address _nodeAddress, uint256 _amount) external; 22 | } 23 | -------------------------------------------------------------------------------- /test/_helpers/bn.js: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | const hre = require('hardhat'); 4 | const ethers = hre.ethers; 5 | 6 | const _assertBN = { 7 | equal: function (actual, expected, message) { 8 | assert.strictEqual(actual, BigInt(expected), message); 9 | }, 10 | notEqual: function (actual, expected, message) { 11 | assert.notEqual(actual, BigInt(expected), message); 12 | }, 13 | isBelow: function (actual, n, message) { 14 | assert.equal(actual < BigInt(n), true, message); 15 | }, 16 | isAbove: function (actual, n, message) { 17 | assert.equal(actual > BigInt(n), true, message); 18 | }, 19 | isAtMost: function (actual, n, message) { 20 | assert.equal(actual <= BigInt(n), true, message); 21 | }, 22 | isAtLeast: function (actual, n, message) { 23 | assert.equal(actual >= BigInt(n), true, message); 24 | }, 25 | isZero: function (actual, message) { 26 | assert.strictEqual(actual, 0n, message); 27 | }, 28 | } 29 | 30 | export function injectBNHelpers() { 31 | String.prototype.__defineGetter__('ether', function () { 32 | return ethers.parseUnits(this, 'ether'); 33 | }); 34 | String.prototype.__defineGetter__('gwei', function () { 35 | return ethers.parseUnits(this, 'gwei'); 36 | }); 37 | String.prototype.__defineGetter__('BN', function () { 38 | return BigInt(this); 39 | }); 40 | Number.prototype.__defineGetter__('BN', function () { 41 | return BigInt(this); 42 | }); 43 | Number.prototype.__defineGetter__('ether', function () { 44 | return ethers.parseUnits(this.toString(), 'ether'); 45 | }); 46 | Number.prototype.__defineGetter__('gwei', function () { 47 | return ethers.parseUnits(this.toString(), 'gwei'); 48 | }); 49 | } 50 | 51 | export const assertBN = _assertBN; 52 | 53 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/RocketDAOProtocolInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | pragma abicoder v2; 3 | 4 | import "../../../types/SettingType.sol"; 5 | 6 | // SPDX-License-Identifier: GPL-3.0-only 7 | 8 | interface RocketDAOProtocolInterface { 9 | function getMemberLastProposalTime(address _nodeAddress) external view returns (uint256); 10 | function getBootstrapModeDisabled() external view returns (bool); 11 | function bootstrapSettingMulti(string[] memory _settingContractNames, string[] memory _settingPaths, SettingType[] memory _types, bytes[] memory _values) external; 12 | function bootstrapSettingUint(string memory _settingContractName, string memory _settingPath, uint256 _value) external; 13 | function bootstrapSettingBool(string memory _settingContractName, string memory _settingPath, bool _value) external; 14 | function bootstrapSettingAddress(string memory _settingContractName, string memory _settingPath, address _value) external; 15 | function bootstrapSettingClaimers(uint256 _trustedNodePerc, uint256 _protocolPerc, uint256 _nodePerc) external; 16 | function bootstrapSpendTreasury(string memory _invoiceID, address _recipientAddress, uint256 _amount) external; 17 | function bootstrapTreasuryNewContract(string memory _contractName, address _recipientAddress, uint256 _amountPerPeriod, uint256 _periodLength, uint256 _startTime, uint256 _numPeriods) external; 18 | function bootstrapTreasuryUpdateContract(string memory _contractName, address _recipientAddress, uint256 _amountPerPeriod, uint256 _periodLength, uint256 _numPeriods) external; 19 | function bootstrapSecurityInvite(string memory _id, address _memberAddress) external; 20 | function bootstrapSecurityKick(address _memberAddress) external; 21 | function bootstrapDisable(bool _confirmDisableBootstrapMode) external; 22 | function bootstrapEnableGovernance() external; 23 | } 24 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/RocketDAOProtocolVerifierInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | interface Types { 6 | enum ChallengeState { 7 | Unchallenged, 8 | Challenged, 9 | Responded, 10 | Paid 11 | } 12 | 13 | struct Proposal { 14 | address proposer; 15 | uint32 blockNumber; 16 | uint128 nodeCount; 17 | bytes32 hash; 18 | uint256 sum; 19 | } 20 | 21 | struct Node { 22 | uint256 sum; 23 | bytes32 hash; 24 | } 25 | 26 | struct Leaf { 27 | address nodeAddress; 28 | uint256 effectiveRpl; 29 | } 30 | } 31 | 32 | interface RocketDAOProtocolVerifierInterface { 33 | function getDefeatIndex(uint256 _proposalID) external view returns (uint256); 34 | function getProposalBond(uint256 _proposalID) external view returns (uint256); 35 | function getChallengeBond(uint256 _proposalID) external view returns (uint256); 36 | function getChallengePeriod(uint256 _proposalID) external view returns (uint256); 37 | function getDepthPerRound() external pure returns (uint256); 38 | function submitProposalRoot(uint256 _proposalId, address _proposer, uint32 _blockNumber, Types.Node[] memory _treeNodes) external; 39 | function burnProposalBond(uint256 _proposalID) external; 40 | function createChallenge(uint256 _proposalID, uint256 _index, Types.Node calldata _node, Types.Node[] calldata _witness) external; 41 | function submitRoot(uint256 propId, uint256 index, Types.Node[] memory nodes) external; 42 | function getChallengeState(uint256 _proposalID, uint256 _index) external view returns (Types.ChallengeState); 43 | function verifyVote(address _voter, uint256 _nodeIndex, uint256 _proposalID, uint256 _votingPower, Types.Node[] calldata _witness) external view returns (bool); 44 | } 45 | -------------------------------------------------------------------------------- /contracts/contract/rewards/RocketSmoothingPool.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | pragma abicoder v2; 3 | 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | import "../RocketBase.sol"; 7 | import "../../interface/rewards/RocketSmoothingPoolInterface.sol"; 8 | import "@openzeppelin/contracts/math/SafeMath.sol"; 9 | 10 | /* 11 | Receives priority fees and MEV via fee_recipient 12 | 13 | NOTE: This contract intentionally does not use RocketVault to store ETH because there is no way to account for ETH being 14 | added to this contract via fee_recipient. This also means if this contract is upgraded, the ETH must be manually 15 | transferred from this contract to the upgraded one. 16 | */ 17 | 18 | contract RocketSmoothingPool is RocketBase, RocketSmoothingPoolInterface { 19 | 20 | // Libs 21 | using SafeMath for uint256; 22 | 23 | // Events 24 | event EtherWithdrawn(string indexed by, address indexed to, uint256 amount, uint256 time); 25 | 26 | // Construct 27 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 28 | // Version 29 | version = 1; 30 | } 31 | 32 | // Allow receiving ETH 33 | receive() payable external {} 34 | 35 | // Withdraws ETH to given address 36 | // Only accepts calls from Rocket Pool network contracts 37 | function withdrawEther(address _to, uint256 _amount) override external onlyLatestNetworkContract { 38 | // Valid amount? 39 | require(_amount > 0, "No valid amount of ETH given to withdraw"); 40 | // Get contract name 41 | string memory contractName = getContractName(msg.sender); 42 | // Send the ETH 43 | (bool result,) = _to.call{value: _amount}(""); 44 | require(result, "Failed to withdraw ETH"); 45 | // Emit ether withdrawn event 46 | emit EtherWithdrawn(contractName, _to, _amount, block.timestamp); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/contract/node/RocketNodeDistributorFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../RocketBase.sol"; 6 | import "./RocketNodeDistributor.sol"; 7 | import "./RocketNodeDistributorStorageLayout.sol"; 8 | import "../../interface/node/RocketNodeDistributorFactoryInterface.sol"; 9 | 10 | contract RocketNodeDistributorFactory is RocketBase, RocketNodeDistributorFactoryInterface { 11 | // Events 12 | event ProxyCreated(address _address); 13 | 14 | // Construct 15 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 16 | version = 1; 17 | } 18 | 19 | function getProxyBytecode() override public pure returns (bytes memory) { 20 | return type(RocketNodeDistributor).creationCode; 21 | } 22 | 23 | // Calculates the predetermined distributor contract address from given node address 24 | function getProxyAddress(address _nodeAddress) override external view returns(address) { 25 | bytes memory contractCode = getProxyBytecode(); 26 | bytes memory initCode = abi.encodePacked(contractCode, abi.encode(_nodeAddress, rocketStorage)); 27 | 28 | bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), uint256(0), keccak256(initCode))); 29 | 30 | return address(uint160(uint(hash))); 31 | } 32 | 33 | // Uses CREATE2 to deploy a RocketNodeDistributor at predetermined address 34 | function createProxy(address _nodeAddress) override external onlyLatestContract("rocketNodeManager", msg.sender) { 35 | // Salt is not required as the initCode is already unique per node address (node address is constructor argument) 36 | RocketNodeDistributor dist = new RocketNodeDistributor{salt: ''}(_nodeAddress, address(rocketStorage)); 37 | emit ProxyCreated(address(dist)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/token/scenario-rpl-burn-fixed.js: -------------------------------------------------------------------------------- 1 | import { RocketTokenDummyRPL, RocketTokenRPL } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | // Burn current fixed supply RPL for new RPL 5 | export async function burnFixedRPL(amount, txOptions) { 6 | // Load contracts 7 | const rocketTokenRPL = await RocketTokenRPL.deployed(); 8 | const rocketTokenDummyRPL = await RocketTokenDummyRPL.deployed(); 9 | 10 | // Get balances 11 | function getBalances() { 12 | return Promise.all([ 13 | rocketTokenDummyRPL.balanceOf(txOptions.from), 14 | rocketTokenRPL.totalSupply(), 15 | rocketTokenRPL.balanceOf(txOptions.from), 16 | rocketTokenDummyRPL.balanceOf(rocketTokenRPL.target), 17 | rocketTokenRPL.balanceOf(rocketTokenRPL.target), 18 | ]).then( 19 | ([rplFixedUserBalance, rplTokenSupply, rplUserBalance, rplContractBalanceOfFixedSupply, rplContractBalanceOfSelf]) => 20 | ({ 21 | rplFixedUserBalance, 22 | rplTokenSupply, 23 | rplUserBalance, 24 | rplContractBalanceOfFixedSupply, 25 | rplContractBalanceOfSelf, 26 | }), 27 | ); 28 | } 29 | 30 | // Get initial balances 31 | let balances1 = await getBalances(); 32 | 33 | // Burn tokens & get tx fee 34 | await rocketTokenRPL.connect(txOptions.from).swapTokens(amount, txOptions); 35 | 36 | // Get updated balances 37 | let balances2 = await getBalances(); 38 | 39 | // Calculate values 40 | let mintAmount = BigInt(amount); 41 | 42 | // Check balances 43 | assertBN.equal(balances2.rplUserBalance, balances1.rplUserBalance + mintAmount, 'Incorrect updated user token balance'); 44 | assertBN.equal(balances2.rplContractBalanceOfSelf, balances1.rplContractBalanceOfSelf - mintAmount, 'RPL contract has not sent the RPL to the user address'); 45 | } 46 | -------------------------------------------------------------------------------- /contracts/contract/helper/SnapshotTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.18; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../RocketBase.sol"; 6 | import "../../interface/network/RocketNetworkSnapshotsInterface.sol"; 7 | 8 | // THIS CONTRACT IS NOT DEPLOYED TO MAINNET 9 | 10 | // Helper contract used to insert arbitrary snapshots in for testing 11 | contract SnapshotTest is RocketBase { 12 | 13 | RocketNetworkSnapshotsInterface snapshots; 14 | 15 | // Construct 16 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 17 | snapshots = RocketNetworkSnapshotsInterface(getContractAddress("rocketNetworkSnapshots")); 18 | } 19 | 20 | function push(string calldata _key, uint224 _value) external { 21 | bytes32 key = keccak256(abi.encodePacked(_key)); 22 | 23 | snapshots.push(key, _value); 24 | } 25 | 26 | function lookup(string calldata _key, uint32 _block) external view returns (uint224){ 27 | bytes32 key = keccak256(abi.encodePacked(_key)); 28 | return snapshots.lookup(key, _block); 29 | } 30 | 31 | function lookupRecent(string calldata _key, uint32 _block, uint256 _recency) external view returns (uint224) { 32 | bytes32 key = keccak256(abi.encodePacked(_key)); 33 | return snapshots.lookupRecent(key, _block, _recency); 34 | } 35 | 36 | function lookupGas(string calldata _key, uint32 _block) external view returns (uint256) { 37 | bytes32 key = keccak256(abi.encodePacked(_key)); 38 | uint256 gasBefore = gasleft(); 39 | snapshots.lookup(key, _block); 40 | return gasBefore - gasleft(); 41 | } 42 | 43 | function lookupRecentGas(string calldata _key, uint32 _block, uint256 _recency) external view returns (uint256) { 44 | bytes32 key = keccak256(abi.encodePacked(_key)); 45 | uint256 gasBefore = gasleft(); 46 | snapshots.lookupRecent(key, _block, _recency); 47 | return gasBefore - gasleft(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/interface/auction/RocketAuctionManagerInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketAuctionManagerInterface { 6 | function getTotalRPLBalance() external view returns (uint256); 7 | function getAllottedRPLBalance() external view returns (uint256); 8 | function getRemainingRPLBalance() external view returns (uint256); 9 | function getLotCount() external view returns (uint256); 10 | function getLotExists(uint256 _index) external view returns (bool); 11 | function getLotStartBlock(uint256 _index) external view returns (uint256); 12 | function getLotEndBlock(uint256 _index) external view returns (uint256); 13 | function getLotStartPrice(uint256 _index) external view returns (uint256); 14 | function getLotReservePrice(uint256 _index) external view returns (uint256); 15 | function getLotTotalRPLAmount(uint256 _index) external view returns (uint256); 16 | function getLotTotalBidAmount(uint256 _index) external view returns (uint256); 17 | function getLotAddressBidAmount(uint256 _index, address _bidder) external view returns (uint256); 18 | function getLotRPLRecovered(uint256 _index) external view returns (bool); 19 | function getLotPriceAtBlock(uint256 _index, uint256 _block) external view returns (uint256); 20 | function getLotPriceAtCurrentBlock(uint256 _index) external view returns (uint256); 21 | function getLotPriceByTotalBids(uint256 _index) external view returns (uint256); 22 | function getLotCurrentPrice(uint256 _index) external view returns (uint256); 23 | function getLotClaimedRPLAmount(uint256 _index) external view returns (uint256); 24 | function getLotRemainingRPLAmount(uint256 _index) external view returns (uint256); 25 | function getLotIsCleared(uint256 _index) external view returns (bool); 26 | function createLot() external; 27 | function placeBid(uint256 _lotIndex) external payable; 28 | function claimBid(uint256 _lotIndex) external; 29 | function recoverUnclaimedRPL(uint256 _lotIndex) external; 30 | } 31 | -------------------------------------------------------------------------------- /test/node/scenario-set-withdrawal-address.js: -------------------------------------------------------------------------------- 1 | import { RocketStorage } from '../../test/_utils/artifacts'; 2 | import * as assert from 'assert'; 3 | 4 | // Set a node's withdrawal address 5 | export async function setWithdrawalAddress(nodeAddress, withdrawalAddress, confirm, txOptions) { 6 | // Load contracts 7 | const rocketStorage = await RocketStorage.deployed(); 8 | 9 | // Set withdrawal address 10 | await rocketStorage.connect(txOptions.from).setWithdrawalAddress(nodeAddress, withdrawalAddress, confirm, txOptions); 11 | 12 | // Get current & pending withdrawal addresses 13 | let nodeWithdrawalAddress = await rocketStorage.getNodeWithdrawalAddress(nodeAddress); 14 | let nodePendingWithdrawalAddress = await rocketStorage.getNodePendingWithdrawalAddress(nodeAddress); 15 | 16 | // Confirmed update check 17 | if (confirm) { 18 | assert.strictEqual(nodeWithdrawalAddress, withdrawalAddress, 'Incorrect updated withdrawal address'); 19 | } 20 | 21 | // Unconfirmed update check 22 | else { 23 | assert.strictEqual(nodePendingWithdrawalAddress, withdrawalAddress, 'Incorrect updated pending withdrawal address'); 24 | } 25 | } 26 | 27 | // Confirm a node's net withdrawal address 28 | export async function confirmWithdrawalAddress(nodeAddress, txOptions) { 29 | // Load contracts 30 | const rocketStorage = await RocketStorage.deployed(); 31 | 32 | // Confirm withdrawal address 33 | await rocketStorage.connect(txOptions.from).confirmWithdrawalAddress(nodeAddress, txOptions); 34 | 35 | // Get current & pending withdrawal addresses 36 | let nodeWithdrawalAddress = await rocketStorage.getNodeWithdrawalAddress(nodeAddress); 37 | let nodePendingWithdrawalAddress = await rocketStorage.getNodePendingWithdrawalAddress(nodeAddress); 38 | 39 | // Check 40 | assert.strictEqual(nodeWithdrawalAddress, txOptions.from.address, 'Incorrect updated withdrawal address'); 41 | assert.strictEqual(nodePendingWithdrawalAddress, '0x0000000000000000000000000000000000000000', 'Incorrect pending withdrawal address'); 42 | } 43 | -------------------------------------------------------------------------------- /test/minipool/scenario-close.js: -------------------------------------------------------------------------------- 1 | import { RocketNodeManager, RocketNodeStaking } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | const hre = require('hardhat'); 5 | const ethers = hre.ethers; 6 | 7 | // Close a minipool 8 | export async function close(minipool, txOptions) { 9 | // Load contracts 10 | const rocketNodeManager = await RocketNodeManager.deployed(); 11 | const rocketNodeStaking = await RocketNodeStaking.deployed(); 12 | 13 | // Get parameters 14 | let nodeAddress = await minipool.getNodeAddress(); 15 | let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress(nodeAddress); 16 | 17 | // Get initial node balance & minipool balances 18 | let [nodeBalance1, ethMatched1, minipoolBalance, userDepositBalance] = await Promise.all([ 19 | ethers.provider.getBalance(nodeWithdrawalAddress), 20 | rocketNodeStaking.getNodeETHMatched(txOptions.from), 21 | ethers.provider.getBalance(minipool.target), 22 | minipool.getUserDepositBalance(), 23 | ]); 24 | 25 | // Set gas price 26 | let gasPrice = '20'.gwei; 27 | txOptions.gasPrice = gasPrice; 28 | 29 | // Close & get tx fee 30 | let tx = await minipool.connect(txOptions.from).close(txOptions); 31 | let txReceipt = await tx.wait(); 32 | let txFee = gasPrice * txReceipt.gasUsed; 33 | 34 | // Get updated node balance & minipool contract code 35 | let [nodeBalance2, ethMatched2] = await Promise.all([ 36 | ethers.provider.getBalance(nodeWithdrawalAddress), 37 | rocketNodeStaking.getNodeETHMatched(txOptions.from), 38 | ]); 39 | 40 | // Check balances 41 | let expectedNodeBalance = nodeBalance1 + minipoolBalance; 42 | if (nodeWithdrawalAddress === nodeAddress) expectedNodeBalance = expectedNodeBalance - txFee; 43 | assertBN.equal(nodeBalance2, expectedNodeBalance, 'Incorrect updated node nETH balance'); 44 | 45 | // Expect node's ETH matched to be decreased by userDepositBalance 46 | assertBN.equal(ethMatched1 - ethMatched2, userDepositBalance, 'Incorrect ETH matched'); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /test/auction/scenario-recover-rpl.js: -------------------------------------------------------------------------------- 1 | import { RocketAuctionManager } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | import * as assert from 'assert'; 4 | 5 | // Recover unclaimed RPL from a lot 6 | export async function recoverUnclaimedRPL(lotIndex, txOptions) { 7 | // Load contracts 8 | const rocketAuctionManager = await RocketAuctionManager.deployed(); 9 | 10 | // Get auction contract details 11 | function getContractDetails() { 12 | return Promise.all([ 13 | rocketAuctionManager.getAllottedRPLBalance(), 14 | rocketAuctionManager.getRemainingRPLBalance(), 15 | ]).then( 16 | ([allottedRplBalance, remainingRplBalance]) => 17 | ({ allottedRplBalance, remainingRplBalance }), 18 | ); 19 | } 20 | 21 | // Get lot details 22 | function getLotDetails() { 23 | return Promise.all([ 24 | rocketAuctionManager.getLotRPLRecovered(lotIndex), 25 | rocketAuctionManager.getLotRemainingRPLAmount(lotIndex), 26 | ]).then( 27 | ([rplRecovered, remainingRplAmount]) => 28 | ({ rplRecovered, remainingRplAmount }), 29 | ); 30 | } 31 | 32 | // Get initial details 33 | let [details1, lot1] = await Promise.all([ 34 | getContractDetails(), 35 | getLotDetails(), 36 | ]); 37 | 38 | // Recover RPL 39 | await rocketAuctionManager.connect(txOptions.from).recoverUnclaimedRPL(lotIndex, txOptions); 40 | 41 | // Get updated details 42 | let [details2, lot2] = await Promise.all([ 43 | getContractDetails(), 44 | getLotDetails(), 45 | ]); 46 | 47 | // Check details 48 | assertBN.equal(details2.allottedRplBalance, details1.allottedRplBalance - lot1.remainingRplAmount, 'Incorrect updated contract allotted RPL balance'); 49 | assertBN.equal(details2.remainingRplBalance, details1.remainingRplBalance + lot1.remainingRplAmount, 'Incorrect updated contract remaining RPL balance'); 50 | assert.equal(lot2.rplRecovered, true, 'Incorrect updated lot RPL recovered status'); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /contracts/contract/token/temp/RocketTokenDummyRPL.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | 9 | /// @title Dummy Rocket Pool Token (RPL) contract (do not deploy to mainnet) 10 | /// @author Jake Pospischil 11 | 12 | contract RocketTokenDummyRPL is ERC20, Ownable { 13 | 14 | 15 | /**** Properties ***********/ 16 | 17 | uint8 constant decimalPlaces = 18; 18 | uint256 constant public exponent = 10**uint256(decimalPlaces); 19 | uint256 constant public totalSupplyCap = 18.5 * (10**6) * exponent; // 18 Million tokens 20 | 21 | 22 | /**** Libs *****************/ 23 | 24 | using SafeMath for uint; 25 | 26 | 27 | /*** Events ****************/ 28 | 29 | event MintToken(address _minter, address _address, uint256 _value); 30 | 31 | 32 | /**** Methods ***********/ 33 | 34 | // Construct with our token details 35 | constructor(address _rocketStorageAddress) ERC20("Rocket Pool Dummy RPL", "DRPL") {} 36 | 37 | 38 | // @dev Mint the Rocket Pool Tokens (RPL) 39 | // @param _to The address that will receive the minted tokens. 40 | // @param _amount The amount of tokens to mint. 41 | // @return A boolean that indicates if the operation was successful. 42 | function mint(address _to, uint _amount) external onlyOwner returns (bool) { 43 | // Check token amount is positive 44 | require(_amount > 0); 45 | // Check we don't exceed the supply cap 46 | require(totalSupply().add(_amount) <= totalSupplyCap); 47 | // Mint tokens at address 48 | _mint(_to, _amount); 49 | // Fire mint token event 50 | emit MintToken(msg.sender, _to, _amount); 51 | // Return success flag 52 | return true; 53 | } 54 | 55 | 56 | /// @dev Returns the amount of tokens that can still be minted 57 | function getRemainingTokens() external view returns(uint256) { 58 | return totalSupplyCap.sub(totalSupply()); 59 | } 60 | 61 | 62 | } -------------------------------------------------------------------------------- /test/minipool/scenario-refund.js: -------------------------------------------------------------------------------- 1 | import { RocketNodeManager } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | const hre = require('hardhat'); 5 | const ethers = hre.ethers; 6 | 7 | // Refund refinanced node balance from a minipool 8 | export async function refund(minipool, txOptions) { 9 | // Load contracts 10 | const rocketNodeManager = await RocketNodeManager.deployed(); 11 | 12 | // Get parameters 13 | let nodeAddress = await minipool.getNodeAddress(); 14 | let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress(nodeAddress); 15 | 16 | // Get balances 17 | function getBalances() { 18 | return Promise.all([ 19 | minipool.getNodeRefundBalance(), 20 | ethers.provider.getBalance(minipool.target), 21 | ethers.provider.getBalance(nodeWithdrawalAddress), 22 | ]).then( 23 | ([nodeRefund, minipoolEth, nodeEth]) => 24 | ({ nodeRefund, minipoolEth, nodeEth }), 25 | ); 26 | } 27 | 28 | // Get initial balances 29 | let balances1 = await getBalances(); 30 | 31 | // Set gas price 32 | let gasPrice = '20'.gwei; 33 | txOptions.gasPrice = gasPrice; 34 | 35 | // Refund & get tx fee 36 | let tx = await minipool.connect(txOptions.from).refund(txOptions); 37 | let txReceipt = await tx.wait(); 38 | let txFee = gasPrice * txReceipt.gasUsed; 39 | 40 | // Get updated balances 41 | let balances2 = await getBalances(); 42 | 43 | // Check balances 44 | let expectedNodeBalance = balances1.nodeEth + balances1.nodeRefund; 45 | if (nodeWithdrawalAddress === nodeAddress) expectedNodeBalance = expectedNodeBalance - txFee; 46 | assertBN.isAbove(balances1.nodeRefund, '0'.ether, 'Incorrect initial node refund balance'); 47 | assertBN.equal(balances2.nodeRefund, '0'.ether, 'Incorrect updated node refund balance'); 48 | assertBN.equal(balances2.minipoolEth, balances1.minipoolEth - balances1.nodeRefund, 'Incorrect updated minipool ETH balance'); 49 | assertBN.equal(balances2.nodeEth, expectedNodeBalance, 'Incorrect updated node ETH balance'); 50 | } 51 | -------------------------------------------------------------------------------- /contracts/interface/node/RocketNodeStakingInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | interface RocketNodeStakingInterface { 5 | function getTotalRPLStake() external view returns (uint256); 6 | function getNodeRPLStake(address _nodeAddress) external view returns (uint256); 7 | function getNodeETHMatched(address _nodeAddress) external view returns (uint256); 8 | function getNodeETHProvided(address _nodeAddress) external view returns (uint256); 9 | function getNodeETHCollateralisationRatio(address _nodeAddress) external view returns (uint256); 10 | function getNodeRPLStakedTime(address _nodeAddress) external view returns (uint256); 11 | function getNodeEffectiveRPLStake(address _nodeAddress) external view returns (uint256); 12 | function getNodeMinimumRPLStake(address _nodeAddress) external view returns (uint256); 13 | function getNodeMaximumRPLStake(address _nodeAddress) external view returns (uint256); 14 | function getNodeETHMatchedLimit(address _nodeAddress) external view returns (uint256); 15 | function getRPLLockingAllowed(address _nodeAddress) external view returns (bool); 16 | function stakeRPL(uint256 _amount) external; 17 | function stakeRPLFor(address _nodeAddress, uint256 _amount) external; 18 | function setRPLLockingAllowed(address _nodeAddress, bool _allowed) external; 19 | function setStakeRPLForAllowed(address _caller, bool _allowed) external; 20 | function setStakeRPLForAllowed(address _nodeAddress, address _caller, bool _allowed) external; 21 | function getNodeRPLLocked(address _nodeAddress) external view returns (uint256); 22 | function lockRPL(address _nodeAddress, uint256 _amount) external; 23 | function unlockRPL(address _nodeAddress, uint256 _amount) external; 24 | function transferRPL(address _from, address _to, uint256 _amount) external; 25 | function burnRPL(address _from, uint256 _amount) external; 26 | function withdrawRPL(uint256 _amount) external; 27 | function withdrawRPL(address _nodeAddress, uint256 _amount) external; 28 | function slashRPL(address _nodeAddress, uint256 _ethSlashAmount) external; 29 | } 30 | -------------------------------------------------------------------------------- /contracts/interface/dao/node/RocketDAONodeTrustedInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAONodeTrustedInterface { 6 | function getBootstrapModeDisabled() external view returns (bool); 7 | function getMemberQuorumVotesRequired() external view returns (uint256); 8 | function getMemberAt(uint256 _index) external view returns (address); 9 | function getMemberCount() external view returns (uint256); 10 | function getMemberMinRequired() external view returns (uint256); 11 | function getMemberIsValid(address _nodeAddress) external view returns (bool); 12 | function getMemberLastProposalTime(address _nodeAddress) external view returns (uint256); 13 | function getMemberID(address _nodeAddress) external view returns (string memory); 14 | function getMemberUrl(address _nodeAddress) external view returns (string memory); 15 | function getMemberJoinedTime(address _nodeAddress) external view returns (uint256); 16 | function getMemberProposalExecutedTime(string memory _proposalType, address _nodeAddress) external view returns (uint256); 17 | function getMemberRPLBondAmount(address _nodeAddress) external view returns (uint256); 18 | function getMemberIsChallenged(address _nodeAddress) external view returns (bool); 19 | function getMemberUnbondedValidatorCount(address _nodeAddress) external view returns (uint256); 20 | function incrementMemberUnbondedValidatorCount(address _nodeAddress) external; 21 | function decrementMemberUnbondedValidatorCount(address _nodeAddress) external; 22 | function bootstrapMember(string memory _id, string memory _url, address _nodeAddress) external; 23 | function bootstrapSettingUint(string memory _settingContractName, string memory _settingPath, uint256 _value) external; 24 | function bootstrapSettingBool(string memory _settingContractName, string memory _settingPath, bool _value) external; 25 | function bootstrapUpgrade(string memory _type, string memory _name, string memory _contractAbi, address _contractAddress) external; 26 | function bootstrapDisable(bool _confirmDisableBootstrapMode) external; 27 | function memberJoinRequired(string memory _id, string memory _url) external; 28 | } 29 | -------------------------------------------------------------------------------- /contracts/contract/dao/node/settings/RocketDAONodeTrustedSettingsRewards.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | 7 | import "./RocketDAONodeTrustedSettings.sol"; 8 | import "../../../../interface/dao/node/settings/RocketDAONodeTrustedSettingsRewardsInterface.sol"; 9 | import "../../../../interface/dao/protocol/settings/RocketDAOProtocolSettingsRewardsInterface.sol"; 10 | 11 | 12 | // The Trusted Node DAO Rewards settings 13 | 14 | contract RocketDAONodeTrustedSettingsRewards is RocketDAONodeTrustedSettings, RocketDAONodeTrustedSettingsRewardsInterface { 15 | 16 | using SafeMath for uint; 17 | 18 | // Construct 19 | constructor(RocketStorageInterface _rocketStorageAddress) RocketDAONodeTrustedSettings(_rocketStorageAddress, "rewards") { 20 | // Set version 21 | version = 2; 22 | // Initialize settings on deployment 23 | if(!getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) { 24 | // Init settings 25 | setSettingBool("rewards.network.enabled", true); 26 | // Settings initialised 27 | setBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")), true); 28 | } 29 | } 30 | 31 | // Update a setting, overrides inherited setting method with extra checks for this contract 32 | function setSettingBool(string memory _settingPath, bool _value) override public onlyDAONodeTrustedProposal { 33 | // Some safety guards for certain settings 34 | if(getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) { 35 | // oDAO should never disable main net rewards 36 | if(keccak256(abi.encodePacked(_settingPath)) == keccak256(abi.encodePacked("rewards.network.enabled", uint256(0)))) { 37 | revert("Cannot disable network 0"); 38 | } 39 | } 40 | // Update setting now 41 | setBool(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value); 42 | } 43 | 44 | 45 | // Getters 46 | 47 | function getNetworkEnabled(uint256 _network) override external view returns (bool) { 48 | return getBool(keccak256(abi.encodePacked(settingNameSpace, "rewards.network.enabled", _network))); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/_utils/evm.js: -------------------------------------------------------------------------------- 1 | // Take a snapshot of the EVM state 2 | export function takeSnapshot(web3) { 3 | return new Promise((resolve, reject) => { 4 | web3.currentProvider.send({ 5 | jsonrpc: '2.0', 6 | method: 'evm_snapshot', 7 | id: (new Date()).getTime(), 8 | }, function(err, response) { 9 | if (err) { reject(err); } 10 | else if (response && response.result) { resolve(response.result); } 11 | else { reject('Unknown error'); } 12 | }); 13 | }); 14 | } 15 | 16 | 17 | // Restore a snapshot of EVM state 18 | export function revertSnapshot(web3, snapshotId) { 19 | return new Promise((resolve, reject) => { 20 | web3.currentProvider.send({ 21 | jsonrpc: '2.0', 22 | method: 'evm_revert', 23 | params: [snapshotId], 24 | id: (new Date()).getTime(), 25 | }, function(err, response) { 26 | if (err) { reject(err); } 27 | else { resolve(); } 28 | }); 29 | }); 30 | } 31 | 32 | 33 | // Mine a number of blocks 34 | export async function mineBlocks(web3, numBlocks) { 35 | for (let i = 0; i < numBlocks; ++i) { 36 | await new Promise((resolve, reject) => { 37 | web3.currentProvider.send({ 38 | jsonrpc: '2.0', 39 | method: 'evm_mine', 40 | id: (new Date()).getTime(), 41 | }, function(err, response) { 42 | if (err) { reject(err); } 43 | else { resolve(); } 44 | }); 45 | }); 46 | } 47 | } 48 | 49 | // Fast-forward time 50 | export async function increaseTime(web3, seconds) { 51 | await new Promise((resolve, reject) => { 52 | web3.currentProvider.send({ 53 | jsonrpc: '2.0', 54 | method: 'evm_increaseTime', 55 | params: [seconds], 56 | id: (new Date()).getTime(), 57 | }, function(err, response) { 58 | if (err) { reject(err); } 59 | else { resolve(); } 60 | }); 61 | }); 62 | // Mine a block using the new time 63 | await mineBlocks(web3, 1); 64 | } 65 | 66 | // Retrieve current time on block chain 67 | export async function getCurrentTime(web3) { 68 | return (await web3.eth.getBlock('latest')).timestamp 69 | } -------------------------------------------------------------------------------- /test/_helpers/tokens.js: -------------------------------------------------------------------------------- 1 | import { RocketTokenDummyRPL, RocketTokenRETH, RocketTokenRPL } from '../_utils/artifacts'; 2 | 3 | // Get the RPL balance of an address 4 | export async function getRplBalance(address) { 5 | const rocketTokenRPL = await RocketTokenRPL.deployed(); 6 | return rocketTokenRPL.balanceOf(address); 7 | } 8 | 9 | // Get the rETH balance of an address 10 | export async function getRethBalance(address) { 11 | const rocketTokenRETH = await RocketTokenRETH.deployed(); 12 | return rocketTokenRETH.balanceOf(address); 13 | } 14 | 15 | // Get the current rETH exchange rate 16 | export async function getRethExchangeRate() { 17 | const rocketTokenRETH = await RocketTokenRETH.deployed(); 18 | return rocketTokenRETH.getExchangeRate(); 19 | } 20 | 21 | // Get the current rETH collateral rate 22 | export async function getRethCollateralRate() { 23 | const rocketTokenRETH = await RocketTokenRETH.deployed(); 24 | return rocketTokenRETH.getCollateralRate(); 25 | } 26 | 27 | // Get the current rETH token supply 28 | export async function getRethTotalSupply() { 29 | const rocketTokenRETH = await RocketTokenRETH.deployed(); 30 | return rocketTokenRETH.totalSupply(); 31 | } 32 | 33 | // Mint RPL to an address 34 | export async function mintRPL(owner, toAddress, amount) { 35 | // Load contracts 36 | const [rocketTokenDummyRPL, rocketTokenRPL] = await Promise.all([ 37 | RocketTokenDummyRPL.deployed(), 38 | RocketTokenRPL.deployed(), 39 | ]); 40 | 41 | // Mint dummy RPL to address 42 | await rocketTokenDummyRPL.connect(owner).mint(toAddress, amount); 43 | 44 | // Swap dummy RPL for RPL 45 | await rocketTokenDummyRPL.connect(toAddress).approve(rocketTokenRPL.target, amount); 46 | await rocketTokenRPL.connect(toAddress).swapTokens(amount); 47 | } 48 | 49 | // Approve RPL to be spend by an address 50 | export async function approveRPL(spender, amount, txOptions) { 51 | const rocketTokenRPL = await RocketTokenRPL.deployed(); 52 | await rocketTokenRPL.connect(txOptions.from).approve(spender, amount, txOptions); 53 | } 54 | 55 | export async function depositExcessCollateral(txOptions) { 56 | const rocketTokenRETH = await RocketTokenRETH.deployed(); 57 | await rocketTokenRETH.connect(txOptions.from).depositExcessCollateral(txOptions); 58 | } 59 | -------------------------------------------------------------------------------- /contracts/interface/dao/RocketDAOProposalInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketDAOProposalInterface { 6 | 7 | // Possible states that a proposal may be in 8 | enum ProposalState { 9 | Pending, 10 | Active, 11 | Cancelled, 12 | Defeated, 13 | Succeeded, 14 | Expired, 15 | Executed 16 | } 17 | 18 | function getTotal() external view returns (uint256); 19 | function getDAO(uint256 _proposalID) external view returns (string memory); 20 | function getProposer(uint256 _proposalID) external view returns (address); 21 | function getMessage(uint256 _proposalID) external view returns (string memory); 22 | function getStart(uint256 _proposalID) external view returns (uint256); 23 | function getEnd(uint256 _proposalID) external view returns (uint256); 24 | function getExpires(uint256 _proposalID) external view returns (uint256); 25 | function getCreated(uint256 _proposalID) external view returns (uint256); 26 | function getVotesFor(uint256 _proposalID) external view returns (uint256); 27 | function getVotesAgainst(uint256 _proposalID) external view returns (uint256); 28 | function getVotesRequired(uint256 _proposalID) external view returns (uint256); 29 | function getCancelled(uint256 _proposalID) external view returns (bool); 30 | function getExecuted(uint256 _proposalID) external view returns (bool); 31 | function getPayload(uint256 _proposalID) external view returns (bytes memory); 32 | function getReceiptHasVoted(uint256 _proposalID, address _nodeAddress) external view returns (bool); 33 | function getReceiptSupported(uint256 _proposalID, address _nodeAddress) external view returns (bool); 34 | function getState(uint256 _proposalID) external view returns (ProposalState); 35 | function add(address _member, string memory _dao, string memory _message, uint256 _startBlock, uint256 _durationBlocks, uint256 _expiresBlocks, uint256 _votesRequired, bytes memory _payload) external returns (uint256); 36 | function vote(address _member, uint256 _votes, uint256 _proposalID, bool _support) external; 37 | function cancel(address _member, uint256 _proposalID) external; 38 | function execute(uint256 _proposalID) external; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /test/_helpers/defaults.js: -------------------------------------------------------------------------------- 1 | import { setDAOProtocolBootstrapSetting } from '../dao/scenario-dao-protocol-bootstrap'; 2 | import { 3 | RocketDAOProtocolSettingsDeposit, RocketDAOProtocolSettingsInflation, 4 | RocketDAOProtocolSettingsMinipool, RocketDAOProtocolSettingsNetwork, 5 | RocketDAOProtocolSettingsNode 6 | } from '../_utils/artifacts'; 7 | 8 | const hre = require('hardhat'); 9 | const ethers = hre.ethers; 10 | 11 | export async function setDefaultParameters() { 12 | const [guardian] = await ethers.getSigners(); 13 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsDeposit, 'deposit.enabled', true, { from: guardian }); 14 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsDeposit, 'deposit.assign.enabled', true, { from: guardian }); 15 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsDeposit, 'deposit.pool.maximum', '1000'.ether, { from: guardian }); 16 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNode, 'node.registration.enabled', true, { from: guardian }); 17 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNode, 'node.deposit.enabled', true, { from: guardian }); 18 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsMinipool, 'minipool.submit.withdrawable.enabled', true, { from: guardian }); 19 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.fee.minimum', '0.05'.ether, { from: guardian }); 20 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.fee.target', '0.1'.ether, { from: guardian }); 21 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.fee.maximum', '0.2'.ether, { from: guardian }); 22 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.demand.range', '1000'.ether, { from: guardian }); 23 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsInflation, 'rpl.inflation.interval.start', Math.floor(new Date().getTime() / 1000) + (60 * 60 * 24 * 14), { from: guardian }); 24 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsMinipool, 'minipool.bond.reduction.enabled', true, { from: guardian }); 25 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNode, 'node.vacant.minipools.enabled', true, { from: guardian }); 26 | } -------------------------------------------------------------------------------- /test/deposit/scenario-deposit.js: -------------------------------------------------------------------------------- 1 | import { 2 | RocketDAOProtocolSettingsDeposit, 3 | RocketDepositPool, 4 | RocketTokenRETH, 5 | RocketVault, 6 | } from '../_utils/artifacts'; 7 | import { assertBN } from '../_helpers/bn'; 8 | 9 | const hre = require('hardhat'); 10 | const ethers = hre.ethers; 11 | 12 | // Make a deposit into the deposit pool 13 | export async function deposit(txOptions) { 14 | // Load contracts 15 | const [ 16 | rocketDAOProtocolSettingsDeposit, 17 | rocketDepositPool, 18 | rocketTokenRETH, 19 | rocketVault, 20 | ] = await Promise.all([ 21 | RocketDAOProtocolSettingsDeposit.deployed(), 22 | RocketDepositPool.deployed(), 23 | RocketTokenRETH.deployed(), 24 | RocketVault.deployed(), 25 | ]); 26 | 27 | // Get parameters 28 | let depositFeePerc = await rocketDAOProtocolSettingsDeposit.getDepositFee(); 29 | 30 | // Get balances 31 | function getBalances() { 32 | return Promise.all([ 33 | rocketDepositPool.getBalance(), 34 | rocketDepositPool.getNodeBalance(), 35 | ethers.provider.getBalance(rocketVault.target), 36 | rocketTokenRETH.balanceOf(txOptions.from), 37 | ]).then( 38 | ([depositPoolEth, depositPoolNodeEth, vaultEth, userReth]) => 39 | ({depositPoolEth, depositPoolNodeEth, vaultEth, userReth}) 40 | ); 41 | } 42 | 43 | // Get initial balances 44 | let balances1 = await getBalances(); 45 | 46 | // Deposit 47 | await rocketDepositPool.connect(txOptions.from).deposit(txOptions); 48 | 49 | // Get updated balances 50 | let balances2 = await getBalances(); 51 | 52 | // Calculate values 53 | let txValue = BigInt(txOptions.value); 54 | let calcBase = '1'.ether; 55 | let depositFee = txValue * depositFeePerc / calcBase; 56 | let expectedRethMinted = await rocketTokenRETH.getRethValue(txValue - depositFee); 57 | 58 | // Check balances 59 | assertBN.equal(balances2.depositPoolEth, balances1.depositPoolEth + txValue, 'Incorrect updated deposit pool ETH balance'); 60 | assertBN.equal(balances2.vaultEth, balances1.vaultEth + txValue, 'Incorrect updated vault ETH balance'); 61 | assertBN.equal(balances2.userReth, balances1.userReth + expectedRethMinted, 'Incorrect updated user rETH balance'); 62 | } 63 | -------------------------------------------------------------------------------- /contracts/contract/minipool/RocketMinipoolPenalty.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | 7 | import "../RocketBase.sol"; 8 | import "../../interface/minipool/RocketMinipoolPenaltyInterface.sol"; 9 | 10 | // Non-upgradable contract which gives guardian control over maximum penalty rates 11 | 12 | contract RocketMinipoolPenalty is RocketBase, RocketMinipoolPenaltyInterface { 13 | 14 | // Events 15 | event MaxPenaltyRateUpdated(uint256 rate, uint256 time); 16 | 17 | // Libs 18 | using SafeMath for uint; 19 | 20 | // Storage (purposefully does not use RocketStorage to prevent oDAO from having power over this feature) 21 | uint256 maxPenaltyRate = 0 ether; // The most the oDAO is allowed to penalty a minipool (as a percentage) 22 | 23 | // Construct 24 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 25 | } 26 | 27 | // Get/set the current max penalty rate 28 | function setMaxPenaltyRate(uint256 _rate) external override onlyGuardian { 29 | // Update rate 30 | maxPenaltyRate = _rate; 31 | // Emit event 32 | emit MaxPenaltyRateUpdated(_rate, block.timestamp); 33 | } 34 | function getMaxPenaltyRate() external override view returns (uint256) { 35 | return maxPenaltyRate; 36 | } 37 | 38 | // Retrieves the amount to penalty a minipool 39 | function getPenaltyRate(address _minipoolAddress) external override view returns(uint256) { 40 | // Quick out which avoids a call to RocketStorage 41 | if (maxPenaltyRate == 0) { 42 | return 0; 43 | } 44 | // Retrieve penalty rate for this minipool 45 | uint256 penaltyRate = getUint(keccak256(abi.encodePacked("minipool.penalty.rate", _minipoolAddress))); 46 | // min(maxPenaltyRate, penaltyRate) 47 | if (penaltyRate > maxPenaltyRate) { 48 | return maxPenaltyRate; 49 | } 50 | return penaltyRate; 51 | } 52 | 53 | // Sets the penalty rate for the given minipool 54 | function setPenaltyRate(address _minipoolAddress, uint256 _rate) external override onlyLatestNetworkContract { 55 | setUint(keccak256(abi.encodePacked("minipool.penalty.rate", _minipoolAddress)), _rate); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Rocket Pool - Decentralised Ethereum Liquid Staking Protocol 3 | 4 | 5 | # Rocket Pool - Decentralised Ethereum Liquid Staking Protocol 6 | 7 | Rocket Pool is a decentralized Ethereum liquid staking protocol. It lets people participate in Ethereum staking without needing to run a full validator with 32 ETH, and it also lowers the technical barrier for running nodes. Here’s a breakdown: 8 | 9 | ### For Regular Stakers 10 | - Users can stake as little as 0.01 ETH by depositing into Rocket Pool’s smart contracts 11 | - In return, they receive rETH (Rocket Pool ETH), a liquid staking token that automatically accrues staking rewards over time 12 | - rETH can be traded, used in DeFi, or redeemed for ETH + rewards 13 | 14 | ### For Node Operators 15 | - People who want to run validator nodes can join Rocket Pool by staking 8 ETH (instead of the full 32 ETH) 16 | - Rocket Pool pairs that 8 ETH with the ETH deposited by rETH users to make a full validator 17 | - They earn consensus rewards (ETH) plus commission from the rETH users for running the node 18 | 19 | ### Why It’s Different 20 | - Lower capital requirement, 8 ETH instead of 32 ETH 21 | - Better yield than solo staking for node operators 22 | - Decentralized alternative to centralized exchanges’ staking services 23 | - Permissionless: anyone can run a node, no approval required 24 | - rETH token means stakers don’t have their ETH locked; they can use rETH across DeFi 25 | 26 | Learn more at [https://rocketpool.net](https://rocketpool.net). 27 | 28 | # Test Rocket Pool 29 | 30 | 31 | Rocket Pool - Testing Ethereum Proof-of-Stake (PoS) Infrastructure Service and Pool for Ethereum 2.0 Beacon Chain 32 | 33 | 34 | To see Rocket Pool in action, clone the repo and run the test suite with the following commands: 35 | ```bash 36 | $ npm install 37 | $ npm test 38 | ``` 39 | 40 | 41 | Having issues? Have an idea? Interested in research? 42 | 43 | Our friendly community are available to help via our discord. 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /contracts/interface/RocketStorageInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | interface RocketStorageInterface { 6 | 7 | // Deploy status 8 | function getDeployedStatus() external view returns (bool); 9 | 10 | // Guardian 11 | function getGuardian() external view returns(address); 12 | function setGuardian(address _newAddress) external; 13 | function confirmGuardian() external; 14 | 15 | // Getters 16 | function getAddress(bytes32 _key) external view returns (address); 17 | function getUint(bytes32 _key) external view returns (uint); 18 | function getString(bytes32 _key) external view returns (string memory); 19 | function getBytes(bytes32 _key) external view returns (bytes memory); 20 | function getBool(bytes32 _key) external view returns (bool); 21 | function getInt(bytes32 _key) external view returns (int); 22 | function getBytes32(bytes32 _key) external view returns (bytes32); 23 | 24 | // Setters 25 | function setAddress(bytes32 _key, address _value) external; 26 | function setUint(bytes32 _key, uint _value) external; 27 | function setString(bytes32 _key, string calldata _value) external; 28 | function setBytes(bytes32 _key, bytes calldata _value) external; 29 | function setBool(bytes32 _key, bool _value) external; 30 | function setInt(bytes32 _key, int _value) external; 31 | function setBytes32(bytes32 _key, bytes32 _value) external; 32 | 33 | // Deleters 34 | function deleteAddress(bytes32 _key) external; 35 | function deleteUint(bytes32 _key) external; 36 | function deleteString(bytes32 _key) external; 37 | function deleteBytes(bytes32 _key) external; 38 | function deleteBool(bytes32 _key) external; 39 | function deleteInt(bytes32 _key) external; 40 | function deleteBytes32(bytes32 _key) external; 41 | 42 | // Arithmetic 43 | function addUint(bytes32 _key, uint256 _amount) external; 44 | function subUint(bytes32 _key, uint256 _amount) external; 45 | 46 | // Protected storage 47 | function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address); 48 | function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address); 49 | function setWithdrawalAddress(address _nodeAddress, address _newWithdrawalAddress, bool _confirm) external; 50 | function confirmWithdrawalAddress(address _nodeAddress) external; 51 | } 52 | -------------------------------------------------------------------------------- /test/dao/scenario-dao-proposal.js: -------------------------------------------------------------------------------- 1 | import { RocketDAOProposal } from '../_utils/artifacts'; 2 | 3 | // Possible states that a proposal may be in 4 | export const proposalStates = { 5 | Pending: 0, 6 | Active: 1, 7 | Cancelled: 2, 8 | Defeated: 3, 9 | Succeeded: 4, 10 | Expired: 5, 11 | Executed: 6, 12 | }; 13 | 14 | // Possible vote direction 15 | export const voteStates = { 16 | NoVote: 0, 17 | Abstain: 1, 18 | For: 2, 19 | Against: 3, 20 | AgainstWithVeto: 4, 21 | }; 22 | 23 | // Get the status of a proposal 24 | export async function getDAOProposalState(proposalID, txOptions) { 25 | // Load contracts 26 | const rocketDAOProposal = await RocketDAOProposal.deployed(); 27 | return Number(await rocketDAOProposal.getState(proposalID)); 28 | } 29 | 30 | // Get the block a proposal can start being voted on 31 | export async function getDAOProposalStartTime(proposalID, txOptions) { 32 | // Load contracts 33 | const rocketDAOProposal = await RocketDAOProposal.deployed(); 34 | return Number(await rocketDAOProposal.getStart(proposalID)); 35 | } 36 | 37 | // Get the block a proposal can end being voted on 38 | export async function getDAOProposalEndTime(proposalID, txOptions) { 39 | // Load contracts 40 | const rocketDAOProposal = await RocketDAOProposal.deployed(); 41 | return Number(await rocketDAOProposal.getEnd(proposalID)); 42 | } 43 | 44 | // Get the block a proposal expires 45 | export async function getDAOProposalExpires(proposalID, txOptions) { 46 | // Load contracts 47 | const rocketDAOProposal = await RocketDAOProposal.deployed(); 48 | return Number(await rocketDAOProposal.getExpires(proposalID)); 49 | } 50 | 51 | // Get the vote count for a proposal 52 | export async function getDAOProposalVotesFor(proposalID, txOptions) { 53 | // Load contracts 54 | const rocketDAOProposal = await RocketDAOProposal.deployed(); 55 | return await rocketDAOProposal.getVotesFor(proposalID); 56 | } 57 | 58 | // Get the vote count against a proposal 59 | export async function getDAOProposalVotesAgainst(proposalID, txOptions) { 60 | // Load contracts 61 | const rocketDAOProposal = await RocketDAOProposal.deployed(); 62 | return await rocketDAOProposal.getVotesAgainst(proposalID); 63 | } 64 | 65 | // Get the quroum for a proposal 66 | export async function getDAOProposalVotesRequired(proposalID, txOptions) { 67 | // Load contracts 68 | const rocketDAOProposal = await RocketDAOProposal.deployed(); 69 | return await rocketDAOProposal.getVotesRequired(proposalID); 70 | } 71 | -------------------------------------------------------------------------------- /contracts/contract/minipool/RocketMinipoolFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity 0.7.6; 3 | 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | import "@openzeppelin/contracts/proxy/Clones.sol"; 6 | 7 | import "../RocketBase.sol"; 8 | import "../../interface/minipool/RocketMinipoolBaseInterface.sol"; 9 | import "../../interface/minipool/RocketMinipoolFactoryInterface.sol"; 10 | 11 | /// @notice Performs CREATE2 deployment of minipool contracts 12 | contract RocketMinipoolFactory is RocketBase, RocketMinipoolFactoryInterface { 13 | 14 | // Libs 15 | using SafeMath for uint; 16 | using Clones for address; 17 | 18 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 19 | version = 2; 20 | } 21 | 22 | /// @notice Returns the expected minipool address for a node operator given a user-defined salt 23 | /// @param _salt The salt used in minipool creation 24 | function getExpectedAddress(address _nodeOperator, uint256 _salt) external override view returns (address) { 25 | // Ensure rocketMinipoolBase is setAddress 26 | address rocketMinipoolBase = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketMinipoolBase"))); 27 | // Calculate node specific salt value 28 | bytes32 salt = keccak256(abi.encodePacked(_nodeOperator, _salt)); 29 | // Return expected address 30 | return rocketMinipoolBase.predictDeterministicAddress(salt, address(this)); 31 | } 32 | 33 | /// @notice Performs a CREATE2 deployment of a minipool contract with given salt 34 | /// @param _nodeAddress Owning node operator's address 35 | /// @param _salt A salt used in determining minipool address 36 | function deployContract(address _nodeAddress, uint256 _salt) override external onlyLatestContract("rocketMinipoolFactory", address(this)) onlyLatestContract("rocketMinipoolManager", msg.sender) returns (address) { 37 | // Ensure rocketMinipoolBase is setAddress 38 | address rocketMinipoolBase = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketMinipoolBase"))); 39 | require(rocketMinipoolBase != address(0)); 40 | // Construct final salt 41 | bytes32 salt = keccak256(abi.encodePacked(_nodeAddress, _salt)); 42 | // Deploy the minipool 43 | address proxy = rocketMinipoolBase.cloneDeterministic(salt); 44 | // Initialise the minipool storage 45 | RocketMinipoolBaseInterface(proxy).initialise(address(rocketStorage), _nodeAddress); 46 | // Return address 47 | return proxy; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /test/_helpers/network.js: -------------------------------------------------------------------------------- 1 | import { 2 | RocketNetworkBalances, 3 | RocketNetworkFees, 4 | RocketNetworkPrices, 5 | RocketNetworkVoting, 6 | } from '../_utils/artifacts'; 7 | 8 | // Get the network total ETH balance 9 | export async function getTotalETHBalance() { 10 | const rocketNetworkBalances = await RocketNetworkBalances.deployed(); 11 | return rocketNetworkBalances.getTotalETHBalance(); 12 | } 13 | 14 | // Get the network staking ETH balance 15 | export async function getStakingETHBalance() { 16 | const rocketNetworkBalances = await RocketNetworkBalances.deployed(); 17 | return rocketNetworkBalances.getStakingETHBalance(); 18 | } 19 | 20 | // Get the network ETH utilization rate 21 | export async function getETHUtilizationRate() { 22 | const rocketNetworkBalances = await RocketNetworkBalances.deployed(); 23 | return rocketNetworkBalances.getETHUtilizationRate(); 24 | } 25 | 26 | // Submit network balances 27 | export async function submitBalances(block, slotTimestamp, totalEth, stakingEth, rethSupply, txOptions) { 28 | const rocketNetworkBalances = await RocketNetworkBalances.deployed(); 29 | await rocketNetworkBalances.connect(txOptions.from).submitBalances(block, slotTimestamp, totalEth, stakingEth, rethSupply, txOptions); 30 | } 31 | 32 | // Submit network token prices 33 | export async function submitPrices(block, slotTimestamp, rplPrice, txOptions) { 34 | const rocketNetworkPrices = await RocketNetworkPrices.deployed(); 35 | await rocketNetworkPrices.connect(txOptions.from).submitPrices(block, slotTimestamp, rplPrice, txOptions); 36 | } 37 | 38 | // Get network RPL price 39 | export async function getRPLPrice() { 40 | const rocketNetworkPrices = await RocketNetworkPrices.deployed(); 41 | return rocketNetworkPrices.getRPLPrice(); 42 | } 43 | 44 | // Get the network node demand 45 | export async function getNodeDemand() { 46 | const rocketNetworkFees = await RocketNetworkFees.deployed(); 47 | return rocketNetworkFees.getNodeDemand(); 48 | } 49 | 50 | // Get the current network node fee 51 | export async function getNodeFee() { 52 | const rocketNetworkFees = await RocketNetworkFees.deployed(); 53 | return rocketNetworkFees.getNodeFee(); 54 | } 55 | 56 | // Get the network node fee for a node demand value 57 | export async function getNodeFeeByDemand(nodeDemand) { 58 | const rocketNetworkFees = await RocketNetworkFees.deployed(); 59 | return rocketNetworkFees.getNodeFeeByDemand(nodeDemand); 60 | } 61 | 62 | export async function setDelegate(nodeAddress, txOptions) { 63 | const rocketNetworkVoting = await RocketNetworkVoting.deployed(); 64 | await rocketNetworkVoting.connect(txOptions.from).setDelegate(nodeAddress, txOptions); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /contracts/contract/minipool/RocketMinipoolStorageLayout.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../interface/RocketStorageInterface.sol"; 6 | import "../../types/MinipoolDeposit.sol"; 7 | import "../../types/MinipoolStatus.sol"; 8 | 9 | // The RocketMinipool contract storage layout, shared by RocketMinipoolDelegate 10 | 11 | // ****************************************************** 12 | // Note: This contract MUST NOT BE UPDATED after launch. 13 | // All deployed minipool contracts must maintain a 14 | // Consistent storage layout with RocketMinipoolDelegate. 15 | // ****************************************************** 16 | 17 | abstract contract RocketMinipoolStorageLayout { 18 | // Storage state enum 19 | enum StorageState { 20 | Undefined, 21 | Uninitialised, 22 | Initialised 23 | } 24 | 25 | // Main Rocket Pool storage contract 26 | RocketStorageInterface internal rocketStorage = RocketStorageInterface(0); 27 | 28 | // Status 29 | MinipoolStatus internal status; 30 | uint256 internal statusBlock; 31 | uint256 internal statusTime; 32 | uint256 internal withdrawalBlock; 33 | 34 | // Deposit type 35 | MinipoolDeposit internal depositType; 36 | 37 | // Node details 38 | address internal nodeAddress; 39 | uint256 internal nodeFee; 40 | uint256 internal nodeDepositBalance; 41 | bool internal nodeDepositAssigned; // NO LONGER IN USE 42 | uint256 internal nodeRefundBalance; 43 | uint256 internal nodeSlashBalance; 44 | 45 | // User deposit details 46 | uint256 internal userDepositBalanceLegacy; 47 | uint256 internal userDepositAssignedTime; 48 | 49 | // Upgrade options 50 | bool internal useLatestDelegate = false; 51 | address internal rocketMinipoolDelegate; 52 | address internal rocketMinipoolDelegatePrev; 53 | 54 | // Local copy of RETH address 55 | address internal rocketTokenRETH; 56 | 57 | // Local copy of penalty contract 58 | address internal rocketMinipoolPenalty; 59 | 60 | // Used to prevent direct access to delegate and prevent calling initialise more than once 61 | StorageState internal storageState = StorageState.Undefined; 62 | 63 | // Whether node operator has finalised the pool 64 | bool internal finalised; 65 | 66 | // Trusted member scrub votes 67 | mapping(address => bool) internal memberScrubVotes; 68 | uint256 internal totalScrubVotes; 69 | 70 | // Variable minipool 71 | uint256 internal preLaunchValue; 72 | uint256 internal userDepositBalance; 73 | 74 | // Vacant minipool 75 | bool internal vacant; 76 | uint256 internal preMigrationBalance; 77 | 78 | // User distribution 79 | bool internal userDistributed; 80 | uint256 internal userDistributeTime; 81 | } 82 | -------------------------------------------------------------------------------- /contracts/interface/node/RocketNodeManagerInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | import "../../types/NodeDetails.sol"; 6 | 7 | interface RocketNodeManagerInterface { 8 | 9 | // Structs 10 | struct TimezoneCount { 11 | string timezone; 12 | uint256 count; 13 | } 14 | 15 | function getNodeCount() external view returns (uint256); 16 | function getNodeCountPerTimezone(uint256 offset, uint256 limit) external view returns (TimezoneCount[] memory); 17 | function getNodeAt(uint256 _index) external view returns (address); 18 | function getNodeExists(address _nodeAddress) external view returns (bool); 19 | function getNodeWithdrawalAddress(address _nodeAddress) external view returns (address); 20 | function getNodePendingWithdrawalAddress(address _nodeAddress) external view returns (address); 21 | function getNodeRPLWithdrawalAddress(address _nodeAddress) external view returns (address); 22 | function getNodeRPLWithdrawalAddressIsSet(address _nodeAddress) external view returns (bool); 23 | function unsetRPLWithdrawalAddress(address _nodeAddress) external; 24 | function setRPLWithdrawalAddress(address _nodeAddress, address _newRPLWithdrawalAddress, bool _confirm) external; 25 | function confirmRPLWithdrawalAddress(address _nodeAddress) external; 26 | function getNodePendingRPLWithdrawalAddress(address _nodeAddress) external view returns (address); 27 | function getNodeTimezoneLocation(address _nodeAddress) external view returns (string memory); 28 | function registerNode(string calldata _timezoneLocation) external; 29 | function getNodeRegistrationTime(address _nodeAddress) external view returns (uint256); 30 | function setTimezoneLocation(string calldata _timezoneLocation) external; 31 | function setRewardNetwork(address _nodeAddress, uint256 network) external; 32 | function getRewardNetwork(address _nodeAddress) external view returns (uint256); 33 | function getFeeDistributorInitialised(address _nodeAddress) external view returns (bool); 34 | function initialiseFeeDistributor() external; 35 | function getAverageNodeFee(address _nodeAddress) external view returns (uint256); 36 | function setSmoothingPoolRegistrationState(bool _state) external; 37 | function getSmoothingPoolRegistrationState(address _nodeAddress) external returns (bool); 38 | function getSmoothingPoolRegistrationChanged(address _nodeAddress) external returns (uint256); 39 | function getSmoothingPoolRegisteredNodeCount(uint256 _offset, uint256 _limit) external view returns (uint256); 40 | function getNodeDetails(address _nodeAddress) external view returns (NodeDetails memory); 41 | function getNodeAddresses(uint256 _offset, uint256 _limit) external view returns (address[] memory); 42 | } 43 | -------------------------------------------------------------------------------- /test/network/network-voting-tests.js: -------------------------------------------------------------------------------- 1 | import { before, describe, it } from 'mocha'; 2 | import { printTitle } from '../_utils/formatting'; 3 | import { RocketNetworkSnapshots, RocketNetworkVoting } from '../_utils/artifacts'; 4 | import { assertBN } from '../_helpers/bn'; 5 | import { nodeStakeRPL, registerNode } from '../_helpers/node'; 6 | import { createMinipool, getMinipoolMaximumRPLStake } from '../_helpers/minipool'; 7 | import { mintRPL } from '../_helpers/tokens'; 8 | import { userDeposit } from '../_helpers/deposit'; 9 | import { globalSnapShot } from '../_utils/snapshotting'; 10 | import { BigSqrt } from '../_helpers/bigmath'; 11 | 12 | const helpers = require('@nomicfoundation/hardhat-network-helpers'); 13 | const hre = require('hardhat'); 14 | const ethers = hre.ethers; 15 | 16 | export default function() { 17 | describe('RocketNetworkVoting', () => { 18 | let owner, 19 | node, 20 | random; 21 | 22 | let networkSnapshots; 23 | let networkVoting; 24 | 25 | // Setup 26 | before(async () => { 27 | await globalSnapShot(); 28 | 29 | [ 30 | owner, 31 | node, 32 | random, 33 | ] = await ethers.getSigners(); 34 | 35 | // Get contracts 36 | networkSnapshots = await RocketNetworkSnapshots.deployed(); 37 | networkVoting = await RocketNetworkVoting.deployed(); 38 | 39 | // Register node & set withdrawal address 40 | await registerNode({ from: node }); 41 | 42 | // Stake RPL to cover minipools 43 | let minipoolRplStake = await getMinipoolMaximumRPLStake(); 44 | let rplStake = minipoolRplStake * 2n; 45 | await mintRPL(owner, node, rplStake); 46 | await nodeStakeRPL(rplStake, { from: node }); 47 | 48 | // Add some ETH into the DP 49 | await userDeposit({ from: random, value: '320'.ether }); 50 | }); 51 | 52 | it(printTitle('Voting Power', 'Should correctly snapshot values'), async () => { 53 | // Create a minipool to set the active count to non-zero 54 | await createMinipool({ from: node, value: '16'.ether }); 55 | const blockBefore = (await ethers.provider.getBlockNumber()); 56 | await createMinipool({ from: node, value: '16'.ether }); 57 | const blockAfter = (await ethers.provider.getBlockNumber()); 58 | 59 | const votingPowerBefore = await networkVoting.getVotingPower(node, blockBefore); 60 | const votingPowerAfter = await networkVoting.getVotingPower(node, blockAfter); 61 | 62 | assertBN.equal(votingPowerBefore, BigSqrt('2400'.ether * '1'.ether)); 63 | assertBN.equal(votingPowerAfter, BigSqrt('4800'.ether * '1'.ether)); 64 | }); 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /test/minipool/scenario-stake.js: -------------------------------------------------------------------------------- 1 | import { RocketMinipoolManager } from '../_utils/artifacts'; 2 | import { getDepositDataRoot, getValidatorSignature } from '../_utils/beacon'; 3 | import { assertBN } from '../_helpers/bn'; 4 | import { minipoolStates } from '../_helpers/minipool'; 5 | import * as assert from 'assert'; 6 | 7 | const hre = require('hardhat'); 8 | const ethers = hre.ethers; 9 | 10 | // Stake a minipool 11 | export async function stake(minipool, withdrawalCredentials, txOptions, validatorPubkey = null) { 12 | // Load contracts 13 | const [ 14 | rocketMinipoolManager, 15 | ] = await Promise.all([ 16 | RocketMinipoolManager.deployed(), 17 | ]); 18 | 19 | // Get minipool validator pubkey 20 | if (!validatorPubkey) validatorPubkey = await rocketMinipoolManager.getMinipoolPubkey(minipool.target); 21 | 22 | // Get minipool withdrawal credentials 23 | if (!withdrawalCredentials) withdrawalCredentials = await rocketMinipoolManager.getMinipoolWithdrawalCredentials(minipool.target); 24 | 25 | // Get validator deposit data 26 | let depositData = { 27 | pubkey: Buffer.from(validatorPubkey.substr(2), 'hex'), 28 | withdrawalCredentials: Buffer.from(withdrawalCredentials.substr(2), 'hex'), 29 | amount: BigInt(31000000000), // 31 ETH in gwei 30 | signature: getValidatorSignature(), 31 | }; 32 | let depositDataRoot = getDepositDataRoot(depositData); 33 | 34 | // Get minipool details 35 | function getMinipoolDetails() { 36 | return Promise.all([ 37 | minipool.getStatus(), 38 | ethers.provider.getBalance(minipool.target), 39 | ]).then( 40 | ([status, balance]) => 41 | ({ status: Number(status), balance }), 42 | ); 43 | } 44 | 45 | // Get initial minipool details & minipool by validator pubkey 46 | let [details1] = await Promise.all([ 47 | getMinipoolDetails(), 48 | ]); 49 | 50 | // Stake 51 | await minipool.connect(txOptions.from).stake(depositData.signature, depositDataRoot, txOptions); 52 | 53 | // Get updated minipool details & minipool by validator pubkey 54 | let [details2, validatorMinipool2] = await Promise.all([ 55 | getMinipoolDetails(), 56 | rocketMinipoolManager.getMinipoolByPubkey(validatorPubkey), 57 | ]); 58 | 59 | // Check minpool details 60 | assert.notEqual(details1.status, minipoolStates.Staking, 'Incorrect initial minipool status'); 61 | assert.equal(details2.status, minipoolStates.Staking, 'Incorrect updated minipool status'); 62 | assertBN.equal(details2.balance, details1.balance - '31'.ether, 'Incorrect updated minipool ETH balance'); 63 | 64 | // Check minipool by validator pubkey 65 | assert.strictEqual(validatorMinipool2, minipool.target, 'Incorrect updated minipool by validator pubkey'); 66 | } 67 | -------------------------------------------------------------------------------- /test/network/network-snapshots-tests.js: -------------------------------------------------------------------------------- 1 | import { before, describe, it } from 'mocha'; 2 | import { printTitle } from '../_utils/formatting'; 3 | import { RocketNetworkSnapshots, RocketStorage, SnapshotTest } from '../_utils/artifacts'; 4 | import { setDaoNodeTrustedBootstrapUpgrade } from '../dao/scenario-dao-node-trusted-bootstrap'; 5 | import { assertBN } from '../_helpers/bn'; 6 | import { globalSnapShot } from '../_utils/snapshotting'; 7 | 8 | const helpers = require('@nomicfoundation/hardhat-network-helpers'); 9 | const hre = require('hardhat'); 10 | const ethers = hre.ethers; 11 | 12 | export default function() { 13 | describe('RocketNetworkSnapshots', () => { 14 | let owner; 15 | 16 | let snapshotTest; 17 | let networkSnapshots; 18 | 19 | // Setup 20 | before(async () => { 21 | await globalSnapShot(); 22 | 23 | [ 24 | owner, 25 | ] = await ethers.getSigners(); 26 | 27 | // Add penalty helper contract 28 | const rocketStorage = await RocketStorage.deployed(); 29 | snapshotTest = await SnapshotTest.new(rocketStorage.target, { from: owner }); 30 | await setDaoNodeTrustedBootstrapUpgrade('addContract', 'rocketSnapshotTest', SnapshotTest.abi, snapshotTest.target, { 31 | from: owner, 32 | }); 33 | 34 | // Get contracts 35 | networkSnapshots = await RocketNetworkSnapshots.deployed(); 36 | }); 37 | 38 | it(printTitle('contract', 'can insert values into snapshot'), async () => { 39 | const blockNumber = await ethers.provider.getBlockNumber(); 40 | await snapshotTest.push('test', '50'.BN); // block + 1 41 | await snapshotTest.push('test', '150'.BN); // block + 2 42 | await helpers.mine(2); 43 | await snapshotTest.push('test', '250'.BN); // block + 5 44 | 45 | assertBN.equal(await snapshotTest.lookup('test', blockNumber + 1), '50'.BN); 46 | assertBN.equal(await snapshotTest.lookup('test', blockNumber + 2), '150'.BN); 47 | assertBN.equal(await snapshotTest.lookup('test', blockNumber + 3), '150'.BN); 48 | assertBN.equal(await snapshotTest.lookup('test', blockNumber + 4), '150'.BN); 49 | assertBN.equal(await snapshotTest.lookup('test', blockNumber + 5), '250'.BN); 50 | assertBN.equal(await snapshotTest.lookupRecent('test', blockNumber + 1, 10), '50'.BN); 51 | assertBN.equal(await snapshotTest.lookupRecent('test', blockNumber + 2, 10), '150'.BN); 52 | assertBN.equal(await snapshotTest.lookupRecent('test', blockNumber + 3, 10), '150'.BN); 53 | assertBN.equal(await snapshotTest.lookupRecent('test', blockNumber + 4, 10), '150'.BN); 54 | assertBN.equal(await snapshotTest.lookupRecent('test', blockNumber + 5, 10), '250'.BN); 55 | }); 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /contracts/contract/dao/node/settings/RocketDAONodeTrustedSettings.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../../RocketBase.sol"; 6 | import "../../../../interface/dao/node/settings/RocketDAONodeTrustedSettingsInterface.sol"; 7 | 8 | // Settings in RP which the DAO will have full control over 9 | // This settings contract enables storage using setting paths with namespaces, rather than explicit set methods 10 | abstract contract RocketDAONodeTrustedSettings is RocketBase, RocketDAONodeTrustedSettingsInterface { 11 | 12 | 13 | // The namespace for a particular group of settings 14 | bytes32 settingNameSpace; 15 | 16 | 17 | // Only allow updating from the DAO proposals contract 18 | modifier onlyDAONodeTrustedProposal() { 19 | // If this contract has been initialised, only allow access from the proposals contract 20 | if(getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) require(getContractAddress("rocketDAONodeTrustedProposals") == msg.sender, "Only DAO Node Trusted Proposals contract can update a setting"); 21 | _; 22 | } 23 | 24 | 25 | // Construct 26 | constructor(RocketStorageInterface _rocketStorageAddress, string memory _settingNameSpace) RocketBase(_rocketStorageAddress) { 27 | // Apply the setting namespace 28 | settingNameSpace = keccak256(abi.encodePacked("dao.trustednodes.setting.", _settingNameSpace)); 29 | } 30 | 31 | 32 | /*** Uints ****************/ 33 | 34 | // A general method to return any setting given the setting path is correct, only accepts uints 35 | function getSettingUint(string memory _settingPath) public view override returns (uint256) { 36 | return getUint(keccak256(abi.encodePacked(settingNameSpace, _settingPath))); 37 | } 38 | 39 | // Update a Uint setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed 40 | function setSettingUint(string memory _settingPath, uint256 _value) virtual public override onlyDAONodeTrustedProposal { 41 | // Update setting now 42 | setUint(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value); 43 | } 44 | 45 | 46 | /*** Bools ****************/ 47 | 48 | // A general method to return any setting given the setting path is correct, only accepts bools 49 | function getSettingBool(string memory _settingPath) public view override returns (bool) { 50 | return getBool(keccak256(abi.encodePacked(settingNameSpace, _settingPath))); 51 | } 52 | 53 | // Update a setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed 54 | function setSettingBool(string memory _settingPath, bool _value) virtual public override onlyDAONodeTrustedProposal { 55 | // Update setting now 56 | setBool(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /contracts/contract/dao/node/settings/RocketDAONodeTrustedSettingsProposals.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "./RocketDAONodeTrustedSettings.sol"; 6 | import "../../../../interface/dao/node/settings/RocketDAONodeTrustedSettingsProposalsInterface.sol"; 7 | 8 | 9 | // The Trusted Node DAO Members 10 | contract RocketDAONodeTrustedSettingsProposals is RocketDAONodeTrustedSettings, RocketDAONodeTrustedSettingsProposalsInterface { 11 | 12 | // Construct 13 | constructor(RocketStorageInterface _rocketStorageAddress) RocketDAONodeTrustedSettings(_rocketStorageAddress, "proposals") { 14 | // Set version 15 | version = 1; 16 | // Initialize settings on deployment 17 | if(!getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) { 18 | // Init settings 19 | setSettingUint("proposal.cooldown.time", 2 days); // How long before a member can make sequential proposals 20 | setSettingUint("proposal.vote.time", 2 weeks); // How long a proposal can be voted on 21 | setSettingUint("proposal.vote.delay.time", 1 weeks); // How long before a proposal can be voted on after it is created 22 | setSettingUint("proposal.execute.time", 4 weeks); // How long a proposal can be executed after its voting period is finished 23 | setSettingUint("proposal.action.time", 4 weeks); // Certain proposals require a secondary action to be run after the proposal is successful (joining, leaving etc). This is how long until that action expires 24 | // Settings initialised 25 | setBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")), true); 26 | } 27 | } 28 | 29 | 30 | // Getters 31 | 32 | // How long before a member can make sequential proposals 33 | function getCooldownTime() override external view returns (uint256) { 34 | return getSettingUint("proposal.cooldown.time"); 35 | } 36 | 37 | // How long a proposal can be voted on 38 | function getVoteTime() override external view returns (uint256) { 39 | return getSettingUint("proposal.vote.time"); 40 | } 41 | 42 | // How long before a proposal can be voted on after it is created 43 | function getVoteDelayTime() override external view returns (uint256) { 44 | return getSettingUint("proposal.vote.delay.time"); 45 | } 46 | 47 | // How long a proposal can be executed after its voting period is finished 48 | function getExecuteTime() override external view returns (uint256) { 49 | return getSettingUint("proposal.execute.time"); 50 | } 51 | 52 | // Certain proposals require a secondary action to be run after the proposal is successful (joining, leaving etc). This is how long until that action expires 53 | function getActionTime() override external view returns (uint256) { 54 | return getSettingUint("proposal.action.time"); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /contracts/interface/minipool/RocketMinipoolInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../../types/MinipoolDeposit.sol"; 6 | import "../../types/MinipoolStatus.sol"; 7 | import "../RocketStorageInterface.sol"; 8 | 9 | interface RocketMinipoolInterface { 10 | function version() external view returns (uint8); 11 | function initialise(address _nodeAddress) external; 12 | function getStatus() external view returns (MinipoolStatus); 13 | function getFinalised() external view returns (bool); 14 | function getStatusBlock() external view returns (uint256); 15 | function getStatusTime() external view returns (uint256); 16 | function getScrubVoted(address _member) external view returns (bool); 17 | function getDepositType() external view returns (MinipoolDeposit); 18 | function getNodeAddress() external view returns (address); 19 | function getNodeFee() external view returns (uint256); 20 | function getNodeDepositBalance() external view returns (uint256); 21 | function getNodeRefundBalance() external view returns (uint256); 22 | function getNodeDepositAssigned() external view returns (bool); 23 | function getPreLaunchValue() external view returns (uint256); 24 | function getNodeTopUpValue() external view returns (uint256); 25 | function getVacant() external view returns (bool); 26 | function getPreMigrationBalance() external view returns (uint256); 27 | function getUserDistributed() external view returns (bool); 28 | function getUserDepositBalance() external view returns (uint256); 29 | function getUserDepositAssigned() external view returns (bool); 30 | function getUserDepositAssignedTime() external view returns (uint256); 31 | function getTotalScrubVotes() external view returns (uint256); 32 | function calculateNodeShare(uint256 _balance) external view returns (uint256); 33 | function calculateUserShare(uint256 _balance) external view returns (uint256); 34 | function preDeposit(uint256 _bondingValue, bytes calldata _validatorPubkey, bytes calldata _validatorSignature, bytes32 _depositDataRoot) external payable; 35 | function deposit() external payable; 36 | function userDeposit() external payable; 37 | function distributeBalance(bool _rewardsOnly) external; 38 | function beginUserDistribute() external; 39 | function userDistributeAllowed() external view returns (bool); 40 | function refund() external; 41 | function slash() external; 42 | function finalise() external; 43 | function canStake() external view returns (bool); 44 | function canPromote() external view returns (bool); 45 | function stake(bytes calldata _validatorSignature, bytes32 _depositDataRoot) external; 46 | function prepareVacancy(uint256 _bondAmount, uint256 _currentBalance) external; 47 | function promote() external; 48 | function dissolve() external; 49 | function close() external; 50 | function voteScrub() external; 51 | function reduceBondAmount() external; 52 | } 53 | -------------------------------------------------------------------------------- /contracts/interface/util/IERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) 3 | 4 | pragma solidity >0.5.0 <0.9.0; 5 | 6 | /** 7 | * @dev Interface of the ERC20 standard as defined in the EIP. 8 | */ 9 | interface IERC20 { 10 | /** 11 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 12 | * another (`to`). 13 | * 14 | * Note that `value` may be zero. 15 | */ 16 | event Transfer(address indexed from, address indexed to, uint256 value); 17 | 18 | /** 19 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 20 | * a call to {approve}. `value` is the new allowance. 21 | */ 22 | event Approval(address indexed owner, address indexed spender, uint256 value); 23 | 24 | /** 25 | * @dev Returns the amount of tokens in existence. 26 | */ 27 | function totalSupply() external view returns (uint256); 28 | 29 | /** 30 | * @dev Returns the amount of tokens owned by `account`. 31 | */ 32 | function balanceOf(address account) external view returns (uint256); 33 | 34 | /** 35 | * @dev Moves `amount` tokens from the caller's account to `to`. 36 | * 37 | * Returns a boolean value indicating whether the operation succeeded. 38 | * 39 | * Emits a {Transfer} event. 40 | */ 41 | function transfer(address to, uint256 amount) external returns (bool); 42 | 43 | /** 44 | * @dev Returns the remaining number of tokens that `spender` will be 45 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 46 | * zero by default. 47 | * 48 | * This value changes when {approve} or {transferFrom} are called. 49 | */ 50 | function allowance(address owner, address spender) external view returns (uint256); 51 | 52 | /** 53 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 54 | * 55 | * Returns a boolean value indicating whether the operation succeeded. 56 | * 57 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 58 | * that someone may use both the old and the new allowance by unfortunate 59 | * transaction ordering. One possible solution to mitigate this race 60 | * condition is to first reduce the spender's allowance to 0 and set the 61 | * desired value afterwards: 62 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 63 | * 64 | * Emits an {Approval} event. 65 | */ 66 | function approve(address spender, uint256 amount) external returns (bool); 67 | 68 | /** 69 | * @dev Moves `amount` tokens from `from` to `to` using the 70 | * allowance mechanism. `amount` is then deducted from the caller's 71 | * allowance. 72 | * 73 | * Returns a boolean value indicating whether the operation succeeded. 74 | * 75 | * Emits a {Transfer} event. 76 | */ 77 | function transferFrom(address from, address to, uint256 amount) external returns (bool); 78 | } 79 | -------------------------------------------------------------------------------- /test/rewards/scenario-claim-rewards.js: -------------------------------------------------------------------------------- 1 | import { 2 | RocketMerkleDistributorMainnet, 3 | RocketNodeManager, 4 | RocketRewardsPool, 5 | RocketTokenRPL, 6 | } from '../_utils/artifacts'; 7 | import { parseRewardsMap } from '../_utils/merkle-tree'; 8 | import { assertBN } from '../_helpers/bn'; 9 | 10 | const hre = require('hardhat'); 11 | const ethers = hre.ethers; 12 | 13 | // Submit network prices 14 | export async function claimRewards(nodeAddress, indices, rewards, txOptions) { 15 | 16 | // Load contracts 17 | const [ 18 | rocketRewardsPool, 19 | rocketNodeManager, 20 | rocketMerkleDistributorMainnet, 21 | rocketTokenRPL, 22 | ] = await Promise.all([ 23 | RocketRewardsPool.deployed(), 24 | RocketNodeManager.deployed(), 25 | RocketMerkleDistributorMainnet.deployed(), 26 | RocketTokenRPL.deployed(), 27 | ]); 28 | 29 | // Get node withdrawal address 30 | let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress(nodeAddress); 31 | 32 | // Get balances 33 | function getBalances() { 34 | return Promise.all([ 35 | rocketRewardsPool.getClaimIntervalTimeStart(), 36 | rocketTokenRPL.balanceOf(nodeWithdrawalAddress), 37 | ethers.provider.getBalance(nodeWithdrawalAddress), 38 | ]).then( 39 | ([claimIntervalTimeStart, nodeRpl, nodeEth]) => 40 | ({ claimIntervalTimeStart, nodeRpl, nodeEth }), 41 | ); 42 | } 43 | 44 | let [balances1] = await Promise.all([ 45 | getBalances(), 46 | ]); 47 | 48 | // Construct claim arguments 49 | let claimer = nodeAddress; 50 | let amountsRPL = []; 51 | let amountsETH = []; 52 | let proofs = []; 53 | let totalAmountRPL = 0n; 54 | let totalAmountETH = 0n; 55 | 56 | for (let i = 0; i < indices.length; i++) { 57 | let treeData = parseRewardsMap(rewards[i]); 58 | 59 | let proof = treeData.proof.claims[ethers.getAddress(claimer)]; 60 | 61 | if (!proof) { 62 | throw new Error('No proof in merkle tree for ' + claimer); 63 | } 64 | 65 | amountsRPL.push(proof.amountRPL); 66 | amountsETH.push(proof.amountETH); 67 | proofs.push(proof.proof); 68 | 69 | totalAmountRPL = totalAmountRPL + proof.amountRPL; 70 | totalAmountETH = totalAmountETH + proof.amountETH; 71 | } 72 | 73 | const tx = await rocketMerkleDistributorMainnet.connect(txOptions.from).claim(nodeAddress, indices, amountsRPL, amountsETH, proofs, txOptions); 74 | let gasUsed = 0n; 75 | 76 | if (ethers.getAddress(nodeWithdrawalAddress) === ethers.getAddress(txOptions.from.address)) { 77 | const txReceipt = await tx.wait(); 78 | gasUsed = BigInt(txReceipt.gasUsed * txReceipt.gasPrice); 79 | } 80 | 81 | let [balances2] = await Promise.all([ 82 | getBalances(), 83 | ]); 84 | 85 | assertBN.equal(balances2.nodeRpl - balances1.nodeRpl, totalAmountRPL, 'Incorrect updated node RPL balance'); 86 | assertBN.equal(balances2.nodeEth - balances1.nodeEth + gasUsed, totalAmountETH, 'Incorrect updated node ETH balance'); 87 | } 88 | -------------------------------------------------------------------------------- /test/network/network-fees-tests.js: -------------------------------------------------------------------------------- 1 | import { before, describe, it } from 'mocha'; 2 | import { printTitle } from '../_utils/formatting'; 3 | import { getNodeFeeByDemand } from '../_helpers/network'; 4 | import { RocketDAOProtocolSettingsNetwork } from '../_utils/artifacts'; 5 | import { setDAOProtocolBootstrapSetting } from '../dao/scenario-dao-protocol-bootstrap'; 6 | import { assertBN } from '../_helpers/bn'; 7 | import { globalSnapShot } from '../_utils/snapshotting'; 8 | 9 | const helpers = require('@nomicfoundation/hardhat-network-helpers'); 10 | const hre = require('hardhat'); 11 | const ethers = hre.ethers; 12 | 13 | export default function() { 14 | describe('RocketNetworkFees', () => { 15 | let owner; 16 | 17 | // Constants 18 | const minNodeFee = '0.10'.ether; 19 | const targetNodeFee = '0.15'.ether; 20 | const maxNodeFee = '0.20'.ether; 21 | const demandRange = '1'.ether; 22 | 23 | // Setup 24 | before(async () => { 25 | await globalSnapShot(); 26 | 27 | [ 28 | owner, 29 | ] = await ethers.getSigners(); 30 | 31 | // Set network settings 32 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.fee.minimum', minNodeFee, { from: owner }); 33 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.fee.target', targetNodeFee, { from: owner }); 34 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.fee.maximum', maxNodeFee, { from: owner }); 35 | await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNetwork, 'network.node.fee.demand.range', demandRange, { from: owner }); 36 | }); 37 | 38 | it(printTitle('network node fee', 'has correct value based on node demand'), async () => { 39 | // Set expected fees for node demand values 40 | let values = [ 41 | { demand: '-1.25'.ether, expectedFee: '0.1'.ether }, 42 | { demand: '-1.00'.ether, expectedFee: '0.1'.ether }, 43 | { demand: '-0.75'.ether, expectedFee: '0.12890625'.ether }, 44 | { demand: '-0.50'.ether, expectedFee: '0.14375'.ether }, 45 | { demand: '-0.25'.ether, expectedFee: '0.14921875'.ether }, 46 | { demand: '0.00'.ether, expectedFee: '0.15'.ether }, 47 | { demand: '0.25'.ether, expectedFee: '0.15078125'.ether }, 48 | { demand: '0.50'.ether, expectedFee: '0.15625'.ether }, 49 | { demand: '0.75'.ether, expectedFee: '0.17109375'.ether }, 50 | { demand: '1.00'.ether, expectedFee: '0.2'.ether }, 51 | { demand: '1.25'.ether, expectedFee: '0.2'.ether }, 52 | ]; 53 | 54 | // Check fees 55 | for (let vi = 0; vi < values.length; ++vi) { 56 | let v = values[vi]; 57 | let nodeFee = await getNodeFeeByDemand(v.demand); 58 | assertBN.equal(nodeFee, v.expectedFee, 'Node fee does not match expected fee for node demand value'); 59 | } 60 | }); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /contracts/contract/util/AddressSetStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | // SPDX-License-Identifier: GPL-3.0-only 4 | 5 | import "../RocketBase.sol"; 6 | import "../../interface/util/AddressSetStorageInterface.sol"; 7 | 8 | import "@openzeppelin/contracts/math/SafeMath.sol"; 9 | 10 | // Address set storage helper for RocketStorage data (contains unique items; has reverse index lookups) 11 | 12 | contract AddressSetStorage is RocketBase, AddressSetStorageInterface { 13 | 14 | using SafeMath for uint; 15 | 16 | // Construct 17 | constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) { 18 | version = 1; 19 | } 20 | 21 | // The number of items in a set 22 | function getCount(bytes32 _key) override external view returns (uint) { 23 | return getUint(keccak256(abi.encodePacked(_key, ".count"))); 24 | } 25 | 26 | // The item in a set by index 27 | function getItem(bytes32 _key, uint _index) override external view returns (address) { 28 | return getAddress(keccak256(abi.encodePacked(_key, ".item", _index))); 29 | } 30 | 31 | // The index of an item in a set 32 | // Returns -1 if the value is not found 33 | function getIndexOf(bytes32 _key, address _value) override external view returns (int) { 34 | return int(getUint(keccak256(abi.encodePacked(_key, ".index", _value)))) - 1; 35 | } 36 | 37 | // Add an item to a set 38 | // Requires that the item does not exist in the set 39 | function addItem(bytes32 _key, address _value) override external onlyLatestContract("addressSetStorage", address(this)) onlyLatestNetworkContract { 40 | require(getUint(keccak256(abi.encodePacked(_key, ".index", _value))) == 0, "Item already exists in set"); 41 | uint count = getUint(keccak256(abi.encodePacked(_key, ".count"))); 42 | setAddress(keccak256(abi.encodePacked(_key, ".item", count)), _value); 43 | setUint(keccak256(abi.encodePacked(_key, ".index", _value)), count.add(1)); 44 | setUint(keccak256(abi.encodePacked(_key, ".count")), count.add(1)); 45 | } 46 | 47 | // Remove an item from a set 48 | // Swaps the item with the last item in the set and truncates it; computationally cheap 49 | // Requires that the item exists in the set 50 | function removeItem(bytes32 _key, address _value) override external onlyLatestContract("addressSetStorage", address(this)) onlyLatestNetworkContract { 51 | uint256 index = getUint(keccak256(abi.encodePacked(_key, ".index", _value))); 52 | require(index-- > 0, "Item does not exist in set"); 53 | uint count = getUint(keccak256(abi.encodePacked(_key, ".count"))); 54 | if (index < count.sub(1)) { 55 | address lastItem = getAddress(keccak256(abi.encodePacked(_key, ".item", count.sub(1)))); 56 | setAddress(keccak256(abi.encodePacked(_key, ".item", index)), lastItem); 57 | setUint(keccak256(abi.encodePacked(_key, ".index", lastItem)), index.add(1)); 58 | } 59 | setUint(keccak256(abi.encodePacked(_key, ".index", _value)), 0); 60 | setUint(keccak256(abi.encodePacked(_key, ".count")), count.sub(1)); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /test/minipool/scenario-reduce-bond.js: -------------------------------------------------------------------------------- 1 | import { 2 | RocketMinipoolBondReducer, 3 | RocketMinipoolManager, 4 | RocketNodeDeposit, 5 | RocketNodeStaking, 6 | } from '../_utils/artifacts'; 7 | import { assertBN } from '../_helpers/bn'; 8 | 9 | // Reduce bonding amount of a minipool 10 | export async function reduceBond(minipool, txOptions = null) { 11 | const rocketNodeDeposit = await RocketNodeDeposit.deployed(); 12 | const rocketNodeStaking = await RocketNodeStaking.deployed(); 13 | const rocketMinipoolBondReducer = await RocketMinipoolBondReducer.deployed(); 14 | const rocketMinipoolManager = await RocketMinipoolManager.deployed(); 15 | const node = await minipool.getNodeAddress(); 16 | 17 | const newBond = await rocketMinipoolBondReducer.getReduceBondValue(minipool.target); 18 | const prevBond = await minipool.getNodeDepositBalance(); 19 | 20 | // Get minipool balances 21 | function getMinipoolBalances() { 22 | return Promise.all([ 23 | minipool.getNodeDepositBalance(), 24 | minipool.getUserDepositBalance(), 25 | rocketNodeDeposit.getNodeDepositCredit(node), 26 | rocketNodeStaking.getNodeETHMatched(node), 27 | ]).then( 28 | ([nodeDepositBalance, userDepositBalance, nodeDepositCredit, ethMatched]) => 29 | ({ nodeDepositBalance, userDepositBalance, nodeDepositCredit, ethMatched }), 30 | ); 31 | } 32 | 33 | // Get node details 34 | function getNodeDetails() { 35 | return Promise.all([ 36 | rocketMinipoolManager.getNodeStakingMinipoolCountBySize(node, prevBond), 37 | rocketMinipoolManager.getNodeStakingMinipoolCountBySize(node, newBond), 38 | rocketMinipoolManager.getNodeStakingMinipoolCount(node), 39 | ]).then( 40 | ([prevBondCount, newBondCount, totalCount]) => 41 | ({ prevBondCount, newBondCount, totalCount }), 42 | ); 43 | } 44 | 45 | // Get new bond amount 46 | const amount = await rocketMinipoolBondReducer.getReduceBondValue(minipool.target); 47 | 48 | // Record balances before and after calling reduce bond function 49 | const balances1 = await getMinipoolBalances(); 50 | const details1 = await getNodeDetails(); 51 | await minipool.connect(txOptions.from).reduceBondAmount(txOptions); 52 | const balances2 = await getMinipoolBalances(); 53 | const details2 = await getNodeDetails(); 54 | 55 | // Verify results 56 | const delta = balances1.nodeDepositBalance - amount; 57 | assertBN.equal(balances2.nodeDepositBalance, delta); 58 | assertBN.equal(balances2.userDepositBalance - balances1.userDepositBalance, delta); 59 | assertBN.equal(balances2.nodeDepositCredit - balances1.nodeDepositCredit, delta); 60 | assertBN.equal(balances2.ethMatched - balances1.ethMatched, delta); 61 | 62 | // Overall number of minipools shouldn't change 63 | assertBN.equal(details2.totalCount, details1.totalCount); 64 | // Prev bond amount should decrement by 1 65 | assertBN.equal(details1.prevBondCount - details2.prevBondCount, 1n); 66 | // New bond amount should increment by 1 67 | assertBN.equal(details2.newBondCount - details1.newBondCount, 1n); 68 | } 69 | -------------------------------------------------------------------------------- /test/dao/scenario-dao-protocol-treasury.js: -------------------------------------------------------------------------------- 1 | import { RocketClaimDAO, RocketTokenRPL } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | const helpers = require('@nomicfoundation/hardhat-network-helpers'); 5 | 6 | export async function payOutContracts(_contractNames, txOptions) { 7 | // Load contracts 8 | const rocketClaimDAO = await RocketClaimDAO.deployed(); 9 | 10 | // Calculate expected payouts 11 | let contracts = []; 12 | let expectedPayouts = {}; 13 | for (const name of _contractNames) { 14 | contracts.push(await rocketClaimDAO.getContract(name)); 15 | } 16 | 17 | const currentTime = await helpers.time.latest(); 18 | 19 | for (const contract of contracts) { 20 | const lastPaymentTime = Number(contract.lastPaymentTime); 21 | const periodLength = Number(contract.periodLength); 22 | const numPeriods = Number(contract.numPeriods); 23 | const periodsPaid = Number(contract.periodsPaid); 24 | 25 | if (periodsPaid >= numPeriods) { 26 | continue; 27 | } 28 | 29 | let periodsToPay = Math.floor((currentTime - lastPaymentTime) / periodLength); 30 | if (periodsToPay + periodsPaid > numPeriods) { 31 | periodsToPay = numPeriods - periodsPaid; 32 | } 33 | const expectedPayout = contract.amountPerPeriod * BigInt(periodsToPay); 34 | 35 | if (!expectedPayouts.hasOwnProperty(contract.recipient)) { 36 | expectedPayouts[contract.recipient] = 0n; 37 | } 38 | expectedPayouts[contract.recipient] = expectedPayouts[contract.recipient] + expectedPayout; 39 | } 40 | 41 | async function getBalances() { 42 | let balances = {}; 43 | for (const address in expectedPayouts) { 44 | balances[address] = await rocketClaimDAO.getBalance(address); 45 | } 46 | return balances; 47 | } 48 | 49 | // Record balances before, execute, record balances after 50 | const balancesBefore = await getBalances(); 51 | await rocketClaimDAO.connect(txOptions.from).payOutContracts(_contractNames, txOptions); 52 | const balancesAfter = await getBalances(); 53 | 54 | // Check balance deltas 55 | for (const address in expectedPayouts) { 56 | const delta = balancesAfter[address] - balancesBefore[address]; 57 | assertBN.equal(delta, expectedPayouts[address], 'Unexpected change in balance'); 58 | } 59 | } 60 | 61 | export async function withdrawBalance(recipient, txOptions) { 62 | // Load contracts 63 | const rocketClaimDAO = await RocketClaimDAO.deployed(); 64 | const rocketTokenRPL = await RocketTokenRPL.deployed(); 65 | 66 | // Get balance before withdrawal 67 | const balanceBefore = await rocketClaimDAO.getBalance(recipient); 68 | const tokenBalanceBefore = await rocketTokenRPL.balanceOf(recipient); 69 | 70 | // Withdraw 71 | await rocketClaimDAO.connect(txOptions.from).withdrawBalance(recipient, txOptions); 72 | 73 | // Check change in balances 74 | const balanceAfter = await rocketClaimDAO.getBalance(recipient); 75 | const tokenBalanceAfter = await rocketTokenRPL.balanceOf(recipient); 76 | 77 | assertBN.equal(balanceAfter, 0n, 'Balance did not zero'); 78 | assertBN.equal(tokenBalanceAfter - tokenBalanceBefore, balanceBefore, 'Unexpected change in RPL balance'); 79 | 80 | return balanceBefore; 81 | } 82 | 83 | -------------------------------------------------------------------------------- /test/auction/scenario-claim-bid.js: -------------------------------------------------------------------------------- 1 | import { RocketAuctionManager, RocketTokenRPL, RocketVault } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | // Claim RPL from a lot 5 | export async function claimBid(lotIndex, txOptions) { 6 | 7 | // Load contracts 8 | const [ 9 | rocketAuctionManager, 10 | rocketTokenRPL, 11 | rocketVault, 12 | ] = await Promise.all([ 13 | RocketAuctionManager.deployed(), 14 | RocketTokenRPL.deployed(), 15 | RocketVault.deployed(), 16 | ]); 17 | 18 | // Get auction contract details 19 | function getContractDetails() { 20 | return Promise.all([ 21 | rocketAuctionManager.getAllottedRPLBalance(), 22 | rocketAuctionManager.getRemainingRPLBalance(), 23 | ]).then( 24 | ([allottedRplBalance, remainingRplBalance]) => 25 | ({ allottedRplBalance, remainingRplBalance }), 26 | ); 27 | } 28 | 29 | // Get lot details 30 | function getLotDetails(bidderAddress) { 31 | return Promise.all([ 32 | rocketAuctionManager.getLotAddressBidAmount(lotIndex, bidderAddress), 33 | rocketAuctionManager.getLotCurrentPrice(lotIndex), 34 | ]).then( 35 | ([addressBidAmount, currentPrice]) => 36 | ({ addressBidAmount, currentPrice }), 37 | ); 38 | } 39 | 40 | // Get balances 41 | function getBalances(bidderAddress) { 42 | return Promise.all([ 43 | rocketTokenRPL.balanceOf(bidderAddress), 44 | rocketTokenRPL.balanceOf(rocketVault.target), 45 | rocketVault.balanceOfToken('rocketAuctionManager', rocketTokenRPL.target), 46 | ]).then( 47 | ([bidderRpl, vaultRpl, contractRpl]) => 48 | ({ bidderRpl, vaultRpl, contractRpl }), 49 | ); 50 | } 51 | 52 | // Get initial details & balances 53 | let [details1, lot1, balances1] = await Promise.all([ 54 | getContractDetails(), 55 | getLotDetails(txOptions.from), 56 | getBalances(txOptions.from), 57 | ]); 58 | 59 | // Claim RPL 60 | await rocketAuctionManager.connect(txOptions.from).claimBid(lotIndex, txOptions); 61 | 62 | // Get updated details & balances 63 | let [details2, lot2, balances2] = await Promise.all([ 64 | getContractDetails(), 65 | getLotDetails(txOptions.from), 66 | getBalances(txOptions.from), 67 | ]); 68 | 69 | // Get expected values 70 | const calcBase = '1'.ether; 71 | const expectedRplAmount = calcBase * lot1.addressBidAmount / lot1.currentPrice; 72 | 73 | // Check details 74 | assertBN.equal(details2.allottedRplBalance, details1.allottedRplBalance - expectedRplAmount, 'Incorrect updated contract allotted RPL balance'); 75 | assertBN.equal(details2.remainingRplBalance, details1.remainingRplBalance, 'Contract remaining RPL balance updated and should not have'); 76 | assertBN.equal(lot2.addressBidAmount, 0, 'Incorrect updated address bid amount'); 77 | 78 | // Check balances 79 | assertBN.equal(balances2.bidderRpl, balances1.bidderRpl + expectedRplAmount, 'Incorrect updated address RPL balance'); 80 | assertBN.equal(balances2.contractRpl, balances1.contractRpl - expectedRplAmount, 'Incorrect updated auction contract RPL balance'); 81 | assertBN.equal(balances2.vaultRpl, balances1.vaultRpl - expectedRplAmount, 'Incorrect updated vault RPL balance'); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /test/rocket-pool-tests.js: -------------------------------------------------------------------------------- 1 | import { beforeEach, afterEach, before, after } from 'mocha'; 2 | import { endSnapShot, startSnapShot } from './_utils/snapshotting'; 3 | import { deployRocketPool } from './_helpers/deployment'; 4 | import { setDefaultParameters } from './_helpers/defaults'; 5 | import { suppressLog } from './_helpers/console'; 6 | import { injectBNHelpers } from './_helpers/bn'; 7 | import { checkInvariants } from './_helpers/invariants'; 8 | 9 | import auctionTests from './auction/auction-tests'; 10 | import daoProtocolTests from './dao/dao-protocol-tests'; 11 | import daoProtocolTreasuryTests from './dao/dao-protocol-treasury-tests'; 12 | import daoNodeTrustedTests from './dao/dao-node-trusted-tests'; 13 | import daoSecurityTests from './dao/dao-security-tests'; 14 | import depositPoolTests from './deposit/deposit-pool-tests'; 15 | import minipoolScrubTests from './minipool/minipool-scrub-tests'; 16 | import minipoolTests from './minipool/minipool-tests'; 17 | import minipoolVacantTests from './minipool/minipool-vacant-tests'; 18 | import minipoolStatusTests from './minipool/minipool-status-tests'; 19 | import minipoolWithdrawalTests from './minipool/minipool-withdrawal-tests'; 20 | import networkBalancesTests from './network/network-balances-tests'; 21 | import networkPenaltiesTests from './network/network-penalties-tests'; 22 | import networkFeesTests from './network/network-fees-tests'; 23 | import networkPricesTests from './network/network-prices-tests'; 24 | import networkSnapshotsTests from './network/network-snapshots-tests'; 25 | import networkVotingTests from './network/network-voting-tests'; 26 | import nodeDepositTests from './node/node-deposit-tests'; 27 | import nodeManagerTests from './node/node-manager-tests'; 28 | import nodeStakingTests from './node/node-staking-tests'; 29 | import nodeDistributorTests from './node/node-distributor-tests'; 30 | import rethTests from './token/reth-tests'; 31 | import rplTests from './token/rpl-tests'; 32 | import rewardsPoolTests from './rewards/rewards-tests'; 33 | 34 | // Header 35 | console.log('\n'); 36 | console.log('______ _ _ ______ _ '); 37 | console.log('| ___ \\ | | | | | ___ \\ | |'); 38 | console.log('| |_/ /___ ___| | _____| |_ | |_/ /__ ___ | |'); 39 | console.log('| // _ \\ / __| |/ / _ \\ __| | __/ _ \\ / _ \\| |'); 40 | console.log('| |\\ \\ (_) | (__| < __/ |_ | | | (_) | (_) | |'); 41 | console.log('\\_| \\_\\___/ \\___|_|\\_\\___|\\__| \\_| \\___/ \\___/|_|'); 42 | 43 | // BN helpers 44 | injectBNHelpers(); 45 | 46 | // State snapshotting and gas usage tracking 47 | beforeEach(startSnapShot); 48 | afterEach(checkInvariants); 49 | afterEach(endSnapShot); 50 | 51 | before(async function() { 52 | // Deploy Rocket Pool 53 | await deployRocketPool(); 54 | // Set starting parameters for all tests 55 | await setDefaultParameters(); 56 | }); 57 | 58 | // Run tests 59 | auctionTests(); 60 | daoProtocolTests(); 61 | daoProtocolTreasuryTests(); 62 | daoNodeTrustedTests(); 63 | daoSecurityTests(); 64 | depositPoolTests(); 65 | minipoolScrubTests(); 66 | minipoolTests(); 67 | minipoolVacantTests(); 68 | minipoolStatusTests(); 69 | minipoolWithdrawalTests(); 70 | networkBalancesTests(); 71 | networkPenaltiesTests(); 72 | networkFeesTests(); 73 | networkPricesTests(); 74 | networkSnapshotsTests(); 75 | networkVotingTests(); 76 | nodeDepositTests(); 77 | nodeManagerTests(); 78 | nodeStakingTests(); 79 | nodeDistributorTests(); 80 | rethTests(); 81 | rplTests(); 82 | rewardsPoolTests(); 83 | -------------------------------------------------------------------------------- /contracts/interface/dao/protocol/RocketDAOProtocolProposalInterface.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | pragma abicoder v2; 4 | 5 | import "../../../types/SettingType.sol"; 6 | import "./RocketDAOProtocolVerifierInterface.sol"; 7 | 8 | interface RocketDAOProtocolProposalInterface { 9 | // Possible states that a proposal may be in 10 | enum ProposalState { 11 | Pending, 12 | ActivePhase1, 13 | ActivePhase2, 14 | Destroyed, 15 | Vetoed, 16 | QuorumNotMet, 17 | Defeated, 18 | Succeeded, 19 | Expired, 20 | Executed 21 | } 22 | 23 | enum VoteDirection { 24 | NoVote, 25 | Abstain, 26 | For, 27 | Against, 28 | AgainstWithVeto 29 | } 30 | 31 | function getTotal() external view returns (uint256); 32 | function getProposer(uint256 _proposalID) external view returns (address); 33 | function getMessage(uint256 _proposalID) external view returns (string memory); 34 | function getStart(uint256 _proposalID) external view returns (uint256); 35 | function getPhase1End(uint256 _proposalID) external view returns (uint256); 36 | function getPhase2End(uint256 _proposalID) external view returns (uint256); 37 | function getExpires(uint256 _proposalID) external view returns (uint256); 38 | function getCreated(uint256 _proposalID) external view returns (uint256); 39 | function getVotingPowerFor(uint256 _proposalID) external view returns (uint256); 40 | function getVotingPowerAgainst(uint256 _proposalID) external view returns (uint256); 41 | function getVotingPowerVeto(uint256 _proposalID) external view returns (uint256); 42 | function getVotingPowerAbstained(uint256 _proposalID) external view returns (uint256); 43 | function getVotingPowerRequired(uint256 _proposalID) external view returns (uint256); 44 | function getDestroyed(uint256 _proposalID) external view returns (bool); 45 | function getFinalised(uint256 _proposalID) external view returns (bool); 46 | function getExecuted(uint256 _proposalID) external view returns (bool); 47 | function getVetoQuorum(uint256 _proposalID) external view returns (uint256); 48 | function getVetoed(uint256 _proposalID) external view returns (bool); 49 | function getPayload(uint256 _proposalID) external view returns (bytes memory); 50 | function getReceiptHasVoted(uint256 _proposalID, address _nodeAddress) external view returns (bool); 51 | function getReceiptHasVotedPhase1(uint256 _proposalID, address _nodeAddress) external view returns (bool); 52 | function getReceiptDirection(uint256 _proposalID, address _nodeAddress) external view returns (VoteDirection); 53 | function getState(uint256 _proposalID) external view returns (ProposalState); 54 | 55 | function getProposalBlock(uint256 _proposalID) external view returns (uint256); 56 | function getProposalVetoQuorum(uint256 _proposalID) external view returns (uint256); 57 | 58 | function propose(string memory _proposalMessage, bytes memory _payload, uint32 _blockNumber, Types.Node[] calldata _treeNodes) external returns (uint256); 59 | function vote(uint256 _proposalID, VoteDirection _vote, uint256 _votingPower, uint256 _nodeIndex, Types.Node[] calldata _witness) external; 60 | function overrideVote(uint256 _proposalID, VoteDirection _voteDirection) external; 61 | function finalise(uint256 _proposalID) external; 62 | function execute(uint256 _proposalID) external; 63 | function destroy(uint256 _proposalID) external; 64 | } 65 | -------------------------------------------------------------------------------- /contracts/interface/minipool/RocketMinipoolManagerInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.0 <0.9.0; 2 | pragma abicoder v2; 3 | 4 | // SPDX-License-Identifier: GPL-3.0-only 5 | 6 | import "../../types/MinipoolDeposit.sol"; 7 | import "../../types/MinipoolDetails.sol"; 8 | import "./RocketMinipoolInterface.sol"; 9 | 10 | interface RocketMinipoolManagerInterface { 11 | function getMinipoolCount() external view returns (uint256); 12 | function getStakingMinipoolCount() external view returns (uint256); 13 | function getFinalisedMinipoolCount() external view returns (uint256); 14 | function getActiveMinipoolCount() external view returns (uint256); 15 | function getMinipoolRPLSlashed(address _minipoolAddress) external view returns (bool); 16 | function getMinipoolCountPerStatus(uint256 offset, uint256 limit) external view returns (uint256, uint256, uint256, uint256, uint256); 17 | function getPrelaunchMinipools(uint256 offset, uint256 limit) external view returns (address[] memory); 18 | function getMinipoolAt(uint256 _index) external view returns (address); 19 | function getNodeMinipoolCount(address _nodeAddress) external view returns (uint256); 20 | function getNodeActiveMinipoolCount(address _nodeAddress) external view returns (uint256); 21 | function getNodeFinalisedMinipoolCount(address _nodeAddress) external view returns (uint256); 22 | function getNodeStakingMinipoolCount(address _nodeAddress) external view returns (uint256); 23 | function getNodeStakingMinipoolCountBySize(address _nodeAddress, uint256 _depositSize) external view returns (uint256); 24 | function getNodeMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address); 25 | function getNodeValidatingMinipoolCount(address _nodeAddress) external view returns (uint256); 26 | function getNodeValidatingMinipoolAt(address _nodeAddress, uint256 _index) external view returns (address); 27 | function getMinipoolByPubkey(bytes calldata _pubkey) external view returns (address); 28 | function getMinipoolExists(address _minipoolAddress) external view returns (bool); 29 | function getMinipoolDestroyed(address _minipoolAddress) external view returns (bool); 30 | function getMinipoolPubkey(address _minipoolAddress) external view returns (bytes memory); 31 | function updateNodeStakingMinipoolCount(uint256 _previousBond, uint256 _newBond, uint256 _previousFee, uint256 _newFee) external; 32 | function getMinipoolWithdrawalCredentials(address _minipoolAddress) external pure returns (bytes memory); 33 | function createMinipool(address _nodeAddress, uint256 _salt) external returns (RocketMinipoolInterface); 34 | function createVacantMinipool(address _nodeAddress, uint256 _salt, bytes calldata _validatorPubkey, uint256 _bondAmount, uint256 _currentBalance) external returns (RocketMinipoolInterface); 35 | function removeVacantMinipool() external; 36 | function getVacantMinipoolCount() external view returns (uint256); 37 | function getVacantMinipoolAt(uint256 _index) external view returns (address); 38 | function destroyMinipool() external; 39 | function incrementNodeStakingMinipoolCount(address _nodeAddress) external; 40 | function decrementNodeStakingMinipoolCount(address _nodeAddress) external; 41 | function tryDistribute(address _nodeAddress) external; 42 | function incrementNodeFinalisedMinipoolCount(address _nodeAddress) external; 43 | function setMinipoolPubkey(bytes calldata _pubkey) external; 44 | function getMinipoolDepositType(address _minipoolAddress) external view returns (MinipoolDeposit); 45 | } 46 | -------------------------------------------------------------------------------- /test/rewards/scenario-claim-and-stake-rewards.js: -------------------------------------------------------------------------------- 1 | import { 2 | RocketMerkleDistributorMainnet, 3 | RocketNodeManager, RocketNodeStaking, 4 | RocketRewardsPool, 5 | RocketTokenRPL, 6 | } from '../_utils/artifacts'; 7 | import { parseRewardsMap } from '../_utils/merkle-tree'; 8 | import { assertBN } from '../_helpers/bn'; 9 | 10 | const hre = require('hardhat'); 11 | const ethers = hre.ethers; 12 | 13 | // Submit network prices 14 | export async function claimAndStakeRewards(nodeAddress, indices, rewards, stakeAmount, txOptions) { 15 | // Load contracts 16 | const [ 17 | rocketRewardsPool, 18 | rocketNodeManager, 19 | rocketNodeStaking, 20 | rocketMerkleDistributorMainnet, 21 | rocketTokenRPL, 22 | ] = await Promise.all([ 23 | RocketRewardsPool.deployed(), 24 | RocketNodeManager.deployed(), 25 | RocketNodeStaking.deployed(), 26 | RocketMerkleDistributorMainnet.deployed(), 27 | RocketTokenRPL.deployed(), 28 | ]); 29 | 30 | // Get node withdrawal address 31 | let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress(nodeAddress); 32 | 33 | // Get balances 34 | function getBalances() { 35 | return Promise.all([ 36 | rocketRewardsPool.getClaimIntervalTimeStart(), 37 | rocketTokenRPL.balanceOf(nodeWithdrawalAddress), 38 | rocketNodeStaking.getNodeRPLStake(nodeAddress), 39 | ethers.provider.getBalance(nodeWithdrawalAddress), 40 | rocketMerkleDistributorMainnet.getOutstandingEth(nodeWithdrawalAddress), 41 | ]).then( 42 | ([claimIntervalTimeStart, nodeRpl, rplStake, nodeEth, outstandingEth]) => 43 | ({claimIntervalTimeStart, nodeRpl, rplStake, nodeEth, outstandingEth}) 44 | ); 45 | } 46 | 47 | let [balances1] = await Promise.all([ 48 | getBalances(), 49 | ]); 50 | 51 | // Construct claim arguments 52 | let claimer = nodeAddress; 53 | let claimerIndices = []; 54 | let amountsRPL = []; 55 | let amountsETH = []; 56 | let proofs = []; 57 | let totalAmountRPL = 0n; 58 | let totalAmountETH = 0n; 59 | 60 | for (let i = 0; i < indices.length; i++) { 61 | let treeData = parseRewardsMap(rewards[i]); 62 | 63 | let proof = treeData.proof.claims[ethers.getAddress(claimer)]; 64 | 65 | if (!proof) { 66 | throw new Error('No proof in merkle tree for ' + claimer) 67 | } 68 | 69 | claimerIndices.push(proof.index); 70 | amountsRPL.push(proof.amountRPL); 71 | amountsETH.push(proof.amountETH); 72 | proofs.push(proof.proof); 73 | 74 | totalAmountRPL = totalAmountRPL + proof.amountRPL; 75 | totalAmountETH = totalAmountETH + proof.amountETH; 76 | } 77 | 78 | const tx = await rocketMerkleDistributorMainnet.connect(txOptions.from).claimAndStake(nodeAddress, indices, amountsRPL, amountsETH, proofs, stakeAmount, txOptions); 79 | let gasUsed = 0n; 80 | 81 | if(nodeWithdrawalAddress.toLowerCase() === txOptions.from.address.toLowerCase()) { 82 | const txReceipt = await tx.wait(); 83 | gasUsed = BigInt(txReceipt.gasUsed * txReceipt.gasPrice); 84 | } 85 | 86 | let [balances2] = await Promise.all([ 87 | getBalances(), 88 | ]); 89 | 90 | let amountStaked = balances2.rplStake - balances1.rplStake; 91 | 92 | assertBN.equal(balances2.nodeRpl - balances1.nodeRpl, totalAmountRPL - amountStaked, 'Incorrect updated node RPL balance'); 93 | const ethDiff = balances2.nodeEth - balances1.nodeEth + gasUsed + balances2.outstandingEth - balances1.outstandingEth; 94 | assertBN.equal(ethDiff, totalAmountETH, 'Incorrect updated node ETH balance'); 95 | } 96 | -------------------------------------------------------------------------------- /contracts/contract/dao/protocol/settings/RocketDAOProtocolSettings.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0-only 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | import "../../../RocketBase.sol"; 5 | import "../../../../interface/dao/protocol/settings/RocketDAOProtocolSettingsInterface.sol"; 6 | 7 | // Settings in RP which the DAO will have full control over 8 | // This settings contract enables storage using setting paths with namespaces, rather than explicit set methods 9 | abstract contract RocketDAOProtocolSettings is RocketBase, RocketDAOProtocolSettingsInterface { 10 | 11 | 12 | // The namespace for a particular group of settings 13 | bytes32 settingNameSpace; 14 | 15 | 16 | // Only allow updating from the DAO proposals contract 17 | modifier onlyDAOProtocolProposal() { 18 | // If this contract has been initialised, only allow access from the proposals contract 19 | if(getBool(keccak256(abi.encodePacked(settingNameSpace, "deployed")))) require(getContractAddress("rocketDAOProtocolProposals") == msg.sender, "Only DAO Protocol Proposals contract can update a setting"); 20 | _; 21 | } 22 | 23 | 24 | // Construct 25 | constructor(RocketStorageInterface _rocketStorageAddress, string memory _settingNameSpace) RocketBase(_rocketStorageAddress) { 26 | // Apply the setting namespace 27 | settingNameSpace = keccak256(abi.encodePacked("dao.protocol.setting.", _settingNameSpace)); 28 | } 29 | 30 | 31 | /*** Uints ****************/ 32 | 33 | // A general method to return any setting given the setting path is correct, only accepts uints 34 | function getSettingUint(string memory _settingPath) public view override returns (uint256) { 35 | return getUint(keccak256(abi.encodePacked(settingNameSpace, _settingPath))); 36 | } 37 | 38 | // Update a Uint setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed 39 | function setSettingUint(string memory _settingPath, uint256 _value) virtual public override onlyDAOProtocolProposal { 40 | // Update setting now 41 | setUint(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value); 42 | } 43 | 44 | 45 | /*** Bools ****************/ 46 | 47 | // A general method to return any setting given the setting path is correct, only accepts bools 48 | function getSettingBool(string memory _settingPath) public view override returns (bool) { 49 | return getBool(keccak256(abi.encodePacked(settingNameSpace, _settingPath))); 50 | } 51 | 52 | // Update a setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed 53 | function setSettingBool(string memory _settingPath, bool _value) virtual public override onlyDAOProtocolProposal { 54 | // Update setting now 55 | setBool(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value); 56 | } 57 | 58 | 59 | /*** Addresses ****************/ 60 | 61 | // A general method to return any setting given the setting path is correct, only accepts addresses 62 | function getSettingAddress(string memory _settingPath) external view override returns (address) { 63 | return getAddress(keccak256(abi.encodePacked(settingNameSpace, _settingPath))); 64 | } 65 | 66 | // Update a setting, can only be executed by the DAO contract when a majority on a setting proposal has passed and been executed 67 | function setSettingAddress(string memory _settingPath, address _value) virtual external override onlyDAOProtocolProposal { 68 | // Update setting now 69 | setAddress(keccak256(abi.encodePacked(settingNameSpace, _settingPath)), _value); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /test/minipool/scenario-skim-rewards.js: -------------------------------------------------------------------------------- 1 | import { RocketNodeManager, RocketTokenRETH } from '../_utils/artifacts'; 2 | import { assertBN } from '../_helpers/bn'; 3 | 4 | const hre = require('hardhat'); 5 | const ethers = hre.ethers; 6 | 7 | export async function skimRewards(minipool, txOptions) { 8 | // Load contracts 9 | const [ 10 | rocketTokenRETH, 11 | rocketNodeManager, 12 | ] = await Promise.all([ 13 | RocketTokenRETH.deployed(), 14 | RocketNodeManager.deployed(), 15 | ]); 16 | 17 | // Get parameters 18 | let [ 19 | nodeAddress, 20 | nodeFee, 21 | nodeCapital, 22 | userCapital, 23 | ] = await Promise.all([ 24 | minipool.getNodeAddress(), 25 | minipool.getNodeFee(), 26 | minipool.getNodeDepositBalance(), 27 | minipool.getUserDepositBalance(), 28 | ]); 29 | 30 | // Get node parameters 31 | let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress(nodeAddress); 32 | 33 | // Get balances 34 | function getBalances() { 35 | return Promise.all([ 36 | ethers.provider.getBalance(rocketTokenRETH.target), 37 | ethers.provider.getBalance(nodeWithdrawalAddress), 38 | ethers.provider.getBalance(minipool.target), 39 | minipool.getNodeRefundBalance(), 40 | ]).then( 41 | ([rethContractEth, nodeWithdrawalEth, minipoolEth, nodeRefundBalance]) => 42 | ({ rethContractEth, nodeWithdrawalEth, minipoolEth, nodeRefundBalance }), 43 | ); 44 | } 45 | 46 | // Get initial balances & withdrawal processed status 47 | const balances1 = await getBalances(); 48 | 49 | const realBalance = balances1.minipoolEth - balances1.nodeRefundBalance; 50 | assertBN.isBelow(realBalance, '8'.ether, 'Cannot skim rewards greater than 8 ETH'); 51 | 52 | // Set gas price 53 | txOptions.gasPrice = '20'.gwei; 54 | 55 | // Payout the balances now 56 | let tx = await minipool.connect(txOptions.from).distributeBalance(true, txOptions); 57 | let txReceipt = await tx.wait(); 58 | 59 | let txFee = txOptions.gasPrice * txReceipt.gasUsed; 60 | 61 | // Get updated balances & withdrawal processed status 62 | const balances2 = await getBalances(); 63 | 64 | // Add the fee back into the balance to make assertions easier 65 | if (txOptions.from === nodeWithdrawalAddress) { 66 | balances2.nodeWithdrawalEth = balances2.nodeWithdrawalEth + txFee; 67 | } 68 | 69 | // Calculate actual rewards 70 | const nodeBalanceChange = balances2.nodeWithdrawalEth - balances1.nodeWithdrawalEth; 71 | const nodeRefundBalanceChange = balances2.nodeRefundBalance - balances2.nodeRefundBalance; 72 | const rethBalanceChange = balances2.rethContractEth - balances1.rethContractEth; 73 | 74 | // Calculate expected rewards 75 | const rewards = balances1.minipoolEth - balances1.nodeRefundBalance; 76 | const nodePortion = rewards * nodeCapital / (userCapital + nodeCapital); 77 | const userPortion = rewards - nodePortion; 78 | const nodeRewards = nodePortion + (userPortion * nodeFee / '1'.ether); 79 | const userRewards = rewards - nodeRewards; 80 | 81 | // Check rETH balance has increased by expected amount 82 | assertBN.equal(rethBalanceChange, userRewards, 'Incorrect user rewards distributed'); 83 | 84 | if (txOptions.from === nodeWithdrawalAddress || txOptions.from === nodeAddress) { 85 | // When NO calls it should send the skimmed rewards + any accured in the refund balance to withdrawal address 86 | assertBN.equal(nodeBalanceChange, nodeRewards + balances1.nodeRefundBalance, 'Incorrect node rewards distributed'); 87 | } else { 88 | // When someone else calls it just accrues in refund balance 89 | assertBN.equal(nodeRefundBalanceChange, nodeRefundBalanceChange, 'Incorrect node rewards distributed'); 90 | } 91 | } 92 | --------------------------------------------------------------------------------