-
Notifications
You must be signed in to change notification settings - Fork 29
Rpc fix with json #643
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 with json #643
Changes from all commits
207fad2
88102f6
60fc64b
5241add
15646ee
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 |
|---|---|---|
| @@ -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(() => { | ||
| 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" /> | ||
|
Comment on lines
+100
to
+101
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.
When a connected account has no readable native balance (for example Useful? React with 👍 / 👎. |
||
| )} | ||
| {hasPendingTransactions ? ( | ||
| <div className="flex items-center justify-between"> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| /* eslint-env jest */ | ||
|
|
||
| const CHAINLIST_RPCS_SOURCE = [ | ||
| { | ||
| chainId: 1, | ||
| name: 'Ethereum Mainnet', | ||
| rpc: [ | ||
| { url: 'https://eth.llamarpc.com' }, | ||
| { url: 'https://1rpc.io/eth' }, | ||
| { url: 'wss://ethereum-rpc.publicnode.com' }, | ||
| ], | ||
| }, | ||
| { | ||
| chainId: 122, | ||
| name: 'Fuse Mainnet', | ||
| rpc: [{ url: 'https://rpc.fuse.io' }, { url: 'https://fuse.drpc.org' }], | ||
| }, | ||
| { | ||
| chainId: 42220, | ||
| name: 'Celo Mainnet', | ||
| rpc: [{ url: 'https://forno.celo.org' }, { url: 'wss://forno.celo.org/ws' }], | ||
| }, | ||
| { | ||
| chainId: 50, | ||
| name: 'XDC Network', | ||
| rpc: [{ url: 'https://rpc.xinfin.network' }, { url: 'https://erpc.xinfin.network' }], | ||
| }, | ||
| ] | ||
|
|
||
| describe('rpcParsing', () => { | ||
| const originalEnv = process.env | ||
|
|
||
| beforeEach(() => { | ||
| process.env = { ...originalEnv } | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| process.env = originalEnv | ||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| it('parses HTTP(S) RPCs from chainlist json', async () => { | ||
| jest.spyOn(global, 'fetch').mockResolvedValue({ | ||
| ok: true, | ||
| json: async () => CHAINLIST_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.
issue (complexity): Consider extracting the balance timeout/fallback logic into a dedicated hook so Web3StatusInner stays mostly presentational and simpler to reason about.
You can keep the new fallback behavior but move the imperative logic out of
Web3StatusInnerinto a small dedicated hook. That will reduce responsibilities (and tests) forWeb3StatusInnerwithout changing functionality.1. Extract balance + timeout + fallback into a hook
2. Simplify
Web3StatusInnerto use the hookThis keeps all existing behavior (timeout, fallback to
library.getBalance, merging both sources, spinner vs value) but makesWeb3StatusInnermostly presentational again and isolates the imperative logic into a focused, testable hook.