Solidity

使用 Hardhat 測試時出現錯誤恐慌程式碼 0x32 已解決

  • November 1, 2022

當我嘗試使用 Hardhat 測試此契約時,我收到與“批准 3 個帳戶的交易”測試相關的下一個錯誤。但我不知道為什麼會觸發此錯誤。

錯誤:處理事務時出現 VM 異常:使用緊急程式碼 0x32 恢復(在越界或負索引處訪問的數組)

有人可以幫忙嗎?請。

智能合約 MultiSigWallet.sol


pragma solidity ^0.8.10;

contract MultiSigWallet {
   event Deposit(address indexed sender, uint256 amount, uint256 balance);
   event SubmitTransaction(uint256 indexed txId);
   event ApproveTransaction(address indexed owner, uint256 indexed txId);
   event RevokeTransaction(address indexed owner, uint256 indexed txId);
   event ExecuteTransaction(uint256 indexed txId);

   struct Transaction {
       address payable to;
       uint256 amount;
       bytes data;
       bool executed;
       uint256 numConfirmations;
   }

   Transaction[] public transactions;

   address[] owners;
   mapping(address => bool) isOwner;

   // mapping from tx index => owner => bool
   mapping(uint256 => mapping(address => bool)) approved;
   mapping(uint256 => bool) executed;
   uint256 confirmationsRequired;

   modifier onlyOwner() {
       require(isOwner[msg.sender], "You are not an owner");
       _;
   }

   modifier txNotApproved(uint256 _txId) {
       require(
           !approved[_txId][msg.sender],
           "The transaction is already approved"
       );
       _;
   }

   modifier txNotExecuted(uint256 _txId) {
       require(
           !transactions[_txId].executed,
           "The transaction has been already executed"
       );
       _;
   }

   modifier txExists(uint256 _txId) {
       require(
           transactions.length >= _txId,
           "The transaction does not exists"
       );
       _;
   }

   constructor(address[] memory _owners, uint256 _confirmationsRequired) {
       require(_owners.length > 0, "Owners needs to be greater than 0");
       require(
           _confirmationsRequired > 0,
           "Owners required needs to be greater than 0"
       );
       for (uint256 i = 0; _owners.length > i; i++) {
           address _owner = _owners[i];
           require(_owner != address(0));
           isOwner[_owners[i]] = true;
       }
       owners = _owners;
       confirmationsRequired = _confirmationsRequired;
   }

   receive() external payable {
       emit Deposit(msg.sender, msg.value, address(this).balance);
   }

   function submitTransaction(
       uint256 _amount,
       address payable _to,
       bytes memory _data
   ) public onlyOwner {
       transactions.push(
           Transaction({
               to: _to,
               amount: _amount,
               data: _data,
               executed: false,
               numConfirmations: 0
           })
       );
       uint256 txIndex = transactions.length;

       emit SubmitTransaction(txIndex);
   }

   function approveTransaction(uint256 _txId)
       public
       onlyOwner
       txExists(_txId)
       txNotApproved(_txId)
       txNotExecuted(_txId)
   {
       transactions[_txId].numConfirmations += 1;
       approved[_txId][msg.sender] = true;

       emit ApproveTransaction(msg.sender, _txId);
   }

   function revokeConfirmation(uint256 _txId)
       public
       onlyOwner
       txExists(_txId)
       txNotExecuted(_txId)
   {
       require(
           approved[_txId][msg.sender] = true,
           "This Transaction is not approved by the msg.sender"
       );
       approved[_txId][msg.sender] = false;

       emit RevokeTransaction(msg.sender, _txId);
   }

   function executeTransaction(uint256 _txId)
       public
       payable
       onlyOwner
       txNotExecuted(_txId)
       txExists(_txId)
   {
       Transaction storage transaction = transactions[_txId];
       require(
           transaction.numConfirmations >= confirmationsRequired,
           "Not enough confirmations from the Owners"
       );

       transaction.executed = true;
       uint256 _transactionAmount = transaction.amount;
       address payable _to = transaction.to;
       _to.transfer(_transactionAmount);

       emit ExecuteTransaction(_txId);
   }

   function getOwners() public view returns (address[] memory) {
       return owners;
   }

   function checkExecuted(uint256 _txId) public view returns (bool) {
       return executed[_txId];
   }

   function getTransactions() public view returns (uint256) {
       return transactions.length;
   }

   function getConfirmations(uint256 _txId) public view returns (uint256) {
       return transactions[_txId].numConfirmations;
   }
}

