diff --git a/contracts/Nexus.sol b/contracts/Nexus.sol index e70cea2..dd05043 100644 --- a/contracts/Nexus.sol +++ b/contracts/Nexus.sol @@ -13,14 +13,15 @@ import {BytesArrayLibrary} from "./libraries/BytesArrayLibrary.sol"; /** * @title Nexus Core Contract + * @author RohitAudit * @dev This contract is heart and soul of Nexus Network and is used for Operations like * 1. Onboarding of rollup to Nexus Network - * 2. Change in staking limit for rollup + * 2. Change parameters for rollup * 3. Whitelisting rollup address * 4. Submitting keys to rollup bridge * 5. Submitting keyshares to SSV contract * 6. Recharge funds in SSV contract for validator operation - * 7. Reward distribution for rollup to DAO and Nexus Fee Contract + * 7. Keep track of validator status and exits */ contract Nexus is INexusInterface, Ownable, Proxiable { using EnumerableSet for EnumerableSet.AddressSet; diff --git a/contracts/NodeOperator.sol b/contracts/NodeOperator.sol index 3661b70..2bfbd4e 100644 --- a/contracts/NodeOperator.sol +++ b/contracts/NodeOperator.sol @@ -4,9 +4,22 @@ import {Ownable} from "./utils/NexusOwnable.sol"; import {Proxiable} from "./utils/UUPSUpgreadable.sol"; import {INodeOperator} from "./interfaces/INodeOperator.sol"; +/** + * @title Nexus Node Operator Contract + * @author RohitAudit + * @dev This contract handles the Node Operator operations which includes: + * 1. Registration of node operators + * 2. Updation of node operators + * 3. Creation of SSV clusters + * + * In future, we will also introduce scoring in the contract itself. + */ contract NodeOperator is Ownable, Proxiable, INodeOperator{ + // This stores the DKG ip needed for DKG ceremony mapping(uint64=>string) public ssvDKGIP; + + // This stores the id of operators in a particular cluster mapping (uint64=>uint64[]) public ssvClusters; function initialize() public initilizeOnce { @@ -17,6 +30,13 @@ contract NodeOperator is Ownable, Proxiable, INodeOperator{ updateCodeAddress(newImplemetation); } + /** + * This function is used to register node operators + * @param _operator_id: Operator ID as registered with SSV + * @param _pub_key: Operator public key used for key share creation + * @param _ip_address: DKG IP used for DKG ceremony + * @param name: Name of the operator registered with SSV + */ function registerSSVOperator(uint64 _operator_id, string calldata _pub_key, string calldata _ip_address, string calldata name) external onlyOwner{ bytes memory ip = bytes(ssvDKGIP[_operator_id]); if (ip.length != 0) revert OperatorAlreadyRegistered(); @@ -24,6 +44,11 @@ contract NodeOperator is Ownable, Proxiable, INodeOperator{ emit SSVOperatorRegistered(name,_operator_id,_pub_key,_ip_address); } + /** + * This function is used to update the DKG IP for node operator + * @param _operator_id: Operator ID as registered with SSV + * @param _ip_address: DKG IP used for DKG ceremony + */ function updateSSVOperatorIP(uint64 _operator_id,string calldata _ip_address) external onlyOwner{ bytes memory ip = bytes(ssvDKGIP[_operator_id]); if (ip.length == 0) revert OperatorNotRegistered(); @@ -31,6 +56,11 @@ contract NodeOperator is Ownable, Proxiable, INodeOperator{ emit SSVOperatorUpdated(_operator_id,_ip_address); } + /** + * This function is used to create clusters using node operators + * @param operatorIds: Operator IDs as registered with SSV + * @param clusterId: Cluster ID associated with the cluster + */ function addCluster( uint64[] calldata operatorIds, uint64 clusterId diff --git a/contracts/ValidatorExecutionRewards.sol b/contracts/ValidatorExecutionRewards.sol index d2b3670..c9b124e 100644 --- a/contracts/ValidatorExecutionRewards.sol +++ b/contracts/ValidatorExecutionRewards.sol @@ -1,7 +1,13 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Ownable} from "./utils/NexusOwnable.sol"; - +/** + * @title Validator Execution Reward Contract + * @author RohitAudit + * @dev This contract handles the proposer awards for the validators. As the rewards are associated with particular + * validator one needs to update it in the contract as to who can claim those rewards. The reward bot performs that + * functionality by tracking proposer of the block + */ contract ValidatorExecutionRewards is Ownable{ struct RollupExecutionReward{ address rollupAdmin; @@ -40,6 +46,10 @@ contract ValidatorExecutionRewards is Ownable{ emit ExecutionRewardsReceived(msg.value); } + /** + * Used for updation of rewards for a particular rollup + * @param rewards: Array of rollups and their rewards earned by their validators + */ function updateRewardsRollup(RollupExecutionReward[] calldata rewards) external onlyRewardBot { uint256 total_rewards; for(uint i=0;i(rewardsEarned-rewardsClaimed)) revert IncorrectRewards(); } + /** + * This function can be used by rollupAdmin to claim the proposer rewards asscociated with their validators + */ function claimRewards() external { if (executionRewards[msg.sender] == 0) revert RewardNotPresent(); uint256 amount_to_send = executionRewards[msg.sender]; diff --git a/contracts/libraries/BytesArrayLibrary.sol b/contracts/libraries/BytesArrayLibrary.sol index 2c96752..ebef980 100644 --- a/contracts/libraries/BytesArrayLibrary.sol +++ b/contracts/libraries/BytesArrayLibrary.sol @@ -1,6 +1,13 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +/** + * @title BytesArray Library + * @author RohitAudit + * @dev This library is used for managing bytes array by providing following functionality: + * 1. Removing element from the array + * 2. Adding element to the array + */ library BytesArrayLibrary { function addElement(bytes[] storage arr, bytes memory data) internal { arr.push(data); diff --git a/contracts/nexus_bridge/NexusBaseBridge.sol b/contracts/nexus_bridge/NexusBaseBridge.sol index f01badc..ad09412 100644 --- a/contracts/nexus_bridge/NexusBaseBridge.sol +++ b/contracts/nexus_bridge/NexusBaseBridge.sol @@ -7,6 +7,7 @@ import {INexusBridge} from "../interfaces/INexusBridge.sol"; /** * @title Nexus Bridge Contract + * @author RohitAudit * @dev This contract is used to enable eth staking via native bridge ontract of any rollup. It * enables the integration with Nexus Network. It also gives permission to Nexus contract to submit * keys using the unique withdrawal credentials for rollup. diff --git a/contracts/nexus_bridge/NexusBridgeDAO.sol b/contracts/nexus_bridge/NexusBridgeDAO.sol index 9ce4d80..c5e2fa5 100644 --- a/contracts/nexus_bridge/NexusBridgeDAO.sol +++ b/contracts/nexus_bridge/NexusBridgeDAO.sol @@ -3,13 +3,10 @@ pragma solidity ^0.8.19; import {NexusBaseBridge} from "./NexusBaseBridge.sol"; /** - * @title Nexus Bridge Contract - * @dev This contract is used to enable eth staking via native bridge ontract of any rollup. It - * enables the integration with Nexus Network. It also gives permission to Nexus contract to submit - * keys using the unique withdrawal credentials for rollup. - * - * The staking ratio is maintained by the Nexus Contract and is set during the registration.It - * can be changed anytime by rollup while doing a transaction to the Nexus Contract. + * @title Nexus Bridge DAO Contract + * @author RohitAudit + * @dev This contract enables DAO governing body of the rollup to claim the staking rewards + * from the bridge contract and use it for Ecosystem incentives etc. */ abstract contract NexusBridgeDAO is NexusBaseBridge { uint256 public rewardsClaimed; @@ -22,7 +19,7 @@ abstract contract NexusBridgeDAO is NexusBaseBridge { if (msg.sender != DAO) revert NotDAO(); _; } - + function updateExitedValidators() external override onlyNexus { if(getRewards() < VALIDATOR_DEPOSIT) revert ValidatorNotExited(); validatorCount -= 1; diff --git a/contracts/nexus_bridge/NexusBridgeUserCValue.sol b/contracts/nexus_bridge/NexusBridgeUserCValue.sol index 4e99343..690ddc4 100644 --- a/contracts/nexus_bridge/NexusBridgeUserCValue.sol +++ b/contracts/nexus_bridge/NexusBridgeUserCValue.sol @@ -3,13 +3,10 @@ pragma solidity ^0.8.19; import {NexusBaseBridge} from "./NexusBaseBridge.sol"; /** - * @title Nexus Bridge Contract - * @dev This contract is used to enable eth staking via native bridge ontract of any rollup. It - * enables the integration with Nexus Network. It also gives permission to Nexus contract to submit - * keys using the unique withdrawal credentials for rollup. - * - * The staking ratio is maintained by the Nexus Contract and is set during the registration.It - * can be changed anytime by rollup while doing a transaction to the Nexus Contract. + * @title Nexus Bridge User C value Contract + * @author RohitAudi + * @dev This contract is used to distribute the rewards back to the users by changing the + * token value depending on the rewards earned. */ abstract contract NexusBridgeUserCValue is NexusBaseBridge { uint256 public cValue; diff --git a/contracts/nexus_bridge/NexusBridgeUserRebase.sol b/contracts/nexus_bridge/NexusBridgeUserRebase.sol index 883eef1..2b28afc 100644 --- a/contracts/nexus_bridge/NexusBridgeUserRebase.sol +++ b/contracts/nexus_bridge/NexusBridgeUserRebase.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import {NexusBaseBridge} from "./NexusBaseBridge.sol"; /** - * @title Nexus Bridge Contract + * @title Nexus Bridge Rebase Contract * @dev This contract is used to enable eth staking via native bridge ontract of any rollup. It * enables the integration with Nexus Network. It also gives permission to Nexus contract to submit * keys using the unique withdrawal credentials for rollup. diff --git a/contracts/nexus_bridge/NexusDAIBridge.sol b/contracts/nexus_bridge/NexusDAIBridge.sol index 469d0b3..3f36faf 100644 --- a/contracts/nexus_bridge/NexusDAIBridge.sol +++ b/contracts/nexus_bridge/NexusDAIBridge.sol @@ -14,7 +14,11 @@ interface IERC20 { function approve(address spender, uint256 value) external returns (bool); } - +/** + * @title Nexus DAI contract + * @author RohitAudit + * @dev + */ abstract contract NexusDai { uint256 public DAIDeposited; uint256 public DAIRedeemed; diff --git a/contracts/nexus_bridge/nexusLibrary.sol b/contracts/nexus_bridge/nexusLibrary.sol new file mode 100644 index 0000000..66332bf --- /dev/null +++ b/contracts/nexus_bridge/nexusLibrary.sol @@ -0,0 +1,166 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; +import {IDepositContract} from "../interfaces/IDepositContract.sol"; +import {INexusInterface} from "../interfaces/INexusInterface.sol"; +import {INexusBridge} from "../interfaces/INexusBridge.sol"; + +/** + * @title Nexus Tangible Library + * @author RohitAudit + * @dev This is the library contract used by any bridge contract to integrate with Nexus Network without + * adding addittional size to already contract + */ +contract NexusLibrary { + address public constant NEXUS_NETWORK = + 0x7610dd2DE44aA3c03313b4c2812C482D86F3a9e7; + address public constant NEXUS_FEE_ADDRESS = + 0x735bf02E4435dFADfE47a5FE5FBD42Ef375864A9; + + // slot to be calculated = keccak256("NAME_VARIABLE") + bytes32 public constant AMOUNT_DEPOSITED_SLOT = + 0xca4e9536f4b6163e8b3c485d13888b64170049f120695cca4a7920674f669123; + bytes32 public constant AMOUNT_WITHDRAWN_SLOT = + 0x0727682b75deaf0886514bd82c90f1c6e80521cdb4aeb9ed6f2ada2d5f20f112; + bytes32 public constant AMOUNT_SLASHED_SLOT = + 0x67a602d91fdafa346635ea0b2cfecfa87c6c87b3eb52ac04414bcb5fffd82e5f; + bytes32 public constant VALIDATOR_COUNT_SLOT = + 0x2d5a8d8ceecd33ab6923979e59fb92c16d966ad0b4d5ecdfb4adac6bafdd0ae5; + bytes32 public constant NEXUS_FEE_PERCENTAGE_SLOT = + 0xac871e33a0d6f83ef496541ab4d2cb28c9c72346647360c557889bbd13ec109d; + bytes32 public constant REWARDS_CLAIMED_SLOT = + 0x9fbc0f1af8e544d8995616c6f64d9fc6ca8bf8885fb0f08f57738b97334c0f73; + + // To be changed to the respective network addresses: + address public constant DAO = 0x14630e0428B9BbA12896402257fa09035f9F7447; + address public constant DEPOSIT_CONTRACT = + 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b; + + uint256 public constant VALIDATOR_DEPOSIT = 32 ether; + uint256 public constant BASIS_POINT = 10000; + error NotNexus(); + error IncorrectWithdrawalCredentials(); + error StakingLimitExceeding(); + error IncorrectNexusFee(); + error ValidatorNotExited(); + error WaitingForValidatorExits(); + error NotDAO(); + + event RewardsRedeemed(uint256 amount); + event SlashingUpdated(uint256 amount); + event NexusFeeChanged(uint256 _nexus_fee); + event NexusRewardsRedeemed(uint256 amount); + + modifier onlyNexus() { + if (msg.sender != NEXUS_NETWORK) revert NotNexus(); + _; + } + + modifier onlyDAO() { + if (msg.sender != DAO) revert NotDAO(); + _; + } + + function setVariable(bytes32 _slot, uint256 amount) public { + assembly { + sstore(_slot, amount) + } + } + + function getVariable(bytes32 _slot) public view returns (uint256) { + uint256 variableValue; + assembly { + variableValue := sload(_slot) + } + return variableValue; + } + + function updateExitedValidators() external onlyNexus { + if (getRewards() < VALIDATOR_DEPOSIT) revert ValidatorNotExited(); + uint256 validatorCount = getVariable(VALIDATOR_COUNT_SLOT); + validatorCount -= 1; + setVariable(VALIDATOR_COUNT_SLOT,validatorCount); + } + + function setNexusFee(uint256 _nexus_fee) external onlyNexus { + if (_nexus_fee > (BASIS_POINT) / 10) revert IncorrectNexusFee(); + setVariable(NEXUS_FEE_PERCENTAGE_SLOT, _nexus_fee); + emit NexusFeeChanged(_nexus_fee); + } + + function depositValidatorNexus( + INexusInterface.Validator[] calldata _validators, + uint256 stakingLimit + ) external onlyNexus { + for (uint i = 0; i < _validators.length; i++) { + bytes memory withdrawalFromCred = _validators[i] + .withdrawalAddress[12:]; + if ( + keccak256(withdrawalFromCred) != + keccak256(abi.encodePacked(address(this))) + ) revert IncorrectWithdrawalCredentials(); + } + uint256 validatorCount = getVariable(VALIDATOR_COUNT_SLOT); + if ( + (((validatorCount + _validators.length) * + (VALIDATOR_DEPOSIT) * + BASIS_POINT) / + (address(this).balance + + (validatorCount + _validators.length) * + (VALIDATOR_DEPOSIT))) > stakingLimit + ) revert StakingLimitExceeding(); + + for (uint i = 0; i < _validators.length; i++) { + IDepositContract(DEPOSIT_CONTRACT).deposit{ + value: VALIDATOR_DEPOSIT + }( + _validators[i].pubKey, + _validators[i].withdrawalAddress, + _validators[i].signature, + _validators[i].depositRoot + ); + } + validatorCount += _validators.length; + } + + function validatorsSlashed(uint256 amount) external onlyNexus { + setVariable(AMOUNT_SLASHED_SLOT, amount); + emit SlashingUpdated(amount); + } + + function getRewards() public view returns (uint256) { + uint256 validatorCount = getVariable(VALIDATOR_COUNT_SLOT); + uint256 amountDeposited = getVariable(AMOUNT_DEPOSITED_SLOT); + uint256 amountWithdrawn = getVariable(AMOUNT_WITHDRAWN_SLOT); + uint256 slashedAmount = getVariable(AMOUNT_SLASHED_SLOT); + return + (address(this).balance + (validatorCount * VALIDATOR_DEPOSIT)) - + (amountDeposited - amountWithdrawn) - + slashedAmount; + } + + function redeemRewards(address reward_account) external onlyDAO { + uint256 total_rewards = getRewards(); + if (total_rewards > VALIDATOR_DEPOSIT) + revert WaitingForValidatorExits(); + uint256 NexusFeePercentage = getVariable(NEXUS_FEE_PERCENTAGE_SLOT); + uint256 _nexus_rewards = (NexusFeePercentage * total_rewards) / + BASIS_POINT; + (bool nexus_success, bytes memory nexus_data) = NEXUS_FEE_ADDRESS.call{ + value: _nexus_rewards, + gas: 5000 + }(""); + if (nexus_success) { + emit NexusRewardsRedeemed(_nexus_rewards); + } + (bool dao_success, bytes memory dao_data) = reward_account.call{ + value: (total_rewards - _nexus_rewards), + gas: 5000 + }(""); + uint256 rewardsClaimed = getVariable(REWARDS_CLAIMED_SLOT); + rewardsClaimed += total_rewards; + setVariable(REWARDS_CLAIMED_SLOT, rewardsClaimed); + if (dao_success) { + emit RewardsRedeemed((total_rewards - _nexus_rewards)); + } + } +} diff --git a/contracts/utils/NexusOwnable.sol b/contracts/utils/NexusOwnable.sol index 9920b03..7d053ff 100644 --- a/contracts/utils/NexusOwnable.sol +++ b/contracts/utils/NexusOwnable.sol @@ -1,6 +1,11 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +/** + * @title Ownable Contract + * @author RohitAudit + * @dev This contract is used to implement ownable features to the contracts + */ contract Ownable { address private owner; bool private initialized = false; @@ -27,6 +32,10 @@ contract Ownable { initialized = true; } + /** + * This function transfers the ownership of the contract + * @param newOwner New Owner of the contract + */ function transferOwnership(address newOwner) external onlyOwner{ emit OwnerChanged(owner, newOwner); owner = newOwner; diff --git a/contracts/utils/NexusProxy.sol b/contracts/utils/NexusProxy.sol index f6ee147..1a66326 100644 --- a/contracts/utils/NexusProxy.sol +++ b/contracts/utils/NexusProxy.sol @@ -1,6 +1,11 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +/** + * @title Proxy Contract + * @author RohitAudit + * @dev This is the UUPS proxy contract on which the transactions are sent + */ contract Proxy{ // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7" diff --git a/contracts/utils/UUPSUpgreadable.sol b/contracts/utils/UUPSUpgreadable.sol index 3a65208..764c60e 100644 --- a/contracts/utils/UUPSUpgreadable.sol +++ b/contracts/utils/UUPSUpgreadable.sol @@ -1,6 +1,12 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +/** + * @title Proxiable + * @author RohitAudit + * @dev This contract is implemented in the implementation contract to make it upgreadable. Removing this contract + * will remove the upgreadability feature of the contract + */ contract Proxiable { // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7"