想要超越交换和桥接? 借助 Composer,你可以使用与下面所示相同的 API 模式,在单笔交易中完成存入金库、质押和借贷等操作。参见 Composer 快速开始。
分步说明
1
请求报价或路由
import axios from 'axios';
const getQuote = async (
fromChain: number,
toChain: number,
fromToken: string,
toToken: string,
fromAmount: string,
fromAddress: string,
) => {
const result = await axios.get('https://li.quest/v1/quote', {
params: {
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
fromAddress,
},
});
return result.data;
};
const fromChain = 42161; // Arbitrum
const fromToken = 'USDC';
const toChain = 100; // Gnosis
const toToken = 'USDC';
const fromAmount = '1000000';
const fromAddress = '0xYOUR_WALLET_ADDRESS';
const quote = await getQuote(fromChain, toChain, fromToken, toToken, fromAmount, fromAddress);
2
如果使用了 /advanced/routes,则选择所需路由并从 /advanced/stepTransaction 获取交易数据
仅当使用了
/advanced/routes 端点时才需要此步骤。/quote 已经在响应中返回了交易数据。/quote 与 /advanced/routes 之间的区别在此处描述。3
设置授权额度
在发送任何交易之前,必须确保用户被允许从钱包中发送所请求的金额。本示例使用经典的
approve() 方式。若要通过链下 EIP-712 签名减少授权交易,参见 Permit 与 Permit2 授权流程。import { erc20Abi, zeroAddress, type Address } from 'viem';
import type { PublicClient, WalletClient } from 'viem';
// Get the current allowance and update it if needed
const checkAndSetAllowance = async (
publicClient: PublicClient,
walletClient: WalletClient,
tokenAddress: Address,
approvalAddress: Address,
amount: bigint,
) => {
// Transactions with the native token don't need approval
if (tokenAddress === zeroAddress) {
return;
}
const [account] = await walletClient.getAddresses();
const allowance = await publicClient.readContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'allowance',
args: [account, approvalAddress],
});
if (allowance < amount) {
const hash = await walletClient.writeContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'approve',
args: [approvalAddress, amount],
account,
chain: walletClient.chain,
});
await publicClient.waitForTransactionReceipt({ hash });
}
};
await checkAndSetAllowance(
publicClient,
walletClient,
quote.action.fromToken.address as Address,
quote.estimate.approvalAddress as Address,
BigInt(fromAmount),
);
4
发送交易
获取报价后,必须发送交易以触发转账。首先需要配置钱包。交易在源链上执行,因此以下示例将你的钱包连接到 Arbitrum:随后,可以使用先前获取的报价中的
import { createPublicClient, createWalletClient, http } from 'viem';
import { mnemonicToAccount } from 'viem/accounts';
import { arbitrum } from 'viem/chains';
const account = mnemonicToAccount('YOUR_PERSONAL_MNEMONIC');
const publicClient = createPublicClient({ chain: arbitrum, transport: http() });
const walletClient = createWalletClient({ account, chain: arbitrum, transport: http() });
transactionRequest 来发送交易:import type { Address, Hex } from 'viem';
const hash = await walletClient.sendTransaction({
to: quote.transactionRequest.to as Address,
data: quote.transactionRequest.data as Hex,
value: BigInt(quote.transactionRequest.value),
gas: BigInt(quote.transactionRequest.gasLimit),
gasPrice: BigInt(quote.transactionRequest.gasPrice),
});
await publicClient.waitForTransactionReceipt({ hash });
5
如适用,执行第二步
如果使用了两步路由,则必须在第一步完成后执行第二步。按照下一步所述获取第一步的状态,然后从
/advanced/stepTransaction 端点请求 transactionData。6
获取转账状态
若要检查代币是否已成功发送到接收链,可以调用 /status 端点:
const getStatus = async (
bridge: string,
fromChain: number,
toChain: number,
txHash: string,
) => {
const result = await axios.get('https://li.quest/v1/status', {
params: {
bridge,
fromChain,
toChain,
txHash,
},
});
return result.data;
};
const result = await getStatus(quote.tool, fromChain, toChain, hash);
完整示例
import axios from 'axios';
import {
createPublicClient,
createWalletClient,
erc20Abi,
http,
zeroAddress,
type Address,
type Hex,
} from 'viem';
import { mnemonicToAccount } from 'viem/accounts';
import { arbitrum } from 'viem/chains';
const API_URL = 'https://li.quest/v1';
// Get a quote for your desired transfer
const getQuote = async (
fromChain: number,
toChain: number,
fromToken: string,
toToken: string,
fromAmount: string,
fromAddress: string,
) => {
const result = await axios.get(`${API_URL}/quote`, {
params: {
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
fromAddress,
},
});
return result.data;
};
// Check the status of your transfer
const getStatus = async (
bridge: string,
fromChain: number,
toChain: number,
txHash: string,
) => {
const result = await axios.get(`${API_URL}/status`, {
params: {
bridge,
fromChain,
toChain,
txHash,
},
});
return result.data;
};
const fromChain: number = 42161; // Arbitrum
const fromToken = 'USDC';
const toChain: number = 100; // Gnosis
const toToken = 'USDC';
const fromAmount = '1000000';
// Set up your wallet on the source chain
const account = mnemonicToAccount('YOUR_PERSONAL_MNEMONIC');
const publicClient = createPublicClient({ chain: arbitrum, transport: http() });
const walletClient = createWalletClient({ account, chain: arbitrum, transport: http() });
// Get the current allowance and update it if needed
const checkAndSetAllowance = async (
tokenAddress: Address,
approvalAddress: Address,
amount: bigint,
) => {
// Transactions with the native token don't need approval
if (tokenAddress === zeroAddress) {
return;
}
const allowance = await publicClient.readContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'allowance',
args: [account.address, approvalAddress],
});
if (allowance < amount) {
const approveHash = await walletClient.writeContract({
address: tokenAddress,
abi: erc20Abi,
functionName: 'approve',
args: [approvalAddress, amount],
});
await publicClient.waitForTransactionReceipt({ hash: approveHash });
}
};
const run = async () => {
const quote = await getQuote(
fromChain,
toChain,
fromToken,
toToken,
fromAmount,
account.address,
);
await checkAndSetAllowance(
quote.action.fromToken.address as Address,
quote.estimate.approvalAddress as Address,
BigInt(fromAmount),
);
const hash = await walletClient.sendTransaction({
to: quote.transactionRequest.to as Address,
data: quote.transactionRequest.data as Hex,
value: BigInt(quote.transactionRequest.value),
gas: BigInt(quote.transactionRequest.gasLimit),
gasPrice: BigInt(quote.transactionRequest.gasPrice),
});
await publicClient.waitForTransactionReceipt({ hash });
// Only needed for cross chain transfers
if (fromChain !== toChain) {
let result;
do {
result = await getStatus(quote.tool, fromChain, toChain, hash);
if (result.status !== 'DONE' && result.status !== 'FAILED') {
await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5s
}
} while (result.status !== 'DONE' && result.status !== 'FAILED');
}
};
run().then(() => {
console.log('DONE!');
});