測試腳本(安全帽)

const { ethers } = require("hardhat");
const { expect } = require("chai");
const { loadFixture } = require("ethereum-waffle");
const { TransactionDescription } = require("ethers/lib/utils");

// Ganache accounts (Testnet):
// 1: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
// 2: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
// 3: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
// 4: 0x90F79bf6EB2c4f870365E785982E1f101E93b906

describe("MultiSigWallet", function () {
 async function deploy() {
   const MultiSigWallet = await ethers.getContractFactory("MultiSigWallet");
   const multiSigWallet = await MultiSigWallet.deploy(
     [
       "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
       "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
       "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
     ],
     3
   );
   await multiSigWallet.deployed();

   console.log("* MultiSigWallet contract has been deployed.");
   console.log("* MultiSigWallet contract address:", multiSigWallet.address);

   return { multiSigWallet };
 }

 it("Check the Owners array length", async () => {
   const { multiSigWallet } = await loadFixture(deploy);
   const owners = await multiSigWallet.getOwners();
   expect(owners.length).to.equal(3);
 });

 it("Submit a new transaction", async () => {
   const { multiSigWallet } = await loadFixture(deploy);
   await multiSigWallet.submitTransaction(
     6,
     "0x90F79bf6EB2c4f870365E785982E1f101E93b906",
     "0x6e65775f6d6963726f7761766500000000000000000000000000000000000000"
   );
   const transactions = await multiSigWallet.getTransactions();

   expect(transactions).to.equal(1);
 });

 it("Transfer 5 ethers from the 3 owners to the contract and check SC balance", async () => {
   const { multiSigWallet } = await loadFixture(deploy);
   const [deployer, addr1, addr2] = await ethers.getSigners();
   await deployer.sendTransaction({
     to: multiSigWallet.address,
     value: ethers.utils.parseEther("2.0"),
   });
   await addr1.sendTransaction({
     to: multiSigWallet.address,
     value: ethers.utils.parseEther("2.0"),
   });
   await addr2.sendTransaction({
     to: multiSigWallet.address,
     value: ethers.utils.parseEther("2.0"),
   });

   const ScBalance = await ethers.provider.getBalance(multiSigWallet.address);
   expect(ScBalance).to.equal("6000000000000000000"); // 6000000000000000000 = 6 ethers
 });

 it("Approve Transaction with the 3 accounts", async () => {
   const { multiSigWallet } = await loadFixture(deploy);
   const [deployer, addr1, addr2] = await ethers.getSigners();

   await multiSigWallet.connect(deployer).approveTransaction(1);
   await multiSigWallet.connect(addr1).approveTransaction(1);
   await multiSigWallet.connect(addr2).approveTransaction(1);

   const confirmations = await multiSigWallet.getConfirmations(1);

   expect(confirmations).to.equal(3);
 });
});

問題在第 51 行

       require(
           transactions.length >= _txId,
           "The transaction does not exists"
       );

將其替換為

       require(
           transactions.length > _txId,
           "The transaction does not exist"
       );

**我剛剛解決了這個問題。**我指向數組中的不同位置。

之前

await multiSigWallet.connect(deployer).approveTransaction(1);
await multiSigWallet.connect(addr1).approveTransaction(1);
await multiSigWallet.connect(addr2).approveTransaction(1);

const confirmations = await multiSigWallet.getConfirmations(1);

之後

await multiSigWallet.connect(deployer).approveTransaction(0);
await multiSigWallet.connect(addr1).approveTransaction(0);
await multiSigWallet.connect(addr2).approveTransaction(0);

const confirmations = await multiSigWallet.getConfirmations(0);

我把它留在這裡,以防將來有人遇到同樣的錯誤。

引用自:https://ethereum.stackexchange.com/questions/138472