-
Notifications
You must be signed in to change notification settings - Fork 29
Rpc fix clean pr #642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Rpc fix clean pr #642
Changes from all commits
207fad2
88102f6
60fc64b
5241add
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| name: Jest Tests | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: | ||
| - master | ||
|
|
||
| jobs: | ||
| jest: | ||
| name: Run Jest tests | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Check out Git repository | ||
| uses: actions/checkout@v3 | ||
|
|
||
| - name: Set up Node | ||
| uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: 20 | ||
|
|
||
| - name: Install dependencies | ||
| run: yarn install --immutable | ||
|
|
||
| - name: Run Jest tests | ||
| run: yarn test --testPathPattern="rpcParsing" --testTimeout=30000 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| module.exports = { | ||
| presets: [ | ||
| ['@babel/preset-env', { targets: { node: 'current' } }], | ||
| '@babel/preset-typescript', | ||
| ], | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| const nodeFetch = require('node-fetch') | ||
| const globalObject = typeof globalThis !== 'undefined' ? globalThis : global | ||
| if (!globalObject.fetch) { | ||
| globalObject.fetch = nodeFetch.default || nodeFetch | ||
| globalObject.Request = nodeFetch.Request | ||
| globalObject.Response = nodeFetch.Response | ||
| globalObject.Headers = nodeFetch.Headers | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,8 +26,10 @@ | |
| "@apollo/react-components": "^3.1.5", | ||
| "@apollo/react-hooks": "^3.1.5", | ||
| "@babel/core": "^7.15.8", | ||
| "@babel/preset-env": "^7.29.7", | ||
| "@babel/preset-flow": "^7.18.6", | ||
| "@babel/preset-react": "^7.18.6", | ||
| "@babel/preset-typescript": "^7.29.7", | ||
| "@commitlint/cli": "^12.1.1", | ||
| "@commitlint/config-conventional": "^12.1.1", | ||
| "@ethersproject/experimental": "^5.0.1", | ||
|
|
@@ -87,6 +89,7 @@ | |
| "@web3-react/core": "^6.1.9", | ||
| "ajv": "^6.12.3", | ||
| "autoprefixer": "^9", | ||
| "babel-jest": "29", | ||
| "babel-plugin-import": "^1.13.5", | ||
| "babel-plugin-macros": "^3.1.0", | ||
| "babel-plugin-react-native-web": "^0.18.10", | ||
|
|
@@ -114,6 +117,8 @@ | |
| "graphql": "^15.5.1", | ||
| "husky": "^8.0.2", | ||
| "jazzicon": "^1.5.0", | ||
| "jest": "29", | ||
| "jest-environment-jsdom": "^30.4.1", | ||
|
Comment on lines
+120
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): The Jest core version and
Comment on lines
+120
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Adding Jest 29 plus Useful? React with 👍 / 👎. |
||
| "jotai-immer": "^0.2.0", | ||
| "jsbi": "^3.1.5", | ||
| "lint-staged": "^13.1.0", | ||
|
|
@@ -122,6 +127,7 @@ | |
| "luxon": "^1.25.0", | ||
| "multicodec": "^2.0.0", | ||
| "multihashes": "^3.0.1", | ||
| "node-fetch": "2", | ||
| "node-vibrant": "^3.1.5", | ||
| "numeral": "^2.0.6", | ||
| "patch-package": "^6.4.7", | ||
|
|
@@ -165,6 +171,7 @@ | |
| "swr": "^0.5.5", | ||
| "tailwindcss": "^3.3.2", | ||
| "tailwindcss-border-gradient-radius": "^2.0.0", | ||
| "ts-jest": "^29.4.11", | ||
| "use-count-up": "^2.2.5", | ||
| "vercel": "^52.0.0", | ||
| "vite": "^4.3.5", | ||
|
|
@@ -250,5 +257,12 @@ | |
| "wagmi": "^2.14.13", | ||
| "web3": "1.8.2" | ||
| }, | ||
| "packageManager": "yarn@3.6.1" | ||
| "packageManager": "yarn@3.6.1", | ||
| "jest": { | ||
| "testEnvironment": "jsdom", | ||
| "setupFiles": [ | ||
| "./jest.setup.js" | ||
| ], | ||
| "testTimeout": 30000 | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react' | |
| import { t } from '@lingui/macro' | ||
| import { useLingui } from '@lingui/react' | ||
| import { SupportedChains, useSwitchNetwork } from '@gooddollar/web3sdk-v2' | ||
| import { Text, Link } from 'native-base' | ||
| import { Text, Link, Spinner, HStack } from 'native-base' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what are these file changes why are they in this pr?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was testing if the RPCs were working and noticed a bug specifically on Ethereum, so I pushed a fix. thought it minor enough that it did not matter so much |
||
| import { SwitchChainModal } from '@gooddollar/good-design' | ||
| import { ChainId } from '@sushiswap/sdk' | ||
| import { UnsupportedChainId } from '@gooddollar/web3sdk' | ||
|
|
@@ -39,11 +39,11 @@ const TextWrapper = styled.div` | |
| } | ||
| ` | ||
|
|
||
| const ChainOption = ({ chainId, chain, toggleNetworkModal, switchChain, labels, icons, error }: any) => { | ||
| const ChainOption = ({ chainId, chain, switchChain, labels, icons, error, disabled }: any) => { | ||
| const onOptionClick = useCallback(() => { | ||
| toggleNetworkModal() | ||
| if (disabled) return | ||
| switchChain(chain) | ||
| }, [switchChain, toggleNetworkModal, chain]) | ||
| }, [disabled, switchChain, chain]) | ||
|
|
||
| const isUnsupported = error instanceof UnsupportedChainId | ||
|
|
||
|
|
@@ -56,6 +56,7 @@ const ChainOption = ({ chainId, chain, toggleNetworkModal, switchChain, labels, | |
| icon={icons[chain]} | ||
| id={String(chain)} | ||
| onClick={onOptionClick} | ||
| disabled={disabled} | ||
| /> | ||
| ) | ||
| } | ||
|
|
@@ -72,6 +73,7 @@ export default function NetworkModal(): JSX.Element | null { | |
| const networkModalOpen = useModalOpen(ApplicationModal.NETWORK) | ||
| const toggleNetworkModal = useNetworkModalToggle() | ||
| const [toAddNetwork, setToAddNetwork] = useState<SupportedChains | undefined>() | ||
| const [switchingChain, setSwitchingChain] = useState(false) | ||
|
|
||
| const networkLabel: string | null = error ? null : (NETWORK_LABEL as any)[+(chainId ?? 42220)] | ||
| const network = getEnv() | ||
|
|
@@ -102,17 +104,19 @@ export default function NetworkModal(): JSX.Element | null { | |
|
|
||
| const switchChain = useCallback( | ||
| async (chain: SupportedChains) => { | ||
| setSwitchingChain(true) | ||
| try { | ||
| await new Promise((resolve) => setTimeout(resolve, 250)) | ||
| await switchNetwork(chain) | ||
| sendData({ | ||
| event: 'network_switch', | ||
| action: 'network_switch_success', | ||
| network: ChainId[chain], | ||
| }) | ||
| toggleNetworkModal() | ||
| } catch (e: any) { | ||
| if (e?.code === 4902) { | ||
| setToAddNetwork(chain) | ||
| toggleNetworkModal() | ||
| return | ||
| } | ||
|
|
||
|
|
@@ -124,6 +128,7 @@ export default function NetworkModal(): JSX.Element | null { | |
| network: ChainId[chain], | ||
| }) | ||
| console.warn('Wallet not initialized. Network preference saved.') | ||
| toggleNetworkModal() | ||
| return | ||
| } | ||
|
|
||
|
|
@@ -135,6 +140,8 @@ export default function NetworkModal(): JSX.Element | null { | |
|
|
||
| error: e?.message || 'Unknown error', | ||
| }) | ||
| } finally { | ||
| setSwitchingChain(false) | ||
| } | ||
| }, | ||
|
|
||
|
|
@@ -174,6 +181,13 @@ export default function NetworkModal(): JSX.Element | null { | |
| )} | ||
| </TextWrapper> | ||
|
|
||
| {switchingChain && ( | ||
| <HStack mt={3} space={2} alignItems="center"> | ||
| <Spinner size="sm" /> | ||
| <Text fontSize="sm">{i18n._(t`Switching network...`)}</Text> | ||
| </HStack> | ||
| )} | ||
|
|
||
| <div className="flex flex-col mt-3 space-y-5 overflow-y-auto"> | ||
| {allowedNetworks.map((chain: SupportedChains) => ( | ||
| <ChainOption | ||
|
|
@@ -182,9 +196,9 @@ export default function NetworkModal(): JSX.Element | null { | |
| chain={chain} | ||
| labels={NETWORK_LABEL} | ||
| icons={NETWORK_ICON} | ||
| toggleNetworkModal={toggleNetworkModal} | ||
| switchChain={switchChain} | ||
| error={error} | ||
| disabled={switchingChain} | ||
| /> | ||
| ))} | ||
| </div> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import React, { useMemo } from 'react' | ||
| import React, { useEffect, useMemo, useState } from 'react' | ||
| import useENSName from '../../hooks/useENSName' | ||
| import { isTransactionRecent, useAllTransactions } from '../../state/transactions/hooks' | ||
| import { TransactionDetails } from '../../state/transactions/reducer' | ||
|
|
@@ -12,6 +12,9 @@ import { Text, HStack } from 'native-base' | |
| import { useNativeBalance } from '@gooddollar/web3sdk-v2' | ||
| import { Currency } from '@sushiswap/sdk' | ||
| import { useAppKitAccount, useAppKitNetwork } from '@reown/appkit/react' | ||
| import { useEthers } from '@usedapp/core' | ||
| import { Spinner } from 'native-base' | ||
| import { formatUnits } from 'viem' | ||
|
|
||
| // we want the latest one to come first, so return negative if a is after b | ||
| function newTransactionsFirst(a: TransactionDetails, b: TransactionDetails) { | ||
|
|
@@ -23,11 +26,54 @@ function Web3StatusInner() { | |
| const sendData = useSendAnalyticsData() | ||
| const { address } = useAppKitAccount() | ||
| const { chainId } = useAppKitNetwork() | ||
| const { library } = useEthers() as any | ||
|
|
||
| const { ENSName } = useENSName(address ?? undefined) | ||
|
|
||
| const allTransactions = useAllTransactions() | ||
| const nativeBalance = useNativeBalance() | ||
| const [directNativeBalance, setDirectNativeBalance] = useState<string | undefined>() | ||
| const [showBalance, setShowBalance] = useState(false) | ||
|
|
||
| // added a timed-out fallback for when native-balance does not seem to complete (happening on mainnet) | ||
| // it uses a timeout to avoid mis-formatted amounts based on old-chain decimals. | ||
| useEffect(() => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (complexity): Consider extracting the new balance timeout/fallback logic into a dedicated hook so Web3StatusInner is responsible only for rendering and not side‑effect orchestration. You can keep the new behavior while reducing 1. Extract a
|
||
| let cancelled = false | ||
| setShowBalance(false) | ||
| setDirectNativeBalance(undefined) | ||
| const timeoutId = window.setTimeout(() => { | ||
| if (!cancelled) { | ||
| setShowBalance(true) | ||
| } | ||
| }, 500) | ||
|
|
||
| async function readBalance() { | ||
| if (!address || !library?.getBalance) { | ||
| return | ||
| } | ||
|
|
||
| try { | ||
| const balance = await library.getBalance(address) | ||
| if (!cancelled) { | ||
| setDirectNativeBalance(formatUnits(balance.toString(), 18)) | ||
| } | ||
| } catch { | ||
| if (!cancelled) { | ||
| setDirectNativeBalance(undefined) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void readBalance() | ||
|
|
||
| return () => { | ||
| cancelled = true | ||
| window.clearTimeout(timeoutId) | ||
| } | ||
| }, [address, library, /*used*/ chainId]) | ||
|
|
||
| const displayNativeBalance = nativeBalance ?? directNativeBalance | ||
| const shouldShowBalance = showBalance && !!displayNativeBalance | ||
|
|
||
| const sortedRecentTransactions = useMemo(() => { | ||
| const txs = Object.values(allTransactions) | ||
|
|
@@ -46,10 +92,13 @@ function Web3StatusInner() { | |
| <HStack space={8} flexDirection="row"> | ||
| {address && ( | ||
| <div className="flex flex-row gap-4"> | ||
| {nativeBalance && ( | ||
| {shouldShowBalance ? ( | ||
| <Text fontSize="sm" fontFamily="subheading" fontWeight="normal" color="gdPrimary"> | ||
| {parseFloat(nativeBalance).toFixed(4)} {Currency.getNativeCurrencySymbol(+(chainId ?? 1))} | ||
| {parseFloat(displayNativeBalance!).toFixed(4)}{' '} | ||
| {Currency.getNativeCurrencySymbol(+(chainId ?? 1))} | ||
| </Text> | ||
| ) : ( | ||
| <Spinner size="sm" color="gdPrimary" /> | ||
| )} | ||
| {hasPendingTransactions ? ( | ||
| <div className="flex items-center justify-between"> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| /* eslint-env jest */ | ||
|
|
||
| const EXTRA_RPCS_SOURCE = ` | ||
| const privacyStatement = { | ||
| onerpc: 'ignored', | ||
| } | ||
|
|
||
| export const extraRpcs = { | ||
| 1: { | ||
| rpcs: [ | ||
| "https://eth.llamarpc.com", | ||
| { url: "https://1rpc.io/eth", trackingDetails: privacyStatement.onerpc }, | ||
| { url: "wss://ethereum-rpc.publicnode.com" }, | ||
| ], | ||
| }, | ||
| 122: { rpcs: [{ url: "https://rpc.fuse.io" }, { url: "https://fuse.drpc.org" }] }, | ||
| 1220: { rpcs: [{ url: "https://should-not-match-1220.example" }] }, | ||
| 42220: { rpcs: ["https://forno.celo.org", { url: "wss://forno.celo.org/ws" }] }, | ||
| 422201: { rpcs: [{ url: "https://should-not-match-422201.example" }] }, | ||
| 11142220: { rpcs: [{ url: "https://should-not-match-11142220.example" }] }, | ||
| 50: { rpcs: ["https://rpc.xinfin.network", { url: "https://erpc.xinfin.network" }] }, | ||
| 500: { rpcs: [{ url: "https://should-not-match-500.example" }] }, | ||
| 5050: { rpcs: [{ url: "https://should-not-match-5050.example" }] }, | ||
| 1750: { rpcs: [{ url: "https://should-not-match-1750.example" }] }, | ||
| 250: { rpcs: [{ url: "https://should-not-match-250.example" }] }, | ||
| } | ||
| ` | ||
|
|
||
| describe('rpcParsing', () => { | ||
| const originalEnv = process.env | ||
|
|
||
| beforeEach(() => { | ||
| process.env = { ...originalEnv } | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| process.env = originalEnv | ||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('parses HTTP(S) RPCs from extraRpcs.js', async () => { | ||
| jest.spyOn(global, 'fetch').mockResolvedValue({ | ||
| ok: true, | ||
| text: async () => EXTRA_RPCS_SOURCE, | ||
| } as Response) | ||
|
|
||
| const { fetchRpcsFromChainlist } = await import('./rpcParsing') | ||
|
|
||
| await expect(fetchRpcsFromChainlist()).resolves.toEqual({ | ||
| '1': ['https://eth.llamarpc.com', 'https://1rpc.io/eth'], | ||
| '122': ['https://rpc.fuse.io', 'https://fuse.drpc.org'], | ||
| '42220': ['https://forno.celo.org'], | ||
| '50': ['https://rpc.xinfin.network', 'https://erpc.xinfin.network'], | ||
| }) | ||
| }) | ||
|
|
||
| it('returns only configured fallback chains', async () => { | ||
| process.env.REACT_APP_RPC_FALLBACK_CHAIN_IDS = '122,50' | ||
| process.env.REACT_APP_FUSE_RPC = 'https://fuse.example' | ||
|
|
||
| const { getFallbackRpcsByChain } = await import('./rpcParsing') | ||
|
|
||
| expect(getFallbackRpcsByChain()).toEqual({ | ||
| '1': [], | ||
| '122': ['https://fuse.example'], | ||
| '42220': [], | ||
| '50': ['https://rpc.xinfin.network'], | ||
| }) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why only this test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you want all tests we should work on them in a separate PR as there is a lot of legacy tests that have not been maintained failing and would block this PR from merging and I thought we want to have this fix for rpc handling to resolve.