目 录CONTENT

文章目录

测试网部署出入金合约

懿曲折扇情
2026-01-31 / 0 评论 / 0 点赞 / 24 阅读 / 3,356 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2026-01-31,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
广告 广告

测试网部署入金,出金合约

合约代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title DepositWithdraw
 * @dev 一个简单的充值和提款合约
 * 用户可以存入 ETH,也可以提取他们存入的 ETH
 */
contract DepositWithdraw {
    // 合约所有者
    address public owner;
    
    // 存储每个地址的余额
    mapping(address => uint256) public balances;
    
    // 存储每个地址的总存款金额
    mapping(address => uint256) public totalDeposits;
    
    // 存储每个地址的总提款金额
    mapping(address => uint256) public totalWithdrawals;
    
    // 事件:存款
    event Deposit(address indexed user, uint256 amount, uint256 timestamp);
    
    // 事件:提款
    event Withdraw(address indexed user, uint256 amount, uint256 timestamp);
    
    // 修饰符:只有所有者可以调用
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can call this function");
        _;
    }
    
    // 构造函数
    constructor() {
        owner = msg.sender;
    }
    
    /**
     * @dev 充值函数 - 接收 ETH
     */
    function deposit() public payable {
        require(msg.value > 0, "Deposit amount must be greater than 0");
        
        balances[msg.sender] += msg.value;
        totalDeposits[msg.sender] += msg.value;
        
        emit Deposit(msg.sender, msg.value, block.timestamp);
    }
    
    /**
     * @dev 提款函数 - 提取 ETH
     * @param amount 要提取的金额(单位:wei)
     */
    function withdraw(uint256 amount) public {
        require(amount > 0, "Withdrawal amount must be greater than 0");
        require(balances[msg.sender] >= amount, "Insufficient balance");
        
        balances[msg.sender] -= amount;
        totalWithdrawals[msg.sender] += amount;
        
        // 转账 ETH 给用户
        (bool success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "Transfer failed");
        
        emit Withdraw(msg.sender, amount, block.timestamp);
    }
    
    /**
     * @dev 提取全部余额
     */
    function withdrawAll() public {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance to withdraw");
        
        withdraw(amount);
    }
    
    /**
     * @dev 查询用户余额
     * @param user 用户地址
     * @return 用户余额
     */
    function getBalance(address user) public view returns (uint256) {
        return balances[user];
    }
    
    /**
     * @dev 查询合约总余额
     * @return 合约总余额
     */
    function getContractBalance() public view returns (uint256) {
        return address(this).balance;
    }
    
    /**
     * @dev 接收 ETH 的回退函数(也可以用于充值)
     */
    receive() external payable {
        deposit();
    }
    
    /**
     * @dev 回退函数
     */
    fallback() external payable {
        deposit();
    }
}

部署代码

const hre = require("hardhat");

