templates.multisig
1# Two Level Multisig - Example for illustrative purposes only. 2 3import smartpy as sp 4 5# MultiSigFactory is a two level multisig factory contract 6# - two level because to validate a property, we can use several groups of participants and not only one group, 7# - and a factory because it can hold several such two level multisig contracts. 8 9 10@sp.module 11def t(): 12 participant: type = sp.record(hasVoted=sp.bool, weight=sp.int, id=sp.address) 13 14 group: type = sp.record( 15 weight=sp.int, 16 voters=sp.int, 17 contractWeight=sp.int, 18 thresholdWeight=sp.int, 19 thresholdVoters=sp.int, 20 participants=sp.list[participant], 21 ok=sp.bool, 22 ) 23 24 contract: type = sp.record( 25 amount=sp.mutez, 26 name=sp.string, 27 owner=sp.address, 28 thresholdGroupsOK=sp.int, 29 groupsOK=sp.int, 30 thresholdWeight=sp.int, 31 weight=sp.int, 32 groups=sp.list[group], 33 ok=sp.bool, 34 ) 35 36 37@sp.module 38def main(): 39 class MultiSigFactory(sp.Contract): 40 def __init__(self): 41 self.data.multisigs = sp.cast(sp.big_map(), sp.big_map[sp.nat, t.contract]) 42 self.data.nbMultisigs = sp.nat(0) 43 44 @sp.entrypoint 45 def build(self, params): 46 self.data.multisigs[self.data.nbMultisigs] = params.contract 47 self.data.nbMultisigs += 1 48 49 @sp.entrypoint 50 def sign(self, id, contractId, contractName): 51 assert id == sp.sender 52 sp.cast(contractName, sp.string) 53 contract = self.data.multisigs[contractId] 54 assert contractName == contract.name 55 sp.cast(contract.weight, sp.int) 56 sp.cast(contract.groupsOK, sp.int) 57 for group in contract.groups: 58 for participant in group.participants: 59 if participant.id == id: 60 assert not participant.hasVoted 61 participant.hasVoted = True 62 sp.cast(group.weight, sp.int) 63 group.weight += participant.weight 64 group.voters += 1 65 if ( 66 not group.ok 67 and group.thresholdVoters <= group.voters 68 and group.thresholdWeight <= group.weight 69 ): 70 group.ok = True 71 contract.weight += group.contractWeight 72 contract.groupsOK += 1 73 if ( 74 not contract.ok 75 and contract.thresholdGroupsOK <= contract.groupsOK 76 and contract.thresholdWeight <= contract.weight 77 ): 78 contract.ok = True 79 self.onOK(contract) 80 81 @sp.private(with_storage="read-write", with_operations=True) 82 def onOK(self, contract): 83 return () 84 85 # MultiSigFactoryWithPayment inherits from MultiSigFactory and adds a 86 # payment functionality 87 88 class MultiSigFactoryWithPayment(MultiSigFactory): 89 def __init__(self): 90 MultiSigFactory.__init__(self) 91 92 @sp.private(with_storage="read-write", with_operations=True) 93 def onOK(self, contract): 94 sp.send(contract.owner, contract.amount) 95 96 97def addMultiSig(c, thresholdWeight, thresholdGroupsOK): 98 # tgroup = c.getStorageType().go('multisigs').go('list').go('groups').go('list') 99 # tparticipant = tgroup.go('participants').go('list') 100 def group(contractWeight, thresholdWeight, thresholdVoters, participants): 101 participants = [ 102 sp.record(hasVoted=False, weight=weight, id=id) 103 for (id, weight) in participants 104 ] 105 return sp.record( 106 weight=0, 107 voters=0, 108 contractWeight=contractWeight, 109 thresholdWeight=thresholdWeight, 110 thresholdVoters=thresholdVoters, 111 participants=participants, 112 ok=False, 113 ) 114 115 p1 = sp.address("tz1NFevnqBrtcZTZTeKP2YBBjsPs9bih5i3J") 116 p2 = sp.address("tz1ZRjMiF9K9n3S9AcUrTGUzR2okS7dn9KXS") 117 p3 = sp.address("tz1NLJRAAwYdggijWz9EFtX5Dgs95BLfD6mP") 118 g1 = group(5, 5, 2, [(p1, 2), (p2, 8), (p3, 1)]) 119 g2 = group(7, 5, 1, [(p1, 7), (p2, 8)]) 120 g3 = group(7, 10, 1, [(p3, 10)]) 121 contract = sp.record( 122 amount=sp.tez(0), 123 name="demo", 124 owner=p1, 125 thresholdGroupsOK=thresholdGroupsOK, 126 groupsOK=0, 127 thresholdWeight=thresholdWeight, 128 weight=0, 129 groups=sp.list([g1, g2, g3]), 130 ok=False, 131 ) 132 return c.build(contract=contract) 133 134 135# Tests 136@sp.add_test() 137def test(): 138 alice = sp.test_account("Alice") 139 bob = sp.test_account("Rob") 140 charlie = sp.test_account("Charlie") 141 142 scenario = sp.test_scenario("MultiSig", [t, main]) 143 c1 = main.MultiSigFactory() 144 scenario.h1("Multi Sig") 145 scenario.h2("Contract") 146 scenario.h3("Simple multisig factories") 147 scenario += c1 148 scenario.h2("First: define a simple multisig") 149 addMultiSig(c1, thresholdWeight=10, thresholdGroupsOK=2) 150 scenario.h2("Message execution") 151 scenario.h3("A first move") 152 c1.sign(id=alice.address, contractId=0, contractName="demo", _sender=alice) 153 c1.sign(id=bob.address, contractId=0, contractName="demo", _sender=bob) 154 155 scenario.h2("First: define a simple multi-sig") 156 addMultiSig(c1, thresholdWeight=10, thresholdGroupsOK=3) 157 scenario.h2("Message execution") 158 scenario.h3("A first move") 159 c1.sign(id=alice.address, contractId=1, contractName="demo", _sender=alice) 160 c1.sign(id=bob.address, contractId=1, contractName="demo", _sender=bob) 161 scenario.h4("We need a third vote") 162 c1.sign(id=charlie.address, contractId=1, contractName="demo", _sender=charlie) 163 scenario.h4("Final state") 164 scenario.show(c1.data) 165 166 scenario.h3("Multisig factories with payments") 167 c2 = main.MultiSigFactoryWithPayment() 168 scenario += c2
def
addMultiSig(c, thresholdWeight, thresholdGroupsOK):
98def addMultiSig(c, thresholdWeight, thresholdGroupsOK): 99 # tgroup = c.getStorageType().go('multisigs').go('list').go('groups').go('list') 100 # tparticipant = tgroup.go('participants').go('list') 101 def group(contractWeight, thresholdWeight, thresholdVoters, participants): 102 participants = [ 103 sp.record(hasVoted=False, weight=weight, id=id) 104 for (id, weight) in participants 105 ] 106 return sp.record( 107 weight=0, 108 voters=0, 109 contractWeight=contractWeight, 110 thresholdWeight=thresholdWeight, 111 thresholdVoters=thresholdVoters, 112 participants=participants, 113 ok=False, 114 ) 115 116 p1 = sp.address("tz1NFevnqBrtcZTZTeKP2YBBjsPs9bih5i3J") 117 p2 = sp.address("tz1ZRjMiF9K9n3S9AcUrTGUzR2okS7dn9KXS") 118 p3 = sp.address("tz1NLJRAAwYdggijWz9EFtX5Dgs95BLfD6mP") 119 g1 = group(5, 5, 2, [(p1, 2), (p2, 8), (p3, 1)]) 120 g2 = group(7, 5, 1, [(p1, 7), (p2, 8)]) 121 g3 = group(7, 10, 1, [(p3, 10)]) 122 contract = sp.record( 123 amount=sp.tez(0), 124 name="demo", 125 owner=p1, 126 thresholdGroupsOK=thresholdGroupsOK, 127 groupsOK=0, 128 thresholdWeight=thresholdWeight, 129 weight=0, 130 groups=sp.list([g1, g2, g3]), 131 ok=False, 132 ) 133 return c.build(contract=contract)