@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
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import { OneClickService, OpenAPI, QuoteRequest } from '@defuse-protocol/one-click-sdk-typescript'
|
|
2
|
+
import { ChainId } from '@openocean.finance/widget-sdk'
|
|
3
|
+
import { Currency, SolanaToken } from '../constants/index.js'
|
|
4
|
+
import { useWalletSelector } from '@near-wallet-selector/react-hook'
|
|
5
|
+
import {
|
|
6
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
7
|
+
TOKEN_PROGRAM_ID,
|
|
8
|
+
createAssociatedTokenAccountInstruction,
|
|
9
|
+
createTransferInstruction,
|
|
10
|
+
getAccount,
|
|
11
|
+
getAssociatedTokenAddress,
|
|
12
|
+
} from '@solana/spl-token'
|
|
13
|
+
import { WalletAdapterProps } from '@solana/wallet-adapter-base'
|
|
14
|
+
import { Connection, PublicKey, SystemProgram, Transaction } from '@solana/web3.js'
|
|
15
|
+
import { WalletClient, formatUnits } from 'viem'
|
|
16
|
+
|
|
17
|
+
import { BTC_DEFAULT_RECEIVER, CROSS_CHAIN_FEE_RECEIVER, SOLANA_NATIVE, ZERO_ADDRESS } from '../constants/index.js'
|
|
18
|
+
|
|
19
|
+
import { Quote } from '../registry.js'
|
|
20
|
+
import {
|
|
21
|
+
BaseSwapAdapter,
|
|
22
|
+
Chain,
|
|
23
|
+
NearQuoteParams,
|
|
24
|
+
NonEvmChain,
|
|
25
|
+
NormalizedQuote,
|
|
26
|
+
NormalizedTxResponse,
|
|
27
|
+
SwapStatus,
|
|
28
|
+
} from './BaseSwapAdapter.js'
|
|
29
|
+
|
|
30
|
+
export const MappingChainIdToBlockChain: Record<string, string> = {
|
|
31
|
+
[NonEvmChain.Bitcoin]: 'btc',
|
|
32
|
+
[NonEvmChain.Solana]: 'sol',
|
|
33
|
+
[ChainId.MAINNET]: 'eth',
|
|
34
|
+
[ChainId.ARBITRUM]: 'arb',
|
|
35
|
+
[ChainId.BSCMAINNET]: 'bsc',
|
|
36
|
+
[ChainId.BERA]: 'bera',
|
|
37
|
+
[ChainId.MATIC]: 'pol',
|
|
38
|
+
[ChainId.BASE]: 'base',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const erc20Abi = [
|
|
42
|
+
{
|
|
43
|
+
inputs: [
|
|
44
|
+
{ type: 'address', name: 'recipient' },
|
|
45
|
+
{ type: 'uint256', name: 'amount' },
|
|
46
|
+
],
|
|
47
|
+
name: 'transfer',
|
|
48
|
+
outputs: [{ type: 'bool', name: '' }],
|
|
49
|
+
stateMutability: 'nonpayable',
|
|
50
|
+
type: 'function',
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
export class NearIntentsAdapter extends BaseSwapAdapter {
|
|
55
|
+
constructor() {
|
|
56
|
+
super()
|
|
57
|
+
// Initialize the API client
|
|
58
|
+
OpenAPI.BASE = 'https://1click.chaindefuser.com'
|
|
59
|
+
OpenAPI.TOKEN =
|
|
60
|
+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjUtMDQtMjMtdjEifQ.eyJ2IjoxLCJrZXlfdHlwZSI6ImRpc3RyaWJ1dGlvbl9jaGFubmVsIiwicGFydG5lcl9pZCI6Imt5YmVyIiwiaWF0IjoxNzQ5MDQyNDk1LCJleHAiOjE3ODA1Nzg0OTV9.sC5g1Jn4BRIGXkIRmN4dnK2BzbIglLOVuOmnrTItGaAP-QU69lbyYs2QGPE-5c7dRC9Cc3s0ktO50W9VXiqQEefu-VCQTKtjsfIwfAm7wDC1XKUT7lbQL2uODqXxR6yg5d8ENu6p8F2t86_T8IEpid6b1yBidKladbs9tI2QebSp3Sn6bjtsnpD-9W2dsW0Gd6PUkpZizb--YqkmdPQ8Eu85fIxtDO64qbp0Xp6NY8caFEA1yakbwaMEUWXnNX6PB_elfH28sF0cMbqlyAGiHe98J8tZ47kga6e6yZP4UHoak3Y_eRNuX_CpwoXfULx1t8YLoSJEQuP9JsPIoyw5dA'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getName(): string {
|
|
64
|
+
return 'Near Intents'
|
|
65
|
+
}
|
|
66
|
+
getIcon(): string {
|
|
67
|
+
return 'https://storage.googleapis.com/ks-setting-1d682dca/000c677f-2ebc-44cc-8d76-e4c6d07627631744962669170.png'
|
|
68
|
+
}
|
|
69
|
+
getSupportedChains(): Chain[] {
|
|
70
|
+
return [
|
|
71
|
+
NonEvmChain.Solana,
|
|
72
|
+
NonEvmChain.Bitcoin,
|
|
73
|
+
NonEvmChain.Near,
|
|
74
|
+
...Object.keys(MappingChainIdToBlockChain).map(Number),
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getSupportedTokens(_sourceChain: Chain, _destChain: Chain): Currency[] {
|
|
79
|
+
return []
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getQuote(params: NearQuoteParams): Promise<NormalizedQuote> {
|
|
83
|
+
const deadline = new Date()
|
|
84
|
+
|
|
85
|
+
// 1 hour for Bitcoin, 20 minutes for other chains
|
|
86
|
+
deadline.setSeconds(deadline.getSeconds() + (params.fromChain === NonEvmChain.Bitcoin ? 60 * 60 : 60 * 20))
|
|
87
|
+
|
|
88
|
+
const fromAssetId =
|
|
89
|
+
'assetId' in params.fromToken
|
|
90
|
+
? params.fromToken.assetId === 'near'
|
|
91
|
+
? 'nep141:wrap.near'
|
|
92
|
+
: params.fromToken.assetId
|
|
93
|
+
: params.nearTokens.find(token => {
|
|
94
|
+
const blockchain = MappingChainIdToBlockChain[params.fromChain as ChainId]
|
|
95
|
+
|
|
96
|
+
if (params.fromChain === 'solana')
|
|
97
|
+
return (params.fromToken as SolanaToken).id === SOLANA_NATIVE
|
|
98
|
+
? token.symbol === 'SOL' && token.blockchain === 'sol'
|
|
99
|
+
: token.blockchain === blockchain && token.contractAddress === (params.fromToken as any).id
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
token.blockchain === blockchain &&
|
|
103
|
+
((params.fromToken as any).isNative
|
|
104
|
+
? token.symbol.toLowerCase() === params.fromToken.symbol?.toLowerCase() &&
|
|
105
|
+
token.assetId.includes('omft')
|
|
106
|
+
: token.contractAddress?.toLowerCase() === (params.fromToken as any).wrapped?.address.toLowerCase())
|
|
107
|
+
)
|
|
108
|
+
})?.assetId
|
|
109
|
+
|
|
110
|
+
const toAssetId =
|
|
111
|
+
'assetId' in params.toToken
|
|
112
|
+
? params.toToken.assetId === 'near'
|
|
113
|
+
? 'nep141:wrap.near'
|
|
114
|
+
: params.toToken.assetId
|
|
115
|
+
: params.nearTokens.find(token => {
|
|
116
|
+
const blockchain = MappingChainIdToBlockChain[params.toChain as ChainId]
|
|
117
|
+
if (params.toChain === 'solana')
|
|
118
|
+
return (params.toToken as SolanaToken).id === SOLANA_NATIVE
|
|
119
|
+
? token.symbol === 'SOL' && token.blockchain === 'sol'
|
|
120
|
+
: token.blockchain === blockchain && token.contractAddress === (params.toToken as any).id
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
token.blockchain === blockchain &&
|
|
124
|
+
((params.toToken as any).isNative
|
|
125
|
+
? token.symbol.toLowerCase() === params.toToken.symbol?.toLowerCase() && token.assetId.includes('omft')
|
|
126
|
+
: token.contractAddress?.toLowerCase() === (params.toToken as any).wrapped?.address.toLowerCase())
|
|
127
|
+
)
|
|
128
|
+
})?.assetId
|
|
129
|
+
|
|
130
|
+
if (!fromAssetId || !toAssetId) {
|
|
131
|
+
throw new Error('not supported tokens')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Create a quote request
|
|
135
|
+
const quoteRequest: QuoteRequest = {
|
|
136
|
+
dry: true,
|
|
137
|
+
deadline: deadline.toISOString(),
|
|
138
|
+
slippageTolerance: params.slippage,
|
|
139
|
+
swapType: QuoteRequest.swapType.EXACT_INPUT,
|
|
140
|
+
|
|
141
|
+
originAsset: fromAssetId,
|
|
142
|
+
depositType: QuoteRequest.depositType.ORIGIN_CHAIN,
|
|
143
|
+
|
|
144
|
+
destinationAsset: toAssetId,
|
|
145
|
+
amount: params.amount,
|
|
146
|
+
|
|
147
|
+
refundTo: params.sender,
|
|
148
|
+
refundType: QuoteRequest.refundType.ORIGIN_CHAIN,
|
|
149
|
+
referral: 'kyberswap',
|
|
150
|
+
|
|
151
|
+
recipient: params.recipient,
|
|
152
|
+
recipientType: QuoteRequest.recipientType.DESTINATION_CHAIN,
|
|
153
|
+
appFees: [
|
|
154
|
+
{
|
|
155
|
+
recipient: CROSS_CHAIN_FEE_RECEIVER.toLowerCase(),
|
|
156
|
+
fee: params.feeBps,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const quote = await OneClickService.getQuote(quoteRequest)
|
|
162
|
+
const formattedInputAmount = formatUnits(BigInt(params.amount), params.fromToken.decimals)
|
|
163
|
+
const formattedOutputAmount = formatUnits(BigInt(quote.quote.amountOut), params.toToken.decimals)
|
|
164
|
+
const inputUsd = +quote.quote.amountInUsd
|
|
165
|
+
const outputUsd = +quote.quote.amountOutUsd
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
quoteParams: params,
|
|
169
|
+
outputAmount: BigInt(quote.quote.amountOut),
|
|
170
|
+
formattedOutputAmount,
|
|
171
|
+
inputUsd: +quote.quote.amountInUsd,
|
|
172
|
+
outputUsd: +quote.quote.amountOutUsd,
|
|
173
|
+
priceImpact: !inputUsd || !outputUsd ? NaN : ((inputUsd - outputUsd) * 100) / inputUsd,
|
|
174
|
+
rate: +formattedOutputAmount / +formattedInputAmount,
|
|
175
|
+
gasFeeUsd: 0,
|
|
176
|
+
timeEstimate: quote.quote.timeEstimate || 0,
|
|
177
|
+
// Near intent dont need to approve, we send token to contract directly
|
|
178
|
+
contractAddress: ZERO_ADDRESS,
|
|
179
|
+
rawQuote: quote,
|
|
180
|
+
protocolFee: 0,
|
|
181
|
+
platformFeePercent: (params.feeBps * 100) / 10_000,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async executeSwap(
|
|
186
|
+
{ quote }: Quote,
|
|
187
|
+
walletClient: WalletClient,
|
|
188
|
+
nearWallet?: ReturnType<typeof useWalletSelector>,
|
|
189
|
+
sendBtcFn?: (params: { recipient: string; amount: string | number }) => Promise<string>,
|
|
190
|
+
sendSolanaFn?: WalletAdapterProps['sendTransaction'],
|
|
191
|
+
solanaConnection?: Connection,
|
|
192
|
+
): Promise<NormalizedTxResponse> {
|
|
193
|
+
const quoteParams = {
|
|
194
|
+
...quote.rawQuote.quoteRequest,
|
|
195
|
+
dry: false,
|
|
196
|
+
// adjust slippage to 0,01% to accept the rate change
|
|
197
|
+
slippageTolerance:
|
|
198
|
+
Math.floor(quote.quoteParams.slippage * 0.9) > 1
|
|
199
|
+
? Math.floor(quote.quoteParams.slippage * 0.9)
|
|
200
|
+
: quote.quoteParams.slippage,
|
|
201
|
+
}
|
|
202
|
+
delete quoteParams.correlationId
|
|
203
|
+
|
|
204
|
+
const refreshedQuote = await OneClickService.getQuote(quoteParams)
|
|
205
|
+
const depositAddress = refreshedQuote?.quote?.depositAddress
|
|
206
|
+
|
|
207
|
+
if (!depositAddress) {
|
|
208
|
+
throw new Error('Deposit address not found')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (
|
|
212
|
+
refreshedQuote.quoteRequest.recipient === ZERO_ADDRESS ||
|
|
213
|
+
refreshedQuote.quoteRequest.refundTo === ZERO_ADDRESS ||
|
|
214
|
+
refreshedQuote.quoteRequest.recipient.toLowerCase() === BTC_DEFAULT_RECEIVER ||
|
|
215
|
+
refreshedQuote.quoteRequest.refundTo.toLowerCase() === BTC_DEFAULT_RECEIVER
|
|
216
|
+
) {
|
|
217
|
+
throw new Error('Near Intent recipient or refundTo is ZERO ADDRESS')
|
|
218
|
+
}
|
|
219
|
+
if (BigInt(refreshedQuote.quote.minAmountOut) < BigInt(quote.rawQuote.quote.minAmountOut)) {
|
|
220
|
+
throw new Error('Quote amount out is less than expected')
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const params = {
|
|
224
|
+
sender: quote.quoteParams.sender,
|
|
225
|
+
id: depositAddress, // specific id for each provider
|
|
226
|
+
adapter: this.getName(),
|
|
227
|
+
sourceChain: quote.quoteParams.fromChain,
|
|
228
|
+
targetChain: quote.quoteParams.toChain,
|
|
229
|
+
inputAmount: quote.quoteParams.amount,
|
|
230
|
+
outputAmount: quote.outputAmount.toString(),
|
|
231
|
+
sourceToken: quote.quoteParams.fromToken,
|
|
232
|
+
targetToken: quote.quoteParams.toToken,
|
|
233
|
+
timestamp: new Date().getTime(),
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (quote.quoteParams.fromChain === NonEvmChain.Solana) {
|
|
237
|
+
return new Promise<NormalizedTxResponse>(async (resolve, reject) => {
|
|
238
|
+
if (!sendSolanaFn || !solanaConnection) {
|
|
239
|
+
reject('Not connected')
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
const waitForConfirmation = async (txId: string) => {
|
|
243
|
+
try {
|
|
244
|
+
const latestBlockhash = await solanaConnection.getLatestBlockhash()
|
|
245
|
+
|
|
246
|
+
// Wait for confirmation with timeout
|
|
247
|
+
const confirmation = await Promise.race([
|
|
248
|
+
solanaConnection.confirmTransaction(
|
|
249
|
+
{
|
|
250
|
+
signature: txId,
|
|
251
|
+
blockhash: latestBlockhash.blockhash,
|
|
252
|
+
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
|
253
|
+
},
|
|
254
|
+
'confirmed',
|
|
255
|
+
),
|
|
256
|
+
new Promise((_, reject) =>
|
|
257
|
+
setTimeout(() => reject(new Error('Transaction confirmation timeout')), 60000),
|
|
258
|
+
),
|
|
259
|
+
])
|
|
260
|
+
|
|
261
|
+
const confirmationResult = confirmation as { value: { err: any } }
|
|
262
|
+
if (confirmationResult.value.err) {
|
|
263
|
+
throw new Error(`Transaction failed: ${JSON.stringify(confirmationResult.value.err)}`)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
console.log('Transaction confirmed successfully!')
|
|
267
|
+
} catch (confirmError) {
|
|
268
|
+
console.error('Transaction confirmation failed:', confirmError)
|
|
269
|
+
|
|
270
|
+
// Check if transaction actually succeeded despite timeout
|
|
271
|
+
const txStatus = await solanaConnection.getSignatureStatus(txId)
|
|
272
|
+
if (txStatus?.value?.confirmationStatus !== 'confirmed') {
|
|
273
|
+
throw new Error(`Transaction was not confirmed: ${confirmError.message}`)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const fromPubkey = new PublicKey(quote.quoteParams.sender)
|
|
279
|
+
const recipientPubkey = new PublicKey(depositAddress)
|
|
280
|
+
|
|
281
|
+
const fromToken = quote.quoteParams.fromToken as SolanaToken
|
|
282
|
+
|
|
283
|
+
// const latestBlockhash = await solanaConnection.getLatestBlockhash('confirmed')
|
|
284
|
+
|
|
285
|
+
if (fromToken.id === SOLANA_NATIVE) {
|
|
286
|
+
const transaction = new Transaction().add(
|
|
287
|
+
SystemProgram.transfer({
|
|
288
|
+
fromPubkey: fromPubkey,
|
|
289
|
+
toPubkey: recipientPubkey,
|
|
290
|
+
lamports: BigInt(quote.quoteParams.amount),
|
|
291
|
+
}),
|
|
292
|
+
)
|
|
293
|
+
try {
|
|
294
|
+
const signature = await sendSolanaFn(transaction, solanaConnection)
|
|
295
|
+
await waitForConfirmation(signature)
|
|
296
|
+
|
|
297
|
+
resolve({
|
|
298
|
+
...params,
|
|
299
|
+
sourceTxHash: signature,
|
|
300
|
+
})
|
|
301
|
+
} catch (error) {
|
|
302
|
+
reject(error)
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
const mintPubkey = new PublicKey(fromToken.id)
|
|
306
|
+
// Get associated token addresses
|
|
307
|
+
const senderTokenAddress = await getAssociatedTokenAddress(
|
|
308
|
+
mintPubkey,
|
|
309
|
+
fromPubkey,
|
|
310
|
+
false,
|
|
311
|
+
TOKEN_PROGRAM_ID,
|
|
312
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
313
|
+
)
|
|
314
|
+
const recipientTokenAddress = await getAssociatedTokenAddress(
|
|
315
|
+
mintPubkey,
|
|
316
|
+
recipientPubkey,
|
|
317
|
+
false,
|
|
318
|
+
TOKEN_PROGRAM_ID,
|
|
319
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
320
|
+
)
|
|
321
|
+
const transaction = new Transaction()
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
// Check if recipient's token account exists
|
|
325
|
+
await getAccount(solanaConnection, recipientTokenAddress)
|
|
326
|
+
} catch (err) {
|
|
327
|
+
// Account doesn't exist, create it
|
|
328
|
+
console.log('Creating recipient token account...')
|
|
329
|
+
transaction.add(
|
|
330
|
+
createAssociatedTokenAccountInstruction(
|
|
331
|
+
fromPubkey, // payer
|
|
332
|
+
recipientTokenAddress, // associated token account
|
|
333
|
+
recipientPubkey, // owner
|
|
334
|
+
mintPubkey, // mint
|
|
335
|
+
TOKEN_PROGRAM_ID,
|
|
336
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
337
|
+
),
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Add transfer instruction
|
|
342
|
+
transaction.add(
|
|
343
|
+
createTransferInstruction(
|
|
344
|
+
senderTokenAddress, // source
|
|
345
|
+
recipientTokenAddress, // destination
|
|
346
|
+
fromPubkey, // owner
|
|
347
|
+
BigInt(quote.quoteParams.amount),
|
|
348
|
+
[],
|
|
349
|
+
TOKEN_PROGRAM_ID,
|
|
350
|
+
),
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const signature = await sendSolanaFn(transaction, solanaConnection)
|
|
355
|
+
await waitForConfirmation(signature)
|
|
356
|
+
|
|
357
|
+
resolve({
|
|
358
|
+
...params,
|
|
359
|
+
sourceTxHash: signature,
|
|
360
|
+
})
|
|
361
|
+
} catch (error) {
|
|
362
|
+
reject(error)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (quote.quoteParams.fromChain === NonEvmChain.Bitcoin) {
|
|
370
|
+
return new Promise<NormalizedTxResponse>(async (resolve, reject) => {
|
|
371
|
+
if (!sendBtcFn) {
|
|
372
|
+
reject('Not connected')
|
|
373
|
+
return
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
const tx = await sendBtcFn({
|
|
378
|
+
recipient: depositAddress,
|
|
379
|
+
amount: quote.quoteParams.amount,
|
|
380
|
+
})
|
|
381
|
+
await OneClickService.submitDepositTx({
|
|
382
|
+
txHash: tx,
|
|
383
|
+
depositAddress,
|
|
384
|
+
}).catch(e => {
|
|
385
|
+
console.log('NearIntents submitDepositTx failed', e)
|
|
386
|
+
})
|
|
387
|
+
resolve({
|
|
388
|
+
...params,
|
|
389
|
+
sourceTxHash: tx,
|
|
390
|
+
})
|
|
391
|
+
} catch (e) {
|
|
392
|
+
console.log(e)
|
|
393
|
+
reject(e)
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (quote.quoteParams.fromChain === NonEvmChain.Near) {
|
|
400
|
+
return new Promise<NormalizedTxResponse>(async (resolve, reject) => {
|
|
401
|
+
if (!nearWallet || !nearWallet.signedAccountId) {
|
|
402
|
+
reject('Not connected')
|
|
403
|
+
return
|
|
404
|
+
}
|
|
405
|
+
const isNative = (quote.quoteParams.fromToken as any).assetId === 'near'
|
|
406
|
+
|
|
407
|
+
const transactions: any = []
|
|
408
|
+
if (!isNative)
|
|
409
|
+
transactions.push({
|
|
410
|
+
signerId: nearWallet.signedAccountId,
|
|
411
|
+
receiverId: (quote.quoteParams.fromToken as any).contractAddress,
|
|
412
|
+
actions: [
|
|
413
|
+
{
|
|
414
|
+
type: 'FunctionCall',
|
|
415
|
+
params: {
|
|
416
|
+
methodName: 'storage_deposit',
|
|
417
|
+
args: { account_id: depositAddress, registration_only: true },
|
|
418
|
+
gas: '30000000000000',
|
|
419
|
+
deposit: '1250000000000000000000', // 0.00125 NEAR
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
transactions.push({
|
|
426
|
+
signerId: nearWallet.signedAccountId,
|
|
427
|
+
receiverId: isNative ? depositAddress : (quote.quoteParams.fromToken as any).contractAddress,
|
|
428
|
+
actions: [
|
|
429
|
+
isNative
|
|
430
|
+
? {
|
|
431
|
+
type: 'Transfer',
|
|
432
|
+
params: {
|
|
433
|
+
deposit: quote.quoteParams.amount,
|
|
434
|
+
},
|
|
435
|
+
}
|
|
436
|
+
: {
|
|
437
|
+
type: 'FunctionCall',
|
|
438
|
+
params: {
|
|
439
|
+
methodName: 'ft_transfer',
|
|
440
|
+
args: {
|
|
441
|
+
receiver_id: depositAddress,
|
|
442
|
+
amount: quote.quoteParams.amount,
|
|
443
|
+
},
|
|
444
|
+
gas: '30000000000000',
|
|
445
|
+
deposit: '1',
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
// My near wallet is redirect to wallet website -> need store to process later
|
|
452
|
+
if (nearWallet?.wallet?.id === 'my-near-wallet')
|
|
453
|
+
localStorage.setItem(
|
|
454
|
+
'cross-chain-swap-my-near-wallet-tx',
|
|
455
|
+
JSON.stringify({
|
|
456
|
+
...params,
|
|
457
|
+
sourceTxHash: depositAddress,
|
|
458
|
+
}),
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
await nearWallet
|
|
462
|
+
.signAndSendTransactions({
|
|
463
|
+
transactions,
|
|
464
|
+
})
|
|
465
|
+
.catch(e => {
|
|
466
|
+
console.log('NearIntents signAndSendTransactions failed', e)
|
|
467
|
+
if (nearWallet?.wallet?.id === 'my-near-wallet') reject()
|
|
468
|
+
else reject(e)
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
resolve({
|
|
472
|
+
...params,
|
|
473
|
+
sourceTxHash: depositAddress,
|
|
474
|
+
})
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return new Promise<NormalizedTxResponse>(async (resolve, reject) => {
|
|
479
|
+
try {
|
|
480
|
+
if (!walletClient || !walletClient.account) reject('Not connected')
|
|
481
|
+
if (quote.quoteParams.sender === ZERO_ADDRESS || quote.quoteParams.recipient === ZERO_ADDRESS) {
|
|
482
|
+
reject('Near Intent refundTo or recipient is ZERO ADDRESS')
|
|
483
|
+
return
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const account = walletClient.account?.address as `0x${string}`
|
|
487
|
+
|
|
488
|
+
const fromToken = quote.quoteParams.fromToken
|
|
489
|
+
|
|
490
|
+
const hash = await ((fromToken as any).isNative
|
|
491
|
+
? walletClient.sendTransaction({
|
|
492
|
+
to: depositAddress as `0x${string}`,
|
|
493
|
+
value: BigInt(quote.quoteParams.amount),
|
|
494
|
+
chain: undefined,
|
|
495
|
+
account,
|
|
496
|
+
})
|
|
497
|
+
: walletClient.writeContract({
|
|
498
|
+
address: ('contractAddress' in fromToken
|
|
499
|
+
? fromToken.contractAddress
|
|
500
|
+
: (fromToken as any).wrapped.address) as `0x${string}`,
|
|
501
|
+
abi: erc20Abi,
|
|
502
|
+
functionName: 'transfer',
|
|
503
|
+
args: [depositAddress, quote.quoteParams.amount],
|
|
504
|
+
chain: undefined,
|
|
505
|
+
account,
|
|
506
|
+
}))
|
|
507
|
+
await OneClickService.submitDepositTx({
|
|
508
|
+
txHash: hash,
|
|
509
|
+
depositAddress,
|
|
510
|
+
}).catch(e => {
|
|
511
|
+
console.log('NearIntents submitDepositTx failed', e)
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
resolve({
|
|
515
|
+
...params,
|
|
516
|
+
sourceTxHash: hash,
|
|
517
|
+
})
|
|
518
|
+
} catch (e) {
|
|
519
|
+
reject(e)
|
|
520
|
+
}
|
|
521
|
+
})
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async getTransactionStatus(p: NormalizedTxResponse): Promise<SwapStatus> {
|
|
525
|
+
const res = await OneClickService.getExecutionStatus(p.id)
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
txHash: res.swapDetails?.destinationChainTxHashes[0]?.hash || '',
|
|
529
|
+
status:
|
|
530
|
+
res.status === 'SUCCESS'
|
|
531
|
+
? 'Success'
|
|
532
|
+
: res.status === 'FAILED'
|
|
533
|
+
? 'Failed'
|
|
534
|
+
: res.status === 'REFUNDED'
|
|
535
|
+
? 'Refunded'
|
|
536
|
+
: 'Processing',
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|