@openocean.finance/widget 1.0.27 → 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 +217 -195
- 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 +305 -282
- package/src/stores/routes/createRouteExecutionStore.ts +13 -4
- package/src/types/widget.ts +3 -0
|
@@ -6,116 +6,116 @@ import type {
|
|
|
6
6
|
Route,
|
|
7
7
|
} from '@openocean.finance/widget-sdk'
|
|
8
8
|
import { Connection, Transaction, VersionedTransaction } from '@solana/web3.js'
|
|
9
|
-
import
|
|
9
|
+
import { ethers } from 'ethers'
|
|
10
10
|
import { getPublicClient, getWalletClient } from 'wagmi/actions'
|
|
11
|
-
import {
|
|
11
|
+
import { bridgeExecuteSwap } from '../cross/crossChainQuote.js'
|
|
12
12
|
import { useSettingsStore } from '../stores/settings/useSettingsStore.js'
|
|
13
|
-
import {
|
|
13
|
+
import { sendAndConfirmSolanaTransaction } from './SendAndConfirmSolanaTransaction.js'
|
|
14
14
|
|
|
15
15
|
const OpenOceanABI = [
|
|
16
16
|
{
|
|
17
|
-
|
|
17
|
+
inputs: [
|
|
18
|
+
{
|
|
19
|
+
internalType: 'contract IOpenOceanCaller',
|
|
20
|
+
name: 'caller',
|
|
21
|
+
type: 'address',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
components: [
|
|
18
25
|
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
internalType: 'contract IERC20',
|
|
27
|
+
name: 'srcToken',
|
|
28
|
+
type: 'address',
|
|
22
29
|
},
|
|
23
30
|
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"name": "srcToken",
|
|
28
|
-
"type": "address"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"internalType": "contract IERC20",
|
|
32
|
-
"name": "dstToken",
|
|
33
|
-
"type": "address"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"internalType": "address",
|
|
37
|
-
"name": "srcReceiver",
|
|
38
|
-
"type": "address"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"internalType": "address",
|
|
42
|
-
"name": "dstReceiver",
|
|
43
|
-
"type": "address"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"internalType": "uint256",
|
|
47
|
-
"name": "amount",
|
|
48
|
-
"type": "uint256"
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"internalType": "uint256",
|
|
52
|
-
"name": "minReturnAmount",
|
|
53
|
-
"type": "uint256"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"internalType": "uint256",
|
|
57
|
-
"name": "guaranteedAmount",
|
|
58
|
-
"type": "uint256"
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"internalType": "uint256",
|
|
62
|
-
"name": "flags",
|
|
63
|
-
"type": "uint256"
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"internalType": "address",
|
|
67
|
-
"name": "referrer",
|
|
68
|
-
"type": "address"
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"internalType": "bytes",
|
|
72
|
-
"name": "permit",
|
|
73
|
-
"type": "bytes"
|
|
74
|
-
}
|
|
75
|
-
],
|
|
76
|
-
"internalType": "struct OpenOceanExchange.SwapDescription",
|
|
77
|
-
"name": "desc",
|
|
78
|
-
"type": "tuple"
|
|
31
|
+
internalType: 'contract IERC20',
|
|
32
|
+
name: 'dstToken',
|
|
33
|
+
type: 'address',
|
|
79
34
|
},
|
|
80
35
|
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"type": "uint256"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"internalType": "uint256",
|
|
89
|
-
"name": "gasLimit",
|
|
90
|
-
"type": "uint256"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"internalType": "uint256",
|
|
94
|
-
"name": "value",
|
|
95
|
-
"type": "uint256"
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"internalType": "bytes",
|
|
99
|
-
"name": "data",
|
|
100
|
-
"type": "bytes"
|
|
101
|
-
}
|
|
102
|
-
],
|
|
103
|
-
"internalType": "struct IOpenOceanCaller.CallDescription[]",
|
|
104
|
-
"name": "calls",
|
|
105
|
-
"type": "tuple[]"
|
|
106
|
-
}
|
|
107
|
-
],
|
|
108
|
-
"name": "swap",
|
|
109
|
-
"outputs": [
|
|
36
|
+
internalType: 'address',
|
|
37
|
+
name: 'srcReceiver',
|
|
38
|
+
type: 'address',
|
|
39
|
+
},
|
|
110
40
|
{
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
41
|
+
internalType: 'address',
|
|
42
|
+
name: 'dstReceiver',
|
|
43
|
+
type: 'address',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
internalType: 'uint256',
|
|
47
|
+
name: 'amount',
|
|
48
|
+
type: 'uint256',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
internalType: 'uint256',
|
|
52
|
+
name: 'minReturnAmount',
|
|
53
|
+
type: 'uint256',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
internalType: 'uint256',
|
|
57
|
+
name: 'guaranteedAmount',
|
|
58
|
+
type: 'uint256',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
internalType: 'uint256',
|
|
62
|
+
name: 'flags',
|
|
63
|
+
type: 'uint256',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
internalType: 'address',
|
|
67
|
+
name: 'referrer',
|
|
68
|
+
type: 'address',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
internalType: 'bytes',
|
|
72
|
+
name: 'permit',
|
|
73
|
+
type: 'bytes',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
internalType: 'struct OpenOceanExchange.SwapDescription',
|
|
77
|
+
name: 'desc',
|
|
78
|
+
type: 'tuple',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
components: [
|
|
82
|
+
{
|
|
83
|
+
internalType: 'uint256',
|
|
84
|
+
name: 'target',
|
|
85
|
+
type: 'uint256',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
internalType: 'uint256',
|
|
89
|
+
name: 'gasLimit',
|
|
90
|
+
type: 'uint256',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
internalType: 'uint256',
|
|
94
|
+
name: 'value',
|
|
95
|
+
type: 'uint256',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
internalType: 'bytes',
|
|
99
|
+
name: 'data',
|
|
100
|
+
type: 'bytes',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
internalType: 'struct IOpenOceanCaller.CallDescription[]',
|
|
104
|
+
name: 'calls',
|
|
105
|
+
type: 'tuple[]',
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
name: 'swap',
|
|
109
|
+
outputs: [
|
|
110
|
+
{
|
|
111
|
+
internalType: 'uint256',
|
|
112
|
+
name: 'returnAmount',
|
|
113
|
+
type: 'uint256',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
stateMutability: 'payable',
|
|
117
|
+
type: 'function',
|
|
118
|
+
},
|
|
119
119
|
]
|
|
120
120
|
|
|
121
121
|
// Add helper function for handling hexadecimal conversion
|
|
@@ -124,6 +124,121 @@ function hexToUint8Array(hexString: string): Uint8Array {
|
|
|
124
124
|
return new Uint8Array(pairs.map((s) => Number.parseInt(s, 16)))
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Convert amount with precision to actual amount
|
|
129
|
+
* @param amount Amount with precision
|
|
130
|
+
* @param decimals Precision
|
|
131
|
+
* @returns Actual amount
|
|
132
|
+
*/
|
|
133
|
+
function decimals2Amount(amount: string | number, decimals = 18): number {
|
|
134
|
+
return Number(amount) / 10 ** decimals
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Swap response type
|
|
139
|
+
*/
|
|
140
|
+
interface SwapResponse {
|
|
141
|
+
inAmount?: string
|
|
142
|
+
inToken?: {
|
|
143
|
+
decimals?: number
|
|
144
|
+
price?: string | number
|
|
145
|
+
priceUSD?: string | number
|
|
146
|
+
address?: string
|
|
147
|
+
}
|
|
148
|
+
data?: string
|
|
149
|
+
from?: string
|
|
150
|
+
to?: string
|
|
151
|
+
value?: string
|
|
152
|
+
minOutAmount?: string
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Adjust transaction parameters based on dynamic slippage to provide MEV protection
|
|
157
|
+
*/
|
|
158
|
+
async function swapQuoteMEV(
|
|
159
|
+
response: SwapResponse,
|
|
160
|
+
options?: { publicClient?: any }
|
|
161
|
+
): Promise<SwapResponse> {
|
|
162
|
+
try {
|
|
163
|
+
const inAmount = response?.inAmount
|
|
164
|
+
const inTokenDecimals = response?.inToken?.decimals || 18
|
|
165
|
+
const inTokenPrice = Number(response?.inToken?.priceUSD || 0)
|
|
166
|
+
const amount = decimals2Amount(inAmount, inTokenDecimals) * inTokenPrice
|
|
167
|
+
if (amount < 1) {
|
|
168
|
+
return response
|
|
169
|
+
}
|
|
170
|
+
const { publicClient } = options
|
|
171
|
+
const OPENOCEAN_CONTRACT = new ethers.Contract(
|
|
172
|
+
'0x6352a56caadC4F1E25CD6c75970Fa768A3304e64',
|
|
173
|
+
OpenOceanABI
|
|
174
|
+
)
|
|
175
|
+
if (
|
|
176
|
+
ethers.hexlify(ethers.getBytes(response?.data || '0x').slice(0, 4)) !==
|
|
177
|
+
OPENOCEAN_CONTRACT.interface.getFunction('swap').selector
|
|
178
|
+
) {
|
|
179
|
+
return response
|
|
180
|
+
}
|
|
181
|
+
const oldCallData = OPENOCEAN_CONTRACT.interface.decodeFunctionData(
|
|
182
|
+
'swap',
|
|
183
|
+
response?.data
|
|
184
|
+
)
|
|
185
|
+
const callData = [...oldCallData]
|
|
186
|
+
callData[1] = [...oldCallData[1]]
|
|
187
|
+
const minOutAmount = BigInt(callData[1][5] || 0)
|
|
188
|
+
const outAmount = BigInt(callData[1][6] || 0)
|
|
189
|
+
const slippageAmount = outAmount - minOutAmount
|
|
190
|
+
|
|
191
|
+
const minOutAmounts = await Promise.all(
|
|
192
|
+
[1, 2, 3].map(async (i) => {
|
|
193
|
+
const mockMinOutAmount =
|
|
194
|
+
minOutAmount + (slippageAmount / 4n) * BigInt(i)
|
|
195
|
+
callData[1][5] = mockMinOutAmount
|
|
196
|
+
const params = {
|
|
197
|
+
from: response?.from as `0x${string}`,
|
|
198
|
+
to: response?.to as `0x${string}`,
|
|
199
|
+
data: OPENOCEAN_CONTRACT.interface.encodeFunctionData(
|
|
200
|
+
'swap',
|
|
201
|
+
callData
|
|
202
|
+
) as `0x${string}`,
|
|
203
|
+
value: BigInt(response?.value || '0'),
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
await publicClient.estimateGas(params)
|
|
207
|
+
return mockMinOutAmount
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error('Failed to estimate gas:', error)
|
|
210
|
+
return undefined
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
)
|
|
214
|
+
let [min1, min2] = minOutAmounts
|
|
215
|
+
.filter((value) => value !== undefined)
|
|
216
|
+
.sort((a, b) => (BigInt(b || 0) > BigInt(a || 0) ? 1 : -1))
|
|
217
|
+
.slice(0, 2)
|
|
218
|
+
min1 = min1 ?? minOutAmount
|
|
219
|
+
min2 = min2 ?? minOutAmount
|
|
220
|
+
|
|
221
|
+
const randomFactor = BigInt(Math.floor(Math.random() * 10000))
|
|
222
|
+
const minOutAmountDiff = BigInt(min1 || 0) - BigInt(min2 || 0)
|
|
223
|
+
const finalMinOutAmount =
|
|
224
|
+
BigInt(min2 || 0) + (minOutAmountDiff * randomFactor) / BigInt(10000)
|
|
225
|
+
|
|
226
|
+
if (finalMinOutAmount < minOutAmount) {
|
|
227
|
+
return response
|
|
228
|
+
}
|
|
229
|
+
callData[1][5] = finalMinOutAmount
|
|
230
|
+
const finalCallData = OPENOCEAN_CONTRACT.interface.encodeFunctionData(
|
|
231
|
+
'swap',
|
|
232
|
+
callData
|
|
233
|
+
)
|
|
234
|
+
response.minOutAmount = finalMinOutAmount.toString()
|
|
235
|
+
response.data = finalCallData
|
|
236
|
+
return response
|
|
237
|
+
} catch {
|
|
238
|
+
return response
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
127
242
|
interface ExecuteRouteOptions {
|
|
128
243
|
updateRouteHook?: (route: Route) => void
|
|
129
244
|
acceptExchangeRateUpdateHook?: (params: any) => Promise<boolean>
|
|
@@ -136,6 +251,7 @@ interface ExecuteRouteOptions {
|
|
|
136
251
|
}
|
|
137
252
|
|
|
138
253
|
interface ExtendedOpenOceanStep extends OpenOceanStep {
|
|
254
|
+
quoteData?: any
|
|
139
255
|
execution?: {
|
|
140
256
|
status: ExecutionStatus
|
|
141
257
|
process: Process[]
|
|
@@ -147,8 +263,9 @@ interface ExtendedOpenOceanStep extends OpenOceanStep {
|
|
|
147
263
|
|
|
148
264
|
interface ExtendedRoute extends Route {
|
|
149
265
|
steps: ExtendedOpenOceanStep[]
|
|
150
|
-
|
|
151
|
-
|
|
266
|
+
prependedOperatingExpenseCost?: string
|
|
267
|
+
data?: {
|
|
268
|
+
prependedOperatingExpenseCost?: string
|
|
152
269
|
}
|
|
153
270
|
}
|
|
154
271
|
|
|
@@ -172,16 +289,18 @@ async function executeSolanaSwap(
|
|
|
172
289
|
const txData: any = step.transactionRequest?.data || ''
|
|
173
290
|
const dexId = step.transactionRequest?.type || 0
|
|
174
291
|
if (step.action.fromChainId === step.action.toChainId) {
|
|
175
|
-
if (dexId
|
|
292
|
+
if (dexId === 6 || dexId === 7 || dexId === 9) {
|
|
176
293
|
transaction = VersionedTransaction.deserialize(hexToUint8Array(txData))
|
|
177
294
|
} else {
|
|
178
295
|
transaction = Transaction.from(hexToUint8Array(txData))
|
|
179
296
|
}
|
|
180
297
|
} else {
|
|
181
|
-
transaction = VersionedTransaction.deserialize(
|
|
298
|
+
transaction = VersionedTransaction.deserialize(
|
|
299
|
+
hexToUint8Array(txData.slice(2))
|
|
300
|
+
)
|
|
182
301
|
|
|
183
|
-
const { blockhash } = await connection.getLatestBlockhash()
|
|
184
|
-
transaction.message.recentBlockhash = blockhash
|
|
302
|
+
const { blockhash } = await connection.getLatestBlockhash()
|
|
303
|
+
transaction.message.recentBlockhash = blockhash
|
|
185
304
|
}
|
|
186
305
|
|
|
187
306
|
// Check signTransaction method exists
|
|
@@ -254,7 +373,7 @@ async function executeEvmSwap(
|
|
|
254
373
|
): Promise<void> {
|
|
255
374
|
try {
|
|
256
375
|
let walletClient = await getWalletClient(options.wagmiConfig)
|
|
257
|
-
|
|
376
|
+
|
|
258
377
|
// Check if wallet is connected
|
|
259
378
|
if (!walletClient) {
|
|
260
379
|
if (options.account?.connector && options.onDisconnect) {
|
|
@@ -265,18 +384,18 @@ async function executeEvmSwap(
|
|
|
265
384
|
}
|
|
266
385
|
throw new Error('Please connect wallet first')
|
|
267
386
|
}
|
|
268
|
-
|
|
387
|
+
|
|
269
388
|
// Check if current chain matches target chain
|
|
270
389
|
const currentChainId = walletClient.chain.id
|
|
271
390
|
const targetChainId = step.action.fromChainId
|
|
272
|
-
|
|
391
|
+
|
|
273
392
|
if (currentChainId !== targetChainId) {
|
|
274
393
|
try {
|
|
275
394
|
// Try to switch to target chain
|
|
276
395
|
await walletClient.switchChain({ id: targetChainId })
|
|
277
|
-
|
|
396
|
+
|
|
278
397
|
// Get updated walletClient after chain switch
|
|
279
|
-
walletClient = await getWalletClient(options.wagmiConfig) as any
|
|
398
|
+
walletClient = (await getWalletClient(options.wagmiConfig)) as any
|
|
280
399
|
if (!walletClient || walletClient.chain.id !== targetChainId) {
|
|
281
400
|
throw new Error('Failed to switch chain')
|
|
282
401
|
}
|
|
@@ -299,19 +418,23 @@ async function executeEvmSwap(
|
|
|
299
418
|
throw new Error('Public client not found')
|
|
300
419
|
}
|
|
301
420
|
|
|
302
|
-
console.log(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
)
|
|
307
|
-
console.log('Token Address:', step.action.fromToken.address)
|
|
308
|
-
console.log('Token Chain ID:', step.action.fromToken.chainId)
|
|
309
|
-
console.log('Owner Address:', walletClient.account.address)
|
|
310
|
-
console.log('Spender Address:', step.estimate.approvalAddress)
|
|
311
|
-
|
|
421
|
+
// console.log(
|
|
422
|
+
// 'Current Chain:',
|
|
423
|
+
// publicClient.chain?.id,
|
|
424
|
+
// publicClient.chain?.name
|
|
425
|
+
// )
|
|
426
|
+
// console.log('Token Address:', step.action.fromToken.address)
|
|
427
|
+
// console.log('Token Chain ID:', step.action.fromToken.chainId)
|
|
428
|
+
// console.log('Owner Address:', walletClient.account.address)
|
|
429
|
+
// console.log('Spender Address:', step.estimate.approvalAddress)
|
|
312
430
|
// Check token approval
|
|
313
431
|
if (
|
|
314
|
-
[
|
|
432
|
+
[
|
|
433
|
+
'0x0000000000000000000000000000000000000000',
|
|
434
|
+
'0x0000000000000000000000000000000000001010',
|
|
435
|
+
].indexOf(step.action.fromToken.address) === -1 &&
|
|
436
|
+
step.estimate.approvalAddress !==
|
|
437
|
+
'0x0000000000000000000000000000000000000000'
|
|
315
438
|
) {
|
|
316
439
|
let allowance = 0n
|
|
317
440
|
try {
|
|
@@ -347,7 +470,11 @@ async function executeEvmSwap(
|
|
|
347
470
|
|
|
348
471
|
const amount =
|
|
349
472
|
BigInt(step.action.fromAmount) +
|
|
350
|
-
BigInt(
|
|
473
|
+
BigInt(
|
|
474
|
+
route?.prependedOperatingExpenseCost ||
|
|
475
|
+
route?.data?.prependedOperatingExpenseCost ||
|
|
476
|
+
'0'
|
|
477
|
+
)
|
|
351
478
|
if (allowance < BigInt(amount)) {
|
|
352
479
|
const approvalAmount = options.infiniteApproval
|
|
353
480
|
? BigInt(
|
|
@@ -382,58 +509,68 @@ async function executeEvmSwap(
|
|
|
382
509
|
}
|
|
383
510
|
}
|
|
384
511
|
|
|
385
|
-
const { transactionRequest } = step || {}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
512
|
+
const { transactionRequest, type, quoteData } = step || {}
|
|
513
|
+
let hash: any = ''
|
|
514
|
+
if ((type as any) === 'bridge') {
|
|
515
|
+
const result = await bridgeExecuteSwap({
|
|
516
|
+
quoteData: quoteData,
|
|
517
|
+
walletClient: walletClient,
|
|
518
|
+
})
|
|
519
|
+
hash = result.sourceTxHash
|
|
520
|
+
} else {
|
|
521
|
+
const txRequest = {
|
|
522
|
+
chain: publicClient.chain,
|
|
523
|
+
to: transactionRequest?.to as `0x${string}`,
|
|
524
|
+
data: (transactionRequest?.data as `0x${string}`) || '0x',
|
|
525
|
+
value: BigInt(transactionRequest?.value || '0x0'),
|
|
526
|
+
account: walletClient.account.address,
|
|
527
|
+
}
|
|
393
528
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
529
|
+
// Check if dynamicSlippage is enabled in useSettingsStore to determine whether to call swap_quote_mev
|
|
530
|
+
const { dynamicSlippage } = useSettingsStore.getState()
|
|
531
|
+
if (dynamicSlippage) {
|
|
532
|
+
try {
|
|
533
|
+
// Build response object
|
|
534
|
+
const response = {
|
|
535
|
+
inAmount: step.action.fromAmount || '0',
|
|
536
|
+
inToken: step.action.fromToken,
|
|
537
|
+
data: transactionRequest?.data,
|
|
538
|
+
from: walletClient.account.address,
|
|
539
|
+
to: transactionRequest?.to,
|
|
540
|
+
value: transactionRequest?.value || '0',
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Call swap_quote_mev to get adjusted transaction data
|
|
544
|
+
const adjustedResponse = await swapQuoteMEV(response, {
|
|
545
|
+
publicClient,
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
// If swap_quote_mev returns modified data, update transaction request
|
|
549
|
+
if (adjustedResponse && adjustedResponse.data !== response.data) {
|
|
550
|
+
txRequest.data = adjustedResponse.data as `0x${string}`
|
|
551
|
+
txRequest.value = BigInt(adjustedResponse.value || '0')
|
|
552
|
+
// Applied MEV protection with dynamic slippage
|
|
553
|
+
}
|
|
554
|
+
} catch (error) {
|
|
555
|
+
console.error('Failed to apply MEV protection:', error)
|
|
556
|
+
// Continue with original transaction request on error
|
|
416
557
|
}
|
|
417
|
-
} catch (error) {
|
|
418
|
-
console.error('Failed to apply MEV protection:', error);
|
|
419
|
-
// 错误时继续使用原始交易请求
|
|
420
558
|
}
|
|
421
|
-
}
|
|
422
559
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
console.log('estimatedGas', estimatedGas)
|
|
560
|
+
// Estimate gas
|
|
561
|
+
const estimatedGas = await publicClient.estimateGas(txRequest)
|
|
426
562
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
563
|
+
// Add estimated gas to transaction request (using 2x the estimated value to ensure transaction success)
|
|
564
|
+
const finalTxRequest = {
|
|
565
|
+
...txRequest,
|
|
566
|
+
gas: estimatedGas * 2n,
|
|
567
|
+
}
|
|
432
568
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
569
|
+
hash = await walletClient.sendTransaction({
|
|
570
|
+
...finalTxRequest,
|
|
571
|
+
kzg: undefined,
|
|
572
|
+
})
|
|
573
|
+
}
|
|
437
574
|
|
|
438
575
|
process.status = 'PENDING'
|
|
439
576
|
process.txHash = hash
|
|
@@ -476,120 +613,6 @@ async function executeEvmSwap(
|
|
|
476
613
|
}
|
|
477
614
|
}
|
|
478
615
|
|
|
479
|
-
/**
|
|
480
|
-
* 将带精度的金额转换为实际金额
|
|
481
|
-
* @param amount 带精度的金额
|
|
482
|
-
* @param decimals 精度
|
|
483
|
-
* @returns 实际金额
|
|
484
|
-
*/
|
|
485
|
-
function decimals2Amount(amount: string | number, decimals = 18): number {
|
|
486
|
-
return Number(amount) / Math.pow(10, decimals);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* 交易响应类型
|
|
491
|
-
*/
|
|
492
|
-
interface SwapResponse {
|
|
493
|
-
inAmount?: string;
|
|
494
|
-
inToken?: {
|
|
495
|
-
decimals?: number;
|
|
496
|
-
price?: string | number;
|
|
497
|
-
priceUSD?: string | number;
|
|
498
|
-
address?: string;
|
|
499
|
-
};
|
|
500
|
-
data?: string;
|
|
501
|
-
from?: string;
|
|
502
|
-
to?: string;
|
|
503
|
-
value?: string;
|
|
504
|
-
minOutAmount?: string;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* 根据动态滑点调整交易参数,提供 MEV 保护
|
|
509
|
-
*/
|
|
510
|
-
async function swapQuoteMEV(
|
|
511
|
-
response: SwapResponse,
|
|
512
|
-
options?: { publicClient?: any }
|
|
513
|
-
): Promise<SwapResponse> {
|
|
514
|
-
try {
|
|
515
|
-
const inAmount = response?.inAmount;
|
|
516
|
-
const inTokenDecimals = response?.inToken?.decimals || 18;
|
|
517
|
-
const inTokenPrice = Number(response?.inToken?.priceUSD || 0);
|
|
518
|
-
const amount = decimals2Amount(inAmount, inTokenDecimals) * inTokenPrice;
|
|
519
|
-
if (amount < 1) {
|
|
520
|
-
return response;
|
|
521
|
-
}
|
|
522
|
-
const { publicClient } = options;
|
|
523
|
-
const OPENOCEAN_CONTRACT = new ethers.Contract(
|
|
524
|
-
"0x6352a56caadC4F1E25CD6c75970Fa768A3304e64",
|
|
525
|
-
OpenOceanABI
|
|
526
|
-
);
|
|
527
|
-
if (
|
|
528
|
-
ethers.hexlify(ethers.getBytes(response?.data || "0x").slice(0, 4)) !==
|
|
529
|
-
OPENOCEAN_CONTRACT.interface.getFunction("swap").selector
|
|
530
|
-
) {
|
|
531
|
-
return response;
|
|
532
|
-
}
|
|
533
|
-
let oldCallData = OPENOCEAN_CONTRACT.interface.decodeFunctionData(
|
|
534
|
-
"swap",
|
|
535
|
-
response?.data
|
|
536
|
-
);
|
|
537
|
-
let callData = [...oldCallData];
|
|
538
|
-
callData[1] = [...oldCallData[1]];
|
|
539
|
-
const minOutAmount = BigInt(callData[1][5] || 0);
|
|
540
|
-
const outAmount = BigInt(callData[1][6] || 0);
|
|
541
|
-
const slippageAmount = outAmount - minOutAmount;
|
|
542
|
-
|
|
543
|
-
let minOutAmounts = await Promise.all(
|
|
544
|
-
[1, 2, 3].map(async (i) => {
|
|
545
|
-
const mockMinOutAmount = minOutAmount + (slippageAmount / 4n) * BigInt(i);
|
|
546
|
-
callData[1][5] = mockMinOutAmount;
|
|
547
|
-
const params = {
|
|
548
|
-
from: response?.from as `0x${string}`,
|
|
549
|
-
to: response?.to as `0x${string}`,
|
|
550
|
-
data: OPENOCEAN_CONTRACT.interface.encodeFunctionData(
|
|
551
|
-
"swap",
|
|
552
|
-
callData
|
|
553
|
-
) as `0x${string}`,
|
|
554
|
-
value: BigInt(response?.value || '0'),
|
|
555
|
-
};
|
|
556
|
-
try {
|
|
557
|
-
await publicClient.estimateGas(params);
|
|
558
|
-
return mockMinOutAmount;
|
|
559
|
-
} catch (error) {
|
|
560
|
-
console.error('Failed to estimate gas:', error);
|
|
561
|
-
return undefined;
|
|
562
|
-
}
|
|
563
|
-
})
|
|
564
|
-
);
|
|
565
|
-
let [min1, min2] = minOutAmounts
|
|
566
|
-
.filter((value) => value !== undefined)
|
|
567
|
-
.sort((a, b) => (BigInt(b || 0) > BigInt(a || 0) ? 1 : -1))
|
|
568
|
-
.slice(0, 2);
|
|
569
|
-
min1 = min1 ?? minOutAmount;
|
|
570
|
-
min2 = min2 ?? minOutAmount;
|
|
571
|
-
|
|
572
|
-
const randomFactor = BigInt(Math.floor(Math.random() * 10000));
|
|
573
|
-
const minOutAmountDiff = BigInt(min1 || 0) - BigInt(min2 || 0);
|
|
574
|
-
const finalMinOutAmount = BigInt(min2 || 0) +
|
|
575
|
-
(minOutAmountDiff * randomFactor / BigInt(10000));
|
|
576
|
-
|
|
577
|
-
if (finalMinOutAmount < minOutAmount) {
|
|
578
|
-
return response;
|
|
579
|
-
}
|
|
580
|
-
callData[1][5] = finalMinOutAmount;
|
|
581
|
-
const finalCallData = OPENOCEAN_CONTRACT.interface.encodeFunctionData(
|
|
582
|
-
"swap",
|
|
583
|
-
callData
|
|
584
|
-
);
|
|
585
|
-
response.minOutAmount = finalMinOutAmount.toString();
|
|
586
|
-
response.data = finalCallData;
|
|
587
|
-
return response;
|
|
588
|
-
} catch (error) {
|
|
589
|
-
return response;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
616
|
// Execute transaction
|
|
594
617
|
async function executeSwap(
|
|
595
618
|
route: ExtendedRoute,
|