// 重试函数
async function retryOperation(operation, maxRetries = 3, delay = 5000) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await operation();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      console.log(`\n⚠️  尝试 ${i + 1}/${maxRetries} 失败,${delay / 1000} 秒后重试...`);
      console.log(`错误信息: ${error.message}`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// 测试 RPC 连接
async function testRpcConnection() {
  const networkName = hre.network.name;
  const currentUrl = hre.network.config.url;
  
  console.log("🔍 测试 RPC 连接...");
  console.log(`   当前 RPC: ${currentUrl}`);
  
  try {
    // 设置超时
    const timeoutPromise = new Promise((_, reject) => 
      setTimeout(() => reject(new Error("连接超时(30秒)")), 30000)
    );
    
    const blockNumberPromise = hre.ethers.provider.getBlockNumber();
    const blockNumber = await Promise.race([blockNumberPromise, timeoutPromise]);
    
    console.log(`✅ RPC 连接成功,当前区块: ${blockNumber}`);
    return true;
  } catch (error) {
    console.error(`❌ RPC 连接失败: ${error.message}`);
    
    // 提供备用 RPC URL 建议
    const sepoliaAlternatives = [
      "https://ethereum-sepolia-rpc.publicnode.com",
      "https://sepolia.drpc.org",
      "https://rpc2.sepolia.org"
    ];
    
    const goerliAlternatives = [
      "https://ethereum-goerli-rpc.publicnode.com",
      "https://rpc.ankr.com/eth_goerli"
    ];
    
    const alternatives = networkName === "sepolia" ? sepoliaAlternatives : goerliAlternatives;
    
    console.error("\n💡 建议尝试以下操作:");
    console.error("   1. 检查网络连接");
    console.error("   2. 在 .env 文件中设置备用 RPC URL:");
    if (networkName === "sepolia") {
      console.error(`      SEPOLIA_RPC_URL=${alternatives[0]}`);
    } else if (networkName === "goerli") {
      console.error(`      GOERLI_RPC_URL=${alternatives[0]}`);
    }
    console.error("   3. 或使用 Infura/Alchemy 的 RPC URL(更稳定)");
    console.error("      - Infura: https://infura.io (免费注册)");
    console.error("      - Alchemy: https://alchemy.com (免费注册)");
    
    throw new Error("无法连接到 RPC 节点");
  }
}

async function main() {
  console.log("🚀 开始部署合约...\n");
  
  // 测试 RPC 连接
  await testRpcConnection();
  
  // 获取部署账户
  const [deployer] = await hre.ethers.getSigners();
  console.log("📝 部署账户地址:", deployer.address);
  
  // 检查账户余额
  let balance;
  try {
    balance = await hre.ethers.provider.getBalance(deployer.address);
    console.log("💰 账户余额:", hre.ethers.formatEther(balance), "ETH");
    
    if (balance < hre.ethers.parseEther("0.01")) {
      console.warn("⚠️  警告: 账户余额可能不足以支付 gas 费用");
      console.warn("   建议至少准备 0.01 ETH 用于部署");
    }
  } catch (error) {
    console.error("❌ 无法获取账户余额:", error.message);
    throw error;
  }
  
  // 部署合约(带重试机制)
  console.log("\n📦 正在部署 DepositWithdraw 合约...");
  
  let contract;
  let contractAddress;
  
  try {
    const DepositWithdraw = await hre.ethers.getContractFactory("DepositWithdraw");
    
    // 使用重试机制部署
    const deploymentResult = await retryOperation(async () => {
      console.log("   发送部署交易...");
      const contractInstance = await DepositWithdraw.deploy();
      console.log("   等待部署确认...");
      await contractInstance.waitForDeployment();
      return contractInstance;
    }, 3, 10000); // 最多重试 3 次,每次间隔 10 秒
    
    contract = deploymentResult;
    contractAddress = await contract.getAddress();
    
    console.log("✅ 合约部署成功!");
    console.log("📍 合约地址:", contractAddress);
    console.log("🌐 部署网络:", hre.network.name);
    
  } catch (error) {
    console.error("\n❌ 部署失败:", error.message);
    if (error.message.includes("timeout") || error.message.includes("Headers Timeout")) {
      console.error("\n💡 建议:");
      console.error("   1. 检查网络连接");
      console.error("   2. 尝试更换 RPC URL(在 .env 文件中设置 SEPOLIA_RPC_URL)");
      console.error("   3. 稍后重试");
    }
    throw error;
  }
  
  // 等待区块确认
  if (hre.network.name !== "hardhat") {
    try {
      console.log("\n⏳ 等待区块确认...");
      const deploymentTx = contract.deploymentTransaction();
      if (deploymentTx) {
        await deploymentTx.wait(2); // 等待 2 个区块确认
        console.log("✅ 已确认 2 个区块");
      }
    } catch (error) {
      console.warn("⚠️  等待确认时出错(合约可能已部署):", error.message);
    }
  }
  
  // 验证合约信息
  try {
    console.log("\n🔍 验证合约信息...");
    const owner = await contract.owner();
    console.log("👤 合约所有者:", owner);
    
    const contractBalance = await hre.ethers.provider.getBalance(contractAddress);
    console.log("💵 合约余额:", hre.ethers.formatEther(contractBalance), "ETH");
  } catch (error) {
    console.warn("⚠️  验证合约信息时出错:", error.message);
  }
  
  // 输出部署摘要
  console.log("\n" + "=".repeat(60));
  console.log("📋 部署信息摘要");
  console.log("=".repeat(60));
  console.log("合约地址:", contractAddress);
  console.log("网络:", hre.network.name);
  console.log("部署账户:", deployer.address);
  console.log("=".repeat(60));
  
  // 如果在测试网,提供 Etherscan 链接
  if (hre.network.name === "sepolia") {
    console.log("\n🔍 在 Etherscan 查看合约:");
    console.log(`   https://sepolia.etherscan.io/address/${contractAddress}`);
  } else if (hre.network.name === "goerli") {
    console.log("\n🔍 在 Etherscan 查看合约:");
    console.log(`   https://goerli.etherscan.io/address/${contractAddress}`);
  }
  
  console.log("\n✅ 部署完成!");
  console.log("\n💡 下一步:");
  console.log("   1. 保存合约地址:", contractAddress);
  console.log("   2. 运行测试脚本: npx hardhat run scripts/test.js --network " + hre.network.name);
  console.log("   3. 在 Etherscan 上查看合约详情");
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error("\n❌ 部署失败:", error.message);
    if (error.stack) {
      console.error("\n详细错误信息:");
      console.error(error.stack);
    }
    process.exit(1);
  });

