diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 00000000..3471077a --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,24 @@ +name: Publish npm package + +on: + release: + types: [created] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm install + + - name: Publish to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..9054d3fc --- /dev/null +++ b/.npmignore @@ -0,0 +1,19 @@ +/.github/ +/.vscode/ + +node_modules +.env +.env.prod +.env.stage +#Hardhat files +cache +coverage +coverage.json +artifacts +typechain-types/ +.openzeppelin/dev-*.json +.DS_Store +.history +.dccache + +package-lock.json diff --git a/README.md b/README.md index 5cd2d984..4809bc26 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SSV Network Smart Contracts -### Intro | [Architecture](./docs/architecture.md) | [Setup](./docs/setup.md) | [Tasks](./docs/tasks.md) | [Local development](./docs/local-dev.md) | [Roles](./docs/roles.md) +### Intro | [Architecture](./docs/architecture.md) | [Setup](./docs/setup.md) | [Tasks](./docs/tasks.md) | [Local development](./docs/local-dev.md) | [Roles](./docs/roles.md) | [Publish](./docs/publish.md) This repository contains the Solidity smart contracts for the SSV Network. The SSV Network is a decentralized network for the operation of Ethereum validators. It allows for secure, scalable, and decentralized staking on the Ethereum blockchain. The key elements of this system are represented through several Ethereum smart contracts, all of which are outlined below. @@ -39,4 +39,4 @@ If you believe you've identified a vulnerability in our smart contracts, we enco Visit our [bounty page](https://immunefi.com/bounty/ssvnetwork/) to get detailed information on the types of vulnerabilities we're interested in, potential reward amounts, and the guidelines for participation. -Please note: Failing to abide by the participation guidelines may result in disqualification from the program and forfeiture of potential rewards. \ No newline at end of file +Please note: Failing to abide by the participation guidelines may result in disqualification from the program and forfeiture of potential rewards. diff --git a/abis/SSVNetwork.json b/abis/SSVNetwork.json new file mode 100644 index 00000000..1167cb48 --- /dev/null +++ b/abis/SSVNetwork.json @@ -0,0 +1,1717 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalNotWithinTimeframe", + "type": "error" + }, + { + "inputs": [], + "name": "CallerNotOwner", + "type": "error" + }, + { + "inputs": [], + "name": "CallerNotWhitelisted", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterAlreadyEnabled", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterDoesNotExists", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterIsLiquidated", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterNotLiquidatable", + "type": "error" + }, + { + "inputs": [], + "name": "ExceedValidatorLimit", + "type": "error" + }, + { + "inputs": [], + "name": "FeeExceedsIncreaseLimit", + "type": "error" + }, + { + "inputs": [], + "name": "FeeIncreaseNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "FeeTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "FeeTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectClusterState", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectValidatorState", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOperatorIdsLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPublicKeyLength", + "type": "error" + }, + { + "inputs": [], + "name": "MaxValueExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "NewBlockPeriodIsBelowMinimum", + "type": "error" + }, + { + "inputs": [], + "name": "NoFeeDeclared", + "type": "error" + }, + { + "inputs": [], + "name": "NotAuthorized", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorsListNotUnique", + "type": "error" + }, + { + "inputs": [], + "name": "SameFeeChangeNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "TargetModuleDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "TokenTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "UnsortedOperatorsList", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorDoesNotExist", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "ClusterDeposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "ClusterLiquidated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "ClusterReactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "ClusterWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "value", + "type": "uint64" + } + ], + "name": "DeclareOperatorFeePeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "value", + "type": "uint64" + } + ], + "name": "ExecuteOperatorFeePeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipientAddress", + "type": "address" + } + ], + "name": "FeeRecipientAddressUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "value", + "type": "uint64" + } + ], + "name": "LiquidationThresholdPeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "MinimumLiquidationCollateralUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "NetworkEarningsWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newFee", + "type": "uint256" + } + ], + "name": "NetworkFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "OperatorAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "OperatorFeeDeclarationCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "OperatorFeeDeclared", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "OperatorFeeExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "value", + "type": "uint64" + } + ], + "name": "OperatorFeeIncreaseLimitUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "maxFee", + "type": "uint64" + } + ], + "name": "OperatorMaximumFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "OperatorRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "address", + "name": "whitelisted", + "type": "address" + } + ], + "name": "OperatorWhitelistUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "OperatorWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "shares", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "ValidatorAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "indexed": true, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "name": "ValidatorExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "ValidatorRemoved", + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "fallback" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "cancelDeclaredOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "declareOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "clusterOwner", + "type": "address" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "executeOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + } + ], + "name": "exitValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getVersion", + "outputs": [ + { + "internalType": "string", + "name": "version", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token_", + "type": "address" + }, + { + "internalType": "contract ISSVOperators", + "name": "ssvOperators_", + "type": "address" + }, + { + "internalType": "contract ISSVClusters", + "name": "ssvClusters_", + "type": "address" + }, + { + "internalType": "contract ISSVDAO", + "name": "ssvDAO_", + "type": "address" + }, + { + "internalType": "contract ISSVViews", + "name": "ssvViews_", + "type": "address" + }, + { + "internalType": "uint64", + "name": "minimumBlocksBeforeLiquidation_", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "minimumLiquidationCollateral_", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "validatorsPerOperatorLimit_", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "declareOperatorFeePeriod_", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "executeOperatorFeePeriod_", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "operatorMaxFeeIncrease_", + "type": "uint64" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "clusterOwner", + "type": "address" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "liquidate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "reactivate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "reduceOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "registerOperator", + "outputs": [ + { + "internalType": "uint64", + "name": "id", + "type": "uint64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "bytes", + "name": "sharesData", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "registerValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "removeOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "removeValidator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipientAddress", + "type": "address" + } + ], + "name": "setFeeRecipientAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "internalType": "address", + "name": "whitelisted", + "type": "address" + } + ], + "name": "setOperatorWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "timeInSeconds", + "type": "uint64" + } + ], + "name": "updateDeclareOperatorFeePeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "timeInSeconds", + "type": "uint64" + } + ], + "name": "updateExecuteOperatorFeePeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "blocks", + "type": "uint64" + } + ], + "name": "updateLiquidationThresholdPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "maxFee", + "type": "uint64" + } + ], + "name": "updateMaximumOperatorFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "updateMinimumLiquidationCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum SSVModules", + "name": "moduleId", + "type": "uint8" + }, + { + "internalType": "address", + "name": "moduleAddress", + "type": "address" + } + ], + "name": "updateModule", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "updateNetworkFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "percentage", + "type": "uint64" + } + ], + "name": "updateOperatorFeeIncreaseLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "withdrawAllOperatorEarnings", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawNetworkEarnings", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawOperatorEarnings", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abis/SSVNetworkViews.json b/abis/SSVNetworkViews.json new file mode 100644 index 00000000..64cc9826 --- /dev/null +++ b/abis/SSVNetworkViews.json @@ -0,0 +1,875 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ApprovalNotWithinTimeframe", + "type": "error" + }, + { + "inputs": [], + "name": "CallerNotOwner", + "type": "error" + }, + { + "inputs": [], + "name": "CallerNotWhitelisted", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterAlreadyEnabled", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterDoesNotExists", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterIsLiquidated", + "type": "error" + }, + { + "inputs": [], + "name": "ClusterNotLiquidatable", + "type": "error" + }, + { + "inputs": [], + "name": "ExceedValidatorLimit", + "type": "error" + }, + { + "inputs": [], + "name": "FeeExceedsIncreaseLimit", + "type": "error" + }, + { + "inputs": [], + "name": "FeeIncreaseNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "FeeTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "FeeTooLow", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectClusterState", + "type": "error" + }, + { + "inputs": [], + "name": "IncorrectValidatorState", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOperatorIdsLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPublicKeyLength", + "type": "error" + }, + { + "inputs": [], + "name": "MaxValueExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "NewBlockPeriodIsBelowMinimum", + "type": "error" + }, + { + "inputs": [], + "name": "NoFeeDeclared", + "type": "error" + }, + { + "inputs": [], + "name": "NotAuthorized", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorsListNotUnique", + "type": "error" + }, + { + "inputs": [], + "name": "SameFeeChangeNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "TargetModuleDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "TokenTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "UnsortedOperatorsList", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorDoesNotExist", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "clusterOwner", + "type": "address" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "getBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "clusterOwner", + "type": "address" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "getBurnRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLiquidationThresholdPeriod", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMaximumOperatorFee", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumLiquidationCollateral", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNetworkEarnings", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNetworkFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNetworkValidatorsCount", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "getOperatorById", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "", + "type": "uint32" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "getOperatorDeclaredFee", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "id", + "type": "uint64" + } + ], + "name": "getOperatorEarnings", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "operatorId", + "type": "uint64" + } + ], + "name": "getOperatorFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOperatorFeeIncreaseLimit", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOperatorFeePeriods", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "clusterOwner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + } + ], + "name": "getValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getValidatorsPerOperatorLimit", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVersion", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract ISSVViews", + "name": "ssvNetwork_", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "clusterOwner", + "type": "address" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "isLiquidatable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "clusterOwner", + "type": "address" + }, + { + "internalType": "uint64[]", + "name": "operatorIds", + "type": "uint64[]" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "validatorCount", + "type": "uint32" + }, + { + "internalType": "uint64", + "name": "networkFeeIndex", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "index", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "active", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "internalType": "struct ISSVNetworkCore.Cluster", + "name": "cluster", + "type": "tuple" + } + ], + "name": "isLiquidated", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proxiableUUID", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ssvNetwork", + "outputs": [ + { + "internalType": "contract ISSVViews", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/docs/architecture.md b/docs/architecture.md index 5874a158..e43d21ff 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,17 +1,19 @@ # SSV Network -### [Intro](../README.md) | Architecture | [Setup](setup.md) | [Tasks](tasks.md) | [Local development](local-dev.md) | [Roles](roles.md) +### [Intro](../README.md) | Architecture | [Setup](setup.md) | [Tasks](tasks.md) | [Local development](local-dev.md) | [Roles](roles.md) | [Publish](publish.md) ## Contract Architecture The architecture of the contracts is based on [EIP-2535 Diamond MultiFacet Proxy](https://eips.ethereum.org/EIPS/eip-2535) with some changes mainly to be compatible with regular block explorers like Etherscan. Main goals: -* **Modularity** - As the system evolves, we need to be able to move fast incorporating or changing functionalities without facing limitations like the contract size or disturbing existing architecture. -* **Upgradeability** - Allowing the DAO to evolve the system or solve issues. The process can be deactivated if such a decision is made. -* **Resilient innovation** - To encourage developer adoption, we designed a system easy to integrate and use. +- **Modularity** - As the system evolves, we need to be able to move fast incorporating or changing functionalities without facing limitations like the contract size or disturbing existing architecture. +- **Upgradeability** - Allowing the DAO to evolve the system or solve issues. The process can be deactivated if such a decision is made. +- **Resilient innovation** - To encourage developer adoption, we designed a system easy to integrate and use. ### Main components + #### SSVNetwork + It's the main entry point for users, used for operations and management. It acts as a proxy for the _module_ contracts, where all functions that contain logic reside. All events are fired from the SSVNetwork contract. It's an [UUPS](https://eips.ethereum.org/EIPS/eip-1822) upgradeable contract. Apart from the state variables inherited by the UUPS Openzeppelin implementation, the contract storage is managed by the [Diamond storage pattern](https://eip2535diamonds.substack.com/i/65777640/diamond-storage) using a specific library. @@ -20,15 +22,19 @@ The fallback function is implemented to delegate all calls to the SSVViews modul Any module interface can be used with this contract, so then you can access only the functions and events related to the specific interface of the module. This is helpful when you want access to a restricted set of functionalities belonging to Operators, Clusters, etc. #### SSVNetworkViews + It's the main contract for reading information about the network and its participants. #### Modules + Non-upgradeable, stateless contracts that contain the logic to support Clusters, Operators, and Protocol (DAO / Network) functionalities. **Important**: Interacting directly with module contracts is not effective as you are not interacting with the correct state maintained by the main contract `SSVNetwork`. All interactions should be done via main contracts: `SSVNetwork` or `SSVNetworkViews`. #### Libraries -Libraries are a fundamental part of the architecture to support reusable pieces efficiently. Also, `SSVStorage` and `SSVStorageProtocol` implement the Diamond storage pattern. + +Libraries are a fundamental part of the architecture to support reusable pieces efficiently. Also, `SSVStorage` and `SSVStorageProtocol` implement the Diamond storage pattern. #### SSV Token + The native SSV token is used to facilitate payments between stakers and SSV node operators to maintain their validators. diff --git a/docs/local-dev.md b/docs/local-dev.md index d159e826..6a68f3bb 100644 --- a/docs/local-dev.md +++ b/docs/local-dev.md @@ -1,15 +1,19 @@ # SSV Network -### [Intro](../README.md) | [Architecture](architecture.md) | [Setup](setup.md) | [Tasks](tasks.md) | Local development | [Roles](roles.md) +### [Intro](../README.md) | [Architecture](architecture.md) | [Setup](setup.md) | [Tasks](tasks.md) | Local development | [Roles](roles.md) | [Publish](publish.md) ## Running against a local node / testnet + You can deploy and run these contracts in a local node like Hardhat's, Ganache, or public testnets. This guide will cover the process. ### Run [Setup](setup.md) + Execute the steps to set up all tools needed. ### Configure Environment + Copy [.env.example](../.env.example) to `.env` and edit to suit. + - `[NETWORK]_ETH_NODE_URL` RPC URL of the node - `[NETWORK]_OWNER_PRIVATE_KEY` Private key of the deployer account, without 0x prefix - `GAS_PRICE` Example 30000000000 @@ -17,39 +21,46 @@ Copy [.env.example](../.env.example) to `.env` and edit to suit. - `ETHERSCAN_KEY` Etherescan API key to verify deployed contracts - `SSV_TOKEN_ADDRESS` SSV Token contract address to be used in custom networks. Keep it empty to deploy a mocked SSV token. - `MINIMUM_BLOCKS_BEFORE_LIQUIDATION` A number of blocks before the cluster enters into a liquidatable state. Example: 214800 = 30 days -- `OPERATOR_MAX_FEE_INCREASE` The fee increase limit in percentage with this format: 100% = 10000, 10% = 1000 - using 10000 to represent 2 digit precision +- `OPERATOR_MAX_FEE_INCREASE` The fee increase limit in percentage with this format: 100% = 10000, 10% = 1000 - using 10000 to represent 2 digit precision - `DECLARE_OPERATOR_FEE_PERIOD` The period in which an operator can declare a fee change (seconds) - `EXECUTE_OPERATOR_FEE_PERIOD` The period in which an operator fee change can be executed (seconds) - `VALIDATORS_PER_OPERATOR_LIMIT` The number of validators an operator can manage - `MINIMUM_LIQUIDATION_COLLATERAL` The lowest number in wei a cluster can have before its liquidatable - #### Network configuration + In [hardhat.config.ts](../hardhat.config.ts) you can find specific configs for different networks, that are taken into account only when the `[NETWORK]_ETH_NODE_URL` parameter in `.env` file is set. For example, in `.env` file you can set: + ``` GOERLI_ETH_NODE_URL="https://goerli.infura.io/v3/..." GOERLI_OWNER_PRIVATE_KEY="d79d.." ``` + That means Hardhat will pick `config.networks.goerli` section in `hardhat.config.ts` to set the network parameters. ### Start the local node + To run the local node, execute the command in a separate terminal. ```sh npx hardhat node ``` + For more details about it and how to use MainNet forking you can find [here](https://hardhat.org/hardhat-network/). ### Deployment + The inital deployment process involves the deployment of all main modules (SSVClusters, SSVOperators, SSVDAO and SSVViews), SSVNetwork and SSVNetworkViews contracts. Note: The SSV token address used when deploying to live networks (goerli, mainnet) is set in the hardhat config file. To deploy the contracts to a custom network defined in the hardhat config file, leave `SSVTOKEN_ADDRESS` empty in the `.env` file. You can set a specific SSV token address for custom networks too, if needed. To run the deployment, execute: + ```sh npx hardhat --network deploy:all ``` + Output of this action will be: ```sh @@ -65,10 +76,13 @@ Deploying SSVNetworkViews with SSVNetwork 0x5FC8... SSVNetworkViews proxy deployed to: 0xa513... SSVNetworkViews implementation deployed to: 0x0165... ``` -As general rule, you can target any network configured in the `hardhat.config.ts`, specifying the right [network]_ETH_NODE_URL and [network]_OWNER_PRIVATE_KEY in `.env` file. + +As general rule, you can target any network configured in the `hardhat.config.ts`, specifying the right [network]\_ETH_NODE_URL and [network]\_OWNER_PRIVATE_KEY in `.env` file. ### Verification on etherscan (only public networks) + You can now go to Etherscan and see: + - `SSVNetwork` proxy contract is deployed to the address shown previously in `SSVNetwork proxy deployed to` - `SSVNetwork` implementation contract is deployed to the address shown previously in `SSVNetwork implementation deployed to` - `SSVNetworkViews` proxy contract is deployed to the address shown previously in `SSVNetworkViews proxy deployed to` @@ -87,6 +101,7 @@ npx hardhat verify --network ``` Output of this action will be: + ```sh Nothing to compile No need to generate any newer typings. diff --git a/docs/publish.md b/docs/publish.md new file mode 100644 index 00000000..c4fc5018 --- /dev/null +++ b/docs/publish.md @@ -0,0 +1,44 @@ +# SSV Network + +### [Intro](../README.md) | [Architecture](architecture.md) | [Setup](setup.md) | [Tasks](tasks.md) | [Local development](local-dev.md) | [Roles](roles.md) | Publish + +## Prerequisites + +- Ensure you have [Node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) installed. +- An npm account. [Sign up here](https://www.npmjs.com/signup) if you don't have one. + +## Prepare Package + +Before publishing, make sure your `package.json` is properly set up: + +- `name`: The package name (must be unique on npm). +- `version`: The current version of the package. +- `description`: A brief description of your package. +- `main`: The entry point of your package (usually `index.js`). +- `scripts`: Any scripts you want to include, like build or test scripts. +- `author`: The author's name and contact information. +- `repository`: The repository URL where your code is located. +- `keywords`: An array of keywords to help users discover your package. +- `files`: An array of file patterns that describes which files should be included when your package is installed. +- `dependencies` and `devDependencies`: Any required packages. + +## Authenticate with npm + +- Log in to your npm account from the command line: + +```bash +npm login +``` + +- Enter your npm username, password, and email address when prompted. + +## Configure GitHub Actions for Automated Publishing + +- Create a [.github/workflows/publish.yaml](../.github/workflows/publish.yaml) file in your project. +- Define the npm publishing process using GitHub Actions: +- Add your npm token `NPM_TOKEN` to the GitHub repository secrets (Settings > Secrets). + +## Publish Package + +- Generate a release in the `main` branch of the `ssv_network` GitHub repository. +- The GitHub Actions workflow will automatically publish the package to npm. diff --git a/docs/roles.md b/docs/roles.md index f40417fb..b227a8b7 100644 --- a/docs/roles.md +++ b/docs/roles.md @@ -1,41 +1,44 @@ # SSV Network -### [Intro](../README.md) | [Architecture](architecture.md) | [Setup](setup.md) | [Tasks](tasks.md) | [Local development](local-dev.md) | Roles - +### [Intro](../README.md) | [Architecture](architecture.md) | [Setup](setup.md) | [Tasks](tasks.md) | [Local development](local-dev.md) | Roles | [Publish](publish.md) ## Contract owner -The contract owner can perform operational actions over the contract and protocol updates. +The contract owner can perform operational actions over the contract and protocol updates. ### Contract operations -* Upgrade `SSVNetwork` and `SSVNetworkViews` -* `SSVNetwork.upgradeModule()` - Update any module + +- Upgrade `SSVNetwork` and `SSVNetworkViews` +- `SSVNetwork.upgradeModule()` - Update any module ### Protocol updates -* `SSVNetwork.updateNetworkFee()` - Updates the network fee -* `SSVNetwork.withdrawNetworkEarnings()` - Withdraws network earnings -* `SSVNetwork.updateOperatorFeeIncreaseLimit()` - Updates the limit on the percentage increase in operator fees -* `SSVNetwork.updateDeclareOperatorFeePeriod()` - Updates the period for declaring operator fees -* `SSVNetwork.updateExecuteOperatorFeePeriod()` - Updates the period for executing operator fees -* `SSVNetwork.updateLiquidationThresholdPeriod()` - Updates the liquidation threshold period -* `SSVNetwork.updateMinimumLiquidationCollateral()` - Updates the minimum collateral required to prevent liquidation + +- `SSVNetwork.updateNetworkFee()` - Updates the network fee +- `SSVNetwork.withdrawNetworkEarnings()` - Withdraws network earnings +- `SSVNetwork.updateOperatorFeeIncreaseLimit()` - Updates the limit on the percentage increase in operator fees +- `SSVNetwork.updateDeclareOperatorFeePeriod()` - Updates the period for declaring operator fees +- `SSVNetwork.updateExecuteOperatorFeePeriod()` - Updates the period for executing operator fees +- `SSVNetwork.updateLiquidationThresholdPeriod()` - Updates the liquidation threshold period +- `SSVNetwork.updateMinimumLiquidationCollateral()` - Updates the minimum collateral required to prevent liquidation ## Operator owner + Only the owner of an operator can execute these functions: -* `SSVNetwork.removeOperator` - Removes an existing operator -* `SSVNetwork.setOperatorWhitelist` - Sets the whitelist address for an operator -* `SSVNetwork.declareOperatorFee` - Declares the operator's fee change -* `SSVNetwork.executeOperatorFee` - Executes the operator's fee change -* `SSVNetwork.cancelDeclaredOperatorFee` - Cancels the declared operator's fee -* `SSVNetwork.reduceOperatorFee` - Reduces the operator's fee -* `SSVNetwork.withdrawOperatorEarnings` - Withdraws operator earnings -* `SSVNetwork.withdrawAllOperatorEarnings` - Withdraws all operator earnings +- `SSVNetwork.removeOperator` - Removes an existing operator +- `SSVNetwork.setOperatorWhitelist` - Sets the whitelist address for an operator +- `SSVNetwork.declareOperatorFee` - Declares the operator's fee change +- `SSVNetwork.executeOperatorFee` - Executes the operator's fee change +- `SSVNetwork.cancelDeclaredOperatorFee` - Cancels the declared operator's fee +- `SSVNetwork.reduceOperatorFee` - Reduces the operator's fee +- `SSVNetwork.withdrawOperatorEarnings` - Withdraws operator earnings +- `SSVNetwork.withdrawAllOperatorEarnings` - Withdraws all operator earnings ## Cluster owner + Only the owner of a cluster can execute these functions: -* `SSVNetwork.registerValidator` - Registers a new validator on the SSV Network -* `SSVNetwork.removeValidator` - Removes an existing validator from the SSV Network -* `SSVNetwork.reactivate` - Reactivates a cluster -* `SSVNetwork.withdraw` - Withdraws tokens from a cluster \ No newline at end of file +- `SSVNetwork.registerValidator` - Registers a new validator on the SSV Network +- `SSVNetwork.removeValidator` - Removes an existing validator from the SSV Network +- `SSVNetwork.reactivate` - Reactivates a cluster +- `SSVNetwork.withdraw` - Withdraws tokens from a cluster diff --git a/docs/setup.md b/docs/setup.md index c6c6da37..1bcacae6 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -1,20 +1,23 @@ # SSV Network -### [Intro](../README.md) | [Architecture](architecture.md) | Setup | [Tasks](tasks.md) | [Local development](local-dev.md) | [Roles](roles.md) - +### [Intro](../README.md) | [Architecture](architecture.md) | Setup | [Tasks](tasks.md) | [Local development](local-dev.md) | [Roles](roles.md) | [Publish](publish.md) ## Developer Setup + The stack is a simple one: -* Solidity -* JavaScript -* Node/NPM -* HardHat -* Ethers + +- Solidity +- JavaScript +- Node/NPM +- HardHat +- Ethers ### Install Node (also installs NPM) -* Use the latest [LTS (long-term support) version](https://nodejs.org/en/download/). + +- Use the latest [LTS (long-term support) version](https://nodejs.org/en/download/). ### Install required Node modules + All NPM resources are project local. No global installs are required. ``` @@ -23,8 +26,9 @@ npm install ``` ### Configure Environment + - Copy [.env.example](../.env.example) to `.env` and edit to suit. - API keys are only needed for deploying to public networks. - `.env` is included in `.gitignore` and will not be committed to the repo. - + At this moment you are ready to run tests, compile contracts and run coverage tests. diff --git a/docs/tasks.md b/docs/tasks.md index 240817a4..7e595677 100644 --- a/docs/tasks.md +++ b/docs/tasks.md @@ -1,11 +1,13 @@ # SSV Network -### [Intro](../README.md) | [Architecture](architecture.md) | [Setup](setup.md) | Tasks | [Local development](local-dev.md) | [Roles](roles.md) +### [Intro](../README.md) | [Architecture](architecture.md) | [Setup](setup.md) | Tasks | [Local development](local-dev.md) | [Roles](roles.md) | [Publish](publish.md) ## Development scripts + All scripts can be executed using `package.json` scripts. - ### Build the contracts +### Build the contracts + This creates the build artifacts for deployment or testing ``` @@ -13,6 +15,7 @@ npm run build ``` ### Test the contracts + This builds the contracts and runs the unit tests. It also runs the gas reporter and it outputs the report at the end of the tests. ``` @@ -20,6 +23,7 @@ npm run test ``` ### Run the code coverage + This builds the contracts and runs the code coverage. This is slower than testing since it makes sure that every line of our contracts is tested. It outputs the report in folder `coverage`. ``` @@ -27,27 +31,34 @@ npm run solidity-coverage ``` ### Slither + Runs the static analyzer [Slither](https://github.com/crytic/slither), to search for common solidity vulnerabilities. By default it analyzes all contracts. -```npm run slither``` +`npm run slither` ### Size contracts + Compiles the contracts and report the size of each one. Useful to check to not surpass the 24k limit. + ``` npm run size-contracts ``` ## Development tasks + This project uses hardhat tasks to perform the deployment and upgrade of the main contracts and modules. Following Hardhat's way of working, you must specify the network against which you want to run the task using the `--network` parameter. In all the following examples, the goerli network will be used, but you can specify any defined in your `hardhat.config` file. ### Deploy all contracts + Runs the deployment of the main SSVNetwork and SSVNetworkViews contracts, along with their associated modules: + ``` npx hardhat --network goerli_testnet deploy:all ``` When deploying to live networks like Goerli or Mainnet, please double check the environment variables: + - MINIMUM_BLOCKS_BEFORE_LIQUIDATION - MINIMUM_LIQUIDATION_COLLATERAL - VALIDATORS_PER_OPERATOR_LIMIT @@ -56,12 +67,15 @@ When deploying to live networks like Goerli or Mainnet, please double check the - OPERATOR_MAX_FEE_INCREASE ## Upgrade process + We use [UUPS Proxy Upgrade pattern](https://docs.openzeppelin.com/contracts/4.x/api/proxy) for `SSVNetwork` and `SSVNetworkViews` contracts to have an ability to upgrade them later. **Important**: It's critical to not add any state variable to `SSVNetwork` nor `SSVNetworkViews` when upgrading. All the state variables are managed by [SSVStorage](../contracts/libraries/SSVStorage.sol) and [SSVStorageProtocol](../contracts/libraries/SSVStorageProtocol.sol). Only modify the logic part of the main contracts or the modules. ### Upgrade SSVNetwork / SSVNetworkViews + #### Upgrade contract logic + In this case, the upgrade add / delete / modify a function, but no other piece in the system is changed (libraries or modules). Set `SSVNETWORK_PROXY_ADDRESS` in `.env` file to the right value. @@ -73,18 +87,19 @@ Usage: hardhat [GLOBAL OPTIONS] upgrade:proxy [--contract ] [--init-func OPTIONS: --contract New contract upgrade - --init-function Function to be executed after upgrading + --init-function Function to be executed after upgrading --proxy-address Proxy address of SSVNetwork / SSVNetworkViews POSITIONAL ARGUMENTS: - params Function parameters + params Function parameters Example: npx hardhat --network goerli_testnet upgrade:proxy --proxy-address 0x1234... --contract SSVNetworkV2 --init-function initializev2 param1 param2 ``` ### Update a module -Sometimes you only need to perform changes in the logic of a function of a module, add a private function or do something that doesn't affect other components in the architecture. Then you can use the task to update a module. + +Sometimes you only need to perform changes in the logic of a function of a module, add a private function or do something that doesn't affect other components in the architecture. Then you can use the task to update a module. This task first deploys a new version of a specified SSV module contract, and then updates the SSVNetwork contract to use this new module version only if `--attach-module` flag is set to `true`. @@ -104,16 +119,17 @@ npx hardhat --network goerli_testnet update:module --module SSVOperators --attac ``` ### Upgrade a library + When you change a library that `SSVNetwork` uses, you need to also update all modules where that library is used. Set `SSVNETWORK_PROXY_ADDRESS` in `.env` file to the right value. Run the task to upgrade SSVNetwork proxy contract as described in [Upgrade SSVNetwork / SSVNetworkViews](#upgrade-contract-logic) - Run the right script to update the module affected by the library change, as described in [Update a module](#update-a-module) section. ### Manual upgrade of SSVNetwork / SSVNetworkViews + Validates and deploys a new implementation contract. Use this task to prepare an upgrade to be run from an owner address you do not control directly or cannot use from Hardhat. ``` @@ -129,5 +145,3 @@ npx hardhat --network goerli_testnet upgrade:prepare --proxy-address 0x1234... - ``` The task will return the new implementation address. After that, you can run `upgradeTo` or `upgradeToAndCall` in SSVNetwork / SSVNetworkViews proxy address, providing it as a parameter. - - diff --git a/hardhat.config.ts b/hardhat.config.ts index 7b2d5eb2..d3908f89 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,52 +1,52 @@ import 'dotenv/config'; import { HardhatUserConfig } from 'hardhat/config'; -import { NetworkUserConfig } from "hardhat/types"; +import { NetworkUserConfig } from 'hardhat/types'; import '@nomicfoundation/hardhat-toolbox'; import '@openzeppelin/hardhat-upgrades'; import 'hardhat-tracer'; import '@nomiclabs/hardhat-solhint'; import 'hardhat-contract-sizer'; import 'hardhat-storage-layout-changes'; +import 'hardhat-abi-exporter'; import './tasks/deploy'; import './tasks/update-module'; import './tasks/upgrade'; type SSVNetworkConfig = NetworkUserConfig & { ssvToken: string; -} - +}; const config: HardhatUserConfig = { // Your type-safe config goes here mocha: { - timeout: 40000000000000000 + timeout: 40000000000000000, }, solidity: { compilers: [ { - version: "0.8.4", + version: '0.8.4', }, { version: '0.8.18', settings: { optimizer: { enabled: true, - runs: 10000 - } - } - } + runs: 10000, + }, + }, + }, ], }, networks: { ganache: { chainId: 1337, - url: "http://127.0.0.1:8585", - ssvToken: process.env.SSVTOKEN_ADDRESS // if empty, deploy SSV mock token + url: 'http://127.0.0.1:8585', + ssvToken: process.env.SSVTOKEN_ADDRESS, // if empty, deploy SSV mock token } as SSVNetworkConfig, hardhat: { - allowUnlimitedContractSize: true - } + allowUnlimitedContractSize: true, + }, }, etherscan: { // Your API key for Etherscan @@ -54,20 +54,29 @@ const config: HardhatUserConfig = { apiKey: process.env.ETHERSCAN_KEY, customChains: [ { - network: "holesky", + network: 'holesky', chainId: 17000, urls: { - apiURL: "https://api-holesky.etherscan.io/api", - browserURL: "https://holesky.etherscan.io" - } - } - ] + apiURL: 'https://api-holesky.etherscan.io/api', + browserURL: 'https://holesky.etherscan.io', + }, + }, + ], }, gasReporter: { enabled: true, currency: 'USD', - gasPrice: 0.3 - } + gasPrice: 0.3, + }, + abiExporter: { + path: './abis', + runOnCompile: true, + clear: true, + flat: true, + spacing: 2, + pretty: false, + only: ['contracts/SSVNetwork.sol', 'contracts/SSVNetworkViews.sol'], + }, }; if (process.env.GOERLI_ETH_NODE_URL) { @@ -82,13 +91,13 @@ if (process.env.GOERLI_ETH_NODE_URL) { ...config.networks, goerli_development: { ...sharedConfig, - ssvToken: '0x6471F70b932390f527c6403773D082A0Db8e8A9F' + ssvToken: '0x6471F70b932390f527c6403773D082A0Db8e8A9F', } as SSVNetworkConfig, goerli_testnet: { ...sharedConfig, - ssvToken: '0x3a9f01091C446bdE031E39ea8354647AFef091E7' + ssvToken: '0x3a9f01091C446bdE031E39ea8354647AFef091E7', } as SSVNetworkConfig, - } + }; } if (process.env.HOLESKY_ETH_NODE_URL) { @@ -103,13 +112,13 @@ if (process.env.HOLESKY_ETH_NODE_URL) { ...config.networks, holesky_development: { ...sharedConfig, - ssvToken: '0x68A8DDD7a59A900E0657e9f8bbE02B70c947f25F' + ssvToken: '0x68A8DDD7a59A900E0657e9f8bbE02B70c947f25F', } as SSVNetworkConfig, holesky_testnet: { ...sharedConfig, - ssvToken: '0xad45A78180961079BFaeEe349704F411dfF947C6' + ssvToken: '0xad45A78180961079BFaeEe349704F411dfF947C6', } as SSVNetworkConfig, - } + }; } if (process.env.MAINNET_ETH_NODE_URL) { @@ -121,9 +130,9 @@ if (process.env.MAINNET_ETH_NODE_URL) { accounts: [`0x${process.env.MAINNET_OWNER_PRIVATE_KEY}`], gasPrice: +(process.env.GAS_PRICE || ''), gas: +(process.env.GAS || ''), - ssvToken: '0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54' - } as SSVNetworkConfig - } + ssvToken: '0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54', + } as SSVNetworkConfig, + }; } export default config; diff --git a/package-lock.json b/package-lock.json index 2f91c227..50b5a065 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10 +1,13 @@ { "name": "ssv-network", + "version": "1.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ssv-network", + "version": "1.0.2", + "license": "MIT", "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^2.0.0", "@nomiclabs/hardhat-ethers": "^2.2.3", @@ -24,6 +27,7 @@ "eslint": "^8.23.0", "gh-pages": "^3.2.3", "hardhat": "^2.14.0", + "hardhat-abi-exporter": "^2.10.1", "hardhat-contract-sizer": "^2.6.1", "hardhat-storage-layout-changes": "^0.1.2", "hardhat-tracer": "^1.2.1", @@ -4017,6 +4021,36 @@ "node": ">=0.4.0" } }, + "node_modules/delete-empty": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/delete-empty/-/delete-empty-3.0.0.tgz", + "integrity": "sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.0", + "minimist": "^1.2.0", + "path-starts-with": "^2.0.0", + "rimraf": "^2.6.2" + }, + "bin": { + "delete-empty": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/delete-empty/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6558,6 +6592,22 @@ } } }, + "node_modules/hardhat-abi-exporter": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/hardhat-abi-exporter/-/hardhat-abi-exporter-2.10.1.tgz", + "integrity": "sha512-X8GRxUTtebMAd2k4fcPyVnCdPa6dYK4lBsrwzKP5yiSq4i+WadWPIumaLfce53TUf/o2TnLpLOduyO1ylE2NHQ==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.5.0", + "delete-empty": "^3.0.0" + }, + "engines": { + "node": ">=14.14.0" + }, + "peerDependencies": { + "hardhat": "^2.0.0" + } + }, "node_modules/hardhat-contract-sizer": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/hardhat-contract-sizer/-/hardhat-contract-sizer-2.10.0.tgz", @@ -8671,6 +8721,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-starts-with": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-starts-with/-/path-starts-with-2.0.1.tgz", + "integrity": "sha512-wZ3AeiRBRlNwkdUxvBANh0+esnt38DLffHDujZyRHkqkaKHTglnY2EP5UX3b8rdeiSutgO4y9NEJwXezNP5vHg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -15249,6 +15308,29 @@ "dev": true, "peer": true }, + "delete-empty": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/delete-empty/-/delete-empty-3.0.0.tgz", + "integrity": "sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.0", + "minimist": "^1.2.0", + "path-starts-with": "^2.0.0", + "rimraf": "^2.6.2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -17378,6 +17460,16 @@ } } }, + "hardhat-abi-exporter": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/hardhat-abi-exporter/-/hardhat-abi-exporter-2.10.1.tgz", + "integrity": "sha512-X8GRxUTtebMAd2k4fcPyVnCdPa6dYK4lBsrwzKP5yiSq4i+WadWPIumaLfce53TUf/o2TnLpLOduyO1ylE2NHQ==", + "dev": true, + "requires": { + "@ethersproject/abi": "^5.5.0", + "delete-empty": "^3.0.0" + } + }, "hardhat-contract-sizer": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/hardhat-contract-sizer/-/hardhat-contract-sizer-2.10.0.tgz", @@ -18865,6 +18957,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-starts-with": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-starts-with/-/path-starts-with-2.0.1.tgz", + "integrity": "sha512-wZ3AeiRBRlNwkdUxvBANh0+esnt38DLffHDujZyRHkqkaKHTglnY2EP5UX3b8rdeiSutgO4y9NEJwXezNP5vHg==", + "dev": true + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", diff --git a/package.json b/package.json index 14bc97dd..979bf9e6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,32 @@ { "name": "ssv-network", + "version": "1.0.2", + "description": "Solidity smart contracts for the SSV Network", + "author": "SSV.Network", + "repository": { + "type": "git", + "url": "https://github.com/bloxapp/ssv-network.git" + }, + "license": "MIT", + "keywords": [ + "ssv", + "ssv.network", + "solidity", + "staking" + ], + "files": [ + "contracts/**/*.sol", + "!contracts/**/deprecated/**", + "!contracts/**/mocks/**", + "!contracts/**/test/**", + "!contracts/**/upgrades/**", + "abis/*.json", + "tasks/", + "docs/", + "README.md", + "LICENSE", + "CHANGELOG.md" + ], "scripts": { "build": "npx hardhat compile", "test": "npx hardhat test", @@ -31,9 +58,10 @@ "hardhat-contract-sizer": "^2.6.1", "hardhat-storage-layout-changes": "^0.1.2", "hardhat-tracer": "^1.2.1", + "hardhat-abi-exporter": "^2.10.1", "prompts": "^2.4.2", "simple-git": "^3.16.1", "ts-node": "^10.7.0", "typescript": "^4.6.3" } -} \ No newline at end of file +}