0

When I try to test this contract with Hardhat I get the next error related to the "Approve Transaction with the 3 accounts" test. But I have no idea why this error is triggered.

Error: VM Exception while processing transaction: reverted with panic code 0x32 (Array accessed at an out-of-bounds or negative index)

Could someone help? Please.

Smart contract 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;
    }
}

Testing script (Hardhat)

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);
  });
});

2 Answers 2

0

The issue is in line 51

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

Replace it with

        require(
            transactions.length > _txId,
            "The transaction does not exist"
        );
1
  • 1
    Transanctions.length needs to be greater or equal to the _txId, as the array can contain different transactions. This does not look to be the problem, but thanks :) Commented Oct 31, 2022 at 15:15
0

I just solved the problem. I was pointing to an different position in the array.

Before:

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

const confirmations = await multiSigWallet.getConfirmations(1);

After:

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

const confirmations = await multiSigWallet.getConfirmations(0);

I leave it here in case someone has the same error in the future.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.