本页面使用机器翻译自英语,可能包含错误或不清楚的语言。如需最准确的信息,请参阅英文原文。由于更新频繁,部分内容可能与英文原文有出入。请加入我们在 Crowdin 上的努力,帮助我们改进本页面的翻译。 (Crowdin translation page, Contributing guide)
将天然气自由美元兑美元纳入 KAIA 掉期交易
本指南概述了 Gas-Free USDT 到 KAIA Swap 功能,包括其目的、前提条件、集成步骤和 API 参考资料。 它旨在帮助开发人员将无气体交换功能集成到 Kaia 网络上的去中心化应用程序(DApps)中。
导言
推出了 "gasFreeSwapKaia "API,使用户能够在 Kaia 网络上执行无燃气的 ERC20 代币交换(目前仅限于 USDT),而无需持有 KAIA 代币支付燃气费,甚至无需代表用户支付交易成本。 该应用程序接口特别支持使用 ERC20 许可证签名将 USDT 交换为 KAIA,以实现完全无气体的用户体验。
益处
- 100%无气体验:用户无需任何 KAIA 代币即可进行交换
- 增强用户入职:新用户无需获取 KAIA 即可立即开始交换代币
- ERC20 许可证集成:使用标准 ERC20 许可证签名进行安全、无气体令牌审批
如何使用
- 用户启动交换:用户选择USDT金额交换KAIA
- 前端创建许可证:DApp 构建一个 ERC20 许可签名,供用户签署
- 用户签署许可证:用户签署许可信息(无需气体)
- DApp 调用 API:前端向应用程序接口发送交换参数和许可证签名
- 后台执行:API 验证许可证、执行交换并支付所有天然气费用
- 用户收到 KAIA:本地 KAIA 令牌直接发送到用户钱包
先决条件和支持的环境
服务端点
- Kaia Mainnet
- Kairos Testnet
支持的令牌对
应用程序接口目前仅支持单一交易对:
- Kaia Mainnet
- Kairos Testnet
代币输入: USDT (0xd077a400968890eacc75cdc901f0356c943e4fdb)
Token Out: WKAIA (`0x19aac5f612f524b754ca7e7c41cbfa2e981a4432`)
令牌输入: TEST (0xcb00ba2cab67a3771f9ca1fa48fda8881b457750)
Token Out: WKAIA (`0x043c471bEe060e00A56CcD02c0Ca286808a5A436`)
获取测试令牌
为 Kairos Testnet 获取 TEST 令牌:
- 在 Kaiascan 上打开 ERC20 水龙头
- 转到 "合同 "选项卡,然后选择 "撰写合同"。
- 找到 claim(token) 函数
- 输入 Kairos 上受支持的 GA 令牌的地址(本指南使用 TEST 的地址)
- 单击 Query 提交请求。 您将很快收到 TEST 令牌。

