templates.multisig_lambda
1import smartpy as sp 2 3 4@sp.module 5def main(): 6 operation_lambda: type = sp.lambda_(sp.unit, sp.unit, with_operations=True) 7 8 class MultisigLambda(sp.Contract): 9 """Multiple members vote for executing lambdas. 10 11 This contract can be originated with a list of addresses and a number of 12 required votes. Any member can submit as much lambdas as he wants and vote 13 for active proposals. When a lambda reaches the required votes, its code is 14 called and the output operations are executed. This allows this contract to 15 do anything that a contract can do: transferring tokens, managing assets, 16 administrating another contract... 17 18 When a lambda is applied, all submitted lambdas until now are inactivated. 19 The members can still submit new lambdas. 20 """ 21 22 def __init__(self, members, required_votes): 23 """Constructor 24 25 Args: 26 members (sp.set of sp.address): people who can submit and vote 27 for lambda. 28 required_votes (sp.nat): number of votes required 29 """ 30 assert required_votes <= sp.len( 31 members 32 ), "required_votes must be <= len(members)" 33 self.data.lambdas = sp.cast( 34 sp.big_map(), sp.big_map[sp.nat, operation_lambda] 35 ) 36 self.data.votes = sp.cast( 37 sp.big_map(), sp.big_map[sp.nat, sp.set[sp.address]] 38 ) 39 self.data.nextId = 0 40 self.data.inactiveBefore = 0 41 self.data.members = sp.cast(members, sp.set[sp.address]) 42 self.data.required_votes = sp.cast(required_votes, sp.nat) 43 44 @sp.entrypoint 45 def submit_lambda(self, lambda_): 46 """Submit a new lambda to the vote. 47 48 Submitting a proposal does not imply casting a vote in favour of it. 49 50 Args: 51 lambda_(sp.lambda with operations): lambda proposed to vote. 52 Raises: 53 `You are not a member` 54 """ 55 assert self.data.members.contains(sp.sender), "You are not a member" 56 self.data.lambdas[self.data.nextId] = lambda_ 57 self.data.votes[self.data.nextId] = set() 58 self.data.nextId += 1 59 60 @sp.entrypoint 61 def vote_lambda(self, id): 62 """Vote for a lambda. 63 64 Args: 65 id(sp.nat): id of the lambda to vote for. 66 Raises: 67 `You are not a member`, `The lambda is inactive`, `Lambda not found` 68 69 There is no vote against or pass. If someone disagrees with a lambda 70 they can avoid to vote. 71 """ 72 assert self.data.members.contains(sp.sender), "You are not a member" 73 assert id >= self.data.inactiveBefore, "The lambda is inactive" 74 assert self.data.lambdas.contains(id), "Lambda not found" 75 self.data.votes[id].add(sp.sender) 76 if sp.len(self.data.votes[id]) >= self.data.required_votes: 77 self.data.lambdas[id]() 78 self.data.inactiveBefore = self.data.nextId 79 80 @sp.onchain_view() 81 def get_lambda(self, id): 82 """Return the corresponding lambda. 83 84 Args: 85 id (sp.nat): id of the lambda to get. 86 87 Return: 88 pair of the lambda and a boolean showing if the lambda is active. 89 """ 90 return (self.data.lambdas[id], id >= self.data.inactiveBefore) 91 92 93# if "main" in __name__: 94 95 96@sp.module 97def test(): 98 class Administrated(sp.Contract): 99 def __init__(self, admin): 100 self.data.admin = admin 101 self.data.value = sp.int(0) 102 103 @sp.entrypoint 104 def set_value(self, value): 105 assert sp.sender == self.data.admin 106 self.data.value = value 107 108 109@sp.add_test() 110def basic_scenario(): 111 """Use the multisigLambda as an administrator of an example contract. 112 113 Tests: 114 - Origination 115 - Lambda submission 116 - Lambda vote 117 """ 118 sc = sp.test_scenario("MultisigLambda basic scenario", [main, test]) 119 sc.h1("Basic scenario.") 120 121 member1 = sp.test_account("member1") 122 member2 = sp.test_account("member2") 123 member3 = sp.test_account("member3") 124 members = sp.set([member1.address, member2.address, member3.address]) 125 126 sc.h2("MultisigLambda: origination") 127 c1 = main.MultisigLambda(members, 2) 128 sc += c1 129 130 sc.h2("Administrated: origination") 131 c2 = test.Administrated(c1.address) 132 sc += c2 133 134 sc.h2("MultisigLambda: submit_lambda") 135 136 def set_42(params): 137 administrated = sp.contract(sp.int, c2.address, entrypoint="set_value") 138 sp.transfer(sp.int(42), sp.tez(0), administrated.unwrap_some()) 139 140 lambda_ = sp.build_lambda(set_42, with_operations=True) 141 c1.submit_lambda(lambda_, _sender=member1) 142 143 sc.h2("MultisigLambda: vote_lambda") 144 c1.vote_lambda(0, _sender=member1) 145 c1.vote_lambda(0, _sender=member2) 146 147 # We can check that the administrated contract received the transfer. 148 sc.verify(c2.data.value == 42)
basic_scenario =
None
Use the multisigLambda as an administrator of an example contract.
Tests:
- Origination
- Lambda submission
- Lambda vote