Multisign wallet
Intro Multisig wallet
Usually, blockchain wallets are generally generated by a unique private key. All assets or transactions are controlled and signed by that private key. That key holder can do anything with that wallet. This is great for individuals, as it ensures privacy and security, as only the owner of the private key has full rights to those assets.
But with an organization, such as assets after IDO, private sale, or common company assets, it is different, if this asset is controlled by a single person, there will be a lot of risk. can happen like:
- This person took all the assets and disappeared.
- Due to some reason, this person loses the private key, then recovering the private key is not possible, which means that the entire property will also be inaccessible.
- If this person unfortunately dies, no one can inherit or use the property anymore because there is no private key.
To solve the above problems, Multisig Wallet was born to minimize those risks.
What is multisig wallet?
In essence Multisig Wallet is a smart contract on the blockchain that allows certain logic to be processed when there are enough required signatures.
Multisig is short for Multi Signature.
A Multisig Wallet has the following properties:
- Parties agree on an action
- The rules in the smart contract have been built
- Smart contract can receive cryptocurrency (e.g. Ether)
- Smart contract can receive request, can be able to process that request based on consensus signing
- According to the multisig address configuration, it may require a different key combination: 2-of-3 is the most common key, where only 2 signatures are enough to access the funds of an address 3 signature. However, there are many other variations, such as 2 of 2, 3 of 3, 3 of 4, etc.
How does it work?
As a simple analogy, we can picture a safe with two locks and two keys. One key is held by Alice and the other is held by Bob. The only way they can open the box is to provide both keys at the same time, so one cannot open the box without the other’s consent.
Basically, funds stored on a multi-signature address can only be accessed using 2 or more signatures. Thus, using a multisig wallet allows users to create an extra layer of security for their funds. But before going any further, it is important to understand the basics of standard Bitcoin addresses, which are based on a single key rather than multiple (single key addresses).
Pros and cons of Multisig Wallet
Everything has two sides, security and convenience are always two opposite sides of each other, so is Multisig Wallet.
Advantages
- Higher level of safety for any web3 user.
- Multisig Wallet enhances the security of assets: For example, we can set up a multisig wallet for 3 accounts at 3 devices: phone, tablet, laptop. Each transaction can only be done when there is confirmation from 2 of those 3 devices. At this time, if we assume that we lose our phone, we can still confirm the transaction using tablets and laptops, while the thief cannot confirm the transaction with just one device.
- Multisig Wallet dispute resolution: An example is A and B buying and selling assets, they decide to use a 2-of-3 multisig wallet with the participation of 3 parties A, B, and ruling party C. In case A and B have signed agree or cancelled, the participation of C will not be needed. Otherwise if only one sign co-sign, and the other person signs cancellation, then C’s signature will decide whether the transaction will be agreeed or cancelled by a majority of 2 out of 3 decisions.
- Decision making: A board of directors might use a multisig wallet to control access to a company’s funds. For example, by setting up a 4-of-6 wallet where each board member holds one key, no individual board member is able to misuse the funds. Therefore, only decisions that are agreed upon by the majority can be executed.
Defect
- In the above example, suppose we need 3 devices to confirm the transaction, and we lose our phone? neither me nor the thief can confirm any more transactions? That’s the downside of Multisig Wallet: the rules are too complicated to set up effectively for each specific problem.
In this case, 2FA can be an effective solution to save the backup code for the account on the device, if we lose the device, we can still get the backup code back, which means getting the account back. .
- Both blockchain and multisig are still new technologies, the security audit is still limited. There is no 100% guarantee that what works today won’t be hacked tomorrow. For example, the $300 million hack of Parity Wallet.
- More gas fee when you have to submit your signature to verify a request and deplay to making final transaction.
- Still limited action by smart contract operations.
Build Minimum Multisig Wallet
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract MultiSigWallet {
event Deposit(address indexed sender, uint amount, uint balance);
event SubmitTransaction(
address indexed owner,
uint indexed txIndex,
address indexed to,
uint value,
bytes data
);
event ConfirmTransaction(address indexed owner, uint indexed txIndex);
event RevokeConfirmation(address indexed owner, uint indexed txIndex);
event ExecuteTransaction(address indexed owner, uint indexed txIndex);
address[] public owners;
mapping(address => bool) public isOwner;
uint public numConfirmationsRequired;
struct Transaction {
address to;
uint value;
bytes data;
bool executed;
uint numConfirmations;
}
// mapping from tx index => owner => bool
mapping(uint => mapping(address => bool)) public isConfirmed;
Transaction[] public transactions;
modifier onlyOwner() {
require(isOwner[msg.sender], "not owner");
_;
}
modifier txExists(uint _txIndex) {
require(_txIndex < transactions.length, "tx does not exist");
_;
}
modifier notExecuted(uint _txIndex) {
require(!transactions[_txIndex].executed, "tx already executed");
_;
}
modifier notConfirmed(uint _txIndex) {
require(!isConfirmed[_txIndex][msg.sender], "tx already confirmed");
_;
}
constructor(address[] memory _owners, uint _numConfirmationsRequired) {
require(_owners.length > 0, "owners required");
require(
_numConfirmationsRequired > 0 &&
_numConfirmationsRequired <= _owners.length,
"invalid number of required confirmations"
);
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);
}
numConfirmationsRequired = _numConfirmationsRequired;
}
receive() external payable {
emit Deposit(msg.sender, msg.value, address(this).balance);
}
function submitTransaction(
address _to,
uint _value,
bytes memory _data
) public onlyOwner {
uint txIndex = transactions.length;
transactions.push(
Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
numConfirmations: 0
})
);
emit SubmitTransaction(msg.sender, txIndex, _to, _value, _data);
}
function confirmTransaction(uint _txIndex)
public
onlyOwner
txExists(_txIndex)
notExecuted(_txIndex)
notConfirmed(_txIndex)
{
Transaction storage transaction = transactions[_txIndex];
transaction.numConfirmations += 1;
isConfirmed[_txIndex][msg.sender] = true;
emit ConfirmTransaction(msg.sender, _txIndex);
}
function executeTransaction(uint _txIndex)
public
onlyOwner
txExists(_txIndex)
notExecuted(_txIndex)
{
Transaction storage transaction = transactions[_txIndex];
require(
transaction.numConfirmations >= numConfirmationsRequired,
"cannot execute tx"
);
transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(
transaction.data
);
require(success, "tx failed");
emit ExecuteTransaction(msg.sender, _txIndex);
}
function revokeConfirmation(uint _txIndex)
public
onlyOwner
txExists(_txIndex)
notExecuted(_txIndex)
{
Transaction storage transaction = transactions[_txIndex];
require(isConfirmed[_txIndex][msg.sender], "tx not confirmed");
transaction.numConfirmations -= 1;
isConfirmed[_txIndex][msg.sender] = false;
emit RevokeConfirmation(msg.sender, _txIndex);
}
function getOwners() public view returns (address[] memory) {
return owners;
}
function getTransactionCount() public view returns (uint) {
return transactions.length;
}
function getTransaction(uint _txIndex)
public
view
returns (
address to,
uint value,
bytes memory data,
bool executed,
uint numConfirmations
)
{
Transaction storage transaction = transactions[_txIndex];
return (
transaction.to,
transaction.value,
transaction.data,
transaction.executed,
transaction.numConfirmations
);
}
}
Deployed Multisig Wallets
There is no universal standard for writing Multisig Wallet, but we can refer to the implementation from famous wallets being used in the world to be able to design or inherit our own implementation.
- ConsenSys’ Multisig Wallet: This can be considered the simplest implementation of Multisig Wallet, the solidity version used is also 0.4.10 a long time ago. , but is extremely valuable, at the time of writing this wallet is holding 80,000 Ether, or about 17 million dollars. You can look up this wallet here.
- Gnosis’ Multisig Wallet: is an upgraded version of Consensys Multisig Wallet, written according to Truffle project’s structure, fully tested and regularly updated. At the time of writing, this github project is still being updated.
- Gnosis’ Multisig Wallet: is an upgraded version of Consensys Multisig Wallet, written according to the structure of the hardhat project.
- BitGo’s Multisig Wallet: also a structured version of Truffle, fully tested and regularly updated. The difference here is that the contract has more complex logic, one of which is ERC20-Token Compatibility. And this wallet implements 2-of-3 signing method, which means that there are exactly 3 parties involved, and 2 signatures are needed to agree for a transaction to take place.
- Ethereum Dapp’s Multisig Wallet: Compatible with Ethereum Wallet or Mist, we can deploy Multisig Wallet here, and easily call
send transaction
orconfirm
directly. However, there is a disadvantage that there is no document, we have to read the code for more details. - Parity’s Multisig Wallet (NOT RECOMMENDED): This is also a very famous Multisig Wallet before it was [hacked and lost $300 million](https://medium.com /chain-cloud-company-blog/parity-multisig-hack-again-b46771eaa838) on 11/06/2017. The reason is because the implementation is not good, and therefore it is not recommended anymore.
Referrence
- https://www.curvegrid.com/docs/multi-signature-multisig-wallet-smart-contracts
- https://www.binance.vision/security/what-is-a-multisig-wallet
- https://medium.com/hellogold/ethereum-multi-signature-wallets-77ab926ab63b
- https://www.gemini.com/cryptopedia/what-is-a-multi-sig-wallet-crypto-multi-signature-wallet