Skip to content

Latest commit

 

History

History
241 lines (188 loc) · 7.85 KB

File metadata and controls

241 lines (188 loc) · 7.85 KB

Remix 部署 Bank 合约到 Sepolia 指南

合约验证失败问题解决方案

问题原因

Routescan 验证失败通常由以下原因导致:

  1. 编译器版本不匹配 - Remix 使用的编译器版本与部署时不一致
  2. 优化设置不匹配 - 编译优化选项不一致
  3. 合约代码格式问题 - 多文件导入在验证时可能有问题
  4. 验证时机过早 - 合约还未完全确认就尝试验证

解决方案

方法 1:使用 Remix 手动验证(推荐)

  1. 等待合约完全部署

    • 确保交易已确认(至少等待几个区块)
    • 在 Routescan 上确认合约地址存在
  2. 打开 Remix 的 "Contract Verification" 插件

    • 在 Remix 左侧面板找到 "Contract Verification" 插件
    • 如果没有,在插件管理器中搜索并安装
  3. 填写验证信息

    • Contract Address: 0xdb38C9D838b119ffa1e25731970dB41c01Cb7CE5
    • Chain: Sepolia (Chain ID: 11155111)
    • Compiler Version: 0.8.0 (必须与部署时一致)
    • Optimization: 如果部署时启用了优化,选择 Yes,否则选择 No
    • Runs: 如果启用了优化,通常设置为 20010000
  4. 合约代码

    • 将完整的 Bank.sol 代码粘贴到验证框中
    • 确保代码与部署时完全一致
  5. 构造函数参数

    • Bank 合约的构造函数没有参数,留空即可
  6. 点击验证

方法 2:直接在 Routescan 上验证

  1. 访问 Routescan Sepolia
  2. 搜索你的合约地址:0xdb38C9D838b119ffa1e25731970dB41c01Cb7CE5
  3. 点击 "Contract" 标签页
  4. 点击 "Verify and Publish"
  5. 选择验证方式:
    • Via Standard JSON Input (推荐)
    • Via Solidity (Single file)
    • Via Solidity (Standard JSON Input)

方法 3:使用标准 JSON Input(最可靠)

如果上述方法都失败,使用标准 JSON Input:

  1. 在 Remix 中:

    • 编译合约
    • 在 "Solidity Compiler" 插件中,点击 "Compilation Details"
    • 复制 "Standard JSON Input" 的内容
  2. 在 Routescan 验证页面:

    • 选择 "Via Standard JSON Input"
    • 粘贴 JSON 内容
    • 填写合约地址和编译器版本
    • 提交验证

完整的 Bank.sol 代码(用于 Remix)

以下是完整的单文件合约代码,可以直接在 Remix 中使用:

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

