A secure, robust, and gas-efficient multi-signature wallet implemented in Solidity. This project allows multiple parties to collectively manage funds, requiring a minimum number of approvals before a transaction can be executed. It's built with the Foundry framework for development, testing, and deployment.
A multi-signature (multi-sig) wallet is a type of digital wallet that requires two or more private keys to sign and authorize a transaction. The core idea is to enhance security by distributing control over funds among multiple participants (owners). This prevents a single point of failure and protects against unauthorized access.
This implementation provides a flexible and secure on-chain wallet where you can define a list of owners and a "threshold"—the number of approvals required to execute a transaction. It's ideal for DAOs, corporate treasuries, or any group needing to manage shared assets securely.
- Customizable Threshold: Define the exact number of owner approvals needed for a transaction.
- Transaction Lifecycle: Full support for submitting, approving, revoking, canceling, and executing transactions.
- Event-Driven: Emits detailed events for all significant actions (deposits, submissions, approvals, etc.) for easy off-chain monitoring.
- Security First: Follows best practices, including the checks-effects-interactions pattern to prevent reentrancy attacks.
- Developer Friendly: Built with Foundry and includes a comprehensive
Makefile
for a streamlined development workflow. - Multi-Network Deployment: Includes scripts and configurations for easy deployment to local networks (Anvil) and public testnets (Sepolia).
Follow these instructions to get a copy of the project up and running on your local machine for development and testing.
You must have the Foundry development toolchain installed. If you don't, please follow the installation instructions here.
- Clone the repository:
git clone <YOUR_REPOSITORY_URL> cd <YOUR_PROJECT_DIRECTORY>
- Install dependencies:
This project uses
forge-std
andfoundry-devops
. Install them using the built-inmake
command, which runsforge install
.make install
This project is managed through a Makefile
that simplifies the entire development lifecycle.
For deployment to a live network like Sepolia, you need to create a .env
file in the root directory and populate it with the necessary credentials.
Create a .env
file:
touch .env
Add the following variables to your .env
file:
# Your Sepolia RPC URL from a provider like Infura or Alchemy
RPC_URL="https://sepolia.infura.io/v3/your-infura-project-id"
# The private keys of the wallet owners
PRIVATE_KEY1="0x..."
PRIVATE_KEY2="0x..."
# Add more keys if needed for your configuration
# Your Etherscan API key for contract verification
API_KEY="your-etherscan-api-key"
Note: The Makefile
is pre-configured to load these variables.
Here are the most common commands you will use:
Command | Description |
---|---|
make all |
A full reset. Cleans the project, reinstalls dependencies, updates them, and builds the contracts. |
make build |
Compiles the smart contracts. |
make test |
Runs the unit and integration tests. |
make snapshot |
Generates a gas snapshot to track gas usage changes. |
make coverage |
Runs tests and generates a code coverage report in the terminal. |
make coverage-html |
Generates a detailed HTML coverage report located in the coverage-html directory. |
make format |
Formats the Solidity code using forge fmt . |
make anvil |
Starts a local Anvil node for testing and development. |
make deploy-anvil |
Deploys the MultiSigWallet contract to the local Anvil network. |
make fund-anvil |
Sends 0.01 ETH to the most recently deployed contract on Anvil. |
make deploy-sepolia |
Deploys and verifies the MultiSigWallet contract on the Sepolia testnet. (Requires .env file) |
make fund-sepolia |
Sends ETH to the most recently deployed contract on Sepolia. (Requires .env file) |
make submit-sepolia |
Submits a new transaction from owner 1 on Sepolia. (Requires .env file) |
make approve-sepolia1 |
Approves the latest transaction with owner 1's key on Sepolia. (Requires .env file) |
make approve-sepolia2 |
Approves the latest transaction with owner 2's key on Sepolia. (Requires .env file) |
make execute-sepolia |
Executes the latest transaction (once the threshold is met) on Sepolia. (Requires .env file) |
Here’s a typical workflow for testing the contract on your local machine.
-
Start the Anvil chain in a separate terminal window:
make anvil
-
Deploy the contract to your local Anvil chain:
make deploy-anvil
Take note of the contract address printed in the console.
-
Fund the wallet with some test Ether:
make fund-anvil
-
Interact with the contract: Use
cast
or other scripts to submit, approve, and execute transactions.
This example assumes the default HelperConfig.s.sol
configuration: 3 owners and a threshold of 2.
-
Set up your
.env
file with yourRPC_URL
, at least twoPRIVATE_KEY
s, and anAPI_KEY
. -
Deploy the wallet to Sepolia:
make deploy-sepolia
-
Fund the wallet so it can execute transactions:
make fund-sepolia
-
Submit a transaction (e.g., sending 0.01 ETH to a target address). This is done by
PRIVATE_KEY1
:make submit-sepolia
-
Approve the transaction. Since the threshold is 2, we need two separate owners to approve it.
- Approve with owner 1 (
PRIVATE_KEY1
):make approve-sepolia1
- Approve with owner 2 (
PRIVATE_KEY2
):make approve-sepolia2
- Approve with owner 1 (
-
Execute the transaction. Now that the threshold (2 approvals) has been met, any owner can execute the transaction.
make execute-sepolia
Success! The funds have been moved according to the approved transaction. 🎉
The project is centered around the MultiSigWallet.sol
contract.
-
State Variables:
owners
: An array of addresses that co-own the wallet.isOwner
: A mapping for efficientO(1)
owner lookups.threshold
: The immutable number of approvals required for execution.transactions
: An array storing all proposed transactions.approved
: A nested mapping (txId
=>owner
=>bool
) to track approvals.
-
Core Functions:
constructor
: Initializes the wallet with a set of owners and a threshold.submit
: Allows an owner to propose a new transaction.approve
: Allows an owner to approve a pending transaction.revoke
: Allows an owner to withdraw their approval.cancel
: Allows an owner to cancel a transaction if it hasn't met the threshold.execute
: Executes a transaction if it has met the approval threshold.
-
Scripts:
DeployMultiSigWallet.s.sol
: Handles the deployment logic.HelperConfig.s.sol
: Manages network-specific configurations (like owners and threshold) for seamless multi-chain deployment.Interactions.s.sol
: A collection of scripts to interact with the deployed contract (deposit, submit, approve, etc.).
Contributions are welcome! If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the "enhancement" tag.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
This project is distributed under the MIT License. See LICENSE
for more information.