pancakeswap-info.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import { MockUSDT__factory, ControlledERC20__factory } from "../typechain-types"
  2. import { IPancakeRouter02__factory, IPancakeFactory__factory, IPancakePair__factory } from "../typechain-types"
  3. import { config as dotenvConfig } from "dotenv"
  4. import { ethers } from "ethers"
  5. import fs from "fs"
  6. // BSC Testnet PancakeSwap V2 合约地址
  7. const PANCAKE_ROUTER_ADDRESS = "0xD99D1c33F9fC3444f8101754aBC46c52416550D1"
  8. const PANCAKE_FACTORY_ADDRESS = "0x6725F303b657a9451d8BA641348b6761A6CC7a17"
  9. const WBNB_ADDRESS = "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd"
  10. /**
  11. * @dev PancakeSwap信息查询脚本
  12. * 查询交易对的流动性、价格、用户余额等信息
  13. * 包含价格影响分析、LP代币信息、用户份额计算等功能
  14. */
  15. async function main() {
  16. dotenvConfig()
  17. const rpcUrl = process.env.BSC_TESTNET_RPC_URL
  18. const privateKey = process.env.PRIVATE_KEY
  19. if (!rpcUrl || !privateKey) {
  20. throw new Error("请在.env中配置BSC_TESTNET_RPC_URL和PRIVATE_KEY")
  21. }
  22. const provider = new ethers.JsonRpcProvider(rpcUrl)
  23. const wallet = new ethers.Wallet(privateKey, provider)
  24. console.log("📊 查询PancakeSwap信息...")
  25. console.log("📝 查询账户:", wallet.address)
  26. // 读取部署信息
  27. let deploymentInfo
  28. try {
  29. deploymentInfo = JSON.parse(fs.readFileSync("deployment-bsc-testnet.json", "utf8"))
  30. } catch (error) {
  31. console.error("❌ 无法读取部署信息文件,请先运行部署脚本")
  32. process.exit(1)
  33. }
  34. const mockUSDTAddress = deploymentInfo.contracts.mockUSDT
  35. const controlledERC20Address = deploymentInfo.contracts.controlledERC20
  36. console.log("📄 MockUSDT地址:", mockUSDTAddress)
  37. console.log("📄 ControlledERC20地址:", controlledERC20Address)
  38. // 获取合约实例
  39. const mockUSDT = MockUSDT__factory.connect(mockUSDTAddress, wallet)
  40. const controlledERC20 = ControlledERC20__factory.connect(controlledERC20Address, wallet)
  41. const pancakeRouter = IPancakeRouter02__factory.connect(PANCAKE_ROUTER_ADDRESS, wallet)
  42. const pancakeFactory = IPancakeFactory__factory.connect(PANCAKE_FACTORY_ADDRESS, wallet)
  43. // 检查交易对是否存在(如果不存在则提示先添加流动性)
  44. console.log("\n🔍 检查交易对...")
  45. const pairAddress = await pancakeFactory.getPair(mockUSDTAddress, controlledERC20Address)
  46. if (pairAddress === ethers.ZeroAddress) {
  47. console.log("❌ 交易对不存在,请先添加流动性")
  48. process.exit(1)
  49. }
  50. console.log("✅ 交易对地址:", pairAddress)
  51. // 获取交易对详细信息(储备量、代币地址等)
  52. const pair = IPancakePair__factory.connect(pairAddress, wallet)
  53. const [reserve0, reserve1, blockTimestampLast] = await pair.getReserves()
  54. const token0 = await pair.token0()
  55. const token1 = await pair.token1()
  56. // 确定哪个储备量对应哪个代币(PancakeSwap按地址排序)
  57. let mockUSDTReserve: bigint
  58. let controlledERC20Reserve: bigint
  59. let mockUSDTDecimals: number
  60. let controlledERC20Decimals: number
  61. if (token0.toLowerCase() === mockUSDTAddress.toLowerCase()) {
  62. mockUSDTReserve = reserve0
  63. controlledERC20Reserve = reserve1
  64. mockUSDTDecimals = 6
  65. controlledERC20Decimals = 18
  66. } else {
  67. mockUSDTReserve = reserve1
  68. controlledERC20Reserve = reserve0
  69. mockUSDTDecimals = 6
  70. controlledERC20Decimals = 18
  71. }
  72. // 显示流动性信息
  73. console.log("\n💧 流动性信息:")
  74. console.log("MockUSDT储备:", ethers.formatUnits(mockUSDTReserve, mockUSDTDecimals), "mUSDT")
  75. console.log("ControlledERC20储备:", ethers.formatUnits(controlledERC20Reserve, controlledERC20Decimals), "CTRL")
  76. // 计算代币价格(考虑小数位数差异)
  77. const price1 =
  78. (Number(controlledERC20Reserve) / Number(mockUSDTReserve)) *
  79. Math.pow(10, mockUSDTDecimals - controlledERC20Decimals)
  80. const price2 =
  81. (Number(mockUSDTReserve) / Number(controlledERC20Reserve)) *
  82. Math.pow(10, controlledERC20Decimals - mockUSDTDecimals)
  83. console.log("\n💰 价格信息:")
  84. console.log("1 mUSDT =", price1.toFixed(6), "CTRL")
  85. console.log("1 CTRL =", price2.toFixed(6), "mUSDT")
  86. // 获取LP代币总供应量
  87. const totalSupply = await pair.totalSupply()
  88. console.log("\n🪙 LP代币信息:")
  89. console.log("LP代币总供应量:", ethers.formatEther(totalSupply))
  90. // 检查用户LP代币余额
  91. const userLPBalance = await pair.balanceOf(wallet.address)
  92. if (userLPBalance > 0n) {
  93. console.log("用户LP代币余额:", ethers.formatEther(userLPBalance))
  94. const userShare = (Number(userLPBalance) / Number(totalSupply)) * 100
  95. console.log("用户份额:", userShare.toFixed(4) + "%")
  96. }
  97. // 模拟不同数量的交易(分析价格影响)
  98. console.log("\n📈 价格影响分析:")
  99. const testAmounts = [
  100. ethers.parseUnits("100", 6), // 100 mUSDT
  101. ethers.parseUnits("1000", 6), // 1000 mUSDT
  102. ethers.parseUnits("10000", 6), // 10000 mUSDT
  103. ]
  104. for (const amount of testAmounts) {
  105. try {
  106. const path = [mockUSDTAddress, controlledERC20Address]
  107. const amountsOut = await pancakeRouter.getAmountsOut(amount, path)
  108. const priceImpact = ((Number(amount) - Number(amountsOut[1]) * price1) / Number(amount)) * 100
  109. console.log(
  110. `${ethers.formatUnits(amount, 6)} mUSDT -> ${ethers.formatUnits(amountsOut[1], 18)} CTRL (价格影响: ${priceImpact.toFixed(2)}%)`
  111. )
  112. } catch (error) {
  113. console.log(`${ethers.formatUnits(amount, 6)} mUSDT -> 交易失败 (流动性不足)`)
  114. }
  115. }
  116. // 反向交易测试
  117. console.log("\n📉 反向价格影响分析:")
  118. const testAmountsReverse = [
  119. ethers.parseEther("100"), // 100 CTRL
  120. ethers.parseEther("1000"), // 1000 CTRL
  121. ethers.parseEther("10000"), // 10000 CTRL
  122. ]
  123. for (const amount of testAmountsReverse) {
  124. try {
  125. const path = [controlledERC20Address, mockUSDTAddress]
  126. const amountsOut = await pancakeRouter.getAmountsOut(amount, path)
  127. const priceImpact = ((Number(amount) - Number(amountsOut[1]) * price2) / Number(amount)) * 100
  128. console.log(
  129. `${ethers.formatUnits(amount, 18)} CTRL -> ${ethers.formatUnits(amountsOut[1], 6)} mUSDT (价格影响: ${priceImpact.toFixed(2)}%)`
  130. )
  131. } catch (error) {
  132. console.log(`${ethers.formatUnits(amount, 18)} CTRL -> 交易失败 (流动性不足)`)
  133. }
  134. }
  135. // 获取用户代币余额
  136. const userMockUSDTBalance = await mockUSDT.balanceOf(wallet.address)
  137. const userControlledERC20Balance = await controlledERC20.balanceOf(wallet.address)
  138. console.log("\n👤 用户余额:")
  139. console.log("MockUSDT余额:", ethers.formatUnits(userMockUSDTBalance, 6), "mUSDT")
  140. console.log("ControlledERC20余额:", ethers.formatEther(userControlledERC20Balance), "CTRL")
  141. // 保存信息到文件
  142. const info = {
  143. pairAddress: pairAddress,
  144. reserves: {
  145. mockUSDT: mockUSDTReserve.toString(),
  146. controlledERC20: controlledERC20Reserve.toString(),
  147. },
  148. prices: {
  149. mUSDTtoCTRL: price1,
  150. CTRLtoMUSDT: price2,
  151. },
  152. totalSupply: totalSupply.toString(),
  153. userLPBalance: userLPBalance.toString(),
  154. userBalances: {
  155. mockUSDT: userMockUSDTBalance.toString(),
  156. controlledERC20: userControlledERC20Balance.toString(),
  157. },
  158. timestamp: new Date().toISOString(),
  159. }
  160. fs.writeFileSync("pancakeswap-info.json", JSON.stringify(info, null, 2))
  161. console.log("\n💾 信息已保存到 pancakeswap-info.json")
  162. console.log("\n🔗 PancakeSwap链接:")
  163. console.log("交易页面: https://testnet.pancakeswap.finance/swap")
  164. console.log("流动性页面: https://testnet.pancakeswap.finance/liquidity")
  165. }
  166. main()
  167. .then(() => process.exit(0))
  168. .catch((error) => {
  169. console.error("❌ 查询失败:", error)
  170. process.exit(1)
  171. })