templates.multisig_view

  1import smartpy as sp
  2
  3
  4@sp.module
  5def main():
  6    class MultisigView(sp.Contract):
  7        """Multiple members vote for arbitrary bytes.
  8
  9        This contract can be originated with a list of addresses and a number of
 10        required votes. Any member can submit as many bytes as they want and vote
 11        for active proposals.
 12
 13        Any bytes that reached the required votes can be confirmed via a view.
 14        """
 15
 16        def __init__(self, members, required_votes):
 17            """Constructor
 18
 19            Args:
 20                members (sp.set of sp.address): people who can submit and vote for
 21                    lambda.
 22                required_votes (sp.nat): number of votes required
 23            """
 24            assert required_votes <= sp.len(
 25                members
 26            ), "required_votes must be <= len(members)"
 27            self.data.proposals = sp.cast(sp.big_map(), sp.big_map[sp.bytes, sp.bool])
 28            self.data.votes = sp.cast(
 29                sp.big_map(), sp.big_map[sp.bytes, sp.set[sp.address]]
 30            )
 31            self.data.members = sp.cast(members, sp.set[sp.address])
 32            self.data.required_votes = sp.cast(required_votes, sp.nat)
 33
 34        @sp.entrypoint
 35        def submit_proposal(self, bytes):
 36            """Submit a new proposal to the vote.
 37
 38            Submitting a proposal does not imply casting a vote in favour of it.
 39
 40            Args:
 41                bytes(sp.bytes): bytes proposed to vote.
 42            Raises:
 43                `You are not a member`
 44            """
 45            assert self.data.members.contains(sp.sender), "You are not a member"
 46            self.data.proposals[bytes] = False
 47            self.data.votes[bytes] = set()
 48
 49        @sp.entrypoint
 50        def vote_proposal(self, bytes):
 51            """Vote for a proposal.
 52
 53            There is no vote against or pass. If one disagrees with a proposal they
 54            can avoid to vote. Warning: old non-voted proposals never become
 55            obsolete.
 56
 57            Args:
 58                id(sp.bytes): bytes of the proposal.
 59            Raises:
 60                `You are not a member`, `Proposal not found`
 61            """
 62            assert self.data.members.contains(sp.sender), "You are not a member"
 63            assert self.data.proposals.contains(bytes), "Proposal not found"
 64            self.data.votes[bytes].add(sp.sender)
 65            if sp.len(self.data.votes[bytes]) >= self.data.required_votes:
 66                self.data.proposals[bytes] = True
 67
 68        @sp.onchain_view()
 69        def is_voted(self, id):
 70            """Returns a boolean indicating whether the proposal has been voted on.
 71
 72            Args:
 73                id (sp.bytes): bytes of the proposal
 74            Return:
 75                (sp.bool): True if the proposal has been voted, False otherwise.
 76            """
 77            return self.data.proposals.get(id, error="Proposal not found")
 78
 79
 80if "main" in __name__:
 81
 82    @sp.add_test()
 83    def basic_scenario():
 84        """A scenario with a vote on the multisigView contract.
 85
 86        Tests:
 87        - Origination
 88        - Proposal submission
 89        - Proposal vote
 90        """
 91        sc = sp.test_scenario("MultisigView basic scenario", main)
 92        sc.h1("Basic scenario.")
 93
 94        member1 = sp.test_account("member1")
 95        member2 = sp.test_account("member2")
 96        member3 = sp.test_account("member3")
 97        members = sp.set([member1.address, member2.address, member3.address])
 98
 99        sc.h2("Origination")
100        c1 = main.MultisigView(members, 2)
101        sc += c1
102
103        sc.h2("submit_proposal")
104        c1.submit_proposal(sp.bytes("0x42"), _sender=member1)
105
106        sc.h2("vote_proposal")
107        c1.vote_proposal(sp.bytes("0x42"), _sender=member1)
108        c1.vote_proposal(sp.bytes("0x42"), _sender=member2)
109
110        # We can check that the proposal has been validated.
111        sc.verify(c1.is_voted(sp.bytes("0x42")))