What is MultiSignature Wallets?
Digital wallets are financial accounts that store funds and make transactions. You can also track transaction histories. Instead of using rupees, dollars we can also use cryptocurrencies such as Bitcoin, Ether, and many more. Generally, a wallet needs one signature to sign a transaction. We can also have multisignature wallets whose access is shared with more than one member i.e we can have copayers. To simplify it, to complete a transaction successfully, we need more than one signature. It has the functionality of setting a minimum signature count i.e the minimum number of signatures required to complete the transaction.
For example, A multisignature wallet has 10000 tokens and is shared between Ajay, Neha, and Amit and the minimum signature count is 2. So at least two of them need to sign a transaction for it to become successful.
This concept is not new. It has been used to secure assets for quite some time but digital implementation has been done recently. For example, limited access such as partial keys of a locker was given earlier to protect the assets.
- Armory: It is an open-source wallet without any need for third-party servers. A maximum of seven authorized signers can be a part of a single wallet. It is available for Windows, Linux, and Mac Desktops.
- Electrum: It is an open-source and one of the most popular multisignature wallets. It can easily be integrated with third-party hardware wallets such as Ledger and Trezor. A maximum of fifteen authorized signers can be a part of a single wallet. It is available for Windows, Linux, and Mac Desktops as well as on mobile.
- BitPay: It is an open-source wallet without any need for third-party servers. A maximum of three authorized signers can be a part of a single wallet. It is available for Windows, Linux, and Mac Desktops as well as on mobile. We also have a Chrome extension for this.
- FreeWallet: It is not an open-source wallet. A maximum of three authorized signers can be a part of a single wallet. It is available for iOS, Android, and through our browsers.
There are several other wallets such as Carbon, BitGo, Casa, and many more and you can choose one depending on your need.
- It can be used in escrow transactions. An escrow transaction is when assets are held by a third party on behalf of other participating parties till all the obligations are met.
- It can be used in companies having large funds. It can be used when the board of directors wants to control access to funds.
- It creates an additional layer of security. Even if one of the owners loses it’s key it is still safe.
- It also helps in preventing the misuse of funds.
- It can also be used by a single person to add security. He/She can keep both the keys separately. So even if one of them is lost it can still be accessed.
- Setting up a multisignature wallet requires some technical information, particularly on the off chance that you would prefer not to depend on third-party providers.
- Since blockchain and multisignature wallets are both relatively new, it might be hard to look for a legal alternative if something goes wrong.
How can we code for a multi-signature wallet in solidity?
There are a lot of ways we can code a multi signature wallet.
To begin, let’s define the requirements for our multi-signature wallet. Our wallet will have a fixed number of co-signers, each with its unique private key. A transaction will only be authorized if a certain number of these co-signers approve. For example, if our wallet has 3 co-signers and requires 2 approvals for a transaction to be made, then at least 2 of the 3 co-signers must provide their private key to authorize the transaction.
Now that we have defined the requirements for our wallet let’s start implementing it in Solidity.
First, we will define the struct for our co-signer. This struct will store the address and the public key of the co-signer.
Next, we will define the multi signature wallet contract. This contract will store the array of co-signers, the required number of approvals for a transaction, and the current number of approvals for the current transaction.
Now, let’s define the function that allows a co-signer to add their approval to the current transaction. This function will take in the co-signer’s address and their signed message as parameters. It will then verify the signature using the co-signer’s public key, and if the signature is valid, it will add the co-signer’s approval to the current transaction.
We also need to define the function that allows a co-signer to revoke their approval from the current transaction. This function will simply decrease the current number of approvals by one.
Finally, we will define the function executing the transaction if the required approvals have been reached. This function will take in the destination address and the transfer value as parameters. It will then send the specified amount.
Below is the complete code for a multi signature wallet:
Here is a brief overview of the main features and functions in the contract:
- Timelock: The startTimer, endTimer, and timeIsOver functions and variables are used to implement a timelock feature. The startTimer function sets the start variable to the current block’s timestamp, and the endTimer function sets the end variable to the sum of the desired timelock period and the start variable. The timeIsOver modifier is used to ensure that a function can only be called if the current block’s timestamp is within the timelock period.
- Transactions: The Transaction struct stores information about a transaction, such as a recipient address, the number of funds to be transferred, and any associated data. The transactions array stores all the transactions submitted to the wallet.
- Confirmations: The numConfirmationsRequired variable stores the number of confirmations required for a transaction to be executed. The isConfirmed mapping is used to track the confirmations received for each transaction. An owner can confirm or revoke their confirmation for a transaction using the confirmTransaction and revokeConfirmation functions.
- Executing transactions: The executeTransaction function is used to execute a transaction if the required number of confirmations have been received and the timelock period has not expired. The notExecuted and timeIsOver modifiers are used to ensure that a transaction is not executed multiple times and that it is executed within the timelock period, respectively.
- Access control: The onlyOwner modifier is used to ensure that only owners can call certain functions. The isOwner mapping is used to track which addresses are owners.
Here is a detailed explanation of the above code:
The start and end variables store the start and end times for the timelock period, respectively. The timeIsOver modifier ensures that a function can only be called if the current block’s timestamp is within the timelock period.
The startTimer function sets the start variable to the current block’s timestamp, and the endTimer function sets the end variable to the sum of the desired timelock period and the start variable. The timeLeft function returns the time left in the timelock period.
The owners array stores the addresses of the owners of the wallet. The isOwner mapping is used to track which addresses are owners. The numConfirmationsRequired variable stores the number of confirmations required for a transaction to be executed. The Transaction struct stores information about a transaction, such as a recipient address, the number of funds to be transferred, and any associated data. The transactions array stores all the transactions submitted to the wallet. The isConfirmed mapping is used to track the confirmations received for each transaction.
The onlyOwner modifier ensures that only owners can call certain functions. The txExists modifier ensures that a transaction with the specified index exists. The notExecuted modifier is used to ensure that a transaction has not already been executed. The notConfirmed modifier is used to ensure that the calling address has not already confirmed a transaction.
The constructor is used to initialize the wallet. It takes an array of owner addresses as input and stores them in the owner’s array. It also sets the isOwner mapping for each owner address and calculates the number of confirmations required for a transaction to be executed.
The receive function is the fallback function for the contract. It allows the contract to receive funds from external sources. It emits the Deposit event to track the incoming funds.
The submitTransaction function is used to submit a new transaction to the wallet. It takes the recipient address, the transfer value, and associated data as input. It also takes a timelock period as input, which determines how long the transaction will be open for confirmations. The function adds a new transaction to the transactions array and sets the end variable to the sum of the timelock period and the start variable. It also emits the SubmitTransactionDetails event to track the submission of the transaction.
The confirmTransaction function is used to confirm a transaction. It takes the index of the transaction as input and increments the numConfirmations field in the Transaction struct. It also sets the isConfirmed mapping for the transaction and the calling address to true. The function is restricted to only owners, and it checks that the transaction exists, has not been executed, and has not already been confirmed by the calling address. It emits the ConfirmTransaction event to track the confirmation of the transaction.
The executeTransaction function is used to execute a transaction. It takes the index of the transaction as input and checks that the transaction has received enough confirm
However, we are free to add several functionalities to the contract. Here are some of the extra functionalities we can add to the contract. Please take it as an exercise.
- Transaction queue: You can add a queue of transactions waiting for approvals. This will allow co-signers to add their approvals for multiple transactions at once rather than waiting for each transaction to be executed before adding their approval for the next one.
- Nonce system: You can add a nonce system to prevent replay attacks. A replay attack is when a transaction is copied and broadcasted again, potentially causing it to be executed multiple times. A nonce is a unique number that is incremented with each transaction, and it can be included in the signed message to ensure that each transaction is only executed once.
- Emergency stop: You can add an emergency stop function that allows one co-signer to halt all transactions until the emergency is resolved. This can be useful in cases where attackers are targeting the wallet or if a contract bug needs to be fixed.
- Daily limit: You can add a daily limit to the number of funds transferred from the wallet. This can be useful in preventing large, unauthorized transfers from occurring.
Whitelist: You can add a whitelist of addresses that are allowed to receive funds from the wallet. This can be useful to prevent funds from being sent to addresses that are not intended to receive them.
Please Login to comment...