@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openocean.finance/widget",
3
- "version": "1.0.3",
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
- if (state === 'bridge') {
49
- const prependedOperatingExpenseCost = await getCost({
50
- srcChainId: fromChainId === 1151111081099710 ? 7565164 : fromChainId,
51
- srcChainTokenIn: fromTokenAddress,
52
- srcChainTokenInAmount: maxAmount,
53
- dstChainTokenOutAmount: 'auto',
54
- dstChainId: toChainId === 1151111081099710 ? 7565164 : toChainId,
55
- dstChainTokenOut: toTokenAddress,
56
- prependOperatingExpenses: true,
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
- if (state === 'swap') {
74
- if (
75
- chain?.nativeToken.address === fromTokenAddress &&
76
- data?.recommended
77
- ) {
78
- gas = BigInt(data.recommended.amount) / 2n
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
- if (token.amount > gas) {
83
- maxAmount = maxAmount - gas
84
- } else {
85
- maxAmount = 0n
86
- }
105
+ if (token.amount > gas) {
106
+ maxAmount = maxAmount - gas
107
+ } else {
108
+ maxAmount = 0n
109
+ }
87
110
 
88
- if (maxAmount) {
89
- setFieldValue(
90
- FormKeyHelper.getAmountKey(formType),
91
- formatUnits(maxAmount, token.decimals),
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
- } else {
97
- setFieldValue(FormKeyHelper.getAmountKey(formType), '', {
98
- isTouched: true,
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 onClick={handleMax}>{t('button.max')}</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
- let connection: any = null
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
- hexToUint8Array(txData.slice(2))
73
- )
74
- // Create connection instance
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) {