@deserialize/multi-vm-wallet 1.3.3 → 1.4.0
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/IChainWallet.d.ts +2 -0
- package/dist/IChainWallet.js.map +1 -1
- package/dist/evm/evm.d.ts +14 -172
- package/dist/evm/evm.js +38 -504
- package/dist/evm/evm.js.map +1 -1
- package/dist/evm/transaction.utils.d.ts +3 -3
- package/dist/evm/utils.d.ts +115 -80
- package/dist/evm/utils.js +272 -497
- package/dist/evm/utils.js.map +1 -1
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +5 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/price.d.ts +2 -0
- package/dist/price.js +33 -0
- package/dist/price.js.map +1 -0
- package/dist/price.types.d.ts +38 -0
- package/dist/price.types.js +4 -0
- package/dist/price.types.js.map +1 -0
- package/dist/savings/index.d.ts +0 -0
- package/dist/savings/index.js +25 -0
- package/dist/savings/index.js.map +1 -0
- package/dist/savings/saving-actions.d.ts +29 -0
- package/dist/savings/saving-actions.js +56 -0
- package/dist/savings/saving-actions.js.map +1 -0
- package/dist/savings/savings-manager.d.ts +1 -1
- package/dist/savings/savings-manager.js +3 -3
- package/dist/savings/savings-manager.js.map +1 -1
- package/dist/svm/svm.d.ts +2 -0
- package/dist/svm/svm.js +12 -0
- package/dist/svm/svm.js.map +1 -1
- package/dist/test.js +7 -7
- package/dist/test.js.map +1 -1
- package/dist/vm.js.map +1 -1
- package/package.json +1 -1
- package/utils/IChainWallet.ts +2 -0
- package/utils/evm/evm.ts +326 -681
- package/utils/evm/utils.ts +438 -662
- package/utils/helpers/index.ts +6 -0
- package/utils/index.ts +1 -0
- package/utils/price.ts +37 -0
- package/utils/price.types.ts +45 -0
- package/utils/savings/index.ts +28 -0
- package/utils/savings/saving-actions.ts +77 -0
- package/utils/savings/savings-manager.ts +1 -1
- package/utils/svm/svm.ts +16 -2
- package/utils/test.ts +13 -4
- package/utils/vm.ts +2 -1
package/utils/evm/utils.ts
CHANGED
|
@@ -4,8 +4,16 @@ import BN from 'bn.js'
|
|
|
4
4
|
import { HelperAPI } from '../helpers';
|
|
5
5
|
import BigNumber from 'bignumber.js';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import type { TransactionReceipt as EthersTransactionReceipt } from 'ethers'
|
|
8
|
+
import type { Chain, TransactionReceipt as ViemTransactionReceipt } from 'viem'
|
|
9
|
+
import {
|
|
10
|
+
PublicClient,
|
|
11
|
+
WalletClient,
|
|
12
|
+
Hex,
|
|
13
|
+
encodeFunctionData,
|
|
14
|
+
parseEther,
|
|
15
|
+
formatEther,
|
|
16
|
+
} from 'viem'
|
|
9
17
|
export interface TransactionParams {
|
|
10
18
|
to: string
|
|
11
19
|
value?: string | bigint // For native token transfers
|
|
@@ -19,7 +27,8 @@ export interface TransactionParams {
|
|
|
19
27
|
|
|
20
28
|
interface TransactionResult {
|
|
21
29
|
hash: string
|
|
22
|
-
receipt:
|
|
30
|
+
receipt: EthersTransactionReceipt | null
|
|
31
|
+
viemReceipt?: ViemTransactionReceipt
|
|
23
32
|
success: boolean
|
|
24
33
|
gasUsed?: bigint
|
|
25
34
|
effectiveGasPrice?: bigint
|
|
@@ -137,508 +146,555 @@ interface KyberSwapParams {
|
|
|
137
146
|
}
|
|
138
147
|
|
|
139
148
|
// ERC-20 ABI
|
|
140
|
-
const ERC20_ABI = [
|
|
141
|
-
"function balanceOf(address owner) view returns (uint256)",
|
|
142
|
-
"function decimals() view returns (uint8)",
|
|
143
|
-
"function symbol() view returns (string)",
|
|
144
|
-
"function name() view returns (string)",
|
|
145
|
-
"function transfer(address to, uint256 amount) returns (bool)",
|
|
146
|
-
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
147
|
-
"function approve(address spender, uint256 amount) returns (bool)",
|
|
148
|
-
"function allowance(address owner, address spender) view returns (uint256)"
|
|
149
|
-
]
|
|
150
|
-
|
|
151
|
-
export const getNativeBalance = async (address: string, provider: JsonRpcProvider): Promise<Balance> => {
|
|
152
|
-
const balance = await provider.getBalance(address)
|
|
153
|
-
const final = ethers.formatEther(balance)
|
|
154
149
|
|
|
150
|
+
export const ERC20_ABI = [
|
|
151
|
+
{
|
|
152
|
+
name: 'balanceOf',
|
|
153
|
+
type: 'function',
|
|
154
|
+
stateMutability: 'view',
|
|
155
|
+
inputs: [{ name: 'owner', type: 'address' }],
|
|
156
|
+
outputs: [{ type: 'uint256' }],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'decimals',
|
|
160
|
+
type: 'function',
|
|
161
|
+
stateMutability: 'view',
|
|
162
|
+
inputs: [],
|
|
163
|
+
outputs: [{ type: 'uint8' }],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'symbol',
|
|
167
|
+
type: 'function',
|
|
168
|
+
stateMutability: 'view',
|
|
169
|
+
inputs: [],
|
|
170
|
+
outputs: [{ type: 'string' }],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'name',
|
|
174
|
+
type: 'function',
|
|
175
|
+
stateMutability: 'view',
|
|
176
|
+
inputs: [],
|
|
177
|
+
outputs: [{ type: 'string' }],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'transfer',
|
|
181
|
+
type: 'function',
|
|
182
|
+
stateMutability: 'nonpayable',
|
|
183
|
+
inputs: [
|
|
184
|
+
{ name: 'to', type: 'address' },
|
|
185
|
+
{ name: 'amount', type: 'uint256' },
|
|
186
|
+
],
|
|
187
|
+
outputs: [{ type: 'bool' }],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: 'approve',
|
|
191
|
+
type: 'function',
|
|
192
|
+
stateMutability: 'nonpayable',
|
|
193
|
+
inputs: [
|
|
194
|
+
{ name: 'spender', type: 'address' },
|
|
195
|
+
{ name: 'amount', type: 'uint256' },
|
|
196
|
+
],
|
|
197
|
+
outputs: [{ type: 'bool' }],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: 'allowance',
|
|
201
|
+
type: 'function',
|
|
202
|
+
stateMutability: 'view',
|
|
203
|
+
inputs: [
|
|
204
|
+
{ name: 'owner', type: 'address' },
|
|
205
|
+
{ name: 'spender', type: 'address' },
|
|
206
|
+
],
|
|
207
|
+
outputs: [{ type: 'uint256' }],
|
|
208
|
+
},
|
|
209
|
+
] as const
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
export const fromChainToViemChain = (config: ChainWalletConfig): Chain => {
|
|
155
213
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
214
|
+
rpcUrls: {
|
|
215
|
+
default: {
|
|
216
|
+
http: [config.rpcUrl]
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
id: config.chainId,
|
|
220
|
+
name: config.name,
|
|
221
|
+
nativeCurrency: {
|
|
222
|
+
name: config.nativeToken.name,
|
|
223
|
+
symbol: config.nativeToken.symbol,
|
|
224
|
+
decimals: config.nativeToken.decimals
|
|
225
|
+
},
|
|
226
|
+
blockExplorers: {
|
|
227
|
+
default: {
|
|
228
|
+
name: config.name + " Explorer",
|
|
229
|
+
url: config.explorerUrl,
|
|
230
|
+
apiUrl: config.explorerUrl
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
testnet: config.testnet || false
|
|
234
|
+
|
|
159
235
|
}
|
|
160
|
-
}
|
|
161
236
|
|
|
162
|
-
export const getTokenInfo = async (
|
|
163
|
-
tokenAddress: string,
|
|
164
|
-
provider: JsonRpcProvider
|
|
165
|
-
): Promise<TokenInfo> => {
|
|
166
|
-
try {
|
|
167
|
-
// Create contract instance
|
|
168
|
-
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider)
|
|
169
237
|
|
|
238
|
+
}
|
|
239
|
+
export function viemReceiptToEthersReceipt(
|
|
240
|
+
receipt: ViemTransactionReceipt,
|
|
241
|
+
): EthersTransactionReceipt {
|
|
242
|
+
return {
|
|
243
|
+
to: receipt.to ?? null,
|
|
244
|
+
from: receipt.from,
|
|
245
|
+
contractAddress: receipt.contractAddress ?? null,
|
|
246
|
+
|
|
247
|
+
transactionIndex: Number(receipt.transactionIndex),
|
|
248
|
+
gasUsed: receipt.gasUsed,
|
|
249
|
+
logsBloom: receipt.logsBloom,
|
|
250
|
+
blockHash: receipt.blockHash,
|
|
251
|
+
transactionHash: receipt.transactionHash,
|
|
252
|
+
|
|
253
|
+
logs: receipt.logs.map((log) => ({
|
|
254
|
+
address: log.address,
|
|
255
|
+
topics: log.topics,
|
|
256
|
+
data: log.data,
|
|
257
|
+
blockNumber: Number(log.blockNumber),
|
|
258
|
+
transactionHash: log.transactionHash,
|
|
259
|
+
transactionIndex: Number(log.transactionIndex),
|
|
260
|
+
blockHash: log.blockHash,
|
|
261
|
+
logIndex: Number(log.logIndex),
|
|
262
|
+
removed: false,
|
|
263
|
+
})),
|
|
264
|
+
|
|
265
|
+
blockNumber: Number(receipt.blockNumber),
|
|
266
|
+
confirmations: 0, // ethers usually fills this lazily
|
|
267
|
+
cumulativeGasUsed: receipt.cumulativeGasUsed,
|
|
268
|
+
effectiveGasPrice: receipt.effectiveGasPrice,
|
|
269
|
+
|
|
270
|
+
status: receipt.status === 'success' ? 1 : 0,
|
|
271
|
+
type: receipt.type,
|
|
272
|
+
} as unknown as EthersTransactionReceipt
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
export const getNativeBalance = async (
|
|
277
|
+
address: Hex,
|
|
278
|
+
client: PublicClient
|
|
279
|
+
): Promise<Balance> => {
|
|
280
|
+
const balance = await client.getBalance({ address })
|
|
170
281
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
282
|
+
return {
|
|
283
|
+
balance: new BN(balance.toString()),
|
|
284
|
+
formatted: Number(formatEther(balance)),
|
|
285
|
+
decimal: 18,
|
|
286
|
+
}
|
|
287
|
+
}
|
|
176
288
|
|
|
177
|
-
])
|
|
178
289
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
290
|
+
export const getTokenInfo = async (
|
|
291
|
+
tokenAddress: Hex,
|
|
292
|
+
client: PublicClient
|
|
293
|
+
): Promise<TokenInfo> => {
|
|
294
|
+
const [decimals, name, symbol] = await Promise.all([
|
|
295
|
+
client.readContract({
|
|
182
296
|
address: tokenAddress,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
297
|
+
abi: ERC20_ABI,
|
|
298
|
+
functionName: 'decimals',
|
|
299
|
+
}),
|
|
300
|
+
client.readContract({
|
|
301
|
+
address: tokenAddress,
|
|
302
|
+
abi: ERC20_ABI,
|
|
303
|
+
functionName: 'name',
|
|
304
|
+
}),
|
|
305
|
+
client.readContract({
|
|
306
|
+
address: tokenAddress,
|
|
307
|
+
abi: ERC20_ABI,
|
|
308
|
+
functionName: 'symbol',
|
|
309
|
+
}),
|
|
310
|
+
])
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
name,
|
|
314
|
+
symbol,
|
|
315
|
+
address: tokenAddress,
|
|
316
|
+
decimals,
|
|
188
317
|
}
|
|
189
318
|
}
|
|
319
|
+
|
|
190
320
|
export const getTokenBalance = async (
|
|
191
|
-
tokenAddress:
|
|
192
|
-
walletAddress:
|
|
193
|
-
|
|
321
|
+
tokenAddress: Hex,
|
|
322
|
+
walletAddress: Hex,
|
|
323
|
+
client: PublicClient
|
|
194
324
|
): Promise<Balance> => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
325
|
+
const [balance, decimals] = await Promise.all([
|
|
326
|
+
client.readContract({
|
|
327
|
+
address: tokenAddress,
|
|
328
|
+
abi: ERC20_ABI,
|
|
329
|
+
functionName: 'balanceOf',
|
|
330
|
+
args: [walletAddress],
|
|
331
|
+
}),
|
|
332
|
+
client.readContract({
|
|
333
|
+
address: tokenAddress,
|
|
334
|
+
abi: ERC20_ABI,
|
|
335
|
+
functionName: 'decimals',
|
|
336
|
+
}),
|
|
337
|
+
])
|
|
201
338
|
|
|
202
|
-
|
|
203
|
-
const decimals = await tokenContract.decimals()
|
|
204
|
-
// Format balance by dividing by 10^decimals
|
|
205
|
-
const formattedBalance = balance / (10n ** BigInt(decimals))
|
|
339
|
+
const formatted = balance / 10n ** BigInt(decimals)
|
|
206
340
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.error('Error fetching token balance:', error)
|
|
214
|
-
throw error
|
|
341
|
+
return {
|
|
342
|
+
balance: new BN(balance.toString()),
|
|
343
|
+
formatted: Number(formatted),
|
|
344
|
+
decimal: decimals,
|
|
215
345
|
}
|
|
216
346
|
}
|
|
217
347
|
|
|
348
|
+
|
|
218
349
|
/**
|
|
219
350
|
* Sign, send, and confirm any EVM transaction
|
|
220
351
|
*/
|
|
221
352
|
export const signSendAndConfirm = async (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
353
|
+
walletClient: WalletClient,
|
|
354
|
+
publicClient: PublicClient,
|
|
355
|
+
params: {
|
|
356
|
+
to: Hex
|
|
357
|
+
data?: Hex
|
|
358
|
+
value?: bigint
|
|
359
|
+
gas?: bigint
|
|
360
|
+
nonce?: number
|
|
361
|
+
maxFeePerGas?: bigint
|
|
362
|
+
maxPriorityFeePerGas?: bigint
|
|
363
|
+
},
|
|
364
|
+
confirmations = 1
|
|
226
365
|
): Promise<TransactionResult> => {
|
|
227
|
-
try {
|
|
228
|
-
// Prepare transaction object
|
|
229
|
-
const tx: TransactionRequest = {
|
|
230
|
-
to: transactionParams.to,
|
|
231
|
-
value: transactionParams.value || 0,
|
|
232
|
-
data: transactionParams.data || '0x',
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Set gas parameters
|
|
236
|
-
if (transactionParams.gasLimit) {
|
|
237
|
-
tx.gasLimit = transactionParams.gasLimit
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Handle gas pricing (EIP-1559 vs legacy)
|
|
241
|
-
if (transactionParams.maxFeePerGas && transactionParams.maxPriorityFeePerGas) {
|
|
242
|
-
// EIP-1559 transaction
|
|
243
|
-
tx.maxFeePerGas = transactionParams.maxFeePerGas
|
|
244
|
-
tx.maxPriorityFeePerGas = transactionParams.maxPriorityFeePerGas
|
|
245
|
-
} else if (transactionParams.gasPrice) {
|
|
246
|
-
// Legacy transaction
|
|
247
|
-
tx.gasPrice = transactionParams.gasPrice
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Set nonce if provided
|
|
251
|
-
if (transactionParams.nonce !== undefined) {
|
|
252
|
-
tx.nonce = transactionParams.nonce
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Estimate gas if not provided
|
|
256
|
-
if (!tx.gasLimit) {
|
|
257
|
-
tx.gasLimit = await wallet.estimateGas(tx)
|
|
258
|
-
}
|
|
259
366
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
])
|
|
274
|
-
|
|
275
|
-
const result: TransactionResult = {
|
|
276
|
-
hash: txResponse.hash,
|
|
277
|
-
receipt: receipt,
|
|
278
|
-
success: receipt?.status === 1,
|
|
279
|
-
gasUsed: receipt?.gasUsed,
|
|
280
|
-
effectiveGasPrice: receipt?.gasPrice,
|
|
281
|
-
blockNumber: receipt?.blockNumber,
|
|
282
|
-
confirmations: confirmations
|
|
283
|
-
}
|
|
367
|
+
if (walletClient.account === undefined) {
|
|
368
|
+
throw new Error("wallet Client is not Initialized with an Account")
|
|
369
|
+
}
|
|
370
|
+
const hash = await walletClient.sendTransaction({
|
|
371
|
+
to: params.to,
|
|
372
|
+
data: params.data,
|
|
373
|
+
value: params.value,
|
|
374
|
+
gas: params.gas,
|
|
375
|
+
nonce: params.nonce,
|
|
376
|
+
maxFeePerGas: params.maxFeePerGas,
|
|
377
|
+
maxPriorityFeePerGas: params.maxPriorityFeePerGas,
|
|
378
|
+
account: walletClient.account,
|
|
379
|
+
chain: publicClient.chain
|
|
284
380
|
|
|
285
|
-
|
|
381
|
+
})
|
|
286
382
|
|
|
287
|
-
|
|
383
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
384
|
+
hash,
|
|
385
|
+
confirmations,
|
|
386
|
+
})
|
|
288
387
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
388
|
+
return {
|
|
389
|
+
hash,
|
|
390
|
+
receipt: viemReceiptToEthersReceipt(receipt),
|
|
391
|
+
viemReceipt: receipt,
|
|
392
|
+
success: receipt.status === 'success',
|
|
393
|
+
gasUsed: receipt.gasUsed,
|
|
394
|
+
effectiveGasPrice: receipt.effectiveGasPrice,
|
|
395
|
+
blockNumber: Number(receipt.blockNumber.toString()),
|
|
396
|
+
confirmations,
|
|
292
397
|
}
|
|
293
398
|
}
|
|
294
399
|
|
|
400
|
+
|
|
295
401
|
/**
|
|
296
402
|
* Send native token (ETH, BNB, MATIC, etc.)
|
|
297
403
|
*/
|
|
298
404
|
export const sendNativeToken = async (
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
confirmations
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
},
|
|
405
|
+
walletClient: WalletClient,
|
|
406
|
+
publicClient: PublicClient,
|
|
407
|
+
to: Hex,
|
|
408
|
+
amount: string | bigint,
|
|
409
|
+
confirmations = 1
|
|
410
|
+
) => {
|
|
411
|
+
const value =
|
|
412
|
+
typeof amount === 'string' ? parseEther(amount) : amount
|
|
413
|
+
|
|
414
|
+
return signSendAndConfirm(
|
|
415
|
+
walletClient,
|
|
416
|
+
publicClient,
|
|
417
|
+
{ to, value },
|
|
312
418
|
confirmations
|
|
313
419
|
)
|
|
314
420
|
}
|
|
315
421
|
|
|
422
|
+
|
|
316
423
|
/**
|
|
317
424
|
* Send ERC-20 token
|
|
318
425
|
*/
|
|
319
426
|
export const sendERC20Token = async (
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
confirmations
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const data = tokenContract.interface.encodeFunctionData('transfer', [to, amount])
|
|
333
|
-
|
|
334
|
-
return await signSendAndConfirm(
|
|
335
|
-
wallet,
|
|
336
|
-
{
|
|
337
|
-
to: tokenAddress,
|
|
338
|
-
data,
|
|
339
|
-
gasLimit
|
|
340
|
-
},
|
|
341
|
-
confirmations
|
|
342
|
-
)
|
|
343
|
-
} catch (error) {
|
|
344
|
-
console.error('ERC-20 transfer failed:', error)
|
|
345
|
-
throw error
|
|
346
|
-
}
|
|
347
|
-
}
|
|
427
|
+
walletClient: WalletClient,
|
|
428
|
+
publicClient: PublicClient,
|
|
429
|
+
tokenAddress: Hex,
|
|
430
|
+
to: Hex,
|
|
431
|
+
amount: bigint,
|
|
432
|
+
confirmations = 1
|
|
433
|
+
) => {
|
|
434
|
+
const data = encodeFunctionData({
|
|
435
|
+
abi: ERC20_ABI,
|
|
436
|
+
functionName: 'transfer',
|
|
437
|
+
args: [to, amount],
|
|
438
|
+
})
|
|
348
439
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
abi: any[],
|
|
356
|
-
methodName: string,
|
|
357
|
-
methodParams: any[] = [],
|
|
358
|
-
value?: string | bigint, // For payable methods
|
|
359
|
-
gasLimit?: string | bigint,
|
|
360
|
-
confirmations: number = 1
|
|
361
|
-
): Promise<TransactionResult> => {
|
|
362
|
-
try {
|
|
363
|
-
// Create contract instance
|
|
364
|
-
const contract = new Contract(contractAddress, abi, wallet)
|
|
365
|
-
|
|
366
|
-
// Encode method call
|
|
367
|
-
const data = contract.interface.encodeFunctionData(methodName, methodParams)
|
|
368
|
-
|
|
369
|
-
return await signSendAndConfirm(
|
|
370
|
-
wallet,
|
|
371
|
-
{
|
|
372
|
-
to: contractAddress,
|
|
373
|
-
data,
|
|
374
|
-
value,
|
|
375
|
-
gasLimit
|
|
376
|
-
},
|
|
377
|
-
confirmations
|
|
378
|
-
)
|
|
379
|
-
} catch (error) {
|
|
380
|
-
console.error('Contract method execution failed:', error)
|
|
381
|
-
throw error
|
|
382
|
-
}
|
|
440
|
+
return signSendAndConfirm(
|
|
441
|
+
walletClient,
|
|
442
|
+
publicClient,
|
|
443
|
+
{ to: tokenAddress, data },
|
|
444
|
+
confirmations
|
|
445
|
+
)
|
|
383
446
|
}
|
|
384
447
|
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
385
451
|
/**
|
|
386
452
|
* Get current gas prices (both legacy and EIP-1559)
|
|
387
453
|
*/
|
|
388
|
-
export const getGasPrices = async (
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
gasPrice: feeData.gasPrice,
|
|
395
|
-
// EIP-1559
|
|
396
|
-
maxFeePerGas: feeData.maxFeePerGas,
|
|
397
|
-
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
|
|
398
|
-
}
|
|
399
|
-
} catch (error) {
|
|
400
|
-
console.error('Error fetching gas prices:', error)
|
|
401
|
-
throw error
|
|
454
|
+
export const getGasPrices = async (client: PublicClient) => {
|
|
455
|
+
const fees = await client.estimateFeesPerGas()
|
|
456
|
+
return {
|
|
457
|
+
gasPrice: fees.gasPrice,
|
|
458
|
+
maxFeePerGas: fees.maxFeePerGas,
|
|
459
|
+
maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
|
|
402
460
|
}
|
|
403
461
|
}
|
|
404
462
|
|
|
463
|
+
|
|
405
464
|
/**
|
|
406
465
|
* Estimate gas for a transaction
|
|
407
466
|
*/
|
|
408
467
|
export const estimateGas = async (
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
to: transactionParams.to,
|
|
415
|
-
value: transactionParams.value || 0,
|
|
416
|
-
data: transactionParams.data || '0x'
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
return await provider.estimateGas(tx)
|
|
420
|
-
} catch (error) {
|
|
421
|
-
console.error('Gas estimation failed:', error)
|
|
422
|
-
throw error
|
|
468
|
+
client: PublicClient,
|
|
469
|
+
params: {
|
|
470
|
+
to: Hex
|
|
471
|
+
data?: Hex
|
|
472
|
+
value?: bigint
|
|
423
473
|
}
|
|
474
|
+
) => {
|
|
475
|
+
return client.estimateGas(params)
|
|
424
476
|
}
|
|
425
477
|
|
|
426
478
|
|
|
427
|
-
/**
|
|
428
|
-
* Check ERC-20 token allowance
|
|
429
|
-
*/
|
|
430
479
|
export const checkAllowance = async (
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
)
|
|
436
|
-
allowance
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
// Format allowance for display
|
|
450
|
-
const formattedAllowance = allowance / (10n ** BigInt(decimals))
|
|
480
|
+
client: PublicClient,
|
|
481
|
+
tokenAddress: Hex,
|
|
482
|
+
owner: Hex,
|
|
483
|
+
spender: Hex,
|
|
484
|
+
) => {
|
|
485
|
+
const [allowance, decimals] = await Promise.all([
|
|
486
|
+
client.readContract({
|
|
487
|
+
address: tokenAddress,
|
|
488
|
+
abi: ERC20_ABI,
|
|
489
|
+
functionName: 'allowance',
|
|
490
|
+
args: [owner, spender],
|
|
491
|
+
}),
|
|
492
|
+
client.readContract({
|
|
493
|
+
address: tokenAddress,
|
|
494
|
+
abi: ERC20_ABI,
|
|
495
|
+
functionName: 'decimals',
|
|
496
|
+
}),
|
|
497
|
+
])
|
|
451
498
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
} catch (error) {
|
|
458
|
-
console.error('Error checking allowance:', error)
|
|
459
|
-
throw error
|
|
499
|
+
return {
|
|
500
|
+
allowance,
|
|
501
|
+
formatted: (allowance / 10n ** BigInt(decimals)).toString(),
|
|
502
|
+
decimals,
|
|
460
503
|
}
|
|
461
504
|
}
|
|
462
505
|
|
|
506
|
+
|
|
463
507
|
/**
|
|
464
508
|
* Check if allowance is sufficient for a transaction
|
|
465
509
|
*/
|
|
466
510
|
export const isAllowanceSufficient = async (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
511
|
+
publicClient: PublicClient,
|
|
512
|
+
tokenAddress: `0x${string}`,
|
|
513
|
+
owner: `0x${string}`,
|
|
514
|
+
spender: `0x${string}`,
|
|
470
515
|
requiredAmount: string | bigint,
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
516
|
+
) => {
|
|
517
|
+
const { allowance } = await checkAllowance(
|
|
518
|
+
publicClient,
|
|
519
|
+
tokenAddress,
|
|
520
|
+
owner,
|
|
521
|
+
spender,
|
|
522
|
+
)
|
|
476
523
|
|
|
477
|
-
|
|
478
|
-
} catch (error) {
|
|
479
|
-
console.error('Error checking allowance sufficiency:', error)
|
|
480
|
-
throw error
|
|
481
|
-
}
|
|
524
|
+
return allowance >= BigInt(requiredAmount)
|
|
482
525
|
}
|
|
483
526
|
|
|
527
|
+
|
|
484
528
|
/**
|
|
485
529
|
* Approve ERC-20 token spending
|
|
486
530
|
*/
|
|
487
531
|
export const approveToken = async (
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
confirmations
|
|
494
|
-
)
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
532
|
+
walletClient: WalletClient,
|
|
533
|
+
publicClient: PublicClient,
|
|
534
|
+
tokenAddress: Hex,
|
|
535
|
+
spender: Hex,
|
|
536
|
+
amount: bigint,
|
|
537
|
+
confirmations = 1
|
|
538
|
+
) => {
|
|
539
|
+
const data = encodeFunctionData({
|
|
540
|
+
abi: ERC20_ABI,
|
|
541
|
+
functionName: 'approve',
|
|
542
|
+
args: [spender, amount],
|
|
543
|
+
})
|
|
500
544
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
},
|
|
508
|
-
confirmations
|
|
509
|
-
)
|
|
510
|
-
} catch (error) {
|
|
511
|
-
console.error('Token approval failed:', error)
|
|
512
|
-
throw error
|
|
513
|
-
}
|
|
545
|
+
return signSendAndConfirm(
|
|
546
|
+
walletClient,
|
|
547
|
+
publicClient,
|
|
548
|
+
{ to: tokenAddress, data },
|
|
549
|
+
confirmations
|
|
550
|
+
)
|
|
514
551
|
}
|
|
515
552
|
|
|
553
|
+
const MAX_UINT256 =
|
|
554
|
+
2n ** 256n - 1n
|
|
555
|
+
|
|
516
556
|
/**
|
|
517
557
|
* Approve unlimited token spending (MaxUint256)
|
|
518
558
|
*/
|
|
519
559
|
export const approveTokenUnlimited = async (
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
560
|
+
walletClient: WalletClient,
|
|
561
|
+
publicClient: PublicClient,
|
|
562
|
+
tokenAddress: `0x${string}`,
|
|
563
|
+
spender: `0x${string}`,
|
|
564
|
+
gas?: bigint,
|
|
565
|
+
confirmations = 1,
|
|
566
|
+
) => {
|
|
567
|
+
return approveToken(
|
|
568
|
+
walletClient,
|
|
569
|
+
publicClient,
|
|
570
|
+
tokenAddress,
|
|
571
|
+
spender,
|
|
572
|
+
MAX_UINT256,
|
|
573
|
+
confirmations,
|
|
574
|
+
)
|
|
530
575
|
}
|
|
531
576
|
|
|
532
577
|
/**
|
|
533
578
|
* Check allowance and approve if necessary
|
|
534
579
|
*/
|
|
535
580
|
export const checkAndApprove = async (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
581
|
+
walletClient: WalletClient,
|
|
582
|
+
publicClient: PublicClient,
|
|
583
|
+
tokenAddress: `0x${string}`,
|
|
584
|
+
spender: `0x${string}`,
|
|
585
|
+
requiredAmount: bigint,
|
|
586
|
+
approvalAmount?: bigint,
|
|
587
|
+
gas?: bigint,
|
|
588
|
+
confirmations = 1,
|
|
543
589
|
): Promise<{
|
|
544
|
-
approvalNeeded: boolean
|
|
545
|
-
currentAllowance: bigint
|
|
590
|
+
approvalNeeded: boolean
|
|
591
|
+
currentAllowance: bigint
|
|
546
592
|
approvalResult?: TransactionResult
|
|
547
593
|
}> => {
|
|
548
|
-
|
|
549
|
-
const owner = await wallet.getAddress()
|
|
550
|
-
const provider = wallet.provider as JsonRpcProvider
|
|
551
|
-
|
|
552
|
-
// Check current allowance
|
|
553
|
-
const { allowance } = await checkAllowance(tokenAddress, owner, spender, provider)
|
|
554
|
-
const required = typeof requiredAmount === 'string' ? BigInt(requiredAmount) : requiredAmount
|
|
594
|
+
const owner = walletClient.account!.address
|
|
555
595
|
|
|
556
|
-
|
|
596
|
+
const allowance = await publicClient.readContract({
|
|
597
|
+
address: tokenAddress,
|
|
598
|
+
abi: ERC20_ABI,
|
|
599
|
+
functionName: 'allowance',
|
|
600
|
+
args: [owner, spender],
|
|
601
|
+
})
|
|
557
602
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
603
|
+
if (allowance >= requiredAmount) {
|
|
604
|
+
return {
|
|
605
|
+
approvalNeeded: false,
|
|
606
|
+
currentAllowance: allowance,
|
|
563
607
|
}
|
|
608
|
+
}
|
|
564
609
|
|
|
565
|
-
|
|
610
|
+
const amountToApprove = approvalAmount ?? requiredAmount
|
|
566
611
|
|
|
567
|
-
|
|
568
|
-
|
|
612
|
+
const approvalResult = await approveToken(
|
|
613
|
+
walletClient,
|
|
614
|
+
publicClient,
|
|
615
|
+
tokenAddress,
|
|
616
|
+
spender,
|
|
617
|
+
amountToApprove,
|
|
569
618
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
wallet,
|
|
573
|
-
tokenAddress,
|
|
574
|
-
spender,
|
|
575
|
-
amountToApprove,
|
|
576
|
-
gasLimit,
|
|
577
|
-
confirmations
|
|
578
|
-
)
|
|
619
|
+
confirmations,
|
|
620
|
+
)
|
|
579
621
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
} catch (error) {
|
|
586
|
-
console.error('Check and approve failed:', error)
|
|
587
|
-
throw error
|
|
622
|
+
return {
|
|
623
|
+
approvalNeeded: true,
|
|
624
|
+
currentAllowance: allowance,
|
|
625
|
+
approvalResult,
|
|
588
626
|
}
|
|
589
627
|
}
|
|
590
628
|
|
|
629
|
+
|
|
591
630
|
/**
|
|
592
631
|
* Reset token allowance to zero (security best practice before setting new allowance)
|
|
593
632
|
*/
|
|
594
633
|
export const resetAllowance = async (
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
634
|
+
walletClient: WalletClient,
|
|
635
|
+
publicClient: PublicClient,
|
|
636
|
+
tokenAddress: `0x${string}`,
|
|
637
|
+
spender: `0x${string}`,
|
|
638
|
+
gas?: bigint,
|
|
639
|
+
confirmations = 1,
|
|
640
|
+
) => {
|
|
641
|
+
return approveToken(
|
|
642
|
+
walletClient,
|
|
643
|
+
publicClient,
|
|
644
|
+
tokenAddress,
|
|
645
|
+
spender,
|
|
646
|
+
0n,
|
|
647
|
+
confirmations,
|
|
648
|
+
)
|
|
602
649
|
}
|
|
603
650
|
|
|
651
|
+
|
|
604
652
|
/**
|
|
605
653
|
* Safe approve: Reset to zero first, then approve the desired amount
|
|
606
654
|
* (Some tokens like USDT require this)
|
|
607
655
|
*/
|
|
608
656
|
export const safeApprove = async (
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
657
|
+
walletClient: WalletClient,
|
|
658
|
+
publicClient: PublicClient,
|
|
659
|
+
tokenAddress: `0x${string}`,
|
|
660
|
+
spender: `0x${string}`,
|
|
661
|
+
amount: bigint,
|
|
662
|
+
gas?: bigint,
|
|
663
|
+
confirmations = 1,
|
|
615
664
|
): Promise<{
|
|
616
|
-
resetResult: TransactionResult
|
|
665
|
+
resetResult: TransactionResult
|
|
617
666
|
approveResult: TransactionResult
|
|
618
667
|
}> => {
|
|
619
|
-
|
|
620
|
-
|
|
668
|
+
const resetResult = await resetAllowance(
|
|
669
|
+
walletClient,
|
|
670
|
+
publicClient,
|
|
671
|
+
tokenAddress,
|
|
672
|
+
spender,
|
|
673
|
+
gas,
|
|
674
|
+
confirmations,
|
|
675
|
+
)
|
|
621
676
|
|
|
622
|
-
|
|
623
|
-
|
|
677
|
+
if (!resetResult.success) {
|
|
678
|
+
throw new Error('Failed to reset allowance')
|
|
679
|
+
}
|
|
624
680
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
681
|
+
const approveResult = await approveToken(
|
|
682
|
+
walletClient,
|
|
683
|
+
publicClient,
|
|
684
|
+
tokenAddress,
|
|
685
|
+
spender,
|
|
686
|
+
amount,
|
|
628
687
|
|
|
629
|
-
|
|
630
|
-
|
|
688
|
+
confirmations,
|
|
689
|
+
)
|
|
631
690
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
}
|
|
636
|
-
} catch (error) {
|
|
637
|
-
console.error('Safe approve failed:', error)
|
|
638
|
-
throw error
|
|
691
|
+
return {
|
|
692
|
+
resetResult,
|
|
693
|
+
approveResult,
|
|
639
694
|
}
|
|
640
695
|
}
|
|
641
696
|
|
|
697
|
+
|
|
642
698
|
export const discoverTokens = async (wallet: string, chain: ChainWalletConfig): Promise<UserTokenBalance<string>[]> => {
|
|
643
699
|
const balances = await HelperAPI.getUserToken(wallet, chain.vmType ?? "EVM", chain.chainId)
|
|
644
700
|
|
|
@@ -676,287 +732,7 @@ export function calcTokenAmount(value: number | string | BigNumber, decimals?: n
|
|
|
676
732
|
const divisor = new BigNumber(10).pow(decimals ?? 0);
|
|
677
733
|
return new BigNumber(String(value)).div(divisor);
|
|
678
734
|
}
|
|
679
|
-
//swaps
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
//kyber swap here
|
|
684
|
-
//docs -. https://docs.kyberswap.com/kyberswap-solutions/kyberswap-aggregator/developer-guides/execute-a-swap-with-the-aggregator-api
|
|
685
|
-
// the major constrain is that each function should return a transaction to sign, do not sign transaction or send transaction within util functions
|
|
686
|
-
// let the ChainWalletClass be the one to sign and send,
|
|
687
|
-
//so in you chainWallet.swap, you can have the futil swap function to get the transaction then another function to sign and send and confirm the transaction
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
export async function getKyberSwapRoute(params: KyberSwapParams): Promise<KyberSwapResponse> {
|
|
692
|
-
const chainName = KYBER_SUPPORTED_CHAINS[params.chainId];
|
|
693
|
-
if (!chainName) {
|
|
694
|
-
throw new Error(`Unsupported chain ID: ${params.chainId}`);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
const queryParams = new URLSearchParams({
|
|
698
|
-
tokenIn: params.tokenIn,
|
|
699
|
-
tokenOut: params.tokenOut,
|
|
700
|
-
amountIn: params.amountIn,
|
|
701
|
-
});
|
|
702
|
-
|
|
703
|
-
if (params.feeAmount) queryParams.append('feeAmount', params.feeAmount);
|
|
704
|
-
if (params.feeReceiver) queryParams.append('feeReceiver', params.feeReceiver);
|
|
705
|
-
if (params.isInBps !== undefined) queryParams.append('isInBps', params.isInBps.toString());
|
|
706
|
-
if (params.chargeFeeBy) queryParams.append('chargeFeeBy', params.chargeFeeBy);
|
|
707
|
-
|
|
708
|
-
const url = `${KYBER_BASE_URL}/${chainName}/api/v1/routes?${queryParams}`;
|
|
709
|
-
|
|
710
|
-
const headers: { [key: string]: string } = {};
|
|
711
|
-
if (params.clientId) {
|
|
712
|
-
headers['x-client-id'] = params.clientId;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
try {
|
|
716
|
-
const response = await fetch(url, { headers });
|
|
717
|
-
const data = await response.json();
|
|
718
|
-
|
|
719
|
-
if (!response.ok) {
|
|
720
|
-
throw new Error(`KyberSwap API error: ${data.message || response.statusText}`);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
return data;
|
|
724
|
-
} catch (error) {
|
|
725
|
-
console.error('Error fetching KyberSwap route:', error);
|
|
726
|
-
throw error;
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
export async function buildKyberSwapTransaction(
|
|
732
|
-
chainId: string,
|
|
733
|
-
routeSummary: KyberRoute,
|
|
734
|
-
sender: string,
|
|
735
|
-
recipient: string,
|
|
736
|
-
slippageTolerance: number = 50,
|
|
737
|
-
deadline?: number,
|
|
738
|
-
clientId?: string
|
|
739
|
-
): Promise<KyberBuildResponse> {
|
|
740
|
-
const chainName = KYBER_SUPPORTED_CHAINS[chainId];
|
|
741
|
-
if (!chainName) {
|
|
742
|
-
throw new Error(`Unsupported chain ID: ${chainId}`);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
const url = `${KYBER_BASE_URL}/${chainName}/api/v1/route/build`;
|
|
746
|
-
|
|
747
|
-
const txDeadline = deadline || Math.floor(Date.now() / 1000) + 1200;
|
|
748
|
-
|
|
749
|
-
const body = {
|
|
750
|
-
routeSummary,
|
|
751
|
-
sender,
|
|
752
|
-
recipient,
|
|
753
|
-
slippageTolerance,
|
|
754
|
-
deadline: txDeadline,
|
|
755
|
-
source: clientId || 'MyWalletApp'
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
const headers: { [key: string]: string } = {
|
|
759
|
-
'Content-Type': 'application/json',
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
if (clientId) {
|
|
763
|
-
headers['x-client-id'] = clientId;
|
|
764
|
-
}
|
|
765
735
|
|
|
766
|
-
try {
|
|
767
|
-
const response = await fetch(url, {
|
|
768
|
-
method: 'POST',
|
|
769
|
-
headers,
|
|
770
|
-
body: JSON.stringify(body)
|
|
771
|
-
});
|
|
772
|
-
|
|
773
|
-
const data = await response.json();
|
|
774
|
-
|
|
775
|
-
if (!response.ok) {
|
|
776
|
-
throw new Error(`KyberSwap build API error: ${data.message || response.statusText}`);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
return data;
|
|
780
|
-
} catch (error) {
|
|
781
|
-
console.error('Error building KyberSwap transaction:', error);
|
|
782
|
-
throw error;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
export async function performSwap(params: {
|
|
787
|
-
chainId: string;
|
|
788
|
-
tokenIn: string;
|
|
789
|
-
tokenOut: string;
|
|
790
|
-
amountIn: string;
|
|
791
|
-
sender: string;
|
|
792
|
-
recipient?: string;
|
|
793
|
-
slippageTolerance?: number;
|
|
794
|
-
deadline?: number;
|
|
795
|
-
feeAmount?: string;
|
|
796
|
-
feeReceiver?: string;
|
|
797
|
-
isInBps?: boolean;
|
|
798
|
-
chargeFeeBy?: 'currency_in' | 'currency_out';
|
|
799
|
-
clientId?: string;
|
|
800
|
-
}): Promise<TransactionParams> {
|
|
801
|
-
if (!KYBER_SUPPORTED_CHAINS[params.chainId]) {
|
|
802
|
-
throw new Error(`KyberSwap does not support chain ID: ${params.chainId}`);
|
|
803
|
-
}
|
|
804
|
-
try {
|
|
805
|
-
console.log('Starting KyberSwap aggregation...', {
|
|
806
|
-
tokenIn: params.tokenIn,
|
|
807
|
-
tokenOut: params.tokenOut,
|
|
808
|
-
amountIn: params.amountIn,
|
|
809
|
-
chainId: params.chainId
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
console.log('Fetching best swap route across all DEXs...');
|
|
813
|
-
|
|
814
|
-
const routeResponse = await getKyberSwapRoute({
|
|
815
|
-
chainId: params.chainId,
|
|
816
|
-
tokenIn: params.tokenIn,
|
|
817
|
-
tokenOut: params.tokenOut,
|
|
818
|
-
amountIn: params.amountIn,
|
|
819
|
-
feeAmount: params.feeAmount,
|
|
820
|
-
feeReceiver: params.feeReceiver,
|
|
821
|
-
isInBps: params.isInBps,
|
|
822
|
-
chargeFeeBy: params.chargeFeeBy,
|
|
823
|
-
clientId: params.clientId || 'MyWalletApp'
|
|
824
|
-
});
|
|
825
|
-
|
|
826
|
-
// Debug: Log the full response to understand structure
|
|
827
|
-
console.log('Full KyberSwap route response:', JSON.stringify(routeResponse, null, 2));
|
|
828
|
-
|
|
829
|
-
if (!routeResponse.data || !routeResponse.data.routeSummary) {
|
|
830
|
-
throw new Error('No valid route found for the swap');
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
const { routeSummary, routerAddress } = routeResponse.data;
|
|
834
|
-
|
|
835
|
-
// Debug: Log what we actually received
|
|
836
|
-
console.log('routeSummary keys:', Object.keys(routeSummary));
|
|
837
|
-
console.log('routeSummary.swaps exists:', !!routeSummary.swaps);
|
|
838
|
-
|
|
839
|
-
// Safe logging that handles undefined swaps
|
|
840
|
-
console.log('✅ Best route found:', {
|
|
841
|
-
tokenIn: routeSummary.tokenIn,
|
|
842
|
-
tokenOut: routeSummary.tokenOut,
|
|
843
|
-
amountIn: routeSummary.amountIn,
|
|
844
|
-
amountOut: routeSummary.amountOut,
|
|
845
|
-
gasEstimate: routeSummary.gas,
|
|
846
|
-
routerAddress,
|
|
847
|
-
// Only try to access swaps if it exists
|
|
848
|
-
swapsCount: routeSummary.swaps ? routeSummary.swaps.length : 0,
|
|
849
|
-
// Only extract exchange names if swaps exists and has the expected structure
|
|
850
|
-
dexSources: routeSummary.swaps && Array.isArray(routeSummary.swaps)
|
|
851
|
-
? routeSummary.swaps.map(swap => swap?.exchange || 'unknown').filter(Boolean)
|
|
852
|
-
: ['unknown']
|
|
853
|
-
});
|
|
854
|
-
|
|
855
|
-
console.log('Building executable transaction...');
|
|
856
|
-
|
|
857
|
-
const buildResponse = await buildKyberSwapTransaction(
|
|
858
|
-
params.chainId,
|
|
859
|
-
routeSummary,
|
|
860
|
-
params.sender,
|
|
861
|
-
params.recipient || params.sender,
|
|
862
|
-
params.slippageTolerance || 50,
|
|
863
|
-
params.deadline,
|
|
864
|
-
params.clientId || 'MyWalletApp'
|
|
865
|
-
);
|
|
866
|
-
|
|
867
|
-
// Debug: Log build response
|
|
868
|
-
console.log('Build response:', JSON.stringify(buildResponse, null, 2));
|
|
869
|
-
|
|
870
|
-
if (!buildResponse.data || !buildResponse.data.data) {
|
|
871
|
-
throw new Error('Failed to build transaction data');
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
const { data: encodedData, gas, routerAddress: finalRouterAddress } = buildResponse.data;
|
|
875
|
-
|
|
876
|
-
console.log('✅ Transaction built successfully:', {
|
|
877
|
-
to: finalRouterAddress,
|
|
878
|
-
dataLength: encodedData.length,
|
|
879
|
-
gasEstimate: gas,
|
|
880
|
-
expectedOutput: buildResponse.data.amountOut
|
|
881
|
-
});
|
|
882
|
-
|
|
883
|
-
return {
|
|
884
|
-
to: finalRouterAddress,
|
|
885
|
-
data: encodedData,
|
|
886
|
-
gasLimit: gas,
|
|
887
|
-
value: params.tokenIn.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
|
|
888
|
-
? params.amountIn
|
|
889
|
-
: '0'
|
|
890
|
-
};
|
|
891
|
-
|
|
892
|
-
} catch (error) {
|
|
893
|
-
console.error('❌ KyberSwap aggregation failed:', error);
|
|
894
|
-
|
|
895
|
-
// More detailed error logging
|
|
896
|
-
if (error instanceof Error) {
|
|
897
|
-
console.error('Error details:', {
|
|
898
|
-
message: error.message,
|
|
899
|
-
stack: error.stack,
|
|
900
|
-
name: error.name
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
throw new Error(`Swap preparation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
export function getKyberSupportedChains(): { [key: string]: string } {
|
|
909
|
-
return { ...KYBER_SUPPORTED_CHAINS };
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
export function isChainSupportedByKyber(chainId: string): boolean {
|
|
913
|
-
return chainId in KYBER_SUPPORTED_CHAINS;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
export function isChainSupportedByDebonk(chainId: string): boolean {
|
|
917
|
-
return chainId in DESERIALIZED_SUPPORTED_CHAINS;
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
export function getNativeTokenAddress(): string {
|
|
921
|
-
return '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
export function formatAmountToWei(amount: string, decimals: number): string {
|
|
925
|
-
return parseUnits(amount, decimals).toString();
|
|
926
|
-
}
|
|
927
|
-
export function formatAmountFromWei(amountWei: string, decimals: number): string {
|
|
928
|
-
return formatUnits(amountWei, decimals);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
export function prepareSwapParams(
|
|
932
|
-
tokenIn: string,
|
|
933
|
-
tokenOut: string,
|
|
934
|
-
amountIn: string,
|
|
935
|
-
tokenInDecimals: number,
|
|
936
|
-
isNativeIn: boolean = false,
|
|
937
|
-
isNativeOut: boolean = false
|
|
938
|
-
): {
|
|
939
|
-
tokenInAddress: string;
|
|
940
|
-
tokenOutAddress: string;
|
|
941
|
-
formattedAmountIn: string;
|
|
942
|
-
} {
|
|
943
|
-
const tokenInAddress = isNativeIn ? getNativeTokenAddress() : tokenIn;
|
|
944
|
-
const tokenOutAddress = isNativeOut ? getNativeTokenAddress() : tokenOut;
|
|
945
|
-
|
|
946
|
-
const formattedAmountIn = amountIn.includes('.')
|
|
947
|
-
? formatAmountToWei(amountIn, tokenInDecimals)
|
|
948
|
-
: amountIn;
|
|
949
|
-
|
|
950
|
-
return {
|
|
951
|
-
tokenInAddress,
|
|
952
|
-
tokenOutAddress,
|
|
953
|
-
formattedAmountIn
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
export function convertSlippageForDebonk(slippageBps: number): number {
|
|
958
|
-
return slippageBps / 100;
|
|
959
|
-
}
|
|
960
736
|
|
|
961
737
|
export const transformEVMNFTToUnified = (nft: EVMNFT): NFT => {
|
|
962
738
|
// Extract image URL from various sources
|