@openocean.finance/widget 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/AmountInput/AmountInput.tsx +1 -9
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +82 -46
- package/src/pages/TransactionDetailsPage/TransferIdCard.tsx +2 -1
- package/src/services/DebridgeService.ts +26 -107
- package/src/services/ExecuteRoute.ts +29 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openocean.finance/widget",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Openocean Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -81,15 +81,7 @@ export const AmountInputBase: React.FC<
|
|
|
81
81
|
const { onChange, onBlur, value } = useFieldController({ name: amountKey })
|
|
82
82
|
inputValue = value || ''
|
|
83
83
|
if (formType === 'to') {
|
|
84
|
-
const {
|
|
85
|
-
routes,
|
|
86
|
-
isLoading,
|
|
87
|
-
isFetching,
|
|
88
|
-
isFetched,
|
|
89
|
-
dataUpdatedAt,
|
|
90
|
-
refetchTime,
|
|
91
|
-
refetch,
|
|
92
|
-
} = useRoutes()
|
|
84
|
+
const { routes } = useRoutes()
|
|
93
85
|
if (routes) {
|
|
94
86
|
const router = routes[0] || { toAmount: 0, toToken: { decimals: 18 } }
|
|
95
87
|
inputValue = formatTokenAmount(
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { InputAdornment } from '@mui/material'
|
|
2
|
+
import { CircularProgress } from '@mui/material'
|
|
3
|
+
import { useState } from 'react'
|
|
2
4
|
import { useTranslation } from 'react-i18next'
|
|
3
5
|
import { formatUnits } from 'viem'
|
|
4
6
|
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
5
7
|
import { useGasRecommendation } from '../../hooks/useGasRecommendation.js'
|
|
8
|
+
import { useRoutes } from '../../hooks/useRoutes.js'
|
|
6
9
|
import { useTokenAddressBalance } from '../../hooks/useTokenAddressBalance.js'
|
|
7
10
|
import { DebridgeService } from '../../services/DebridgeService.js'
|
|
8
11
|
import type { FormTypeProps } from '../../stores/form/types.js'
|
|
@@ -16,6 +19,7 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
16
19
|
const { t } = useTranslation()
|
|
17
20
|
const { getChainById } = useAvailableChains()
|
|
18
21
|
const { setFieldValue } = useFieldActions()
|
|
22
|
+
const [isMaxLoading, setIsMaxLoading] = useState(false)
|
|
19
23
|
|
|
20
24
|
const [fromChainId, fromTokenAddress] = useFieldValues(
|
|
21
25
|
FormKeyHelper.getChainKey(formType),
|
|
@@ -35,6 +39,16 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
35
39
|
const { data } = useGasRecommendation(fromChainId)
|
|
36
40
|
|
|
37
41
|
const [state] = useSplitSubvariantStore((storeState) => [storeState.state])
|
|
42
|
+
|
|
43
|
+
const { routes } = useRoutes()
|
|
44
|
+
let prependedOperatingExpenseCost = 0n
|
|
45
|
+
if (routes) {
|
|
46
|
+
const router = routes[0] || { toAmount: 0, toToken: { decimals: 18 } }
|
|
47
|
+
if (router.data && router.data.prependedOperatingExpenseCost) {
|
|
48
|
+
prependedOperatingExpenseCost = router.data.prependedOperatingExpenseCost
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
const handleMax = async () => {
|
|
39
53
|
if (!token?.amount) {
|
|
40
54
|
return
|
|
@@ -44,59 +58,73 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
44
58
|
let maxAmount = token.amount
|
|
45
59
|
|
|
46
60
|
let gas = 0n
|
|
61
|
+
try {
|
|
62
|
+
if (
|
|
63
|
+
state === 'bridge' &&
|
|
64
|
+
fromChainId !== toChainId &&
|
|
65
|
+
fromTokenAddress &&
|
|
66
|
+
toTokenAddress
|
|
67
|
+
) {
|
|
68
|
+
if (!prependedOperatingExpenseCost) {
|
|
69
|
+
setIsMaxLoading(true)
|
|
70
|
+
prependedOperatingExpenseCost = await getCost({
|
|
71
|
+
srcChainId:
|
|
72
|
+
fromChainId === 1151111081099710 ? 7565164 : fromChainId,
|
|
73
|
+
srcChainTokenIn: fromTokenAddress,
|
|
74
|
+
srcChainTokenInAmount: maxAmount,
|
|
75
|
+
dstChainTokenOutAmount: 'auto',
|
|
76
|
+
dstChainId: toChainId === 1151111081099710 ? 7565164 : toChainId,
|
|
77
|
+
dstChainTokenOut: toTokenAddress,
|
|
78
|
+
prependOperatingExpenses: true,
|
|
79
|
+
additionalTakerRewardBps: 0,
|
|
80
|
+
tab: new Date().getTime(),
|
|
81
|
+
})
|
|
82
|
+
}
|
|
47
83
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
additionalTakerRewardBps: 0,
|
|
58
|
-
tab: new Date().getTime(),
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
gas = BigInt(
|
|
62
|
-
Number.parseInt((1.15 * prependedOperatingExpenseCost).toString())
|
|
63
|
-
)
|
|
64
|
-
if (chain?.nativeToken.address === fromTokenAddress) {
|
|
65
|
-
if (fromChainId === 1151111081099710) {
|
|
66
|
-
gas += 15000000n + (8157120n + 4000000n + 890880n)
|
|
67
|
-
} else if (fromChainId === 8453) {
|
|
68
|
-
gas += 1000000000000000n + 1000000000000n
|
|
84
|
+
gas = BigInt(
|
|
85
|
+
Number.parseInt((1.15 * prependedOperatingExpenseCost).toString())
|
|
86
|
+
)
|
|
87
|
+
if (chain?.nativeToken.address === fromTokenAddress) {
|
|
88
|
+
if (fromChainId === 1151111081099710) {
|
|
89
|
+
gas += 15000000n + (8157120n + 4000000n + 890880n)
|
|
90
|
+
} else if (fromChainId === 8453) {
|
|
91
|
+
gas += 1000000000000000n + 1000000000000n
|
|
92
|
+
}
|
|
69
93
|
}
|
|
70
94
|
}
|
|
71
|
-
}
|
|
72
95
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
if (state === 'swap' || fromChainId === toChainId) {
|
|
97
|
+
if (
|
|
98
|
+
chain?.nativeToken.address === fromTokenAddress &&
|
|
99
|
+
data?.recommended
|
|
100
|
+
) {
|
|
101
|
+
gas = BigInt(data.recommended.amount) / 2n
|
|
102
|
+
}
|
|
79
103
|
}
|
|
80
|
-
}
|
|
81
104
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
if (token.amount > gas) {
|
|
106
|
+
maxAmount = maxAmount - gas
|
|
107
|
+
} else {
|
|
108
|
+
maxAmount = 0n
|
|
109
|
+
}
|
|
87
110
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
if (maxAmount) {
|
|
112
|
+
setFieldValue(
|
|
113
|
+
FormKeyHelper.getAmountKey(formType),
|
|
114
|
+
formatUnits(maxAmount, token.decimals),
|
|
115
|
+
{
|
|
116
|
+
isTouched: true,
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
} else {
|
|
120
|
+
setFieldValue(FormKeyHelper.getAmountKey(formType), '', {
|
|
93
121
|
isTouched: true,
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('handleMax error', error)
|
|
126
|
+
} finally {
|
|
127
|
+
setIsMaxLoading(false)
|
|
100
128
|
}
|
|
101
129
|
}
|
|
102
130
|
|
|
@@ -105,7 +133,15 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
105
133
|
{isLoading && fromTokenAddress ? (
|
|
106
134
|
<MaxButtonSkeleton variant="rectangular" />
|
|
107
135
|
) : formType === 'from' && token?.amount ? (
|
|
108
|
-
<MaxButton
|
|
136
|
+
<MaxButton
|
|
137
|
+
onClick={handleMax}
|
|
138
|
+
disabled={isMaxLoading}
|
|
139
|
+
startIcon={
|
|
140
|
+
isMaxLoading ? <CircularProgress size={14} color="inherit" /> : null
|
|
141
|
+
}
|
|
142
|
+
>
|
|
143
|
+
{t('button.max')}
|
|
144
|
+
</MaxButton>
|
|
109
145
|
) : null}
|
|
110
146
|
</InputAdornment>
|
|
111
147
|
)
|
|
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'
|
|
|
5
5
|
import { Card } from '../../components/Card/Card.js'
|
|
6
6
|
import { CardIconButton } from '../../components/Card/CardIconButton.js'
|
|
7
7
|
import { CardTitle } from '../../components/Card/CardTitle.js'
|
|
8
|
+
import { useExplorer } from '../../hooks/useExplorer.js'
|
|
8
9
|
|
|
9
10
|
interface TransferIdCardProps {
|
|
10
11
|
transferId: string
|
|
@@ -37,7 +38,7 @@ export const TransferIdCard = ({ transferId, chain }: TransferIdCardProps) => {
|
|
|
37
38
|
flex: 1,
|
|
38
39
|
}}
|
|
39
40
|
>
|
|
40
|
-
<CardTitle flex={1}>{t('main.transferId')}</CardTitle>
|
|
41
|
+
<CardTitle flex={1}>{t('translation:main.transferId')}</CardTitle>
|
|
41
42
|
<Box
|
|
42
43
|
sx={{
|
|
43
44
|
gap: 1,
|
|
@@ -41,6 +41,32 @@ interface BuildSolanaBridgeDataParams {
|
|
|
41
41
|
// Define mapping between chain IDs and Debridge internal chain IDs
|
|
42
42
|
const DEBRIDGE_CHAIN_IDS: Record<number | string, string> = {
|
|
43
43
|
1151111081099710: '7565164', // Solana
|
|
44
|
+
100: '100000002', // Gnosis Chain
|
|
45
|
+
42161: '42161', // Arbitrum
|
|
46
|
+
43114: '43114', // Avalanche
|
|
47
|
+
56: '56', // BNB Chain
|
|
48
|
+
1: '1', // Ethereum
|
|
49
|
+
137: '137', // Polygon
|
|
50
|
+
250: '250', // Fantom
|
|
51
|
+
59144: '59144', // Linea
|
|
52
|
+
10: '10', // Optimism
|
|
53
|
+
8453: '8453', // Base
|
|
54
|
+
245022934: '100000001', // Neon
|
|
55
|
+
1890: '100000003', // Lightlink (suspended)
|
|
56
|
+
1088: '100000004', // Metis
|
|
57
|
+
7171: '100000005', // Bitrock
|
|
58
|
+
4158: '100000006', // CrossFi
|
|
59
|
+
388: '100000010', // Cronos zkEVM
|
|
60
|
+
1514: '100000013', // Story
|
|
61
|
+
146: '100000014', // Sonic
|
|
62
|
+
48900: '100000015', // Zircuit
|
|
63
|
+
2741: '100000017', // Abstract
|
|
64
|
+
80094: '100000020', // Berachain
|
|
65
|
+
60808: '100000021', // BOB
|
|
66
|
+
999: '100000022', // HyperEVM
|
|
67
|
+
5000: '100000023', // Mantle
|
|
68
|
+
747: '100000009', // Flow
|
|
69
|
+
32769: '100000008', // Zilliqa
|
|
44
70
|
// Other EVM chain IDs use numeric strings directly
|
|
45
71
|
}
|
|
46
72
|
|
|
@@ -59,89 +85,6 @@ const NATIVE_TOKEN_ADDRESSES = [
|
|
|
59
85
|
'', // Empty string might be used in some cases
|
|
60
86
|
].map((addr) => addr.toLowerCase())
|
|
61
87
|
|
|
62
|
-
// Contract addresses and configurations
|
|
63
|
-
const CONTRACT_CONFIG: Record<number, Record<string, any>> = {
|
|
64
|
-
56: {
|
|
65
|
-
// BSC
|
|
66
|
-
debridge: '0x...', // Supplement Debridge contract address
|
|
67
|
-
ooSwap: '0x22fefa40802E7aD7878FdE48700462e3f920E2C5',
|
|
68
|
-
bridgeId: { debridge: 22 /* Example ID */ },
|
|
69
|
-
messageBus: { debridge: '0x...' /* Example */ },
|
|
70
|
-
middlewareId: { ooSwap: 11 },
|
|
71
|
-
},
|
|
72
|
-
250: {
|
|
73
|
-
// Fantom
|
|
74
|
-
debridge: '0x...',
|
|
75
|
-
ooSwap: '0x521a8903B8D4C21b55768aE14f6cA31879B98d5e',
|
|
76
|
-
bridgeId: { debridge: 22 },
|
|
77
|
-
messageBus: { debridge: '0x...' },
|
|
78
|
-
middlewareId: { ooSwap: 5 },
|
|
79
|
-
},
|
|
80
|
-
137: {
|
|
81
|
-
// Polygon
|
|
82
|
-
debridge: '0x...',
|
|
83
|
-
ooSwap: '0x22fefa40802E7aD7878FdE48700462e3f920E2C5',
|
|
84
|
-
bridgeId: { debridge: 22 },
|
|
85
|
-
messageBus: { debridge: '0x...' },
|
|
86
|
-
middlewareId: { ooSwap: 7 },
|
|
87
|
-
},
|
|
88
|
-
43114: {
|
|
89
|
-
// Avalanche
|
|
90
|
-
debridge: '0x...',
|
|
91
|
-
ooSwap: '0x521a8903B8D4C21b55768aE14f6cA31879B98d5e',
|
|
92
|
-
bridgeId: { debridge: 22 },
|
|
93
|
-
messageBus: { debridge: '0x...' },
|
|
94
|
-
middlewareId: { ooSwap: 7 },
|
|
95
|
-
},
|
|
96
|
-
1: {
|
|
97
|
-
// Ethereum
|
|
98
|
-
debridge: '0x...',
|
|
99
|
-
ooSwap: '0xf951a9ea6b8400de7094d4b2b8d2ff2176d7ca81',
|
|
100
|
-
bridgeId: { debridge: 22 },
|
|
101
|
-
messageBus: { debridge: '0x...' },
|
|
102
|
-
middlewareId: { ooSwap: 11 },
|
|
103
|
-
},
|
|
104
|
-
42161: {
|
|
105
|
-
// Arbitrum
|
|
106
|
-
debridge: '0x65C757C04141a1602705F1F90260FEd66a86d0b8',
|
|
107
|
-
ooSwap: '0x900822ea09123b5583ef5ca8fd8ca542ea21699a',
|
|
108
|
-
bridgeId: { debridge: 22 },
|
|
109
|
-
messageBus: { debridge: '0x...' /* Needs supplement */ },
|
|
110
|
-
middlewareId: { ooSwap: 6 },
|
|
111
|
-
},
|
|
112
|
-
10: {
|
|
113
|
-
// Optimism
|
|
114
|
-
debridge: '0x...',
|
|
115
|
-
ooSwap: '0x521a8903B8D4C21b55768aE14f6cA31879B98d5e',
|
|
116
|
-
bridgeId: { debridge: 22 },
|
|
117
|
-
messageBus: { debridge: '0x...' },
|
|
118
|
-
middlewareId: { ooSwap: 2 },
|
|
119
|
-
},
|
|
120
|
-
324: {
|
|
121
|
-
// zkSync Era
|
|
122
|
-
debridge: '0x...',
|
|
123
|
-
ooSwap: '0xB6d35eE980Cde5e706C0866DF0122b076D26C44d',
|
|
124
|
-
bridgeId: { debridge: 22 },
|
|
125
|
-
messageBus: { debridge: '0x...' },
|
|
126
|
-
middlewareId: { ooSwap: 1 },
|
|
127
|
-
},
|
|
128
|
-
8453: {
|
|
129
|
-
// Base
|
|
130
|
-
debridge: '0x4045734fe21c7B7E0cE516BE009780Cc2BA39A8f',
|
|
131
|
-
// ooSwap: '...', // Base chain might not have ooSwap
|
|
132
|
-
bridgeId: { debridge: 3 },
|
|
133
|
-
messageBus: { debridge: '0x...' /* Needs supplement */ },
|
|
134
|
-
// middlewareId: {},
|
|
135
|
-
},
|
|
136
|
-
7565164: {
|
|
137
|
-
// Solana (Special handling)
|
|
138
|
-
debridge: '', // Solana might not need EVM address, but program ID
|
|
139
|
-
bridgeId: { debridge: -1 /* Placeholder */ },
|
|
140
|
-
messageBus: {},
|
|
141
|
-
},
|
|
142
|
-
// ... Add other chain Debridge configurations
|
|
143
|
-
}
|
|
144
|
-
|
|
145
88
|
// Native token information
|
|
146
89
|
const NATIVE_TOKENS: Record<number, Asset> = {
|
|
147
90
|
1: {
|
|
@@ -292,30 +235,6 @@ export class DebridgeService {
|
|
|
292
235
|
return NATIVE_TOKENS[chainId]
|
|
293
236
|
}
|
|
294
237
|
|
|
295
|
-
/**
|
|
296
|
-
* Get contract configuration information for specified chain and contract type
|
|
297
|
-
* @param chainId Chain ID
|
|
298
|
-
* @param contractType Contract type ('debridge', 'ooSwap', 'bridgeId', 'messageBus', 'middlewareId')
|
|
299
|
-
* @param subType Optional, for getting specific values under 'bridgeId', 'messageBus', 'middlewareId'
|
|
300
|
-
* @returns Configuration value or undefined
|
|
301
|
-
*/
|
|
302
|
-
private static getContractConfig(
|
|
303
|
-
chainId: number,
|
|
304
|
-
contractType: string,
|
|
305
|
-
subType?: string
|
|
306
|
-
): any {
|
|
307
|
-
const chainConfig = CONTRACT_CONFIG[chainId]
|
|
308
|
-
if (!chainConfig) {
|
|
309
|
-
console.warn(`No contract config found for chainId: ${chainId}`)
|
|
310
|
-
return undefined
|
|
311
|
-
}
|
|
312
|
-
const typeConfig = chainConfig[contractType]
|
|
313
|
-
if (subType) {
|
|
314
|
-
return typeConfig ? typeConfig[subType] : undefined
|
|
315
|
-
}
|
|
316
|
-
return typeConfig
|
|
317
|
-
}
|
|
318
|
-
|
|
319
238
|
/**
|
|
320
239
|
* Get Debridge internal chain ID
|
|
321
240
|
* @param chainId Original chain ID
|
|
@@ -58,7 +58,9 @@ async function executeSolanaSwap(
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
let transaction: any = ''
|
|
61
|
-
|
|
61
|
+
const connection = new Connection(
|
|
62
|
+
'https://burned-practical-bird.solana-mainnet.quiknode.pro/33f4786133c252415e194b29ee69ffc7671480ab'
|
|
63
|
+
)
|
|
62
64
|
const txData: any = step.transactionRequest?.data || ''
|
|
63
65
|
const dexId = step.transactionRequest?.type || 0
|
|
64
66
|
if (step.action.fromChainId === step.action.toChainId) {
|
|
@@ -68,15 +70,10 @@ async function executeSolanaSwap(
|
|
|
68
70
|
transaction = Transaction.from(hexToUint8Array(txData))
|
|
69
71
|
}
|
|
70
72
|
} else {
|
|
71
|
-
transaction = VersionedTransaction.deserialize(
|
|
72
|
-
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
connection = new Connection(
|
|
76
|
-
'https://burned-practical-bird.solana-mainnet.quiknode.pro/33f4786133c252415e194b29ee69ffc7671480ab'
|
|
77
|
-
)
|
|
78
|
-
const { blockhash } = await connection.getLatestBlockhash()
|
|
79
|
-
transaction.message.recentBlockhash = blockhash
|
|
73
|
+
transaction = VersionedTransaction.deserialize(hexToUint8Array(txData.slice(2)))
|
|
74
|
+
|
|
75
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
76
|
+
transaction.message.recentBlockhash = blockhash;
|
|
80
77
|
}
|
|
81
78
|
|
|
82
79
|
// Check signTransaction method exists
|
|
@@ -149,17 +146,37 @@ async function executeEvmSwap(
|
|
|
149
146
|
): Promise<void> {
|
|
150
147
|
try {
|
|
151
148
|
const walletClient = await getWalletClient(options.wagmiConfig)
|
|
149
|
+
|
|
150
|
+
// Check if wallet is connected
|
|
152
151
|
if (!walletClient) {
|
|
153
|
-
// If disconnect callback exists, disconnect the current connection first
|
|
154
152
|
if (options.account?.connector && options.onDisconnect) {
|
|
155
153
|
await options.onDisconnect(options.account)
|
|
156
154
|
}
|
|
157
|
-
// Open wallet menu to let user reconnect
|
|
158
155
|
if (options.onOpenWalletMenu) {
|
|
159
156
|
options.onOpenWalletMenu()
|
|
160
157
|
}
|
|
161
158
|
throw new Error('Please connect wallet first')
|
|
162
159
|
}
|
|
160
|
+
|
|
161
|
+
// Check if current chain matches target chain
|
|
162
|
+
const currentChainId = walletClient.chain.id
|
|
163
|
+
const targetChainId = step.action.fromChainId
|
|
164
|
+
|
|
165
|
+
if (currentChainId !== targetChainId) {
|
|
166
|
+
try {
|
|
167
|
+
// Try to switch to target chain
|
|
168
|
+
await walletClient.switchChain({ id: targetChainId })
|
|
169
|
+
|
|
170
|
+
// Get updated walletClient after chain switch
|
|
171
|
+
const updatedWalletClient = await getWalletClient(options.wagmiConfig)
|
|
172
|
+
if (!updatedWalletClient || updatedWalletClient.chain.id !== targetChainId) {
|
|
173
|
+
throw new Error('Failed to switch chain')
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error('Failed to switch chain:', error)
|
|
177
|
+
throw new Error(`Please manually switch to chain ID: ${targetChainId}`)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
163
180
|
|
|
164
181
|
const publicClient = getPublicClient(options.wagmiConfig)
|
|
165
182
|
if (!publicClient) {
|