diff --git a/.env.sample b/.env.sample index 8f1e6047..42e3fcea 100644 --- a/.env.sample +++ b/.env.sample @@ -1,9 +1,10 @@ # EL_RPC_URLS_{CHAIN_ID} list or URLs delimeted by commas, first entry is primary, else are fallbacks EL_RPC_URLS_1= EL_RPC_URLS_5= +EL_RPC_URLS_17000= # supported networks for connecting wallet -SUPPORTED_CHAINS=1,5 +SUPPORTED_CHAINS=1,5,17000 # this chain uses when a wallet is not connected DEFAULT_CHAIN=5 @@ -20,5 +21,6 @@ CSP_REPORT_URI= # subgraph urls SUBGRAPH_MAINNET=https://api.thegraph.com/subgraphs/name/lidofinance/lido SUBGRAPH_GOERLI=https://api.thegraph.com/subgraphs/name/lidofinance/lido-testnet +SUBGRAPH_HOLESKY=https://api.thegraph.com/subgraphs/name/lidofinance/lido-testnet WALLETCONNECT_PROJECT_ID= diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 413c58c4..e9b9437f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ -* @lidofinance/lido-eth-ui @lidofinance/lido-qa +* @lidofinance/lido-dao-ops-team @lidofinance/lido-qa .github @lidofinance/review-gh-workflows diff --git a/.github/workflows/ci-dev-goerli.yml b/.github/workflows/ci-dev-goerli.yml new file mode 100644 index 00000000..369419ce --- /dev/null +++ b/.github/workflows/ci-dev-goerli.yml @@ -0,0 +1,29 @@ +name: CI Dev Goerli + +on: + workflow_dispatch: + push: + branches: + - goerli + paths-ignore: + - ".github/**" + +permissions: {} + +jobs: + # test: + # ... + + deploy: + runs-on: ubuntu-latest + # needs: test + name: Build and deploy + steps: + - name: Testnet deploy + uses: lidofinance/dispatch-workflow@v1 + env: + APP_ID: ${{ secrets.APP_ID }} + APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} + TARGET_REPO: "lidofinance/infra-mainnet" + TARGET_WORKFLOW: "deploy_testnet_easytrack_ui.yaml" + TARGET: "goerli" diff --git a/.github/workflows/ci-dev.yml b/.github/workflows/ci-dev.yml index 461e424e..c10ec9d2 100644 --- a/.github/workflows/ci-dev.yml +++ b/.github/workflows/ci-dev.yml @@ -1,4 +1,4 @@ -name: CI Dev +name: CI Dev Holesky on: workflow_dispatch: @@ -25,5 +25,5 @@ jobs: APP_ID: ${{ secrets.APP_ID }} APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} TARGET_REPO: "lidofinance/infra-mainnet" - TARGET_WORKFLOW: "deploy_testnet_easytrack_ui.yaml" + TARGET_WORKFLOW: "deploy_holesky_testnet_easytrack_ui.yaml" TARGET: "develop" diff --git a/.gitignore b/.gitignore index 86a72d52..cf79e9f9 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,7 @@ yarn-error.log* # vercel .vercel + +# ide +.idea +.vscode diff --git a/abi/AragonACL.abi.json b/abi/AragonACL.abi.json index 5c47facd..bc273363 100644 --- a/abi/AragonACL.abi.json +++ b/abi/AragonACL.abi.json @@ -1,74 +1,14 @@ [ - { - "constant": true, - "inputs": [], - "name": "proxyType", - "outputs": [{ "name": "proxyTypeId", "type": "uint256" }], - "payable": false, - "stateMutability": "pure", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "isDepositable", - "outputs": [{ "name": "", "type": "bool" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "implementation", - "outputs": [{ "name": "", "type": "address" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "appId", - "outputs": [{ "name": "", "type": "bytes32" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "kernel", - "outputs": [{ "name": "", "type": "address" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { "name": "_kernel", "type": "address" }, - { "name": "_appId", "type": "bytes32" }, - { "name": "_initializePayload", "type": "bytes" } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { "payable": true, "stateMutability": "payable", "type": "fallback" }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "name": "sender", "type": "address" }, - { "indexed": false, "name": "value", "type": "uint256" } - ], - "name": "ProxyDeposit", - "type": "event" - }, { "constant": true, "inputs": [], "name": "hasInitialized", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -76,8 +16,14 @@ { "constant": false, "inputs": [ - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "createBurnedPermission", "outputs": [], @@ -88,8 +34,14 @@ { "constant": false, "inputs": [ - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "burnPermissionManager", "outputs": [], @@ -100,9 +52,18 @@ { "constant": false, "inputs": [ - { "name": "_entity", "type": "address" }, - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_entity", + "type": "address" + }, + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "grantPermission", "outputs": [], @@ -113,12 +74,26 @@ { "constant": true, "inputs": [ - { "name": "_entity", "type": "address" }, - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_entity", + "type": "address" + }, + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "getPermissionParamsLength", - "outputs": [{ "name": "", "type": "uint256" }], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -126,14 +101,34 @@ { "constant": true, "inputs": [ - { "name": "_paramsHash", "type": "bytes32" }, - { "name": "_who", "type": "address" }, - { "name": "_where", "type": "address" }, - { "name": "_what", "type": "bytes32" }, - { "name": "_how", "type": "uint256[]" } + { + "name": "_paramsHash", + "type": "bytes32" + }, + { + "name": "_who", + "type": "address" + }, + { + "name": "_where", + "type": "address" + }, + { + "name": "_what", + "type": "bytes32" + }, + { + "name": "_how", + "type": "uint256[]" + } ], "name": "evalParams", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -142,16 +137,31 @@ "constant": true, "inputs": [], "name": "NO_PERMISSION", - "outputs": [{ "name": "", "type": "bytes32" }], + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, - "inputs": [{ "name": "_script", "type": "bytes" }], + "inputs": [ + { + "name": "_script", + "type": "bytes" + } + ], "name": "getEVMScriptExecutor", - "outputs": [{ "name": "", "type": "address" }], + "outputs": [ + { + "name": "", + "type": "address" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -160,7 +170,12 @@ "constant": true, "inputs": [], "name": "getRecoveryVault", - "outputs": [{ "name": "", "type": "address" }], + "outputs": [ + { + "name": "", + "type": "address" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -169,7 +184,12 @@ "constant": true, "inputs": [], "name": "CREATE_PERMISSIONS_ROLE", - "outputs": [{ "name": "", "type": "bytes32" }], + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -177,10 +197,22 @@ { "constant": false, "inputs": [ - { "name": "_entity", "type": "address" }, - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" }, - { "name": "_params", "type": "uint256[]" } + { + "name": "_entity", + "type": "address" + }, + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + }, + { + "name": "_params", + "type": "uint256[]" + } ], "name": "grantPermissionP", "outputs": [], @@ -191,21 +223,59 @@ { "constant": true, "inputs": [ - { "name": "_who", "type": "address" }, - { "name": "_where", "type": "address" }, - { "name": "_what", "type": "bytes32" } + { + "name": "_who", + "type": "address" + }, + { + "name": "_where", + "type": "address" + }, + { + "name": "_what", + "type": "bytes32" + } ], "name": "hasPermission", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, - "inputs": [{ "name": "token", "type": "address" }], + "inputs": [ + { + "name": "token", + "type": "address" + } + ], "name": "allowRecoverability", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "appId", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -214,7 +284,12 @@ "constant": true, "inputs": [], "name": "getInitializationBlock", - "outputs": [{ "name": "", "type": "uint256" }], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -222,9 +297,18 @@ { "constant": false, "inputs": [ - { "name": "_entity", "type": "address" }, - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_entity", + "type": "address" + }, + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "revokePermission", "outputs": [], @@ -234,7 +318,12 @@ }, { "constant": false, - "inputs": [{ "name": "_token", "type": "address" }], + "inputs": [ + { + "name": "_token", + "type": "address" + } + ], "name": "transferToVault", "outputs": [], "payable": false, @@ -244,16 +333,37 @@ { "constant": true, "inputs": [ - { "name": "_entity", "type": "address" }, - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" }, - { "name": "_index", "type": "uint256" } + { + "name": "_entity", + "type": "address" + }, + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + }, + { + "name": "_index", + "type": "uint256" + } ], "name": "getPermissionParam", "outputs": [ - { "name": "", "type": "uint8" }, - { "name": "", "type": "uint8" }, - { "name": "", "type": "uint240" } + { + "name": "", + "type": "uint8" + }, + { + "name": "", + "type": "uint8" + }, + { + "name": "", + "type": "uint240" + } ], "payable": false, "stateMutability": "view", @@ -262,12 +372,26 @@ { "constant": true, "inputs": [ - { "name": "_sender", "type": "address" }, - { "name": "_role", "type": "bytes32" }, - { "name": "_params", "type": "uint256[]" } + { + "name": "_sender", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + }, + { + "name": "_params", + "type": "uint256[]" + } ], "name": "canPerform", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -276,7 +400,12 @@ "constant": true, "inputs": [], "name": "getEVMScriptRegistry", - "outputs": [{ "name": "", "type": "address" }], + "outputs": [ + { + "name": "", + "type": "address" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -285,7 +414,12 @@ "constant": true, "inputs": [], "name": "ANY_ENTITY", - "outputs": [{ "name": "", "type": "address" }], + "outputs": [ + { + "name": "", + "type": "address" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -293,8 +427,14 @@ { "constant": false, "inputs": [ - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "removePermissionManager", "outputs": [], @@ -305,9 +445,18 @@ { "constant": false, "inputs": [ - { "name": "_newManager", "type": "address" }, - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_newManager", + "type": "address" + }, + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "setPermissionManager", "outputs": [], @@ -318,11 +467,22 @@ { "constant": true, "inputs": [ - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" } + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + } ], "name": "getPermissionManager", - "outputs": [{ "name": "", "type": "address" }], + "outputs": [ + { + "name": "", + "type": "address" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -330,10 +490,22 @@ { "constant": false, "inputs": [ - { "name": "_entity", "type": "address" }, - { "name": "_app", "type": "address" }, - { "name": "_role", "type": "bytes32" }, - { "name": "_manager", "type": "address" } + { + "name": "_entity", + "type": "address" + }, + { + "name": "_app", + "type": "address" + }, + { + "name": "_role", + "type": "bytes32" + }, + { + "name": "_manager", + "type": "address" + } ], "name": "createPermission", "outputs": [], @@ -343,7 +515,12 @@ }, { "constant": false, - "inputs": [{ "name": "_permissionsCreator", "type": "address" }], + "inputs": [ + { + "name": "_permissionsCreator", + "type": "address" + } + ], "name": "initialize", "outputs": [], "payable": false, @@ -354,7 +531,26 @@ "constant": true, "inputs": [], "name": "EMPTY_PARAM_HASH", - "outputs": [{ "name": "", "type": "bytes32" }], + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kernel", + "outputs": [ + { + "name": "", + "type": "address" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -363,7 +559,12 @@ "constant": true, "inputs": [], "name": "isPetrified", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -372,7 +573,12 @@ "constant": true, "inputs": [], "name": "BURN_ENTITY", - "outputs": [{ "name": "", "type": "address" }], + "outputs": [ + { + "name": "", + "type": "address" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -380,13 +586,30 @@ { "constant": true, "inputs": [ - { "name": "_who", "type": "address" }, - { "name": "_where", "type": "address" }, - { "name": "_what", "type": "bytes32" }, - { "name": "_how", "type": "uint256[]" } + { + "name": "_who", + "type": "address" + }, + { + "name": "_where", + "type": "address" + }, + { + "name": "_what", + "type": "bytes32" + }, + { + "name": "_how", + "type": "uint256[]" + } ], "name": "hasPermission", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -394,13 +617,30 @@ { "constant": true, "inputs": [ - { "name": "_who", "type": "address" }, - { "name": "_where", "type": "address" }, - { "name": "_what", "type": "bytes32" }, - { "name": "_how", "type": "bytes" } + { + "name": "_who", + "type": "address" + }, + { + "name": "_where", + "type": "address" + }, + { + "name": "_what", + "type": "bytes32" + }, + { + "name": "_how", + "type": "bytes" + } ], "name": "hasPermission", - "outputs": [{ "name": "", "type": "bool" }], + "outputs": [ + { + "name": "", + "type": "bool" + } + ], "payable": false, "stateMutability": "view", "type": "function" @@ -408,10 +648,26 @@ { "anonymous": false, "inputs": [ - { "indexed": true, "name": "entity", "type": "address" }, - { "indexed": true, "name": "app", "type": "address" }, - { "indexed": true, "name": "role", "type": "bytes32" }, - { "indexed": false, "name": "allowed", "type": "bool" } + { + "indexed": true, + "name": "entity", + "type": "address" + }, + { + "indexed": true, + "name": "app", + "type": "address" + }, + { + "indexed": true, + "name": "role", + "type": "bytes32" + }, + { + "indexed": false, + "name": "allowed", + "type": "bool" + } ], "name": "SetPermission", "type": "event" @@ -419,10 +675,26 @@ { "anonymous": false, "inputs": [ - { "indexed": true, "name": "entity", "type": "address" }, - { "indexed": true, "name": "app", "type": "address" }, - { "indexed": true, "name": "role", "type": "bytes32" }, - { "indexed": false, "name": "paramsHash", "type": "bytes32" } + { + "indexed": true, + "name": "entity", + "type": "address" + }, + { + "indexed": true, + "name": "app", + "type": "address" + }, + { + "indexed": true, + "name": "role", + "type": "bytes32" + }, + { + "indexed": false, + "name": "paramsHash", + "type": "bytes32" + } ], "name": "SetPermissionParams", "type": "event" @@ -430,9 +702,21 @@ { "anonymous": false, "inputs": [ - { "indexed": true, "name": "app", "type": "address" }, - { "indexed": true, "name": "role", "type": "bytes32" }, - { "indexed": true, "name": "manager", "type": "address" } + { + "indexed": true, + "name": "app", + "type": "address" + }, + { + "indexed": true, + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "name": "manager", + "type": "address" + } ], "name": "ChangePermissionManager", "type": "event" @@ -440,12 +724,50 @@ { "anonymous": false, "inputs": [ - { "indexed": true, "name": "executor", "type": "address" }, - { "indexed": false, "name": "script", "type": "bytes" }, - { "indexed": false, "name": "input", "type": "bytes" }, - { "indexed": false, "name": "returnData", "type": "bytes" } + { + "indexed": true, + "name": "executor", + "type": "address" + }, + { + "indexed": false, + "name": "script", + "type": "bytes" + }, + { + "indexed": false, + "name": "input", + "type": "bytes" + }, + { + "indexed": false, + "name": "returnData", + "type": "bytes" + } ], "name": "ScriptResult", "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "vault", + "type": "address" + }, + { + "indexed": true, + "name": "token", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + } + ], + "name": "RecoverToVault", + "type": "event" } ] diff --git a/abi/TopUp/AllowedTokensRegistry.abi.json b/abi/TopUp/AllowedTokensRegistry.abi.json new file mode 100644 index 00000000..748702b4 --- /dev/null +++ b/abi/TopUp/AllowedTokensRegistry.abi.json @@ -0,0 +1,259 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_admin", "type": "address" }, + { + "internalType": "address[]", + "name": "_addTokenToAllowedListRoleHolders", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "_removeTokenFromAllowedListRoleHolders", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "TokenAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "TokenRemoved", + "type": "event" + }, + { + "inputs": [], + "name": "ADD_TOKEN_TO_ALLOWED_LIST_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "REMOVE_TOKEN_FROM_ALLOWED_LIST_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_token", "type": "address" } + ], + "name": "addToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "allowedTokens", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getAllowedTokens", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_token", "type": "address" } + ], + "name": "isTokenAllowed", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenAmount", "type": "uint256" }, + { "internalType": "address", "name": "_token", "type": "address" } + ], + "name": "normalizeAmount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_token", "type": "address" } + ], + "name": "removeToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/TopUp/TopUpWithLimitsStables.abi.json b/abi/TopUp/TopUpWithLimitsStables.abi.json new file mode 100644 index 00000000..a5314a6b --- /dev/null +++ b/abi/TopUp/TopUpWithLimitsStables.abi.json @@ -0,0 +1,103 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_allowedRecipientsRegistry", + "type": "address" + }, + { + "internalType": "address", + "name": "_allowedTokensRegistry", + "type": "address" + }, + { "internalType": "address", "name": "_finance", "type": "address" }, + { "internalType": "address", "name": "_easyTrack", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "allowedRecipientsRegistry", + "outputs": [ + { + "internalType": "contract IAllowedRecipientsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allowedTokensRegistry", + "outputs": [ + { + "internalType": "contract IAllowedTokensRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "easyTrack", + "outputs": [ + { "internalType": "contract IEasyTrack", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finance", + "outputs": [ + { "internalType": "contract IFinance", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/ActivateNodeOperators.abi.json b/abi/dvt/ActivateNodeOperators.abi.json new file mode 100644 index 00000000..e0d35fa6 --- /dev/null +++ b/abi/dvt/ActivateNodeOperators.abi.json @@ -0,0 +1,85 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + }, + { "internalType": "address", "name": "_acl", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "acl", + "outputs": [ + { "internalType": "contract IACL", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nodeOperatorId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "managerAddress", + "type": "address" + } + ], + "internalType": "struct ActivateNodeOperators.ActivateNodeOperatorInput[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/AddNodeOperators.abi.json b/abi/dvt/AddNodeOperators.abi.json new file mode 100644 index 00000000..0a7152e7 --- /dev/null +++ b/abi/dvt/AddNodeOperators.abi.json @@ -0,0 +1,91 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + }, + { "internalType": "address", "name": "_acl", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "acl", + "outputs": [ + { "internalType": "contract IACL", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "internalType": "uint256", + "name": "nodeOperatorsCount", + "type": "uint256" + }, + { + "components": [ + { "internalType": "string", "name": "name", "type": "string" }, + { + "internalType": "address", + "name": "rewardAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "managerAddress", + "type": "address" + } + ], + "internalType": "struct AddNodeOperators.AddNodeOperatorInput[]", + "name": "nodeOperators", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/ChangeNodeOperatorManagers.abi.json b/abi/dvt/ChangeNodeOperatorManagers.abi.json new file mode 100644 index 00000000..3fb29029 --- /dev/null +++ b/abi/dvt/ChangeNodeOperatorManagers.abi.json @@ -0,0 +1,90 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + }, + { "internalType": "address", "name": "_acl", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "acl", + "outputs": [ + { "internalType": "contract IACL", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nodeOperatorId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "oldManagerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "newManagerAddress", + "type": "address" + } + ], + "internalType": "struct ChangeNodeOperatorManager.PermissionInput[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/DeactivateNodeOperators.abi.json b/abi/dvt/DeactivateNodeOperators.abi.json new file mode 100644 index 00000000..62290e5d --- /dev/null +++ b/abi/dvt/DeactivateNodeOperators.abi.json @@ -0,0 +1,85 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + }, + { "internalType": "address", "name": "_acl", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "acl", + "outputs": [ + { "internalType": "contract IACL", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nodeOperatorId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "managerAddress", + "type": "address" + } + ], + "internalType": "struct DeactivateNodeOperators.DeactivateNodeOperatorInput[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/SDVTRegistry.abi.json b/abi/dvt/SDVTRegistry.abi.json new file mode 100644 index 00000000..f8d7ac1b --- /dev/null +++ b/abi/dvt/SDVTRegistry.abi.json @@ -0,0 +1,892 @@ +[ + { + "constant": true, + "inputs": [], + "name": "hasInitialized", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_keysCount", "type": "uint256" }, + { "name": "_publicKeys", "type": "bytes" }, + { "name": "_signatures", "type": "bytes" } + ], + "name": "addSigningKeys", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getType", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_script", "type": "bytes" }], + "name": "getEVMScriptExecutor", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "clearNodeOperatorPenalty", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRecoveryVault", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "_offset", "type": "uint256" }, + { "name": "_limit", "type": "uint256" } + ], + "name": "getNodeOperatorIds", + "outputs": [{ "name": "nodeOperatorIds", "type": "uint256[]" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_offset", "type": "uint256" }, + { "name": "_limit", "type": "uint256" } + ], + "name": "getSigningKeys", + "outputs": [ + { "name": "pubkeys", "type": "bytes" }, + { "name": "signatures", "type": "bytes" }, + { "name": "used", "type": "bool[]" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_fromIndex", "type": "uint256" }, + { "name": "_keysCount", "type": "uint256" } + ], + "name": "removeSigningKeysOperatorBH", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "getNodeOperatorIsActive", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_name", "type": "string" } + ], + "name": "setNodeOperatorName", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_totalRewardShares", "type": "uint256" }], + "name": "getRewardsDistribution", + "outputs": [ + { "name": "recipients", "type": "address[]" }, + { "name": "shares", "type": "uint256[]" }, + { "name": "penalized", "type": "bool[]" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_indexFrom", "type": "uint256" }, + { "name": "_indexTo", "type": "uint256" } + ], + "name": "invalidateReadyToDepositKeysRange", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_locator", "type": "address" }, + { "name": "_type", "type": "bytes32" }, + { "name": "_stuckPenaltyDelay", "type": "uint256" } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_delay", "type": "uint256" }], + "name": "setStuckPenaltyDelay", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getStuckPenaltyDelay", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_index", "type": "uint256" } + ], + "name": "removeSigningKey", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_fromIndex", "type": "uint256" }, + { "name": "_keysCount", "type": "uint256" } + ], + "name": "removeSigningKeys", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "isOperatorPenalized", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "deactivateNodeOperator", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "token", "type": "address" }], + "name": "allowRecoverability", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "STAKING_ROUTER_ROLE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_keysCount", "type": "uint256" }, + { "name": "_publicKeys", "type": "bytes" }, + { "name": "_signatures", "type": "bytes" } + ], + "name": "addSigningKeysOperatorBH", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "appId", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getActiveNodeOperatorsCount", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_name", "type": "string" }, + { "name": "_rewardAddress", "type": "address" } + ], + "name": "addNodeOperator", + "outputs": [{ "name": "id", "type": "uint256" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getContractVersion", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getInitializationBlock", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "getUnusedSigningKeyCount", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "", "type": "uint256" }], + "name": "onRewardsMinted", + "outputs": [], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MANAGE_NODE_OPERATOR_ROLE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "onWithdrawalCredentialsChanged", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "activateNodeOperator", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_rewardAddress", "type": "address" } + ], + "name": "setNodeOperatorRewardAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_fullInfo", "type": "bool" } + ], + "name": "getNodeOperator", + "outputs": [ + { "name": "active", "type": "bool" }, + { "name": "name", "type": "string" }, + { "name": "rewardAddress", "type": "address" }, + { "name": "totalVettedValidators", "type": "uint64" }, + { "name": "totalExitedValidators", "type": "uint64" }, + { "name": "totalAddedValidators", "type": "uint64" }, + { "name": "totalDepositedValidators", "type": "uint64" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_locator", "type": "address" }, + { "name": "_type", "type": "bytes32" }, + { "name": "_stuckPenaltyDelay", "type": "uint256" } + ], + "name": "finalizeUpgrade_v2", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getStakingModuleSummary", + "outputs": [ + { "name": "totalExitedValidators", "type": "uint256" }, + { "name": "totalDepositedValidators", "type": "uint256" }, + { "name": "depositableValidatorsCount", "type": "uint256" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorIds", "type": "bytes" }, + { "name": "_exitedValidatorsCounts", "type": "bytes" } + ], + "name": "updateExitedValidatorsCount", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorIds", "type": "bytes" }, + { "name": "_stuckValidatorsCounts", "type": "bytes" } + ], + "name": "updateStuckValidatorsCount", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_token", "type": "address" }], + "name": "transferToVault", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "_sender", "type": "address" }, + { "name": "_role", "type": "bytes32" }, + { "name": "_params", "type": "uint256[]" } + ], + "name": "canPerform", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_refundedValidatorsCount", "type": "uint256" } + ], + "name": "updateRefundedValidatorsCount", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEVMScriptRegistry", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getNodeOperatorsCount", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_isTargetLimitActive", "type": "bool" }, + { "name": "_targetLimit", "type": "uint256" } + ], + "name": "updateTargetValidatorsLimits", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_vettedSigningKeysCount", "type": "uint64" } + ], + "name": "setNodeOperatorStakingLimit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "getNodeOperatorSummary", + "outputs": [ + { "name": "isTargetLimitActive", "type": "bool" }, + { "name": "targetValidatorsCount", "type": "uint256" }, + { "name": "stuckValidatorsCount", "type": "uint256" }, + { "name": "refundedValidatorsCount", "type": "uint256" }, + { "name": "stuckPenaltyEndTimestamp", "type": "uint256" }, + { "name": "totalExitedValidators", "type": "uint256" }, + { "name": "totalDepositedValidators", "type": "uint256" }, + { "name": "depositableValidatorsCount", "type": "uint256" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_index", "type": "uint256" } + ], + "name": "getSigningKey", + "outputs": [ + { "name": "key", "type": "bytes" }, + { "name": "depositSignature", "type": "bytes" }, + { "name": "used", "type": "bool" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_NODE_OPERATOR_NAME_LENGTH", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_depositsCount", "type": "uint256" }, + { "name": "", "type": "bytes" } + ], + "name": "obtainDepositData", + "outputs": [ + { "name": "publicKeys", "type": "bytes" }, + { "name": "signatures", "type": "bytes" } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getKeysOpIndex", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getNonce", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kernel", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getLocator", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "SET_NODE_OPERATOR_LIMIT_ROLE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "getTotalSigningKeyCount", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isPetrified", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_STUCK_PENALTY_DELAY", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "onExitedAndStuckValidatorsCountsUpdated", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MAX_NODE_OPERATORS_COUNT", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_index", "type": "uint256" } + ], + "name": "removeSigningKeyOperatorBH", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_nodeOperatorId", "type": "uint256" }, + { "name": "_exitedValidatorsCount", "type": "uint256" }, + { "name": "_stuckValidatorsCount", "type": "uint256" } + ], + "name": "unsafeUpdateValidatorsCount", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MANAGE_SIGNING_KEYS", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_nodeOperatorId", "type": "uint256" }], + "name": "isOperatorPenaltyCleared", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "name", "type": "string" }, + { "indexed": false, "name": "rewardAddress", "type": "address" }, + { "indexed": false, "name": "stakingLimit", "type": "uint64" } + ], + "name": "NodeOperatorAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "active", "type": "bool" } + ], + "name": "NodeOperatorActiveSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "name", "type": "string" } + ], + "name": "NodeOperatorNameSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "rewardAddress", "type": "address" } + ], + "name": "NodeOperatorRewardAddressSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "totalKeysTrimmed", "type": "uint64" } + ], + "name": "NodeOperatorTotalKeysTrimmed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "name": "keysOpIndex", "type": "uint256" }], + "name": "KeysOpIndexSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "name": "moduleType", "type": "bytes32" }], + "name": "StakingModuleTypeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "rewardAddress", "type": "address" }, + { "indexed": false, "name": "sharesAmount", "type": "uint256" } + ], + "name": "RewardsDistributed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "name": "locatorAddress", "type": "address" } + ], + "name": "LocatorContractSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "approvedValidatorsCount", "type": "uint256" } + ], + "name": "VettedSigningKeysCountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { + "indexed": false, + "name": "depositedValidatorsCount", + "type": "uint256" + } + ], + "name": "DepositedSigningKeysCountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "exitedValidatorsCount", "type": "uint256" } + ], + "name": "ExitedSigningKeysCountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "totalValidatorsCount", "type": "uint256" } + ], + "name": "TotalSigningKeysCountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "name": "nonce", "type": "uint256" }], + "name": "NonceChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "name": "stuckPenaltyDelay", "type": "uint256" } + ], + "name": "StuckPenaltyDelayChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "stuckValidatorsCount", "type": "uint256" }, + { + "indexed": false, + "name": "refundedValidatorsCount", + "type": "uint256" + }, + { + "indexed": false, + "name": "stuckPenaltyEndTimestamp", + "type": "uint256" + } + ], + "name": "StuckPenaltyStateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "nodeOperatorId", "type": "uint256" }, + { "indexed": false, "name": "targetValidatorsCount", "type": "uint256" } + ], + "name": "TargetValidatorsCountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "recipientAddress", "type": "address" }, + { "indexed": false, "name": "sharesPenalizedAmount", "type": "uint256" } + ], + "name": "NodeOperatorPenalized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "name": "version", "type": "uint256" }], + "name": "ContractVersionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "executor", "type": "address" }, + { "indexed": false, "name": "script", "type": "bytes" }, + { "indexed": false, "name": "input", "type": "bytes" }, + { "indexed": false, "name": "returnData", "type": "bytes" } + ], + "name": "ScriptResult", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "vault", "type": "address" }, + { "indexed": true, "name": "token", "type": "address" }, + { "indexed": false, "name": "amount", "type": "uint256" } + ], + "name": "RecoverToVault", + "type": "event" + } +] diff --git a/abi/dvt/SetNodeOperatorNames.abi.json b/abi/dvt/SetNodeOperatorNames.abi.json new file mode 100644 index 00000000..5439c32b --- /dev/null +++ b/abi/dvt/SetNodeOperatorNames.abi.json @@ -0,0 +1,71 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nodeOperatorId", + "type": "uint256" + }, + { "internalType": "string", "name": "name", "type": "string" } + ], + "internalType": "struct SetNodeOperatorNames.SetNameInput[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/SetNodeOperatorRewardAddresses.abi.json b/abi/dvt/SetNodeOperatorRewardAddresses.abi.json new file mode 100644 index 00000000..23d3358f --- /dev/null +++ b/abi/dvt/SetNodeOperatorRewardAddresses.abi.json @@ -0,0 +1,75 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nodeOperatorId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "rewardAddress", + "type": "address" + } + ], + "internalType": "struct SetNodeOperatorRewardAddresses.SetRewardAddressInput[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/SetVettedValidatorsLimits.abi.json b/abi/dvt/SetVettedValidatorsLimits.abi.json new file mode 100644 index 00000000..8e8263fe --- /dev/null +++ b/abi/dvt/SetVettedValidatorsLimits.abi.json @@ -0,0 +1,75 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nodeOperatorId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stakingLimit", + "type": "uint256" + } + ], + "internalType": "struct SetVettedValidatorsLimits.VettedValidatorsLimitInput[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/dvt/UpdateTargetValidatorLimits.abi.json b/abi/dvt/UpdateTargetValidatorLimits.abi.json new file mode 100644 index 00000000..fd83e5b1 --- /dev/null +++ b/abi/dvt/UpdateTargetValidatorLimits.abi.json @@ -0,0 +1,80 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedCaller", + "type": "address" + }, + { + "internalType": "address", + "name": "_nodeOperatorsRegistry", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { "internalType": "address", "name": "_creator", "type": "address" }, + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "createEVMScript", + "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes", "name": "_evmScriptCallData", "type": "bytes" } + ], + "name": "decodeEVMScriptCallData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "nodeOperatorId", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isTargetLimitActive", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "targetLimit", + "type": "uint256" + } + ], + "internalType": "struct UpdateTargetValidatorLimits.TargetValidatorsLimit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "nodeOperatorsRegistry", + "outputs": [ + { + "internalType": "contract INodeOperatorsRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "trustedCaller", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/modules/appWagmiConfig/appWagmiConfig.tsx b/modules/appWagmiConfig/appWagmiConfig.tsx index 1b773f71..cc2ab277 100644 --- a/modules/appWagmiConfig/appWagmiConfig.tsx +++ b/modules/appWagmiConfig/appWagmiConfig.tsx @@ -1,5 +1,5 @@ import { FC } from 'react' -import { WagmiConfig, configureChains, createClient } from 'wagmi' +import { configureChains, createClient, WagmiConfig } from 'wagmi' import * as wagmiChains from 'wagmi/chains' import { jsonRpcProvider } from 'wagmi/providers/jsonRpc' import { getConnectors } from 'reef-knot/core-react' @@ -7,6 +7,32 @@ import getConfig from 'next/config' import { CHAINS } from '@lido-sdk/constants' import { getBackendRpcUrl } from 'modules/blockChain/utils/getBackendRpcUrl' +export const holesky = { + id: CHAINS.Holesky, + name: 'Holesky', + network: 'holesky', + nativeCurrency: { + decimals: 18, + name: 'holeskyETH', + symbol: 'ETH', + }, + rpcUrls: { + public: { http: [getBackendRpcUrl(CHAINS.Holesky)] }, + default: { http: [getBackendRpcUrl(CHAINS.Holesky)] }, + }, + blockExplorers: { + etherscan: { name: 'holesky', url: 'https://holesky.etherscan.io/' }, + default: { name: 'holesky', url: 'https://holesky.etherscan.io/' }, + }, + testnet: true, + contracts: { + multicall3: { + address: '0xcA11bde05977b3631167028862bE2a173976CA11', + blockCreated: 77, + }, + }, +} as const + const { publicRuntimeConfig } = getConfig() let supportedChainIds: number[] = [] @@ -19,7 +45,11 @@ if (publicRuntimeConfig.supportedChains != null) { supportedChainIds = [parseInt(publicRuntimeConfig.defaultChain)] } -const wagmiChainsArray = Object.values(wagmiChains) +const wagmiChainsArray = Object.values({ + ...wagmiChains, + [CHAINS.Holesky]: holesky, +}) + const supportedChains = wagmiChainsArray.filter( chain => // Temporary wagmi fix, need to hardcode it to not affect non-wagmi wallets diff --git a/modules/blockChain/chains.ts b/modules/blockChain/chains.ts index 3aa9b504..4f68daa9 100644 --- a/modules/blockChain/chains.ts +++ b/modules/blockChain/chains.ts @@ -7,10 +7,11 @@ export const ChainNames = { [CHAINS.Goerli]: 'Goerli', [CHAINS.Kovan]: 'Kovan', [CHAINS.Kintsugi]: 'Kintsugi', + [CHAINS.Holesky]: 'Holesky', } as const export const parseChainId = (chainId: number | string) => { - return Number(chainId) as CHAINS + return Number(chainId) as keyof typeof ChainNames } export const getChainName = (chainId: number) => diff --git a/modules/blockChain/constants.ts b/modules/blockChain/constants.ts new file mode 100644 index 00000000..70c2ceed --- /dev/null +++ b/modules/blockChain/constants.ts @@ -0,0 +1 @@ +export const DEFAULT_DECIMALS = 18 diff --git a/modules/blockChain/contractAddresses.ts b/modules/blockChain/contractAddresses.ts index 423a891b..aadee637 100644 --- a/modules/blockChain/contractAddresses.ts +++ b/modules/blockChain/contractAddresses.ts @@ -4,16 +4,19 @@ import type { ChainAddressMap } from './types' export const NodeOperatorsRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5', [CHAINS.Goerli]: '0x9D4AF1Ee19Dad8857db3a45B0374c81c8A1C6320', + [CHAINS.Holesky]: '0x595F64Ddc3856a3b5Ff4f4CC1d1fb4B46cFd2bAC', } export const EasyTrack: ChainAddressMap = { [CHAINS.Mainnet]: '0xF0211b7660680B49De1A7E9f25C65660F0a13Fea', [CHAINS.Goerli]: '0xAf072C8D368E4DD4A9d4fF6A76693887d6ae92Af', + [CHAINS.Holesky]: '0x1763b9ED3586B08AE796c7787811a2E1bc16163a', } export const GovernanceToken: ChainAddressMap = { [CHAINS.Mainnet]: '0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32', [CHAINS.Goerli]: '0x56340274fB5a72af1A3C6609061c451De7961Bd4', + [CHAINS.Holesky]: '0x14ae7daeecdf57034f3E9db8564e46Dba8D97344', } export const RewardProgramRegistry: ChainAddressMap = { @@ -29,16 +32,19 @@ export const ReferralPartnersRegistry: ChainAddressMap = { export const STETH: ChainAddressMap = { [CHAINS.Mainnet]: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', [CHAINS.Goerli]: '0x1643E812aE58766192Cf7D2Cf9567dF2C37e9B7F', + [CHAINS.Holesky]: '0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034', } export const DAI: ChainAddressMap = { [CHAINS.Mainnet]: '0x6B175474E89094C44Da98b954EedeAC495271d0F', [CHAINS.Goerli]: '0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844', + [CHAINS.Holesky]: '0x2eb8e9198e647f80ccf62a5e291bcd4a5a3ca68c', } export const Finance: ChainAddressMap = { [CHAINS.Mainnet]: '0xB9E5CBB9CA5b0d659238807E84D0176930753d86', [CHAINS.Goerli]: '0x75c7b1D23f1cad7Fb4D60281d7069E46440BC179', + [CHAINS.Holesky]: ' 0xf0F281E5d7FBc54EAFcE0dA225CDbde04173AB16', } export const AllowedRecipientRegistry: ChainAddressMap = { @@ -54,29 +60,32 @@ export const AllowedRecipientReferralDaiRegistry: ChainAddressMap = { export const AllowedRecipientTrpLdoRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0x231Ac69A1A37649C6B06a71Ab32DdD92158C80b8', [CHAINS.Goerli]: '0x8C96a6522aEc036C4a384f8B7e05D93d6f3Dae39', + [CHAINS.Holesky]: '0x5f4E9A917d6556dB91Cf351f49b0edCc5A255bAE', } export const LegoLDORegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0x97615f72c3428A393d65A84A3ea6BBD9ad6C0D74', [CHAINS.Goerli]: '0x6342213719839c56fEe817539863aFB9821B03cb', + [CHAINS.Holesky]: '0x77CF728329920E4191a6Edd9b009cD055D3cD29A', } export const LegoDAIRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0xb0FE4D300334461523D9d61AaD90D0494e1Abb43', [CHAINS.Goerli]: '0x5884f5849414D4317d175fEc144e2F87f699B082', + [CHAINS.Holesky]: '0x10Ff9c02C65775379D9E20BFF9AC92Cbaf15Ab8F', } -export const RccDAIRegistry: ChainAddressMap = { +export const RccStablesRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0xDc1A0C7849150f466F07d48b38eAA6cE99079f80', [CHAINS.Goerli]: '0x1440E8aDbE3a42a9EDB4b30059df8F6c35205a15', } -export const PmlDAIRegistry: ChainAddressMap = { +export const PmlStablesRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0xDFfCD3BF14796a62a804c1B16F877Cf7120379dB', [CHAINS.Goerli]: '0xAadfBd1ADE92d85c967f4aE096141F0F802F46Db', } -export const AtcDAIRegistry: ChainAddressMap = { +export const AtcStablesRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0xe07305F43B11F230EaA951002F6a55a16419B707', [CHAINS.Goerli]: '0xedD3B813275e1A88c2283FAfa5bf5396938ef59e', } @@ -84,29 +93,56 @@ export const AtcDAIRegistry: ChainAddressMap = { export const gasFunderETHRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0xCf46c4c7f936dF6aE12091ADB9897E3F2363f16F', [CHAINS.Goerli]: '0x0000000000000000000000000000000000000000', + [CHAINS.Holesky]: '0x0000000000000000000000000000000000000000', } export const StethRewardProgramRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0x48c4929630099b217136b64089E8543dB0E5163a', [CHAINS.Goerli]: '0x78797efCca6C537BF92FA6b25cBb14A6f1c128A0', + [CHAINS.Holesky]: '0x55B304a585D540421F1fD3579Ef12Abab7304492', } export const StethGasSupplyRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0x49d1363016aA899bba09ae972a1BF200dDf8C55F', [CHAINS.Goerli]: '0xF08a5f00824D4554a1FBebaE726609418dc819fb', + [CHAINS.Holesky]: '0x1B68a7BeE396e2eaAD9D2716E0A271A4BB568BCd', } export const AragonACL: ChainAddressMap = { [CHAINS.Mainnet]: '0x9895F0F17cc1d1891b6f18ee0b483B6f221b37Bb', [CHAINS.Goerli]: '0xb3cf58412a00282934d3c3e73f49347567516e98', + [CHAINS.Holesky]: '0xfd1E42595CeC3E83239bf8dFc535250e7F48E0bC', } export const EVMScriptExecutor: ChainAddressMap = { [CHAINS.Mainnet]: '0xF0211b7660680B49De1A7E9f25C65660F0a13Fea', [CHAINS.Goerli]: '0x3c9AcA237b838c59612d79198685e7f20C7fE783', + [CHAINS.Holesky]: '0x2819B65021E13CEEB9AC33E77DB32c7e64e7520D', } export const RewardsShareProgramRegistry: ChainAddressMap = { [CHAINS.Mainnet]: '0xdc7300622948a7AdaF339783F6991F9cdDD79776', [CHAINS.Goerli]: '0x8b59609f4bEa230E565Ae0C3C7b6913746Df1cF2', + [CHAINS.Holesky]: '0xAc2F596191c75B77c2835Afe83c3a9097f0AC071', +} + +export const SDVTRegistry: ChainAddressMap = { + [CHAINS.Goerli]: '0x6370FA71b9Fd83aFC4196ee189a0d348C90E93b0', + [CHAINS.Holesky]: '0x11a93807078f8BB880c1BD0ee4C387537de4b4b6', +} + +export const SandboxNodeOperatorsRegistry: ChainAddressMap = { + [CHAINS.Holesky]: '0xD6C2ce3BB8bea2832496Ac8b5144819719f343AC', +} + +export const AllowedTokensRegistry: ChainAddressMap = { + [CHAINS.Goerli]: '0xeda5a9F02a580B4A879aEA65E2a7B7fEc0956b0E', +} + +export const SandboxStablesAllowedTokensRegistry: ChainAddressMap = { + [CHAINS.Holesky]: '0x091c0ec8b4d54a9fcb36269b5d5e5af43309e666', +} + +export const SandboxStablesAllowedRecipientRegistry: ChainAddressMap = { + [CHAINS.Holesky]: '0xF8a63a36B954D72de197097377aa00C238c653Cf', } diff --git a/modules/blockChain/contracts.ts b/modules/blockChain/contracts.ts index 0c2d9669..de10a81c 100644 --- a/modules/blockChain/contracts.ts +++ b/modules/blockChain/contracts.ts @@ -124,31 +124,25 @@ export const ContractEvmLegoDAITopUp = createContractHelpers({ address: EvmAddressesByType[MotionType.LegoDAITopUp], }) -export const ContractRccDAIRegistry = createContractHelpers({ - factory: TypeChain.RegistryWithLimitsAbi__factory, - address: CONTRACT_ADDRESSES.RccDAIRegistry, -}) - +/** + * @deprecated + */ export const ContractEvmRccDAITopUp = createContractHelpers({ factory: TypeChain.TopUpWithLimitsAbi__factory, address: EvmAddressesByType[MotionType.RccDAITopUp], }) -export const ContractPmlDAIRegistry = createContractHelpers({ - factory: TypeChain.RegistryWithLimitsAbi__factory, - address: CONTRACT_ADDRESSES.PmlDAIRegistry, -}) - +/** + * @deprecated + */ export const ContractEvmPmlDAITopUp = createContractHelpers({ factory: TypeChain.TopUpWithLimitsAbi__factory, address: EvmAddressesByType[MotionType.PmlDAITopUp], }) -export const ContractAtcDAIRegistry = createContractHelpers({ - factory: TypeChain.RegistryWithLimitsAbi__factory, - address: CONTRACT_ADDRESSES.AtcDAIRegistry, -}) - +/** + * @deprecated + */ export const ContractEvmAtcDAITopUp = createContractHelpers({ factory: TypeChain.TopUpWithLimitsAbi__factory, address: EvmAddressesByType[MotionType.AtcDAITopUp], @@ -282,3 +276,123 @@ export const ContractRewardsShareProgramTopUp = createContractHelpers({ factory: TypeChain.TopUpAllowedRecipientsAbi__factory, address: EvmAddressesByType[MotionType.RewardsShareProgramTopUp], }) + +export const ContractSDVTRegistry = createContractHelpers({ + factory: TypeChain.SDVTRegistryAbi__factory, + address: CONTRACT_ADDRESSES.SDVTRegistry, +}) + +export const ContractSDVTNodeOperatorsAdd = createContractHelpers({ + factory: TypeChain.AddNodeOperatorsAbi__factory, + address: EvmAddressesByType[MotionType.SDVTNodeOperatorsAdd], +}) + +export const ContractSDVTNodeOperatorsActivate = createContractHelpers({ + factory: TypeChain.ActivateNodeOperatorsAbi__factory, + address: EvmAddressesByType[MotionType.SDVTNodeOperatorsActivate], +}) + +export const ContractSDVTNodeOperatorsDeactivate = createContractHelpers({ + factory: TypeChain.DeactivateNodeOperatorsAbi__factory, + address: EvmAddressesByType[MotionType.SDVTNodeOperatorsDeactivate], +}) + +export const ContractSDVTVettedValidatorsLimitsSet = createContractHelpers({ + factory: TypeChain.SetVettedValidatorsLimitsAbi__factory, + address: EvmAddressesByType[MotionType.SDVTVettedValidatorsLimitsSet], +}) + +export const ContractSDVTNodeOperatorNamesSet = createContractHelpers({ + factory: TypeChain.SetNodeOperatorNamesAbi__factory, + address: EvmAddressesByType[MotionType.SDVTNodeOperatorNamesSet], +}) + +export const ContractSDVTNodeOperatorRewardAddressesSet = createContractHelpers( + { + factory: TypeChain.SetNodeOperatorRewardAddressesAbi__factory, + address: EvmAddressesByType[MotionType.SDVTNodeOperatorRewardAddressesSet], + }, +) + +export const ContractSDVTNodeOperatorManagerChange = createContractHelpers({ + factory: TypeChain.ChangeNodeOperatorManagersAbi__factory, + address: EvmAddressesByType[MotionType.SDVTNodeOperatorManagerChange], +}) + +export const ContractSDVTTargetValidatorLimitsUpdate = createContractHelpers({ + factory: TypeChain.UpdateTargetValidatorLimitsAbi__factory, + address: EvmAddressesByType[MotionType.SDVTTargetValidatorLimitsUpdate], +}) + +export const ContractSandboxNodeOperatorsRegistry = createContractHelpers({ + factory: TypeChain.NodeOperatorsAbi__factory, + address: CONTRACT_ADDRESSES.SandboxNodeOperatorsRegistry, +}) + +export const ContractEvmSandboxNodeOperatorIncreaseLimit = + createContractHelpers({ + factory: TypeChain.EvmIncreaseNodeOperatorStakingLimitAbi__factory, + address: EvmAddressesByType[MotionType.SandboxNodeOperatorIncreaseLimit], + }) + +export const ContractAllowedTokensRegistry = createContractHelpers({ + factory: TypeChain.AllowedTokensRegistryAbi__factory, + address: CONTRACT_ADDRESSES.AllowedTokensRegistry, +}) + +export const ContractRccStablesRegistry = createContractHelpers({ + factory: TypeChain.RegistryWithLimitsAbi__factory, + address: CONTRACT_ADDRESSES.RccStablesRegistry, +}) + +export const ContractEvmRccStablesTopUp = createContractHelpers({ + factory: TypeChain.TopUpWithLimitsStablesAbi__factory, + address: EvmAddressesByType[MotionType.RccStablesTopUp], +}) + +export const ContractPmlStablesRegistry = createContractHelpers({ + factory: TypeChain.RegistryWithLimitsAbi__factory, + address: CONTRACT_ADDRESSES.PmlStablesRegistry, +}) + +export const ContractEvmPmlStablesTopUp = createContractHelpers({ + factory: TypeChain.TopUpWithLimitsStablesAbi__factory, + address: EvmAddressesByType[MotionType.PmlStablesTopUp], +}) + +export const ContractAtcStablesRegistry = createContractHelpers({ + factory: TypeChain.RegistryWithLimitsAbi__factory, + address: CONTRACT_ADDRESSES.AtcStablesRegistry, +}) + +export const ContractEvmAtcStablesTopUp = createContractHelpers({ + factory: TypeChain.TopUpWithLimitsStablesAbi__factory, + address: EvmAddressesByType[MotionType.AtcStablesTopUp], +}) + +export const ContractSandboxStablesAllowedRecipientRegistry = + createContractHelpers({ + factory: TypeChain.RegistryWithLimitsAbi__factory, + address: CONTRACT_ADDRESSES.SandboxStablesAllowedRecipientRegistry, + }) + +export const ContractSandboxStablesAllowedTokensRegistry = + createContractHelpers({ + factory: TypeChain.AllowedTokensRegistryAbi__factory, + address: CONTRACT_ADDRESSES.SandboxStablesAllowedTokensRegistry, + }) + +export const ContractEvmSandboxStablesAdd = createContractHelpers({ + factory: TypeChain.AddAllowedRecipientAbi__factory, + address: EvmAddressesByType[MotionType.SandboxStablesAdd], +}) + +export const ContractEvmSandboxStablesRemove = createContractHelpers({ + factory: TypeChain.RemoveAllowedRecipientAbi__factory, + address: EvmAddressesByType[MotionType.SandboxStablesRemove], +}) + +export const ContractEvmSandboxStablesTopUp = createContractHelpers({ + factory: TypeChain.TopUpWithLimitsStablesAbi__factory, + address: EvmAddressesByType[MotionType.SandboxStablesTopUp], +}) diff --git a/modules/blockChain/utils/getBackendRpcUrl.ts b/modules/blockChain/utils/getBackendRpcUrl.ts index 06c46bb7..0d224d22 100644 --- a/modules/blockChain/utils/getBackendRpcUrl.ts +++ b/modules/blockChain/utils/getBackendRpcUrl.ts @@ -9,4 +9,5 @@ export const getBackendRpcUrl = (chainId: CHAINS) => { export const backendRPC = { [CHAINS.Mainnet]: getBackendRpcUrl(CHAINS.Mainnet), [CHAINS.Goerli]: getBackendRpcUrl(CHAINS.Goerli), + [CHAINS.Holesky]: getBackendRpcUrl(CHAINS.Holesky), } diff --git a/modules/blockChain/utils/getGnosisSafeLink.ts b/modules/blockChain/utils/getGnosisSafeLink.ts index 6773fd65..fc866212 100644 --- a/modules/blockChain/utils/getGnosisSafeLink.ts +++ b/modules/blockChain/utils/getGnosisSafeLink.ts @@ -11,6 +11,9 @@ export const getGnosisSafeLink = ( address: string, txHash: string, ) => { + if (chainId === CHAINS.Holesky) { + return `https://holesky-safe.protofire.io/transactions?safe=holesky:${address}` + } const chain = get(PREFIXES, chainId, '?') return `https://app.safe.global/transactions/tx?safe=${chain}:${address}&id=multisig_${address}_${txHash}` } diff --git a/modules/motions/constants.ts b/modules/motions/constants.ts index 1ebfd3e7..455faca9 100644 --- a/modules/motions/constants.ts +++ b/modules/motions/constants.ts @@ -1,10 +1,37 @@ -export const MOTION_ATTENTION_PERIOD = 1 / 24 +import { + ContractEvmNodeOperatorIncreaseLimit, + ContractEvmSandboxNodeOperatorIncreaseLimit, + ContractNodeOperatorsRegistry, + ContractSandboxNodeOperatorsRegistry, +} from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' -export const tokenLimitError = ( - governanceSymbol: string | undefined, - transitionLimit: number, -) => - `${governanceSymbol} transition is limited by ${transitionLimit.toLocaleString()}` +export const MOTION_ATTENTION_PERIOD = 1 / 24 export const periodLimitError = () => 'The top-up is higher than the remaining current period limit' + +export const noSigningKeysRoleError = + 'Address is not allowed to manage signing keys' + +export const INCREASE_LIMIT_MOTION_MAP = { + [MotionType.NodeOperatorIncreaseLimit]: { + evmContract: ContractEvmNodeOperatorIncreaseLimit, + registryType: 'curated', + motionType: MotionType.NodeOperatorIncreaseLimit, + }, + [MotionType.SandboxNodeOperatorIncreaseLimit]: { + evmContract: ContractEvmSandboxNodeOperatorIncreaseLimit, + registryType: 'sandbox', + motionType: MotionType.SandboxNodeOperatorIncreaseLimit, + }, +} as const + +export type IncreaseLimitMotionType = keyof typeof INCREASE_LIMIT_MOTION_MAP + +export const NODE_OPERATORS_REGISTRY_MAP = { + curated: ContractNodeOperatorsRegistry, + sandbox: ContractSandboxNodeOperatorsRegistry, +} as const + +export type NodeOperatorsRegistryType = keyof typeof NODE_OPERATORS_REGISTRY_MAP diff --git a/modules/motions/evmAddresses.ts b/modules/motions/evmAddresses.ts index 553d213c..1f9aa4c9 100644 --- a/modules/motions/evmAddresses.ts +++ b/modules/motions/evmAddresses.ts @@ -1,13 +1,22 @@ import { flow, map, toPairs, fromPairs, mapValues } from 'lodash/fp' import { CHAINS } from '@lido-sdk/constants' import { MotionType } from './types' -import type { Invert } from 'modules/shared/utils/utilTypes' +import { Invert } from 'modules/shared/utils/utilTypes' -// -// Addresses should be lower cased -// +const EvmSupportedChains = [ + CHAINS.Mainnet, + CHAINS.Goerli, + CHAINS.Holesky, +] as const -export const EvmAddressesByChain = { +export type EvmSupportedChain = typeof EvmSupportedChains[number] + +type EvmAddresses = Record< + EvmSupportedChain, + Partial> +> + +export const EvmAddressesByChain: EvmAddresses = { // Mainnet [CHAINS.Mainnet]: { [MotionType.NodeOperatorIncreaseLimit]: @@ -79,6 +88,9 @@ export const EvmAddressesByChain = { [MotionType.RccDAITopUp]: '0xd0411e7c4A24E7d4509D5F13AEd19aeb8e5644AB', [MotionType.PmlDAITopUp]: '0xc749aD24572263887Bc888d3Cb854FCD50eCCB61', [MotionType.AtcDAITopUp]: '0xF4b8b5760EE4b5c5Cb154edd0f0841465d821006', + [MotionType.RccStablesTopUp]: '0xd50eE42B31Bc500409B7caD99A2D16FB1Bfecdc6', + [MotionType.PmlStablesTopUp]: '0x5F379512158A46ab7a91f8b799A97691eC498b9a', + [MotionType.AtcStablesTopUp]: '0xB87300405050e7f1dBC35c6C9ce9ea4417D3Ad81', [MotionType.StethRewardProgramAdd]: '0x785A8B1CDC03Bb191670Ed4696e9ED5B11Af910A', [MotionType.StethRewardProgramRemove]: @@ -97,6 +109,22 @@ export const EvmAddressesByChain = { '0x932aab3D6057ed2Beef95471414831C4535600E9', [MotionType.RewardsShareProgramTopUp]: '0x5Bb391170899A7b8455A442cca65078ff3E1639C', + [MotionType.SDVTNodeOperatorsAdd]: + '0x69ab4BeD4D136F1e22c6072277BA5E52A246672B', + [MotionType.SDVTNodeOperatorsActivate]: + '0x4C0e79308f2E672b9dB9f2E6fD183Ec6025eFc37', + [MotionType.SDVTNodeOperatorsDeactivate]: + '0x2b956B578D0f44E0BD484d1A63c8A164BBEf6B58', + [MotionType.SDVTVettedValidatorsLimitsSet]: + '0x7f5395AC6Ff3967CEd48e6a99029747B48239b31', + [MotionType.SDVTTargetValidatorLimitsUpdate]: + '0x3F65d94E804bfEF570A13FC6923855865098EEB6', + [MotionType.SDVTNodeOperatorRewardAddressesSet]: + '0x85350e579C71a78810305f860380a3315b3e6Ed9', + [MotionType.SDVTNodeOperatorNamesSet]: + '0xc8b9F2bfFFF2f2B8F9C32A7b39a5AAa0644Fe632', + [MotionType.SDVTNodeOperatorManagerChange]: + '0x2Ed0FB58ba7637f972100Db7427614C9E30Ed684', // next motion factories are @deprecated // we are keeping them here to display history data @@ -126,6 +154,77 @@ export const EvmAddressesByChain = { [MotionType.AllowedRecipientTopUpReferralDai]: '0x9534A77029D57E249c467E5A1E0854cc26Cd75A0', }, + + // Holesky + [CHAINS.Holesky]: { + [MotionType.NodeOperatorIncreaseLimit]: + '0x18Ff3bD97739bf910cDCDb8d138976c6afDB4449', + [MotionType.AllowedRecipientTopUpTrpLdo]: + '0xD618F0CF48F057B5256e102dC18d8011e08c19D3', + [MotionType.LegoLDOTopUp]: '0xCfaFcD35ACcc4383e2CCDf7DD3F58114914F1955', + [MotionType.LegoDAITopUp]: '0xBCcfe42cc3EF530db9888dC8F82B1B4A4DfB9DB4', + [MotionType.StethRewardProgramAdd]: + '0xf0968B9bE18282dD23bbbC79a1c9C8996CE6984D', + [MotionType.StethRewardProgramRemove]: + '0xF0F34b82241cD49BB3952149BD30A08Eb9D8B54E', + [MotionType.StethRewardProgramTopUp]: + '0xBB06DD9a3C7eE8cE093860094e769a1E3D6F97F6', + [MotionType.StethGasSupplyAdd]: + '0x13dB9E1ddE54d2641f571EA288D9e79C0E8bce2e', + [MotionType.StethGasSupplyRemove]: + '0x64CE36D2DC7e7786BF56D2DF8A5F3c788977Fb19', + [MotionType.StethGasSupplyTopUp]: + '0xf97E048A952d170d5D5E817C8D9c8253f4D50F96', + [MotionType.RewardsShareProgramAdd]: + '0x49D3211203e8E18B4e60F74C1126934da2520987', + [MotionType.RewardsShareProgramRemove]: + '0x112c48c4659A9a1d42a3e45EBc8e37B6150F2B0C', + [MotionType.RewardsShareProgramTopUp]: + '0x089bc04630c056D76fF4Ec172e752A7d5B855e16', + + [MotionType.SDVTNodeOperatorsAdd]: + '0xeF5233A5bbF243149E35B353A73FFa8931FDA02b', + [MotionType.SDVTNodeOperatorsActivate]: + '0x5b4A9048176D5bA182ceec8e673D8aA6927A40D6', + [MotionType.SDVTNodeOperatorsDeactivate]: + '0x88d247cdf4ff4A4AAA8B3DD9dd22D1b89219FB3B', + [MotionType.SDVTVettedValidatorsLimitsSet]: + '0x30Cb36DBb0596aD9Cf5159BD2c4B1456c18e47E8', + [MotionType.SDVTTargetValidatorLimitsUpdate]: + '0xC91a676A69Eb49be9ECa1954fE6fc861AE07A9A2', + [MotionType.SDVTNodeOperatorRewardAddressesSet]: + '0x6Bfc576018C7f3D2a9180974E5c8e6CFa021f617', + [MotionType.SDVTNodeOperatorNamesSet]: + '0x4792BaC0a262200fA7d3b68e7622bFc1c2c3a72d', + [MotionType.SDVTNodeOperatorManagerChange]: + '0xb8C4728bc0826bA5864D02FA53148de7A44C2f7E', + + [MotionType.SandboxNodeOperatorIncreaseLimit]: + '0xbD37e55748c6f4Ece637AeD3e278e7575346B587', + [MotionType.SandboxStablesAdd]: + '0xB238fB1e7c8da5da022140dA956Fc3052808fC56', + [MotionType.SandboxStablesRemove]: + '0x51c730af05777c4D3CcC8c8B80558F4D155bb7BF', + [MotionType.SandboxStablesTopUp]: + '0x71bcEf1f4E4945005e1D22d68F02085D5167ab43', + + // next motion factories are @deprecated + // we are keeping them here to display history data + [MotionType.LEGOTopUp]: '', + [MotionType.GasFunderETHTopUp]: '0x', + [MotionType.RewardProgramAdd]: '', + [MotionType.RewardProgramRemove]: '', + [MotionType.RewardProgramTopUp]: '', + [MotionType.ReferralPartnerAdd]: '', + [MotionType.ReferralPartnerRemove]: '', + [MotionType.ReferralPartnerTopUp]: '', + [MotionType.AllowedRecipientAdd]: '', + [MotionType.AllowedRecipientRemove]: '', + [MotionType.AllowedRecipientTopUp]: '', + [MotionType.AllowedRecipientAddReferralDai]: '', + [MotionType.AllowedRecipientRemoveReferralDai]: '', + [MotionType.AllowedRecipientTopUpReferralDai]: '', + }, } export const parseEvmSupportedChainId = ( @@ -140,16 +239,6 @@ export const parseEvmSupportedChainId = ( return numChainId } -export const EvmSupportedChains = Object.keys(EvmAddressesByChain) - .map(v => Number(v)) - .filter(v => Number.isFinite(v)) as EvmSupportedChain[] - -export type EvmSupportedChain = keyof EvmAddresses - -// intentionally -// eslint-disable-next-line @typescript-eslint/no-redeclare -export type EvmAddresses = typeof EvmAddressesByChain - export const EvmUnrecognized = 'EvmUnrecognized' // intentionally // eslint-disable-next-line @typescript-eslint/no-redeclare @@ -169,13 +258,17 @@ export const EvmTypesByAdress = mapValues( export const EvmAddressesByType = Object.values(MotionType).reduce( (res, motionType) => ({ ...res, - [motionType]: EvmSupportedChains.reduce( - (resIn, chainId) => ({ + [motionType]: EvmSupportedChains.reduce((resIn, chainId) => { + const address = EvmAddressesByChain[chainId][motionType] + if (!address) { + return resIn + } + + return { ...resIn, - [chainId]: EvmAddressesByChain[chainId][motionType], - }), - {} as { [C in EvmSupportedChain]: EvmAddresses[C][typeof motionType] }, - ), + [chainId]: address, + } + }, {} as { [C in EvmSupportedChain]: EvmAddresses[C][typeof motionType] }), }), {} as { [M in MotionType]: { [C in EvmSupportedChain]: EvmAddresses[C][M] } diff --git a/modules/motions/hooks/index.ts b/modules/motions/hooks/index.ts index 113aab04..866803bf 100644 --- a/modules/motions/hooks/index.ts +++ b/modules/motions/hooks/index.ts @@ -12,3 +12,4 @@ export * from './useRewardPrograms' export * from './useAvailableMotions' export * from './useRegistryWithLimits' export * from './useTokenByTopUpType' +export * from './useRegistrySDVT' diff --git a/modules/motions/hooks/useAllowedTokensRegistry.ts b/modules/motions/hooks/useAllowedTokensRegistry.ts new file mode 100644 index 00000000..0c980da3 --- /dev/null +++ b/modules/motions/hooks/useAllowedTokensRegistry.ts @@ -0,0 +1,59 @@ +import { + ContractAllowedTokensRegistry, + ContractSandboxStablesAllowedTokensRegistry, +} from 'modules/blockChain/contracts' +import { useSWR } from 'modules/network/hooks/useSwr' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' +import { connectERC20Contract } from '../utils/connectTokenContract' +import { MotionType } from '../types' + +const TOKENS_REGISTRY_BY_MOTION_TYPE = { + [MotionType.AtcStablesTopUp]: ContractAllowedTokensRegistry, + [MotionType.PmlStablesTopUp]: ContractAllowedTokensRegistry, + [MotionType.RccStablesTopUp]: ContractAllowedTokensRegistry, + [MotionType.SandboxStablesTopUp]: ContractSandboxStablesAllowedTokensRegistry, +} + +type RegistryType = keyof typeof TOKENS_REGISTRY_BY_MOTION_TYPE + +export function useAllowedTokens(registryType: RegistryType) { + const { chainId, library } = useWeb3() + const registry = TOKENS_REGISTRY_BY_MOTION_TYPE[registryType].useRpc() + + const { data, initialLoading } = useSWR( + `allowed-tokens-${registryType}-${chainId}`, + async () => { + if (!library) { + return + } + + const tokensAddresses = await registry.getAllowedTokens() + + const allowedTokens = await Promise.all( + tokensAddresses.map(async tokenAddress => { + const tokenContract = connectERC20Contract(tokenAddress, chainId) + + const label = await tokenContract.symbol() + const decimals = await tokenContract.decimals() + + return { address: tokenAddress, label, decimals } + }), + ) + + const tokensDecimalsMap: Record = {} + + for (const token of allowedTokens) { + tokensDecimalsMap[token.address] = token.decimals + } + + return { allowedTokens, tokensDecimalsMap } + }, + { revalidateOnFocus: false, revalidateOnReconnect: false }, + ) + + return { + allowedTokens: data?.allowedTokens, + tokensDecimalsMap: data?.tokensDecimalsMap, + initialLoading, + } +} diff --git a/modules/motions/hooks/useAvailableMotions.ts b/modules/motions/hooks/useAvailableMotions.ts index 328c8efd..56b7c2c2 100644 --- a/modules/motions/hooks/useAvailableMotions.ts +++ b/modules/motions/hooks/useAvailableMotions.ts @@ -20,36 +20,52 @@ const isHasTrustedCaller = ( return false } +type NodeOperatorsList = ReturnType['data'] + +const getIsNodeOperatorConnected = ( + walletAddress: string | null | undefined, + nodeOperatorsList: NodeOperatorsList, +) => { + if (!walletAddress || !nodeOperatorsList) return false + const isWalletInList = nodeOperatorsList.some( + o => utils.getAddress(o.rewardAddress) === utils.getAddress(walletAddress), + ) + + return isWalletInList +} + export const useAvailableMotions = () => { const { chainId, walletAddress } = useWeb3() const [availableMotions, setAvailableMotions] = useState>() - const nodeOperators = useNodeOperatorsList() - const currentNodeOperator = useMemo(() => { - if (!walletAddress) return [undefined, null] - const idx = nodeOperators.data?.list.findIndex( - o => - utils.getAddress(o.rewardAddress) === utils.getAddress(walletAddress), - ) - const operator = idx !== undefined && nodeOperators.data?.list[idx] - return operator - }, [walletAddress, nodeOperators]) - const isNodeOperatorConnected = - (nodeOperators.data && !nodeOperators.data.isRegistrySupported) || - Boolean(currentNodeOperator) + const nodeOperators = useNodeOperatorsList('curated') + + const sandboxNodeOperators = useNodeOperatorsList('sandbox') const nodeOperatorIncreaseLimitAddressMap = EvmAddressesByType[MotionTypeForms.NodeOperatorIncreaseLimit] const nodeOperatorIncreaseLimitAddress = nodeOperatorIncreaseLimitAddressMap[parseEvmSupportedChainId(chainId)] + const sandboxNodeOperatorIncreaseLimitAddressMap = + EvmAddressesByType[MotionTypeForms.SandboxNodeOperatorIncreaseLimit] + const sandboxNodeOperatorIncreaseLimitAddress = + sandboxNodeOperatorIncreaseLimitAddressMap[ + parseEvmSupportedChainId(chainId) + ] const contracts = useMemo(() => { return Object.values(EVM_CONTRACTS).filter( contract => - contract.address[chainId] !== nodeOperatorIncreaseLimitAddress, + contract.address[chainId] && + contract.address[chainId] !== nodeOperatorIncreaseLimitAddress && + contract.address[chainId] !== sandboxNodeOperatorIncreaseLimitAddress, ) - }, [chainId, nodeOperatorIncreaseLimitAddress]) + }, [ + chainId, + nodeOperatorIncreaseLimitAddress, + sandboxNodeOperatorIncreaseLimitAddress, + ]) const getTrustedConnectionInfo = useCallback(async () => { const promiseResult = await Promise.allSettled( @@ -62,6 +78,15 @@ export const useAvailableMotions = () => { }), ) + const isNodeOperatorConnected = getIsNodeOperatorConnected( + walletAddress, + nodeOperators.data, + ) + const isSandboxNodeOperatorConnected = getIsNodeOperatorConnected( + walletAddress, + sandboxNodeOperators.data, + ) + const trustedCallerConnectedMap = promiseResult.reduce( (acc, cur, index) => { if (cur.status !== 'fulfilled') return acc @@ -71,7 +96,11 @@ export const useAvailableMotions = () => { const contractType = EvmTypesByAdress[parseEvmSupportedChainId(chainId)][contractAddress] - if (!Object.keys(MotionTypeForms).includes(contractType)) return acc + if ( + !contractType || + !Object.keys(MotionTypeForms).includes(contractType) + ) + return acc acc[contractType as MotionTypeForms] = cur.value === walletAddress @@ -79,11 +108,18 @@ export const useAvailableMotions = () => { }, { [MotionTypeForms.NodeOperatorIncreaseLimit]: isNodeOperatorConnected, + [MotionTypeForms.SandboxNodeOperatorIncreaseLimit]: + isSandboxNodeOperatorConnected, } as Record, ) - setAvailableMotions(trustedCallerConnectedMap) - }, [chainId, contracts, isNodeOperatorConnected, walletAddress]) + }, [ + contracts, + walletAddress, + nodeOperators.data, + sandboxNodeOperators.data, + chainId, + ]) useEffect(() => { getTrustedConnectionInfo() diff --git a/modules/motions/hooks/useContractEvmScript.ts b/modules/motions/hooks/useContractEvmScript.ts index 29fdc112..3e0be5b9 100644 --- a/modules/motions/hooks/useContractEvmScript.ts +++ b/modules/motions/hooks/useContractEvmScript.ts @@ -49,6 +49,29 @@ export const EVM_CONTRACTS = { CONTRACTS.ContractRewardsShareProgramRemove, [MotionType.RewardsShareProgramTopUp]: CONTRACTS.ContractRewardsShareProgramTopUp, + [MotionType.SDVTNodeOperatorsAdd]: CONTRACTS.ContractSDVTNodeOperatorsAdd, + [MotionType.SDVTNodeOperatorsActivate]: + CONTRACTS.ContractSDVTNodeOperatorsActivate, + [MotionType.SDVTNodeOperatorsDeactivate]: + CONTRACTS.ContractSDVTNodeOperatorsDeactivate, + [MotionType.SDVTVettedValidatorsLimitsSet]: + CONTRACTS.ContractSDVTVettedValidatorsLimitsSet, + [MotionType.SDVTTargetValidatorLimitsUpdate]: + CONTRACTS.ContractSDVTTargetValidatorLimitsUpdate, + [MotionType.SDVTNodeOperatorRewardAddressesSet]: + CONTRACTS.ContractSDVTNodeOperatorRewardAddressesSet, + [MotionType.SDVTNodeOperatorNamesSet]: + CONTRACTS.ContractSDVTNodeOperatorNamesSet, + [MotionType.SDVTNodeOperatorManagerChange]: + CONTRACTS.ContractSDVTNodeOperatorManagerChange, + [MotionType.SandboxNodeOperatorIncreaseLimit]: + CONTRACTS.ContractEvmSandboxNodeOperatorIncreaseLimit, + [MotionType.RccStablesTopUp]: CONTRACTS.ContractEvmRccStablesTopUp, + [MotionType.PmlStablesTopUp]: CONTRACTS.ContractEvmPmlStablesTopUp, + [MotionType.AtcStablesTopUp]: CONTRACTS.ContractEvmAtcStablesTopUp, + [MotionType.SandboxStablesAdd]: CONTRACTS.ContractEvmSandboxStablesAdd, + [MotionType.SandboxStablesTopUp]: CONTRACTS.ContractEvmSandboxStablesTopUp, + [MotionType.SandboxStablesRemove]: CONTRACTS.ContractEvmSandboxStablesRemove, } as const export function useContractEvmScript( diff --git a/modules/motions/hooks/useEVMScriptDecoder.ts b/modules/motions/hooks/useEVMScriptDecoder.ts index fc3d9295..e6bf2de6 100644 --- a/modules/motions/hooks/useEVMScriptDecoder.ts +++ b/modules/motions/hooks/useEVMScriptDecoder.ts @@ -35,15 +35,18 @@ export function useEVMScriptDecoder() { abis.AllowedRecipientsRegistryAbi__factory.abi, [KEYS.LegoLDORegistry]: abis.RegistryWithLimitsAbi__factory.abi, [KEYS.LegoDAIRegistry]: abis.RegistryWithLimitsAbi__factory.abi, - [KEYS.RccDAIRegistry]: abis.RegistryWithLimitsAbi__factory.abi, - [KEYS.PmlDAIRegistry]: abis.RegistryWithLimitsAbi__factory.abi, - [KEYS.AtcDAIRegistry]: abis.RegistryWithLimitsAbi__factory.abi, + [KEYS.RccStablesRegistry]: abis.RegistryWithLimitsAbi__factory.abi, + [KEYS.PmlStablesRegistry]: abis.RegistryWithLimitsAbi__factory.abi, + [KEYS.AtcStablesRegistry]: abis.RegistryWithLimitsAbi__factory.abi, [KEYS.gasFunderETHRegistry]: abis.RegistryWithLimitsAbi__factory.abi, [KEYS.StethRewardProgramRegistry]: abis.RegistryWithLimitsAbi__factory.abi, [KEYS.StethGasSupplyRegistry]: abis.RegistryWithLimitsAbi__factory.abi, [KEYS.RewardsShareProgramRegistry]: abis.RegistryWithLimitsAbi__factory.abi, + [KEYS.AragonACL]: abis.AragonACLAbi__factory.abi, + [KEYS.SDVTRegistry]: abis.SDVTRegistryAbi__factory.abi, + [KEYS.SandboxNodeOperatorsRegistry]: abis.NodeOperatorsAbi__factory.abi, }), ) }, `evm-script-decoder-${chainId}`) diff --git a/modules/motions/hooks/useMotionTokenData.ts b/modules/motions/hooks/useMotionTokenData.ts new file mode 100644 index 00000000..2fe4e642 --- /dev/null +++ b/modules/motions/hooks/useMotionTokenData.ts @@ -0,0 +1,32 @@ +import { DEFAULT_DECIMALS } from 'modules/blockChain/constants' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' +import { useSWR } from 'modules/network/hooks/useSwr' +import { connectERC20Contract } from '../utils/connectTokenContract' + +export function useMotionTokenData(tokenAddress: string | null | undefined) { + const { chainId } = useWeb3() + const { data: tokenData, initialLoading: isTokenDataLoading } = useSWR( + tokenAddress?.length ? `token-data-${tokenAddress}-${chainId}` : null, + async () => { + if (!tokenAddress?.length) return null + try { + const tokenContract = connectERC20Contract(tokenAddress, chainId) + const label = await tokenContract.symbol() + const decimals = await tokenContract.decimals() + + return { + label, + address: tokenAddress, + decimals, + } + } catch (error) { + return { + label: 'Unknown token', + address: tokenAddress, + decimals: DEFAULT_DECIMALS, + } + } + }, + ) + return { tokenData, isTokenDataLoading } +} diff --git a/modules/motions/hooks/useNodeOperatorKeysInfo.ts b/modules/motions/hooks/useNodeOperatorKeysInfo.ts index 3f4b6e06..c76c1f15 100644 --- a/modules/motions/hooks/useNodeOperatorKeysInfo.ts +++ b/modules/motions/hooks/useNodeOperatorKeysInfo.ts @@ -2,12 +2,22 @@ import { useSWR } from 'modules/network/hooks/useSwr' import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' import { nodeOperatorsKeysInfo } from 'modules/network/utils/urlsApi' import { fetcherStandard } from 'modules/network/utils/fetcherStandard' -import type { KeysInfo } from '../types' +import { KeysInfo } from '../types' +import { + NodeOperatorsRegistryType, + NODE_OPERATORS_REGISTRY_MAP, +} from '../constants' -export function useNodeOperatorKeysInfo() { - const { chainId } = useWeb3() +export function useNodeOperatorKeysInfo( + registryType: NodeOperatorsRegistryType, +) { + const { chainId, walletAddress } = useWeb3() const nodeOperatorsInfo = useSWR( - nodeOperatorsKeysInfo(chainId), + nodeOperatorsKeysInfo( + chainId, + `${walletAddress}`, + `${NODE_OPERATORS_REGISTRY_MAP[registryType].address[chainId]}`, + ), fetcherStandard, ) return nodeOperatorsInfo diff --git a/modules/motions/hooks/useNodeOperatorsList.ts b/modules/motions/hooks/useNodeOperatorsList.ts index e4d9a18a..2d007fbd 100644 --- a/modules/motions/hooks/useNodeOperatorsList.ts +++ b/modules/motions/hooks/useNodeOperatorsList.ts @@ -1,36 +1,34 @@ -import { CHAINS } from '@lido-sdk/constants' import { useSWR } from 'modules/network/hooks/useSwr' import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' -import { ContractNodeOperatorsRegistry } from 'modules/blockChain/contracts' -import type { UnpackedPromise } from '@lido-sdk/react/dist/esm/hooks/types' +import { + NodeOperatorsRegistryType, + NODE_OPERATORS_REGISTRY_MAP, +} from '../constants' -export function useNodeOperatorsList() { +export function useNodeOperatorsList(registryType: NodeOperatorsRegistryType) { const { chainId, account } = useWeb3() - const registry = ContractNodeOperatorsRegistry.useRpc() const nodeOperatorsList = useSWR( - `${registry.address}-${chainId}-${account}-operators-list`, + `${chainId}-${account}-${registryType}-operators-list`, async () => { - const isRegistrySupported = chainId !== CHAINS.Rinkeby - if (!isRegistrySupported) { - return { - list: [] as UnpackedPromise< - ReturnType - >[], - isRegistrySupported, - } - } - const count = (await registry.getNodeOperatorsCount()).toNumber() - const nodeOperators = await Promise.all( - Array.from(Array(count)).map((_, i) => - registry.getNodeOperator(i, true), - ), - ) - return { - list: nodeOperators, - isRegistrySupported, + try { + const registry = NODE_OPERATORS_REGISTRY_MAP[registryType].connectRpc({ + chainId, + }) + + const count = (await registry.getNodeOperatorsCount()).toNumber() + const nodeOperators = await Promise.all( + Array.from(Array(count)).map(async (_, i) => { + const nodeOperator = await registry.getNodeOperator(i, true) + return { ...nodeOperator, id: i } + }), + ) + return nodeOperators + } catch (error) { + return [] } }, + { revalidateOnFocus: false, revalidateOnReconnect: false }, ) return nodeOperatorsList diff --git a/modules/motions/hooks/usePeriodLimitsInfo.ts b/modules/motions/hooks/usePeriodLimitsInfo.ts index 3861f2f0..f1f0f268 100644 --- a/modules/motions/hooks/usePeriodLimitsInfo.ts +++ b/modules/motions/hooks/usePeriodLimitsInfo.ts @@ -7,10 +7,10 @@ import { ContractAllowedRecipientReferralDaiRegistry, ContractAllowedRecipientTrpLdoRegistry, ContractLegoLDORegistry, - ContractRccDAIRegistry, - ContractAtcDAIRegistry, + ContractRccStablesRegistry, + ContractAtcStablesRegistry, ContractGasFunderETHRegistry, - ContractPmlDAIRegistry, + ContractPmlStablesRegistry, ContractStethRewardProgramRegistry, } from 'modules/blockChain/contracts' import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' @@ -103,9 +103,9 @@ const registryByMotionType: { } = { [MotionType.LegoLDOTopUp]: ContractLegoLDORegistry, [MotionType.LegoDAITopUp]: ContractLegoDAIRegistry, - [MotionType.RccDAITopUp]: ContractRccDAIRegistry, - [MotionType.PmlDAITopUp]: ContractPmlDAIRegistry, - [MotionType.AtcDAITopUp]: ContractAtcDAIRegistry, + [MotionType.RccStablesTopUp]: ContractRccStablesRegistry, + [MotionType.PmlStablesTopUp]: ContractPmlStablesRegistry, + [MotionType.AtcStablesTopUp]: ContractAtcStablesRegistry, [MotionType.GasFunderETHTopUp]: ContractGasFunderETHRegistry, [MotionType.AllowedRecipientTopUp]: ContractAllowedRecipientRegistry, [MotionType.AllowedRecipientTopUpReferralDai]: diff --git a/modules/motions/hooks/useRegistrySDVT.ts b/modules/motions/hooks/useRegistrySDVT.ts new file mode 100644 index 00000000..ed26dae9 --- /dev/null +++ b/modules/motions/hooks/useRegistrySDVT.ts @@ -0,0 +1,29 @@ +import { useSWR } from 'modules/network/hooks/useSwr' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' +import { ContractSDVTRegistry } from 'modules/blockChain/contracts' + +export function useSDVTOperatorsCounts() { + const { chainId } = useWeb3() + const registry = ContractSDVTRegistry.useRpc() + + return useSWR( + `sdvt-operators-counts-${chainId}-${registry.address}`, + async () => { + const [current, max] = await Promise.all([ + registry.getNodeOperatorsCount(), + registry.MAX_NODE_OPERATORS_COUNT(), + ]) + return { current: current.toNumber(), max: max.toNumber() } + }, + ) +} + +export function useSDVTOperatorNameLimit() { + const { chainId } = useWeb3() + const registry = ContractSDVTRegistry.useRpc() + + return useSWR( + `sdvt-operator-name-length-${chainId}-${registry.address}`, + async () => registry.MAX_NODE_OPERATOR_NAME_LENGTH(), + ) +} diff --git a/modules/motions/hooks/useRegistryWithLimits.ts b/modules/motions/hooks/useRegistryWithLimits.ts index 982dc973..6ca38523 100644 --- a/modules/motions/hooks/useRegistryWithLimits.ts +++ b/modules/motions/hooks/useRegistryWithLimits.ts @@ -4,9 +4,8 @@ import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' import { ContractLegoLDORegistry, ContractLegoDAIRegistry, - ContractRccDAIRegistry, - ContractPmlDAIRegistry, - ContractAtcDAIRegistry, + ContractPmlStablesRegistry, + ContractAtcStablesRegistry, ContractGasFunderETHRegistry, ContractAllowedRecipientRegistry, ContractAllowedRecipientReferralDaiRegistry, @@ -14,6 +13,8 @@ import { ContractStethRewardProgramRegistry, ContractStethGasSupplyRegistry, ContractRewardsShareProgramRegistry, + ContractRccStablesRegistry, + ContractSandboxStablesAllowedRecipientRegistry, } from 'modules/blockChain/contracts' import { getEventsRecipientAdded } from 'modules/motions/utils' import { MotionType } from 'modules/motions/types' @@ -28,9 +29,9 @@ type AllowedRecipient = { export const REGISTRY_WITH_LIMITS_BY_MOTION_TYPE = { [MotionType.LegoLDOTopUp]: ContractLegoLDORegistry, [MotionType.LegoDAITopUp]: ContractLegoDAIRegistry, - [MotionType.RccDAITopUp]: ContractRccDAIRegistry, - [MotionType.PmlDAITopUp]: ContractPmlDAIRegistry, - [MotionType.AtcDAITopUp]: ContractAtcDAIRegistry, + [MotionType.RccDAITopUp]: ContractRccStablesRegistry, + [MotionType.PmlDAITopUp]: ContractPmlStablesRegistry, + [MotionType.AtcDAITopUp]: ContractAtcStablesRegistry, [MotionType.GasFunderETHTopUp]: ContractGasFunderETHRegistry, [MotionType.AllowedRecipientTopUp]: ContractAllowedRecipientRegistry, [MotionType.AllowedRecipientRemove]: ContractAllowedRecipientRegistry, @@ -52,6 +53,15 @@ export const REGISTRY_WITH_LIMITS_BY_MOTION_TYPE = { [MotionType.RewardsShareProgramAdd]: ContractRewardsShareProgramRegistry, [MotionType.RewardsShareProgramRemove]: ContractRewardsShareProgramRegistry, [MotionType.RewardsShareProgramTopUp]: ContractRewardsShareProgramRegistry, + [MotionType.RccStablesTopUp]: ContractRccStablesRegistry, + [MotionType.PmlStablesTopUp]: ContractPmlStablesRegistry, + [MotionType.AtcStablesTopUp]: ContractAtcStablesRegistry, + [MotionType.SandboxStablesAdd]: + ContractSandboxStablesAllowedRecipientRegistry, + [MotionType.SandboxStablesRemove]: + ContractSandboxStablesAllowedRecipientRegistry, + [MotionType.SandboxStablesTopUp]: + ContractSandboxStablesAllowedRecipientRegistry, } as const type HookArgs = { @@ -94,7 +104,7 @@ export function useRecipientAll({ registryType }: HookArgs) { } export function useRecipientActual({ registryType }: HookArgs) { - const chainId = useWeb3() + const { chainId } = useWeb3() const recipientsAll = useRecipientAll({ registryType }) const registry = REGISTRY_WITH_LIMITS_BY_MOTION_TYPE[registryType].useRpc() diff --git a/modules/motions/hooks/useRewardPrograms.ts b/modules/motions/hooks/useRewardPrograms.ts index 532cd293..6303a4ba 100644 --- a/modules/motions/hooks/useRewardPrograms.ts +++ b/modules/motions/hooks/useRewardPrograms.ts @@ -57,7 +57,7 @@ export function useRewardProgramsAll() { * @deprecated */ export function useRewardProgramsActual() { - const chainId = useWeb3() + const { chainId } = useWeb3() const programsAll = useRewardProgramsAll() const rewardProgramRegistry = ContractRewardProgramRegistry.useRpc() diff --git a/modules/motions/hooks/useSDVTNodeOperatorsList.ts b/modules/motions/hooks/useSDVTNodeOperatorsList.ts new file mode 100644 index 00000000..5bc7866c --- /dev/null +++ b/modules/motions/hooks/useSDVTNodeOperatorsList.ts @@ -0,0 +1,67 @@ +import { BigNumber, utils } from 'ethers' +import { useSWR } from 'modules/network/hooks/useSwr' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' +import { ContractSDVTRegistry } from 'modules/blockChain/contracts' +import { getManagerAddressesMap } from '../utils/getManagerAddressesMap' +import { UnpackedPromise } from 'modules/shared/utils/utilTypes' +import { SDVTRegistryAbi } from 'generated' + +type NodeOperatorSummary = UnpackedPromise< + ReturnType +> + +type Args = { + withSummary?: boolean +} + +export function useSDVTNodeOperatorsList(args?: Args) { + const { chainId, account } = useWeb3() + const registry = ContractSDVTRegistry.useRpc() + + return useSWR( + `${registry.address}-${chainId}-${account}-operators-list${ + args?.withSummary ? '-with-summary' : '' + }`, + async () => { + const count = (await registry.getNodeOperatorsCount()).toNumber() + const MANAGE_SIGNING_KEYS_ROLE = await registry.MANAGE_SIGNING_KEYS() + + const managerAddressesMap = await getManagerAddressesMap( + registry.address, + MANAGE_SIGNING_KEYS_ROLE, + chainId, + ) + + const nodeOperators = await Promise.all( + Array.from(Array(count)).map(async (_, i) => { + const nodeOperatorInfo = await registry.getNodeOperator(i, true) + let nodeOperatorSummary: NodeOperatorSummary | undefined + + if (args?.withSummary) { + nodeOperatorSummary = await registry.getNodeOperatorSummary(i) + } + + const rawPermissionParam = BigNumber.from(1).shl(240).add(i) + const permissionParam = utils.solidityKeccak256( + ['uint256'], + [rawPermissionParam], + ) + + const managerAddress = managerAddressesMap[permissionParam] + + return { + ...nodeOperatorInfo, + id: i, + managerAddress, + ...nodeOperatorSummary, + } + }), + ) + return nodeOperators + }, + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + }, + ) +} diff --git a/modules/motions/hooks/useTransitionLimits.ts b/modules/motions/hooks/useTransitionLimits.ts index 8c7131d4..28b2ef98 100644 --- a/modules/motions/hooks/useTransitionLimits.ts +++ b/modules/motions/hooks/useTransitionLimits.ts @@ -7,13 +7,42 @@ import { ContractEVMScriptExecutor, } from 'modules/blockChain/contracts' -import { BigNumber, utils } from 'ethers' +import { utils, constants, BigNumber } from 'ethers' import { Big } from 'modules/shared/utils/bigNumber' +import { connectERC20Contract } from '../utils/connectTokenContract' +import { DEFAULT_DECIMALS } from 'modules/blockChain/constants' + +// Data structure reference +// https://github.com/lidofinance/scripts/blob/2a30b9654abc90b20debf837f99cd02f248d6644/scripts/setup_easytrack_limits.py#L67-L100 +const LDO_INDEX = 1 +const ETH_INDEX = 4 +const DAI_INDEX = 7 +const STETH_INDEX = 10 +const USDC_INDEX = 13 +const USDT_INDEX = 16 + +const TOKEN_INDEXES = [ + LDO_INDEX, + ETH_INDEX, + DAI_INDEX, + STETH_INDEX, + USDC_INDEX, + USDT_INDEX, +] + +const decodeLimit = (val: BigNumber, decimals: number | null) => { + if (!decimals) { + return null + } + return new Big(Number(val)).div(10 ** decimals).toNumber() +} + +type LimitsMap = Record export const useTransitionLimits = () => { const { chainId } = useWeb3() - const result = useSWR(`permission-param-${chainId}`, async () => { + const result = useSWR(`permission-param-${chainId}`, async () => { const contractFinance = ContractFinance.connectRpc({ chainId }) const contractAragonAcl = ContractAragonAcl.connectRpc({ chainId }) const evmScriptExecutorAddress = ContractEVMScriptExecutor.address[chainId]! @@ -39,20 +68,31 @@ export const useTransitionLimits = () => { const params = await Promise.all(paramRequests) - const decodeLimit = (val: BigNumber) => - new Big(Number(val)).div(10 ** 18).toNumber() - - // Data structure reference - // https://github.com/lidofinance/scripts/blob/2a30b9654abc90b20debf837f99cd02f248d6644/scripts/setup_easytrack_limits.py#L67-L100 - const LDO = utils.getAddress(params[1][2].toHexString()) - const ETH = '0x0000000000000000000000000000000000000000' // literal definition because params[4][2].toHexString() === '0x00 - const DAI = utils.getAddress(params[7][2].toHexString()) - const STETH = utils.getAddress(params[10][2].toHexString()) - const limits = { - [LDO]: decodeLimit(params[2][2]), - [ETH]: decodeLimit(params[5][2]), - [DAI]: decodeLimit(params[8][2]), - [STETH]: decodeLimit(params[11][2]), + const limits: LimitsMap = {} + + for (const index of TOKEN_INDEXES) { + const rawAddress: string | undefined = + // literal definition because params[4][2].toHexString() === '0x00 + index === ETH_INDEX + ? constants.AddressZero + : params[index]?.[2].toHexString() + const address = rawAddress ? utils.getAddress(rawAddress) : null + + if (address) { + let decimals: number | null = null + if (address === constants.AddressZero) { + decimals = DEFAULT_DECIMALS + } else { + const tokenContract = connectERC20Contract(address, chainId) + try { + decimals = await tokenContract.decimals() + } catch (error) { + decimals = null + } + } + + limits[address] = decodeLimit(params[index + 1][2], decimals) + } } return limits diff --git a/modules/motions/providers/motionDetailed.tsx b/modules/motions/providers/motionDetailed.tsx index 9daab59a..94fcd21a 100644 --- a/modules/motions/providers/motionDetailed.tsx +++ b/modules/motions/providers/motionDetailed.tsx @@ -1,5 +1,5 @@ import { FC, createContext, useMemo, useCallback } from 'react' -import { formatEther } from 'ethers/lib/utils' +import { formatEther, formatUnits } from 'ethers/lib/utils' import invariant from 'tiny-invariant' import { useSWR } from 'modules/network/hooks/useSwr' @@ -23,6 +23,29 @@ import { useTransactionSender } from 'modules/blockChain/hooks/useTransactionSen import { EvmUnrecognized } from 'modules/motions/evmAddresses' import { Motion, MotionStatus } from 'modules/motions/types' import { ContractEasyTrack } from 'modules/blockChain/contracts' +import { useMotionTokenData } from '../hooks/useMotionTokenData' +import { BigNumber } from 'ethers' +import { DEFAULT_DECIMALS } from 'modules/blockChain/constants' + +const getTopUpAmount = (callData: any, tokenDecimals = DEFAULT_DECIMALS) => { + if (!callData) { + return 0 + } + + if (callData[1]?.[0]?._isBigNumber) { + return Number(formatEther(callData[1][0])) || 0 + } + + if (Array.isArray(callData.amounts)) { + const amountsSum = (callData.amounts as BigNumber[]).reduce((acc, amount) => + acc.add(amount), + ) + + return Number(formatUnits(amountsSum, tokenDecimals)) + } + + return 0 +} export type MotionDetailedValue = { isArchived: boolean @@ -87,13 +110,19 @@ export const MotionDetailedProvider: FC = props => { }, ) + const { tokenData } = useMotionTokenData( + callData?.token ?? topUpToken.address, + ) + const isArchived = motion.status !== MotionStatus.ACTIVE && motion.status !== MotionStatus.PENDING - const motionTopUpAmount: number = - (callData?.[1]?.[0]?._isBigNumber && Number(formatEther(callData[1][0]))) || - 0 // TODO: refactor - const motionTopUpToken = topUpToken.label || '' + + const motionTopUpAmount = getTopUpAmount( + callData, + tokenData?.decimals ?? DEFAULT_DECIMALS, + ) + const motionTopUpToken = tokenData?.label ?? '' const pending = isCallDataLoading || isEventLoading || isPeriodLimitsDataLoading diff --git a/modules/motions/types.ts b/modules/motions/types.ts index d2020fb7..158c4646 100644 --- a/modules/motions/types.ts +++ b/modules/motions/types.ts @@ -4,13 +4,15 @@ import type { EasyTrackAbi } from 'generated' // Only motions currently supported to start export const MotionTypeForms = { + // old node operator NodeOperatorIncreaseLimit: 'NodeOperatorIncreaseLimit', + // ET AllowedRecipientTopUpTrpLdo: 'AllowedRecipientTopUpTrpLdo', LegoLDOTopUp: 'LegoLDOTopUp', LegoDAITopUp: 'LegoDAITopUp', - RccDAITopUp: 'RccDAITopUp', - PmlDAITopUp: 'PmlDAITopUp', - AtcDAITopUp: 'AtcDAITopUp', + RccStablesTopUp: 'RccStablesTopUp', + PmlStablesTopUp: 'PmlStablesTopUp', + AtcStablesTopUp: 'AtcStablesTopUp', StethRewardProgramAdd: 'StethRewardProgramAdd', StethRewardProgramRemove: 'StethRewardProgramRemove', StethRewardProgramTopUp: 'StethRewardProgramTopUp', @@ -20,6 +22,24 @@ export const MotionTypeForms = { RewardsShareProgramAdd: 'RewardsShareProgramAdd', RewardsShareProgramRemove: 'RewardsShareProgramRemove', RewardsShareProgramTopUp: 'RewardsShareProgramTopUp', + // ET DVT + SDVTNodeOperatorsAdd: 'SDVTNodeOperatorsAdd', + SDVTNodeOperatorsActivate: 'SDVTNodeOperatorsActivate', + SDVTNodeOperatorsDeactivate: 'SDVTNodeOperatorsDeactivate', + SDVTVettedValidatorsLimitsSet: 'SDVTVettedValidatorsLimitsSet', + SDVTTargetValidatorLimitsUpdate: 'SDVTTargetValidatorLimitsUpdate', + SDVTNodeOperatorRewardAddressesSet: 'SDVTNodeOperatorRewardAddressesSet', + SDVTNodeOperatorNamesSet: 'SDVTNodeOperatorNamesSet', + SDVTNodeOperatorManagerChange: 'SDVTNodeOperatorManagerChange', + + SandboxNodeOperatorIncreaseLimit: 'SandboxNodeOperatorIncreaseLimit', + + SandboxStablesTopUp: 'SandboxStablesTopUp', + SandboxStablesAdd: 'SandboxStablesAdd', + SandboxStablesRemove: 'SandboxStablesRemove', + RccDAITopUp: 'RccDAITopUp', + PmlDAITopUp: 'PmlDAITopUp', + AtcDAITopUp: 'AtcDAITopUp', } as const // intentionally // eslint-disable-next-line @typescript-eslint/no-redeclare @@ -135,20 +155,5 @@ export type KeysInfoOperator = { } export type KeysInfo = { - operators: undefined | KeysInfoOperator[] - keys: { - signatureVerified: number - duplicatesVerified: number - total: number - unusedButNotNew: number - unused: number - used: number - unconfirmed: number - } - health: { - nextUpdateTime: number - lastUpdateTime: number - serverTime: number - status: string - } + operators?: KeysInfoOperator[] } diff --git a/modules/motions/ui/MotionCardPreview/MotionCardPreviewStyle.ts b/modules/motions/ui/MotionCardPreview/MotionCardPreviewStyle.ts index 1f56737b..5bba027c 100644 --- a/modules/motions/ui/MotionCardPreview/MotionCardPreviewStyle.ts +++ b/modules/motions/ui/MotionCardPreview/MotionCardPreviewStyle.ts @@ -18,6 +18,7 @@ export const CardTitle = styled(Text).attrs({ export const CardDescription = styled(Text).attrs({ size: 12, weight: 500, + truncateLines: 5, })` opacity: 0.6; margin-bottom: 16px; diff --git a/modules/motions/ui/MotionDescription/DescNodeOperator.tsx b/modules/motions/ui/MotionDescription/DescNodeOperator.tsx deleted file mode 100644 index c36609f0..00000000 --- a/modules/motions/ui/MotionDescription/DescNodeOperator.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useNodeOperatorsList } from 'modules/motions/hooks/useNodeOperatorsList' -import { EvmIncreaseNodeOperatorStakingLimitAbi } from 'generated' -import { NestProps } from './types' - -// NodeOperatorIncreaseLimit -export function DescNodeOperatorIncreaseLimit({ - callData, -}: NestProps< - EvmIncreaseNodeOperatorStakingLimitAbi['decodeEVMScriptCallData'] ->) { - const nodeOperatorId = Number(callData._nodeOperatorId) - const nodeOperators = useNodeOperatorsList() - const nodeOperator = nodeOperators.data?.list[nodeOperatorId] - return ( -
- Node operator {nodeOperator ? nodeOperator.name : ''} (id:{' '} - {nodeOperatorId}) wants to increase staking limit to{' '} - {Number(callData._stakingLimit)} -
- ) -} diff --git a/modules/motions/ui/MotionDescription/DescNodeOperatorLimitIncrease.tsx b/modules/motions/ui/MotionDescription/DescNodeOperatorLimitIncrease.tsx new file mode 100644 index 00000000..1c234be2 --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescNodeOperatorLimitIncrease.tsx @@ -0,0 +1,27 @@ +import { useNodeOperatorsList } from 'modules/motions/hooks/useNodeOperatorsList' +import { EvmIncreaseNodeOperatorStakingLimitAbi } from 'generated' +import { NestProps } from './types' +import { IncreaseLimitMotionType } from 'modules/motions/constants' +import { getNodeOperatorRegistryType } from 'modules/motions/utils/getNodeOperatorRegistryType' + +// IncreaseNodeOperatorLimit +export function DescNodeOperatorIncreaseLimit({ + callData, + motionType, +}: NestProps< + EvmIncreaseNodeOperatorStakingLimitAbi['decodeEVMScriptCallData'] +> & { + motionType: IncreaseLimitMotionType +}) { + const nodeOperatorId = Number(callData._nodeOperatorId) + const registryType = getNodeOperatorRegistryType(motionType) + const { data: nodeOperators } = useNodeOperatorsList(registryType) + const nodeOperatorName = nodeOperators?.[nodeOperatorId].name ?? '' + + return ( +
+ Node operator {nodeOperatorName} (id: {nodeOperatorId}) wants to + increase staking limit to {Number(callData._stakingLimit)} +
+ ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorManagersChange.tsx b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorManagersChange.tsx new file mode 100644 index 00000000..ba5c9685 --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorManagersChange.tsx @@ -0,0 +1,29 @@ +import { ChangeNodeOperatorManagersAbi } from 'generated' +import { NestProps } from './types' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' + +// ChangeNodeOperatorManagers +export function DescSDVTNodeOperatorManagersChange({ + callData, +}: NestProps) { + const { data: nodeOperatorsList } = useSDVTNodeOperatorsList() + return ( + <> + {callData.map((item, index) => { + const nodeOperatorId = item.nodeOperatorId.toNumber() + const nodeOperator = nodeOperatorsList?.[nodeOperatorId] + return ( +
+ Update Node Operator {nodeOperator ? nodeOperator.name : ''}{' '} + (id: {nodeOperatorId}): revoke MANAGE_SIGNING_KEYS role from{' '} + , add{' '} + MANAGE_SIGNING_KEYS role to{' '} + + {index === callData.length - 1 ? '.' : '; '} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorNamesSet.tsx b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorNamesSet.tsx new file mode 100644 index 00000000..1443a338 --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorNamesSet.tsx @@ -0,0 +1,25 @@ +import { SetNodeOperatorNamesAbi } from 'generated' +import { NestProps } from './types' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' + +// SetNodeOperatorNames +export function DescSDVTNodeOperatorNamesSet({ + callData, +}: NestProps) { + const { data: nodeOperatorsList } = useSDVTNodeOperatorsList() + return ( + <> + {callData.map((item, index) => { + const nodeOperatorId = item.nodeOperatorId.toNumber() + const nodeOperator = nodeOperatorsList?.[nodeOperatorId] + return ( +
+ Change Node Operator {nodeOperator ? nodeOperator.name : ''}{' '} + (id: {nodeOperatorId}) name to {item.name} + {index === callData.length - 1 ? '.' : '; '} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorRewardAddressesSet.tsx b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorRewardAddressesSet.tsx new file mode 100644 index 00000000..310a801d --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorRewardAddressesSet.tsx @@ -0,0 +1,35 @@ +import { SetNodeOperatorRewardAddressesAbi } from 'generated' +import { NestProps } from './types' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' + +// SetNodeOperatorRewardAddresses +export function DescSDVTNodeOperatorRewardAddressesSet({ + callData, +}: NestProps) { + const { data: nodeOperatorsList } = useSDVTNodeOperatorsList() + return ( + <> + {callData.map((item, index) => { + const nodeOperatorId = item.nodeOperatorId.toNumber() + const nodeOperator = nodeOperatorsList?.[nodeOperatorId] + return ( +
+ Change reward address of Node Operator{' '} + {nodeOperator ? nodeOperator.name : ''} (id: {nodeOperatorId} + ) + {nodeOperator?.rewardAddress ? ( + <> + {' '} + from{' '} + + + ) : null}{' '} + to + {index === callData.length - 1 ? '.' : '; '} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsActivate.tsx b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsActivate.tsx new file mode 100644 index 00000000..a3c56aae --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsActivate.tsx @@ -0,0 +1,28 @@ +import { ActivateNodeOperatorsAbi } from 'generated' +import { NestProps } from './types' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' + +// ActivateNodeOperators +export function DescSDVTNodeOperatorsActivate({ + callData, +}: NestProps) { + const { data: nodeOperatorsList } = useSDVTNodeOperatorsList() + return ( + <> + {callData.map((item, index) => { + const nodeOperatorId = item.nodeOperatorId.toNumber() + const nodeOperator = nodeOperatorsList?.[nodeOperatorId] + return ( +
+ Activate Node Operator{' '} + {nodeOperator ? nodeOperator.name : ''} (id: {nodeOperatorId} + ) and add MANAGE_SIGNING_KEYS role to{' '} + + {index === callData.length - 1 ? '.' : '; '} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsAdd.tsx b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsAdd.tsx new file mode 100644 index 00000000..725caeb8 --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsAdd.tsx @@ -0,0 +1,26 @@ +import { AddNodeOperatorsAbi } from 'generated' +import { NestProps } from './types' +import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' + +// AddNodeOperators +export function DescSDVTNodeOperatorsAdd({ + callData, +}: NestProps) { + const { nodeOperators, nodeOperatorsCount } = callData + return ( + <> + {nodeOperators.map((nodeOperator, index) => { + return ( +
+ Add Node Operator {nodeOperator.name}(id:{' '} + {Number(nodeOperatorsCount) + index}){' '} + and + add MANAGE_SIGNING_KEYS role to{' '} + + {index === nodeOperators.length - 1 ? '.' : '; '} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsDeactivate.tsx b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsDeactivate.tsx new file mode 100644 index 00000000..42079957 --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTNodeOperatorsDeactivate.tsx @@ -0,0 +1,28 @@ +import { DeactivateNodeOperatorsAbi } from 'generated' +import { NestProps } from './types' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' + +// DeactivateNodeOperators +export function DescSDVTNodeOperatorsDeactivate({ + callData, +}: NestProps) { + const { data: nodeOperatorsList } = useSDVTNodeOperatorsList() + return ( + <> + {callData.map((item, index) => { + const nodeOperatorId = item.nodeOperatorId.toNumber() + const nodeOperator = nodeOperatorsList?.[nodeOperatorId] + return ( +
+ Deactivate Node Operator{' '} + {nodeOperator ? nodeOperator.name : ''} (id: {nodeOperatorId} + ) and revoke MANAGE_SIGNING_KEYS role from{' '} + + {index === callData.length - 1 ? '.' : '; '} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTTargetValidatorLimitsUpdate.tsx b/modules/motions/ui/MotionDescription/DescSDVTTargetValidatorLimitsUpdate.tsx new file mode 100644 index 00000000..3cc8a81a --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTTargetValidatorLimitsUpdate.tsx @@ -0,0 +1,40 @@ +import { UpdateTargetValidatorLimitsAbi } from 'generated' +import { NestProps } from './types' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' + +// UpdateTargetValidatorLimits +export function DescSDVTTargetValidatorLimitsUpdate({ + callData, +}: NestProps) { + const { data: nodeOperatorsList } = useSDVTNodeOperatorsList({ + withSummary: true, + }) + return ( + <> + {callData.map((item, index) => { + const nodeOperatorId = item.nodeOperatorId.toNumber() + const nodeOperator = nodeOperatorsList?.[nodeOperatorId] + + const nodeOperatorName = nodeOperator ? nodeOperator.name : '' + + if (!item.isTargetLimitActive) { + return ( +
+ Disable target validator limit for Node Operator{' '} + {nodeOperatorName} (id: {nodeOperatorId}) +
+ ) + } + + return ( +
+ Set target validator limit for Node Operator{' '} + {nodeOperatorName} (id: {nodeOperatorId}){' '} + {`from ${nodeOperator?.targetValidatorsCount} to ${item.targetLimit}`} + {index === callData.length - 1 ? '.' : '; '} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescSDVTVettedValidatorsLimitsSet.tsx b/modules/motions/ui/MotionDescription/DescSDVTVettedValidatorsLimitsSet.tsx new file mode 100644 index 00000000..8f37f433 --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescSDVTVettedValidatorsLimitsSet.tsx @@ -0,0 +1,27 @@ +import { SetVettedValidatorsLimitsAbi } from 'generated' +import { NestProps } from './types' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' + +// SetVettedValidatorsLimits +export function DescSDVTVettedValidatorsLimitsSet({ + callData, +}: NestProps) { + const { data: nodeOperatorsList } = useSDVTNodeOperatorsList({ + withSummary: true, + }) + return ( + <> + {callData.map(item => { + const nodeOperatorId = item.nodeOperatorId.toNumber() + const nodeOperator = nodeOperatorsList?.[nodeOperatorId] + return ( +
+ Set Node Operator {nodeOperator ? nodeOperator.name : ''}{' '} + (id: {nodeOperatorId}) vetted validators limit{' '} + {`from ${nodeOperator?.totalVettedValidators} to ${item.stakingLimit}`} +
+ ) + })} + + ) +} diff --git a/modules/motions/ui/MotionDescription/DescTopUpWithLimitsAndCustomToken.tsx b/modules/motions/ui/MotionDescription/DescTopUpWithLimitsAndCustomToken.tsx new file mode 100644 index 00000000..a9c022e6 --- /dev/null +++ b/modules/motions/ui/MotionDescription/DescTopUpWithLimitsAndCustomToken.tsx @@ -0,0 +1,49 @@ +import { + useRecipientMapAll, + REGISTRY_WITH_LIMITS_BY_MOTION_TYPE, +} from 'modules/motions/hooks' + +import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' + +import { formatUnits } from 'ethers/lib/utils' +import { TopUpWithLimitsStablesAbi } from 'generated' +import { NestProps } from './types' +import { useMotionTokenData } from 'modules/motions/hooks/useMotionTokenData' + +type Props = NestProps & { + registryType: keyof typeof REGISTRY_WITH_LIMITS_BY_MOTION_TYPE +} + +export const DescTopUpWithLimitsAndCustomToken = ({ + callData, + registryType, +}: Props) => { + const { data: allowedRecipientMap, initialLoading: isRecipientDataLoading } = + useRecipientMapAll({ registryType }) + + const { tokenData, isTokenDataLoading } = useMotionTokenData(callData.token) + + if (isRecipientDataLoading || !allowedRecipientMap || isTokenDataLoading) { + return
Loading...
+ } + + return ( +
+ Top up single allowed recipient: + {callData.recipients.map((address, i) => { + const recipientName = allowedRecipientMap[address] + const formattedAmount = Number( + formatUnits(callData.amounts[i], tokenData?.decimals), + ).toLocaleString('en-EN') + + return ( +
+ {recipientName} + with {formattedAmount}{' '} + {tokenData ? {tokenData.label} : null} +
+ ) + })} +
+ ) +} diff --git a/modules/motions/ui/MotionDescription/MotionDescription.tsx b/modules/motions/ui/MotionDescription/MotionDescription.tsx index 0990ad25..750779c0 100644 --- a/modules/motions/ui/MotionDescription/MotionDescription.tsx +++ b/modules/motions/ui/MotionDescription/MotionDescription.tsx @@ -4,7 +4,6 @@ import { useMotionCreatedEvent } from 'modules/motions/hooks/useMotionCreatedEve import { useContractEvmScript } from 'modules/motions/hooks/useContractEvmScript' import { DescLEGOTopUp } from './DescLEGO' -import { DescNodeOperatorIncreaseLimit } from './DescNodeOperator' import { DescReferralPartnerAdd, DescReferralPartnerTopUp, @@ -22,16 +21,28 @@ import { } from './DescAllowedRecipient' import { DescTopUpWithLimits } from './DescTopUpWithLimits' +import { DescTopUpWithLimitsAndCustomToken } from './DescTopUpWithLimitsAndCustomToken' import { TopUpWithLimitsAbi, RemoveAllowedRecipientAbi, AddAllowedRecipientAbi, + EvmIncreaseNodeOperatorStakingLimitAbi, } from 'generated' import { Motion, MotionType } from 'modules/motions/types' import { EvmUnrecognized } from 'modules/motions/evmAddresses' import { getMotionTypeByScriptFactory } from 'modules/motions/utils/getMotionType' import { NestProps } from './types' +import { DescWrap } from './MotionDescriptionStyle' +import { DescSDVTNodeOperatorsDeactivate } from './DescSDVTNodeOperatorsDeactivate' +import { DescSDVTNodeOperatorsActivate } from './DescSDVTNodeOperatorsActivate' +import { DescSDVTVettedValidatorsLimitsSet } from './DescSDVTVettedValidatorsLimitsSet' +import { DescSDVTTargetValidatorLimitsUpdate } from './DescSDVTTargetValidatorLimitsUpdate' +import { DescSDVTNodeOperatorRewardAddressesSet } from './DescSDVTNodeOperatorRewardAddressesSet' +import { DescSDVTNodeOperatorNamesSet } from './DescSDVTNodeOperatorNamesSet' +import { DescSDVTNodeOperatorsAdd } from './DescSDVTNodeOperatorsAdd' +import { DescSDVTNodeOperatorManagersChange } from './DescSDVTNodeOperatorManagersChange' +import { DescNodeOperatorIncreaseLimit } from './DescNodeOperatorLimitIncrease' type DescWithLimitsProps = NestProps< TopUpWithLimitsAbi['decodeEVMScriptCallData'] @@ -43,8 +54,23 @@ type DescAllowedRecipientAddProps = NestProps< AddAllowedRecipientAbi['decodeEVMScriptCallData'] > +type DescNodeOperatorIncreaseLimitProps = NestProps< + EvmIncreaseNodeOperatorStakingLimitAbi['decodeEVMScriptCallData'] +> + +type GenericDescProps = { + callData: any +} + const MOTION_DESCRIPTIONS = { - [MotionType.NodeOperatorIncreaseLimit]: DescNodeOperatorIncreaseLimit, + [MotionType.NodeOperatorIncreaseLimit]: ( + props: DescNodeOperatorIncreaseLimitProps, + ) => ( + + ), [MotionType.LEGOTopUp]: DescLEGOTopUp, [MotionType.RewardProgramAdd]: DescRewardProgramAdd, [MotionType.RewardProgramTopUp]: DescRewardProgramTopUp, @@ -52,7 +78,7 @@ const MOTION_DESCRIPTIONS = { [MotionType.ReferralPartnerAdd]: DescReferralPartnerAdd, [MotionType.ReferralPartnerTopUp]: DescReferralPartnerTopUp, [MotionType.ReferralPartnerRemove]: DescReferralPartnerRemove, - [MotionType.AllowedRecipientAdd]: (props: DescAllowedRecipientAddProps) => ( + [MotionType.AllowedRecipientAdd]: (props: GenericDescProps) => ( ), + [MotionType.SDVTNodeOperatorsAdd]: DescSDVTNodeOperatorsAdd, + [MotionType.SDVTNodeOperatorsActivate]: DescSDVTNodeOperatorsActivate, + [MotionType.SDVTNodeOperatorsDeactivate]: DescSDVTNodeOperatorsDeactivate, + [MotionType.SDVTVettedValidatorsLimitsSet]: DescSDVTVettedValidatorsLimitsSet, + [MotionType.SDVTNodeOperatorRewardAddressesSet]: + DescSDVTNodeOperatorRewardAddressesSet, + [MotionType.SDVTNodeOperatorNamesSet]: DescSDVTNodeOperatorNamesSet, + [MotionType.SDVTTargetValidatorLimitsUpdate]: + DescSDVTTargetValidatorLimitsUpdate, + [MotionType.SDVTNodeOperatorManagerChange]: + DescSDVTNodeOperatorManagersChange, + [MotionType.SandboxNodeOperatorIncreaseLimit]: ( + props: DescNodeOperatorIncreaseLimitProps, + ) => ( + + ), + [MotionType.RccStablesTopUp]: (props: GenericDescProps) => ( + + ), + [MotionType.PmlStablesTopUp]: (props: GenericDescProps) => ( + + ), + [MotionType.AtcStablesTopUp]: (props: GenericDescProps) => ( + + ), + [MotionType.SandboxStablesAdd]: (props: DescAllowedRecipientAddProps) => ( + + ), + [MotionType.SandboxStablesRemove]: ( + props: DescAllowedRecipientRemoveProps, + ) => ( + + ), + [MotionType.SandboxStablesTopUp]: (props: GenericDescProps) => ( + + ), } as const type Props = { @@ -222,7 +305,12 @@ export function MotionDescription({ motion }: Props) { return <>Loading... } - const Desc = MOTION_DESCRIPTIONS[motionType] + const Desc: React.FunctionComponent = + MOTION_DESCRIPTIONS[motionType] - return + return ( + + + + ) } diff --git a/modules/motions/ui/MotionDescription/MotionDescriptionStyle.ts b/modules/motions/ui/MotionDescription/MotionDescriptionStyle.ts new file mode 100644 index 00000000..44e382fd --- /dev/null +++ b/modules/motions/ui/MotionDescription/MotionDescriptionStyle.ts @@ -0,0 +1,6 @@ +import styled from 'styled-components' + +export const DescWrap = styled.div` + line-height: 1.5; + word-break: break-word; +` diff --git a/modules/motions/ui/MotionFormStartNew/CreateMotionFormStyle.tsx b/modules/motions/ui/MotionFormStartNew/CreateMotionFormStyle.tsx index 0cbc1beb..bde0d8cb 100644 --- a/modules/motions/ui/MotionFormStartNew/CreateMotionFormStyle.tsx +++ b/modules/motions/ui/MotionFormStartNew/CreateMotionFormStyle.tsx @@ -85,6 +85,14 @@ export const MessageBox = styled.div` border-radius: ${({ theme }) => theme.borderRadiusesMap.md + 'px'}; ` +export const ErrorBox = styled.div` + margin-bottom: 20px; + padding: 20px 15px; + font-size: 14px; + background-color: rgba(255, 0, 0, 0.25); + border-radius: ${({ theme }) => theme.borderRadiusesMap.md + 'px'}; +` + export const RetryHint = styled(Text).attrs({ size: 12, weight: 500, diff --git a/modules/motions/ui/MotionFormStartNew/MotionFormStartNew.tsx b/modules/motions/ui/MotionFormStartNew/MotionFormStartNew.tsx index 759b6b25..53e72e9a 100644 --- a/modules/motions/ui/MotionFormStartNew/MotionFormStartNew.tsx +++ b/modules/motions/ui/MotionFormStartNew/MotionFormStartNew.tsx @@ -55,8 +55,19 @@ export function MotionFormStartNew({ onComplete }: Props) { setSubmitting(true) + const evmScriptFactory = getScriptFactoryByMotionType( + chainId, + motionType, + ) + + if (!evmScriptFactory) { + throw new Error( + `EVM script factory for motion type ${motionType} in chain ${chainId} not found`, + ) + } + const tx = await formParts[motionType].populateTx({ - evmScriptFactory: getScriptFactoryByMotionType(chainId, motionType), + evmScriptFactory, formData: e[motionType], contract: contractEasyTrack, }) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientAdd.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientAdd.tsx index 914e366f..df8aa4f0 100644 --- a/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientAdd.tsx +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientAdd.tsx @@ -11,6 +11,7 @@ import { ContractStethGasSupplyAdd, ContractStethRewardProgramAdd, ContractRewardsShareProgramAdd, + ContractEvmSandboxStablesAdd, } from 'modules/blockChain/contracts' import { MotionTypeForms } from 'modules/motions/types' import { createMotionFormPart } from './createMotionFormPart' @@ -29,6 +30,10 @@ export const ALLOWED_RECIPIENT_ADD_MAP = { evmContract: ContractRewardsShareProgramAdd, motionType: MotionTypeForms.RewardsShareProgramAdd, }, + [MotionTypeForms.SandboxStablesAdd]: { + evmContract: ContractEvmSandboxStablesAdd, + motionType: MotionTypeForms.SandboxStablesAdd, + }, } export const formParts = ({ diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientRemove.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientRemove.tsx index f0b371b1..999dafb8 100644 --- a/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientRemove.tsx +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientRemove.tsx @@ -10,6 +10,7 @@ import { ContractStethGasSupplyRemove, ContractStethRewardProgramRemove, ContractRewardsShareProgramRemove, + ContractEvmSandboxStablesRemove, } from 'modules/blockChain/contracts' import { MotionTypeForms } from 'modules/motions/types' import { createMotionFormPart } from './createMotionFormPart' @@ -28,6 +29,10 @@ export const ALLOWED_RECIPIENT_REMOVE_MAP = { evmContract: ContractRewardsShareProgramRemove, motionType: MotionTypeForms.RewardsShareProgramRemove, }, + [MotionTypeForms.SandboxStablesRemove]: { + evmContract: ContractEvmSandboxStablesRemove, + motionType: MotionTypeForms.SandboxStablesRemove, + }, } export const formParts = ({ diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientTopUp.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientTopUp.tsx index 55f31891..0ce0df20 100644 --- a/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientTopUp.tsx +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewAllowedRecipientTopUp.tsx @@ -41,7 +41,8 @@ import { estimateGasFallback, checkInputsGreaterThanLimit, } from 'modules/motions/utils' -import { tokenLimitError, periodLimitError } from 'modules/motions/constants' +import { periodLimitError } from 'modules/motions/constants' +import { validateTransitionLimit } from 'modules/motions/utils/validateTransitionLimit' type Program = { address: string @@ -152,13 +153,15 @@ export const formParts = ({ setValue(fieldNames.programs, [{ address: recipientAddress }]) }, [fieldNames.programs, setValue, allowedRecipients.data]) - const { data: limits } = useTransitionLimits() + const { data: limits, initialLoading: isTransitionLimitsDataLoading } = + useTransitionLimits() const transitionLimit = token.address && limits?.[utils.getAddress(token.address)] if ( trustedCaller.initialLoading || allowedRecipients.initialLoading || + isTransitionLimitsDataLoading || periodLimitsLoading ) { return @@ -227,15 +230,18 @@ export const formParts = ({ rules={{ required: 'Field is required', validate: value => { - const check1 = validateToken(value) - if (typeof check1 === 'string') { - return check1 + const tokenError = validateToken(value) + if (tokenError) { + return tokenError } - if ( - transitionLimit && - Number(value) > transitionLimit - ) { - return tokenLimitError(token.label, transitionLimit) + + const transitionLimitError = validateTransitionLimit( + value, + transitionLimit, + token.label, + ) + if (transitionLimitError) { + return transitionLimitError } const isLargeThenPeriodLimit = diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewNodeOperatorLimitIncrease.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewNodeOperatorLimitIncrease.tsx new file mode 100644 index 00000000..f57192e1 --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewNodeOperatorLimitIncrease.tsx @@ -0,0 +1,157 @@ +import { utils } from 'ethers' +import { useMemo, useEffect } from 'react' +import { useFormContext } from 'react-hook-form' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' +import { useNodeOperatorsList } from 'modules/motions/hooks/useNodeOperatorsList' +import { useNodeOperatorKeysInfo } from 'modules/motions/hooks/useNodeOperatorKeysInfo' + +import { KeysInfoBlock } from 'modules/motions/ui/KeysInfoBlock' +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { InputControl } from 'modules/shared/ui/Controls/Input' +import { InputNumberControl } from 'modules/shared/ui/Controls/InputNumber' +import { Fieldset, MessageBox } from '../CreateMotionFormStyle' + +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils/estimateGasFallback' +import { IncreaseLimitMotionType } from 'modules/motions/constants' +import { getNodeOperatorRegistryType } from 'modules/motions/utils/getNodeOperatorRegistryType' +import { validateUintValue } from 'modules/motions/utils/validateUintValue' + +export const formParts = ({ + motionType, +}: { + motionType: IncreaseLimitMotionType +}) => + createMotionFormPart({ + motionType, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const encodedCallData = new utils.AbiCoder().encode( + ['uint256', 'uint256'], + [Number(formData.nodeOperatorId), Number(formData.newLimit)], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + newLimit: '', + nodeOperatorId: '', + }), + Component: function StartNewMotionNodeOperatorLimitIncrease({ + fieldNames, + submitAction, + }) { + const { setValue } = useFormContext() + const { walletAddress } = useWeb3() + const registryType = getNodeOperatorRegistryType(motionType) + const nodeOperators = useNodeOperatorsList(registryType) + const keysInfo = useNodeOperatorKeysInfo(registryType) + + const currentNodeOperator = useMemo(() => { + if (!walletAddress || !nodeOperators.data) { + return null + } + return nodeOperators.data.find( + o => + utils.getAddress(o.rewardAddress) === + utils.getAddress(walletAddress), + ) + }, [nodeOperators.data, walletAddress]) + + const isNodeOperatorConnected = !!currentNodeOperator + + const connectedKeysInfo = useMemo(() => { + if (!isNodeOperatorConnected || !keysInfo.data || !walletAddress) + return null + return keysInfo.data.operators?.find( + o => + utils.getAddress(o.info.rewardAddress) === + utils.getAddress(walletAddress), + ) + }, [isNodeOperatorConnected, keysInfo.data, walletAddress]) + + useEffect(() => { + if (currentNodeOperator) { + setValue(fieldNames.nodeOperatorId, currentNodeOperator.id) + } + }, [fieldNames.nodeOperatorId, setValue, currentNodeOperator]) + + if (nodeOperators.initialLoading || keysInfo.initialLoading) { + return + } + + if (!isNodeOperatorConnected) { + return You should be connected as node operator + } + + if (!connectedKeysInfo) { + return Error: No keys info found + } + + const isConnectedKeysValid = + connectedKeysInfo.invalid.length === 0 && + connectedKeysInfo.duplicates.length === 0 + + if (!isConnectedKeysValid) { + return + } + + return ( + <> + + +
+ + Node operator {currentNodeOperator.name} with id + + } + rules={{ required: 'Field is required' }} + readOnly + /> +
+ +
+ + New limit (current limit is{' '} + {currentNodeOperator.stakingLimit.toString()}) + + } + rules={{ + required: 'Field is required', + validate: value => { + const uintError = validateUintValue(value) + if (uintError) { + return uintError + } + const valueNum = Number(value) + + const limit = currentNodeOperator.stakingLimit.toNumber() + if (valueNum <= limit) { + return 'New limit value should be greater than current' + } + if (valueNum > connectedKeysInfo.info.totalSigningKeys) { + return 'New limit value should be less than or equal to total signing keys' + } + return true + }, + }} + /> +
+ + {submitAction} + + ) + }, + }) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewNodeOperators.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewNodeOperators.tsx deleted file mode 100644 index 1392645b..00000000 --- a/modules/motions/ui/MotionFormStartNew/Parts/StartNewNodeOperators.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { utils } from 'ethers' -import { useMemo, useEffect } from 'react' -import { useFormContext } from 'react-hook-form' -import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' -import { useNodeOperatorsList } from 'modules/motions/hooks/useNodeOperatorsList' -import { useNodeOperatorKeysInfo } from 'modules/motions/hooks/useNodeOperatorKeysInfo' - -import { KeysInfoBlock } from 'modules/motions/ui/KeysInfoBlock' -import { PageLoader } from 'modules/shared/ui/Common/PageLoader' -import { InputControl } from 'modules/shared/ui/Controls/Input' -import { InputNumberControl } from 'modules/shared/ui/Controls/InputNumber' -import { Fieldset, MessageBox } from '../CreateMotionFormStyle' - -import { MotionType } from 'modules/motions/types' -import { createMotionFormPart } from './createMotionFormPart' -import { estimateGasFallback } from 'modules/motions/utils/estimateGasFallback' - -export const formParts = createMotionFormPart({ - motionType: MotionType.NodeOperatorIncreaseLimit, - populateTx: async ({ evmScriptFactory, formData, contract }) => { - const encodedCallData = new utils.AbiCoder().encode( - ['uint256', 'uint256'], - [Number(formData.nodeOperatorId), Number(formData.newLimit)], - ) - const gasLimit = await estimateGasFallback( - contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), - ) - const tx = await contract.populateTransaction.createMotion( - evmScriptFactory, - encodedCallData, - { gasLimit }, - ) - return tx - }, - getDefaultFormData: () => ({ - newLimit: '', - nodeOperatorId: '', - }), - Component: function StartNewMotionNodeOperators({ - fieldNames, - submitAction, - }) { - const { setValue } = useFormContext() - const { walletAddress } = useWeb3() - const nodeOperators = useNodeOperatorsList() - const keysInfo = useNodeOperatorKeysInfo() - - const [operatorId, currentNodeOperator] = useMemo(() => { - if (!walletAddress) return [undefined, null] - const idx = nodeOperators.data?.list.findIndex( - o => - utils.getAddress(o.rewardAddress) === utils.getAddress(walletAddress), - ) - const operator = idx !== undefined && nodeOperators.data?.list[idx] - return [idx, operator] - }, [walletAddress, nodeOperators]) - - const isNodeOperatorConnected = - !nodeOperators.data?.isRegistrySupported || Boolean(currentNodeOperator) - - const connectedKeysInfo = useMemo(() => { - if (!isNodeOperatorConnected || !keysInfo.data || !walletAddress) - return null - return keysInfo.data.operators?.find( - o => - utils.getAddress(o.info.rewardAddress) === - utils.getAddress(walletAddress), - ) - }, [isNodeOperatorConnected, keysInfo.data, walletAddress]) - - useEffect(() => { - setValue(fieldNames.nodeOperatorId, operatorId) - }, [operatorId, fieldNames.nodeOperatorId, setValue]) - - if (nodeOperators.initialLoading || keysInfo.initialLoading) { - return - } - - if (!isNodeOperatorConnected) { - return You should be connected as node operator - } - - if (!connectedKeysInfo) { - return Error: No keys info found - } - - const isConnectedKeysValid = - connectedKeysInfo.invalid.length === 0 && - connectedKeysInfo.duplicates.length === 0 - - if (!isConnectedKeysValid) { - return - } - - return ( - <> - - -
- - Node operator {currentNodeOperator.name} with id - - ) : ( - `Node operator id is loading` - ) - } - rules={{ required: 'Field is required' }} - readOnly - /> -
- -
- - New limit (current limit is{' '} - {currentNodeOperator.stakingLimit.toString()}) - - ) : ( - `New limit` - ) - } - rules={{ - required: 'Field is required', - validate: value => { - if (value === '') return true - const parsedValue = Number(value) - if (Number.isNaN(parsedValue)) return 'Wrong number format' - const limit = currentNodeOperator - ? currentNodeOperator.stakingLimit.toNumber() - : 0 - if (parsedValue <= limit) { - return 'New limit value should be greater than current' - } - if (parsedValue > connectedKeysInfo.info.totalSigningKeys) { - return 'New limit value should be less than total signing keys' - } - return true - }, - }} - /> -
- - {submitAction} - - ) - }, -}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorManagersChange.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorManagersChange.tsx new file mode 100644 index 00000000..3878989d --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorManagersChange.tsx @@ -0,0 +1,284 @@ +import { utils } from 'ethers' + +import { Fragment, useMemo } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { ContractSDVTNodeOperatorManagerChange } from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { InputControl } from 'modules/shared/ui/Controls/Input' +import { checkIsAddressManagerOfNodeOperator } from 'modules/motions/utils/checkAddressManagerRole' +import { noSigningKeysRoleError } from 'modules/motions/constants' +import { validateAddress } from 'modules/motions/utils/validateAddress' +import { NodeOperatorSelectControl } from '../../NodeOperatorSelectControl' + +type NodeOperator = { + id: string + oldManagerAddress: string + newManagerAddress: string +} + +export const formParts = createMotionFormPart({ + motionType: MotionType.SDVTNodeOperatorManagerChange, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const sortedNodeOperators = formData.nodeOperators.sort( + (a, b) => Number(a.id) - Number(b.id), + ) + + const encodedCallData = new utils.AbiCoder().encode( + [ + 'tuple(uint256 nodeOperatorId, address oldManagerAddress, address newManagerAddress)[]', + ], + [ + sortedNodeOperators.map(nodeOperator => ({ + nodeOperatorId: Number(nodeOperator.id), + oldManagerAddress: utils.getAddress(nodeOperator.oldManagerAddress), + newManagerAddress: utils.getAddress(nodeOperator.newManagerAddress), + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { + id: '', + oldManagerAddress: '', + newManagerAddress: '', + }, + ] as NodeOperator[], + }), + Component: ({ fieldNames, submitAction }) => { + const { walletAddress, chainId } = useWeb3() + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsDataLoading, + } = useSDVTNodeOperatorsList() + + const activeNodeOperators = nodeOperatorsList?.filter( + nodeOperator => nodeOperator.active, + ) + + const trustedCaller = ContractSDVTNodeOperatorManagerChange.useSwrWeb3( + 'trustedCaller', + [], + ) + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + const { watch, setValue } = useFormContext() + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + const managerAddressesMap = useMemo( + () => + nodeOperatorsList?.reduce((acc, item) => { + if (!item.managerAddress) { + return acc + } + + acc[item.managerAddress] = item.id + return acc + }, {} as Record) ?? {}, + [nodeOperatorsList], + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!activeNodeOperators?.length) return [] + const selectedIds = selectedNodeOperators.map(({ id }) => parseInt(id)) + const thisId = parseInt(selectedNodeOperators[fieldIdx]?.id) + return activeNodeOperators.filter( + ({ id }) => !selectedIds.includes(id) || id === thisId, + ) + } + + const handleAddUpdate = () => + fieldsArr.append({ + id: '', + oldManagerAddress: '', + newManagerAddress: '', + } as NodeOperator) + + if (trustedCaller.initialLoading || isNodeOperatorsDataLoading) { + return + } + + if (trustedCaller.data !== walletAddress) { + return You should be connected as trusted caller + } + + if (!nodeOperatorsList?.length || !activeNodeOperators?.length) { + return There are no active node operators + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => { + return ( + + + + {fieldsArr.fields.length > 1 && ( + + Update #{fieldIndex + 1} + + )} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIndex)} + > + Remove update {fieldIndex + 1} + + )} + + +
+ { + const nodeOperator = nodeOperatorsList[Number(value)] + + if (nodeOperator.managerAddress) { + setValue( + `${fieldNames.nodeOperators}.${fieldIndex}.oldManagerAddress`, + nodeOperator.managerAddress, + ) + } + }} + /> +
+ +
+ { + const addressErr = validateAddress(value) + if (addressErr) { + return addressErr + } + + const canAddressManageKeys = + await checkIsAddressManagerOfNodeOperator( + value, + selectedNodeOperators[fieldIndex].id, + chainId, + ) + + if (!canAddressManageKeys) { + return noSigningKeysRoleError + } + }, + }} + /> +
+ +
+ { + const addressErr = validateAddress(value) + if (addressErr) { + return addressErr + } + + const valueAddress = utils.getAddress(value) + + /* + Although the specification does not yet state this, + the new manager address should not match + any of the manager addresses of other operator nodes. + */ + const idInAddressMap = managerAddressesMap[valueAddress] + if (typeof idInAddressMap === 'number') { + return 'Address must not be in use by another node operator' + } + + const addressInSelectedNodeOperatorsIndex = + selectedNodeOperators.findIndex( + ({ newManagerAddress }, index) => + newManagerAddress && + utils.getAddress(newManagerAddress) === + valueAddress && + fieldIndex !== index, + ) + + if (addressInSelectedNodeOperatorsIndex !== -1) { + return 'Address is already in use by another update' + } + + const canAddressManageKeys = + await checkIsAddressManagerOfNodeOperator( + valueAddress, + selectedNodeOperators[fieldIndex].id, + chainId, + ) + + if (canAddressManageKeys) { + return 'Address already has a signing keys manager role' + } + + return true + }, + }} + /> +
+
+
+ ) + })} + + {selectedNodeOperators.length < activeNodeOperators.length && ( +
+ } + color="secondary" + > + One more update + +
+ )} + + {submitAction} + + ) + }, +}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorNamesSet.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorNamesSet.tsx new file mode 100644 index 00000000..20394638 --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorNamesSet.tsx @@ -0,0 +1,217 @@ +import { utils } from 'ethers' + +import { Fragment, useMemo } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { ContractSDVTNodeOperatorNamesSet } from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { InputControl } from 'modules/shared/ui/Controls/Input' +import { useSDVTOperatorNameLimit } from 'modules/motions/hooks' +import { validateNodeOperatorName } from 'modules/motions/utils/validateNodeOperatorName' +import { NodeOperatorSelectControl } from '../../NodeOperatorSelectControl' + +type NodeOperator = { + id: string + name: string +} + +export const formParts = createMotionFormPart({ + motionType: MotionType.SDVTNodeOperatorNamesSet, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const sortedNodeOperators = formData.nodeOperators.sort( + (a, b) => Number(a.id) - Number(b.id), + ) + + const encodedCallData = new utils.AbiCoder().encode( + ['tuple(uint256 nodeOperatorId, string name)[]'], + [ + sortedNodeOperators.map(nodeOperator => ({ + nodeOperatorId: Number(nodeOperator.id), + name: nodeOperator.name, + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { + id: '', + name: '', + }, + ] as NodeOperator[], + }), + Component: ({ fieldNames, submitAction }) => { + const { walletAddress } = useWeb3() + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsDataLoading, + } = useSDVTNodeOperatorsList() + + const { + data: maxNodeOperatorNameLength, + initialLoading: isNodeOperatorMaxNameLengthLoading, + } = useSDVTOperatorNameLimit() + + const trustedCaller = ContractSDVTNodeOperatorNamesSet.useSwrWeb3( + 'trustedCaller', + [], + ) + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + + const { watch } = useFormContext() + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + const nodeOperatorNamesMap = useMemo( + () => + nodeOperatorsList?.reduce((acc, item) => { + acc[item.name] = item.id + return acc + }, {} as Record) ?? {}, + [nodeOperatorsList], + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!nodeOperatorsList?.length) return [] + const selectedIds = selectedNodeOperators.map(({ id }) => parseInt(id)) + const thisId = parseInt(selectedNodeOperators[fieldIdx]?.id) + return nodeOperatorsList.filter( + ({ id }) => !selectedIds.includes(id) || id === thisId, + ) + } + + const handleAddUpdate = () => + fieldsArr.append({ + nodeOperatorId: '', + name: '', + }) + + if ( + trustedCaller.initialLoading || + isNodeOperatorsDataLoading || + isNodeOperatorMaxNameLengthLoading + ) { + return + } + + if (trustedCaller.data !== walletAddress) { + return You should be connected as trusted caller + } + + if (!nodeOperatorsList?.length) { + return Node operators list is empty + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => ( + + + + {fieldsArr.fields.length > 1 && ( + Update #{fieldIndex + 1} + )} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIndex)} + > + Remove update {fieldIndex + 1} + + )} + + +
+ +
+ +
+ { + const nameErr = validateNodeOperatorName( + value, + maxNodeOperatorNameLength, + ) + if (nameErr) { + return nameErr + } + + const idInNameMap = nodeOperatorNamesMap[value] + + if (typeof idInNameMap === 'number') { + return 'Name must not be in use by another node operator' + } + + const nameInSelectedNodeOperatorsIndex = + selectedNodeOperators.findIndex( + ({ name }, index) => + name && + name.toLowerCase() === value.toLowerCase() && + fieldIndex !== index, + ) + + if (nameInSelectedNodeOperatorsIndex !== -1) { + return 'Name is already in use by another update' + } + + return true + }, + }} + /> +
+
+
+ ))} + + {selectedNodeOperators.length < nodeOperatorsList.length && ( +
+ } + color="secondary" + > + One more update + +
+ )} + + {submitAction} + + ) + }, +}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorRewardAddressesSet.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorRewardAddressesSet.tsx new file mode 100644 index 00000000..900594f2 --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorRewardAddressesSet.tsx @@ -0,0 +1,240 @@ +import { utils } from 'ethers' + +import { Fragment, useMemo } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { ContractSDVTNodeOperatorRewardAddressesSet } from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { InputControl } from 'modules/shared/ui/Controls/Input' +import { STETH } from 'modules/blockChain/contractAddresses' +import { validateAddress } from 'modules/motions/utils/validateAddress' +import { NodeOperatorSelectControl } from '../../NodeOperatorSelectControl' +import { MotionInfoBox } from 'modules/shared/ui/Common/MotionInfoBox' + +type NodeOperator = { + id: string + newRewardAddress: string +} + +export const formParts = createMotionFormPart({ + motionType: MotionType.SDVTNodeOperatorRewardAddressesSet, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const sortedNodeOperators = formData.nodeOperators.sort( + (a, b) => Number(a.id) - Number(b.id), + ) + + const encodedCallData = new utils.AbiCoder().encode( + ['tuple(uint256 nodeOperatorId, address rewardAddress)[]'], + [ + sortedNodeOperators.map(nodeOperator => ({ + nodeOperatorId: Number(nodeOperator.id), + rewardAddress: utils.getAddress(nodeOperator.newRewardAddress), + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { + id: '', + newRewardAddress: '', + }, + ] as NodeOperator[], + }), + Component: ({ fieldNames, submitAction }) => { + const { walletAddress, chainId } = useWeb3() + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsDataLoading, + } = useSDVTNodeOperatorsList() + + const trustedCaller = ContractSDVTNodeOperatorRewardAddressesSet.useSwrWeb3( + 'trustedCaller', + [], + ) + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + const { watch } = useFormContext() + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + const rewardAddressesMap = useMemo( + () => + nodeOperatorsList?.reduce((acc, item) => { + acc[item.rewardAddress] = item.id + return acc + }, {} as Record) ?? {}, + [nodeOperatorsList], + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!nodeOperatorsList?.length) { + return [] + } + const selectedIds = selectedNodeOperators.map(({ id }) => parseInt(id)) + const thisId = parseInt(selectedNodeOperators[fieldIdx]?.id) + return nodeOperatorsList.filter( + ({ id }) => !selectedIds.includes(id) || id === thisId, + ) + } + + const handleAddUpdate = () => + fieldsArr.append({ + id: '', + newRewardAddress: '', + } as NodeOperator) + + if (trustedCaller.initialLoading || isNodeOperatorsDataLoading) { + return + } + + if (trustedCaller.data !== walletAddress) { + return You should be connected as trusted caller + } + + if (!nodeOperatorsList?.length) { + return Node operators list is empty + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => { + return ( + + + + {fieldsArr.fields.length > 1 && ( + + Update #{fieldIndex + 1} + + )} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIndex)} + > + Remove update {fieldIndex + 1} + + )} + + +
+ +
+ + {!isNaN(parseInt(selectedNodeOperators[fieldIndex]?.id)) ? ( + + Current reward address:{' '} + { + nodeOperatorsList[ + Number(selectedNodeOperators[fieldIndex].id) + ].rewardAddress + } + + ) : null} + +
+ { + const addressErr = validateAddress(value) + if (addressErr) { + return addressErr + } + + const valueAddress = utils.getAddress(value) + + const idInAddressMap = rewardAddressesMap[valueAddress] + + /* + Although the specification does not yet state this, + according to the code, the new reward address should not match + any of the reward addresses of other operator nodes. + */ + if (typeof idInAddressMap === 'number') { + return 'Address must not be in use by another node operator' + } + + const addressInSelectedNodeOperatorsIndex = + selectedNodeOperators.findIndex( + ({ newRewardAddress }, index) => + newRewardAddress && + utils.getAddress(newRewardAddress) === + valueAddress && + fieldIndex !== index, + ) + + /* + Same as above, each reward address must be unique within the update. + */ + if (addressInSelectedNodeOperatorsIndex !== -1) { + return 'Address is already in use by another update' + } + + const stETHAddress = STETH[chainId] + + if ( + stETHAddress && + valueAddress === utils.getAddress(stETHAddress) + ) { + return 'Address must not be stETH address' + } + }, + }} + /> +
+
+
+ ) + })} + + {selectedNodeOperators.length < nodeOperatorsList.length && ( +
+ } + color="secondary" + > + One more update + +
+ )} + + {submitAction} + + ) + }, +}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorsActivate.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorsActivate.tsx new file mode 100644 index 00000000..12351326 --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorsActivate.tsx @@ -0,0 +1,202 @@ +import { utils } from 'ethers' + +import { Fragment } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { ContractSDVTNodeOperatorsActivate } from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { InputControl } from 'modules/shared/ui/Controls/Input' +import { checkAddressForManageSigningKeysRole } from 'modules/motions/utils/checkAddressManagerRole' +import { validateAddress } from 'modules/motions/utils/validateAddress' +import { NodeOperatorSelectControl } from '../../NodeOperatorSelectControl' + +type NodeOperator = { + id: string + managerAddress: string +} + +export const formParts = createMotionFormPart({ + motionType: MotionType.SDVTNodeOperatorsActivate, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const sortedNodeOperators = formData.nodeOperators.sort( + (a, b) => Number(a.id) - Number(b.id), + ) + + const encodedCallData = new utils.AbiCoder().encode( + ['tuple(uint256 nodeOperatorId, address managerAddress)[]'], + [ + sortedNodeOperators.map(nodeOperator => ({ + nodeOperatorId: Number(nodeOperator.id), + managerAddress: utils.getAddress(nodeOperator.managerAddress), + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { + id: '', + managerAddress: '', + }, + ] as NodeOperator[], + }), + Component: ({ fieldNames, submitAction }) => { + const { walletAddress, chainId } = useWeb3() + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsDataLoading, + } = useSDVTNodeOperatorsList() + + const deactivatedNodeOperators = nodeOperatorsList?.filter( + nodeOperator => !nodeOperator.active, + ) + + const trustedCaller = ContractSDVTNodeOperatorsActivate.useSwrWeb3( + 'trustedCaller', + [], + ) + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + const { watch, setValue } = useFormContext() + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!deactivatedNodeOperators?.length) return [] + const selectedIds = selectedNodeOperators.map(({ id }) => parseInt(id)) + const thisId = parseInt(selectedNodeOperators[fieldIdx]?.id) + return deactivatedNodeOperators.filter( + ({ id }) => !selectedIds.includes(id) || id === thisId, + ) + } + + const handleAddUpdate = () => + fieldsArr.append({ + id: '', + managerAddress: '', + } as NodeOperator) + + if (trustedCaller.initialLoading || isNodeOperatorsDataLoading) { + return + } + + if (trustedCaller.data !== walletAddress) { + return You should be connected as trusted caller + } + + if (!nodeOperatorsList?.length || !deactivatedNodeOperators?.length) { + return There are no node operators to activate + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => { + return ( + + + + {fieldsArr.fields.length > 1 && ( + + Update #{fieldIndex + 1} + + )} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIndex)} + > + Remove update {fieldIndex + 1} + + )} + + +
+ { + const nodeOperator = nodeOperatorsList[Number(value)] + + if (nodeOperator.managerAddress) { + setValue( + `${fieldNames.nodeOperators}.${fieldIndex}.managerAddress`, + nodeOperator.managerAddress, + ) + } + }} + /> +
+ +
+ { + const addressErr = validateAddress(value) + if (addressErr) { + return addressErr + } + const canAddressManageKeys = + await checkAddressForManageSigningKeysRole( + utils.getAddress(value), + chainId, + ) + + if (canAddressManageKeys) { + return 'Address already has a signing keys manager role' + } + }, + }} + /> +
+
+
+ ) + })} + + {selectedNodeOperators.length < deactivatedNodeOperators.length && ( +
+ } + color="secondary" + > + One more update + +
+ )} + + {submitAction} + + ) + }, +}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorsDeactivate.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorsDeactivate.tsx new file mode 100644 index 00000000..ae8bd637 --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTNodeOperatorsDeactivate.tsx @@ -0,0 +1,211 @@ +import { utils } from 'ethers' + +import { Fragment } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { ContractSDVTNodeOperatorsDeactivate } from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { InputControl } from 'modules/shared/ui/Controls/Input' +import { checkIsAddressManagerOfNodeOperator } from 'modules/motions/utils/checkAddressManagerRole' +import { noSigningKeysRoleError } from 'modules/motions/constants' +import { validateAddress } from 'modules/motions/utils/validateAddress' +import { NodeOperatorSelectControl } from '../../NodeOperatorSelectControl' + +type NodeOperator = { + id: string + managerAddress: string +} + +export const formParts = createMotionFormPart({ + motionType: MotionType.SDVTNodeOperatorsDeactivate, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const sortedNodeOperators = formData.nodeOperators.sort( + (a, b) => Number(a.id) - Number(b.id), + ) + + const encodedCallData = new utils.AbiCoder().encode( + ['tuple(uint256 nodeOperatorId, address managerAddress)[]'], + [ + sortedNodeOperators.map(nodeOperator => ({ + nodeOperatorId: Number(nodeOperator.id), + managerAddress: utils.getAddress(nodeOperator.managerAddress), + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { + id: '', + managerAddress: '', + }, + ] as NodeOperator[], + }), + Component: ({ fieldNames, submitAction }) => { + const { walletAddress, chainId } = useWeb3() + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsDataLoading, + } = useSDVTNodeOperatorsList() + + const activeNodeOperators = nodeOperatorsList?.filter( + nodeOperator => nodeOperator.active, + ) + + const trustedCaller = ContractSDVTNodeOperatorsDeactivate.useSwrWeb3( + 'trustedCaller', + [], + ) + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + const { watch, setValue } = useFormContext() + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!activeNodeOperators?.length) return [] + const selectedIds = selectedNodeOperators.map(({ id }) => parseInt(id)) + const thisId = parseInt(selectedNodeOperators[fieldIdx]?.id) + return activeNodeOperators.filter( + ({ id }) => !selectedIds.includes(id) || id === thisId, + ) + } + + const handleAddUpdate = () => + fieldsArr.append({ + id: '', + managerAddress: '', + } as NodeOperator) + + if (trustedCaller.initialLoading || isNodeOperatorsDataLoading) { + return + } + + if (trustedCaller.data !== walletAddress) { + return You should be connected as trusted caller + } + + if (!nodeOperatorsList?.length || !activeNodeOperators?.length) { + return There are no node operators to deactivate + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => { + return ( + + + + {fieldsArr.fields.length > 1 && ( + + Update #{fieldIndex + 1} + + )} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIndex)} + > + Remove update {fieldIndex + 1} + + )} + + +
+ { + const nodeOperator = nodeOperatorsList[Number(value)] + + if (nodeOperator.managerAddress) { + setValue( + `${fieldNames.nodeOperators}.${fieldIndex}.managerAddress`, + nodeOperator.managerAddress, + ) + } + }} + /> +
+ +
+ { + const addressErr = validateAddress(value) + if (addressErr) { + return addressErr + } + + const canAddressManageKeys = + await checkIsAddressManagerOfNodeOperator( + value, + selectedNodeOperators[fieldIndex].id, + chainId, + ) + + if (!canAddressManageKeys) { + return noSigningKeysRoleError + } + }, + }} + /> +
+
+
+ ) + })} + + {selectedNodeOperators.length < activeNodeOperators.length && ( +
+ } + color="secondary" + > + One more update + +
+ )} + + {submitAction} + + ) + }, +}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTTargetValidatorLimitsUpdate.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTTargetValidatorLimitsUpdate.tsx new file mode 100644 index 00000000..d49a33d7 --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTTargetValidatorLimitsUpdate.tsx @@ -0,0 +1,229 @@ +import { BigNumber, utils } from 'ethers' + +import { Fragment } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { ContractSDVTTargetValidatorLimitsUpdate } from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { CheckboxControl } from 'modules/shared/ui/Controls/Checkbox' +import { validateUintValue } from 'modules/motions/utils/validateUintValue' +import { NodeOperatorSelectControl } from 'modules/motions/ui/NodeOperatorSelectControl' +import { InputNumberControl } from 'modules/shared/ui/Controls/InputNumber' + +type NodeOperator = { + id: number | undefined + isTargetLimitActive: boolean + targetLimit: string +} + +const UINT_64_MAX = BigNumber.from('0xFFFFFFFFFFFFFFFF') + +export const formParts = createMotionFormPart({ + motionType: MotionType.SDVTTargetValidatorLimitsUpdate, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const sortedNodeOperators = formData.nodeOperators.sort( + (a, b) => Number(a.id) - Number(b.id), + ) + + const encodedCallData = new utils.AbiCoder().encode( + [ + 'tuple(uint256 nodeOperatorId, bool isTargetLimitActive, uint256 targetLimit)[]', + ], + [ + sortedNodeOperators.map(nodeOperator => ({ + nodeOperatorId: Number(nodeOperator.id), + isTargetLimitActive: nodeOperator.isTargetLimitActive, + targetLimit: Number(nodeOperator.targetLimit), + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { + id: undefined, + isTargetLimitActive: false, + targetLimit: '', + }, + ] as NodeOperator[], + }), + Component: ({ fieldNames, submitAction }) => { + const { walletAddress } = useWeb3() + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsDataLoading, + } = useSDVTNodeOperatorsList({ withSummary: true }) + + const trustedCaller = ContractSDVTTargetValidatorLimitsUpdate.useSwrWeb3( + 'trustedCaller', + [], + ) + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + const { watch, setValue } = useFormContext() + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!nodeOperatorsList?.length) { + return [] + } + + const selectedIds = selectedNodeOperators.map(({ id }) => id) + const thisId = selectedNodeOperators[fieldIdx]?.id + return nodeOperatorsList.filter( + ({ id }) => !selectedIds.includes(id) || id === thisId, + ) + } + + const handleAddUpdate = () => + fieldsArr.append({ + id: undefined, + isTargetLimitActive: false, + targetLimit: '', + } as NodeOperator) + + if (trustedCaller.initialLoading || isNodeOperatorsDataLoading) { + return + } + + if (trustedCaller.data !== walletAddress) { + return You should be connected as trusted caller + } + + if (!nodeOperatorsList?.length) { + return Node operator list is empty + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => { + const currentNodeOperator = + typeof selectedNodeOperators[fieldIndex].id === 'number' + ? nodeOperatorsList[selectedNodeOperators[fieldIndex].id!] + : null + + return ( + + + + {fieldsArr.fields.length > 1 && ( + + Update #{fieldIndex + 1} + + )} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIndex)} + > + Remove update {fieldIndex + 1} + + )} + + +
+ { + const nodeOperator = nodeOperatorsList[Number(value)] + + setValue( + `${fieldNames.nodeOperators}.${fieldIndex}.isTargetLimitActive`, + Boolean(nodeOperator.isTargetLimitActive), + ) + }} + /> +
+ +
+ +
+ +
+ + New limit (current limit is{' '} + {currentNodeOperator.targetValidatorsCount?.toString()} + ) + + ) : ( + `New limit` + ) + } + disabled={ + !selectedNodeOperators[fieldIndex]?.isTargetLimitActive + } + rules={{ + required: 'Field is required', + validate: value => { + const uintError = validateUintValue(value) + if (uintError) { + return uintError + } + + if (UINT_64_MAX.lt(value)) { + return `Value must be less than or equal to ${UINT_64_MAX}` + } + + return true + }, + }} + /> +
+
+
+ ) + })} + + {selectedNodeOperators.length < nodeOperatorsList.length && ( +
+ } + color="secondary" + > + One more update + +
+ )} + + {submitAction} + + ) + }, +}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTVettedValidatorsLimitsSet.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTVettedValidatorsLimitsSet.tsx new file mode 100644 index 00000000..0f089e6b --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewSDVTVettedValidatorsLimitsSet.tsx @@ -0,0 +1,202 @@ +import { utils } from 'ethers' + +import { Fragment } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { ContractSDVTVettedValidatorsLimitsSet } from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { InputNumberControl } from 'modules/shared/ui/Controls/InputNumber' +import { validateUintValue } from 'modules/motions/utils/validateUintValue' +import { NodeOperatorSelectControl } from 'modules/motions/ui/NodeOperatorSelectControl' + +type NodeOperator = { + id: string + vettedValidatorsLimit: string +} + +export const formParts = createMotionFormPart({ + motionType: MotionType.SDVTVettedValidatorsLimitsSet, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const sortedNodeOperators = formData.nodeOperators.sort( + (a, b) => Number(a.id) - Number(b.id), + ) + + const encodedCallData = new utils.AbiCoder().encode( + ['tuple(uint256 nodeOperatorId, uint256 stakingLimit)[]'], + [ + sortedNodeOperators.map(nodeOperator => ({ + nodeOperatorId: Number(nodeOperator.id), + stakingLimit: Number(nodeOperator.vettedValidatorsLimit), + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { + id: '', + vettedValidatorsLimit: '', + }, + ] as NodeOperator[], + }), + Component: ({ fieldNames, submitAction }) => { + const { walletAddress } = useWeb3() + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsDataLoading, + } = useSDVTNodeOperatorsList({ withSummary: true }) + + const nodeOperatorsWithValidators = nodeOperatorsList?.filter( + nodeOperator => nodeOperator.totalAddedValidators.gt(0), + ) + + const trustedCaller = ContractSDVTVettedValidatorsLimitsSet.useSwrWeb3( + 'trustedCaller', + [], + ) + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + const { watch } = useFormContext() + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!nodeOperatorsWithValidators?.length) return [] + const selectedIds = selectedNodeOperators.map(({ id }) => parseInt(id)) + const thisId = parseInt(selectedNodeOperators[fieldIdx]?.id) + return nodeOperatorsWithValidators.filter( + ({ id }) => !selectedIds.includes(id) || id === thisId, + ) + } + + const handleAddProgram = () => + fieldsArr.append({ + id: '', + vettedValidatorsLimit: '', + } as NodeOperator) + + if (trustedCaller.initialLoading || isNodeOperatorsDataLoading) { + return + } + + if (trustedCaller.data !== walletAddress) { + return You should be connected as trusted caller + } + + if (!nodeOperatorsList?.length || !nodeOperatorsWithValidators?.length) { + return ( + There are no node operators with validators yet + ) + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => { + return ( + + + + {fieldsArr.fields.length > 1 && ( + + Update #{fieldIndex + 1} + + )} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIndex)} + > + Remove update {fieldIndex + 1} + + )} + + +
+ +
+ +
+ { + const uintError = validateUintValue(value) + if (uintError) { + return uintError + } + + const nodeOperatorId = parseInt( + selectedNodeOperators[fieldIndex].id, + ) + + if (isNaN(nodeOperatorId)) { + return 'Select node operator first' + } + + const nodeOperator = nodeOperatorsList[nodeOperatorId] + + const { totalAddedValidators } = nodeOperator + + if (totalAddedValidators.lt(value)) { + return `Value must be less than or equal to ${totalAddedValidators}` + } + + return true + }, + }} + /> +
+
+
+ ) + })} + + {selectedNodeOperators.length < nodeOperatorsWithValidators.length && ( +
+ } + color="secondary" + > + One more update + +
+ )} + + {submitAction} + + ) + }, +}) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewTopUpWithLimits.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewTopUpWithLimits.tsx index a57fe10c..a5d23160 100644 --- a/modules/motions/ui/MotionFormStartNew/Parts/StartNewTopUpWithLimits.tsx +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewTopUpWithLimits.tsx @@ -31,10 +31,10 @@ import { import { ContractEvmLegoLDOTopUp, ContractEvmLegoDAITopUp, + ContractEvmGasFunderETHTopUp, ContractEvmRccDAITopUp, ContractEvmPmlDAITopUp, ContractEvmAtcDAITopUp, - ContractEvmGasFunderETHTopUp, } from 'modules/blockChain/contracts' import { MotionType } from 'modules/motions/types' import { createMotionFormPart } from './createMotionFormPart' @@ -43,7 +43,8 @@ import { estimateGasFallback, checkInputsGreaterThanLimit, } from 'modules/motions/utils' -import { tokenLimitError, periodLimitError } from 'modules/motions/constants' +import { periodLimitError } from 'modules/motions/constants' +import { validateTransitionLimit } from 'modules/motions/utils/validateTransitionLimit' export const TOPUP_WITH_LIMITS_MAP = { [MotionType.LegoLDOTopUp]: { @@ -54,6 +55,10 @@ export const TOPUP_WITH_LIMITS_MAP = { evmContract: ContractEvmLegoDAITopUp, motionType: MotionType.LegoDAITopUp, }, + [MotionType.GasFunderETHTopUp]: { + evmContract: ContractEvmGasFunderETHTopUp, + motionType: MotionType.GasFunderETHTopUp, + }, [MotionType.RccDAITopUp]: { evmContract: ContractEvmRccDAITopUp, motionType: MotionType.RccDAITopUp, @@ -66,10 +71,6 @@ export const TOPUP_WITH_LIMITS_MAP = { evmContract: ContractEvmAtcDAITopUp, motionType: MotionType.AtcDAITopUp, }, - [MotionType.GasFunderETHTopUp]: { - evmContract: ContractEvmGasFunderETHTopUp, - motionType: MotionType.GasFunderETHTopUp, - }, } type Program = { @@ -163,13 +164,15 @@ export const formParts = ({ ]) }, [fieldNames.programs, setValue, legoDAIRecipients.data]) - const { data: limits } = useTransitionLimits() + const { data: limits, initialLoading: isTransitionLimitsDataLoading } = + useTransitionLimits() const transitionLimit = token.address && limits?.[utils.getAddress(token.address)] if ( trustedCaller.initialLoading || legoDAIRecipients.initialLoading || + isTransitionLimitsDataLoading || periodLimitsLoading ) { return @@ -237,15 +240,18 @@ export const formParts = ({ rules={{ required: 'Field is required', validate: value => { - const check1 = validateToken(value) - if (typeof check1 === 'string') { - return check1 + const tokenError = validateToken(value) + if (tokenError) { + return tokenError } - if ( - transitionLimit && - Number(value) > transitionLimit - ) { - return tokenLimitError(token.label, transitionLimit) + + const transitionLimitError = validateTransitionLimit( + value, + transitionLimit, + token.label, + ) + if (transitionLimitError) { + return transitionLimitError } const isLargeThenPeriodLimit = diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartNewTopUpWithLimitsAndCustomToken.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartNewTopUpWithLimitsAndCustomToken.tsx new file mode 100644 index 00000000..140ccd99 --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartNewTopUpWithLimitsAndCustomToken.tsx @@ -0,0 +1,353 @@ +import { utils } from 'ethers' + +import { Fragment, useEffect, useMemo } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' +import { useRecipientActual, usePeriodLimitsData } from 'modules/motions/hooks' +import { useTransitionLimits } from 'modules/motions/hooks/useTransitionLimits' +import { + MotionLimitProgress, + MotionLimitProgressWrapper, +} from 'modules/motions/ui/MotionLimitProgress' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { InputNumberControl } from 'modules/shared/ui/Controls/InputNumber' +import { SelectControl, Option } from 'modules/shared/ui/Controls/Select' +import { MotionInfoBox } from 'modules/shared/ui/Common/MotionInfoBox' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, +} from '../CreateMotionFormStyle' + +import { + ContractEvmRccStablesTopUp, + ContractEvmPmlStablesTopUp, + ContractEvmAtcStablesTopUp, + ContractEvmSandboxStablesTopUp, +} from 'modules/blockChain/contracts' +import { MotionType } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { validateToken } from 'modules/tokens/utils/validateToken' +import { + estimateGasFallback, + checkInputsGreaterThanLimit, +} from 'modules/motions/utils' +import { periodLimitError } from 'modules/motions/constants' +import { useAllowedTokens } from 'modules/motions/hooks/useAllowedTokensRegistry' +import { Text } from 'modules/shared/ui/Common/Text' +import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' +import { DEFAULT_DECIMALS } from 'modules/blockChain/constants' +import { validateTransitionLimit } from 'modules/motions/utils/validateTransitionLimit' + +export const TOPUP_WITH_LIMITS_MAP = { + [MotionType.RccStablesTopUp]: { + evmContract: ContractEvmRccStablesTopUp, + motionType: MotionType.RccStablesTopUp, + }, + [MotionType.PmlStablesTopUp]: { + evmContract: ContractEvmPmlStablesTopUp, + motionType: MotionType.PmlStablesTopUp, + }, + [MotionType.AtcStablesTopUp]: { + evmContract: ContractEvmAtcStablesTopUp, + motionType: MotionType.AtcStablesTopUp, + }, + [MotionType.SandboxStablesTopUp]: { + evmContract: ContractEvmSandboxStablesTopUp, + motionType: MotionType.SandboxStablesTopUp, + }, +} + +type Program = { + address: string + amount: string +} + +export const formParts = ({ + registryType, +}: { + registryType: keyof typeof TOPUP_WITH_LIMITS_MAP +}) => + createMotionFormPart({ + motionType: TOPUP_WITH_LIMITS_MAP[registryType].motionType, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const encodedCallData = new utils.AbiCoder().encode( + ['address', 'address[]', 'uint256[]'], + [ + utils.getAddress(formData.tokenAddress), + formData.programs.map(p => utils.getAddress(p.address)), + formData.programs.map(p => + utils.parseUnits(p.amount, formData.tokenDecimals), + ), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + tokenAddress: '', + tokenDecimals: DEFAULT_DECIMALS, + programs: [{ address: '', amount: '' }] as Program[], + }), + Component: function StartNewMotionMotionFormLego({ + fieldNames, + submitAction, + }) { + const { walletAddress } = useWeb3() + const trustedCaller = TOPUP_WITH_LIMITS_MAP[ + registryType + ].evmContract.useSwrWeb3('trustedCaller', []) + const isTrustedCallerConnected = trustedCaller.data === walletAddress + + const { + allowedTokens, + tokensDecimalsMap, + initialLoading: isTokensDataLoading, + } = useAllowedTokens(registryType) + const { + data: periodLimitsData, + initialLoading: isPeriodLimitsDataLoading, + } = usePeriodLimitsData({ registryType }) + + const { + data: actualRecipients, + initialLoading: isRecipientsDataLoading, + } = useRecipientActual({ registryType }) + + const fieldsArr = useFieldArray({ name: fieldNames.programs }) + + const handleAddProgram = () => + fieldsArr.append({ address: '', amount: '' }) + + const { watch, setValue, trigger } = useFormContext() + const selectedPrograms: Program[] = watch(fieldNames.programs) + const selectedTokenAddress: string = watch(fieldNames.tokenAddress) + + const selectedTokenLabel = useMemo(() => { + if (!selectedTokenAddress || !allowedTokens?.length) { + return '' + } + return allowedTokens.find( + ({ address }) => address === selectedTokenAddress, + )?.label + }, [allowedTokens, selectedTokenAddress]) + + const newAmount = selectedPrograms.reduce( + (acc, program) => acc + Number(program.amount), + 0, + ) + + const getFilteredOptions = (fieldIdx: number) => { + if (!actualRecipients) return [] + const thatAddress = selectedPrograms[fieldIdx]?.address + const selectedAddresses = selectedPrograms.map(({ address }) => address) + return actualRecipients.filter( + ({ address }) => + !selectedAddresses.includes(address) || address === thatAddress, + ) + } + + useEffect(() => { + if (selectedTokenAddress) { + selectedPrograms.forEach((program, idx) => { + if (program.amount) { + trigger(`${fieldNames.programs}.${idx}.amount`) + } + }) + } + }, [fieldNames.programs, selectedPrograms, selectedTokenAddress, trigger]) + + useEffect(() => { + const recipientsCount = actualRecipients?.length || 0 + const isMoreThanOne = recipientsCount > 1 + + if (isMoreThanOne) return + + const recipientAddress = actualRecipients?.[0].address || '' + setValue(fieldNames.programs, [ + { address: recipientAddress, amount: '' }, + ]) + }, [fieldNames.programs, setValue, actualRecipients]) + + const { data: limits, initialLoading: isTransitionLimitsDataLoading } = + useTransitionLimits() + const transitionLimit = + selectedTokenAddress && limits + ? limits[utils.getAddress(selectedTokenAddress)] + : null + + if ( + trustedCaller.initialLoading || + isRecipientsDataLoading || + isPeriodLimitsDataLoading || + isTransitionLimitsDataLoading || + isTokensDataLoading + ) { + return + } + + if (!isTrustedCallerConnected) { + return ( + You should be connected as trusted caller + ) + } + + return ( + <> +
+ { + const tokenDecimals = tokensDecimalsMap?.[value] + if (tokenDecimals) { + setValue(fieldNames.tokenDecimals, tokenDecimals) + } + }} + > + {allowedTokens?.map((token, j) => ( + +
+ {selectedTokenAddress && ( + + + {selectedTokenLabel || 'Token'} address:{' '} + + + + )} + {periodLimitsData?.periodData && selectedTokenAddress && ( + + + + )} + + {fieldsArr.fields.map((item, fieldIdx) => ( + + + + Recipient #{fieldIdx + 1} + {fieldsArr.fields.length > 1 && ( + fieldsArr.remove(fieldIdx)} + > + Remove recipient {fieldIdx + 1} + + )} + + {periodLimitsData?.isEndInNextPeriod && ( + + The motion is ending in the next period. The transfer limit + would be replenished by that time. + + )} +
+ + {getFilteredOptions(fieldIdx).map((program, j) => ( + +
+ +
+ { + const tokenError = validateToken(value) + if (tokenError) { + return tokenError + } + + const transitionLimitError = validateTransitionLimit( + value, + transitionLimit, + selectedTokenLabel, + ) + if (transitionLimitError) { + return transitionLimitError + } + + const isLargeThenPeriodLimit = + checkInputsGreaterThanLimit({ + inputValues: selectedPrograms, + spendableBalanceInPeriod: Number( + periodLimitsData?.periodData + .spendableBalanceInPeriod, + ), + currentValue: { value, index: fieldIdx }, + }) + + if ( + periodLimitsData?.periodData + .spendableBalanceInPeriod && + isLargeThenPeriodLimit + ) { + return periodLimitError() + } + return true + }, + }} + /> +
+
+
+ ))} + + {actualRecipients && + fieldsArr.fields.length < actualRecipients.length && ( +
+ } + color="secondary" + > + One more recipient + +
+ )} + + {submitAction} + + ) + }, + }) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/StartSDVTNodeOperatorsAdd.tsx b/modules/motions/ui/MotionFormStartNew/Parts/StartSDVTNodeOperatorsAdd.tsx new file mode 100644 index 00000000..8b77549f --- /dev/null +++ b/modules/motions/ui/MotionFormStartNew/Parts/StartSDVTNodeOperatorsAdd.tsx @@ -0,0 +1,347 @@ +import { utils } from 'ethers' + +import { Fragment, useEffect, useMemo } from 'react' +import { useFieldArray, useFormContext } from 'react-hook-form' +import { Plus, ButtonIcon } from '@lidofinance/lido-ui' + +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' + +import { PageLoader } from 'modules/shared/ui/Common/PageLoader' +import { InputControl } from 'modules/shared/ui/Controls/Input' +import { + Fieldset, + MessageBox, + RemoveItemButton, + FieldsWrapper, + FieldsHeader, + FieldsHeaderDesc, + ErrorBox, +} from '../CreateMotionFormStyle' + +import { ContractSDVTNodeOperatorsAdd } from 'modules/blockChain/contracts' + +import { MotionTypeForms } from 'modules/motions/types' +import { createMotionFormPart } from './createMotionFormPart' +import { estimateGasFallback } from 'modules/motions/utils' +import { + useSDVTOperatorNameLimit, + useSDVTOperatorsCounts, +} from 'modules/motions/hooks' +import { STETH } from 'modules/blockChain/contractAddresses' +import { checkAddressForManageSigningKeysRole } from 'modules/motions/utils/checkAddressManagerRole' +import { useSDVTNodeOperatorsList } from 'modules/motions/hooks/useSDVTNodeOperatorsList' +import { validateNodeOperatorName } from 'modules/motions/utils/validateNodeOperatorName' +import { validateAddress } from 'modules/motions/utils/validateAddress' + +type NodeOperator = { + name: string + rewardAddress: string + managerAddress: string +} + +// DONE: The current number of node operators in the registry MUST be equal to the _nodeOperatorsCount +// DONE: (exec also) The total number of node operators in the registry, after adding the new ones, MUST NOT exceed nodeOperatorsRegistry.MAX_NODE_OPERATORS_COUNT() +// DONE: Manager addresses MUST NOT have duplicates +// DONE: Manager addresses MUST NOT be used as managers for previously added node operators +// DONE: Reward addresses of newly added node operators MUST NOT contain the address of the stETH token +// DONE: Reward addresses of newly added node operators MUST NOT contain zero addresses +// DONE: The names of newly added node operators MUST NOT be an empty string +// DONE: The name lengths of each newly added node operator MUST NOT exceed the nodeOperatorsRegistry.MAX_NODE_OPERATOR_NAME_LENGTH() +export const formParts = () => + createMotionFormPart({ + motionType: MotionTypeForms.SDVTNodeOperatorsAdd, + populateTx: async ({ evmScriptFactory, formData, contract }) => { + const encodedCallData = new utils.AbiCoder().encode( + [ + 'uint256 nodeOperatorsCount', + 'tuple(string name, address rewardAddress, address managerAddress)[]', + ], + [ + formData.nodeOperatorsCount, + formData.nodeOperators.map(item => ({ + name: item.name, + rewardAddress: utils.getAddress(item.rewardAddress), + managerAddress: utils.getAddress(item.managerAddress), + })), + ], + ) + const gasLimit = await estimateGasFallback( + contract.estimateGas.createMotion(evmScriptFactory, encodedCallData), + ) + const tx = await contract.populateTransaction.createMotion( + evmScriptFactory, + encodedCallData, + { gasLimit }, + ) + return tx + }, + getDefaultFormData: () => ({ + nodeOperators: [ + { name: '', rewardAddress: '', managerAddress: '' }, + ] as NodeOperator[], + nodeOperatorsCount: NaN, + }), + Component: ({ fieldNames, submitAction }) => { + const { setValue, watch } = useFormContext() + const { walletAddress, chainId } = useWeb3() + const trustedCaller = ContractSDVTNodeOperatorsAdd.useSwrWeb3( + 'trustedCaller', + [], + ) + const isTrustedCallerConnected = trustedCaller.data === walletAddress + + const { + data: nodeOperatorsList, + initialLoading: isNodeOperatorsListLoading, + } = useSDVTNodeOperatorsList() + const { + data: maxNodeOperatorNameLength, + initialLoading: NONameLengthLoading, + } = useSDVTOperatorNameLimit() + const { data: NOCounts, initialLoading: maxOperatorsLoading } = + useSDVTOperatorsCounts() + + const fieldsArr = useFieldArray({ name: fieldNames.nodeOperators }) + const selectedNodeOperators: NodeOperator[] = watch( + fieldNames.nodeOperators, + ) + + useEffect(() => { + if (typeof NOCounts?.current === 'number') { + setValue(fieldNames.nodeOperatorsCount, NOCounts.current) + } + }, [setValue, NOCounts, fieldNames.nodeOperatorsCount]) + + const nodeOperatorsDetailsMaps = useMemo(() => { + const result: Record< + 'name' | 'rewardAddress' | 'managerAddress', + Record + > = { name: {}, rewardAddress: {}, managerAddress: {} } + if (!nodeOperatorsList) return result + + for (const nodeOperator of nodeOperatorsList) { + result['name'][nodeOperator.name] = nodeOperator.id + result['rewardAddress'][nodeOperator.rewardAddress] = nodeOperator.id + if (nodeOperator.managerAddress) { + result['managerAddress'][nodeOperator.managerAddress] = + nodeOperator.id + } + } + + return result + }, [nodeOperatorsList]) + + const handleAddNodeOperators = () => + fieldsArr.append({ + name: '', + rewardAddress: '', + managerAddress: '', + } as NodeOperator) + + const handleRemoveNodeOperator = (fieldIndex: number) => + fieldsArr.remove(fieldIndex) + + if ( + trustedCaller.initialLoading || + NONameLengthLoading || + maxOperatorsLoading || + isNodeOperatorsListLoading + ) { + return + } + + if (!isTrustedCallerConnected) { + return ( + You should be connected as trusted caller + ) + } + + if (!NOCounts) { + return Cannot load node operators count data + } + + if (NOCounts.current >= NOCounts.max) { + return Node operators limit reached + } + + return ( + <> + {fieldsArr.fields.map((item, fieldIndex) => ( + + + + {fieldsArr.fields.length > 1 && ( + + NodeOperator #{NOCounts.current + fieldIndex} + + )} + {fieldsArr.fields.length > 1 && ( + handleRemoveNodeOperator(fieldIndex)} + > + Remove node operator {NOCounts.current + fieldIndex} + + )} + + +
+ { + const nameErr = validateNodeOperatorName( + value, + maxNodeOperatorNameLength, + ) + if (nameErr) { + return nameErr + } + + const idInNameMap = + nodeOperatorsDetailsMaps['name'][value] + + if (typeof idInNameMap === 'number') { + return 'Name must not be in use by another node operator' + } + + const nameInSelectedNodeOperatorsIndex = + selectedNodeOperators.findIndex( + ({ name }, index) => + name.toLowerCase() === value.toLowerCase() && + fieldIndex !== index, + ) + + if (nameInSelectedNodeOperatorsIndex !== -1) { + return 'Name is already in use by another update' + } + + return true + }, + }} + /> +
+ +
+ { + const addressErr = validateAddress(value) + if (addressErr) { + return addressErr + } + + const valueAddress = utils.getAddress(value) + const stETHAddress = STETH[chainId] + + if ( + stETHAddress && + valueAddress === utils.getAddress(stETHAddress) + ) { + return 'Address must not be stETH address' + } + + const idInAddressMap = + nodeOperatorsDetailsMaps['rewardAddress'][ + valueAddress + ] + + if (typeof idInAddressMap === 'number') { + return 'Address must not be in use by another node operator' + } + + const addressInSelectedNodeOperatorsIndex = + selectedNodeOperators.findIndex( + ({ rewardAddress }, index) => + rewardAddress && + utils.getAddress(rewardAddress) === + valueAddress && + fieldIndex !== index, + ) + + if (addressInSelectedNodeOperatorsIndex !== -1) { + return 'Address is already in use by another update' + } + + return true + }, + }} + /> +
+ +
+ { + const addressErr = validateAddress(value) + if (addressErr) { + return addressErr + } + + const valueAddress = utils.getAddress(value) + const idInAddressMap = + nodeOperatorsDetailsMaps['managerAddress'][ + valueAddress + ] + + if (typeof idInAddressMap === 'number') { + return 'Address must not be in use by another node operator' + } + + const addressInSelectedNodeOperatorsIndex = + selectedNodeOperators.findIndex( + ({ managerAddress }, index) => + managerAddress && + utils.getAddress(managerAddress) === + valueAddress && + fieldIndex !== index, + ) + + if (addressInSelectedNodeOperatorsIndex !== -1) { + return 'Address is already in use by another update' + } + + const isAlreadyManager = + await checkAddressForManageSigningKeysRole( + value, + chainId, + ) + + if (isAlreadyManager) { + return 'Address already has a signing keys manager role' + } + return true + }, + }} + /> +
+
+
+ ))} + {NOCounts.max > fieldsArr.fields.length + NOCounts.current && ( +
+ } + color="secondary" + > + One more node operator + +
+ )} + + {submitAction} + + ) + }, + }) diff --git a/modules/motions/ui/MotionFormStartNew/Parts/index.ts b/modules/motions/ui/MotionFormStartNew/Parts/index.ts index cf5e4ede..efe474e8 100644 --- a/modules/motions/ui/MotionFormStartNew/Parts/index.ts +++ b/modules/motions/ui/MotionFormStartNew/Parts/index.ts @@ -1,13 +1,25 @@ import { MotionTypeForms } from 'modules/motions/types' -import * as formNodeOperators from './StartNewNodeOperators' import * as formAllowedRecipientAdd from './StartNewAllowedRecipientAdd' import * as formAllowedRecipientRemove from './StartNewAllowedRecipientRemove' import * as formAllowedRecipientTopUp from './StartNewAllowedRecipientTopUp' import * as StartNewTopUpWithLimits from './StartNewTopUpWithLimits' +import * as StartNewTopUpWithLimitsAndCustomToken from './StartNewTopUpWithLimitsAndCustomToken' +import * as StartSDVTNodeOperatorsAdd from './StartSDVTNodeOperatorsAdd' +import * as StartNewSDVTNodeOperatorsActivate from './StartNewSDVTNodeOperatorsActivate' +import * as StartNewSDVTNodeOperatorsDeactivate from './StartNewSDVTNodeOperatorsDeactivate' +import * as StartNewSDVTVettedValidatorsLimitsSet from './StartNewSDVTVettedValidatorsLimitsSet' +import * as StartNewSDVTTargetValidatorLimitsUpdate from './StartNewSDVTTargetValidatorLimitsUpdate' +import * as StartNewSDVTNodeOperatorRewardAddressesSet from './StartNewSDVTNodeOperatorRewardAddressesSet' +import * as StartNewSDVTNodeOperatorNamesSet from './StartNewSDVTNodeOperatorNamesSet' +import * as StartNewSDVTNodeOperatorManagersChange from './StartNewSDVTNodeOperatorManagersChange' +import * as StartNewNodeOperatorLimitIncrease from './StartNewNodeOperatorLimitIncrease' export const formParts = { - [MotionTypeForms.NodeOperatorIncreaseLimit]: formNodeOperators.formParts, + [MotionTypeForms.NodeOperatorIncreaseLimit]: + StartNewNodeOperatorLimitIncrease.formParts({ + motionType: MotionTypeForms.NodeOperatorIncreaseLimit, + }), [MotionTypeForms.AllowedRecipientTopUpTrpLdo]: formAllowedRecipientTopUp.formParts({ registryType: MotionTypeForms.AllowedRecipientTopUpTrpLdo, @@ -18,15 +30,18 @@ export const formParts = { [MotionTypeForms.LegoDAITopUp]: StartNewTopUpWithLimits.formParts({ registryType: MotionTypeForms.LegoDAITopUp, }), - [MotionTypeForms.RccDAITopUp]: StartNewTopUpWithLimits.formParts({ - registryType: MotionTypeForms.RccDAITopUp, - }), - [MotionTypeForms.PmlDAITopUp]: StartNewTopUpWithLimits.formParts({ - registryType: MotionTypeForms.PmlDAITopUp, - }), - [MotionTypeForms.AtcDAITopUp]: StartNewTopUpWithLimits.formParts({ - registryType: MotionTypeForms.AtcDAITopUp, - }), + [MotionTypeForms.RccStablesTopUp]: + StartNewTopUpWithLimitsAndCustomToken.formParts({ + registryType: MotionTypeForms.RccStablesTopUp, + }), + [MotionTypeForms.PmlStablesTopUp]: + StartNewTopUpWithLimitsAndCustomToken.formParts({ + registryType: MotionTypeForms.PmlStablesTopUp, + }), + [MotionTypeForms.AtcStablesTopUp]: + StartNewTopUpWithLimitsAndCustomToken.formParts({ + registryType: MotionTypeForms.AtcStablesTopUp, + }), [MotionTypeForms.StethRewardProgramAdd]: formAllowedRecipientAdd.formParts({ registryType: MotionTypeForms.StethRewardProgramAdd, }), @@ -58,6 +73,44 @@ export const formParts = { formAllowedRecipientTopUp.formParts({ registryType: MotionTypeForms.RewardsShareProgramTopUp, }), + [MotionTypeForms.SDVTNodeOperatorsAdd]: StartSDVTNodeOperatorsAdd.formParts(), + [MotionTypeForms.SDVTNodeOperatorsActivate]: + StartNewSDVTNodeOperatorsActivate.formParts, + [MotionTypeForms.SDVTNodeOperatorsDeactivate]: + StartNewSDVTNodeOperatorsDeactivate.formParts, + [MotionTypeForms.SDVTVettedValidatorsLimitsSet]: + StartNewSDVTVettedValidatorsLimitsSet.formParts, + [MotionTypeForms.SDVTTargetValidatorLimitsUpdate]: + StartNewSDVTTargetValidatorLimitsUpdate.formParts, + [MotionTypeForms.SDVTNodeOperatorRewardAddressesSet]: + StartNewSDVTNodeOperatorRewardAddressesSet.formParts, + [MotionTypeForms.SDVTNodeOperatorNamesSet]: + StartNewSDVTNodeOperatorNamesSet.formParts, + [MotionTypeForms.SDVTNodeOperatorManagerChange]: + StartNewSDVTNodeOperatorManagersChange.formParts, + [MotionTypeForms.SandboxNodeOperatorIncreaseLimit]: + StartNewNodeOperatorLimitIncrease.formParts({ + motionType: MotionTypeForms.SandboxNodeOperatorIncreaseLimit, + }), + [MotionTypeForms.SandboxStablesAdd]: formAllowedRecipientAdd.formParts({ + registryType: MotionTypeForms.SandboxStablesAdd, + }), + [MotionTypeForms.SandboxStablesRemove]: formAllowedRecipientRemove.formParts({ + registryType: MotionTypeForms.SandboxStablesRemove, + }), + [MotionTypeForms.SandboxStablesTopUp]: + StartNewTopUpWithLimitsAndCustomToken.formParts({ + registryType: MotionTypeForms.SandboxStablesTopUp, + }), + [MotionTypeForms.RccDAITopUp]: StartNewTopUpWithLimits.formParts({ + registryType: MotionTypeForms.RccDAITopUp, + }), + [MotionTypeForms.AtcDAITopUp]: StartNewTopUpWithLimits.formParts({ + registryType: MotionTypeForms.AtcDAITopUp, + }), + [MotionTypeForms.PmlDAITopUp]: StartNewTopUpWithLimits.formParts({ + registryType: MotionTypeForms.PmlDAITopUp, + }), } as const export type FormData = { diff --git a/modules/motions/ui/NodeOperatorSelectControl/NodeOperatorSelectControl.tsx b/modules/motions/ui/NodeOperatorSelectControl/NodeOperatorSelectControl.tsx new file mode 100644 index 00000000..52dd4dd8 --- /dev/null +++ b/modules/motions/ui/NodeOperatorSelectControl/NodeOperatorSelectControl.tsx @@ -0,0 +1,43 @@ +import { Option } from '@lidofinance/lido-ui' +import { SelectControl } from 'modules/shared/ui/Controls/Select' +import { withFormController } from 'modules/shared/hocs/withFormController' + +type SelectOption = { + id: number + name: string +} + +const MAX_NAME_LENGTH = 45 + +const getOptionLabel = (option: SelectOption) => + `${option.name.slice(0, MAX_NAME_LENGTH)}${ + option.name.length > MAX_NAME_LENGTH ? '...' : '' + } (id: ${option.id})` + +type SelectControlProps = React.ComponentProps< + ReturnType +> + +type Props = Omit & { + options: SelectOption[] +} + +export function NodeOperatorSelectControl(props: Props) { + const { options, ...selectControlProps } = props + + return ( + + {options.map(nodeOperator => ( + + ) +} diff --git a/modules/motions/ui/NodeOperatorSelectControl/index.ts b/modules/motions/ui/NodeOperatorSelectControl/index.ts new file mode 100644 index 00000000..2a7c8f7b --- /dev/null +++ b/modules/motions/ui/NodeOperatorSelectControl/index.ts @@ -0,0 +1 @@ +export * from './NodeOperatorSelectControl' diff --git a/modules/motions/utils/checkAddressManagerRole.ts b/modules/motions/utils/checkAddressManagerRole.ts new file mode 100644 index 00000000..80e83d1c --- /dev/null +++ b/modules/motions/utils/checkAddressManagerRole.ts @@ -0,0 +1,37 @@ +import { CHAINS } from '@lido-sdk/constants' +import { utils } from 'ethers' +import { + ContractAragonAcl, + ContractSDVTRegistry, +} from 'modules/blockChain/contracts' + +export async function checkIsAddressManagerOfNodeOperator( + address: string, + nodeOperatorId: string, + chainId: CHAINS, +) { + try { + const sdvtRegistry = ContractSDVTRegistry.connectRpc({ chainId }) + const role = await sdvtRegistry.MANAGE_SIGNING_KEYS() + return sdvtRegistry.canPerform(utils.getAddress(address), role, [ + parseInt(nodeOperatorId), + ]) + } catch (error) { + return false + } +} + +export const checkAddressForManageSigningKeysRole = async ( + address: string, + chainId: CHAINS, +) => { + const sdvtRegistry = ContractSDVTRegistry.connectRpc({ chainId }) + const contractAragonAcl = ContractAragonAcl.connectRpc({ chainId }) + const MANAGE_SIGNING_KEYS_ROLE = await sdvtRegistry.MANAGE_SIGNING_KEYS() + const result = await contractAragonAcl.getPermissionParamsLength( + utils.getAddress(address), + sdvtRegistry.address, + MANAGE_SIGNING_KEYS_ROLE, + ) + return !result.isZero() +} diff --git a/modules/motions/utils/connectTokenContract.ts b/modules/motions/utils/connectTokenContract.ts new file mode 100644 index 00000000..910a974f --- /dev/null +++ b/modules/motions/utils/connectTokenContract.ts @@ -0,0 +1,10 @@ +import { CHAINS } from '@lido-sdk/constants' +import { getStaticRpcBatchProvider } from '@lido-sdk/providers' +import { Erc20Abi__factory } from 'generated' +import { getBackendRpcUrl } from 'modules/blockChain/utils/getBackendRpcUrl' + +export function connectERC20Contract(contractAddress: string, chainId: CHAINS) { + const library = getStaticRpcBatchProvider(chainId, getBackendRpcUrl(chainId)) + + return Erc20Abi__factory.connect(contractAddress, library) +} diff --git a/modules/motions/utils/getManagerAddressesMap.ts b/modules/motions/utils/getManagerAddressesMap.ts new file mode 100644 index 00000000..ae9bfefc --- /dev/null +++ b/modules/motions/utils/getManagerAddressesMap.ts @@ -0,0 +1,38 @@ +import { utils } from 'ethers' +import { CHAINS } from '@lido-sdk/constants' +import { ContractAragonAcl } from 'modules/blockChain/contracts' + +// Event ABI for ACL's SetPermissionParams event +const ACL_EVENT_ABI = [ + 'event SetPermissionParams(address indexed entity, address indexed app, bytes32 indexed role, bytes32 paramsHash)', +] +const ACL_INTERFACE = new utils.Interface(ACL_EVENT_ABI) + +/* + This function is used to get the list of managers for the Simple DVT node operators + using ACL's SetPermissionParams event parsing. To get a manager address + for a given node operator id, we need to get the permission param for that node + +*/ +export const getManagerAddressesMap = async ( + registryAddress: string, + signingKeysRole: string, + chainId: CHAINS, +) => { + const aclContract = ContractAragonAcl.connectRpc({ chainId }) + + const eventFilter = aclContract.filters.SetPermissionParams( + null, + registryAddress, + signingKeysRole, + ) + + const rawEvents = await aclContract.queryFilter(eventFilter) + return rawEvents.reduce((result, event) => { + const parsedEvent = ACL_INTERFACE.parseLog(event) + + result[parsedEvent.args.paramsHash] = parsedEvent.args.entity + + return result + }, {} as Record) +} diff --git a/modules/motions/utils/getMotionProgress.ts b/modules/motions/utils/getMotionProgress.ts index 73b7b2da..0595084c 100644 --- a/modules/motions/utils/getMotionProgress.ts +++ b/modules/motions/utils/getMotionProgress.ts @@ -7,7 +7,8 @@ export function getMotionProgress(motion: Motion, totalSupply: BigNumber) { const totalSupplyNumber = Number(formatEther(totalSupply)) const objectionsAmount = Number(formatEther(motion.objectionsAmount)) const thresholdAmount = (totalSupplyNumber * thresholdPct) / 100 - const objectionsPct = (objectionsAmount / thresholdAmount) * 100 + // if thresholdAmount is 0, use 1 to avoid division by 0 + const objectionsPct = (objectionsAmount / (thresholdAmount || 1)) * 100 const onlyZeros = Math.ceil(1 - Math.log10(objectionsPct)) const objectionsPctFormatted = diff --git a/modules/motions/utils/getMotionType.ts b/modules/motions/utils/getMotionType.ts index bcc3e9e8..752980dc 100644 --- a/modules/motions/utils/getMotionType.ts +++ b/modules/motions/utils/getMotionType.ts @@ -23,9 +23,11 @@ export const getMotionTypeByScriptFactory = ( scriptFactory: string, ): MotionType | EvmUnrecognized => { try { - return EvmTypesByAdress[parseEvmSupportedChainId(chainId)][ - parseScriptFactory(chainId, scriptFactory) - ] + return ( + EvmTypesByAdress[parseEvmSupportedChainId(chainId)][ + parseScriptFactory(chainId, scriptFactory) + ] ?? EvmUnrecognized + ) } catch { return EvmUnrecognized } diff --git a/modules/motions/utils/getMotionTypeDisplayName.ts b/modules/motions/utils/getMotionTypeDisplayName.ts index 8c10a30b..7ebba4bd 100644 --- a/modules/motions/utils/getMotionTypeDisplayName.ts +++ b/modules/motions/utils/getMotionTypeDisplayName.ts @@ -10,9 +10,9 @@ export const MotionTypeDisplayNames: Record< [MotionType.AllowedRecipientTopUpTrpLdo]: 'Top up LDO TRP', [MotionType.LegoLDOTopUp]: 'Top up LEGO LDO', [MotionType.LegoDAITopUp]: 'Top up LEGO DAI', - [MotionType.RccDAITopUp]: 'Top up RCC DAI', - [MotionType.PmlDAITopUp]: 'Top up PML DAI', - [MotionType.AtcDAITopUp]: 'Top up ATC DAI', + [MotionType.RccStablesTopUp]: 'Top up RCC', + [MotionType.PmlStablesTopUp]: 'Top up PML', + [MotionType.AtcStablesTopUp]: 'Top up ATC', [MotionType.StethRewardProgramAdd]: 'Add stETH reward program', [MotionType.StethRewardProgramRemove]: 'Remove stETH reward program', [MotionType.StethRewardProgramTopUp]: 'Top up stETH reward programs', @@ -24,6 +24,20 @@ export const MotionTypeDisplayNames: Record< 'Remove Rewards Share Program participant', [MotionType.RewardsShareProgramTopUp]: 'Top up Rewards Share Program participants', + [MotionType.SDVTNodeOperatorsAdd]: 'Add node operators', + [MotionType.SDVTNodeOperatorsActivate]: 'Activate node operators', + [MotionType.SDVTNodeOperatorsDeactivate]: 'Deactivate node operators', + [MotionType.SDVTVettedValidatorsLimitsSet]: 'Set vetted validators limits', + [MotionType.SDVTTargetValidatorLimitsUpdate]: + 'Update target validator limits', + [MotionType.SDVTNodeOperatorRewardAddressesSet]: + 'Set node operators reward addresses', + [MotionType.SDVTNodeOperatorManagerChange]: 'Change node operators managers', + [MotionType.SDVTNodeOperatorNamesSet]: 'Set node operators names', + [MotionType.SDVTNodeOperatorManagerChange]: 'Change node operators managers', + [MotionType.SandboxNodeOperatorIncreaseLimit]: + '[NOR SandBox] Increase node operator staking limit', + [EvmUnrecognized]: 'Unrecognized evm factory', // next motion types are retired @@ -42,6 +56,12 @@ export const MotionTypeDisplayNames: Record< [MotionType.AllowedRecipientAddReferralDai]: 'Add DAI referral partner', [MotionType.AllowedRecipientRemoveReferralDai]: 'Remove DAI referral partner', [MotionType.AllowedRecipientTopUpReferralDai]: 'Top up DAI referral partner', + [MotionType.RccDAITopUp]: 'Top up RCC DAI', + [MotionType.PmlDAITopUp]: 'Top up PML DAI', + [MotionType.AtcDAITopUp]: 'Top up ATC DAI', + [MotionType.SandboxStablesTopUp]: 'Top up sandbox stables', + [MotionType.SandboxStablesAdd]: 'Add sandbox stables recipient', + [MotionType.SandboxStablesRemove]: 'Remove sandbox stables recipient', } as const export function getMotionTypeDisplayName( diff --git a/modules/motions/utils/getNodeOperatorRegistryType.ts b/modules/motions/utils/getNodeOperatorRegistryType.ts new file mode 100644 index 00000000..eca5755b --- /dev/null +++ b/modules/motions/utils/getNodeOperatorRegistryType.ts @@ -0,0 +1,10 @@ +import { + IncreaseLimitMotionType, + INCREASE_LIMIT_MOTION_MAP, +} from '../constants' + +export function getNodeOperatorRegistryType( + motionType: IncreaseLimitMotionType, +) { + return INCREASE_LIMIT_MOTION_MAP[motionType].registryType +} diff --git a/modules/motions/utils/validateAddress.ts b/modules/motions/utils/validateAddress.ts new file mode 100644 index 00000000..b1e68f95 --- /dev/null +++ b/modules/motions/utils/validateAddress.ts @@ -0,0 +1,13 @@ +import { constants, utils } from 'ethers' + +export function validateAddress(value: string): string | null { + if (!utils.isAddress(value)) { + return 'Address is not valid' + } + + if (value.toLowerCase() === constants.AddressZero) { + return 'Address must not be zero address' + } + + return null +} diff --git a/modules/motions/utils/validateNodeOperatorName.ts b/modules/motions/utils/validateNodeOperatorName.ts new file mode 100644 index 00000000..7321024a --- /dev/null +++ b/modules/motions/utils/validateNodeOperatorName.ts @@ -0,0 +1,18 @@ +import { BigNumber } from 'ethers' + +export function validateNodeOperatorName( + value: string, + maxNameLength: BigNumber | undefined, +) { + try { + if (!value.trim().length) { + return 'Name must not be empty' + } + + if (maxNameLength?.lt(value.length)) { + return `Name length must be less or equal than ${maxNameLength} characters` + } + } catch (error) { + return 'Unable to parse value' + } +} diff --git a/modules/motions/utils/validateTransitionLimit.ts b/modules/motions/utils/validateTransitionLimit.ts new file mode 100644 index 00000000..16967469 --- /dev/null +++ b/modules/motions/utils/validateTransitionLimit.ts @@ -0,0 +1,21 @@ +export const validateTransitionLimit = ( + value: string, + limit: number | '' | null | undefined, + tokenLabel: string | undefined, +): string | null => { + if (typeof limit !== 'number') { + if (!tokenLabel) { + return 'Transition limit is not defined' + } + + return `Transition limit for ${tokenLabel} is not defined` + } + + if (Number(value) > limit) { + return `${ + tokenLabel ?? 'Token' + } transition is limited by ${limit.toLocaleString()}` + } + + return null +} diff --git a/modules/motions/utils/validateUintValue.ts b/modules/motions/utils/validateUintValue.ts new file mode 100644 index 00000000..dddade4a --- /dev/null +++ b/modules/motions/utils/validateUintValue.ts @@ -0,0 +1,15 @@ +import { BigNumber, utils } from 'ethers' + +export const validateUintValue = (value: string | undefined): string | null => { + try { + BigNumber.from(value) + const parsedValue = utils.parseEther(value ?? '') + if (parsedValue.isNegative()) { + return 'Value must not be negative' + } + } catch (error) { + return 'Unable to parse value' + } + + return null +} diff --git a/modules/network/utils/fetcherStandard.ts b/modules/network/utils/fetcherStandard.ts index 3ed87a09..26efd215 100644 --- a/modules/network/utils/fetcherStandard.ts +++ b/modules/network/utils/fetcherStandard.ts @@ -15,7 +15,6 @@ export const fetcherStandard: FetcherStandard = async ( params = DEFAULT_PARAMS, ) => { const response = await fetch(url, params) - if (!response.ok) { throw new Error('An error occurred while fetching the data.') } diff --git a/modules/network/utils/urlsApi.ts b/modules/network/utils/urlsApi.ts index a3f9c0a8..0b741d76 100644 --- a/modules/network/utils/urlsApi.ts +++ b/modules/network/utils/urlsApi.ts @@ -1,2 +1,6 @@ -export const nodeOperatorsKeysInfo = (chainId: number) => - `/api/node-operators/keys-info?chainId=${chainId}` +export const nodeOperatorsKeysInfo = ( + chainId: number, + walletAddress: string, + moduleAddress: string, +) => + `/api/node-operators/keys-info?chainId=${chainId}&walletAddress=${walletAddress}&moduleAddress=${moduleAddress}` diff --git a/modules/shared/hocs/withFormController.tsx b/modules/shared/hocs/withFormController.tsx index ee695989..f1d27373 100644 --- a/modules/shared/hocs/withFormController.tsx +++ b/modules/shared/hocs/withFormController.tsx @@ -8,6 +8,7 @@ type Rules = ControllerProps['rules'] type WrapperProps = { name: string rules?: Rules + onChange?: (e: any) => void } type InjectedProps = { @@ -24,7 +25,7 @@ export function withFormController( type WrappedProps = Subtract & WrapperProps function FormController(props: WrappedProps) { - const { name, rules, ...restProps } = props + const { name, rules, onChange, ...restProps } = props const { control } = useFormContext() return ( @@ -38,8 +39,12 @@ export function withFormController( ref={args.field.ref} name={args.field.name} value={args.field.value} + checked={args.field.value} onBlur={args.field.onBlur} - onChange={args.field.onChange} + onChange={(e: any) => { + onChange?.(e) + args.field.onChange(e) + }} error={args.fieldState.error?.message} /> )} diff --git a/modules/shared/ui/Common/AddressInlineWithPop/AddressInlineWithPop.tsx b/modules/shared/ui/Common/AddressInlineWithPop/AddressInlineWithPop.tsx index 18486a56..89ba6c4e 100644 --- a/modules/shared/ui/Common/AddressInlineWithPop/AddressInlineWithPop.tsx +++ b/modules/shared/ui/Common/AddressInlineWithPop/AddressInlineWithPop.tsx @@ -4,12 +4,13 @@ import { Wrap } from './AddressInlineWithPopStyle' type Props = { address: string + trim?: boolean } -export function AddressInlineWithPop({ address }: Props) { +export function AddressInlineWithPop({ address, trim = true }: Props) { return ( - + ) } diff --git a/modules/shared/ui/Controls/Checkbox/Checkbox.tsx b/modules/shared/ui/Controls/Checkbox/Checkbox.tsx new file mode 100644 index 00000000..0a12d094 --- /dev/null +++ b/modules/shared/ui/Controls/Checkbox/Checkbox.tsx @@ -0,0 +1,4 @@ +import { Checkbox } from '@lidofinance/lido-ui' +import { withFormController } from 'modules/shared/hocs/withFormController' + +export const CheckboxControl = withFormController(Checkbox) diff --git a/modules/shared/ui/Controls/Checkbox/index.ts b/modules/shared/ui/Controls/Checkbox/index.ts new file mode 100644 index 00000000..0dab1156 --- /dev/null +++ b/modules/shared/ui/Controls/Checkbox/index.ts @@ -0,0 +1 @@ +export * from './Checkbox' diff --git a/modules/shared/utils/sanitize.ts b/modules/shared/utils/sanitize.ts index ade6eeae..6956c623 100644 --- a/modules/shared/utils/sanitize.ts +++ b/modules/shared/utils/sanitize.ts @@ -1,8 +1,13 @@ import getConfig from 'next/config' const { serverRuntimeConfig } = getConfig() -const { infuraApiKey, alchemyApiKey, subgraphMainnet, subgraphGoerli } = - serverRuntimeConfig +const { + infuraApiKey, + alchemyApiKey, + subgraphMainnet, + subgraphGoerli, + subgraphHolesky, +} = serverRuntimeConfig const SECRETS = { INFURA_API_KEY: infuraApiKey ? new RegExp(infuraApiKey, 'ig') : null, @@ -11,6 +16,7 @@ const SECRETS = { ENS_ADDRESS: new RegExp('[a-zA-Z.]+\\.eth', 'gi'), SUBGRAPH_MAINNET: subgraphMainnet ? new RegExp(subgraphMainnet, 'ig') : null, SUBGRAPH_GOERLI: subgraphGoerli ? new RegExp(subgraphGoerli, 'ig') : null, + SUBGRAPH_HOLESKY: subgraphHolesky ? new RegExp(subgraphHolesky, 'ig') : null, } const secretEntries = Object.entries(SECRETS) diff --git a/modules/tokens/utils/validateToken.ts b/modules/tokens/utils/validateToken.ts index 94be5495..7ac70d54 100644 --- a/modules/tokens/utils/validateToken.ts +++ b/modules/tokens/utils/validateToken.ts @@ -1,12 +1,12 @@ import { utils } from 'ethers' export const validateToken = (value: string) => { - if (Number(value) < 0) { - return 'Must not be negative' + if (Number(value) <= 0) { + return 'Must be positive' } try { utils.parseEther(value) - return true + return null } catch (_) { return 'Unable to parse' } diff --git a/modules/wallet/ui/ConnectWalletModal/ConnectWalletModal.tsx b/modules/wallet/ui/ConnectWalletModal/ConnectWalletModal.tsx index 593640b0..e37b09ae 100644 --- a/modules/wallet/ui/ConnectWalletModal/ConnectWalletModal.tsx +++ b/modules/wallet/ui/ConnectWalletModal/ConnectWalletModal.tsx @@ -21,6 +21,9 @@ const HIDDEN_WALLETS: WalletModalForEthProps['hiddenWallets'] = [ 'tally', 'zengo', 'zerion', + 'bitget', + 'bitkeep', + 'taho', ] type Props = WalletModalForEthProps & {} diff --git a/next.config.mjs b/next.config.mjs index d620f8dd..55b7f079 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -14,6 +14,12 @@ const rpcUrls_5 = (process.env.EL_RPC_URLS_5 && infuraApiKey && `https://goerli.infura.io/v3/${infuraApiKey}`, ].filter(Boolean); +const rpcUrls_17000 = (process.env.EL_RPC_URLS_17000 && + process.env.EL_RPC_URLS_17000.split(',')) || [ + alchemyApiKey && `https://eth-goerli.alchemyapi.io/v2/${alchemyApiKey}`, + infuraApiKey && `https://goerli.infura.io/v3/${infuraApiKey}`, +].filter(Boolean); + const defaultChain = process.env.DEFAULT_CHAIN || '1' const supportedChains = process.env.SUPPORTED_CHAINS || '1,5' @@ -23,6 +29,7 @@ const cspReportUri = process.env.CSP_REPORT_URI const subgraphMainnet = process.env.SUBGRAPH_MAINNET const subgraphGoerli = process.env.SUBGRAPH_GOERLI +const subgraphHolesky = process.env.SUBGRAPH_HOLESKY const walletconnectProjectId = process.env.WALLETCONNECT_PROJECT_ID @@ -112,11 +119,13 @@ export default { basePath, rpcUrls_1, rpcUrls_5, + rpcUrls_17000, cspTrustedHosts, cspReportOnly, cspReportUri, subgraphMainnet, subgraphGoerli, + subgraphHolesky, }, publicRuntimeConfig: { defaultChain, diff --git a/package.json b/package.json index 567d6dc6..c36dfc40 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,12 @@ "@walletconnect/modal": "~2.4.7" }, "dependencies": { - "@lido-sdk/constants": "1.3.0", - "@lido-sdk/contracts": "^2.0.4", - "@lido-sdk/fetch": "^2.1.5", - "@lido-sdk/helpers": "^1.4.7", - "@lido-sdk/providers": "^1.4.8", - "@lido-sdk/react": "^1.18.5", + "@lido-sdk/constants": "^3.2.1", + "@lido-sdk/contracts": "^3.0.4", + "@lido-sdk/fetch": "^2.1.12", + "@lido-sdk/helpers": "^1.5.1", + "@lido-sdk/providers": "^1.4.14", + "@lido-sdk/react": "^2.0.3", "@lidofinance/evm-script-decoder": "0.2.2", "@lidofinance/lido-ui": "3.2.0", "@lidofinance/next-cache-files-middleware": "^0.33.0", @@ -58,11 +58,11 @@ "react-dom": "^17", "react-hook-form": "7.27.1", "react-use": "17.3.2", - "reef-knot": "^1.4.5", + "reef-knot": "^1.8.0", "styled-components": "5.3.3", "swr": "1.2.2", "typechain": "7.0.1", - "wagmi": "0.12.17" + "wagmi": "0.12.19" }, "devDependencies": { "@commitlint/cli": "17.3.0", @@ -89,7 +89,7 @@ "lint-staged": "13.1.0", "prettier": "2.5.1", "type-fest": "2.12.0", - "typescript": "^4.9.4", + "typescript": "^4.9.5", "url-loader": "4.1.1", "utility-types": "3.10.0" } diff --git a/pages/api/node-operators/keys-info.ts b/pages/api/node-operators/keys-info.ts index 4b99ded4..576d9557 100644 --- a/pages/api/node-operators/keys-info.ts +++ b/pages/api/node-operators/keys-info.ts @@ -1,17 +1,128 @@ import { createNextConnect } from 'modules/shared/utils/createNextConnect' import { parseChainId } from 'modules/blockChain/chains' import { fetch } from '@lido-sdk/fetch' +import { CHAINS } from '@lido-sdk/constants' +import { KeysInfo } from 'modules/motions/types' +import { utils } from 'ethers' + +export type Module = { + id: number + stakingModuleAddress: string +} + +export type KeysInfoOperatorNew = { + id: number + active: boolean + name: string + rewardAddress: string + totalSigningKeys: number + usedSigningKeys: number + stakingLimit: number + stoppedValidators: number + unusedBellowStakingLimit: number + unusedOverStakingLimit: number + duplicates: string[] + invalid: string[] +} + +export type KeysInfoNew = { + operators: undefined | KeysInfoOperatorNew[] + summary: { + moduleId: number + } +} + +const requestTestnetOperators = async (chainId: number) => { + const data = await fetch( + `https://operators.testnet.fi/api/operators?chainId=${chainId}`, + ) + return data.json() +} + +const requestOperators = async ( + api: + | 'https://operators-holesky.testnet.fi/api' + | 'https://operators.lido.fi/api', + chainId: number, + moduleAddress: string, + walletAddress: string, +) => { + const modulesResp = await fetch(`${api}/modules?chainId=${chainId}`) + const modules: Module[] = await modulesResp.json() + + const module = modules.find( + item => + utils.getAddress(item.stakingModuleAddress) === + utils.getAddress(moduleAddress), + ) + const result: KeysInfo = {} + if (!module) { + return result + } + const moduleStatisticsResp = await fetch( + `${api}/moduleStatistics?moduleId=${module.id}&chainId=${chainId}`, + ) + const moduleStatistics: KeysInfoNew = await moduleStatisticsResp.json() + + const operator = moduleStatistics.operators?.find( + item => + utils.getAddress(item.rewardAddress) === utils.getAddress(walletAddress), + ) + if (!operator) { + return result + } + + const operatorStatisticsResp = await fetch( + `${api}/operatorStatistics?moduleId=${module.id}&operatorId=${operator.id}&chainId=${chainId}`, + ) + const operatorStatistics: KeysInfoOperatorNew = + await operatorStatisticsResp.json() + + result.operators = [ + { + invalid: operatorStatistics.invalid, + duplicates: operatorStatistics.duplicates, + info: { + index: operatorStatistics.id, + active: operatorStatistics.active, + name: operatorStatistics.name, + rewardAddress: operatorStatistics.rewardAddress, + stakingLimit: operatorStatistics.stakingLimit, + stoppedValidators: operatorStatistics.stoppedValidators, + totalSigningKeys: operatorStatistics.totalSigningKeys, + usedSigningKeys: operatorStatistics.usedSigningKeys, + }, + }, + ] + return result +} export default createNextConnect().get(async (req, res) => { try { const chainId = parseChainId(String(req.query.chainId)) - const data = await fetch( - `https://operators.${ - chainId === 1 ? 'lido' : 'testnet' - }.fi/api/operators?chainId=${chainId}`, - ) - const parsed = await data.json() - res.json(parsed) + const walletAddress = String(req.query.walletAddress) + const moduleAddress = String(req.query.moduleAddress) + + let result + if (chainId === CHAINS.Mainnet) { + result = await requestOperators( + 'https://operators.lido.fi/api', + chainId, + moduleAddress, + walletAddress, + ) + } else if (chainId === CHAINS.Holesky) { + result = await requestOperators( + 'https://operators-holesky.testnet.fi/api', + chainId, + moduleAddress, + walletAddress, + ) + } else { + result = await requestTestnetOperators(chainId) + } + + res.json(result) } catch (e) { console.error(e) res.status(500).send({ error: 'Something went wrong!' }) diff --git a/pages/api/rpc.ts b/pages/api/rpc.ts index 36118a82..79b32115 100644 --- a/pages/api/rpc.ts +++ b/pages/api/rpc.ts @@ -7,12 +7,13 @@ import { fetchWithFallback } from 'modules/network/utils/fetchWithFallback' import clone from 'just-clone' const { serverRuntimeConfig } = getConfig() -const { rpcUrls_1, rpcUrls_5 } = serverRuntimeConfig +const { rpcUrls_1, rpcUrls_5, rpcUrls_17000 } = serverRuntimeConfig export default async function rpc(req: NextApiRequest, res: NextApiResponse) { const RPC_URLS: Record = { [CHAINS.Mainnet]: rpcUrls_1, [CHAINS.Goerli]: rpcUrls_5, + [CHAINS.Holesky]: rpcUrls_17000, } const requestInfo = { @@ -33,6 +34,7 @@ export default async function rpc(req: NextApiRequest, res: NextApiResponse) { const requested = await fetchWithFallback(urls, chainId, { method: 'POST', + headers: { 'Content-Type': 'application/json' }, // Next by default parses our body for us, we don't want that here body: JSON.stringify(req.body), }) diff --git a/pages/api/subgraph.ts b/pages/api/subgraph.ts index 67e454c0..f53637cc 100644 --- a/pages/api/subgraph.ts +++ b/pages/api/subgraph.ts @@ -8,6 +8,7 @@ const { serverRuntimeConfig } = getConfig() export const SUBGRAPH_URL: Partial> = { [CHAINS.Mainnet]: serverRuntimeConfig.subgraphMainnet, [CHAINS.Goerli]: serverRuntimeConfig.subgraphGoerli, + [CHAINS.Holesky]: serverRuntimeConfig.subgraphHolesky, } as const export default async function subgraph( diff --git a/yarn.lock b/yarn.lock index 3bbbed56..2b81f281 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2367,99 +2367,101 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@json-rpc-tools/provider@^1.5.5": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@json-rpc-tools/provider/-/provider-1.7.6.tgz#8a17c34c493fa892632e278fd9331104e8491ec6" - integrity sha512-z7D3xvJ33UfCGv77n40lbzOYjZKVM3k2+5cV7xS8G6SCvKTzMkhkUYuD/qzQUNT4cG/lv0e9mRToweEEVLVVmA== - dependencies: - "@json-rpc-tools/utils" "^1.7.6" - axios "^0.21.0" - safe-json-utils "^1.1.1" - ws "^7.4.0" - -"@json-rpc-tools/types@^1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@json-rpc-tools/types/-/types-1.7.6.tgz#5abd5fde01364a130c46093b501715bcce5bdc0e" - integrity sha512-nDSqmyRNEqEK9TZHtM15uNnDljczhCUdBmRhpNZ95bIPKEDQ+nTDmGMFd2lLin3upc5h2VVVd9tkTDdbXUhDIQ== - dependencies: - keyvaluestorage-interface "^1.0.0" - -"@json-rpc-tools/utils@^1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@json-rpc-tools/utils/-/utils-1.7.6.tgz#67f04987dbaa2e7adb6adff1575367b75a9a9ba1" - integrity sha512-HjA8x/U/Q78HRRe19yh8HVKoZ+Iaoo3YZjakJYxR+rw52NHo6jM+VE9b8+7ygkCFXl/EHID5wh/MkXaE/jGyYw== - dependencies: - "@json-rpc-tools/types" "^1.7.6" - "@pedrouid/environment" "^1.0.1" - "@ledgerhq/connect-kit-loader@^1.0.1": version "1.0.2" resolved "https://registry.yarnpkg.com/@ledgerhq/connect-kit-loader/-/connect-kit-loader-1.0.2.tgz#8554e16943f86cc2a5f6348a14dfe6e5bd0c572a" integrity sha512-TQ21IjcZOw/scqypaVFY3jHVqI7X7Hta3qN/us6FvTol3AY06UmrhhXGww0E9xHmAbdX241ddwXEiMBSQZFr9g== -"@ledgerhq/cryptoassets@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-8.0.0.tgz#dd4d620012f49f9a7b0a633479361750db69fe4e" - integrity sha512-6Hv0DcSSi3Nkda9+YZJCxvscR4EKv/a33geE8nXcGxQUDZltrYn0aG1ygzrEoqWoIYHL3KKbIJkSKCsmKe09+A== +"@ledgerhq/cryptoassets@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-11.0.0.tgz#6118a0ce26f85259ef500c7a88e7862aad89fd36" + integrity sha512-r21sWwHbPqj58dU3qIYApdKHTiet2rlCJV/hGYSYQf8AiYJx/s9IURDaeQBkq1pd4Oo9e59svuD1UYip2Nw4jA== dependencies: invariant "2" -"@ledgerhq/devices@^7.0.7": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-7.0.7.tgz#3499304a1c9d3aa7399de2ad390719bef5d1e89c" - integrity sha512-PZ9TtaTGBYUm/g0qNKPbECZt7DDNvqM3ILS5wAtOMna2cBR+mrywUGXrkjuOWlHpuqZ8wenaAKveQBbzF2ba8w== +"@ledgerhq/devices@^8.0.7": + version "8.0.7" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.0.7.tgz#206434dbd8a097529bbfc95f5eef94c2923c7578" + integrity sha512-BbPyET52lXnVs7CxJWrGYqmtGdbGzj+XnfCqLsDnA7QYr1CZREysxmie+Rr6BKpNDBRVesAovXjtaVaZOn+upw== dependencies: - "@ledgerhq/errors" "^6.12.3" + "@ledgerhq/errors" "^6.14.0" "@ledgerhq/logs" "^6.10.1" rxjs "6" semver "^7.3.5" -"@ledgerhq/errors@^6.12.3": - version "6.12.3" - resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.12.3.tgz#a610caae1eeeb7cb038525e5212fe03217dda683" - integrity sha512-djiMSgB/7hnK3aLR/c5ZMMivxjcI7o2+y3VKcsZZpydPoVf9+FXqeJPRfOwmJ0JxbQ//LinUfWpIfHew8LkaVw== +"@ledgerhq/domain-service@^1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@ledgerhq/domain-service/-/domain-service-1.1.12.tgz#f8dc233e9063531a15ad6cbaac5b4f9bcaf3a1c0" + integrity sha512-k41XiV+hDPxw9JWqybfses+Z1HAiuflv5x7AbtS+hBE6h6BYRlqEbPbDdOfMvLFeAxOFltHTwLimFsepFdvuYw== + dependencies: + "@ledgerhq/cryptoassets" "^11.0.0" + "@ledgerhq/errors" "^6.14.0" + "@ledgerhq/logs" "^6.10.1" + "@ledgerhq/types-live" "^6.41.0" + axios "^1.3.4" + eip55 "^2.1.1" + react "^17.0.2" + react-dom "^17.0.2" + +"@ledgerhq/errors@^6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.14.0.tgz#0bf253983773ef12eebce2091f463bc719223b37" + integrity sha512-ZWJw2Ti6Dq1Ott/+qYqJdDWeZm16qI3VNG5rFlb0TQ3UcAyLIQZbnnzzdcVVwVeZiEp66WIpINd/pBdqsHVyOA== + +"@ledgerhq/evm-tools@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@ledgerhq/evm-tools/-/evm-tools-1.0.8.tgz#04bc67a57535045276c376b97960e8ce957add52" + integrity sha512-gObo+42aFYYjj631Z6oxB+1lKz8XMDad20K2HpSly99bYN1D/0vDMl+LqX7LpeXh/dJsrUy0xE+xa5uwPPjP+A== + dependencies: + "@ledgerhq/cryptoassets" "^11.0.0" + "@ledgerhq/live-env" "^0.6.0" + "@ledgerhq/live-network" "^1.1.7" + crypto-js "4.1.1" + ethers "5.7.2" -"@ledgerhq/hw-app-eth@^6.22.3": - version "6.31.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.31.0.tgz#733983c077634385b845d623e692d088f034d24b" - integrity sha512-Ot9JBptf/kTztg/B75pRrX8Jpu8mBUKC4O3/LGJcFFVjputgsvGEjui64KSm2J8XSEaLUPGXVoFVn1czoI475g== +"@ledgerhq/hw-app-eth@^6.33.1": + version "6.34.7" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-6.34.7.tgz#f8a3788d4dfb974fbfc604eb0f2f21d96da817aa" + integrity sha512-a3DdgNRNa/2u97CJxvjZybtOdSp2GxRFF80H0jUweeXsqgGW5WKM+17Ay3NysjXzaKolnvAgTonSNdTS6h0pzw== dependencies: "@ethersproject/abi" "^5.5.0" "@ethersproject/rlp" "^5.5.0" - "@ledgerhq/cryptoassets" "^8.0.0" - "@ledgerhq/errors" "^6.12.3" - "@ledgerhq/hw-transport" "^6.28.0" - "@ledgerhq/hw-transport-mocker" "^6.27.11" + "@ledgerhq/cryptoassets" "^11.0.0" + "@ledgerhq/domain-service" "^1.1.12" + "@ledgerhq/errors" "^6.14.0" + "@ledgerhq/evm-tools" "^1.0.8" + "@ledgerhq/hw-transport" "^6.28.8" + "@ledgerhq/hw-transport-mocker" "^6.27.19" "@ledgerhq/logs" "^6.10.1" - axios "^0.26.1" - bignumber.js "^9.1.0" - crypto-js "^4.1.1" + "@ledgerhq/types-live" "^6.41.0" + axios "^1.3.4" + bignumber.js "^9.1.2" -"@ledgerhq/hw-transport-mocker@^6.27.11": - version "6.27.11" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-mocker/-/hw-transport-mocker-6.27.11.tgz#0751b10d0c582d6ac68c68cb51994bf29dfe18a7" - integrity sha512-0qSc3RBK/3HvtAQUTYwXIrFA5uhUM4jUEKIaYMGAE4sootaYNSohenJA+ddelmHHILpACZ5/voQeOUpwwMaQdw== +"@ledgerhq/hw-transport-mocker@^6.27.19": + version "6.27.19" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-mocker/-/hw-transport-mocker-6.27.19.tgz#ad6ee81ce7e61e86e7b6edb07e09540e99c8a6cd" + integrity sha512-sD/7Ht1flaHZcwIFOi2E1LTHYLdAOe206/JwlwRU78pT0oUan8FnXR5SD8PSmhwgmQjJXto+PFBlmVe5EIt9Lw== dependencies: - "@ledgerhq/hw-transport" "^6.28.0" + "@ledgerhq/hw-transport" "^6.28.8" "@ledgerhq/logs" "^6.10.1" -"@ledgerhq/hw-transport-webhid@^6.20.0": - version "6.27.11" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.27.11.tgz#efb721bb2db7e73cbc3eee6818c3252827ca182c" - integrity sha512-4HAhZdou4UBTEaUOBlFfeAAjTT7CuhmKryoAfgdidbSLXNwFwKGBigdZl6AkYqpubRzsi26AGr0SJonW3Uh/Hg== +"@ledgerhq/hw-transport-webhid@^6.27.13": + version "6.27.19" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.27.19.tgz#5a655b497258d94ec6494db7b56e17dd0c610638" + integrity sha512-RMnktayqqLE2uFQDw9TKoW+WSP8KnT0ElKcIISf3sXVrzHD2y0moPk/wXOzGfi+cgN4uiKy86UD/5mgz3wlm6Q== dependencies: - "@ledgerhq/devices" "^7.0.7" - "@ledgerhq/errors" "^6.12.3" - "@ledgerhq/hw-transport" "^6.28.0" + "@ledgerhq/devices" "^8.0.7" + "@ledgerhq/errors" "^6.14.0" + "@ledgerhq/hw-transport" "^6.28.8" "@ledgerhq/logs" "^6.10.1" -"@ledgerhq/hw-transport@^6.20.0", "@ledgerhq/hw-transport@^6.28.0": - version "6.28.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.28.0.tgz#c77b8cb9da2a876e090a1dba305fad9a539b1dba" - integrity sha512-VRhVwxQN82PWxgsmLUXogWn8K2TOHF6zGBJpyPda3/TtQWxEPmW6xCa1/vomsu1C1pamAUeoKhQrOYagzfFPkg== +"@ledgerhq/hw-transport@^6.28.2", "@ledgerhq/hw-transport@^6.28.8": + version "6.28.8" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.28.8.tgz#f99a5c71c5c09591e9bfb1b970c42aafbe81351f" + integrity sha512-XxQVl4htd018u/M66r0iu5nlHi+J6QfdPsORzDF6N39jaz+tMqItb7tUlXM/isggcuS5lc7GJo7NOuJ8rvHZaQ== dependencies: - "@ledgerhq/devices" "^7.0.7" - "@ledgerhq/errors" "^6.12.3" + "@ledgerhq/devices" "^8.0.7" + "@ledgerhq/errors" "^6.14.0" events "^3.3.0" "@ledgerhq/iframe-provider@0": @@ -2476,72 +2478,95 @@ dependencies: eventemitter3 "^4.0.0" +"@ledgerhq/live-env@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-env/-/live-env-0.6.0.tgz#fc4770fe8041cd7f4ba95d56deb9075ac0d89de1" + integrity sha512-wWlatg4OT0p2jCmUERBtI6dduSe4BIZiSUuh1uSjQpbnTTPoMVHNjn4U7A4Ns1+Zz5TkmDwXS8yE523cRjjVrg== + dependencies: + rxjs "^6.6.7" + utility-types "^3.10.0" + +"@ledgerhq/live-network@^1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-network/-/live-network-1.1.7.tgz#4838e9858489212ff68248e11c6bf7fd77554957" + integrity sha512-OneMFcGRc5DbHWSNG41kfY/81Lm78IxJScIufGMW6beb0Sp6SmlHqkfjwrPLtp1GeHrJ2SqaXAvWROV/AexhUg== + dependencies: + "@ledgerhq/errors" "^6.14.0" + "@ledgerhq/live-env" "^0.6.0" + "@ledgerhq/live-promise" "^0.0.1" + "@ledgerhq/logs" "^6.10.1" + "@types/node" "^20.2.5" + axios "0.26.1" + invariant "^2.2.2" + lru-cache "^7.14.1" + +"@ledgerhq/live-promise@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-promise/-/live-promise-0.0.1.tgz#2c559936dce638e4dfe9c04a414941c4bf80a56c" + integrity sha512-R8sSYUuulsQ8Kd/TNNh0pB8XK1kJQAmSwNKyoU+T++c0X7jwbBv7R6/SphJg9Ts8dtHWyxHaxm9jkDBUVXpm2Q== + dependencies: + "@ledgerhq/logs" "^6.10.1" + "@ledgerhq/logs@^6.10.1": version "6.10.1" resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.10.1.tgz#5bd16082261d7364eabb511c788f00937dac588d" integrity sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w== -"@lido-sdk/constants@1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@lido-sdk/constants/-/constants-1.3.0.tgz#bd4aacfa3de80eca49ecf643a1dccdae3b61a05a" - integrity sha512-K5hSGKiEq9A2nzk4vrDxy+tPdsYX1coRtYz7/7vnkrCLfHx4rSH4gEm9V/xNvFy2R6/AIen/FbPpIb2qj6dEvg== +"@ledgerhq/types-live@^6.41.0": + version "6.41.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/types-live/-/types-live-6.41.0.tgz#a3c08fa5ecd3158b4a860eb2ad2da6d333d96963" + integrity sha512-qiTpQkaQD4YQxFS6tpBVlMkkWLrCc/PIz9foSBcS0SiJMn3apYm2JchC4tdiOuhUwL0IPRMqS0yNOGmMm6Vs8g== dependencies: - tiny-invariant "^1.1.0" - -"@lido-sdk/constants@1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@lido-sdk/constants/-/constants-1.8.1.tgz#ae1c81430ac1ae06f1a579ddf748ed7b7ef65866" - integrity sha512-IL6ZkFYOAfmA+09ekw2s8HFjMnSWw41NU8o8n0i2PoRuOW8Ky1xM4MFjnWt40EC7WKyPggKIMXtPWC92Y66oMQ== - dependencies: - tiny-invariant "^1.1.0" + bignumber.js "^9.1.2" + rxjs "6" -"@lido-sdk/constants@^1.8.1": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@lido-sdk/constants/-/constants-1.9.0.tgz#368edaa23490f5a42b98ab613cee8587ee890ffc" - integrity sha512-PTZqkm/7ZCNIupt9f/Yqr+NFSxA786tNYhWLSKrR3EHnu6DQAzzh23D9PlkSGkgA34u+F6GHE6Z/HaSFd3HfiA== +"@lido-sdk/constants@3.2.1", "@lido-sdk/constants@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@lido-sdk/constants/-/constants-3.2.1.tgz#0c4582d7e76e4f8bc42e8f3c0d14dc0fbe481d77" + integrity sha512-zes0Mw0r1nEQYBNHV5fxK2H9Byowejy4haFy9LYDh1nL72aNJzzdh5S5iM+pKlEuLHQJHV5lVO/k9tunNJIKqQ== dependencies: tiny-invariant "^1.1.0" -"@lido-sdk/contracts@2.0.4", "@lido-sdk/contracts@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@lido-sdk/contracts/-/contracts-2.0.4.tgz#dbb56b2d92334d507675889f7938d464961e15b6" - integrity sha512-qJOMpgkeuOUT0t7Ih5m9ZvjEXbYIpllHsoRkMGgwTM5lLQs4Ybu9EclaDiFpjeipDuOrYooSQWesLOvs2C55Nw== +"@lido-sdk/contracts@3.0.4", "@lido-sdk/contracts@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@lido-sdk/contracts/-/contracts-3.0.4.tgz#85e3b203aa0a38841ecf22d7ac4e5f8d70848920" + integrity sha512-oW7gyHKcrss77sEPMmYm38M0CQ5+3GGlNewu9D+UJhtxRpLa+Jh3nWEd5tq/hMdMSN9cGoerVKFfBAhw6zKajg== dependencies: - "@lido-sdk/constants" "1.8.1" + "@lido-sdk/constants" "3.2.1" tiny-invariant "^1.1.0" -"@lido-sdk/fetch@^2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@lido-sdk/fetch/-/fetch-2.1.5.tgz#baeff86521f4f6d54a34178b925812d97a16133f" - integrity sha512-cGKENQyVEYduUOYhIOVkNHRNHlIJ81i3ffTP9NWqJ3ARd3BgozWi6Jqe8jjK4gbGVRXFaI4UTlbrBDR+XlgUeQ== +"@lido-sdk/fetch@^2.1.12": + version "2.1.12" + resolved "https://registry.yarnpkg.com/@lido-sdk/fetch/-/fetch-2.1.12.tgz#aa0ef16212f4bad7ffed68928143e7cbdab94973" + integrity sha512-kFBrzCVxYnD3Q68cM4KY9E7w/4TAJk1BW2wAcPpJyWm1yQSRL1urhEZ6NGbwkp23QvayVStbNrQiezVOdDnzYw== dependencies: - "@lido-sdk/constants" "1.8.1" + "@lido-sdk/constants" "3.2.1" node-fetch "^2.6.7" tiny-invariant "^1.1.0" -"@lido-sdk/helpers@1.4.7", "@lido-sdk/helpers@^1.4.7": - version "1.4.7" - resolved "https://registry.yarnpkg.com/@lido-sdk/helpers/-/helpers-1.4.7.tgz#43f17ba9e24f3052e17d392fa925bc13d62f52da" - integrity sha512-6SKk+Q73hF167XsI2rq3Dc/LTq8BobmdPxwce626ra7buvS8ZKp1NGp8jOPHHPTdF5/5Jo4KY9gxWjSukbq3+g== +"@lido-sdk/helpers@1.5.1", "@lido-sdk/helpers@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@lido-sdk/helpers/-/helpers-1.5.1.tgz#ced13f1df6e34a1d4ad551fde299524dc237b694" + integrity sha512-n8sTliverpxOy7PeTCUyG+bQPIJdg57AOON+6X2tZ19JxU3r6ZhHzo33x/9022aKu0A/Ya7edREDB6MadymdRg== dependencies: - "@lido-sdk/constants" "1.8.1" + "@lido-sdk/constants" "3.2.1" tiny-invariant "^1.1.0" -"@lido-sdk/providers@^1.4.8": - version "1.4.8" - resolved "https://registry.yarnpkg.com/@lido-sdk/providers/-/providers-1.4.8.tgz#94e483e61d1e756547c4f662a6b57dbbb65d449f" - integrity sha512-cgWVSpXlWQVYClZqEHRMw6Fp4euLznDpiPbZSjIiP+wQDV9qea4jvnOQRG0ediCQin+VTFLWHFBZghwIqzcZMg== +"@lido-sdk/providers@^1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@lido-sdk/providers/-/providers-1.4.14.tgz#b7c714aa753d662c0d51f71ee4990b3cb78ce790" + integrity sha512-m422uXuaGoXoUlF8oyFTIQsj8ljVet/x7nK0xF8UoURm/iuaAhTbEXpcxhmkx8JSSDli1928apJRAwxG0McgnQ== dependencies: - "@lido-sdk/constants" "1.8.1" + "@lido-sdk/constants" "3.2.1" -"@lido-sdk/react@^1.18.5": - version "1.18.5" - resolved "https://registry.yarnpkg.com/@lido-sdk/react/-/react-1.18.5.tgz#40f6c56cf54603e13809e6621221357c9a88e39f" - integrity sha512-ajq0ka1+4Kgocj1Pl6gcb9XSnSg2FVnd4mYAhtrwxgsu3c/mjPMuHabSjhVRqpp5Nu3KiOuxnXtVDTQaEjh1QQ== +"@lido-sdk/react@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@lido-sdk/react/-/react-2.0.3.tgz#dee817762f4fc7f0e7fdeac240b4b41902c48fcb" + integrity sha512-28EcbqFz2FNtop4YXyk7bcy7WtwWMBmYnkoEeotXkH6TWQcBmfepwqF9qfZGIezV4hgNgmRe8fOhi1kf7SdG/Q== dependencies: - "@lido-sdk/constants" "1.8.1" - "@lido-sdk/contracts" "2.0.4" - "@lido-sdk/helpers" "1.4.7" + "@lido-sdk/constants" "3.2.1" + "@lido-sdk/contracts" "3.0.4" + "@lido-sdk/helpers" "1.5.1" swr "^1.0.1" tiny-invariant "^1.1.0" tiny-warning "^1.0.3" @@ -2797,117 +2822,135 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@pedrouid/environment@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@pedrouid/environment/-/environment-1.0.1.tgz#858f0f8a057340e0b250398b75ead77d6f4342ec" - integrity sha512-HaW78NszGzRZd9SeoI3JD11JqY+lubnaOx7Pewj5pfjqWXOEATpeKIFb9Z4t2WBUK2iryiXX3lzWwmYWgUL0Ug== - -"@reef-knot/connect-wallet-modal@1.4.3": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@reef-knot/connect-wallet-modal/-/connect-wallet-modal-1.4.3.tgz#3537ed14e808fa6bb7dd80dc92c0762a79b68687" - integrity sha512-v7xi4iqOdix89feN/2gPGwwkeVh0sOs3UdLYkzAgLPJYM3uLGs9bKo5HDxbCYq1fAl4b/F4Xa+DKpNDBaeWcnQ== +"@reef-knot/connect-wallet-modal@1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@reef-knot/connect-wallet-modal/-/connect-wallet-modal-1.6.2.tgz#de6f8c8eb344929b165cb150fdc22dfbf12fd78a" + integrity sha512-kDJEw85j6cikoh9i5yIfQQl5seQGyUiDJWUvLUT24FPn/gsUM/5cimx/mWP+1yzxhU2ru+/vUIQjMmt4UVpTaQ== dependencies: "@types/react" "17.0.53" "@types/react-dom" "17" -"@reef-knot/core-react@1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@reef-knot/core-react/-/core-react-1.4.2.tgz#09bdeb1c1870efdb445eff22e1fabe5ae3de2ccd" - integrity sha512-L12f9QdgHE6jS2++NfoCQEZAgs6fI9bhfJzvFv/abgnEXTiGyCvybATzv8DUuIRmwRVa7g2YtY1sAD3uF4Vr9A== +"@reef-knot/core-react@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@reef-knot/core-react/-/core-react-1.6.0.tgz#92ea1794b80a2b57c713e6f88b184acb82ad6718" + integrity sha512-Mi50svDMpRqoAcOqSQ9z71WufKJkDaos814n3aiVlNBBqkYS+F7J28/Ks8R0FYqEtcp/4cp2+3CRzX0bkEqPLQ== -"@reef-knot/types@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@reef-knot/types/-/types-1.2.1.tgz#34987fa876538f4261c43a542363f71bead673e9" - integrity sha512-RH0mzQVqYGL1DTPuv8IZHpgyE1pqlx4yQ1WU5YJ0jx8lX/C7H+SwgLas85Fkg9hix2+CFaz7CxwiNxke9bq+2g== +"@reef-knot/ledger-connector@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@reef-knot/ledger-connector/-/ledger-connector-1.0.1.tgz#981b047723edd4f30b24250820a1b806c348a9fe" + integrity sha512-LqExXVZFOJY476uTP5GzeqgaVm3cVsUz6AGo7tzsRN2Ymb8zQFPVVoQLRF6sQeqt+4Oq9IePfVg8+cX49/lGrA== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/providers" "^5.5.2" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ledgerhq/hw-app-eth" "^6.33.1" + "@ledgerhq/hw-transport" "^6.28.2" + "@ledgerhq/hw-transport-webhid" "^6.27.13" + "@ledgerhq/iframe-provider" "0" + "@web3-react/abstract-connector" "^6.0.7" + "@web3-react/types" "^6.0.7" + tiny-invariant "^1.2.0" -"@reef-knot/ui-react@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@reef-knot/ui-react/-/ui-react-1.0.5.tgz#f7d253ecec3a21a2a8e79bac76e4d34640c88209" - integrity sha512-3XOpjcqKrpekVI3vQDKo42si6cZEhl2DnyxqP5VAIh8D2x2Bc1jqlBf/xYkg/19o1NnoNjWjYDUdS5iIOj8v+g== +"@reef-knot/types@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@reef-knot/types/-/types-1.3.0.tgz#aa3f3f1247ffde5986f98146871c1c0cc1506470" + integrity sha512-mKo5tceGBIx39vOBaxDSkKQqpPEyHBLMJeNPDUQ/rFfgjvPz8WFs+oAkA66YHey4WmMlRJDksLT3NhgANw4PwA== + +"@reef-knot/ui-react@1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@reef-knot/ui-react/-/ui-react-1.0.7.tgz#c24286fa4879ce21f8534dbbbff63153869d4afb" + integrity sha512-NZya61s6gIInPzSa19yrsbcBKRq8Kdh4JgoDMuPCw5QHWjgq1GHFQCLLJGqGjuVaKtk6Dzbcedxz7XhCkdxZsQ== dependencies: react-transition-group "4" use-callback-ref "1.2.5" -"@reef-knot/wallet-adapter-ambire@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-ambire/-/wallet-adapter-ambire-1.2.2.tgz#1844b1d3a2f9ac0fe0ad55239d44ee8ca879b433" - integrity sha512-B5JWk+pJaT7fEGHhpfTYH1C01oUW7miuHOoodFyQr8dBSFKoSPtz6EylJt8cVivNc7Ef6RKyB7k0AuHKTn2nwg== +"@reef-knot/wallet-adapter-ambire@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-ambire/-/wallet-adapter-ambire-1.2.4.tgz#8a436a575b1f7cf5eebe5a9a0311e1cccdcfd4b3" + integrity sha512-ZnVEYbQbsMFlyLIOAC8wGFdFlux0p7y3sdSgvBWbhoupDZaxUGNLXNHHauJ9mt1C1nLNhM9RpY8q4BBVKOD24g== -"@reef-knot/wallet-adapter-blockchaincom@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-blockchaincom/-/wallet-adapter-blockchaincom-1.2.2.tgz#2d334464d3350e912d0542c5b81dfd7dfdee9570" - integrity sha512-2p5EwwNlfUlEtCtSjVxOYAXS8hofYlJNTx+5XhnvGScRMssIvwXQ3FKvMwmXlItJrDs3gSOEKvmL89Tufcki4Q== +"@reef-knot/wallet-adapter-bitkeep@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-bitkeep/-/wallet-adapter-bitkeep-1.1.0.tgz#fe430c838b76b9304527ae62a19dd7a126afb72b" + integrity sha512-GjVt1rGXVXCh5vUIAg0vxf9IpNJTiVC6jJxAbu8g1C4mWrLhi0eRkeQzNmO8Zk220b+OoiEIpNJDrF6+ycTRpw== -"@reef-knot/wallet-adapter-exodus@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-exodus/-/wallet-adapter-exodus-1.2.1.tgz#e45034c83bc15257f0e804cad06e784a4c08a671" - integrity sha512-5vbx0z9FEIK0LuRYM8/hP/Q+JK4A8gm64NGP+i3pDv+DZiuzTTXusvPt1RGqZqjXXAuPYXoES2jZHhCGt1c15g== +"@reef-knot/wallet-adapter-blockchaincom@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-blockchaincom/-/wallet-adapter-blockchaincom-1.2.4.tgz#f33a125c658c635b098e333bbc7f84a58dd7a0af" + integrity sha512-RViZe8fepuEJgZfYprRhNCkC8xD+dPNXd5xTwfhO2WqPOjTpUAHBP7kXwqrGr6IPrv7zWwdE6MY7c5PXzUbaQA== -"@reef-knot/wallet-adapter-okx@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-okx/-/wallet-adapter-okx-1.2.1.tgz#dfdcf7446c247c6b343555e8bd4fff24f8f5e582" - integrity sha512-RuJTyBR3e31y2erAJFe0oO7BtnhejvH1YTFN4Ii2/texiPZhPm+UAD2ew1mRT5kUn1S2RGI+A2LrVY0wzyKfUQ== +"@reef-knot/wallet-adapter-exodus@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-exodus/-/wallet-adapter-exodus-1.2.3.tgz#9d9c57fe838a7b1c1ca8ccfcbceb73e4890b84e7" + integrity sha512-CARrT7cSsaAV6T11Wiu2jYBzi7Yxu7DwdgN56kDCenPiX9X2TOeMeLxFY8MflJLyjyTl1jR9HRgOfnm8Qg12Qw== -"@reef-knot/wallet-adapter-phantom@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-phantom/-/wallet-adapter-phantom-1.2.1.tgz#7b08eb3a7a573ea71b600369215d33395c64d53e" - integrity sha512-/3KQflPU/aNlvsT6Jn3Dcn01EqwnNELC0qLUQ4RD3sfEjsUNnpHHfLmAt05S7YKkWbB7DA2YgLas8ktVUoAl+g== +"@reef-knot/wallet-adapter-okx@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-okx/-/wallet-adapter-okx-1.3.0.tgz#b8eadb4b0ae2db602e35c494f7fecaa561c1dc5f" + integrity sha512-E6E7LQ1zy88yRLsJWRlja5IFd9LLALZiNwCikX4OBkn6kxD1c82tIWRIVd1Lzw7+zeuA4dKRz6S3ksya5PZc2g== -"@reef-knot/wallet-adapter-taho@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-taho/-/wallet-adapter-taho-1.2.1.tgz#44f4dda81339a0136e78172b11c4889f0a9dc81d" - integrity sha512-/b4dJQoJC/NmlpHDMBkJkd+vkTkISAn9RA96gcWqKEPbXKzzyrwnsoUlNhbxaux5kSYqbKEmVkjPwwv4u4AQpQ== +"@reef-knot/wallet-adapter-phantom@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-phantom/-/wallet-adapter-phantom-1.3.0.tgz#642831a4b96b99efbe037d99ec6f7b4e4a720843" + integrity sha512-rSHg0eOwo5QsYhrWxXy73EFLjTnEPdAM8/XHwg1dtXEvPGhjKpDd3Aj6Eh4nLvH0ef8JU+Kcta+YHFnNUEz9EQ== -"@reef-knot/wallet-adapter-walletconnect@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-walletconnect/-/wallet-adapter-walletconnect-1.2.2.tgz#495f4b7507a7a45b5494dc27e1638599ff21ac2f" - integrity sha512-59EaWnmW4U0fHJYWP/DxFtu0V5j72APphy9eOoNW+qY6N/zV7wFs/IFVacN/W1xQ38k3GMttFpWZM1IiwDxcAg== +"@reef-knot/wallet-adapter-taho@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-taho/-/wallet-adapter-taho-1.2.3.tgz#a4e233f581963d4addb6596980646aafc39aade9" + integrity sha512-FD7dtC5w1hPAzkDhXYK9PIg66wEZ343FadEn4L/D4G8w/w3FHrJWYZapyz0c4zLGH2jZTz2S1iVTIZlie7stjQ== -"@reef-knot/wallet-adapter-zengo@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-zengo/-/wallet-adapter-zengo-1.2.2.tgz#131f9f6baee00d968349a60b07ab327d0f73df8a" - integrity sha512-jEFs8yJx7nQYiFshcCygr6yHU6Jo/hVB0rXYk7kVwCCvSsv+LfdgN9Ct8cXUFHQo58+K3YylcnDZY7FC//mIrw== +"@reef-knot/wallet-adapter-walletconnect@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-walletconnect/-/wallet-adapter-walletconnect-1.2.4.tgz#5a4d1b23e1fad5cae302c5e996f12af3b1da3cd5" + integrity sha512-KDsCIiJsDypCepx3qU/vSdb598vncL74KdAd93dHzbUYk32ujmb74sR77Yl03zLhJ0Tbv1xU36WLpefQfN2P8g== -"@reef-knot/wallet-adapter-zerion@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-zerion/-/wallet-adapter-zerion-1.2.2.tgz#f11a7fd7dce2e4b63cbc8700bb596128a9b4c2eb" - integrity sha512-9sorZP0cs+tXEo+XW84aJoCVLE3fvPw9W1ODUMi6Le8P9JaDkjqLjl2PrrCnm+0QwbmBKnlFGFvcNodd9DiPSg== +"@reef-knot/wallet-adapter-zengo@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-zengo/-/wallet-adapter-zengo-1.2.4.tgz#b5c12179bdfdf92c7b7ae522c0535f25b8111a75" + integrity sha512-2cGISrhmTb033KqqPmp+4Enl5qt8RzJeqqzOe3gWDvMfe0UOTIMu22hzcmcFznBDQdjXcdp/pXP3aiI3dgxVXg== -"@reef-knot/wallets-helpers@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@reef-knot/wallets-helpers/-/wallets-helpers-1.1.2.tgz#880a874693d9e3acbd14708adbe2ae7c00f1f4ee" - integrity sha512-/lMwAMG0NHo277Vg93ix70opbS3a7iQvFLv4hVS6/2+7Hezcv9fm2vPrj/5iIeeNJ72EMWFNmzFoi4Cit+Oq/g== +"@reef-knot/wallet-adapter-zerion@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@reef-knot/wallet-adapter-zerion/-/wallet-adapter-zerion-1.2.4.tgz#2b01259e0bfb97deb928a653fd55346bc28b171e" + integrity sha512-t2mUkeV8RhGZA53Ch1h46y078hH/fOvPB4LIr4sFjFpZZ9bcQNHPoSPjTVw4/cRS2wJ4dIZSklUFtUWui5MjlQ== + +"@reef-knot/wallets-helpers@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@reef-knot/wallets-helpers/-/wallets-helpers-1.1.5.tgz#bceb7d91a6f7748ec093fbdf7422772bd71708b6" + integrity sha512-OFWR6zsUy04Waujl1VlNNs91P/kyHeGLC49QLWs3vrHvVipEk7ydUhKU/dHrbuhjQBS7quKg4vrodyCUUl4zyQ== "@reef-knot/wallets-icons@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@reef-knot/wallets-icons/-/wallets-icons-1.0.0.tgz#14db791e78309f8a53d4a8a08e561d67a6757d93" integrity sha512-x3Numm/rRHbHLrMzZpD6dGb+b5F+ZdBdYe+0xZyqw3qPmS/K4M0Hh9sGWgw66iVhHOyiYWdmtVWpyg2mbg52Zg== -"@reef-knot/wallets-list@1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@reef-knot/wallets-list/-/wallets-list-1.4.2.tgz#cb2317abcb80072c100950af9ffe4a48e23a26b1" - integrity sha512-/mccMffHkSH4jxx8ghMKLwy8dKI9ri4tAyw1ycy9DdH9fonDhhiGQ0VsKYNxsZXjI1Dn9UTnpzpUazja8zXmvA== - dependencies: - "@reef-knot/wallet-adapter-ambire" "1.2.2" - "@reef-knot/wallet-adapter-blockchaincom" "1.2.2" - "@reef-knot/wallet-adapter-exodus" "1.2.1" - "@reef-knot/wallet-adapter-okx" "1.2.1" - "@reef-knot/wallet-adapter-phantom" "1.2.1" - "@reef-knot/wallet-adapter-taho" "1.2.1" - "@reef-knot/wallet-adapter-walletconnect" "1.2.2" - "@reef-knot/wallet-adapter-zengo" "1.2.2" - "@reef-knot/wallet-adapter-zerion" "1.2.2" - -"@reef-knot/web3-react@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@reef-knot/web3-react/-/web3-react-1.2.2.tgz#4acfa9f1ba307fb1af6273c4447e9c78d0a5a2f6" - integrity sha512-gLUrV2itDwm4j1Oku9AMRfTqiBo2PUblmzZLnyA1Qj6xgK4EG0fOVEGs85BZ7ih9QngYyTmI16FGhzN+MEC7Dg== +"@reef-knot/wallets-list@1.4.5": + version "1.4.5" + resolved "https://registry.yarnpkg.com/@reef-knot/wallets-list/-/wallets-list-1.4.5.tgz#65fb447cf950583d444b6abc7e6ac1c412a59a88" + integrity sha512-PdZHN5XfDImhBO6/crWxLu3bVu3sf5Mh44x4WPUDw9bC9lbBPgXsawyg6l9LdP3Zm6GW8O6x3xcLcVWR1LEUsA== + dependencies: + "@reef-knot/wallet-adapter-ambire" "1.2.4" + "@reef-knot/wallet-adapter-bitkeep" "1.1.0" + "@reef-knot/wallet-adapter-blockchaincom" "1.2.4" + "@reef-knot/wallet-adapter-exodus" "1.2.3" + "@reef-knot/wallet-adapter-okx" "1.3.0" + "@reef-knot/wallet-adapter-phantom" "1.3.0" + "@reef-knot/wallet-adapter-taho" "1.2.3" + "@reef-knot/wallet-adapter-walletconnect" "1.2.4" + "@reef-knot/wallet-adapter-zengo" "1.2.4" + "@reef-knot/wallet-adapter-zerion" "1.2.4" + +"@reef-knot/web3-react@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@reef-knot/web3-react/-/web3-react-1.5.0.tgz#9b9cde9200f51faaad074a5bb0dc57d448bf680e" + integrity sha512-bZIcP8Uly8FNECGq5mqplqGhLoLoXYPPMub+2jZ7xX+QlIf74W4B5l6lIM/8TmIf9vS0NNd9/pbk53Y6M3Z3Sw== dependencies: "@gnosis.pm/safe-apps-web3-react" "0.6.8" "@ledgerhq/iframe-provider" "0.4.2" - "@lido-sdk/constants" "^1.8.1" - "@lido-sdk/providers" "^1.4.8" - "@lido-sdk/react" "^1.18.5" "@web3-react/abstract-connector" "6.0.7" "@web3-react/core" "6.1.9" "@web3-react/injected-connector" "6.0.7" @@ -2915,8 +2958,6 @@ swr "1.3.0" tiny-invariant "^1.1.0" tiny-warning "^1.0.3" - web3-ledgerhq-connector "^1.2.3" - web3-ledgerhq-frame-connector "^1.0.1" "@rushstack/eslint-patch@^1.0.8": version "1.2.0" @@ -3580,6 +3621,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== +"@types/node@^20.2.5": + version "20.8.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.3.tgz#c4ae2bb1cfab2999ed441a95c122bbbe1567a66d" + integrity sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw== + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -3809,50 +3855,50 @@ resolved "https://registry.yarnpkg.com/@wagmi/chains/-/chains-0.2.22.tgz#25e511e134a00742e4fbf5108613dadf876c5bd9" integrity sha512-TdiOzJT6TO1JrztRNjTA5Quz+UmQlbvWFG8N41u9tta0boHA1JCAzGGvU6KuIcOmJfRJkKOUIt67wlbopCpVHg== -"@wagmi/connectors@0.3.21": - version "0.3.21" - resolved "https://registry.yarnpkg.com/@wagmi/connectors/-/connectors-0.3.21.tgz#0bec726c14217ad391f6e49af1203ccf0249786e" - integrity sha512-yXtczgBQzVhUeo6D2L9yu8HmWQv08v6Ji5Cb4ZNL1mM2VVnvXxv7l40fSschcTw6H5jBZytgeGgL/aTYhn3HYQ== +"@wagmi/connectors@0.3.24": + version "0.3.24" + resolved "https://registry.yarnpkg.com/@wagmi/connectors/-/connectors-0.3.24.tgz#2c1d69fc0ae6b85b75a4d57547fc7e2d4bc117e8" + integrity sha512-1pI0G9HRblc651dCz9LXuEu/zWQk23XwOUYqJEINb/c2TTLtw5TnTRIcefxxK6RnxeJvcKfnmK0rdZp/4ujFAA== dependencies: "@coinbase/wallet-sdk" "^3.6.6" "@ledgerhq/connect-kit-loader" "^1.0.1" "@safe-global/safe-apps-provider" "^0.15.2" "@safe-global/safe-apps-sdk" "^7.9.0" - "@walletconnect/ethereum-provider" "2.8.1" + "@walletconnect/ethereum-provider" "2.9.0" "@walletconnect/legacy-provider" "^2.0.0" - "@walletconnect/modal" "^2.4.6" + "@walletconnect/modal" "^2.5.9" abitype "^0.3.0" eventemitter3 "^4.0.7" -"@wagmi/core@0.10.15": - version "0.10.15" - resolved "https://registry.yarnpkg.com/@wagmi/core/-/core-0.10.15.tgz#b9304bc0df07ebdddf6a9c26eef49825d4065964" - integrity sha512-rCrCVk28BxO8smLtBBnCZkvWFU1jI61x6DUidXAMagQ5yZdiDTr/YZpJzOkiR09fQCKq62INyRkJlRsk43SEoQ== +"@wagmi/core@0.10.17": + version "0.10.17" + resolved "https://registry.yarnpkg.com/@wagmi/core/-/core-0.10.17.tgz#d2a641c3c608cad813e9eed290769d577512d935" + integrity sha512-qud45y3IlHp7gYWzoFeyysmhyokRie59Xa5tcx5F1E/v4moD5BY0kzD26mZW/ZQ3WZuVK/lZwiiPRqpqWH52Gw== dependencies: "@wagmi/chains" "0.2.22" - "@wagmi/connectors" "0.3.21" + "@wagmi/connectors" "0.3.24" abitype "^0.3.0" eventemitter3 "^4.0.7" zustand "^4.3.1" -"@walletconnect/core@2.8.1": - version "2.8.1" - resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.8.1.tgz#f74404af372a11e05c214cbc14b5af0e9c0cf916" - integrity sha512-mN9Zkdl/NeThntK8cydDoQOW6jUEpOeFgYR1RCKPLH51VQwlbdSgvvQIeanSQXEY4U7AM3x8cs1sxqMomIfRQg== +"@walletconnect/core@2.9.0": + version "2.9.0" + resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.9.0.tgz#7837a5d015a22b48d35b987bcde2aa9ccdf300d8" + integrity sha512-MZYJghS9YCvGe32UOgDj0mCasaOoGHQaYXWeQblXE/xb8HuaM6kAWhjIQN9P+MNp5QP134BHP5olQostcCotXQ== dependencies: "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-provider" "1.0.13" "@walletconnect/jsonrpc-types" "1.0.3" "@walletconnect/jsonrpc-utils" "1.0.8" - "@walletconnect/jsonrpc-ws-connection" "^1.0.11" + "@walletconnect/jsonrpc-ws-connection" "1.0.12" "@walletconnect/keyvaluestorage" "^1.0.2" "@walletconnect/logger" "^2.0.1" "@walletconnect/relay-api" "^1.0.9" "@walletconnect/relay-auth" "^1.0.4" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.8.1" - "@walletconnect/utils" "2.8.1" + "@walletconnect/types" "2.9.0" + "@walletconnect/utils" "2.9.0" events "^3.3.0" lodash.isequal "4.5.0" uint8arrays "^3.1.0" @@ -3885,19 +3931,19 @@ dependencies: tslib "1.14.1" -"@walletconnect/ethereum-provider@2.8.1": - version "2.8.1" - resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.8.1.tgz#1743072f42b5c940648b0303a382e8907a362a00" - integrity sha512-YlF8CCiFTSEZRyANIBsop/U+t+d1Z1/UXXoE9+iwjSGKJsaym6PgBLPb2d8XdmS/qR6Tcx7lVodTp4cVtezKnA== +"@walletconnect/ethereum-provider@2.9.0": + version "2.9.0" + resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.9.0.tgz#aa6e9e441678c824af8f744c50dafd604f19d69e" + integrity sha512-rSXkC0SXMigJRdIi/M2RMuEuATY1AwtlTWQBnqyxoht7xbO2bQNPCXn0XL4s/GRNrSUtoKSY4aPMHXV4W4yLBA== dependencies: "@walletconnect/jsonrpc-http-connection" "^1.0.7" "@walletconnect/jsonrpc-provider" "^1.0.13" "@walletconnect/jsonrpc-types" "^1.0.3" "@walletconnect/jsonrpc-utils" "^1.0.8" - "@walletconnect/sign-client" "2.8.1" - "@walletconnect/types" "2.8.1" - "@walletconnect/universal-provider" "2.8.1" - "@walletconnect/utils" "2.8.1" + "@walletconnect/sign-client" "2.9.0" + "@walletconnect/types" "2.9.0" + "@walletconnect/universal-provider" "2.9.0" + "@walletconnect/utils" "2.9.0" events "^3.3.0" "@walletconnect/events@^1.0.1": @@ -3970,10 +4016,10 @@ "@walletconnect/jsonrpc-types" "^1.0.2" tslib "1.14.1" -"@walletconnect/jsonrpc-ws-connection@^1.0.11": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.11.tgz#1ce59d86f273d576ca73385961303ebd44dd923f" - integrity sha512-TiFJ6saasKXD+PwGkm5ZGSw0837nc6EeFmurSPgIT/NofnOV4Tv7CVJqGQN0rQYoJUSYu21cwHNYaFkzNpUN+w== +"@walletconnect/jsonrpc-ws-connection@1.0.12": + version "1.0.12" + resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.12.tgz#2192314884fabdda6d0a9d22e157e5b352025ed8" + integrity sha512-HAcadga3Qjt1Cqy+qXEW6zjaCs8uJGdGQrqltzl3OjiK4epGZRdvSzTe63P+t/3z+D2wG+ffEPn0GVcDozmN1w== dependencies: "@walletconnect/jsonrpc-utils" "^1.0.6" "@walletconnect/safe-json" "^1.0.2" @@ -4057,7 +4103,7 @@ pino "7.11.0" tslib "1.14.1" -"@walletconnect/modal@^2.4.6", "@walletconnect/modal@~2.4.7": +"@walletconnect/modal@^2.5.9", "@walletconnect/modal@~2.4.7": version "2.4.7" resolved "https://registry.yarnpkg.com/@walletconnect/modal/-/modal-2.4.7.tgz#fd84d6f1ac767865d63153e32150f790739a189a" integrity sha512-kFpvDTT44CgNGcwQVC0jHrYed4xorghKX1DOGo8ZfBSJ5TJx3p6d6SzLxkH1cZupWbljWkYS6SqvZcUBs8vWpg== @@ -4109,19 +4155,19 @@ dependencies: tslib "1.14.1" -"@walletconnect/sign-client@2.8.1": - version "2.8.1" - resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.8.1.tgz#8c6de724eff6a306c692dd66e66944089be5e30a" - integrity sha512-6DbpjP9BED2YZOZdpVgYo0HwPBV7k99imnsdMFrTn16EFAxhuYP0/qPwum9d072oNMGWJSA6d4rzc8FHNtHsCA== +"@walletconnect/sign-client@2.9.0": + version "2.9.0" + resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.9.0.tgz#fd3b0acb68bc8d56350f01ed70f8c6326e6e89fa" + integrity sha512-mEKc4LlLMebCe45qzqh+MX4ilQK4kOEBzLY6YJpG8EhyT45eX4JMNA7qQoYa9MRMaaVb/7USJcc4e3ZrjZvQmA== dependencies: - "@walletconnect/core" "2.8.1" + "@walletconnect/core" "2.9.0" "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/logger" "^2.0.1" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.8.1" - "@walletconnect/utils" "2.8.1" + "@walletconnect/types" "2.9.0" + "@walletconnect/utils" "2.9.0" events "^3.3.0" "@walletconnect/time@^1.0.2": @@ -4131,10 +4177,10 @@ dependencies: tslib "1.14.1" -"@walletconnect/types@2.8.1": - version "2.8.1" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.8.1.tgz#640eb6ad23866886fbe09a9b29832bf3f8647a09" - integrity sha512-MLISp85b+27vVkm3Wkud+eYCwySXCdOrmn0yQCSN6DnRrrunrD05ksz4CXGP7h2oXUvvXPDt/6lXBf1B4AfqrA== +"@walletconnect/types@2.9.0": + version "2.9.0" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.9.0.tgz#6e5dfdc7212c1ec4ab49a1ec409c743e16093f72" + integrity sha512-ORopsMfSRvUYqtjKKd6scfg8o4/aGebipLxx92AuuUgMTERSU6cGmIrK6rdLu7W6FBJkmngPLEGc9mRqAb9Lug== dependencies: "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" @@ -4143,26 +4189,25 @@ "@walletconnect/logger" "^2.0.1" events "^3.3.0" -"@walletconnect/universal-provider@2.8.1": - version "2.8.1" - resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.8.1.tgz#3fc51c56d1c94a02eb952f9bf948293cc7aace7e" - integrity sha512-6shgE4PM/S+GEh9oTWMloHZlt2BLsCitRn9tBh2Vf+jZiGlug3WNm+tBc/Fo6ILyHuzeYPbkzCM67AxcutOHGQ== +"@walletconnect/universal-provider@2.9.0": + version "2.9.0" + resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.9.0.tgz#a6b4a1f099262536e17b5c25bf7b3c89db9945a8" + integrity sha512-k3nkSBkF69sJJVoe17IVoPtnhp/sgaa2t+x7BvA/BKeMxE0DGdtRJdEXotTc8DBmI7o2tkq6l8+HyFBGjQ/CjQ== dependencies: "@walletconnect/jsonrpc-http-connection" "^1.0.7" "@walletconnect/jsonrpc-provider" "1.0.13" "@walletconnect/jsonrpc-types" "^1.0.2" "@walletconnect/jsonrpc-utils" "^1.0.7" "@walletconnect/logger" "^2.0.1" - "@walletconnect/sign-client" "2.8.1" - "@walletconnect/types" "2.8.1" - "@walletconnect/utils" "2.8.1" - eip1193-provider "1.0.1" + "@walletconnect/sign-client" "2.9.0" + "@walletconnect/types" "2.9.0" + "@walletconnect/utils" "2.9.0" events "^3.3.0" -"@walletconnect/utils@2.8.1": - version "2.8.1" - resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.8.1.tgz#1356f4bba7f8b6664fc5b61ce3497596c8d9d603" - integrity sha512-d6p9OX3v70m6ijp+j4qvqiQZQU1vbEHN48G8HqXasyro3Z+N8vtcB5/gV4pTYsbWgLSDtPHj49mzbWQ0LdIdTw== +"@walletconnect/utils@2.9.0": + version "2.9.0" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.9.0.tgz#c73925edb9fefe79021bcf028e957028f986b728" + integrity sha512-7Tu3m6dZL84KofrNBcblsgpSqU2vdo9ImLD7zWimLXERVGNQ8smXG+gmhQYblebIBhsPzjy9N38YMC3nPlfQNw== dependencies: "@stablelib/chacha20poly1305" "1.0.1" "@stablelib/hkdf" "1.0.1" @@ -4172,7 +4217,7 @@ "@walletconnect/relay-api" "^1.0.9" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.8.1" + "@walletconnect/types" "2.9.0" "@walletconnect/window-getters" "^1.0.1" "@walletconnect/window-metadata" "^1.0.1" detect-browser "5.3.0" @@ -4194,7 +4239,7 @@ "@walletconnect/window-getters" "^1.0.1" tslib "1.14.1" -"@web3-react/abstract-connector@6", "@web3-react/abstract-connector@6.0.7", "@web3-react/abstract-connector@^6.0.7": +"@web3-react/abstract-connector@6.0.7", "@web3-react/abstract-connector@^6.0.7": version "6.0.7" resolved "https://registry.yarnpkg.com/@web3-react/abstract-connector/-/abstract-connector-6.0.7.tgz#401b3c045f1e0fab04256311be49d5144e9badc6" integrity sha512-RhQasA4Ox8CxUC0OENc1AJJm8UTybu/oOCM61Zjg6y0iF7Z0sqv1Ai1VdhC33hrQpA8qSBgoXN9PaP8jKmtdqg== @@ -4221,7 +4266,7 @@ "@web3-react/types" "^6.0.7" tiny-warning "^1.0.3" -"@web3-react/types@6", "@web3-react/types@^6.0.7": +"@web3-react/types@^6.0.7": version "6.0.7" resolved "https://registry.yarnpkg.com/@web3-react/types/-/types-6.0.7.tgz#34a6204224467eedc6123abaf55fbb6baeb2809f" integrity sha512-ofGmfDhxmNT1/P/MgVa8IKSkCStFiyvXe+U5tyZurKdrtTDFU+wJ/LxClPDtFerWpczNFPUSrKcuhfPX1sI6+A== @@ -4522,6 +4567,11 @@ async-mutex@^0.2.6: dependencies: tslib "^2.0.0" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" @@ -4537,20 +4587,22 @@ axe-core@^4.6.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== -axios@^0.21.0: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - -axios@^0.26.1: +axios@0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== dependencies: follow-redirects "^1.14.8" +axios@^1.3.4: + version "1.5.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f" + integrity sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" @@ -4692,11 +4744,16 @@ bigint-buffer@^1.1.5: dependencies: bindings "^1.3.0" -bignumber.js@^9.1.0, bignumber.js@^9.1.1: +bignumber.js@^9.1.1: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== +bignumber.js@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + bind-decorator@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/bind-decorator/-/bind-decorator-1.0.11.tgz#e41bc06a1f65dd9cec476c91c5daf3978488252f" @@ -5063,6 +5120,13 @@ colorette@^2.0.19: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + command-line-args@^5.1.1: version "5.2.1" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" @@ -5259,7 +5323,7 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-js@^4.1.1: +crypto-js@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== @@ -5426,6 +5490,11 @@ delay@^5.0.0: resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + depd@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -5548,12 +5617,12 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -eip1193-provider@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/eip1193-provider/-/eip1193-provider-1.0.1.tgz#420d29cf4f6c443e3f32e718fb16fafb250637c3" - integrity sha512-kSuqwQ26d7CzuS/t3yRXo2Su2cVH0QfvyKbr2H7Be7O5YDyIq4hQGCNTo5wRdP07bt+E2R/8nPCzey4ojBHf7g== +eip55@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/eip55/-/eip55-2.1.1.tgz#28b743c4701ac3c811b1e9fe67e39cf1d0781b96" + integrity sha512-WcagVAmNu2Ww2cDUfzuWVntYwFxbvZ5MvIyLZpMjTTkjD6sCvkGOiS86jTppzu9/gWsc8isLHAeMBWK02OnZmA== dependencies: - "@json-rpc-tools/provider" "^1.5.5" + keccak "^3.0.3" electron-to-chromium@^1.4.284: version "1.4.295" @@ -6193,7 +6262,7 @@ ethers@5.5.4: "@ethersproject/web" "5.5.1" "@ethersproject/wordlists" "5.5.0" -ethers@^5.4.7, ethers@^5.7.2: +ethers@5.7.2, ethers@^5.4.7, ethers@^5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -6474,11 +6543,16 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.14.0, follow-redirects@^1.14.8: +follow-redirects@^1.14.8: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.15.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -6486,6 +6560,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fs-extra@^11.0.0: version "11.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" @@ -6905,7 +6988,7 @@ internal-slot@^1.0.3, internal-slot@^1.0.4: has "^1.0.3" side-channel "^1.0.4" -invariant@2: +invariant@2, invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -7737,6 +7820,15 @@ keccak@^3.0.0, keccak@^3.0.1, keccak@^3.0.2: node-gyp-build "^4.2.0" readable-stream "^3.6.0" +keccak@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + keyvaluestorage-interface@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz#13ebdf71f5284ad54be94bd1ad9ed79adad515ff" @@ -7972,6 +8064,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -8065,7 +8162,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27: +mime-types@^2.1.12, mime-types@^2.1.27: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -8769,6 +8866,11 @@ proxy-compare@2.5.1: resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.5.1.tgz#17818e33d1653fbac8c2ec31406bce8a2966f600" integrity sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" @@ -8858,7 +8960,7 @@ react-collapsed@3.0.2: raf "^3.4.1" tiny-warning "^1.0.3" -react-dom@^17: +react-dom@^17, react-dom@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -8931,7 +9033,7 @@ react-use@17.3.2: ts-easing "^0.2.0" tslib "^2.1.0" -react@^17: +react@^17, react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -9010,19 +9112,20 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== -reef-knot@^1.4.5: - version "1.4.5" - resolved "https://registry.yarnpkg.com/reef-knot/-/reef-knot-1.4.5.tgz#edd20ea04e7a838fae8e678bc71414ece14cfc99" - integrity sha512-QZj7/iWcJ8YNaT4bP/6s/ma5eofYqMOdm8xk6eTN2E7n9JZFuMWlIIYvNHBaSKF1/ldF9UNLyfsyPTtnJEGDYQ== - dependencies: - "@reef-knot/connect-wallet-modal" "1.4.3" - "@reef-knot/core-react" "1.4.2" - "@reef-knot/types" "1.2.1" - "@reef-knot/ui-react" "1.0.5" - "@reef-knot/wallets-helpers" "1.1.2" +reef-knot@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/reef-knot/-/reef-knot-1.8.0.tgz#1b559841d39c8554fec5941595c3b411654ae73a" + integrity sha512-5jR4liihZVhrcb84xjRImDRqb0d2t8EiHfrVO8yQRfIxF3hA/CNFAtLEjyoXSBeYuFCwe9dAHQdW0v6kmSmMuw== + dependencies: + "@reef-knot/connect-wallet-modal" "1.6.2" + "@reef-knot/core-react" "1.6.0" + "@reef-knot/ledger-connector" "1.0.1" + "@reef-knot/types" "1.3.0" + "@reef-knot/ui-react" "1.0.7" + "@reef-knot/wallets-helpers" "1.1.5" "@reef-knot/wallets-icons" "1.0.0" - "@reef-knot/wallets-list" "1.4.2" - "@reef-knot/web3-react" "1.2.2" + "@reef-knot/wallets-list" "1.4.5" + "@reef-knot/web3-react" "1.5.0" regenerate-unicode-properties@^10.1.0: version "10.1.0" @@ -9233,7 +9336,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@6, rxjs@^6.4.0, rxjs@^6.6.3: +rxjs@6, rxjs@^6.4.0, rxjs@^6.6.3, rxjs@^6.6.7: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -9950,7 +10053,7 @@ through2@^4.0.0: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tiny-invariant@1, tiny-invariant@^1.0.6, tiny-invariant@^1.1.0, tiny-invariant@^1.2.0: +tiny-invariant@^1.0.6, tiny-invariant@^1.1.0, tiny-invariant@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== @@ -10152,7 +10255,7 @@ typedarray-to-buffer@3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.6.4, typescript@^4.9.4: +typescript@^4.6.4, typescript@^4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -10279,7 +10382,7 @@ util@^0.12.4: is-typed-array "^1.1.3" which-typed-array "^1.1.2" -utility-types@3.10.0: +utility-types@3.10.0, utility-types@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== @@ -10334,15 +10437,15 @@ vary@^1: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -wagmi@0.12.17: - version "0.12.17" - resolved "https://registry.yarnpkg.com/wagmi/-/wagmi-0.12.17.tgz#ae6787acb747ddfc6d9da3335cec559c8051731e" - integrity sha512-0HArKpVI0nlek135d8LfrIQv38pzCSOZVVUOHGdPS8Mweypeb3niCAHbIjr5ERXhLsoZO8jf9eSUML6ErdXxog== +wagmi@0.12.19: + version "0.12.19" + resolved "https://registry.yarnpkg.com/wagmi/-/wagmi-0.12.19.tgz#5f5038330907f70c033ea51ef8a9136289567256" + integrity sha512-S/el9BDb/HNeQWh1v8TvntMPX/CgKLDAoJqDb8i7jifLfWPqFL7gor3vnI1Vs6ZlB8uh7m+K1Qyg+mKhbITuDQ== dependencies: "@tanstack/query-sync-storage-persister" "^4.27.1" "@tanstack/react-query" "^4.28.0" "@tanstack/react-query-persist-client" "^4.28.0" - "@wagmi/core" "0.10.15" + "@wagmi/core" "0.10.17" abitype "^0.3.0" use-sync-external-store "^1.2.0" @@ -10353,35 +10456,6 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" -web3-ledgerhq-connector@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/web3-ledgerhq-connector/-/web3-ledgerhq-connector-1.2.3.tgz#e3331a15cbe1b82f135e83ecff97479eb12ffa0e" - integrity sha512-g321ggmdk1PJowedIPZYdX3sfNqKYZ4QXeBdUTAcwiK1rdhdOlHF7HbQkhlZESCdNnG+/enUPm7cdvEO6JPg3w== - dependencies: - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/providers" "^5.5.2" - "@ethersproject/strings" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - "@ledgerhq/hw-app-eth" "^6.22.3" - "@ledgerhq/hw-transport" "^6.20.0" - "@ledgerhq/hw-transport-webhid" "^6.20.0" - "@web3-react/abstract-connector" "^6.0.7" - "@web3-react/types" "^6.0.7" - tiny-invariant "^1.2.0" - -web3-ledgerhq-frame-connector@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/web3-ledgerhq-frame-connector/-/web3-ledgerhq-frame-connector-1.0.1.tgz#7554fb5e9d1da19e1ab24e434dbc4d0c012c0527" - integrity sha512-AnSISDK0csoi2V/dMAjcomK8ZbFAYk22KArSoG/chDKlvLgxBgXafWheQPgV7540Efd/wMbtcjo4NotY2M3nDA== - dependencies: - "@ledgerhq/iframe-provider" "0" - "@web3-react/abstract-connector" "6" - "@web3-react/types" "6" - tiny-invariant "1" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -10506,7 +10580,7 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@^7.4.0, ws@^7.4.5, ws@^7.5.1: +ws@^7.4.5, ws@^7.5.1: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==