123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- import "dotenv/config"
- import { ethers } from "ethers"
- import * as fs from "fs"
- import * as path from "path"
- import { StandardMerkleTree } from "@openzeppelin/merkle-tree"
- import { MockUSD1__factory, Launchpad__factory } from "../typechain-types/factories/contracts"
- import type { MockUSD1, Launchpad } from "../typechain-types/contracts"
- async function main() {
- // 获取环境配置
- const environment = process.env.ENVIRONMENT || "bsctest"
- const rpcUrl = process.env.RPC_URL || "https://data-seed-prebsc-1-s1.binance.org:8545/"
- const outputDir = process.env.OUTPUT_DIR || "./output/bsctest"
-
- console.log(`🚀 第4步:测试用户贡献...`)
- console.log(`🌍 环境: ${environment}`)
- console.log(`🔗 RPC: ${rpcUrl}`)
- console.log(`📁 输出目录: ${outputDir}`)
- // 检查环境变量
- if (!process.env.PRIVATE_KEY) {
- console.error("❌ 需要设置 PRIVATE_KEY 环境变量")
- process.exit(1)
- }
- // 从之前的步骤加载数据
- const step2DataPath = path.join(outputDir, "step2-data.json")
- if (!fs.existsSync(step2DataPath)) {
- console.error(`❌ 未找到 ${step2DataPath} 文件。请先运行第3步。`)
- process.exit(1)
- }
- const step2Data = JSON.parse(fs.readFileSync(step2DataPath, "utf8"))
- console.log("📋 已从之前的步骤加载数据")
- // 设置提供者
- const provider = new ethers.JsonRpcProvider(rpcUrl)
- const deployer = new ethers.Wallet(process.env.PRIVATE_KEY, provider)
- // 连接到合约
- const mockUSD1 = MockUSD1__factory.connect(step2Data.mockUSD1, deployer)
- const launchpad = Launchpad__factory.connect(step2Data.launchpad, deployer)
- // 从JSON文件加载测试账户
- const testAccountsPath = "./scripts/testAccount/BLaunchpadTest.json"
- const testAccounts = JSON.parse(fs.readFileSync(testAccountsPath, "utf8"))
- // 从加载的账户创建测试用户(所有用户)
- const testUsers = testAccounts.map((account: any) => new ethers.Wallet(account.privateKey, provider))
- // 根据场景确定参与用户数量
- const scenario = process.env.SCENARIO || "0" // 默认为认购不足
- const target = BigInt(step2Data.saleParams.contributionTarget) // contributionTarget 已经是 wei 值
- const maxContributionPerUser = ethers.parseEther("10000") // 每个用户最大10K USD1
- const usersNeededForTarget = Number(target) / Number(maxContributionPerUser) // 达到目标需要的用户数
- console.log(`📊 目标: ${ethers.formatEther(target)} USD1`)
- console.log(`📊 每个用户最大: ${ethers.formatEther(maxContributionPerUser)} USD1`)
- console.log(`📊 需要用户数: ${usersNeededForTarget}`)
- let participatingUsers: ethers.Wallet[]
- if (scenario === "1") {
- // 超额认购:使用超过目标所需的用户数量
- participatingUsers = testUsers.slice(0, Math.ceil(usersNeededForTarget) + 1) // 6个用户 (5 + 1)
- console.log(`\n🎯 场景:超额认购 (${participatingUsers.length} 个用户参与,目标需要: ${usersNeededForTarget} 个用户)`)
- } else {
- // 认购不足:使用少于目标所需的用户数量
- participatingUsers = testUsers.slice(0, Math.floor(usersNeededForTarget) - 1) // 4个用户 (5 - 1)
- console.log(`\n🎯 场景:认购不足 (${participatingUsers.length} 个用户参与,目标需要: ${usersNeededForTarget} 个用户)`)
- }
- console.log(`📋 ${participatingUsers.length} 个用户将参与销售`)
- // 重新创建Merkle树
- const tree = StandardMerkleTree.of(step2Data.whitelistData, ["address", "uint256"])
- // 检查销售是否已开始
- // 在 Hardhat 环境下使用区块链时间,在真实网络下使用系统时间
- const isHardhat = rpcUrl.includes("127.0.0.1") || rpcUrl.includes("localhost") || rpcUrl.includes("8545")
- let currentTime: number
-
- if (isHardhat) {
- // 在 Hardhat 环境下使用区块链时间
- const blockNumber = await provider.getBlockNumber()
- const block = await provider.getBlock(blockNumber)
- currentTime = block?.timestamp || Math.floor(Date.now() / 1000)
- } else {
- // 在真实网络下使用系统时间
- currentTime = Math.floor(Date.now() / 1000)
- }
-
- const saleStartTime = step2Data.saleParams.startTime
- if (currentTime < saleStartTime) {
- console.log(`\n⏰ 销售尚未开始。当前时间: ${new Date(currentTime * 1000).toLocaleString()}`)
- console.log(`⏰ 销售开始时间: ${new Date(saleStartTime * 1000).toLocaleString()}`)
-
- // 在 Hardhat 环境下自动快进时间
- if (isHardhat) {
- const timeToAdvance = saleStartTime - currentTime + 60 // 快进到开始时间后1分钟
- console.log(`⏰ 在 Hardhat 环境下快进时间 ${timeToAdvance} 秒...`)
- await provider.send("evm_increaseTime", [timeToAdvance])
- await provider.send("evm_mine", [])
-
- // 获取新的当前时间
- const newBlockNumber = await provider.getBlockNumber()
- const newBlock = await provider.getBlock(newBlockNumber)
- const newCurrentTime = newBlock?.timestamp || Math.floor(Date.now() / 1000)
- console.log(`✅ 时间已快进到: ${new Date(newCurrentTime * 1000).toLocaleString()}`)
- } else {
- console.log(`⏰ 等待 ${saleStartTime - currentTime} 秒...`)
- console.log("⏰ 您可以修改 step2-data.json 中的 startTime 为当前时间以立即测试")
- return
- }
- }
- console.log("\n💰 测试用户贡献...")
- // 每个用户贡献他们的最大允许金额
- const contributionPerUser = ethers.parseEther("10000") // 每个用户10K USD1
- for (let i = 0; i < participatingUsers.length; i++) {
- const user = participatingUsers[i]
- console.log(`\n👤 用户${i + 1} (${user.address}) 贡献 ${ethers.formatEther(contributionPerUser)} USD1...`)
- // 在白名单中找到用户的索引
- const userIndex = step2Data.testUsers.indexOf(user.address)
- if (userIndex === -1) {
- console.error(`❌ 用户 ${user.address} 在白名单中未找到`)
- continue
- }
- const userProof = tree.getProof(userIndex)
- try {
- // 获取用户当前nonce
- const userNonce = await provider.getTransactionCount(user.address)
- console.log(`📋 用户${i + 1} 当前nonce: ${userNonce}`)
- // 批准USD1支出
- await mockUSD1.connect(user).approve(step2Data.launchpad, contributionPerUser, { nonce: userNonce })
- console.log(`✅ 用户${i + 1} 已批准USD1支出`)
- // 贡献
- await launchpad.connect(user).contributeWithERC20(
- contributionPerUser,
- contributionPerUser, // 最大金额(与贡献相同)
- userProof,
- { nonce: userNonce + 1 }
- )
- console.log(`✅ 用户${i + 1} 贡献了 ${ethers.formatEther(contributionPerUser)} USD1`)
- } catch (error) {
- console.error(`❌ 用户${i + 1} 贡献失败:`, error)
- }
- }
- // 检查总贡献
- const totalContributed = await launchpad.totalContributionAmount()
- console.log("\n📊 总贡献:", ethers.formatEther(totalContributed), "USD1")
- // 检查销售是否超额认购
- const isOversubscribed = totalContributed > target
- console.log("\n📋 销售状态:")
- console.log("- 目标:", ethers.formatEther(target), "USD1")
- console.log("- 总贡献:", ethers.formatEther(totalContributed), "USD1")
- console.log("- 状态:", isOversubscribed ? "超额认购" : "认购不足")
- // 检查个别用户贡献
- console.log("\n📋 个别贡献:")
- for (let i = 0; i < Math.min(5, participatingUsers.length); i++) {
- const user = participatingUsers[i]
- const userContributed = await launchpad.userContributionAmount(user.address)
- console.log(`- 用户${i + 1}: ${ethers.formatEther(userContributed)} USD1`)
- }
- if (participatingUsers.length > 5) {
- console.log(`- ... 还有 ${participatingUsers.length - 5} 个用户`)
- }
- // 保存贡献数据
- const contributionData = {
- ...step2Data,
- contributions: {
- total: totalContributed.toString(),
- participatingUsers: participatingUsers.map(user => user.address),
- participatingUsersCount: participatingUsers.length,
- isOversubscribed: isOversubscribed,
- },
- }
- const step3DataPath = path.join(outputDir, "step3-data.json")
- fs.writeFileSync(step3DataPath, JSON.stringify(contributionData, null, 2))
- console.log("\n🎉 贡献测试成功完成!")
- console.log(`\n📋 场景: ${isOversubscribed ? "超额认购" : "认购不足"}`)
- console.log("📋 下一步:运行 'npm run 05:claim' 在销售结束后测试领取")
- }
- // 导出main函数供多环境脚本调用
- export { main }
- // 如果直接运行此脚本
- if (require.main === module) {
- main()
- .then(() => process.exit(0))
- .catch((error) => {
- console.error("❌ Error:", error)
- process.exit(1)
- })
- }
|