@openocean.finance/widget 1.0.28 → 1.0.29
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/dist/esm/components/AmountInput/AmountInputEndAdornment.js +46 -39
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
- package/dist/esm/components/Messages/WarningMessages.js +2 -2
- package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
- package/dist/esm/components/Step/Step.js +37 -29
- package/dist/esm/components/Step/Step.js.map +1 -1
- package/dist/esm/components/TransactionDetails.js +2 -5
- package/dist/esm/components/TransactionDetails.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/cross/adapters/AcrossAdapter.d.ts +15 -0
- package/dist/esm/cross/adapters/AcrossAdapter.js +166 -0
- package/dist/esm/cross/adapters/AcrossAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/BaseSwapAdapter.d.ts +107 -0
- package/dist/esm/cross/adapters/BaseSwapAdapter.js +44 -0
- package/dist/esm/cross/adapters/BaseSwapAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/DebridgeAdapter.d.ts +20 -0
- package/dist/esm/cross/adapters/DebridgeAdapter.js +264 -0
- package/dist/esm/cross/adapters/DebridgeAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/LifiAdapter.d.ts +19 -0
- package/dist/esm/cross/adapters/LifiAdapter.js +169 -0
- package/dist/esm/cross/adapters/LifiAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/MayanAdapter.d.ts +14 -0
- package/dist/esm/cross/adapters/MayanAdapter.js +119 -0
- package/dist/esm/cross/adapters/MayanAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/NearIntentsAdapter.d.ts +21 -0
- package/dist/esm/cross/adapters/NearIntentsAdapter.js +425 -0
- package/dist/esm/cross/adapters/NearIntentsAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/OptimexAdapter.d.ts +19 -0
- package/dist/esm/cross/adapters/OptimexAdapter.js +216 -0
- package/dist/esm/cross/adapters/OptimexAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/OrbiterAdapter.d.ts +20 -0
- package/dist/esm/cross/adapters/OrbiterAdapter.js +213 -0
- package/dist/esm/cross/adapters/OrbiterAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/RelayAdapter.d.ts +14 -0
- package/dist/esm/cross/adapters/RelayAdapter.js +171 -0
- package/dist/esm/cross/adapters/RelayAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/SymbiosisAdapter.d.ts +14 -0
- package/dist/esm/cross/adapters/SymbiosisAdapter.js +120 -0
- package/dist/esm/cross/adapters/SymbiosisAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/XYFinanceAdapter.d.ts +14 -0
- package/dist/esm/cross/adapters/XYFinanceAdapter.js +177 -0
- package/dist/esm/cross/adapters/XYFinanceAdapter.js.map +1 -0
- package/dist/esm/cross/adapters/index.d.ts +2 -0
- package/dist/esm/cross/adapters/index.js +10 -0
- package/dist/esm/cross/adapters/index.js.map +1 -0
- package/dist/esm/cross/constants/index.d.ts +202 -0
- package/dist/esm/cross/constants/index.js +183 -0
- package/dist/esm/cross/constants/index.js.map +1 -0
- package/dist/esm/cross/crossChainQuote.d.ts +25 -0
- package/dist/esm/cross/crossChainQuote.js +127 -0
- package/dist/esm/cross/crossChainQuote.js.map +1 -0
- package/dist/esm/cross/factory.d.ts +9 -0
- package/dist/esm/cross/factory.js +125 -0
- package/dist/esm/cross/factory.js.map +1 -0
- package/dist/esm/cross/registry.d.ts +12 -0
- package/dist/esm/cross/registry.js +52 -0
- package/dist/esm/cross/registry.js.map +1 -0
- package/dist/esm/hooks/useChain.d.ts +1 -1
- package/dist/esm/hooks/useGasRefuel.d.ts +1 -1
- package/dist/esm/hooks/useGasSufficiencyBridge.js +1 -2
- package/dist/esm/hooks/useGasSufficiencyBridge.js.map +1 -1
- package/dist/esm/hooks/useRouteExecution.js +2 -1
- package/dist/esm/hooks/useRouteExecution.js.map +1 -1
- package/dist/esm/hooks/useRoutes.js +50 -32
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useSettingMonitor.js +1 -0
- package/dist/esm/hooks/useSettingMonitor.js.map +1 -1
- package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -1
- package/dist/esm/hooks/useTokenPrice.js +4 -2
- package/dist/esm/hooks/useTokenPrice.js.map +1 -1
- package/dist/esm/hooks/useTokens.d.ts +1 -1
- package/dist/esm/services/ExecuteRoute.js +142 -124
- package/dist/esm/services/ExecuteRoute.js.map +1 -1
- package/dist/esm/stores/form/useFieldController.d.ts +1 -1
- package/dist/esm/stores/routes/createRouteExecutionStore.js +6 -3
- package/dist/esm/stores/routes/createRouteExecutionStore.js.map +1 -1
- package/dist/esm/stores/routes/useSetExecutableRoute.d.ts +1 -1
- package/dist/esm/types/widget.d.ts +3 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +14 -4
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +46 -46
- package/src/components/Messages/WarningMessages.tsx +7 -2
- package/src/components/Step/Step.tsx +37 -31
- package/src/components/TransactionDetails.tsx +10 -11
- package/src/config/version.ts +1 -1
- package/src/cross/adapters/AcrossAdapter.ts +193 -0
- package/src/cross/adapters/BaseSwapAdapter.ts +173 -0
- package/src/cross/adapters/DebridgeAdapter.ts +375 -0
- package/src/cross/adapters/LifiAdapter.ts +213 -0
- package/src/cross/adapters/MayanAdapter.ts +179 -0
- package/src/cross/adapters/NearIntentsAdapter.ts +539 -0
- package/src/cross/adapters/OptimexAdapter.ts +273 -0
- package/src/cross/adapters/OrbiterAdapter.ts +270 -0
- package/src/cross/adapters/RelayAdapter.ts +248 -0
- package/src/cross/adapters/SymbiosisAdapter.ts +144 -0
- package/src/cross/adapters/XYFinanceAdapter.ts +213 -0
- package/src/cross/adapters/index.ts +9 -0
- package/src/cross/constants/index.ts +223 -0
- package/src/cross/crossChainQuote.ts +181 -0
- package/src/cross/factory.ts +145 -0
- package/src/cross/registry.ts +65 -0
- package/src/hooks/useGasSufficiencyBridge.ts +1 -3
- package/src/hooks/useRouteExecution.ts +2 -1
- package/src/hooks/useRoutes.ts +64 -43
- package/src/hooks/useSettingMonitor.ts +1 -1
- package/src/hooks/useTokenPrice.ts +5 -3
- package/src/services/ExecuteRoute.ts +184 -171
- package/src/stores/routes/createRouteExecutionStore.ts +13 -4
- package/src/types/widget.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openocean.finance/widget",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
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": "./dist/esm/index.js",
|
|
@@ -48,7 +48,13 @@
|
|
|
48
48
|
"@mui/material": "^6.4.8",
|
|
49
49
|
"@mui/system": "^6.4.8",
|
|
50
50
|
"@openocean.finance/wallet-management": "^1.0.11",
|
|
51
|
-
"@openocean.finance/widget-sdk": "^1.0.
|
|
51
|
+
"@openocean.finance/widget-sdk": "^1.0.9",
|
|
52
|
+
"@across-protocol/app-sdk": "^0.2.0",
|
|
53
|
+
"@near-wallet-selector/react-hook": "^9.0.0",
|
|
54
|
+
"@reservoir0x/relay-sdk": "^2.4.0",
|
|
55
|
+
"@lifi/sdk": "^3.6.8",
|
|
56
|
+
"@mayanfinance/swap-sdk": "^11.0.1",
|
|
57
|
+
"@defuse-protocol/one-click-sdk-typescript": "^0.1.2",
|
|
52
58
|
"@solana/wallet-adapter-base": "^0.9.24",
|
|
53
59
|
"@solana/web3.js": "^1.98.0",
|
|
54
60
|
"@tanstack/react-virtual": "^3.13.4",
|
|
@@ -74,9 +80,13 @@
|
|
|
74
80
|
"@bigmi/react": ">=0.1.0",
|
|
75
81
|
"@solana/wallet-adapter-react": "^0.15.35",
|
|
76
82
|
"@tanstack/react-query": "^5.62.0",
|
|
77
|
-
"react": ">=18",
|
|
78
|
-
"react-dom": ">=18",
|
|
83
|
+
"react": ">=18.0.0",
|
|
84
|
+
"react-dom": ">=18.0.0",
|
|
79
85
|
"wagmi": "^2.14.0"
|
|
80
86
|
},
|
|
87
|
+
"peerDependenciesMeta": {
|
|
88
|
+
"react": { "optional": false },
|
|
89
|
+
"react-dom": { "optional": false }
|
|
90
|
+
},
|
|
81
91
|
"files": ["dist/**", "src/**", "!tsconfig.json"]
|
|
82
92
|
}
|
|
@@ -76,21 +76,21 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
76
76
|
)
|
|
77
77
|
)
|
|
78
78
|
}
|
|
79
|
-
const prependedOperatingExpenseCost = await getCost({
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
})
|
|
90
|
-
return
|
|
79
|
+
// const prependedOperatingExpenseCost = await getCost({
|
|
80
|
+
// srcChainId: fromChainId === 1151111081099710 ? 7565164 : fromChainId,
|
|
81
|
+
// srcChainTokenIn: fromTokenAddress,
|
|
82
|
+
// srcChainTokenInAmount: srcChainTokenInAmount,
|
|
83
|
+
// dstChainTokenOutAmount: 'auto',
|
|
84
|
+
// dstChainId: toChainId === 1151111081099710 ? 7565164 : toChainId,
|
|
85
|
+
// dstChainTokenOut: toTokenAddress,
|
|
86
|
+
// prependOperatingExpenses: true,
|
|
87
|
+
// additionalTakerRewardBps: 0,
|
|
88
|
+
// tab: new Date().getTime(),
|
|
89
|
+
// })
|
|
90
|
+
return 0n
|
|
91
91
|
},
|
|
92
92
|
})
|
|
93
|
-
|
|
93
|
+
const prependedOperatingExpenseCost = prependedOperatingExpenseCostOne || 0
|
|
94
94
|
|
|
95
95
|
const handleMax = async () => {
|
|
96
96
|
if (!token?.amount) {
|
|
@@ -101,40 +101,40 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
101
101
|
let maxAmount = token.amount
|
|
102
102
|
|
|
103
103
|
let gas = 0n
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
104
|
+
// if (
|
|
105
|
+
// state === 'bridge' &&
|
|
106
|
+
// fromChainId !== toChainId &&
|
|
107
|
+
// fromTokenAddress &&
|
|
108
|
+
// toTokenAddress
|
|
109
|
+
// ) {
|
|
110
|
+
// if (routes) {
|
|
111
|
+
// const router: any = routes[0] || {
|
|
112
|
+
// toAmount: 0,
|
|
113
|
+
// toToken: { decimals: 18 },
|
|
114
|
+
// }
|
|
115
|
+
// if (
|
|
116
|
+
// router &&
|
|
117
|
+
// router.data &&
|
|
118
|
+
// router.data.prependedOperatingExpenseCost
|
|
119
|
+
// ) {
|
|
120
|
+
// prependedOperatingExpenseCost =
|
|
121
|
+
// router.data.prependedOperatingExpenseCost
|
|
122
|
+
// }
|
|
123
|
+
// }
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
125
|
+
// gas = BigInt(
|
|
126
|
+
// Number.parseInt(
|
|
127
|
+
// (1.15 * Number(prependedOperatingExpenseCost)).toString()
|
|
128
|
+
// )
|
|
129
|
+
// )
|
|
130
|
+
// if (chain?.nativeToken?.address === fromTokenAddress) {
|
|
131
|
+
// if (fromChainId === 1151111081099710) {
|
|
132
|
+
// gas += 15000000n + (8157120n + 4000000n + 890880n)
|
|
133
|
+
// } else if (fromChainId === 8453) {
|
|
134
|
+
// gas += 1000000000000000n + 1000000000000n
|
|
135
|
+
// }
|
|
136
|
+
// }
|
|
137
|
+
// }
|
|
138
138
|
|
|
139
139
|
if (state === 'swap' || fromChainId === toChainId) {
|
|
140
140
|
if (
|
|
@@ -6,12 +6,12 @@ import { FundsSufficiencyMessage } from './FundsSufficiencyMessage.js'
|
|
|
6
6
|
import { GasSufficiencyMessage } from './GasSufficiencyMessage.js'
|
|
7
7
|
import { GasSufficiencyMessageBridge } from './GasSufficiencyMessageBridge.js'
|
|
8
8
|
import { PriceImpactHighMessage } from './PriceImpactHighMessage.js'
|
|
9
|
+
import { ServerErrorMessage } from './ServeErrorMessage.js'
|
|
9
10
|
import { ToAddressRequiredMessage } from './ToAddressRequiredMessage.js'
|
|
10
11
|
import {
|
|
11
12
|
useMessageQueue,
|
|
12
13
|
useStorePriceImpactAcknowledged,
|
|
13
14
|
} from './useMessageQueue.js'
|
|
14
|
-
import { ServerErrorMessage } from './ServeErrorMessage.js'
|
|
15
15
|
|
|
16
16
|
type WarningMessagesProps = BoxProps & {
|
|
17
17
|
route?: Route
|
|
@@ -59,7 +59,12 @@ export const WarningMessages: React.FC<WarningMessagesProps> = ({
|
|
|
59
59
|
}
|
|
60
60
|
return null
|
|
61
61
|
case 'SERVER_ERROR':
|
|
62
|
-
return
|
|
62
|
+
return (
|
|
63
|
+
<ServerErrorMessage
|
|
64
|
+
errorMsg={messages[0].props?.errorMsg}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
63
68
|
default:
|
|
64
69
|
return null
|
|
65
70
|
}
|
|
@@ -29,10 +29,10 @@ export const Step: React.FC<{
|
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
const getCardTitle = () => {
|
|
32
|
-
const hasBridgeStep = step.includedSteps.some(
|
|
33
|
-
|
|
34
|
-
)
|
|
35
|
-
const hasSwapStep = step.includedSteps.some((step) => step.type === 'swap')
|
|
32
|
+
// const hasBridgeStep = step.includedSteps.some(
|
|
33
|
+
// (step) => step.type === 'cross'
|
|
34
|
+
// )
|
|
35
|
+
// const hasSwapStep = step.includedSteps.some((step) => step.type === 'swap')
|
|
36
36
|
const hasCustomStep = step.includedSteps.some(
|
|
37
37
|
(step) => step.type === 'custom'
|
|
38
38
|
)
|
|
@@ -40,33 +40,39 @@ export const Step: React.FC<{
|
|
|
40
40
|
const isCustomVariant = hasCustomStep && subvariant === 'custom'
|
|
41
41
|
|
|
42
42
|
switch (step.type) {
|
|
43
|
-
case 'openocean': {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
// case 'openocean': {
|
|
44
|
+
// if (hasBridgeStep && hasSwapStep) {
|
|
45
|
+
// return isCustomVariant
|
|
46
|
+
// ? subvariantOptions?.custom === 'deposit'
|
|
47
|
+
// ? t('main.stepBridgeAndDeposit')
|
|
48
|
+
// : t('main.stepBridgeAndBuy')
|
|
49
|
+
// : t('main.stepSwapAndBridge')
|
|
50
|
+
// }
|
|
51
|
+
// if (hasBridgeStep) {
|
|
52
|
+
// return isCustomVariant
|
|
53
|
+
// ? subvariantOptions?.custom === 'deposit'
|
|
54
|
+
// ? t('main.stepBridgeAndDeposit')
|
|
55
|
+
// : t('main.stepBridgeAndBuy')
|
|
56
|
+
// : t('main.stepBridge')
|
|
57
|
+
// }
|
|
58
|
+
// if (hasSwapStep) {
|
|
59
|
+
// return isCustomVariant
|
|
60
|
+
// ? subvariantOptions?.custom === 'deposit'
|
|
61
|
+
// ? t('main.stepSwapAndDeposit')
|
|
62
|
+
// : t('main.stepSwapAndBuy')
|
|
63
|
+
// : t('main.stepSwap')
|
|
64
|
+
// }
|
|
65
|
+
// return isCustomVariant
|
|
66
|
+
// ? subvariantOptions?.custom === 'deposit'
|
|
67
|
+
// ? t('main.stepDeposit')
|
|
68
|
+
// : t('main.stepBuy')
|
|
69
|
+
// : t('main.stepSwap')
|
|
70
|
+
// }
|
|
71
|
+
case 'bridge': {
|
|
72
|
+
return t('main.stepBridge')
|
|
73
|
+
}
|
|
74
|
+
case 'swap': {
|
|
75
|
+
return t('main.stepSwap')
|
|
70
76
|
}
|
|
71
77
|
default:
|
|
72
78
|
return isCustomVariant
|
|
@@ -69,19 +69,18 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
|
|
|
69
69
|
|
|
70
70
|
let gasCostUSD = 0
|
|
71
71
|
const feeCosts = []
|
|
72
|
-
const combinedFeesUSD =
|
|
72
|
+
const combinedFeesUSD =
|
|
73
|
+
gasCostUSD + feeCosts.reduce((sum, fee) => sum + fee.costUSD, 0)
|
|
73
74
|
const feeCostUSD = feeCosts.reduce((sum, fee) => sum + fee.costUSD, 0)
|
|
74
|
-
const estimatedGas =
|
|
75
|
+
const estimatedGas =
|
|
76
|
+
(route as any)?.estimatedGas || (route as any)?.data?.estimatedGas
|
|
75
77
|
if (token && gasPrice && estimatedGas) {
|
|
76
78
|
const d = 10 ** token.decimals
|
|
77
79
|
gasCostUSD =
|
|
78
80
|
Number(token.priceUSD) * ((Number(gasPrice) * Number(estimatedGas)) / d)
|
|
79
81
|
}
|
|
80
|
-
if (isBradge
|
|
81
|
-
|
|
82
|
-
gasCostUSD =
|
|
83
|
-
((route as any).data.prependedOperatingExpenseCost / decimals) *
|
|
84
|
-
Number(route.fromToken.priceUSD)
|
|
82
|
+
if (isBradge) {
|
|
83
|
+
gasCostUSD = (route as any).feeCosts?.[0]?.amountUSD || 0
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
const { priceImpact } = usePriceImpact(route)
|
|
@@ -228,10 +227,10 @@ export const TransactionDetails: React.FC<TransactionDetailsProps> = ({
|
|
|
228
227
|
{t('main.fees.provider')}
|
|
229
228
|
</Typography>
|
|
230
229
|
{feeCostUSD
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
? t('format.currency', {
|
|
231
|
+
value: feeCostUSD,
|
|
232
|
+
})
|
|
233
|
+
: '--'}
|
|
235
234
|
</Box>
|
|
236
235
|
) : null}
|
|
237
236
|
{showIntegratorFeeCollectionDetails ? (
|
package/src/config/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@openocean.finance/widget'
|
|
2
|
-
export const version = '1.0.
|
|
2
|
+
export const version = '1.0.29'
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { AcrossClient, createAcrossClient } from '@across-protocol/app-sdk'
|
|
2
|
+
import { Currency } from '../constants/index.js'
|
|
3
|
+
import { ChainId } from '@openocean.finance/widget-sdk'
|
|
4
|
+
import { ethers } from 'ethers'
|
|
5
|
+
import { WalletClient, formatUnits } from 'viem'
|
|
6
|
+
import { arbitrum, base, blast, linea, mainnet, optimism, polygon, scroll, unichain, zksync } from 'viem/chains'
|
|
7
|
+
|
|
8
|
+
import { CROSS_CHAIN_FEE_RECEIVER } from '../constants/index.js'
|
|
9
|
+
|
|
10
|
+
import { Quote } from '../registry.js'
|
|
11
|
+
import {
|
|
12
|
+
BaseSwapAdapter,
|
|
13
|
+
Chain,
|
|
14
|
+
EvmQuoteParams,
|
|
15
|
+
NOT_SUPPORTED_CHAINS_PRICE_SERVICE,
|
|
16
|
+
NormalizedQuote,
|
|
17
|
+
NormalizedTxResponse,
|
|
18
|
+
SwapStatus,
|
|
19
|
+
} from './BaseSwapAdapter.js'
|
|
20
|
+
|
|
21
|
+
// Define the ABI of the functions
|
|
22
|
+
const transferFunction = 'function transfer(address to, uint256 amount)'
|
|
23
|
+
|
|
24
|
+
// Create Interface instances
|
|
25
|
+
const erc20Interface = new ethers.utils.Interface([transferFunction])
|
|
26
|
+
|
|
27
|
+
const multicalHandlerContract: Record<string, `0x${string}`> = {
|
|
28
|
+
[ChainId.MAINNET]: '0x924a9f036260DdD5808007E1AA95f08eD08aA569',
|
|
29
|
+
[ChainId.ARBITRUM]: '0x924a9f036260DdD5808007E1AA95f08eD08aA569',
|
|
30
|
+
[ChainId.OPTIMISM]: '0x924a9f036260DdD5808007E1AA95f08eD08aA569',
|
|
31
|
+
[ChainId.LINEA]: '0x1015c58894961F4F7Dd7D68ba033e28Ed3ee1cDB',
|
|
32
|
+
[ChainId.MATIC]: '0x924a9f036260DdD5808007E1AA95f08eD08aA569',
|
|
33
|
+
[ChainId.ZKSYNC]: '0x863859ef502F0Ee9676626ED5B418037252eFeb2',
|
|
34
|
+
[ChainId.BASE]: '0x1015c58894961F4F7Dd7D68ba033e28Ed3ee1cDB',
|
|
35
|
+
[ChainId.SCROLL]: '0x1015c58894961F4F7Dd7D68ba033e28Ed3ee1cDB',
|
|
36
|
+
[ChainId.BLAST]: '0x1015c58894961F4F7Dd7D68ba033e28Ed3ee1cDB',
|
|
37
|
+
[ChainId.UNICHAIN]: '0x1015c58894961F4F7Dd7D68ba033e28Ed3ee1cDB',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class AcrossAdapter extends BaseSwapAdapter {
|
|
41
|
+
private acrossClient: AcrossClient
|
|
42
|
+
|
|
43
|
+
constructor() {
|
|
44
|
+
super()
|
|
45
|
+
this.acrossClient = createAcrossClient({
|
|
46
|
+
integratorId: `0x008a`,
|
|
47
|
+
chains: [mainnet, arbitrum, optimism, linea, polygon, zksync, base, scroll, blast, unichain],
|
|
48
|
+
rpcUrls: {
|
|
49
|
+
[ChainId.BASE]: 'https://base.drpc.org',
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getName(): string {
|
|
55
|
+
return 'Across'
|
|
56
|
+
}
|
|
57
|
+
getIcon(): string {
|
|
58
|
+
return 'https://across.to/favicon.ico'
|
|
59
|
+
}
|
|
60
|
+
getSupportedChains(): Chain[] {
|
|
61
|
+
return [
|
|
62
|
+
ChainId.MAINNET,
|
|
63
|
+
ChainId.ARBITRUM,
|
|
64
|
+
ChainId.OPTIMISM,
|
|
65
|
+
ChainId.LINEA,
|
|
66
|
+
ChainId.MATIC,
|
|
67
|
+
ChainId.ZKSYNC,
|
|
68
|
+
ChainId.BASE,
|
|
69
|
+
ChainId.SCROLL,
|
|
70
|
+
ChainId.BLAST,
|
|
71
|
+
ChainId.UNICHAIN,
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getSupportedTokens(_sourceChain: Chain, _destChain: Chain): Currency[] {
|
|
76
|
+
return []
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getQuote(params: EvmQuoteParams): Promise<NormalizedQuote> {
|
|
80
|
+
try {
|
|
81
|
+
const resp = await this.acrossClient.getQuote({
|
|
82
|
+
route: {
|
|
83
|
+
originChainId: +params.fromChain,
|
|
84
|
+
destinationChainId: +params.toChain,
|
|
85
|
+
inputToken: params.fromToken.wrapped.address as `0x${string}`,
|
|
86
|
+
outputToken: params.toToken.wrapped.address as `0x${string}`,
|
|
87
|
+
isNative: params.fromToken.isNative,
|
|
88
|
+
},
|
|
89
|
+
inputAmount: params.amount,
|
|
90
|
+
recipient: multicalHandlerContract[params.toChain],
|
|
91
|
+
crossChainMessage: {
|
|
92
|
+
actions: [
|
|
93
|
+
{
|
|
94
|
+
target: params.toToken.wrapped.address as `0x${string}`,
|
|
95
|
+
callData: erc20Interface.encodeFunctionData('transfer', [CROSS_CHAIN_FEE_RECEIVER, 0n]) as `0x${string}`,
|
|
96
|
+
value: '0',
|
|
97
|
+
update: updatedAmount => ({
|
|
98
|
+
callData: erc20Interface.encodeFunctionData('transfer', [
|
|
99
|
+
CROSS_CHAIN_FEE_RECEIVER,
|
|
100
|
+
(updatedAmount * BigInt(params.feeBps)) / 10_000n,
|
|
101
|
+
]) as `0x${string}`,
|
|
102
|
+
}),
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
fallbackRecipient: params.recipient as `0x${string}`,
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// across only have bridge then we can treat token in and out price usd are the same in case price service is not supported
|
|
110
|
+
const isSameToken = params.fromToken.symbol === params.toToken.symbol
|
|
111
|
+
const tokenInUsd =
|
|
112
|
+
isSameToken && NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.fromChain) && params.tokenOutUsd
|
|
113
|
+
? params.tokenOutUsd
|
|
114
|
+
: params.tokenInUsd
|
|
115
|
+
const tokenOutUsd =
|
|
116
|
+
isSameToken && NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.toChain) && params.tokenInUsd
|
|
117
|
+
? params.tokenInUsd
|
|
118
|
+
: params.tokenOutUsd
|
|
119
|
+
const feeAmount = (BigInt(resp.deposit.outputAmount) * BigInt(params.feeBps)) / 10_000n
|
|
120
|
+
const formattedOutputAmount = formatUnits(BigInt(resp.deposit.outputAmount) - feeAmount, params.toToken.decimals)
|
|
121
|
+
const formattedInputAmount = formatUnits(BigInt(params.amount), params.fromToken.decimals)
|
|
122
|
+
|
|
123
|
+
const inputUsd = tokenInUsd * +formattedInputAmount
|
|
124
|
+
const outputUsd = tokenOutUsd * +formattedOutputAmount
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
quoteParams: params,
|
|
128
|
+
outputAmount: BigInt(resp.deposit.outputAmount) - feeAmount,
|
|
129
|
+
formattedOutputAmount,
|
|
130
|
+
inputUsd: tokenInUsd * +formatUnits(BigInt(params.amount), params.fromToken.decimals),
|
|
131
|
+
outputUsd: tokenOutUsd * +formattedOutputAmount,
|
|
132
|
+
rate: +formattedOutputAmount / +formattedInputAmount,
|
|
133
|
+
timeEstimate: resp.estimatedFillTimeSec,
|
|
134
|
+
priceImpact: !inputUsd || !outputUsd ? NaN : ((inputUsd - outputUsd) * 100) / inputUsd,
|
|
135
|
+
// TODO: what is gas fee for across
|
|
136
|
+
gasFeeUsd: 0,
|
|
137
|
+
contractAddress: resp.deposit.spokePoolAddress,
|
|
138
|
+
rawQuote: resp,
|
|
139
|
+
|
|
140
|
+
protocolFee: 0,
|
|
141
|
+
platformFeePercent: (params.feeBps * 100) / 10_000,
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
console.log('Across getQuote error', e)
|
|
145
|
+
throw e
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async executeSwap(quote: Quote, walletClient: WalletClient): Promise<NormalizedTxResponse> {
|
|
150
|
+
return new Promise<NormalizedTxResponse>((resolve, reject) => {
|
|
151
|
+
this.acrossClient
|
|
152
|
+
.executeQuote({
|
|
153
|
+
walletClient: walletClient as any,
|
|
154
|
+
deposit: quote.quote.rawQuote.deposit,
|
|
155
|
+
onProgress: progress => {
|
|
156
|
+
if (progress.step === 'deposit' && 'txHash' in progress) {
|
|
157
|
+
resolve({
|
|
158
|
+
sender: quote.quote.quoteParams.sender,
|
|
159
|
+
sourceTxHash: progress.txHash,
|
|
160
|
+
adapter: this.getName(),
|
|
161
|
+
id: progress.txHash,
|
|
162
|
+
sourceChain: quote.quote.quoteParams.fromChain,
|
|
163
|
+
targetChain: quote.quote.quoteParams.toChain,
|
|
164
|
+
inputAmount: quote.quote.quoteParams.amount,
|
|
165
|
+
outputAmount: quote.quote.outputAmount.toString(),
|
|
166
|
+
sourceToken: quote.quote.quoteParams.fromToken,
|
|
167
|
+
targetToken: quote.quote.quoteParams.toToken,
|
|
168
|
+
timestamp: new Date().getTime(),
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
.catch(reject)
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
async getTransactionStatus(params: NormalizedTxResponse): Promise<SwapStatus> {
|
|
177
|
+
try {
|
|
178
|
+
const res = await fetch(`https://app.across.to/api/deposit/status?depositTxHash=${params.sourceTxHash}`).then(
|
|
179
|
+
res => res.json(),
|
|
180
|
+
)
|
|
181
|
+
return {
|
|
182
|
+
txHash: res.fillTx || '',
|
|
183
|
+
status: res.status === 'filled' ? 'Success' : 'Processing',
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error('Error fetching transaction status:', error)
|
|
187
|
+
return {
|
|
188
|
+
txHash: '',
|
|
189
|
+
status: 'Processing',
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { useWalletSelector } from '@near-wallet-selector/react-hook'
|
|
2
|
+
import { ChainId } from '@openocean.finance/widget-sdk'
|
|
3
|
+
import type { AdaptedWallet } from '@reservoir0x/relay-sdk'
|
|
4
|
+
import type { WalletAdapterProps } from '@solana/wallet-adapter-base'
|
|
5
|
+
import type { Connection } from '@solana/web3.js'
|
|
6
|
+
import type { WalletClient } from 'viem'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Currency as EvmCurrency,
|
|
10
|
+
NearToken,
|
|
11
|
+
SolanaToken,
|
|
12
|
+
} from '../constants/index.js'
|
|
13
|
+
|
|
14
|
+
import type { Quote } from '../registry.js'
|
|
15
|
+
|
|
16
|
+
export enum NonEvmChain {
|
|
17
|
+
Near = 'near',
|
|
18
|
+
Bitcoin = 'bitcoin',
|
|
19
|
+
Solana = 'solana',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const BitcoinToken = {
|
|
23
|
+
name: 'Bitcoin',
|
|
24
|
+
symbol: 'BTC',
|
|
25
|
+
decimals: 8,
|
|
26
|
+
logo: 'https://storage.googleapis.com/ks-setting-1d682dca/285205e7-a16d-421c-a794-67439cd6b54f1751515894455.png',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type Chain = ChainId | NonEvmChain
|
|
30
|
+
export type Currency =
|
|
31
|
+
| EvmCurrency
|
|
32
|
+
| NearToken
|
|
33
|
+
| typeof BitcoinToken
|
|
34
|
+
| SolanaToken
|
|
35
|
+
|
|
36
|
+
export const NonEvmChainInfo: {
|
|
37
|
+
[key in NonEvmChain]: { name: string; icon: string }
|
|
38
|
+
} = {
|
|
39
|
+
[NonEvmChain.Near]: {
|
|
40
|
+
name: 'NEAR',
|
|
41
|
+
icon: 'https://storage.googleapis.com/ks-setting-1d682dca/000c677f-2ebc-44cc-8d76-e4c6d07627631744962669170.png',
|
|
42
|
+
},
|
|
43
|
+
[NonEvmChain.Bitcoin]: {
|
|
44
|
+
name: 'Bitcoin',
|
|
45
|
+
icon: 'https://storage.googleapis.com/ks-setting-1d682dca/285205e7-a16d-421c-a794-67439cd6b54f1751515894455.png',
|
|
46
|
+
},
|
|
47
|
+
[NonEvmChain.Solana]: {
|
|
48
|
+
name: 'Solana',
|
|
49
|
+
icon: 'https://solana.com/favicon.png',
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const NOT_SUPPORTED_CHAINS_PRICE_SERVICE = [
|
|
54
|
+
ChainId.FTM,
|
|
55
|
+
ChainId.SCL,
|
|
56
|
+
ChainId.BLS,
|
|
57
|
+
// ChainId.ZKSYNC,
|
|
58
|
+
// ChainId.HYPEREVM,
|
|
59
|
+
NonEvmChain.Solana,
|
|
60
|
+
NonEvmChain.Bitcoin,
|
|
61
|
+
NonEvmChain.Near,
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
export interface QuoteParams {
|
|
65
|
+
feeBps: number
|
|
66
|
+
fromChain: Chain
|
|
67
|
+
toChain: Chain
|
|
68
|
+
fromToken: Currency
|
|
69
|
+
toToken: Currency
|
|
70
|
+
amount: string
|
|
71
|
+
slippage: number
|
|
72
|
+
walletClient?: AdaptedWallet | WalletClient
|
|
73
|
+
tokenInUsd: number
|
|
74
|
+
tokenOutUsd: number
|
|
75
|
+
sender: string
|
|
76
|
+
recipient: string
|
|
77
|
+
publicKey?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface EvmQuoteParams extends QuoteParams {
|
|
81
|
+
fromToken: EvmCurrency
|
|
82
|
+
toToken: EvmCurrency
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface NearQuoteParams extends QuoteParams {
|
|
86
|
+
nearTokens: NearToken[]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface NormalizedQuote {
|
|
90
|
+
quoteParams: QuoteParams
|
|
91
|
+
|
|
92
|
+
outputAmount: bigint
|
|
93
|
+
formattedOutputAmount: string
|
|
94
|
+
|
|
95
|
+
inputUsd: number
|
|
96
|
+
outputUsd: number
|
|
97
|
+
|
|
98
|
+
rate: number
|
|
99
|
+
timeEstimate: number // in seconds
|
|
100
|
+
priceImpact: number // percent
|
|
101
|
+
|
|
102
|
+
gasFeeUsd: number
|
|
103
|
+
|
|
104
|
+
contractAddress: string
|
|
105
|
+
|
|
106
|
+
rawQuote: any
|
|
107
|
+
|
|
108
|
+
protocolFee: number
|
|
109
|
+
protocolFeeString?: string
|
|
110
|
+
platformFeePercent: number
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface NormalizedTxResponse {
|
|
114
|
+
id: string // specific id for each provider
|
|
115
|
+
sourceTxHash: string
|
|
116
|
+
sender: string
|
|
117
|
+
adapter: string
|
|
118
|
+
sourceChain: Chain
|
|
119
|
+
targetChain: Chain
|
|
120
|
+
inputAmount: string
|
|
121
|
+
outputAmount: string
|
|
122
|
+
sourceToken: Currency
|
|
123
|
+
targetToken: Currency
|
|
124
|
+
targetTxHash?: string
|
|
125
|
+
timestamp: number
|
|
126
|
+
status?: 'Processing' | 'Success' | 'Failed' | 'Refunded'
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface SwapStatus {
|
|
130
|
+
txHash: string
|
|
131
|
+
status: 'Processing' | 'Success' | 'Failed' | 'Refunded'
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Define a common interface for all swap providers
|
|
135
|
+
export interface SwapProvider {
|
|
136
|
+
getName(): string
|
|
137
|
+
getIcon(): string
|
|
138
|
+
getSupportedChains(): Chain[]
|
|
139
|
+
getSupportedTokens(sourceChain: Chain, destChain: Chain): Currency[]
|
|
140
|
+
getQuote(params: QuoteParams): Promise<NormalizedQuote>
|
|
141
|
+
executeSwap(
|
|
142
|
+
quote: Quote,
|
|
143
|
+
walletClient: WalletClient,
|
|
144
|
+
nearWallet?: ReturnType<typeof useWalletSelector>,
|
|
145
|
+
sendBtcFn?: (params: {
|
|
146
|
+
recipient: string
|
|
147
|
+
amount: string | number
|
|
148
|
+
}) => Promise<string>,
|
|
149
|
+
sendSolanaTransaction?: WalletAdapterProps['sendTransaction'],
|
|
150
|
+
connection?: Connection
|
|
151
|
+
): Promise<NormalizedTxResponse>
|
|
152
|
+
getTransactionStatus(p: NormalizedTxResponse): Promise<SwapStatus>
|
|
153
|
+
}
|
|
154
|
+
export abstract class BaseSwapAdapter implements SwapProvider {
|
|
155
|
+
abstract getName(): string
|
|
156
|
+
abstract getIcon(): string
|
|
157
|
+
abstract getSupportedChains(): Chain[]
|
|
158
|
+
abstract getSupportedTokens(sourceChain: Chain, destChain: Chain): Currency[]
|
|
159
|
+
abstract getQuote(params: QuoteParams): Promise<NormalizedQuote>
|
|
160
|
+
abstract executeSwap(
|
|
161
|
+
params: Quote,
|
|
162
|
+
walletClient: WalletClient,
|
|
163
|
+
nearWallet?: ReturnType<typeof useWalletSelector>
|
|
164
|
+
): Promise<NormalizedTxResponse>
|
|
165
|
+
abstract getTransactionStatus(p: NormalizedTxResponse): Promise<SwapStatus>
|
|
166
|
+
|
|
167
|
+
protected handleError(error: any): never {
|
|
168
|
+
console.error(`[${this.getName()}] Error:`, error)
|
|
169
|
+
throw new Error(
|
|
170
|
+
`${this.getName()} provider error: ${error.message || 'Unknown error'}`
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
}
|