← Blog
Solidity10 minApr 20, 2026

Building a multi-sig wallet in Solidity, step by step

Threshold approvals, replay protection, and deploying to Polygon testnet. A complete walkthrough.

What is a multi-sig wallet

A multi-signature wallet requires M-of-N owners to approve a transaction before it executes. For example, a 2-of-3 wallet means any 2 of 3 designated owners must sign. This is the standard pattern for shared treasury management on-chain.

The contract structure

The core state: a list of owners, the threshold M, and a mapping of proposed transactions. Each transaction has a value, a target address, calldata, and a set of confirmations from owners.

contract MultiSigWallet {
    address[] public owners;
    uint public required;  // threshold
    
    struct Transaction {
        address to;
        uint value;
        bytes data;
        bool executed;
        uint confirmations;
    }
    
    Transaction[] public transactions;
    mapping(uint => mapping(address => bool)) public confirmed;
}

Submitting and confirming

Any owner can submit a transaction. Once M owners confirm it, anyone can trigger execution. The separation of confirmation from execution is intentional — execution is a separate step so the submitter does not have special power to force execution.

function confirmTransaction(uint txIndex) external onlyOwner {
    require(!confirmed[txIndex][msg.sender], "already confirmed");
    confirmed[txIndex][msg.sender] = true;
    transactions[txIndex].confirmations++;
}

function executeTransaction(uint txIndex) external onlyOwner {
    Transaction storage txn = transactions[txIndex];
    require(!txn.executed, "already executed");
    require(txn.confirmations >= required, "not enough confirmations");
    txn.executed = true;
    (bool success, ) = txn.to.call{value: txn.value}(txn.data);
    require(success, "execution failed");
}

Replay protection

Without replay protection, a confirmed transaction could be re-executed. The executed flag on the Transaction struct prevents this — once set, the transaction cannot be re-executed regardless of confirmation count.

Deploying to Polygon testnet

Deployment used Hardhat with the Polygon Mumbai testnet. The key advantage of Polygon over Ethereum testnet: lower gas costs make iterative testing practical.

// hardhat.config.ts
polygon_mumbai: {
    url: process.env.POLYGON_RPC_URL,
    accounts: [process.env.PRIVATE_KEY!],
    chainId: 80001
}

Security considerations

The main risks in multi-sig contracts: owner key compromise (mitigated by the threshold), and griefing by non-cooperative owners (no mitigation — this is a social contract). We also added a timelock on execution for high-value transactions, giving owners a window to revoke confirmations if something looks wrong.