@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.
Files changed (110) hide show
  1. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +46 -39
  2. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  3. package/dist/esm/components/Messages/WarningMessages.js +2 -2
  4. package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
  5. package/dist/esm/components/Step/Step.js +37 -29
  6. package/dist/esm/components/Step/Step.js.map +1 -1
  7. package/dist/esm/components/TransactionDetails.js +2 -5
  8. package/dist/esm/components/TransactionDetails.js.map +1 -1
  9. package/dist/esm/config/version.d.ts +1 -1
  10. package/dist/esm/config/version.js +1 -1
  11. package/dist/esm/cross/adapters/AcrossAdapter.d.ts +15 -0
  12. package/dist/esm/cross/adapters/AcrossAdapter.js +166 -0
  13. package/dist/esm/cross/adapters/AcrossAdapter.js.map +1 -0
  14. package/dist/esm/cross/adapters/BaseSwapAdapter.d.ts +107 -0
  15. package/dist/esm/cross/adapters/BaseSwapAdapter.js +44 -0
  16. package/dist/esm/cross/adapters/BaseSwapAdapter.js.map +1 -0
  17. package/dist/esm/cross/adapters/DebridgeAdapter.d.ts +20 -0
  18. package/dist/esm/cross/adapters/DebridgeAdapter.js +264 -0
  19. package/dist/esm/cross/adapters/DebridgeAdapter.js.map +1 -0
  20. package/dist/esm/cross/adapters/LifiAdapter.d.ts +19 -0
  21. package/dist/esm/cross/adapters/LifiAdapter.js +169 -0
  22. package/dist/esm/cross/adapters/LifiAdapter.js.map +1 -0
  23. package/dist/esm/cross/adapters/MayanAdapter.d.ts +14 -0
  24. package/dist/esm/cross/adapters/MayanAdapter.js +119 -0
  25. package/dist/esm/cross/adapters/MayanAdapter.js.map +1 -0
  26. package/dist/esm/cross/adapters/NearIntentsAdapter.d.ts +21 -0
  27. package/dist/esm/cross/adapters/NearIntentsAdapter.js +425 -0
  28. package/dist/esm/cross/adapters/NearIntentsAdapter.js.map +1 -0
  29. package/dist/esm/cross/adapters/OptimexAdapter.d.ts +19 -0
  30. package/dist/esm/cross/adapters/OptimexAdapter.js +216 -0
  31. package/dist/esm/cross/adapters/OptimexAdapter.js.map +1 -0
  32. package/dist/esm/cross/adapters/OrbiterAdapter.d.ts +20 -0
  33. package/dist/esm/cross/adapters/OrbiterAdapter.js +213 -0
  34. package/dist/esm/cross/adapters/OrbiterAdapter.js.map +1 -0
  35. package/dist/esm/cross/adapters/RelayAdapter.d.ts +14 -0
  36. package/dist/esm/cross/adapters/RelayAdapter.js +171 -0
  37. package/dist/esm/cross/adapters/RelayAdapter.js.map +1 -0
  38. package/dist/esm/cross/adapters/SymbiosisAdapter.d.ts +14 -0
  39. package/dist/esm/cross/adapters/SymbiosisAdapter.js +120 -0
  40. package/dist/esm/cross/adapters/SymbiosisAdapter.js.map +1 -0
  41. package/dist/esm/cross/adapters/XYFinanceAdapter.d.ts +14 -0
  42. package/dist/esm/cross/adapters/XYFinanceAdapter.js +177 -0
  43. package/dist/esm/cross/adapters/XYFinanceAdapter.js.map +1 -0
  44. package/dist/esm/cross/adapters/index.d.ts +2 -0
  45. package/dist/esm/cross/adapters/index.js +10 -0
  46. package/dist/esm/cross/adapters/index.js.map +1 -0
  47. package/dist/esm/cross/constants/index.d.ts +202 -0
  48. package/dist/esm/cross/constants/index.js +183 -0
  49. package/dist/esm/cross/constants/index.js.map +1 -0
  50. package/dist/esm/cross/crossChainQuote.d.ts +25 -0
  51. package/dist/esm/cross/crossChainQuote.js +127 -0
  52. package/dist/esm/cross/crossChainQuote.js.map +1 -0
  53. package/dist/esm/cross/factory.d.ts +9 -0
  54. package/dist/esm/cross/factory.js +125 -0
  55. package/dist/esm/cross/factory.js.map +1 -0
  56. package/dist/esm/cross/registry.d.ts +12 -0
  57. package/dist/esm/cross/registry.js +52 -0
  58. package/dist/esm/cross/registry.js.map +1 -0
  59. package/dist/esm/hooks/useChain.d.ts +1 -1
  60. package/dist/esm/hooks/useGasRefuel.d.ts +1 -1
  61. package/dist/esm/hooks/useGasSufficiencyBridge.js +1 -2
  62. package/dist/esm/hooks/useGasSufficiencyBridge.js.map +1 -1
  63. package/dist/esm/hooks/useRouteExecution.js +2 -1
  64. package/dist/esm/hooks/useRouteExecution.js.map +1 -1
  65. package/dist/esm/hooks/useRoutes.js +50 -32
  66. package/dist/esm/hooks/useRoutes.js.map +1 -1
  67. package/dist/esm/hooks/useSettingMonitor.js +1 -0
  68. package/dist/esm/hooks/useSettingMonitor.js.map +1 -1
  69. package/dist/esm/hooks/useTokenAddressBalance.d.ts +1 -1
  70. package/dist/esm/hooks/useTokenPrice.js +4 -2
  71. package/dist/esm/hooks/useTokenPrice.js.map +1 -1
  72. package/dist/esm/hooks/useTokens.d.ts +1 -1
  73. package/dist/esm/services/ExecuteRoute.js +142 -124
  74. package/dist/esm/services/ExecuteRoute.js.map +1 -1
  75. package/dist/esm/stores/form/useFieldController.d.ts +1 -1
  76. package/dist/esm/stores/routes/createRouteExecutionStore.js +6 -3
  77. package/dist/esm/stores/routes/createRouteExecutionStore.js.map +1 -1
  78. package/dist/esm/stores/routes/useSetExecutableRoute.d.ts +1 -1
  79. package/dist/esm/types/widget.d.ts +3 -0
  80. package/dist/tsconfig.tsbuildinfo +1 -0
  81. package/package.json +14 -4
  82. package/src/components/AmountInput/AmountInputEndAdornment.tsx +46 -46
  83. package/src/components/Messages/WarningMessages.tsx +7 -2
  84. package/src/components/Step/Step.tsx +37 -31
  85. package/src/components/TransactionDetails.tsx +10 -11
  86. package/src/config/version.ts +1 -1
  87. package/src/cross/adapters/AcrossAdapter.ts +193 -0
  88. package/src/cross/adapters/BaseSwapAdapter.ts +173 -0
  89. package/src/cross/adapters/DebridgeAdapter.ts +375 -0
  90. package/src/cross/adapters/LifiAdapter.ts +213 -0
  91. package/src/cross/adapters/MayanAdapter.ts +179 -0
  92. package/src/cross/adapters/NearIntentsAdapter.ts +539 -0
  93. package/src/cross/adapters/OptimexAdapter.ts +273 -0
  94. package/src/cross/adapters/OrbiterAdapter.ts +270 -0
  95. package/src/cross/adapters/RelayAdapter.ts +248 -0
  96. package/src/cross/adapters/SymbiosisAdapter.ts +144 -0
  97. package/src/cross/adapters/XYFinanceAdapter.ts +213 -0
  98. package/src/cross/adapters/index.ts +9 -0
  99. package/src/cross/constants/index.ts +223 -0
  100. package/src/cross/crossChainQuote.ts +181 -0
  101. package/src/cross/factory.ts +145 -0
  102. package/src/cross/registry.ts +65 -0
  103. package/src/hooks/useGasSufficiencyBridge.ts +1 -3
  104. package/src/hooks/useRouteExecution.ts +2 -1
  105. package/src/hooks/useRoutes.ts +64 -43
  106. package/src/hooks/useSettingMonitor.ts +1 -1
  107. package/src/hooks/useTokenPrice.ts +5 -3
  108. package/src/services/ExecuteRoute.ts +184 -171
  109. package/src/stores/routes/createRouteExecutionStore.ts +13 -4
  110. package/src/types/widget.ts +3 -0
