ZRC | Title | Status | Type | Author | Created (yyyy-mm-dd) | Updated (yyyy-mm-dd) |
---|---|---|---|---|---|---|
1 | Standard for Multisig Wallet | Draft | Standard | Yeo Te Ye [email protected] | 2020-04-08 | 2020-04-13 |
A multi-signature wallet is a cryptocurrency wallet owned by two or more owners. Whenever a transaction is created, the transaction has to be approved and signed by two or more owners before it can be executed on the blockchain. A multisig wallet can implement various combination of keys: with 2/3 being the most common where 2 signatures are minimally required out of the 3 owners.
ZRC-4 defines a minimum interface of a multisig wallet smart contract. The contract holds funds that can be paid out to arbitrary users, provided that enough people in the designated list of owners sign off on the payout.
The transaction must be added to the contract before signatures can be collected. Once sufficient signatures are collected, the recipient can request for the transaction to be executed and the money paid out.
A standard for multisig wallet can serve as an interface for developers and other companies to implement their own multisig wallets to contain their funds in a more secure fashion. The multisig wallet can be used in escrow scenarios and decision making scenarios to prevent misuse of funds.
The multisig wallet contract specification describes:
- the global error codes to be declared in the library part of the contract;
- the names and types of the immutable and mutable variables (aka
fields
); - the transitions that will allow the changing of values of the mutable variables;
- the events to be emitted by them.
The multisig contract define the following constants for use as error codes for the Error
event.
Name | Type | Code | Description |
---|---|---|---|
NonOwnerCannotSign |
Int32 |
-1 |
Emit when a non-owner attempts to sign a transaction. |
UnknownTransactionId |
Int32 |
-2 |
Emit when a request is made for a transaction which cannot be found. |
InsufficientFunds |
Int32 |
-3 |
Emit when there is insufficient balance for token transaction. |
NoSignatureListFound |
Int32 |
-4 |
Emit when there are no signatures for a valid transaction request. |
AlreadySigned |
Int32 |
-5 |
Emit when an owner attempts to sign a transaction that has already been signed by the same owner. |
NotAlreadySigned |
Int32 |
-6 |
Emit when a request is made to revoke a signature on a exisiting transaction in which the sender has not signed. |
InvalidContract |
Int32 |
-7 |
Emit when a request is made to an invalid multisig contract. |
InvalidAmount |
Int32 |
-8 |
Emit when an owner attempts to send an empty amount to a recipient wallet. |
NotEnoughSignatures |
Int32 |
-9 |
Emit when the number of signatures counts is less than the number of signatures required to execute a transaction. |
SenderMayNotExecute |
Int32 |
-10 |
Emit when a request is made to execute a transaction in which the sender is neither any of the owners nor the receipent of the transaction. |
NonOwnerCannotSubmit |
Int32 |
-11 |
Emit when a non-owner attempts to create a new transaction. |
IncorrectSignatureCount |
Int32 |
-12 |
Emit when trying to revoke a signature of an existing transaction in which there are no signatures. |
Name | Type | Description |
---|---|---|
owners_list |
List ByStr20 |
The list of owners of the multisig wallet. |
required_signatures |
Uint32 |
The number of signatures required to approve and execute a transaction. |
Note: it is a good idea to set required_signatures
to a value strictly less than the number of owners, so that the remaining owners can retrieve the funds should some owners lose their private keys, or unable or unwilling to sign for new transactions.
Name | Type | Description |
---|---|---|
owners |
Map ByStr20 Bool |
Mapping of multisig wallets owners to a boolean. True indicates an owner. |
transactionCount |
Uint32 |
The number of accumulated transactions from contract initialization. |
signatures |
Map Uint32 (Map ByStr20 Bool) |
Mapping from transaction IDs to signees. |
signature_counts |
Map Uint32 Uint32 |
Mapping from transaction IDs to accumulated count of signatures. |
transactions |
Map Uint32 Transaction |
Mapping from transaction IDs to a Transaction object. Transaction object contains the recipient , amount and tag in the form: Trans of ByStr20 Uint128 String . |
Note: Although owners
is listed as a mutable fields, this multisig wallet contract specification is designed to prevent adding or removing owners. Refer to the section V. Update Owners or Change Number of Required Signatures for more information.
(* Creates a transaction request for future signoff *)
transition SubmitTransaction (recipient : ByStr20, amount : Uint128, tag : String)
Arguments:
Name | Type | Description |
---|---|---|
recipient |
ByStr20 |
Address of the recipient to transfer amount to. |
amount |
Uint128 |
Amount of funds to be transferred. |
tag |
String |
Transition name to be invoked. Designed in the scenario of invoking a transition of another contract. Otherwise, the tag should be set to AddFunds . |
Events:
Name | Description | Event Parameters | |
---|---|---|---|
_eventname |
Transaction created |
Transaction is submitted successfully. |
|
_eventname |
Error |
Transaction is not submitted. |
|
(* Sign off on an existing transaction *)
transition SignTransaction (transactionId : Uint32)
Arguments:
Name | Type | Description |
---|---|---|
transactionId |
Uint32 |
Identifier for the transaction request to be signed. |
Events:
Name | Description | Event Parameters | |
---|---|---|---|
_eventname |
Transaction signed |
Transaction is signed successfully. |
|
_eventname |
Error |
Transaction is not signed. |
|
(* Execute signed-off transaction *)
transition ExecuteTransaction (transactionId : Uint32)
Arguments:
Name | Type | Description |
---|---|---|
transactionId |
Uint32 |
Identifier for the transaction request to be executed. |
Messages sent:
Name | Description | Callback Parameters | |
---|---|---|---|
_tag |
ExecuteTransaction |
Provide the sender the status of the execution. |
|
Events:
Name | Description | Event Parameters | |
---|---|---|---|
_eventname |
Transaction executed |
Transaction is executed successfully. |
|
_eventname |
Error |
Transaction is not executed. |
|
(* Revoke signature of existing transaction, if it has not yet been executed. *)
transition RevokeSignature (transactionId : Uint32)
Arguments:
Name | Type | Description |
---|---|---|
transactionId |
Uint32 |
Transaction identifier in which the signature is to be removed from. |
Events:
Name | Description | Event Parameters | |
---|---|---|---|
_eventname |
Signature revoked |
Signature is revoked from the transaction successfully. |
|
_eventname |
Error |
Signature is not revoked. |
|
(* Add native funds to wallet *)
transition AddFunds ()
This multisig wallet contract is designed to prevent adding or removing owners, or changing the number of required signatures.
The proposed design for performing the aforementioned changes is:
- Deploy a new wallet with the owners and number of required signatures adjusted.
- On the old wallet, invoke
SubmitTransaction
transition with the following parameters:recipient
: Address of new walletamount
: _balance of old wallettag
:AddFunds
- Next, on the old wallet, have the various owners invoke
SignTransaction
transition with the following parameters until the minimal required signature is reached:transactionId
: <transactionId from (2)>
- Lastly, on the old wallet, have one of the owners invoke
ExecuteTransaction
transition. All the existing balance of the old wallet would be transferred to the new wallet from (1).
To test the reference contract, simply go to the example/zrc4
folder and run one of the JS scripts.
Copyright and related rights waived via CC0.