Liquidity Pools 流动性资金池
用户向流动性资金池中注入流动性时(例:同时注入 $CAKE 和 $BNB ),会同时获得对应的 $LP (例:$CAKE-BNB-LP)。
用户的 $LP 将用于确认在对应流动池中的份额,可随时通过移除流动性来赎回质押的两种代币。
创建流动性资金池
初始注入的流动性资金池将决定该交易对当前的最低价格。
注入流动性
后续注入的流动性,将受到当时的最低价格影响,若其中一种币的数量不足,将无法注入流动性。
获利方式
用户通过流动性资金池进行交易时,会产生 0.25% 的交易手续费,其中 0.17% 会流入流动性资金池。
不稳定性
质押资金池受交易对双币种影响,有可能面临币价影响而遭受损失。
Exchange 交易
pancakeSwap 中的流动性注入、以及交易都是通过 智能合约 实现的。
最主要的交易合约包括:
PanckaeFactory 工厂
合约地址: 0xca143ce32fe78f1f7019d7d551a6402fc5350c73 Bsc scan: https://bscscan.com/address/0x73feaa1ee314f8c655e354234017be2193c9e24e
getPair 获取交易对:
根据 tokenA 和 tokenB 的地址 匹配对应交易对,如果不存在则返回
0x0000000000000000000000000000000000
。function getPair(address tokenA, address tokenB) external view returns (address pair);
PanckaeRouter 路由
合约地址: 0x10ed43c718714eb63d5aa57b78b54704e256024e Bscscan: https://bscscan.com/address/0x10ed43c718714eb63d5aa57b78b54704e256024e
getAmountsOut 获取输出金额:
通过输入金额,预估输出金额
function getAmountsOut(uint amountIn, address[] memory path) internal view returns (uint[] memory amounts);
swapExactTokensForTokensSupportingFeeOnTransferTokens:
通过输入金额,输出不确定金额
function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline )
swapTokensForExactTokens:
需要获取精确的输出,输入不确定金额(也可以理解买,如购买100个UNI TOKEN,需要未知weth)
function swapTokensForExactTokens( uint amountOut,//期望输出金额 uint amountInMax,//最大输入 (如果到你打包的交易时,如果实际需要输入的金额大于该金额,交易失败!) address[] calldata path, address to, uint deadline )
swapExactTokensForTokens:
通过输入金额,输出不确定金额 (也可以理解为卖, 如卖掉100个UNI TOKEN,可以获得未知WETH)
function swapExactTokensForTokens( uint amountIn,//实际输入金额 uint amountOutMin,//最小输出 (如果到打包你的交易时,实际输出小于该金额,交易失败!) address[] calldata path, address to, uint deadline )
交易步骤
1.确定交易合约
用户选择需要交易的 tokenA → tokenB,以及其中一个确定的数额 amountIn 。
2. 获取交易对
连接 pancakeFactory 合约,调用合约的
getPair
方法,获取对应 pair
合约。// 连接合约 const factory = new ethers.Contract( '0xca143ce32fe78f1f7019d7d551a6402fc5350c73', ["function getPair(address tokenA, address tokenB) external view returns (address pair)"], account ) // 获取交易对 const pairAddress = await factory.getPair('tokenA address','tokenB address'); // 可能会存在交易对不存在的情况 这里要做出处理 // 或者使用中间币交易
3. 预估交易结果
并通过实时币价计算预估交易结果,通过用户滑点设置计算 amountOutMin 。
const router = new ethers.Contract( data.router, [ 'function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)', 'function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)', 'function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)' ], account ); const amounts = await router.getAmountsOut(amountIn, [tokenIn, tokenOut]); const amountOutMin = amounts * ( 1 - slippage);
4. 获取流动性
如果存在交易对,需要通过返回的addres查询交易对下的 tokenB 的数量是否满足用户需要兑换的数量
// 连接 tokenB 合约 const tokenBContract = new ethers.Contract( 'tokenB address', 'tokenB ABI', account ); // 获取 tokenB 在交易对中的数额 const balance = tokenBContract.balanceOf(pairAddress); // 判断 balance 是否大于 amountOutMin // 否则提示 流动性不足
5. approve 授权
调用 tokenA 合约的
approve
方法,授权router合约花费数额。function approve( address spender, uint value // 数额 ) external returns (bool);
6. 兑换
当流动性充足时,根据具体的交易情况调用
router
合约的交易方法。当不存在流动性时,可以通过 $BNB 作为中间币种,进行多路径交易。
注意: 这些数字的单位均为 wei
// 发起兑换 const tx = await router.swapExactTokensForTokens( amountIn, amountOutMin, [tokenIn, tokenOut], // 或者中间增加一个token toAddress, // 接受tokenB的账户地址 Date.now() + 1000 * 60 * 5, // 交易超时失败事件 { 'gasLimit': data.gasLimit, 'gasPrice': data.gasPrice, 'nonce ' : null // 设定购买区块位置 }); // 等待兑换结果 const receipt = await tx.wait(); console.log(`Transaction receipt : https://www.bscscan.com/tx/${receipt.logs[1].transactionHash}`);