This is the second guide on our on-chain multisig tutorial! In this hands-on guide, we'll explore creating a smart contract for a multi-signature wallet. If you haven't delved into the preceding article yet, take a moment to catch up here to ensure you're in sync with our progress.

Estimated duration to follow along: 15-20 minutes

Prerequisites

In this walkthrough, we'll be implementing an on-chain multisig wallet contract. Make sure you grasp the fundamentals of multisig wallets. If you need a refresher, check out our previous guide.

Tutorial: Creating a Multi-signature Contract

By creating a multisig wallet, you'll learn how to:

  • Implement a multisig wallet in solidity.

  • Deploy a multisig wallet smart contract.

  • Initiate and confirm transactions within the multisig setup.

1

Setting Up the Initial Project

Launch the Remix IDE on the Remix website here. Then, at the top left corner of the Remix interface, hit the file icon to create a new file. For this project, let's name the file MultiSigWallet.sol.

Now, let's proceed to implement the functionality of our multisig wallet.

2

Implementing the functions

Let's walk through the contract.

In the first lines, the pragma solidity statement specifies the version of the Solidity compiler to use. In this case, it's version 0.8.24.

State Variables

owners:  A public array storing the addresses of the contract's owners.

requiredConfirmations: This indicates the number of confirmations required for a transaction to proceed.

transactionCount: A public counter tracking the total number of transactions initiated by the contract.

withdrawalLimit:  This variable sets the maximum amount that can be withdrawn within a specific period.

withdrawalPeriod: This variable defines  the time frame for the withdrawal limit, initially set to 2 minutes.

struct Transaction { ... }: A struct defining the properties of a transaction, including recipient address, amount, confirmations, execution status, timestamp, and a mapping to track confirmations by address.

Mappings

lastWithdrawal: public mapping that records the last withdrawal time for each address.

transactions: public mapping that associates each transaction's unique identifier with its corresponding transaction struct.

Modifiers

onlyOwner: modifier used to restrict the execution of certain functions to the owner of the smart contract by checking if the caller is the owner before proceeding.

Constructor

This constructor initializes a multi-signature wallet with specified owners, a required number of confirmations for transactions, and a withdrawal limit, ensuring all owners are unique and valid, and both the required confirmations and withdrawal limit are set to some  values.

Functions

submitTransaction: This is used to submit a new transaction proposal to the multi-signature wallet, recording its details and incrementing the transaction count.

confirmTransaction: Confirms a submitted transaction by an owner, executing it if the required number of confirmations is reached.

executeTransaction: This executes a confirmed transaction if it meets the required number of confirmations, is not already executed, is past its withdrawal period, and the contract has sufficient balance, marking it as executed upon success.

revokeConfirmation: Allows an owner to revoke their confirmation of a transaction, provided the transaction has not been executed and the owner had previously confirmed it, decrementing the confirmation count and updating the owner's confirmation status.

getTransaction: This is used to retrieve details of a specific transaction by its index.

isOwner: Checks if a given address is one of the contract's owners.

deposit: Allows the contract to receive Ether deposits.

receive: Enables the contract to accept direct Ether transfers.

3

Compiling and deploying the smart contract

To compile our contract, navigate to the Compiler tab on the left and click Compile. Once you've successfully compiled the contract, proceed to the Deploy tab to initiate the deployment. Select Remix VM from the available environments, and configure the constructor value to (owner: 3 addresses, requiredConfirmations: 2, withdrawalLimit: 1ether) and click on transact.

4

Sending Ether to the contract

For our multisig function, we need to fund it with ethers. To achieve this, we will send 1 ether from the first address provided to us by remix. Select the first account, set the value to 1 ether and click on deposit as shown:

Let’s now proceed to execute some transactions.

5

Withdrawing from the Multisig

To achieve this, begin by choosing the initial address from the collection of owner addresses. Then, proceed to click on 'submitTransaction' and input the recipient's address - feel free to pick any address at random from those available on Remix aside from the first three owner addresses along with the specified amount. For the purposes of this tutorial, we will be transferring 0.01 ether.

To confirm if our submitted transaction was successful, click on ‘getTransaction’ and pass in the transaction index- which is 0 in this case since it’s our first transaction.

6

Confirmation and Fund transfers

Once the transaction has been initiated, it must be verified through signatures from the two remaining owner addresses to ensure its successful execution. Select the second address from your list of owner addresses and input '0' before clicking on 'confirmTransaction'. This process should be repeated for the third address, as the transaction requires a minimum of two confirmations to proceed.

Once the confirmation process is successfully completed, the 'executeTransaction' function will be activated, resulting in the transfer of funds to the intended recipient as demonstrated.

There you have it. We’ve successfully transferred funds from the multisig wallet!

Conclusion

Congratulations on successfully completing the journey! You've mastered the intricacies of setting up and managing a multisig wallet, a crucial tool for decentralized decision-making. With this knowledge, you're now equipped to explore the practical uses of multisig wallets in various scenarios. Happy coding!