测试代码

const hre = require("hardhat");

async function main() {
  // ⚠️ 请替换为你的合约地址
  const contractAddress = process.env.CONTRACT_ADDRESS || "0xef610Fc6C8eF7f42B387E9e0961A4790563a3416";
  
  if (contractAddress === "YOUR_CONTRACT_ADDRESS_HERE") {
    console.error("❌ 请先设置合约地址!");
    console.log("方法 1: 设置环境变量 CONTRACT_ADDRESS");
    console.log("方法 2: 编辑此文件,替换 contractAddress 变量");
    process.exit(1);
  }
  
  const [signer] = await hre.ethers.getSigners();
  console.log("当前账户:", signer.address);
  
  // 检查账户余额
  const accountBalance = await hre.ethers.provider.getBalance(signer.address);
  console.log("账户余额:", hre.ethers.formatEther(accountBalance), "ETH");
  
  const DepositWithdraw = await hre.ethers.getContractFactory("DepositWithdraw");
  const contract = DepositWithdraw.attach(contractAddress);
  
  console.log("\n连接到合约:", contractAddress);
  
  // 查询当前余额
  const currentBalance = await contract.getBalance(signer.address);
  console.log("合约中的余额:", hre.ethers.formatEther(currentBalance), "ETH");
  
  // 充值测试
  console.log("\n📥 测试充值功能...");
  const depositAmount = hre.ethers.parseEther("0.01");
  console.log("充值金额: 0.01 ETH");
  
  try {
    const depositTx = await contract.deposit({ value: depositAmount });
    console.log("交易哈希:", depositTx.hash);
    await depositTx.wait();
    console.log("✅ 充值成功!");
    
    // 查询新余额
    const newBalance = await contract.getBalance(signer.address);
    console.log("充值后余额:", hre.ethers.formatEther(newBalance), "ETH");
    
    // 提款测试
    console.log("\n📤 测试提款功能...");
    const withdrawAmount = hre.ethers.parseEther("0.005");
    console.log("提款金额: 0.005 ETH");
    
    const withdrawTx = await contract.withdraw(withdrawAmount);
    console.log("交易哈希:", withdrawTx.hash);
    await withdrawTx.wait();
    console.log("✅ 提款成功!");
    
    // 查询最终余额
    const finalBalance = await contract.getBalance(signer.address);
    console.log("提款后余额:", hre.ethers.formatEther(finalBalance), "ETH");
    
    // 查询合约总余额
    const contractBalance = await contract.getContractBalance();
    console.log("合约总余额:", hre.ethers.formatEther(contractBalance), "ETH");
    
    // 查询总存款和总提款
    const totalDeposits = await contract.totalDeposits(signer.address);
    const totalWithdrawals = await contract.totalWithdrawals(signer.address);
    console.log("\n📊 统计信息:");
    console.log("总存款:", hre.ethers.formatEther(totalDeposits), "ETH");
    console.log("总提款:", hre.ethers.formatEther(totalWithdrawals), "ETH");
    
  } catch (error) {
    console.error("❌ 操作失败:", error.message);
    if (error.message.includes("insufficient funds")) {
      console.log("💡 提示: 账户余额不足,请确保有足够的 ETH 支付 gas 费用");
    }
  }
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

部署结果

> deposit-withdraw-contract@1.0.0 deploy:sepolia
> hardhat run scripts/deploy.js --network sepolia

WARNING: You are currently using Node.js v25.4.0, which is not supported by Hardhat. This can lead to unexpected behavior. See https://v2.hardhat.org/nodejs-versions
🚀 开始部署合约...

🔍 测试 RPC 连接...
   当前 RPC: https://ethereum-sepolia-rpc.publicnode.com
✅ RPC 连接成功,当前区块: 10156419
📝 部署账户地址: 0x41a886A528bBD09A4Fe0ED51e345781dc4D087c6
💰 账户余额: 0.05 ETH

📦 正在部署 DepositWithdraw 合约...
   发送部署交易...
   等待部署确认...
✅ 合约部署成功!
📍 合约地址: 0xef610Fc6C8eF7f42B387E9e0961A4790563a3416
🌐 部署网络: sepolia

⏳ 等待区块确认...
✅ 已确认 2 个区块

🔍 验证合约信息...
👤 合约所有者: 0x41a886A528bBD09A4Fe0ED51e345781dc4D087c6
💵 合约余额: 0.0 ETH

============================================================
📋 部署信息摘要
============================================================
合约地址: 0xef610Fc6C8eF7f42B387E9e0961A4790563a3416
网络: sepolia
部署账户: 0x41a886A528bBD09A4Fe0ED51e345781dc4D087c6
============================================================

🔍 在 Etherscan 查看合约:
   https://sepolia.etherscan.io/address/0xef610Fc6C8eF7f42B387E9e0961A4790563a3416

✅ 部署完成!

💡 下一步:
   1. 保存合约地址: 0xef610Fc6C8eF7f42B387E9e0961A4790563a3416
   2. 运行测试脚本: npx hardhat run scripts/test.js --network sepolia
   3. 在 Etherscan 上查看合约详情

image

测试结果

WARNING: You are currently using Node.js v25.4.0, which is not supported by Hardhat. This can lead to unexpected behavior. See https://v2.hardhat.org/nodejs-versions
当前账户: 0x41a886A528bBD09A4Fe0ED51e345781dc4D087c6
账户余额: 0.04949953127704832 ETH

连接到合约: 0xef610Fc6C8eF7f42B387E9e0961A4790563a3416
合约中的余额: 0.0 ETH

📥 测试充值功能...
充值金额: 0.01 ETH
交易哈希: 0x54c78226f1ec407adbb8d7ea44bf2d9bb74ae39349394a342752a8bdad43aee9
✅ 充值成功!
充值后余额: 0.01 ETH

📤 测试提款功能...
提款金额: 0.005 ETH
交易哈希: 0x99162845b86d95facfb2130f475abb0bf8487a2f0709e490472f0302ff2e6bf8
✅ 提款成功!
提款后余额: 0.005 ETH
合约总余额: 0.005 ETH

📊 统计信息:
总存款: 0.01 ETH
总提款: 0.005 ETH

image-1769789852985

部署readme

# 🎉 合约部署成功!

## ✅ 部署信息

- **合约地址**: `0xef610Fc6C8eF7f42B387E9e0961A4790563a3416`
- **网络**: Sepolia 测试网
- **部署账户**: `0x41a886A528bBD09A4Fe0ED51e345781dc4D087c6`
- **Etherscan**: https://sepolia.etherscan.io/address/0xef610Fc6C8eF7f42B387E9e0961A4790563a3416

## 🧪 测试结果

✅ **充值功能**: 成功充值 0.01 ETH  
✅ **提款功能**: 成功提款 0.005 ETH  
✅ **余额查询**: 正常工作

## 🚀 如何使用合约

### 方法 1: 使用测试脚本(最简单)

```bash
npx hardhat run scripts/test.js --network sepolia

方法 2: 使用 Hardhat Console(交互式)

npx hardhat console --network sepolia

然后在控制台中:

// 连接到合约
const contractAddress = "0xef610Fc6C8eF7f42B387E9e0961A4790563a3416";
const DepositWithdraw = await ethers.getContractFactory("DepositWithdraw");
const contract = DepositWithdraw.attach(contractAddress);

// 查看当前账户
const [signer] = await ethers.getSigners();
console.log("账户:", signer.address);

// 充值 0.1 ETH
await contract.deposit({ value: ethers.parseEther("0.1") });

// 查询余额
const balance = await contract.getBalance(signer.address);
console.log("余额:", ethers.formatEther(balance), "ETH");

// 提款 0.05 ETH
await contract.withdraw(ethers.parseEther("0.05"));

// 提取全部余额
await contract.withdrawAll();

方法 3: 使用 Etherscan(可视化界面)

  1. 访问: https://sepolia.etherscan.io/address/0xef610Fc6C8eF7f42B387E9e0961A4790563a3416
  2. 点击 “Contract” 标签
  3. 点击 “Write Contract” 或 “Read Contract”
  4. 连接你的 MetaMask 钱包(确保切换到 Sepolia 网络)
  5. 使用以下功能:
    • 充值: 在 “Write Contract” 中找到 deposit 函数,点击 “Write”,在 MetaMask 中输入金额
    • 查询余额: 在 “Read Contract” 中找到 getBalance 函数,输入你的地址
    • 提款: 在 “Write Contract” 中找到 withdraw 函数,输入金额(单位:wei)

方法 4: 直接发送 ETH 到合约地址

你也可以直接向合约地址发送 ETH,这会自动触发充值:

  1. 打开 MetaMask
  2. 确保切换到 Sepolia 测试网
  3. 点击 “发送”
  4. 粘贴合约地址: 0xef610Fc6C8eF7f42B387E9e0961A4790563a3416
  5. 输入要充值的金额
  6. 确认交易

📋 合约函数说明

充值函数

  • deposit() - 充值 ETH 到合约
  • 也可以直接向合约地址发送 ETH(会自动调用 deposit)

提款函数

  • withdraw(uint256 amount) - 提取指定数量的 ETH(单位:wei)
  • withdrawAll() - 提取全部余额

查询函数

  • getBalance(address user) - 查询指定用户的余额
  • getContractBalance() - 查询合约总余额
  • balances(address) - 查询用户余额(public mapping)
  • totalDeposits(address) - 查询用户总存款金额
  • totalWithdrawals(address) - 查询用户总提款金额

💡 金额转换

  • 1 ETH = 1,000,000,000,000,000,000 wei (10^18)
  • 0.1 ETH = 100,000,000,000,000,000 wei
  • 0.01 ETH = 10,000,000,000,000,000 wei

在 Hardhat Console 中使用:

ethers.parseEther("0.1")  // 将 0.1 ETH 转换为 wei
ethers.formatEther(balance)  // 将 wei 转换为 ETH

🔍 查看交易记录

所有交易都可以在 Etherscan 上查看:

⚠️ 注意事项

  1. 测试网 ETH: 确保你的账户有足够的 Sepolia 测试网 ETH
  2. Gas 费用: 每次操作都需要支付 gas 费用
  3. 余额检查: 提款前确保合约中有足够的余额
  4. 网络选择: 确保 MetaMask 连接到 Sepolia 测试网

🎯 下一步

现在你可以:

  1. ✅ 测试充值功能
  2. ✅ 测试提款功能
  3. ✅ 查看交易记录
  4. ✅ 在 Etherscan 上查看合约详情

祝你使用愉快!🚀

0

评论区