@@ -0,0 +1,273 @@
1
+ import { ChainId } from '@openocean.finance/widget-sdk'
2
+ import { Currency } from '../constants/index.js'
3
+
4
+ import { WalletClient, formatUnits } from 'viem'
5
+
6
+ import { CROSS_CHAIN_FEE_RECEIVER, ZERO_ADDRESS } from '../constants/index.js'
7
+
8
+ import { Quote } from '../registry.js'
9
+ import {
10
+ BaseSwapAdapter,
11
+ Chain,
12
+ NonEvmChain,
13
+ NormalizedQuote,
14
+ NormalizedTxResponse,
15
+ QuoteParams,
16
+ SwapStatus,
17
+ } from './BaseSwapAdapter.js'
18
+
19
+ const OPTIMEX_API = 'https://ks-provider.optimex.xyz/v1'
20
+
21
+ interface OptimexToken {
22
+ id: number
23
+ network_id: 'ethereum' | 'bitcoin'
24
+ token_id: string
25
+ network_name: string
26
+ network_symbol: string
27
+ network_type: 'EVM' | 'BTC'
28
+ token_name: string
29
+ token_symbol: string
30
+ token_address: string
31
+ token_decimals: number
32
+ token_logo_uri: string
33
+ network_logo_uri: string
34
+ active: boolean
35
+ }
36
+
37
+ export class OptimexAdapter extends BaseSwapAdapter {
38
+ private tokens: OptimexToken[]
39
+
40
+ constructor() {
41
+ super()
42
+ this.tokens = []
43
+ }
44
+
45
+ private async getTokens() {
46
+ try {
47
+ const res = await fetch(`${OPTIMEX_API}/tokens`)
48
+ const { data } = await res.json()
49
+ this.tokens = data.tokens
50
+ } catch (error) {
51
+ console.error('Failed to initialize Optimex tokens:', error)
52
+ // Handle error appropriately
53
+ }
54
+ }
55
+ getName(): string {
56
+ return 'Optimex'
57
+ }
58
+ getIcon(): string {
59
+ return 'https://storage.googleapis.com/ks-setting-1d682dca/464ce79e-a906-4590-bf78-9054e606aa041749023419612.png'
60
+ }
61
+ getSupportedChains(): Chain[] {
62
+ return [NonEvmChain.Bitcoin, ChainId.MAINNET]
63
+ }
64
+
65
+ getSupportedTokens(_sourceChain: Chain, _destChain: Chain): Currency[] {
66
+ return []
67
+ }
68
+
69
+ async getQuote(params: QuoteParams): Promise<NormalizedQuote> {
70
+ if (!this.tokens?.length) {
71
+ await this.getTokens()
72
+ }
73
+
74
+ const isFromBtc = params.fromChain === NonEvmChain.Bitcoin
75
+ const isToBtc = params.toChain === NonEvmChain.Bitcoin
76
+ const fromToken = isFromBtc
77
+ ? { token_id: 'BTC', token_symbol: 'BTC' }
78
+ : this.tokens.find(item => {
79
+ const address = (params.fromToken as any).isNative ? 'native' : (params.fromToken as any).wrapped.address
80
+ return item.network_id === 'ethereum' && address.toLowerCase() === item.token_address.toLowerCase()
81
+ })
82
+ const fromTokenId = fromToken?.token_id
83
+
84
+ const toToken = isToBtc
85
+ ? { token_id: 'BTC', token_symbol: 'BTC' }
86
+ : this.tokens.find(item => {
87
+ const address = (params.toToken as any).isNative ? 'native' : (params.toToken as any).wrapped.address
88
+ return item.network_id === 'ethereum' && address.toLowerCase() === item.token_address.toLowerCase()
89
+ })
90
+ const toTokenId = toToken?.token_id
91
+
92
+ if (!fromTokenId || !toTokenId) {
93
+ console.log('optimex tokens', this.tokens)
94
+ throw new Error(`Optimex does not support ${!fromTokenId ? params.fromToken.symbol : params.toToken.symbol}`)
95
+ }
96
+
97
+ const [quoteRes, estimateRes, token0Usd, token1Usd] = await Promise.all([
98
+ fetch(`${OPTIMEX_API}/solver/indicative-quote`, {
99
+ method: 'POST',
100
+ headers: {
101
+ 'Content-Type': 'application/json',
102
+ },
103
+ body: JSON.stringify({
104
+ debug: false,
105
+ from_token_amount: params.amount,
106
+ from_token_id: fromTokenId,
107
+ to_token_id: toTokenId,
108
+ affiliate_fee_bps: params.feeBps.toString(),
109
+ }),
110
+ }).then(res => res.json()),
111
+ fetch(`${OPTIMEX_API}/trades/estimate?from_token=${fromTokenId}&to_token=${toTokenId}`).then(res => res.json()),
112
+ fetch(`https://api.optimex.xyz/v1/tokens/${fromToken.token_symbol}`)
113
+ .then(res => res.json())
114
+ .then(res => res?.data?.current_price || 0),
115
+ fetch(`https://api.optimex.xyz/v1/tokens/${toToken.token_symbol}`)
116
+ .then(res => res.json())
117
+ .then(res => res?.data?.current_price || 0),
118
+ ])
119
+
120
+ let txData: { deposit_address: string; payload?: string; trade_id: string } | null = null
121
+
122
+ if (params.sender && params.recipient && (isFromBtc ? params.publicKey : true)) {
123
+ const tradeTimeout = new Date()
124
+ tradeTimeout.setHours(tradeTimeout.getHours() + 2)
125
+
126
+ const scriptTimeout = new Date()
127
+ scriptTimeout.setHours(scriptTimeout.getHours() + 24)
128
+
129
+ const res = await fetch(`${OPTIMEX_API}/trades/initiate`, {
130
+ method: 'POST',
131
+ headers: {
132
+ 'Content-Type': 'application/json',
133
+ },
134
+ body: JSON.stringify({
135
+ session_id: quoteRes.data.session_id,
136
+ from_user_address: params.sender,
137
+ amount_in: params.amount,
138
+ min_amount_out: (
139
+ (BigInt(quoteRes.data.best_quote_after_fees) * (10_000n - BigInt(params.slippage))) /
140
+ 10_000n
141
+ ).toString(),
142
+ to_user_address: params.recipient,
143
+ user_refund_pubkey: params.fromChain === NonEvmChain.Bitcoin ? params.publicKey : params.sender,
144
+ user_refund_address: params.sender,
145
+ creator_public_key: params.fromChain === NonEvmChain.Bitcoin ? params.publicKey : params.sender,
146
+ from_wallet_address: params.sender,
147
+ trade_timeout: Math.floor(tradeTimeout.getTime() / 1000),
148
+ script_timeout: Math.floor(scriptTimeout.getTime() / 1000),
149
+ affiliate_info: [
150
+ {
151
+ provider: 'KyberSwap',
152
+ rate: params.feeBps.toString(),
153
+ receiver: CROSS_CHAIN_FEE_RECEIVER,
154
+ network: 'ethereum',
155
+ },
156
+ ],
157
+ }),
158
+ }).then(res => res.json())
159
+
160
+ if (res.data.deposit_address) {
161
+ txData = res.data
162
+ }
163
+ }
164
+
165
+ const formattedOutputAmount = formatUnits(BigInt(quoteRes.data.best_quote_after_fees), params.toToken.decimals)
166
+ const formattedInputAmount = formatUnits(BigInt(params.amount), params.fromToken.decimals)
167
+ const inputUsd = token0Usd * +formattedInputAmount
168
+ const outputUsd = token1Usd * +formattedOutputAmount
169
+
170
+ return {
171
+ quoteParams: params,
172
+ outputAmount: BigInt(quoteRes.data.best_quote_after_fees),
173
+ formattedOutputAmount,
174
+ inputUsd,
175
+ outputUsd,
176
+ priceImpact: !inputUsd || !outputUsd ? NaN : ((inputUsd - outputUsd) * 100) / inputUsd,
177
+ rate: +formattedOutputAmount / +formattedInputAmount,
178
+ gasFeeUsd: 0,
179
+ timeEstimate: estimateRes.data.estimated_time,
180
+ contractAddress: txData?.deposit_address || ZERO_ADDRESS,
181
+ rawQuote: { ...quoteRes.data, txData },
182
+
183
+ protocolFee: 0,
184
+ platformFeePercent: (params.feeBps * 100) / 10000,
185
+ }
186
+ }
187
+
188
+ async executeSwap(
189
+ { quote }: Quote,
190
+ walletClient: WalletClient,
191
+ _nearWallet: any,
192
+ sendBtcFn?: (params: { recipient: string; amount: string | number }) => Promise<string>,
193
+ ): Promise<NormalizedTxResponse> {
194
+ const params = {
195
+ sender: quote.quoteParams.sender,
196
+ id: quote.rawQuote.txData.trade_id,
197
+ adapter: this.getName(),
198
+ sourceChain: quote.quoteParams.fromChain,
199
+ targetChain: quote.quoteParams.toChain,
200
+ inputAmount: quote.quoteParams.amount,
201
+ outputAmount: quote.outputAmount.toString(),
202
+ sourceToken: quote.quoteParams.fromToken,
203
+ targetToken: quote.quoteParams.toToken,
204
+ timestamp: new Date().getTime(),
205
+ }
206
+ if (quote.quoteParams.fromChain === NonEvmChain.Bitcoin) {
207
+ if (!sendBtcFn) throw new Error('sendBtcFn is not defined')
208
+ const res = await sendBtcFn({
209
+ recipient: quote.rawQuote.txData.deposit_address,
210
+ amount: quote.quoteParams.amount,
211
+ }).catch(e => {
212
+ throw e
213
+ })
214
+ await fetch(`${OPTIMEX_API}/trades/${quote.rawQuote.txData.trade_id}/submit-tx`, {
215
+ method: 'POST',
216
+ headers: {
217
+ 'Content-Type': 'application/json',
218
+ },
219
+ body: JSON.stringify({
220
+ tx_id: res,
221
+ }),
222
+ }).catch(e => {
223
+ console.log('submit tx error for optimex', e)
224
+ })
225
+ return {
226
+ ...params,
227
+ sourceTxHash: res,
228
+ }
229
+ }
230
+
231
+ if (!walletClient || !walletClient.account) throw new Error('Not connected')
232
+
233
+ const account = walletClient.account?.address as `0x${string}`
234
+ const hash = await walletClient.sendTransaction({
235
+ to: quote.rawQuote.txData.deposit_address,
236
+ value: (quote.quoteParams.fromToken as any).isNative ? BigInt(quote.quoteParams.amount) : undefined,
237
+ data: quote.rawQuote.txData.payload,
238
+ chain: undefined,
239
+ account,
240
+ })
241
+
242
+ await fetch(`${OPTIMEX_API}/trades/${quote.rawQuote.txData.trade_id}/submit-tx`, {
243
+ method: 'POST',
244
+ headers: {
245
+ 'Content-Type': 'application/json',
246
+ },
247
+ body: JSON.stringify({
248
+ tx_id: hash,
249
+ }),
250
+ }).catch(e => {
251
+ console.log('submit tx error for optimex', e)
252
+ })
253
+ return {
254
+ ...params,
255
+ sourceTxHash: hash,
256
+ }
257
+ }
258
+
259
+ async getTransactionStatus(p: NormalizedTxResponse): Promise<SwapStatus> {
260
+ const res = await fetch(`${OPTIMEX_API}/trades/${p.id}`).then(res => res.json())
261
+
262
+ return {
263
+ txHash: res.data?.payment_bundle?.settlement_tx || '',
264
+ status: ['Done', 'PaymentConfirmed'].includes(res?.data?.state)
265
+ ? 'Success'
266
+ : ['Aborted', 'ToBeAborted', 'Failed', 'Failure', 'UserCancelled'].includes(res?.data?.state)
267
+ ? 'Failed'
268
+ : res?.data?.state === 'Refunded'
269
+ ? 'Refunded'
270
+ : 'Processing',
271
+ }
272
+ }
273
+ }
@@ -0,0 +1,270 @@
1
+
2
+ import { Currency } from '../constants/index.js'
3
+ import { useWalletSelector } from '@near-wallet-selector/react-hook'
4
+ import { WalletAdapterProps } from '@solana/wallet-adapter-base'
5
+ import { Connection, Transaction, VersionedTransaction } from '@solana/web3.js'
6
+ import { getPublicClient } from '@wagmi/core'
7
+ import { WalletClient, formatUnits } from 'viem'
8
+ import { useConfig } from 'wagmi'
9
+ import { CROSS_CHAIN_FEE_RECEIVER, CROSS_CHAIN_FEE_RECEIVER_SOLANA, ZERO_ADDRESS } from '../constants/index.js'
10
+ import { MAINNET_NETWORKS } from '../constants/index.js'
11
+ import { SolanaToken } from '../constants/index.js'
12
+
13
+ import { Quote } from '../registry.js'
14
+ import {
15
+ BaseSwapAdapter,
16
+ Chain,
17
+ NOT_SUPPORTED_CHAINS_PRICE_SERVICE,
18
+ NormalizedQuote,
19
+ NormalizedTxResponse,
20
+ QuoteParams,
21
+ SwapStatus,
22
+ } from './BaseSwapAdapter.js'
23
+
24
+ interface Step {
25
+ action: string
26
+ tx: {
27
+ data: string
28
+ to: string
29
+ value: string
30
+ }
31
+ }
32
+
33
+ export class OrbiterAdapter extends BaseSwapAdapter {
34
+ constructor() {
35
+ super()
36
+ }
37
+
38
+ getName(): string {
39
+ return 'Orbiter'
40
+ }
41
+ getIcon(): string {
42
+ return 'https://www.orbiter.finance/favicon.ico'
43
+ }
44
+ getSupportedChains(): Chain[] {
45
+ return [...MAINNET_NETWORKS]
46
+ }
47
+
48
+ getSupportedTokens(_sourceChain: Chain, _destChain: Chain): Currency[] {
49
+ return []
50
+ }
51
+
52
+ async getQuote(params: QuoteParams): Promise<NormalizedQuote> {
53
+ const fromToken = params.fromToken as any
54
+ const toToken = params.toToken as any
55
+ const body = {
56
+ sourceChainId: params.fromChain === 'solana' ? 'SOLANA_MAIN' : params.fromChain.toString(),
57
+ destChainId: params.toChain === 'solana' ? 'SOLANA_MAIN' : params.toChain.toString(),
58
+ sourceToken:
59
+ params.fromChain === 'solana'
60
+ ? (params.fromToken as SolanaToken).id
61
+ : fromToken.isNative
62
+ ? ZERO_ADDRESS
63
+ : fromToken.address,
64
+
65
+ destToken:
66
+ params.toChain === 'solana'
67
+ ? (params.toToken as SolanaToken).id
68
+ : toToken.isNative
69
+ ? ZERO_ADDRESS
70
+ : toToken.address,
71
+ amount: params.amount.toString(),
72
+ userAddress: params.sender,
73
+ targetRecipient: params.recipient,
74
+ slippage: params.slippage / 10_000,
75
+ feeConfig: {
76
+ feeRecipient: params.fromChain === 'solana' ? CROSS_CHAIN_FEE_RECEIVER_SOLANA : CROSS_CHAIN_FEE_RECEIVER,
77
+ feePercent: (params.feeBps / 10000).toString(),
78
+ },
79
+ channel: 'kyberswap',
80
+ }
81
+ const res = await fetch(`https://api.orbiter.finance/quote`, {
82
+ method: 'POST',
83
+ headers: {
84
+ 'Content-Type': 'application/json',
85
+ },
86
+ body: JSON.stringify(body),
87
+ })
88
+ .then(res => res.json())
89
+ .then(res => res.result)
90
+
91
+ const formattedOutputAmount = formatUnits(BigInt(res.details?.destTokenAmount || '0'), params.toToken.decimals)
92
+ const formattedInputAmount = formatUnits(BigInt(params.amount), params.fromToken.decimals)
93
+
94
+ const inputUsd = NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.fromChain)
95
+ ? Number(res.details?.sourceAmountUSD || 0)
96
+ : params.tokenInUsd * +formattedInputAmount
97
+ const outputUsd = NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.toChain)
98
+ ? Number(res.details?.destAmountUSD || 0)
99
+ : params.tokenOutUsd * +formattedOutputAmount
100
+
101
+ const haveApproval = res.steps.some((step: Step) => step.action === 'approve')
102
+ const approvalContract = res.steps.find((step: Step) => step.action === 'swap' || step.action === 'bridge')
103
+
104
+ return {
105
+ quoteParams: params,
106
+ outputAmount: BigInt(res.details?.destTokenAmount || '0'),
107
+ formattedOutputAmount,
108
+ inputUsd,
109
+ outputUsd,
110
+ priceImpact: !inputUsd || !outputUsd ? NaN : ((inputUsd - outputUsd) * 100) / inputUsd,
111
+ //rate: Number(resp.details?.rate || 0),
112
+ rate: +formattedOutputAmount / +formattedInputAmount,
113
+ gasFeeUsd: 0,
114
+ timeEstimate: 10,
115
+ contractAddress: haveApproval ? approvalContract?.tx.to || ZERO_ADDRESS : ZERO_ADDRESS,
116
+ rawQuote: res,
117
+ protocolFee: 0,
118
+ platformFeePercent: (params.feeBps * 100) / 10000,
119
+ }
120
+ }
121
+
122
+ async executeSwap(
123
+ { quote }: Quote,
124
+ walletClient: WalletClient,
125
+ _nearWallet?: ReturnType<typeof useWalletSelector>,
126
+ _sendBtcFn?: (params: { recipient: string; amount: string | number }) => Promise<string>,
127
+ sendSolanaFn?: WalletAdapterProps['sendTransaction'],
128
+ solanaConnection?: Connection,
129
+ ): Promise<NormalizedTxResponse> {
130
+ if (quote.quoteParams.fromChain === 'solana') {
131
+ if (!solanaConnection || !sendSolanaFn) throw new Error('Connection is not defined for Solana swap')
132
+ const encodedData = quote.rawQuote.steps?.[0]?.tx?.data
133
+ const txBuffer = Buffer.from(encodedData, 'base64')
134
+
135
+ // Try to deserialize as VersionedTransaction first
136
+ let transaction
137
+ try {
138
+ transaction = VersionedTransaction.deserialize(txBuffer)
139
+ console.log('Parsed as VersionedTransaction')
140
+ } catch (versionedError) {
141
+ console.log('Failed to parse as VersionedTransaction, trying legacy Transaction')
142
+ try {
143
+ transaction = Transaction.from(txBuffer)
144
+ console.log('Parsed as legacy Transaction')
145
+ } catch (legacyError) {
146
+ throw new Error('Could not parse transaction as either VersionedTransaction or legacy Transaction')
147
+ }
148
+ }
149
+
150
+ console.log('Transaction parsed successfully:', transaction)
151
+
152
+ const waitForConfirmation = async (txId: string) => {
153
+ try {
154
+ const latestBlockhash = await solanaConnection.getLatestBlockhash()
155
+
156
+ // Wait for confirmation with timeout
157
+ const confirmation = await Promise.race([
158
+ solanaConnection.confirmTransaction(
159
+ {
160
+ signature: txId,
161
+ blockhash: latestBlockhash.blockhash,
162
+ lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
163
+ },
164
+ 'confirmed',
165
+ ),
166
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Transaction confirmation timeout')), 60000)),
167
+ ])
168
+
169
+ const confirmationResult = confirmation as { value: { err: any } }
170
+ if (confirmationResult.value.err) {
171
+ throw new Error(`Transaction failed: ${JSON.stringify(confirmationResult.value.err)}`)
172
+ }
173
+
174
+ console.log('Transaction confirmed successfully!')
175
+ } catch (confirmError) {
176
+ console.error('Transaction confirmation failed:', confirmError)
177
+
178
+ // Check if transaction actually succeeded despite timeout
179
+ const txStatus = await solanaConnection.getSignatureStatus(txId)
180
+ if (txStatus?.value?.confirmationStatus !== 'confirmed') {
181
+ throw new Error(`Transaction was not confirmed: ${confirmError.message}`)
182
+ }
183
+ }
184
+ }
185
+
186
+ // Send through wallet adapter
187
+ const signature = await sendSolanaFn(transaction, solanaConnection)
188
+ await waitForConfirmation(signature)
189
+
190
+ return {
191
+ sender: quote.quoteParams.sender,
192
+ id: signature,
193
+ sourceTxHash: signature,
194
+ adapter: this.getName(),
195
+ sourceChain: quote.quoteParams.fromChain,
196
+ targetChain: quote.quoteParams.toChain,
197
+ inputAmount: quote.quoteParams.amount,
198
+ outputAmount: quote.outputAmount.toString(),
199
+ sourceToken: quote.quoteParams.fromToken,
200
+ targetToken: quote.quoteParams.toToken,
201
+ timestamp: new Date().getTime(),
202
+ }
203
+ }
204
+
205
+ const steps = quote.rawQuote.steps.filter((st: Step) => st.action !== 'approve') // already approve before
206
+
207
+ const account = walletClient.account?.address
208
+ if (!account) throw new Error('WalletClient account is not defined')
209
+ const txs = await Promise.all(
210
+ steps.map(async (step: Step) => {
211
+ const tx = await walletClient.sendTransaction({
212
+ chain: undefined,
213
+ account,
214
+ to: step.tx.to as `0x${string}`,
215
+ value: BigInt(step.tx.value),
216
+ data: step.tx.data as `0x${string}`,
217
+ })
218
+ return tx
219
+ }),
220
+ )
221
+
222
+ if (!txs || txs.length === 0) throw new Error('No transactions found after executing swap steps')
223
+
224
+ return {
225
+ sender: quote.quoteParams.sender,
226
+ sourceTxHash: txs[txs.length - 1],
227
+ adapter: this.getName(),
228
+ id: txs[txs.length - 1],
229
+ sourceChain: quote.quoteParams.fromChain,
230
+ targetChain: quote.quoteParams.toChain,
231
+ inputAmount: quote.quoteParams.amount,
232
+ outputAmount: quote.outputAmount.toString(),
233
+ sourceToken: quote.quoteParams.fromToken,
234
+ targetToken: quote.quoteParams.toToken,
235
+ timestamp: new Date().getTime(),
236
+ }
237
+ }
238
+
239
+ async getTransactionStatus(p: NormalizedTxResponse): Promise<SwapStatus> {
240
+ if (p.sourceChain !== 'solana') {
241
+ const publicClient = getPublicClient(useConfig(), {
242
+ chainId: p.sourceChain as any,
243
+ })
244
+ const receipt = await publicClient?.getTransactionReceipt({
245
+ hash: p.id as `0x${string}`,
246
+ })
247
+ if (receipt.status === 'reverted') {
248
+ return {
249
+ txHash: '',
250
+ status: 'Failed',
251
+ }
252
+ }
253
+ }
254
+
255
+ const res = await fetch(`https://api.orbiter.finance/transaction/${p.id}`).then(r => r.json())
256
+ return {
257
+ txHash: res.result.targetId || '',
258
+ // this is from orbiter source code, their docs dont have info for this
259
+ // https://github.com/Orbiter-Finance/OrbiterFE-V2/blob/2b35399aad581e666c45a829e0151485f4007c93/src/views/statistics/LatestTransactions.vue#L115
260
+ status:
261
+ res.result.opStatus === -1
262
+ ? 'Failed'
263
+ : res.result.opStatus === 80
264
+ ? 'Refunded'
265
+ : res.result.opStatus !== 98 && res.result.opStatus !== 99
266
+ ? 'Processing'
267
+ : 'Success',
268
+ }
269
+ }
270
+ }