Overview

A multi-signature wallet requires multiple signatures to authorize a transaction. This enhances security by ensuring that no single person can control the funds. In this guide, we will create a simple multi-signature wallet smart contract, deploy it, and interact with it using Ethers.js.

1. Writing the Multi-Signature Wallet Smart Contract

First, we need to write a smart contract for the multi-signature wallet. Below is a simple implementation in Solidity:

        
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MultiSigWallet {
address[] public owners;
mapping(address => bool) public isOwner;
uint public required;
struct Transaction {
address to;
uint value;
bool executed;
uint confirmations;
mapping(address => bool) isConfirmed;
}
Transaction[] public transactions;
modifier onlyOwner() {
require(isOwner[msg.sender], `Not an owner`);
_;
}
constructor(address[] memory _owners, uint _required) {
require(_owners.length > 0, `Owners required`);
require(_required > 0 && _required <= _owners.length, `Invalid required number of owners`);
for (uint i = 0; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), `Invalid owner`);
require(!isOwner[owner], `Owner not unique`);
isOwner[owner] = true;
owners.push(owner);
}
required = _required;
}
function submitTransaction(address _to, uint _value) public onlyOwner {
transactions.push(Transaction({
to: _to,
value: _value,
executed: false,
confirmations: 0
}));
}
function confirmTransaction(uint _txIndex) public onlyOwner {
Transaction storage transaction = transactions[_txIndex];
require(!transaction.isConfirmed[msg.sender], `Transaction already confirmed`);
transaction.isConfirmed[msg.sender] = true;
transaction.confirmations++;
}
function executeTransaction(uint _txIndex) public onlyOwner {
Transaction storage transaction = transactions[_txIndex];
require(transaction.confirmations >= required, `Not enough confirmations`);
require(!transaction.executed, `Transaction already executed`);
transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(``);
require(success, `Transaction failed`);
}
receive() external payable {}
}

2. Deploying the Smart Contract

To deploy the smart contract, you can use Ethers.js. Below is an example of how to deploy the contract:

        
// deploy.js
const { ethers } = require(`ethers`);
async function main() {
const provider = new ethers.providers.JsonRpcProvider(`https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID`);
const wallet = new ethers.Wallet(`YOUR_PRIVATE_KEY`, provider);
const MultiSigWallet = await ethers.getContractFactory(`MultiSigWallet`, wallet);
const owners = [`0xOwnerAddress1`, `0xOwnerAddress2`, `0xOwnerAddress3`];
const required = 2;
const multiSigWallet = await MultiSigWallet.deploy(owners, required);
console.log(`MultiSigWallet deployed to:`, multiSigWallet.address);
}
main().catch((error) => {
console.error(error);
process.exit (1);
});

3. Interacting with the Multi-Signature Wallet

Once the contract is deployed, you can interact with it using Ethers.js. Below is an example of how to submit, confirm, and execute a transaction:

        
// interact.js
const { ethers } = require(`ethers`);
async function main() {
const provider = new ethers.providers.JsonRpcProvider(`https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID`);
const wallet = new ethers.Wallet(`YOUR_PRIVATE_KEY`, provider);
const multiSigWalletAddress = `DEPLOYED_CONTRACT_ADDRESS`;
const multiSigWallet = new ethers.Contract(multiSigWalletAddress, [
`function submitTransaction(address _to, uint _value)`,
`function confirmTransaction(uint _txIndex)`,
`function executeTransaction(uint _txIndex)`
], wallet);
// Submit a transaction
const txTo = `0xRecipientAddress`;
const txValue = ethers.utils.parseEther(`0.1`);
await multiSigWallet.submitTransaction(txTo, txValue);
console.log(`Transaction submitted`);
// Confirm the transaction (repeat this for each owner)
await multiSigWallet.confirmTransaction(0);
console.log(`Transaction confirmed`);
// Execute the transaction after enough confirmations
await multiSigWallet.executeTransaction(0);
console.log(`Transaction executed`);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});

Conclusion

In this guide, we created a simple multi-signature wallet using Ethers.js. We wrote a smart contract that requires multiple confirmations to execute transactions, deployed it to the Ethereum blockchain, and interacted with it using Ethers.js. This approach enhances security for managing funds, making it a valuable tool for groups or organizations that require shared control over assets.