contract Bank {
    // 管理员地址
    address public owner;
    
    // 记录每个地址的存款金额
    mapping(address => uint256) public deposits;
    
    // 记录存款金额前3名的用户地址
    address[3] public topDepositors;
    
    // 记录前3名用户的存款金额
    uint256[3] public topDepositAmounts;
    
    // 事件
    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed owner, uint256 amount);
    
    // 自定义错误
    error NotOwner();
    error InvalidDepositAmount();
    error InvalidWithdrawAmount();
    error InsufficientBalance(uint256 requested, uint256 available);
    error WithdrawFailed();
    
    // 修饰符:仅管理员可调用
    modifier onlyOwner() {
        if(msg.sender != owner) revert NotOwner();
        _;
    }
    
    constructor() {
        owner = msg.sender;
    }
    
    // receive 函数:允许直接向合约发送 ETH
    receive() external payable {
        deposit();
    }
    
    // fallback 函数:处理其他调用
    fallback() external payable {
        deposit();
    }
    
    // 存款函数(也可以显式调用)
    function deposit() public payable {
        if (msg.value == 0) revert InvalidDepositAmount();
        
        deposits[msg.sender] += msg.value;
        
        // 更新前3名用户
        updateTopDepositors(msg.sender, deposits[msg.sender]);
        
        emit Deposit(msg.sender, msg.value);
    }
    
    // 更新前3名存款用户
    function updateTopDepositors(address user, uint256 amount) internal {
        // 检查用户是否已经在列表中
        bool alreadyInList = false;
        uint256 existingIndex = 0;
        
        for (uint256 i = 0; i < 3; i++) {
            if (topDepositors[i] == user) {
                alreadyInList = true;
                existingIndex = i;
                break;
            }
        }
        
        if (alreadyInList) {
            // 如果用户已在列表中,更新金额并重新排序
            topDepositAmounts[existingIndex] = amount;
            sortTopDepositors();
        } else {
            // 如果用户不在列表中,检查是否可以进入前3名
            for (uint256 i = 0; i < 3; i++) {
                if (amount > topDepositAmounts[i]) {
                    // 插入新用户并排序
                    shiftAndInsert(i, user, amount);
                    break;
                }
            }
        }
    }
    
    // 插入新用户到指定位置并保持排序
    function shiftAndInsert(uint256 index, address user, uint256 amount) internal {
        // 从后往前移动,为新用户腾出位置
        for (uint256 i = 2; i > index; i--) {
            topDepositors[i] = topDepositors[i - 1];
            topDepositAmounts[i] = topDepositAmounts[i - 1];
        }
        
        // 插入新用户
        topDepositors[index] = user;
        topDepositAmounts[index] = amount;
    }
    
    // 对前3名进行排序(从大到小)
    function sortTopDepositors() internal {
        // 简单的冒泡排序
        for (uint256 i = 0; i < 2; i++) {
            for (uint256 j = 0; j < 2 - i; j++) {
                if (topDepositAmounts[j] < topDepositAmounts[j + 1]) {
                    // 交换金额
                    uint256 tempAmount = topDepositAmounts[j];
                    topDepositAmounts[j] = topDepositAmounts[j + 1];
                    topDepositAmounts[j + 1] = tempAmount;
                    
                    // 交换地址
                    address tempAddr = topDepositors[j];
                    topDepositors[j] = topDepositors[j + 1];
                    topDepositors[j + 1] = tempAddr;
                }
            }
        }
    }
    
    // 提取资金(仅管理员)
    function withdraw(uint256 amount) public onlyOwner {
        if (amount == 0) revert InvalidWithdrawAmount();
        if (address(this).balance < amount) revert InsufficientBalance(amount, address(this).balance);
        
        (bool success, ) = payable(owner).call{value: amount}("");
        if (!success) revert WithdrawFailed();
        
        emit Withdraw(owner, amount);
    }
    
    // 获取合约总余额
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
    
    // 获取用户的存款金额
    function getDeposit(address user) public view returns (uint256) {
        return deposits[user];
    }
}

验证检查清单

  • 编译器版本:0.8.0(必须与部署时完全一致)
  • 优化设置:检查部署时是否启用了优化
  • 合约代码:与部署时完全一致(包括注释和格式)
  • 构造函数参数:无参数(留空)
  • 合约地址:0xdb38C9D838b119ffa1e25731970dB41c01Cb7CE5
  • 网络:Sepolia (Chain ID: 11155111)
  • 等待时间:确保合约已完全部署并确认

常见问题

Q: 如何知道部署时使用的编译器版本?

A: 在 Remix 的 "Solidity Compiler" 插件中查看当前选择的版本,这通常就是部署时使用的版本。

Q: 如何知道是否启用了优化?

A: 在 Remix 编译器中,如果 "Enable optimization" 选项被勾选,则启用了优化。默认通常是关闭的。

Q: 验证一直失败怎么办?

A:

  1. 尝试使用 "Via Standard JSON Input" 方法
  2. 确保合约代码完全一致(包括空格和换行)
  3. 等待更长时间后再试(有时需要等待几分钟)
  4. 检查合约是否真的部署成功(在区块浏览器上查看)