diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..e4e910d09 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# top-most EditorConfig file +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 + +[*.{js,yml,json,cjs}] +indent_size = 2 +max_line_length = 120 diff --git a/.eslintrc b/.eslintrc index 43aa16d92..7028a1e29 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,59 +1,32 @@ { + "plugins": ["prettier", "standard"], "env": { "node": true, - "es2020": true + "es2022": true }, - "extends": [ - "standard", - "plugin:prettier/recommended", - "prettier", - "prettier/standard" - ], -"parser": "babel-eslint", + "extends": ["standard", "plugin:prettier/recommended"], "parserOptions": { - "ecmaVersion": 8, - "sourceType": "module", + "ecmaVersion": 2022, + "sourceType": "script", "ecmaFeatures": { - "jsx": true, - "modules": true + "jsx": true } }, - "plugins": [ - "node", - "prettier", - "standard" - ], - "rules": { "prettier/prettier": "error", - "max-len": ["warn", { "code": 140, "ignoreComments": true, "ignoreUrls": true }], - "no-undef": "warn", - "no-unused-vars": "warn", - "prefer-const": "warn", "camelcase": "off", - "eol-last": "warn", - "no-multiple-empty-lines": "warn", - "comma-dangle": "warn", "quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": true }], "jsx-quotes": ["error", "prefer-single"], - "no-useless-escape": "off", - "valid-jsdoc": "error", - "linebreak-style": ["error", "unix"] + "no-restricted-modules": ["error", { + "name": "@aragon/contract-helpers-test/src/asserts", + "message": "Please use helpers/assert instead." + }] }, "overrides": [ { - "files": [ - "./scripts/{**/,}*.js", - "./test/{**/,}*.js", - "./e2e/test/{**/,}*.js" - ], + "files": ["./scripts/{**/,}*.js", "./test/{**/,}*.js", "./e2e/test/{**/,}*.js"], "env": { "mocha": true - }, - "globals": { - "artifacts": "readonly", - "contract": "readonly", - "web3": "readonly" } } ] diff --git a/.github/deployed-bytecode-consts.js b/.github/deployed-bytecode-consts.js index 4959d22f2..32d74f6f1 100644 --- a/.github/deployed-bytecode-consts.js +++ b/.github/deployed-bytecode-consts.js @@ -1,14 +1,13 @@ const APPS_TO_NAMES = new Map([ ['lido', 'Lido'], ['node-operators-registry', 'NodeOperatorsRegistry'], - ['oracle', 'LidoOracle'] ]) const CONTRACTS_TO_NAMES = new Map([ ['wstethContract', 'WstETH'], ['executionLayerRewardsVault', 'LidoExecutionLayerRewardsVault'], ['compositePostRebaseBeaconReceiver', 'CompositePostRebaseBeaconReceiver'], - ['selfOwnedStETHBurner', 'SelfOwnedStETHBurner'], + ['burner', 'Burner'], ['depositor', 'DepositSecurityModule'] ]) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 43d41c5c8..ab42f861d 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -47,11 +47,10 @@ jobs: run: yarn test:unit - name: Run Solidity linters - run: yarn lint:sol:solhint + run: yarn lint:sol - name: Run JS linters run: yarn lint:js - continue-on-error: true coverage: name: Solidity coverage @@ -96,55 +95,3 @@ jobs: - name: Run Solidity test coverage run: yarn test:coverage continue-on-error: false - - abi-lint: - name: ABI actuality linter - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - persist-credentials: false - - - name: Setup node.js version - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT - - - name: Cache yarn cache - id: cache-yarn-cache - uses: actions/cache@v3 - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: yarn-${{ hashFiles('**/yarn.lock') }} - - - name: Cache node_modules - id: cache-node-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: node_modules-${{ hashFiles('**/yarn.lock') }} - restore-keys: node_modules-${{ hashFiles('**/yarn.lock') }} - - - name: Install modules - run: yarn - if: | - steps.cache-yarn-cache.outputs.cache-hit != 'true' || - steps.cache-node-modules.outputs.cache-hit != 'true' - - - name: Compile code and extract ABI - run: yarn compile - - - name: Check for ABI changes - run: | - git diff --quiet lib/abi && status=clean || status=dirty - if [ $status == "dirty" ]; then - echo "The following ABIs should be commited" - git diff --compact-summary lib/abi - exit 1 - fi diff --git a/.github/workflows/storage-layout-check.yml b/.github/workflows/storage-layout-check.yml index 85685bee2..41d545115 100644 --- a/.github/workflows/storage-layout-check.yml +++ b/.github/workflows/storage-layout-check.yml @@ -20,6 +20,9 @@ jobs: node-version: 16.x cache: 'yarn' + - name: Submodules + run: git submodule update --init --recursive + - name: Install yarn deps run: yarn diff --git a/.gitignore b/.gitignore index 4df73468d..ddfcfd5c4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .vscode **/build/ **/node_modules/ +**/lib/abi/*.json **/artifacts/ **/artifacts-userdoc/ .cache @@ -40,3 +41,13 @@ cli/vendor /accounts.json # e2e temp data /deployed-e2e.json + +# OS relative +.DS_Store + +# foundry artifacts +foundry/cache +foundry/out + +logs +**/verificator_diffs/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..0c2994f67 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "foundry/lib/forge-std"] + path = foundry/lib/forge-std + url = https://github.com/foundry-rs/forge-std + branch = v1.3.0 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..b417ff56f --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,8 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +RED_COLOR='\033[0;31m' +NO_COLOR='\033[0m' + +yarn compile +yarn lint diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 000000000..840674ec6 --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,4 @@ +{ + "require": "hardhat/register", + "timeout": 40000 +} diff --git a/.prettierrc b/.prettierrc index 0d22c886f..c8c5e7306 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,8 +1,9 @@ { "singleQuote": true, "semi": false, - "trailingComma": "none", "bracketSpacing": true, "jsxBracketSameLine": false, - "printWidth": 140 + "options": { + "editorconfig": true + } } diff --git a/.solcover.js b/.solcover.js index aa18fdfbc..f5dca4935 100644 --- a/.solcover.js +++ b/.solcover.js @@ -1,5 +1,6 @@ module.exports = { - skipFiles: ['template', 'test_helpers', 'oracle/test_helpers', 'nos/test_helpers', 'mocks'], + // todo: add support for '**/test_helpers' globs + skipFiles: ['0.4.24/template', '0.4.24/test_helpers', '0.4.24/nos/test_helpers', '0.6.12/mocks', '0.8.9/test_helpers'], mocha: { enableTimeouts: false } diff --git a/.solhint.json b/.solhint.json index b9f9e1ece..4d4b76fd9 100644 --- a/.solhint.json +++ b/.solhint.json @@ -1,15 +1,17 @@ { "extends": "solhint:recommended", - "plugins": [ - "lido" - ], + "plugins": ["lido"], "rules": { + "not-rely-on-time": "off", "no-inline-assembly": "off", "var-name-mixedcase": "off", "compiler-version": "off", "reason-string": "off", "no-empty-blocks": "off", "func-name-mixedcase": "off", - "lido/fixed-compiler-version": "error" + "lido/fixed-compiler-version": "error", + "visibility-modifier-order": "error", + "no-unused-vars": "error", + "func-visibility": ["warn", { "ignoreConstructors": true }] } } diff --git a/.soliumignore b/.solhintignore similarity index 100% rename from .soliumignore rename to .solhintignore diff --git a/.soliumrc.json b/.soliumrc.json deleted file mode 100644 index bd45f50f1..000000000 --- a/.soliumrc.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "extends": "solium:all", - "plugins": ["security"], - "rules": { - "security/no-low-level-calls": "off", - "security/no-inline-assembly": "off", - "security/no-assign-params": "warning", - "error-reason": "off", - "imports-on-top": "error", - "variable-declarations": "error", - "array-declarations": "error", - "operator-whitespace": "error", - "conditionals-whitespace": "error", - "comma-whitespace": "error", - "semicolon-whitespace": "error", - "function-whitespace": "error", - "lbrace": "error", - "mixedcase": "off", - "camelcase": "error", - "uppercase": "error", - "no-empty-blocks": "error", - "no-unused-vars": "error", - "quotes": "error", - "blank-lines": "error", - "indentation": "error", - "arg-overflow": ["error", 8], - "whitespace": "error", - "deprecated-suicide": "error", - "pragma-on-top": "error", - "function-order": [ - "error", - {"ignore": {"functions": ["initialize"]}} - ], - "emit": "error", - "no-constant": "error", - "value-in-payable": "error", - "max-len": "error", - "visibility-first": "error", - "linebreak-style": "error" - } -} diff --git a/.storage-layout b/.storage-layout index 32f772cea..a2e87d918 100644 --- a/.storage-layout +++ b/.storage-layout @@ -2,12 +2,29 @@ ======================= ======================= -➡ CompositePostRebaseBeaconReceiver +➡ AccountingOracle ======================= -| Name | Type | Slot | Offset | Bytes | Contract | -|-----------|-----------|------|--------|-------|-----------------------------------------------------------------------------------------| -| callbacks | address[] | 0 | 0 | 32 | contracts/0.8.9/CompositePostRebaseBeaconReceiver.sol:CompositePostRebaseBeaconReceiver | +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ BeaconChainDepositor +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ Burner +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|-----------------------------|---------|------|--------|-------|-----------------------------------| +| coverSharesBurnRequested | uint256 | 0 | 0 | 32 | contracts/0.8.9/Burner.sol:Burner | +| nonCoverSharesBurnRequested | uint256 | 1 | 0 | 32 | contracts/0.8.9/Burner.sol:Burner | +| totalCoverSharesBurnt | uint256 | 2 | 0 | 32 | contracts/0.8.9/Burner.sol:Burner | +| totalNonCoverSharesBurnt | uint256 | 3 | 0 | 32 | contracts/0.8.9/Burner.sol:Burner | ======================= ➡ DepositContract @@ -25,16 +42,43 @@ | Name | Type | Slot | Offset | Bytes | Contract | |---------------------------------|-----------------------------|------|--------|-------|-----------------------------------------------------------------| -| nodeOperatorsRegistry | address | 0 | 0 | 20 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | -| maxDepositsPerBlock | uint256 | 1 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | -| minDepositBlockDistance | uint256 | 2 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | -| pauseIntentValidityPeriodBlocks | uint256 | 3 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | -| owner | address | 4 | 0 | 20 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | +| maxDepositsPerBlock | uint256 | 0 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | +| minDepositBlockDistance | uint256 | 1 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | +| pauseIntentValidityPeriodBlocks | uint256 | 2 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | +| owner | address | 3 | 0 | 20 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | +| quorum | uint256 | 4 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | | guardians | address[] | 5 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | | guardianIndicesOneBased | mapping(address => uint256) | 6 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | -| quorum | uint256 | 7 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | -| paused | bool | 8 | 0 | 1 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | -| lastDepositBlock | uint256 | 9 | 0 | 32 | contracts/0.8.9/DepositSecurityModule.sol:DepositSecurityModule | + +======================= +➡ EIP712StETH +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ HashConsensus +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|-----------------------|--------------------------------------------------------|------|--------|-------|--------------------------------------------------------| +| _frameConfig | struct HashConsensus.FrameConfig | 0 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _memberStates | struct HashConsensus.MemberState[] | 1 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _memberAddresses | address[] | 2 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _memberIndices1b | mapping(address => uint256) | 3 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _reportingState | struct HashConsensus.ReportingState | 4 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _quorum | uint256 | 5 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _reportVariants | mapping(uint256 => struct HashConsensus.ReportVariant) | 6 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _reportVariantsLength | uint256 | 7 | 0 | 32 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | +| _reportProcessor | address | 8 | 0 | 20 | contracts/0.8.9/oracle/HashConsensus.sol:HashConsensus | + +======================= +➡ LegacyOracle +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| ======================= ➡ Lido @@ -51,7 +95,7 @@ |------|------|------|--------|-------|----------| ======================= -➡ LidoOracle +➡ LidoLocator ======================= | Name | Type | Slot | Offset | Bytes | Contract | @@ -65,12 +109,27 @@ |------|------|------|--------|-------|----------| ======================= -➡ OrderedCallbacksArray +➡ OracleDaemonConfig +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|--------|--------------------------|------|--------|-------|-----------------------------------------------------------| +| values | mapping(string => bytes) | 0 | 0 | 32 | contracts/0.8.9/OracleDaemonConfig.sol:OracleDaemonConfig | + +======================= +➡ OracleReportSanityChecker +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|---------|-------------------------|------|--------|-------|---------------------------------------------------------------------------------------| +| _limits | struct LimitsListPacked | 0 | 0 | 32 | contracts/0.8.9/sanity_checks/OracleReportSanityChecker.sol:OracleReportSanityChecker | + +======================= +➡ OssifiableProxy ======================= -| Name | Type | Slot | Offset | Bytes | Contract | -|-----------|-----------|------|--------|-------|-----------------------------------------------------------------| -| callbacks | address[] | 0 | 0 | 32 | contracts/0.8.9/OrderedCallbacksArray.sol:OrderedCallbacksArray | +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| ======================= ➡ Pausable @@ -80,16 +139,11 @@ |------|------|------|--------|-------|----------| ======================= -➡ SelfOwnedStETHBurner +➡ PausableUntil ======================= -| Name | Type | Slot | Offset | Bytes | Contract | -|--------------------------------|---------|------|--------|-------|---------------------------------------------------------------| -| coverSharesBurnRequested | uint256 | 0 | 0 | 32 | contracts/0.8.9/SelfOwnedStETHBurner.sol:SelfOwnedStETHBurner | -| nonCoverSharesBurnRequested | uint256 | 1 | 0 | 32 | contracts/0.8.9/SelfOwnedStETHBurner.sol:SelfOwnedStETHBurner | -| totalCoverSharesBurnt | uint256 | 2 | 0 | 32 | contracts/0.8.9/SelfOwnedStETHBurner.sol:SelfOwnedStETHBurner | -| totalNonCoverSharesBurnt | uint256 | 3 | 0 | 32 | contracts/0.8.9/SelfOwnedStETHBurner.sol:SelfOwnedStETHBurner | -| maxBurnAmountPerRunBasisPoints | uint256 | 4 | 0 | 32 | contracts/0.8.9/SelfOwnedStETHBurner.sol:SelfOwnedStETHBurner | +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| ======================= ➡ StETH @@ -98,6 +152,55 @@ | Name | Type | Slot | Offset | Bytes | Contract | |------|------|------|--------|-------|----------| +======================= +➡ StETHPermit +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ StakingRouter +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ ValidatorsExitBusOracle +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ Versioned +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ Versioned +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ WithdrawalQueueERC721 +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + +======================= +➡ WithdrawalVault +======================= + +| Name | Type | Slot | Offset | Bytes | Contract | +|------|------|------|--------|-------|----------| + ======================= ➡ WstETH ======================= diff --git a/README.md b/README.md index ce9f98e40..edec88af6 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ The contract also works as a wrapper that accepts stETH tokens and mints wstETH * docker * node.js v12 * (optional) Lerna +* (optional) Foundry ### Installing Aragon & other deps @@ -239,6 +240,14 @@ so full branch coverage will never be reported until [solidity-coverage#219]: https://github.com/sc-forks/solidity-coverage/issues/269 +Run fuzzing tests with foundry: + +```bash +curl -L https://foundry.paradigm.xyz | bash +foundryup +forge test +``` + ## Deploying We have several ways to deploy lido smart-contracts and run DAO locally, you can find documents here: diff --git a/SCRATCH_DEPLOY.md b/SCRATCH_DEPLOY.md index 1bdea95a7..fc6654be3 100644 --- a/SCRATCH_DEPLOY.md +++ b/SCRATCH_DEPLOY.md @@ -56,8 +56,7 @@ Steps for deploy: * [ ] deploy LidoExecutionLayerRewardsVault * [ ] finalize DAO setup (via Lido Template) * [ ] deploy CompositePostRebaseBeaconReceiver -* [ ] deploy SelfOwnedStETHBurner -* [ ] do Aragon voting to attach CompositePostRebaseBeaconReceiver and SelfOwnedStETHBurner to the protocol +* [ ] deploy Burner * [ ] make final deployed DAO check via script * [ ] open and check Lido DAO web interface (via Aragon client) @@ -81,6 +80,3 @@ const rootCid = await uploadDirToIpfs({ dirPath: distPath, ipfsApiUrl: ipfsAPI } It appeared that `@aragon/buidler-aragon@npm:^0.2.9` uses `ipfs-http-client`. `ipfs-http-client` has a brittle API. Neither `41.0.0` nor `50.0.0` versions of it will work with `@aragon/buidler-aragon@npm:^0.2.9`. - -## Default Aragon voting time -To attach SelfOwnedStETHBurner to the protocol a DAO voting is needed. To do it reasonably fast on the protocol deployment the default Aragon voting duration time (see `deployed-local-defaults.json`) is set to seconds. Draft script for changing Aragon voting time is at `scripts/multisig/32-change-voting-time.js`. diff --git a/accounts.sample.json b/accounts.sample.json index f5bf655e6..a0540ca96 100644 --- a/accounts.sample.json +++ b/accounts.sample.json @@ -17,7 +17,8 @@ "path": "m/44'/60'/0'/0", "initialIndex": 0, "count": 50 - } + }, + "mainnet-fork-shapella-upgrade": ["4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"] }, "infura": { "projectId": "INFURA_PROJECT_ID" diff --git a/apps/lido/app/package.json b/apps/lido/app/package.json index 4fd67bb1d..dc14dec61 100644 --- a/apps/lido/app/package.json +++ b/apps/lido/app/package.json @@ -19,25 +19,25 @@ "yup": "^0.29.3" }, "devDependencies": { - "@babel/core": "^7.11.6", + "@babel/core": "^7.21.0", "@babel/preset-env": "^7.11.5", "@babel/preset-react": "^7.10.1", "babel-eslint": "^10.1.0", "babel-plugin-styled-components": "^1.11.1", "copyfiles": "^2.3.0", - "eslint": "^7.9.0", - "eslint-config-prettier": "^6.11.0", - "eslint-config-standard": "^14.1.1", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-config-standard": "^17.0.0", "eslint-config-standard-react": "^9.2.0", - "eslint-plugin-import": "^2.22.0", + "eslint-plugin-import": "^2.27.5", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^3.1.4", - "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.20.6", "eslint-plugin-react-hooks": "^4.1.2", - "eslint-plugin-standard": "^4.0.1", + "eslint-plugin-standard": "^5.0.0", "parcel-bundler": "^1.12.4", - "prettier": "^2.1.2" + "prettier": "^2.8.4" }, "scripts": { "build": "yarn sync-assets && yarn build:app && yarn build:script", diff --git a/apps/lido/app/src/App.js b/apps/lido/app/src/App.js index e20885dc0..19506e8a0 100644 --- a/apps/lido/app/src/App.js +++ b/apps/lido/app/src/App.js @@ -2,10 +2,11 @@ import { useAragonApi } from '@aragon/api-react' import { Header, Main, SyncIndicator, useTheme } from '@aragon/ui' import React from 'react' import { ThemeProvider } from 'styled-components' -import { BeaconStats } from './components/BeaconStats' + +import { Primary } from './components/Primary' +import { Secondary } from './components/Secondary' import { Split } from './components/shared' -import { StakingLimitState } from './components/StakingLimitState' -import { State } from './components/state' + export default function App() { const { appState, currentApp, guiStyle } = useAragonApi() @@ -21,12 +22,9 @@ export default function App() { {isSyncing && }
} + primary={} secondary={ - <> - - - + } /> diff --git a/apps/lido/app/src/LidoLocator.abi.json b/apps/lido/app/src/LidoLocator.abi.json new file mode 100644 index 000000000..855c8079d --- /dev/null +++ b/apps/lido/app/src/LidoLocator.abi.json @@ -0,0 +1,281 @@ +[ + { + "constant": true, + "inputs": [], + "name": "accountingOracle", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "burner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "coreComponents", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "depositSecurityModule", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "elRewardsVault", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "legacyOracle", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "lido", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "oracleDaemonConfig", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "oracleReportComponentsForLido", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "oracleReportSanityChecker", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "postTokenRebaseReceiver", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakingRouter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "treasury", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "validatorsExitBusOracle", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "withdrawalQueue", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "withdrawalVault", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/apps/lido/app/src/components/BeaconStats.js b/apps/lido/app/src/components/BeaconStats.js deleted file mode 100644 index 3033fa7d8..000000000 --- a/apps/lido/app/src/components/BeaconStats.js +++ /dev/null @@ -1,22 +0,0 @@ -import { useAppState } from '@aragon/api-react' -import { Box } from '@aragon/ui' -import React from 'react' -import { ListItem, LoadableElement } from './shared' -import { Ether } from './shared/Ether' - -export const BeaconStats = () => { - const { beaconStat } = useAppState() - - return ( - - - - {beaconStat?.depositedValidators} - - - - - - - ) -} diff --git a/apps/lido/app/src/components/Primary.js b/apps/lido/app/src/components/Primary.js new file mode 100644 index 000000000..9c9c55b10 --- /dev/null +++ b/apps/lido/app/src/components/Primary.js @@ -0,0 +1,116 @@ +import { useAppState } from '@aragon/api-react' +import React from 'react' +import { + BoxUnpadded, + BytesBadge, + ListItem, + ListItemAddress, + ListItemBasisPoints, + ListItemBoolean, + ListItemEther, + ListItemUnformattedValue, + LoadableElement +} from './shared' + +export const Primary = () => { + const { + isStopped, + canDeposit, + bufferedEther, + depositableEther, + totalPooledEther, + totalELRewardsCollected, + beaconStat, + fee, + feeDistribution, + withdrawalCredentials, + treasury, + legacyOracle, + recoveryVault, + lidoLocator, + lido, + accountingOracle, + burner, + depositSecurityModule, + elRewardsVault, + oracleDaemonConfig, + oracleReportSanityChecker, + postTokenRebaseReceiver, + stakingRouter, + validatorsExitBusOracle, + withdrawalQueue, + withdrawalVault, + } = useAppState() + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/apps/lido/app/src/components/Secondary.js b/apps/lido/app/src/components/Secondary.js new file mode 100644 index 000000000..1663e26ea --- /dev/null +++ b/apps/lido/app/src/components/Secondary.js @@ -0,0 +1,81 @@ +import { useAppState, useAragonApi } from '@aragon/api-react' +import React from 'react' +import { + BoxUnpadded, ListItem, + ListItemAddress, ListItemBoolean, + ListItemEther, + ListItemUnformattedValue, + LoadableElement, + RestorationRate, + Tooltip, + Ether +} from './shared' + +export const Secondary = () => { + const { lido, symbol, decimals, totalSupply, stakeLimitFullInfo, hasInitialized, initializationBlock, contractVersion } = useAppState() + + return ( + <> + + + + + + + + + + {stakeLimitFullInfo?.isStakingPaused ? 'Yes' : 'No'} + + + + Limit set + + } + > + + {stakeLimitFullInfo?.isStakingLimitSet ? 'Yes' : 'No'} + + + + Max limit + + } + > + + + + + + Restoration + + } + > + + + + + + + + + + ) +} diff --git a/apps/lido/app/src/components/StakingLimitState.js b/apps/lido/app/src/components/StakingLimitState.js deleted file mode 100644 index 85f52a3a2..000000000 --- a/apps/lido/app/src/components/StakingLimitState.js +++ /dev/null @@ -1,51 +0,0 @@ -import { useAppState } from '@aragon/api-react' -import { Box } from '@aragon/ui' -import React from 'react' -import { ListItem, LoadableElement, RestorationRate, Tooltip } from './shared' -import { Ether } from './shared/Ether' - -export const StakingLimitState = () => { - const { stakingLimitInfo } = useAppState() - - return ( - - - - {stakingLimitInfo?.isStakingPaused ? 'Yes' : 'No'} - - - - Limit set - - } - > - - {stakingLimitInfo?.isStakingLimitSet ? 'Yes' : 'No'} - - - - Max limit - - } - > - - - - Restoration - - } - > - - - - ) -} diff --git a/apps/lido/app/src/components/shared/Ether.js b/apps/lido/app/src/components/shared/Ether.js index e6bc5739e..5d2f80694 100644 --- a/apps/lido/app/src/components/shared/Ether.js +++ b/apps/lido/app/src/components/shared/Ether.js @@ -1,13 +1,12 @@ import React from 'react' -import { LoadingRing } from '@aragon/ui' import { formatEth } from '../../utils' import { constants } from 'ethers' -export const Ether = ({ ether }) => { - if (typeof ether === 'undefined') { - return - } - +export const Ether = ({ + ether, + symbol = constants.EtherSymbol, + symbolAfter = false, +}) => { try { ether = formatEth(ether) } catch (error) { @@ -17,8 +16,9 @@ export const Ether = ({ ether }) => { return ( - {constants.EtherSymbol} + {!symbolAfter && symbol} {ether} + {symbolAfter && ' ' + symbol} ) } diff --git a/apps/lido/app/src/components/shared/ListItem.js b/apps/lido/app/src/components/shared/ListItem.js index ee920d1f0..facd2f490 100644 --- a/apps/lido/app/src/components/shared/ListItem.js +++ b/apps/lido/app/src/components/shared/ListItem.js @@ -6,11 +6,15 @@ const ListItemStyle = styled.li` display: flex; justify-content: space-between; align-items: center; - margin: ${GU * 3}px 0 0 ${(props) => (props.nested ? GU * 4 : 0)}px; + padding: ${GU}px ${GU * 3}px ${GU}px + ${(props) => (props.nested ? GU * 6 : GU * 3)}px; line-height: 40px; + border-top: 1px solid + ${(props) => (props.isDark ? '#2C3A58' : props.theme.border)}; & :first-of-type { margin-top: 0; + border-top: none; } ` @@ -26,9 +30,11 @@ const ListItemValue = styled.strong` export const ListItem = ({ label, children, nested }) => { const theme = useTheme() + const themeDark = theme?._name === 'dark' + return ( - - {label} + + {label} {children} ) diff --git a/apps/lido/app/src/components/shared/ListItemAddress.js b/apps/lido/app/src/components/shared/ListItemAddress.js new file mode 100644 index 000000000..9169852e7 --- /dev/null +++ b/apps/lido/app/src/components/shared/ListItemAddress.js @@ -0,0 +1,13 @@ +import { IdentityBadge } from '@aragon/ui' +import React from 'react' +import { ListItem, LoadableElement } from '../shared' + +export const ListItemAddress = ({ label, value }) => { + return ( + + + + + + ) +} diff --git a/apps/lido/app/src/components/shared/ListItemBasisPoints.js b/apps/lido/app/src/components/shared/ListItemBasisPoints.js new file mode 100644 index 000000000..e33e4aea3 --- /dev/null +++ b/apps/lido/app/src/components/shared/ListItemBasisPoints.js @@ -0,0 +1,14 @@ +import React from 'react' +import { BasisPoints } from './BasisPoints' +import { ListItem } from './ListItem' +import { LoadableElement } from './LoadableElement' + +export const ListItemBasisPoints = ({ label, value, ...rest }) => { + return ( + + + + + + ) +} diff --git a/apps/lido/app/src/components/shared/ListItemBoolean.js b/apps/lido/app/src/components/shared/ListItemBoolean.js new file mode 100644 index 000000000..be4803efe --- /dev/null +++ b/apps/lido/app/src/components/shared/ListItemBoolean.js @@ -0,0 +1,11 @@ +import React from 'react' +import { ListItem } from './ListItem' +import { LoadableElement } from './LoadableElement' + +export const ListItemBoolean = ({ label, value, renderElements = ["Yes", "No"] }) => { + return ( + + {value ? renderElements[0] : renderElements[1]} + + ) +} diff --git a/apps/lido/app/src/components/shared/ListItemEther.js b/apps/lido/app/src/components/shared/ListItemEther.js new file mode 100644 index 000000000..5d4108188 --- /dev/null +++ b/apps/lido/app/src/components/shared/ListItemEther.js @@ -0,0 +1,14 @@ +import React from 'react' +import { ListItem } from './ListItem' +import { Ether } from './Ether' +import { LoadableElement } from './LoadableElement' + +export const ListItemEther = ({ label, value, symbol, symbolAfter }) => { + return ( + + + + + + ) +} diff --git a/apps/lido/app/src/components/shared/ListItemUnformattedValue.js b/apps/lido/app/src/components/shared/ListItemUnformattedValue.js new file mode 100644 index 000000000..9afaa736b --- /dev/null +++ b/apps/lido/app/src/components/shared/ListItemUnformattedValue.js @@ -0,0 +1,11 @@ +import React from 'react' +import { ListItem } from './ListItem' +import { LoadableElement } from './LoadableElement' + +export const ListItemUnformattedValue = ({ label, value }) => { + return ( + + {value} + + ) +} diff --git a/apps/lido/app/src/components/shared/index.js b/apps/lido/app/src/components/shared/index.js index ab21f1c2f..896d3b784 100644 --- a/apps/lido/app/src/components/shared/index.js +++ b/apps/lido/app/src/components/shared/index.js @@ -7,3 +7,9 @@ export { LoadableElement } from './LoadableElement' export { Tooltip } from './Tooltip' export { RestorationRate } from './RestorationRate' export { Split } from './Split' +export { ListItemEther } from './ListItemEther' +export { ListItemUnformattedValue } from './ListItemUnformattedValue' +export { ListItemBoolean } from './ListItemBoolean' +export { ListItemAddress } from './ListItemAddress' +export { ListItemBasisPoints } from './ListItemBasisPoints' +export { Ether } from './Ether' diff --git a/apps/lido/app/src/components/shared/styles.js b/apps/lido/app/src/components/shared/styles.js index c94e07e57..54e4e9101 100644 --- a/apps/lido/app/src/components/shared/styles.js +++ b/apps/lido/app/src/components/shared/styles.js @@ -1,4 +1,4 @@ -import { GU, Info } from '@aragon/ui' +import { Box, GU, Info } from '@aragon/ui' import styled from 'styled-components' export const InfoSpaced = styled(Info)` @@ -18,3 +18,9 @@ export const Controls = styled.div` margin-right: ${GU * 2}px; } ` + +export const BoxUnpadded = styled(Box)` + & > div { + padding: 0; + } +` diff --git a/apps/lido/app/src/components/state/BufferedEther.js b/apps/lido/app/src/components/state/BufferedEther.js deleted file mode 100644 index 0ca60fd3f..000000000 --- a/apps/lido/app/src/components/state/BufferedEther.js +++ /dev/null @@ -1,14 +0,0 @@ -import { useAppState } from '@aragon/api-react' -import React from 'react' -import { ListItem } from '../shared' -import { Ether } from '../shared/Ether' - -export const BufferedEther = () => { - const { bufferedEther } = useAppState() - - return ( - - - - ) -} diff --git a/apps/lido/app/src/components/state/DepositContract.js b/apps/lido/app/src/components/state/DepositContract.js deleted file mode 100644 index 0e5056d78..000000000 --- a/apps/lido/app/src/components/state/DepositContract.js +++ /dev/null @@ -1,16 +0,0 @@ -import { useAppState } from '@aragon/api-react' -import { IdentityBadge } from '@aragon/ui' -import React from 'react' -import { ListItem, LoadableElement } from '../shared' - -export const DepositContract = () => { - const { depositContract } = useAppState() - - return ( - - - - - - ) -} diff --git a/apps/lido/app/src/components/state/ElRewardsVault.js b/apps/lido/app/src/components/state/ElRewardsVault.js deleted file mode 100644 index fbfaf3b04..000000000 --- a/apps/lido/app/src/components/state/ElRewardsVault.js +++ /dev/null @@ -1,98 +0,0 @@ -import { useAppState, useAragonApi } from '@aragon/api-react' -import { Button, IconEdit, IdentityBadge, SidePanel } from '@aragon/ui' -import { Field, Form, Formik } from 'formik' -import React, { useState } from 'react' -import { - Controls, - IconButton, - InfoSpaced, - ListItem, - LoadableElement, - TextField, -} from '../shared' -import * as yup from 'yup' -import { isAddress } from 'web3-utils' - -const fieldName = 'vault' - -const initialValues = { - [fieldName]: '', -} - -const validationSchema = yup.object().shape({ - vault: yup - .string('Vault must be a string.') - .test(fieldName, 'Vault must be a valid address.', (vault) => { - return isAddress(vault) - }), -}) - -export const ElRewardsVault = () => { - const { api } = useAragonApi() - const { elRewardsVault } = useAppState() - - const [sidePanelOpen, setSidePanelOpen] = useState(false) - const openSidePanel = () => setSidePanelOpen(true) - const closeSidePanel = () => setSidePanelOpen(false) - - const submit = ({ vault }) => { - api - .setELRewardsVault(vault) - .toPromise() - .catch(console.error) - .finally(closeSidePanel) - } - - return ( - - - - - - } onClick={openSidePanel} /> - - - - Set a new address for the execution layer rewards vault contract. - - - {({ submitForm, isSubmitting, isValidating }) => { - const handleSubmit = (event) => { - event.preventDefault() - submitForm() - } - - return ( -
- -