繁体   English   中英

如何在本地测试网上诊断 ERC20 错误“错误:执行已恢复”?

[英]How to diagnose an ERC20 error "Error: execution reverted" on a local testnet?

我有一个本地测试网(由这个 Docker 图像表示),我正在尝试将一些 ERC20 令牌从一个地址发送到另一个地址。

为此,我使用以下 HardHat 脚本:

npx hardhat run --network localTestnet scripts/send-usdt-to-exchange.js

执行此文件:

async function main() {
    const USDT = await ethers.getContractFactory("USDT");
    const usdt = await USDT.attach(
  "0xB816192c15160a2C1B4D032CDd7B1009583b21AF"
    );
    const amount = 1;
    const gas = 1000000;
    const exchange = "0x190FD61ED8fE0067f0f09EA992C1BF96209bab66";
    const usdtSender = "0xDd1e8cC92AF9748193459ADDF910E1b96E88154D";

    console.log("Approving the transfer...");
    await usdt.approve(usdtSender, amount + gas);
    console.log("Done");
    console.log("Sending USDT...");
    const result = await usdt.transferFrom(usdtSender, exchange, amount, { gasLimit: gas });
    console.log("Done, result=", result);
}

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

执行此脚本几分钟后,我可以在 Blockscout 中看到事务失败并出现Error: execution reverted错误。

我在Raw Trace下的 Blockscout 中看到了一些 output 。

截图 1

截图 2

[
  {
    "action": {
      "callType": "call",
      "from": "0x411167fefecad12da17f9063143706c39528aa28",
      "gas": "0xEEC98",
      "input": "0x23b872dd000000000000000000000000dd1e8cc92af9748193459addf910e1b96e88154d000000000000000000000000190fd61ed8fe0067f0f09ea992c1bf96209bab660000000000000000000000000000000000000000000000000000000000000001",
      "to": "0xb816192c15160a2c1b4d032cdd7b1009583b21af",
      "value": "0x0"
    },
    "error": "execution reverted",
    "subtraces": 0,
    "traceAddress": [],
    "type": "call"
  }
]

USDT 合约使用以下脚本部署在测试网上:

async function main() {
  // We get the contract to deploy
    const USDT = await ethers.getContractFactory("USDT");
    const usdt = await USDT.deploy("0x190FD61ED8fE0067f0f09EA992C1BF96209bab66", "0xDd1e8cC92AF9748193459ADDF910E1b96E88154D");


  console.log("USDT contract deployed to:", usdt.address);
}

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

合同本身如下所示:

pragma solidity ^0.8.0;

import "./tokens/ERC20/ERC20.sol";

contract USDT is ERC20 {
    constructor(address exchange, address usdtSender) ERC20("Test Tether", "tUSDT") {
        _mint(exchange, 1000000000000000000000);
        _mint(usdtSender, 1000000000000000000000);
    }
}

鉴于所有这些都发生在本地测试网上,我可以做些什么来诊断execution reverted错误以及如何修复它?

更新 1:我注意到一个奇怪的行为。

在执行send-usdt-to-exchange.js之前,我通过打开 geth shell 并运行以下命令将 ETH 添加到usdtSender帐户( 0xDd1e8cC92AF9748193459ADDF910E1b96E88154D ):

geth attach http://localhost:8178

web3.personal.unlockAccount('0x411167FeFecAD12Da17F9063143706C39528aa28',
'carsdrivefasterbecausetheyhavebrakes', 600);

eth.sendTransaction({
  from: '0x411167FeFecAD12Da17F9063143706C39528aa28',
  to: '0xDd1e8cC92AF9748193459ADDF910E1b96E88154D',
  value: web3.toWei(1000, 'ether')});

这将返回事务 ID 0x76794f48a21518a3b3acfe820c2a57f88c188949054573be1805894bb9627471并在 BlockScout 中将此事务标记为成功:

BlockScout 中成功交易的屏幕截图

Blockscout 中的“原始跟踪”如下所示:

[
  {
    "action": {
      "callType": "call",
      "from": "0x411167fefecad12da17f9063143706c39528aa28",
      "gas": "0x0",
      "input": "0x",
      "to": "0xdd1e8cc92af9748193459addf910e1b96e88154d",
      "value": "0x3635C9ADC5DEA00000"
    },
    "result": {
      "gasUsed": "0x0",
      "output": "0x"
    },
    "subtraces": 0,
    "traceAddress": [],
    "type": "call"
  }
]

但是,如果我打电话

console.log("Determining the ETH balance of USDT sender...");
const ethBalance = await usdt.provider.getBalance(usdtAddress);
console.log("ETH balance:", ethBalance);
console.log("Done");

send-usdt-to-exchange.js (在 Blockscout 中将事务标记为成功之后)我得到 output

Determining the ETH balance of USDT sender...
ETH balance: BigNumber { _hex: '0x00', _isBigNumber: true }
Done

出于某种原因,即使交易成功,USDT 发送方的 ETH 余额也为零。

更新 2:在 geth shell 内部, eth.getBalance("0xDd1e8cC92AF9748193459ADDF910E1b96E88154D"); (USDT 发送者账户的 ETH 余额)返回2e+21

更新 3: Go-Ethereum 中的指令中触发了“执行恢复”错误。go 文件:

func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
    offset, size := scope.Stack.pop(), scope.Stack.pop()
    ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))

    interpreter.returnData = ret
    return ret, ErrExecutionReverted
}

这是一个肮脏的黑客,似乎有效。

首先,在合约中添加一个在不改变配额的情况下转移资金的方法:

pragma solidity ^0.8.0;

import "./tokens/ERC20/ERC20.sol";

contract USDT is ERC20 {
    constructor(address exchange, address usdtSender) ERC20("Test Tether", "tUSDT") {
        _mint(exchange, 1000000000000000000000);
        _mint(usdtSender, 1000000000000000000000);
    }

    function transferFromWithoutChangingAllowance(
        address sender,
        address recipient,
        uint256 amount
    ) public returns (bool) {
        _transfer(sender, recipient, amount);
        return true;
    }
}

然后,更改脚本:

async function main() {
    const USDT = await ethers.getContractFactory("USDT");
    const usdtAddress = "0xa682a5972D1A8175E2431B26586F486bBa161A11";
    const usdt = await USDT.attach(usdtAddress);
    const amount = 1;
    const exchange = "0x190FD61ED8fE0067f0f09EA992C1BF96209bab66";
    const usdtSender = "0xDd1e8cC92AF9748193459ADDF910E1b96E88154D";

    console.log("Determining the ETH balance of USDT sender...");
    const ethBalance = await usdt.provider.getBalance(usdtSender);
    console.log("ETH balance:", ethBalance);
    console.log("Done");
    
    console.log("Approving the transfer...");
    const approveResult = await usdt.approve(usdtSender, amount+1);
    console.log("Done, result: ", approveResult);
    console.log("Sending USDT...");
    const result = await usdt.transferFromWithoutChangingAllowance(usdtSender, exchange, amount, { gasLimit: 1000000 });
    console.log("Done, result=", result);
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM