From af44d3118a3bccd697b7aa9eecc9d10c4f41d34f Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Thu, 28 Sep 2023 15:41:06 +0300 Subject: [PATCH 01/76] feat: rebased (hash routing, settings page, ClientConfigProvider, RPC health check, stats, CSP) --- .env.example | 25 +- README.md | 14 +- abi/oracle.abi.json | 528 ------------------ assets/icons/gear.svg | 1 + config/aggregator.ts | 35 +- config/index.ts | 3 +- config/ipfs.ts | 8 + config/oracle.ts | 19 - config/rpc.ts | 26 +- config/storage.ts | 2 + config/types.ts | 15 + env-dynamics.mjs | 13 +- features/home/lido-stats/lido-stats.tsx | 15 + features/home/oneinch-info/oneinch-info.tsx | 9 +- .../stake-faq/list/how-can-i-get-steth.tsx | 19 +- .../list/how-can-i-unstake-steth.tsx | 21 +- features/ipfs/ipfs-base-script.tsx | 20 + features/ipfs/ipfs-info-box/index.tsx | 1 + features/ipfs/ipfs-info-box/ipfs-info-box.tsx | 109 ++++ features/ipfs/ipfs-info-box/styles.tsx | 110 ++++ .../components/rewardsTable/RewardsTable.tsx | 2 +- features/settings/settings-form/index.ts | 2 + .../settings/settings-form/settings-form.tsx | 138 +++++ features/settings/settings-form/styles.ts | 49 ++ .../claim/form/requests-list/request-item.tsx | 4 +- .../form-controller/form-controller.tsx | 19 +- .../list/do_i_need_to_unwrap_my_wsteth.tsx | 21 +- .../wrap-faq/list/how-can-i-get-wsteth.tsx | 21 +- .../how-could-i-unwrap-wsteth-to-steth.tsx | 21 +- next.config.mjs | 60 +- package.json | 27 +- pages/_app.tsx | 48 +- pages/_document.tsx | 43 +- pages/_home/home-page-ipfs.tsx | 102 ++++ pages/_home/home-page-regular.tsx | 32 ++ pages/index.tsx | 29 +- pages/settings.tsx | 18 + pages/withdrawals/[mode].tsx | 7 +- pages/wrap/[[...mode]].tsx | 5 +- providers/custom-config.tsx | 77 +++ providers/web3.tsx | 107 ++-- shared/components/footer/footer.tsx | 32 ++ shared/components/footer/styles.tsx | 20 +- .../components/header-control-button.tsx | 33 ++ .../components/header-settings-button.tsx | 20 + .../header/components/header-wallet.tsx | 19 +- .../components/navigation/navigation.tsx | 16 +- .../header/components/navigation/styles.tsx | 6 +- shared/components/header/styles.tsx | 11 +- shared/components/link-ipfs.tsx | 27 + shared/components/local-link/index.tsx | 21 +- shared/components/switch/styles.tsx | 16 +- shared/components/switch/switch-item.tsx | 18 +- .../controls/token-select-hook-form.tsx | 13 +- shared/hooks/use-prefixed-history.ts | 26 + shared/hooks/use-router-path.ts | 25 + shared/hooks/useLidoStats.ts | 10 +- shared/hooks/useWeb3Key.ts | 9 + types/components.ts | 9 +- utils/appCookies.ts | 55 -- utils/assert.ts | 16 - utils/chains.ts | 1 + utils/check-rpc-url.ts | 33 ++ utils/get-ipfs-base-path.ts | 13 + utils/getErrorMessage.ts | 4 +- utils/getNFTUrl.ts | 2 + utils/index.ts | 1 - utils/is-url.ts | 6 + utils/isClientSide.ts | 3 + utils/parse-env-config.ts | 11 + utilsApi/getEthPrice.ts | 7 +- utilsApi/getStEthPrice.ts | 11 +- utilsApi/getSubgraphUrl.ts | 1 + utilsApi/rpcUrls.ts | 3 +- utilsApi/withCSP.ts | 56 +- yarn.lock | 132 +++-- 76 files changed, 1504 insertions(+), 1007 deletions(-) delete mode 100644 abi/oracle.abi.json create mode 100644 assets/icons/gear.svg create mode 100644 config/ipfs.ts delete mode 100644 config/oracle.ts create mode 100644 config/types.ts create mode 100644 features/ipfs/ipfs-base-script.tsx create mode 100644 features/ipfs/ipfs-info-box/index.tsx create mode 100644 features/ipfs/ipfs-info-box/ipfs-info-box.tsx create mode 100644 features/ipfs/ipfs-info-box/styles.tsx create mode 100644 features/settings/settings-form/index.ts create mode 100644 features/settings/settings-form/settings-form.tsx create mode 100644 features/settings/settings-form/styles.ts create mode 100644 pages/_home/home-page-ipfs.tsx create mode 100644 pages/_home/home-page-regular.tsx create mode 100644 pages/settings.tsx create mode 100644 providers/custom-config.tsx create mode 100644 shared/components/header/components/header-control-button.tsx create mode 100644 shared/components/header/components/header-settings-button.tsx create mode 100644 shared/components/link-ipfs.tsx create mode 100644 shared/hooks/use-prefixed-history.ts create mode 100644 shared/hooks/use-router-path.ts create mode 100644 shared/hooks/useWeb3Key.ts delete mode 100644 utils/assert.ts create mode 100644 utils/check-rpc-url.ts create mode 100644 utils/get-ipfs-base-path.ts create mode 100644 utils/is-url.ts create mode 100644 utils/isClientSide.ts create mode 100644 utils/parse-env-config.ts diff --git a/.env.example b/.env.example index 611b7d0ce..53bcd2640 100644 --- a/.env.example +++ b/.env.example @@ -1,15 +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= - -# depracated -# https://{NETWORK}.infura.io/v3/{INFURA_API_KEY} -INFURA_API_KEY= -# https://eth-{NETWORK}.alchemyapi.io/v2/{ALCHEMY_API_KEY} -ALCHEMY_API_KEY= +EL_RPC_URLS_17000= # supported networks for connecting wallet -SUPPORTED_CHAINS=1,5 +SUPPORTED_CHAINS=1,17000 # this chain uses when a wallet is not connected DEFAULT_CHAIN=1 @@ -17,11 +12,6 @@ DEFAULT_CHAIN=1 # api key for ethplorer for token data ETHPLORER_API_KEY=freekey -# Variables to read/update staking apr stats on cloudflare vk storage -# Not necessary for development -CLOUDFLARE_API_TOKEN= -CLOUDFLARE_ACCOUNT_ID= -CLOUDFLARE_KV_NAMESPACE_ID= # comma-separated trusted hosts for Content Security Policy # e.g. http://localhost:PORT for local development @@ -35,11 +25,8 @@ CSP_REPORT_URI=https://stake.lido.fi/api/csp-report # Subgraph endpoint SUBGRAPH_MAINNET=https://api.thegraph.com/subgraphs/name/lidofinance/lido -SUBGRAPH_ROPSTEN= -SUBGRAPH_RINKEBY= SUBGRAPH_GOERLI= -SUBGRAPH_KOVAN= -SUBGRAPH_KINTSUGI= +SUBGRAPH_HOLESKY= SUBGRAPH_REQUEST_TIMEOUT=5000 @@ -63,3 +50,9 @@ MATOMO_URL= # WalletConnect project ID WALLETCONNECT_PROJECT_ID= + +# Settings prefill +PUBLIC_UNSAFE_SETTINGS_PREFILL_RPC= + +# Widget ETH API in IPFS mode (needs for `Lido statistics`) +IPFS_WIDGET_ETH_API_BASE_PATH= diff --git a/README.md b/README.md index 37fe7ba5b..fd7763220 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A widget for submitting Ether to the pool based on [Lido Frontend Template](http ### Pre-requisites -- Node.js v16+ +- Node.js v16 - Yarn package manager This project requires an .env file which is distributed via private communication channels. A sample can be found in .env.example @@ -31,6 +31,12 @@ Step 4. Start the development server yarn dev ``` +for IPFS mode below: + +```bash +yarn dev-ipfs # will start with HMR +``` + ### Environment variables Note! Avoid using `NEXT_PUBLIC_` environment variables as it hinders our CI pipeline. Please use server-side environment variables and pass them to the client using `getInitialProps` in `_app.js`. @@ -52,6 +58,12 @@ git commit -m "feat: dark theme" yarn build && yarn start ``` +for IPFS mode below: + +```bash +yarn build-ipfs +``` + ## Adding a new route API - create a new file in `pages/api/` folder diff --git a/abi/oracle.abi.json b/abi/oracle.abi.json deleted file mode 100644 index 968daabea..000000000 --- a/abi/oracle.abi.json +++ /dev/null @@ -1,528 +0,0 @@ -[ - { - "constant": true, - "inputs": [], - "name": "getCurrentOraclesReportStatus", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [{ "name": "_value", "type": "uint256" }], - "name": "setAllowedBeaconBalanceAnnualRelativeIncrease", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "hasInitialized", - "outputs": [{ "name": "", "type": "bool" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getVersion", - "outputs": [{ "name": "", "type": "uint256" }], - "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": true, - "inputs": [], - "name": "MANAGE_QUORUM", - "outputs": [{ "name": "", "type": "bytes32" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { "name": "_epochId", "type": "uint256" }, - { "name": "_beaconBalance", "type": "uint64" }, - { "name": "_beaconValidators", "type": "uint32" } - ], - "name": "reportBeacon", - "outputs": [], - "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": "getAllowedBeaconBalanceAnnualRelativeIncrease", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getAllowedBeaconBalanceRelativeDecrease", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getExpectedEpochId", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getLastCompletedReportDelta", - "outputs": [ - { "name": "postTotalPooledEther", "type": "uint256" }, - { "name": "preTotalPooledEther", "type": "uint256" }, - { "name": "timeElapsed", "type": "uint256" } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getLido", - "outputs": [{ "name": "", "type": "address" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "SET_BEACON_REPORT_RECEIVER", - "outputs": [{ "name": "", "type": "bytes32" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "MANAGE_MEMBERS", - "outputs": [{ "name": "", "type": "bytes32" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getCurrentFrame", - "outputs": [ - { "name": "frameEpochId", "type": "uint256" }, - { "name": "frameStartTime", "type": "uint256" }, - { "name": "frameEndTime", "type": "uint256" } - ], - "payable": false, - "stateMutability": "view", - "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": "_index", "type": "uint256" }], - "name": "getCurrentReportVariant", - "outputs": [ - { "name": "beaconBalance", "type": "uint64" }, - { "name": "beaconValidators", "type": "uint32" }, - { "name": "count", "type": "uint16" } - ], - "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": "getLastCompletedEpochId", - "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": false, - "inputs": [ - { - "name": "_allowedBeaconBalanceAnnualRelativeIncrease", - "type": "uint256" - }, - { "name": "_allowedBeaconBalanceRelativeDecrease", "type": "uint256" } - ], - "name": "initialize_v2", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [{ "name": "_addr", "type": "address" }], - "name": "setBeaconReportReceiver", - "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": "SET_BEACON_SPEC", - "outputs": [{ "name": "", "type": "bytes32" }], - "payable": false, - "stateMutability": "view", - "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": true, - "inputs": [], - "name": "getCurrentEpochId", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getEVMScriptRegistry", - "outputs": [{ "name": "", "type": "address" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [{ "name": "_member", "type": "address" }], - "name": "addOracleMember", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getBeaconReportReceiver", - "outputs": [{ "name": "", "type": "address" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "SET_REPORT_BOUNDARIES", - "outputs": [{ "name": "", "type": "bytes32" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [{ "name": "_quorum", "type": "uint256" }], - "name": "setQuorum", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getQuorum", - "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": "getOracleMembers", - "outputs": [{ "name": "", "type": "address[]" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "isPetrified", - "outputs": [{ "name": "", "type": "bool" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [{ "name": "_value", "type": "uint256" }], - "name": "setAllowedBeaconBalanceRelativeDecrease", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getBeaconSpec", - "outputs": [ - { "name": "epochsPerFrame", "type": "uint64" }, - { "name": "slotsPerEpoch", "type": "uint64" }, - { "name": "secondsPerSlot", "type": "uint64" }, - { "name": "genesisTime", "type": "uint64" } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { "name": "_epochsPerFrame", "type": "uint64" }, - { "name": "_slotsPerEpoch", "type": "uint64" }, - { "name": "_secondsPerSlot", "type": "uint64" }, - { "name": "_genesisTime", "type": "uint64" } - ], - "name": "setBeaconSpec", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "MAX_MEMBERS", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getCurrentReportVariantsSize", - "outputs": [{ "name": "", "type": "uint256" }], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [{ "name": "_member", "type": "address" }], - "name": "removeOracleMember", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "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" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "value", "type": "uint256" }], - "name": "AllowedBeaconBalanceAnnualRelativeIncreaseSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "value", "type": "uint256" }], - "name": "AllowedBeaconBalanceRelativeDecreaseSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "callback", "type": "address" }], - "name": "BeaconReportReceiverSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "member", "type": "address" }], - "name": "MemberAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "member", "type": "address" }], - "name": "MemberRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "quorum", "type": "uint256" }], - "name": "QuorumChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "epochId", "type": "uint256" }], - "name": "ExpectedEpochIdUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "name": "epochsPerFrame", "type": "uint64" }, - { "indexed": false, "name": "slotsPerEpoch", "type": "uint64" }, - { "indexed": false, "name": "secondsPerSlot", "type": "uint64" }, - { "indexed": false, "name": "genesisTime", "type": "uint64" } - ], - "name": "BeaconSpecSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "name": "epochId", "type": "uint256" }, - { "indexed": false, "name": "beaconBalance", "type": "uint128" }, - { "indexed": false, "name": "beaconValidators", "type": "uint128" }, - { "indexed": false, "name": "caller", "type": "address" } - ], - "name": "BeaconReported", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "name": "epochId", "type": "uint256" }, - { "indexed": false, "name": "beaconBalance", "type": "uint128" }, - { "indexed": false, "name": "beaconValidators", "type": "uint128" } - ], - "name": "Completed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { "indexed": false, "name": "postTotalPooledEther", "type": "uint256" }, - { "indexed": false, "name": "preTotalPooledEther", "type": "uint256" }, - { "indexed": false, "name": "timeElapsed", "type": "uint256" }, - { "indexed": false, "name": "totalShares", "type": "uint256" } - ], - "name": "PostTotalShares", - "type": "event" - }, - { - "anonymous": false, - "inputs": [{ "indexed": false, "name": "version", "type": "uint256" }], - "name": "ContractVersionSet", - "type": "event" - } -] diff --git a/assets/icons/gear.svg b/assets/icons/gear.svg new file mode 100644 index 000000000..6a7a50fe3 --- /dev/null +++ b/assets/icons/gear.svg @@ -0,0 +1 @@ + diff --git a/config/aggregator.ts b/config/aggregator.ts index a314f77ab..5da55d340 100644 --- a/config/aggregator.ts +++ b/config/aggregator.ts @@ -1,38 +1,19 @@ -import { CHAINS } from 'utils/chains'; -import { AggregatorAbi__factory } from 'generated'; +import { CHAINS } from '@lido-sdk/constants'; +import invariant from 'tiny-invariant'; -// Chainlink: ETH/USD Price Feed -// https://data.chain.link/ethereum/mainnet/crypto-usd/eth-usd -// https://etherscan.io/address/0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 -export const AGGREGATOR_BY_NETWORK: { - [key in CHAINS]: string; -} = { - [CHAINS.Mainnet]: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419', - [CHAINS.Goerli]: '0x0000000000000000000000000000000000000000', -}; - -export const getAggregatorAddress = (chainId: CHAINS): string => { - return AGGREGATOR_BY_NETWORK[chainId]; -}; - -// Chainlink: STETH/USD Price Feed -// https://data.chain.link/ethereum/mainnet/crypto-usd/steth-usd // https://etherscan.io/address/0xcfe54b5cd566ab89272946f602d76ea879cab4a8 export const AGGREGATOR_STETH_USD_PRICE_FEED_BY_NETWORK: { - [key in CHAINS]: string; + [key in CHAINS]?: string; } = { [CHAINS.Mainnet]: '0xcfe54b5cd566ab89272946f602d76ea879cab4a8', - [CHAINS.Goerli]: '0x0000000000000000000000000000000000000000', }; +// Chainlink: STETH/USD Price Feed +// https://data.chain.link/ethereum/mainnet/crypto-usd/steth-usd export const getAggregatorStEthUsdPriceFeedAddress = ( chainId: CHAINS, ): string => { - return AGGREGATOR_STETH_USD_PRICE_FEED_BY_NETWORK[chainId]; -}; - -export type ContractAggregator = typeof AggregatorAbi__factory; - -export const getAggregatorContractFactory = (): ContractAggregator => { - return AggregatorAbi__factory; + const address = AGGREGATOR_STETH_USD_PRICE_FEED_BY_NETWORK[chainId]; + invariant(address, 'chain is not supported'); + return address; }; diff --git a/config/index.ts b/config/index.ts index 40dc96d3a..68d9674af 100644 --- a/config/index.ts +++ b/config/index.ts @@ -6,13 +6,14 @@ export * from './api'; export * from './cache'; export * from './estimate'; export * from './locale'; +export * from './ipfs'; export * from './metrics'; -export * from './oracle'; export * from './rpc'; export * from './steth'; export * from './storage'; export * from './text'; export * from './tx'; +export * from './types'; export * from './units'; export * from './metrics'; export * from './rateLimit'; diff --git a/config/ipfs.ts b/config/ipfs.ts new file mode 100644 index 000000000..b86f3ed73 --- /dev/null +++ b/config/ipfs.ts @@ -0,0 +1,8 @@ +import getConfig from 'next/config'; +const { serverRuntimeConfig } = getConfig(); +import dynamics from './dynamics'; + +// TODO: get from serverRuntimeConfig or? +const { basePath = '' } = serverRuntimeConfig; + +export const BASE_PATH_ASSET = dynamics.ipfsMode ? '.' : basePath; diff --git a/config/oracle.ts b/config/oracle.ts deleted file mode 100644 index fdfe6a8e8..000000000 --- a/config/oracle.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CHAINS } from 'utils/chains'; -import { OracleAbi__factory } from 'generated'; - -export const ORACLE_BY_NETWORK: { - [key in CHAINS]: string; -} = { - [CHAINS.Mainnet]: '0x442af784A788A5bd6F42A01Ebe9F287a871243fb', - [CHAINS.Goerli]: '0x0000000000000000000000000000000000000000', -}; - -export const getOracleAddress = (chainId: CHAINS): string => { - return ORACLE_BY_NETWORK[chainId]; -}; - -export type ContractOracle = typeof OracleAbi__factory; - -export const getOracleContractFactory = (): ContractOracle => { - return OracleAbi__factory; -}; diff --git a/config/rpc.ts b/config/rpc.ts index 5e41da2c9..808e15995 100644 --- a/config/rpc.ts +++ b/config/rpc.ts @@ -1,3 +1,7 @@ +import { useCallback } from 'react'; +import { useSDK } from '@lido-sdk/react'; + +import { useCustomConfig } from 'providers/custom-config'; import { CHAINS } from 'utils/chains'; export const getBackendRPCPath = (chainId: string | number): string => { @@ -5,7 +9,23 @@ export const getBackendRPCPath = (chainId: string | number): string => { return `${BASE_URL}/api/rpc?chainId=${chainId}`; }; -export const backendRPC = { - [CHAINS.Mainnet]: getBackendRPCPath(CHAINS.Mainnet), - [CHAINS.Goerli]: getBackendRPCPath(CHAINS.Goerli), +export const useGetRpcUrl = () => { + const customConfig = useCustomConfig(); + return useCallback( + (chainId: CHAINS) => { + return ( + customConfig.savedCustomConfig.rpcUrls[chainId] || + customConfig.settingsPrefillRpc || + getBackendRPCPath(chainId) + ); + }, + [customConfig], + ); +}; + +export const useRpcUrl = () => { + const { chainId } = useSDK(); + const getRpcUrl = useGetRpcUrl(); + // TODO: use `satisfies` when will be TS v5 + return getRpcUrl(chainId as unknown as CHAINS); }; diff --git a/config/storage.ts b/config/storage.ts index b05cc73b6..b3822a17a 100644 --- a/config/storage.ts +++ b/config/storage.ts @@ -2,3 +2,5 @@ export const STORAGE_TERMS_KEY = 'lido-terms-agree'; export const STORAGE_THEME_AUTO_KEY = 'lido-theme-auto'; export const STORAGE_THEME_MANUAL_KEY = 'lido-theme-manual'; export const STORAGE_CURRENCY_KEY = 'lido-currency'; +export const STORAGE_CUSTOM_CONFIG = 'lido-custom-config'; +export const STORAGE_IPFS_INFO_DISMISS = 'lido-ipfs-info-dismiss'; diff --git a/config/types.ts b/config/types.ts new file mode 100644 index 000000000..9f7190e8f --- /dev/null +++ b/config/types.ts @@ -0,0 +1,15 @@ +export type EnvConfigRaw = { + defaultChain: string | number; + supportedChains: number[]; + settingsPrefillRpc: string; + ipfsMode: boolean; + walletconnectProjectId: string; +}; + +export type EnvConfigParsed = { + defaultChain: number; + supportedChainIds: number[]; + settingsPrefillRpc?: string; + ipfsMode: boolean; + walletconnectProjectId: string; +}; diff --git a/env-dynamics.mjs b/env-dynamics.mjs index 4ef13480d..69e469b1f 100644 --- a/env-dynamics.mjs +++ b/env-dynamics.mjs @@ -18,12 +18,12 @@ const toBoolean = (dataStr) => { /** @type string */ export const matomoHost = process.env.MATOMO_URL; /** @type number */ -export const defaultChain = parseInt(process.env.DEFAULT_CHAIN, 10) || 1; +export const defaultChain = parseInt(process.env.DEFAULT_CHAIN, 10) || 17000; /** @type number[] */ export const supportedChains = process.env?.SUPPORTED_CHAINS?.split(',').map( (chainId) => parseInt(chainId, 10), -) ?? [1, 4, 5]; +) ?? [17000]; /** @type boolean */ export const enableQaHelpers = toBoolean(process.env.ENABLE_QA_HELPERS); /** @type string */ @@ -32,3 +32,12 @@ export const ethAPIBasePath = process.env.ETH_API_BASE_PATH; export const wqAPIBasePath = process.env.WQ_API_BASE_PATH; /** @type string */ export const walletconnectProjectId = process.env.WALLETCONNECT_PROJECT_ID; + +/** @type boolean */ +export const ipfsMode = toBoolean(process.env.IPFS_MODE); + +/** @type string */ +export const settingsPrefillRpc = process.env.PUBLIC_UNSAFE_SETTINGS_PREFILL_RPC; + +/** @type string */ +export const ipfsWidgetEthApiBasePath = process.env.IPFS_WIDGET_ETH_API_BASE_PATH; diff --git a/features/home/lido-stats/lido-stats.tsx b/features/home/lido-stats/lido-stats.tsx index 995735f30..e9abdf5d4 100644 --- a/features/home/lido-stats/lido-stats.tsx +++ b/features/home/lido-stats/lido-stats.tsx @@ -14,10 +14,15 @@ import { LIDO_APR_TOOLTIP_TEXT, DATA_UNAVAILABLE, MATOMO_CLICK_EVENTS_TYPES, + dynamics, } from 'config'; import { useLidoApr, useLidoStats } from 'shared/hooks'; import { FlexCenterVertical } from './styles'; +const isStatItemNotAvailable = (val: unknown) => { + return !val || val === 'N/A'; +}; + export const LidoStats: FC = memo(() => { const { chainId } = useSDK(); const etherscanLink = useMemo(() => { @@ -29,6 +34,16 @@ export const LidoStats: FC = memo(() => { const lidoApr = useLidoApr(); const lidoStats = useLidoStats(); + if ( + dynamics.ipfsMode && + isStatItemNotAvailable(lidoApr.apr) && + isStatItemNotAvailable(lidoStats.data.totalStaked) && + isStatItemNotAvailable(lidoStats.data.stakers) && + isStatItemNotAvailable(lidoStats.data.marketCap) + ) { + return null; + } + return (
{ + const apiOneInchRatePath = '/api/oneinch-rate'; const { data, initialLoading } = useLidoSWR<{ rate: number }>( - '/api/oneinch-rate', + dynamics.ipfsMode + ? `${dynamics.ipfsWidgetEthApiBasePath}/${apiOneInchRatePath}` + : prependBasePath(apiOneInchRatePath), ); const rate = (data && data.rate) || 1; const discount = (100 - (1 / rate) * 100).toFixed(2); diff --git a/features/home/stake-faq/list/how-can-i-get-steth.tsx b/features/home/stake-faq/list/how-can-i-get-steth.tsx index 7f958d6d6..226f16249 100644 --- a/features/home/stake-faq/list/how-can-i-get-steth.tsx +++ b/features/home/stake-faq/list/how-can-i-get-steth.tsx @@ -10,17 +10,14 @@ export const HowCanIGetSteth: FC = () => {

You can get stETH many ways, including interacting with the smart contract directly.Yet, it is much easier to use a{' '} - - - trackMatomoEvent( - MATOMO_CLICK_EVENTS_TYPES.faqHowCanIGetStEthWidget, - ) - } - aria-hidden="true" - > - Lido Ethereum staking widget - + + trackMatomoEvent(MATOMO_CLICK_EVENTS_TYPES.faqHowCanIGetStEthWidget) + } + aria-hidden="true" + > + Lido Ethereum staking widget {' '} and in other{' '} {

You can use our{' '} - - - trackMatomoEvent( - MATOMO_CLICK_EVENTS_TYPES.faqHowCanIUnstakeStEthWithdrawals, - ) - } - aria-hidden="true" - > - Withdrawals Request and Claim tabs - + + trackMatomoEvent( + MATOMO_CLICK_EVENTS_TYPES.faqHowCanIUnstakeStEthWithdrawals, + ) + } + aria-hidden="true" + > + Withdrawals Request and Claim tabs {' '} to unstake stETH and receive ETH at a 1:1 ratio. Under normal circumstances, withdrawal period can take anywhere between 1-5 days. diff --git a/features/ipfs/ipfs-base-script.tsx b/features/ipfs/ipfs-base-script.tsx new file mode 100644 index 000000000..e8e9bd186 --- /dev/null +++ b/features/ipfs/ipfs-base-script.tsx @@ -0,0 +1,20 @@ +// IPFS Next.js configuration reference: +// https://github.com/Velenir/nextjs-ipfs-example + +let ipfsBaseScript = ''; + +// #!if IPFS_MODE === "true" +ipfsBaseScript = ` +(function () { + const base = document.createElement('base') + base.href = window.location.pathname + document.head.append(base) +})(); +`; +// #!endif + +export const InsertIpfsBaseScript = () => { + return ipfsBaseScript ? ( +