Multi-Signature WalletΒΆ
Warning
This is example code for learning purposes. Do not use in production without thorough review and testing.
A multi-signature wallet requiring multiple owner approvals to execute transactions.
1#pragma version >0.3.10
2
3###########################################################################
4## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
5###########################################################################
6
7# An example of how you can implement a wallet in Vyper.
8
9# A list of the owners addresses (there are a maximum of 5 owners)
10owners: public(address[5])
11# The number of owners required to approve a transaction
12threshold: int128
13# The number of transactions that have been approved
14seq: public(int128)
15
16
17@deploy
18def __init__(_owners: address[5], _threshold: int128):
19 for i: uint256 in range(5):
20 if _owners[i] != empty(address):
21 self.owners[i] = _owners[i]
22 self.threshold = _threshold
23
24
25@external
26def testEcrecover(h: bytes32, v:uint8, r:bytes32, s:bytes32) -> address:
27 return ecrecover(h, v, r, s)
28
29
30# `@payable` allows functions to receive ether
31@external
32@payable
33def approve(_seq: int128, to: address, _value: uint256, data: Bytes[4096], sigdata: uint256[3][5]) -> Bytes[4096]:
34 # Throws if the value sent to the contract is less than the sum of the value to be sent
35 assert msg.value >= _value
36 # Every time the number of approvals starts at 0 (multiple signatures can be added through the sigdata argument)
37 approvals: int128 = 0
38 # Starts by combining:
39 # 1) The number of transactions approved thus far.
40 # 2) The address the transaction is going to be sent to (can be a contract or a user).
41 # 3) The value in wei that will be sent with this transaction.
42 # 4) The data to be sent with this transaction (usually data is used to deploy contracts or to call functions on contracts, but you can put whatever you want in it).
43 # Takes the keccak256 hash of the combination
44 h: bytes32 = keccak256(concat(convert(_seq, bytes32), convert(to, bytes32), convert(_value, bytes32), data))
45 # Then we combine the Ethereum Signed message with our previous hash
46 # Owners will have to sign the below message
47 h2: bytes32 = keccak256(concat(b"\x19Ethereum Signed Message:\n32", h))
48 # Verifies that the caller of approve has entered the correct transaction number
49 assert self.seq == _seq
50 # # Iterates through all the owners and verifies that there signatures,
51 # # given as the sigdata argument are correct
52 for i: uint256 in range(5):
53 if sigdata[i][0] != 0:
54 # If an invalid signature is given for an owner then the contract throws
55 assert ecrecover(h2, sigdata[i][0], sigdata[i][1], sigdata[i][2]) == self.owners[i]
56 # ecrecover handles multiple types
57 assert ecrecover(h2, convert(sigdata[i][0], uint8), convert(sigdata[i][1], bytes32), convert(sigdata[i][2], bytes32)) == self.owners[i]
58 # For every valid signature increase the number of approvals by 1
59 approvals += 1
60 # Throw if the number of approvals is less then the number of approvals required (the threshold)
61 assert approvals >= self.threshold
62 # The transaction has been approved
63 # Increase the number of approved transactions by 1
64 self.seq += 1
65 # Use raw_call to send the transaction
66 return raw_call(to, data, max_outsize=4096, gas=3_000_000, value=_value)
67
68
69@external
70@payable
71def __default__():
72 pass
Key concepts:
Threshold signatures: Requires
thresholdout of 5 owners to approveSignature verification: Uses
ecrecoverto verify owner signaturesReplay protection:
seqcounter prevents transaction replayArbitrary calls:
raw_callexecutes any transaction once approved
The approval process:
Owners sign a hash of (sequence number, destination, value, data)
Anyone can call
approve()with the collected signaturesIf enough valid signatures are provided, the transaction executes
Note
This demonstrates signature verification patterns. Production multisigs need additional safeguards like time locks and nonce management.