無氣
從 @kaiachain/web3js-ext 套件匯入 Web3 以在 web3js 上加入無氣功能
ERC20 代幣地址(Kairos 測試網的 TEST 代幣)。
宣告 senderAddr: 執行交換的寄件者的錢包地址,以及 senderPriv: 用於簽署交易的寄件者私密金鑰。
使用 Kaia Kairos 測試網路建立 JSON-RPC 提供者和 Wallet 的實例
從 ** 寄件者私人密碼匙宣告 ** 寄件者帳戶**
ERC20 代幣的最小 ABI,包括查詢小數、符號、免稅額和餘額的功能。
寄件者希望從交換中收到的固定金額 0.01 KAIA(Wei),以便為後續的應用程式交易提供資金。
在地址處顯示代幣:
建立 ERC20 代幣合約的實體,以與其功能互動。
查詢標記的符號 (例如:"TEST" 代表 TEST 標記)。
查詢代幣的小數位數 (例如,大多數 ERC20 代幣如 ETH 或 TEST 為 18)。
查詢寄件者的 ERC20 代幣餘額。
顯示寄件者的初始餘額。
擷取 GaslessSwapRouter 契約的實體,該契約可促進 Kaia 區塊鏈上的無瓦斯代幣交換。
擷取 GaslessSwapRouter 契約的區塊鏈位址。
檢查 GaslessSwapRouter 是否支援指定的 ERC20 令牌進 行無氣體交換。
擷取 GaslessSwapRouter 為促進無瓦斯交換而收取的佣金費率。
顯示 GaslessSwapRouter 位址、true(如果支援代幣)和傭金費率。
檢查 GaslessSwapRouter 允許代表寄件者花費多少代幣。
透過檢查備用金是否為零,決定是否需要進行核准交易。
初始化一個空陣列,用來儲存稍後傳送的交易。
根據 approveRequired 標誌檢查是否需要核准交易。 產生 ERC20 核准交易,允許 GaslessSwapRouter 使用寄件者的代幣。
從區塊鏈擷取目前的瓦斯價格,並將其轉換成 JavaScript 數字。
計算寄件者必須償還給區塊礦工的 KAIA 總金額 (單位:Wei),以支付交易的瓦斯費用。
計算掉期必須產生的最低 KAIA 金額(Wei),以支付還款、傭金和所需的申請交易費用。
計算要交換多少 ERC20 代幣才能至少收到 minAmountOut KAIA,並計入滑點。
檢查寄件者的 ERC20 代幣餘額是否不足以支付交換所需的代幣數量。
產生交換交易,以指定數量的 ERC20 代幣交換至少最低數量的 KAIA,並結合無瓦斯還款邏輯。 將 swap 交易加入 txs 陣列,以便批次執行。
使用寄件者的錢包傳送 txs 陣列中的所有交易。 迭代已傳送的交易,以記錄其詳細資訊。
將已簽署的交易陣列傳送至區塊鏈,並等待其交易收據。
列出 與寄件者相關的區塊交易
寄件人的最終結餘
const { Web3 } = require("@kaiachain/web3js-ext");// Replace with ERC20 token address to be spentconst tokenAddr = "0xcB00BA2cAb67A3771f9ca1Fa48FDa8881B457750"; // Kairos:TEST token// Replace with your wallet address and private keyconst senderAddr = "0x24e8efd18d65bcb6b3ba15a4698c0b0d69d13ff7";const senderPriv = "0x4a72b3d09c3d5e28e8652e0111f9c4ce252e8299aad95bb219a38eb0a3f4da49";const provider = new Web3.providers.HttpProvider("https://public-en-kairos.node.kaia.io");const web3 = new Web3(provider);const senderAccount = web3.eth.accounts.privateKeyToAccount(senderPriv);const ERC20_ABI = JSON.parse('[{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"}]');// senderAddr wants to swap the ERC20 token for at least 0.01 KAIA so she can execute the AppTx.async function main() { const appTxFee = web3.utils.toWei("0.01", "ether"); console.log(`Using token at address: ${tokenAddr}`); const token = new web3.eth.Contract(ERC20_ABI, tokenAddr); const tokenSymbol = await token.methods.symbol().call(); const tokenDecimals = parseInt(await token.methods.decimals().call()); const tokenBalance = await token.methods.balanceOf(senderAddr).call(); console.log(`\nInitial balance of the sender ${senderAddr}`); console.log(`- ${web3.utils.fromWei(await web3.eth.getBalance(senderAddr), "ether")} KAIA`); console.log(`- ${web3.utils.fromWei(tokenBalance, tokenDecimals)} ${tokenSymbol}`); const router = await web3.gasless.getGaslessSwapRouter(); const routerAddr = await router.options.address; const isTokenSupported = await router.methods.isTokenSupported(tokenAddr).call(); const commissionRate = await router.methods.commissionRate().call(); console.log(`\nGaslessSwapRouter address: ${routerAddr}`); console.log(`- The token is supported: ${isTokenSupported}`); console.log(`- Commission rate: ${commissionRate} bps`); // If sender hasn't approved, include ApproveTx first. const allowance = await token.methods.allowance(senderAddr, routerAddr).call(); const approveRequired = (allowance == 0n); const txs = []; if (approveRequired) { console.log("\nAdding ApproveTx because allowance is 0"); const approveTx = await web3.gasless.getApproveTx( senderAddr, tokenAddr, routerAddr, gasPrice, ); txs.push(approveTx); } else { console.log("\nNo ApproveTx needed"); } // - amountRepay (KAIA) is the cost of LendTx, ApproveTx, and SwapTx. The block miner shall fund it first, // then the sender has to repay from the swap output. // - minAmountOut (KAIA) is the required amount of the swap output. It must be enough to cover the amountRepay // and pay the commission, still leaving appTxFee. // - amountIn (token) is the amount of the token to be swapped to produce minAmountOut plus slippage. console.log(`\nCalculating the amount of the token to be swapped...`); const gasPrice = await web3.eth.getGasPrice(); console.log(`- gasPrice: ${web3.utils.fromWei(gasPrice, "gwei")} gkei`); const amountRepay = web3.gasless.getAmountRepay(approveRequired, gasPrice); console.log(`- amountRepay: ${web3.utils.fromWei(amountRepay, "ether")} KAIA`); const minAmountOut = web3.gasless.getMinAmountOut(amountRepay, appTxFee, commissionRate); console.log(`- minAmountOut: ${web3.utils.fromWei(minAmountOut, "ether")} KAIA`); const slippageBps = 50 // 0.5% const amountIn = await web3.gasless.getAmountIn(router, tokenAddr, minAmountOut, slippageBps); console.log(`- amountIn: ${web3.utils.fromWei(amountIn, tokenDecimals)} ${tokenSymbol}`); if (tokenBalance < amountIn) { console.log(`\nInsufficient balance of the token: ${web3.utils.fromWei(tokenBalance, tokenDecimals)} ${tokenSymbol}`); console.log(`- Please transfer more ${tokenSymbol} to the sender ${senderAddr}`); return; } const swapTx = await web3.gasless.getSwapTx( senderAddr, tokenAddr, routerAddr, amountIn, minAmountOut, amountRepay, gasPrice, approveRequired, ); txs.push(swapTx); console.log("\nSending transactions and waiting for them to be mined..."); const signedTxs = []; const txHashes = [] for (const tx of txs) { const signResult = await senderAccount.signTransaction(tx); signedTxs.push(signResult.rawTransaction); txHashes.push(signResult.transactionHash); console.log(`- Tx signed: (nonce: ${tx.nonce}) ${signResult.transactionHash} ${signResult.rawTransaction}`); } const receipts = await web3.eth.sendSignedTransactions(signedTxs); console.log("\nListing the block's transactions related to the sender..."); const block = await web3.eth.getBlock(receipts[0].blockNumber); const names = { [senderAddr.toLowerCase()]: "sender", [tokenAddr.toLowerCase()]: "token", [routerAddr.toLowerCase()]: "router", } for (const txhash of block.transactions) { const tx = await web3.eth.getTransaction(txhash); const fromName = names[tx.from.toLowerCase()] || tx.from; const toName = names[tx.to.toLowerCase()] || tx.to; if (fromName != tx.from || toName != tx.to) { console.log(`- Tx ${tx.hash}: ${fromName} => ${toName}`); } } console.log(`\nFinal balance of the sender ${senderAddr}`); console.log(`- ${web3.utils.fromWei(await web3.eth.getBalance(senderAddr), "ether")} KAIA`); console.log(`- ${web3.utils.fromWei(await token.methods.balanceOf(senderAddr).call(), tokenDecimals)} ${tokenSymbol}`);}main();
❯ node Gasless.jsUsing token at address: 0xcB00BA2cAb67A3771f9ca1Fa48FDa8881B457750Initial balance of the sender 0x24e8efd18d65bcb6b3ba15a4698c0b0d69d13ff7- 49.9969537425 KAIA- 3.0 TESTGaslessSwapRouter address: 0x4b41783732810b731569E4d944F59372F411BEa2- The token is supported: true- Commission rate: 0 bps- 3.0 TESTGaslessSwapRouter address: 0x4b41783732810b731569E4d944F59372F411BEa2- The token is supported: true- Commission rate: 0 bpsGaslessSwapRouter address: 0x4b41783732810b731569E4d944F59372F411BEa2- The token is supported: true- Commission rate: 0 bps- The token is supported: true- Commission rate: 0 bps- Commission rate: 0 bpsAdding ApproveTx because allowance is 0Calculating the amount of the token to be swapped...- gasPrice: 27.5 gkei- amountRepay: 0.0170775 KAIA- minAmountOut: 0.0270775 KAIA- amountIn: 0.027300931296609197 TESTSending transactions and waiting for them to be mined...- Tx signed: (nonce: 5) 0x1d2bd9e5ae11653b8cddb297d040074b189fe456ae986197c4c2bbf74a51e6ef- Tx signed: (nonce: 6) 0x96dd81962d7ae14baf31bd0a8afb900be1a6c9c8d30ad854011a4120fb0efbafListing the block's transactions related to the sender...- Tx 0x50769b14c7814f4e6b169f63595c1c19d9c46ce1c626c14b6b3c5610f691f58a: 0xB74Ff9DEa397fE9E231df545eb53fE2ADF776cb2 => sender- Tx 0x1d2bd9e5ae11653b8cddb297d040074b189fe456ae986197c4c2bbf74a51e6ef: sender => token- Tx 0x96dd81962d7ae14baf31bd0a8afb900be1a6c9c8d30ad854011a4120fb0efbaf: sender => routerFinal balance of the sender 0x24e8efd18d65bcb6b3ba15a4698c0b0d69d13ff7- 50.016275725 KAIA- 2.972699068703390803 TEST