智能合同要求
该应用程序接口与 GaslessERC20PermitSwap 智能合约交互:
- 支持基于 ERC20 许可证的审批
- 与兼容 Uniswap V2 的 DEX 集成
- 自动将 WKAIA 转换为本地 KAIA
- 执行最大交换限制以确保安全
用户要求
在主网上,用户必须拥有零 KAIA 余额才能使用这项无气交换服务。 这一要求可确保只有真正需要进行无煤气交易的用户才能使用该服务。 在测试网络中,出于 测试目的,这一限制被放宽。
主网和测试网的最大交换金额均限制为 1 USDT。 因为该功能的目的是让用户获得足够的 KAIA 以开始他们在 Kaia Chain 上的体验。
整合步骤
完整集成示例
const { JsonRpcProvider, Wallet } = require('@kaiachain/ethers-ext/v6');const { Contract, parseUnits, formatUnits, Signature } = require('ethers');async function fetchJson(url, init) { if (typeof fetch !== 'undefined') { return fetch(url, init); } const { default: nodeFetch } = await import('node-fetch'); return nodeFetch(url, init);}const GASLESS_SWAP_ABI = [ 'function usdtToken() view returns (address)', 'function wkaiaToken() view returns (address)', 'function maxUsdtAmount() view returns (uint256)', 'function getExpectedOutput(address tokenIn, address tokenOut, uint256 amountIn) view returns (uint256)', 'function executeSwapWithPermit(address user, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s)',];const ERC20_METADATA_ABI = [ 'function decimals() view returns (uint8)', 'function symbol() view returns (string)', 'function name() view returns (string)', 'function nonces(address owner) view returns (uint256)', 'function balanceOf(address owner) view returns (uint256)',];async function buildPermitSignature({ token, owner, spender, value, deadline, domainVersion = '1' }) { const [name, version, network, verifyingContract, nonce] = await Promise.all([ token.name(), Promise.resolve(domainVersion), owner.provider.getNetwork(), token.getAddress(), token.nonces(owner.address), ]); const domain = { name, version, chainId: Number(network.chainId), verifyingContract, }; const types = { Permit: [ { name: 'owner', type: 'address' }, { name: 'spender', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'nonce', type: 'uint256' }, { name: 'deadline', type: 'uint256' }, ], }; const message = { owner: owner.address, spender, value, nonce, deadline, }; return Signature.from(await owner.signTypedData(domain, types, message));}async function executeGaslessSwap({ rpcUrl, serverUrl, userWallet, contractAddress, amountIn = '0.01', // Amount in USDT slippageBps = 50, // 0.5% slippage permitDeadlineSeconds = 600 // 10 minutes}) { console.log('🚀 Starting gasless swap'); const provider = new JsonRpcProvider(rpcUrl); const wallet = userWallet.connect(provider); const swap = new Contract(contractAddress, GASLESS_SWAP_ABI, provider); // Get token addresses from contract const [tokenInAddress, tokenOutAddress, maxUsdtAmount] = await Promise.all([ swap.usdtToken(), swap.wkaiaToken(), swap.maxUsdtAmount(), ]); const tokenIn = new Contract(tokenInAddress, ERC20_METADATA_ABI, provider); const tokenOut = new Contract(tokenOutAddress, ERC20_METADATA_ABI, provider); const [tokenInDecimals, tokenOutDecimals, tokenInSymbol, tokenOutSymbol] = await Promise.all([ tokenIn.decimals(), tokenOut.decimals(), tokenIn.symbol(), tokenOut.symbol(), ]); const amountInWei = parseUnits(amountIn, tokenInDecimals); // Check if amount exceeds contract maximum if (amountInWei > maxUsdtAmount) { throw new Error(`Amount (${amountIn} ${tokenInSymbol}) exceeds contract cap (${formatUnits(maxUsdtAmount, tokenInDecimals)} ${tokenInSymbol})`); } // Get expected output and calculate minimum with slippage const expectedOut = await swap.getExpectedOutput(tokenInAddress, tokenOutAddress, amountInWei); const amountOutMin = (expectedOut * BigInt(10_000 - slippageBps)) / 10_000n; // Create permit signature const deadline = BigInt(Math.floor(Date.now() / 1000) + permitDeadlineSeconds); const signature = await buildPermitSignature({ token: tokenIn, owner: wallet, spender: contractAddress, value: amountInWei, deadline, }); // Prepare API payload const payload = { swap: { user: wallet.address, tokenIn: tokenInAddress, tokenOut: tokenOutAddress, amountIn: amountInWei.toString(), amountOutMin: amountOutMin.toString(), deadline: deadline.toString(), },permitSignature: signature.serialized, }; console.log('From:', wallet.address); console.log('Swap amount:', formatUnits(amountInWei, tokenInDecimals), tokenInSymbol); console.log('Minimum out:', formatUnits(amountOutMin, tokenOutDecimals), tokenOutSymbol); // Check balance before swap const balanceBefore = await provider.getBalance(wallet.address); // Call the API const response = await fetchJson(`${serverUrl}/api/gasFreeSwapKaia`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const result = await response.json().catch(() => ({})); console.log('HTTP status:', response.status); console.log('Response:', JSON.stringify(result, null, 2)); if (response.ok && result.status) { console.log('🎉 Gasless swap request succeeded'); // Check balance after swap const balanceAfter = await provider.getBalance(wallet.address); console.log('Balance before:', formatUnits(balanceBefore, 18), 'KAIA'); console.log('Balance after:', formatUnits(balanceAfter, 18), 'KAIA'); console.log('Balance difference:', formatUnits(balanceAfter - balanceBefore, 18), 'KAIA'); return result; } else { console.error('❌ Gasless swap request failed'); throw new Error(`Swap failed: ${result.data || result.message || 'Unknown error'}`); }}// Usage exampleasync function main() { try { const userWallet = new Wallet('your_private_key'); const result = await executeGaslessSwap({ rpcUrl: 'https://public-en-kairos.node.kaia.io', serverUrl: 'https://fee-delegation-kairos.kaia.io', userWallet: userWallet, contractAddress: '0xaaFe47636ACe87E2B8CAaFADb03E87090277Ff7B', amountIn: '0.002', slippageBps: 50, }); console.log('Transaction hash:', result.data.hash); } catch (error) { console.error('💥 Swap failed:', error.message); }}main();
API 参考端点
- URL:
/api/gasFreeSwapKaia. - 方法邮寄
- Content-Type: 应用程序/json
申请正文
{ "swap": { "user": "0x742d35Cc6635C0532925a3b8D400e6D2A4b8E0bb", "tokenIn": "0xcb00ba2cab67a3771f9ca1fa48fda8881b457750", "tokenOut": "0x043c471bEe060e00A56CcD02c0Ca286808a5A436", "amountIn": "1000000", "amountOutMin": "950000000000000000", "deadline": "1699123456" }, "permitSignature": "0x…65-byte signature string…"}