Module multisig_lambda

Expand source code
import smartpy as sp


class MultisigLambda(sp.Contract):
    """Multiple members vote for executing lambdas.

    This contract can be originated with a list of addresses and a number of
    required votes. Any member can submit as much lambdas as he wants and vote
    for active proposals. When a lambda reaches the required votes, its code is
    called and the output operations are executed. This allows this contract to
    do anything that a contract can do: transferring tokens, managing assets,
    administrating another contract...

    When a lambda is applied, all submitted lambdas until now are inactivated.
    The members can still submit new lambdas.
    """

    def __init__(self, members, required_votes):
        """Constructor

        Args:
            members (sp.TSet(sp.TAddress)): people who can submit and vote for
                lambda.
            required_votes (sp.TNat): number of votes required
        """
        assert required_votes <= len(members), "required_votes must be <= len(members)"
        self.init(
            lambdas=sp.big_map(
                tkey=sp.TNat, tvalue=sp.TLambda(sp.TUnit, sp.TUnit, with_operations = True)
            ),
            votes=sp.big_map(tkey=sp.TNat, tvalue=sp.TSet(sp.TAddress)),
            nextId=0,
            inactiveBefore=0,
            members=sp.set(members, t=sp.TAddress),
            required_votes=required_votes,
        )

    @sp.entrypoint(new_syntax=True)
    def submit_lambda(self, lambda_):
        """Submit a new lambda to the vote.

        Submitting a proposal does not imply casting a vote in favour of it.

        Args:
            lambda_(sp.TLambda(sp.TUnit,sp.TList(sp.TOperation))): lambda
                proposed to vote.
        Raises:
            `You are not a member`
        """
        assert self.data.members.contains(sp.sender), "You are not a member"
        self.data.lambdas[self.data.nextId] = lambda_
        self.data.votes[self.data.nextId] = sp.set()
        self.data.nextId += 1

    @sp.entrypoint(new_syntax=True)
    def vote_lambda(self, id):
        """Vote for a lambda.

        Args:
            id(sp.TNat): id of the lambda to vote for.
        Raises:
            `You are not a member`, `The lambda is inactive`, `Lambda not found`

        There is no vote against or pass. If someone disagrees with a lambda
        they can avoid to vote.
        """
        assert self.data.members.contains(sp.sender), "You are not a member"
        assert id >= self.data.inactiveBefore, "The lambda is inactive"
        assert self.data.lambdas.contains(id), "Lambda not found"
        self.data.votes[id].add(sp.sender)
        if sp.len(self.data.votes[id]) >= self.data.required_votes:
            _ = self.data.lambdas[id](())
            self.data.inactiveBefore = self.data.nextId

    @sp.onchain_view()
    def get_lambda(self, id):
        """Return the corresponding lambda.

        Args:
            id (sp.TNat): id of the lambda to get.

        Return:
            pair of the lambda and a boolean showing if the lambda is active.
        """
        sp.result(sp.pair(self.data.lambdas[id], id >= self.data.inactiveBefore))


#if "templates" not in __name__:

class Administrated(sp.Contract):
        def __init__(self, admin):
            self.init(admin=admin, value=sp.int(0))

        @sp.entrypoint(new_syntax=True)
        def set_value(self, value):
            assert sp.sender == self.data.admin
            self.data.value = value

@sp.add_test(name="MultisigLambda basic scenario", is_default=True)
def basic_scenario():
        """Use the multisigLambda as an administrator of an example contract.

        Tests:
        - Origination
        - Lambda submission
        - Lambda vote
        """
        sc = sp.test_scenario()
        sc.h1("Basic scenario.")

        member1 = sp.test_account("member1")
        member2 = sp.test_account("member2")
        member3 = sp.test_account("member3")
        members = [member1.address, member2.address, member3.address]

        sc.h2("MultisigLambda: origination")
        c1 = MultisigLambda(members, required_votes=2)
        sc += c1

        sc.h2("Administrated: origination")
        c2 = Administrated(c1.address)
        sc += c2

        sc.h2("MultisigLambda: submit_lambda")

        def set_42(params):
            administrated = sp.contract(sp.TInt, c2.address, entrypoint="set_value")
            sp.transfer(sp.int(42), sp.tez(0), administrated.open_some())

        lambda_ = sp.build_lambda(set_42, with_operations=True)
        c1.submit_lambda(lambda_).run(sender=member1)

        sc.h2("MultisigLambda: vote_lambda")
        c1.vote_lambda(0).run(member1)
        c1.vote_lambda(0).run(member2)

        # We can check that the administrated contract received the transfer.
        sc.verify(c2.data.value == 42)

Classes

class MultisigLambda (members, required_votes)

Multiple members vote for executing lambdas.

This contract can be originated with a list of addresses and a number of required votes. Any member can submit as much lambdas as he wants and vote for active proposals. When a lambda reaches the required votes, its code is called and the output operations are executed. This allows this contract to do anything that a contract can do: transferring tokens, managing assets, administrating another contract…

When a lambda is applied, all submitted lambdas until now are inactivated. The members can still submit new lambdas.

Constructor

Args

members (sp.TSet(sp.TAddress)): people who can submit and vote for
lambda.
required_votes : sp.TNat
number of votes required
Expand source code
class MultisigLambda(sp.Contract):
    """Multiple members vote for executing lambdas.

    This contract can be originated with a list of addresses and a number of
    required votes. Any member can submit as much lambdas as he wants and vote
    for active proposals. When a lambda reaches the required votes, its code is
    called and the output operations are executed. This allows this contract to
    do anything that a contract can do: transferring tokens, managing assets,
    administrating another contract...

    When a lambda is applied, all submitted lambdas until now are inactivated.
    The members can still submit new lambdas.
    """

    def __init__(self, members, required_votes):
        """Constructor

        Args:
            members (sp.TSet(sp.TAddress)): people who can submit and vote for
                lambda.
            required_votes (sp.TNat): number of votes required
        """
        assert required_votes <= len(members), "required_votes must be <= len(members)"
        self.init(
            lambdas=sp.big_map(
                tkey=sp.TNat, tvalue=sp.TLambda(sp.TUnit, sp.TUnit, with_operations = True)
            ),
            votes=sp.big_map(tkey=sp.TNat, tvalue=sp.TSet(sp.TAddress)),
            nextId=0,
            inactiveBefore=0,
            members=sp.set(members, t=sp.TAddress),
            required_votes=required_votes,
        )

    @sp.entrypoint(new_syntax=True)
    def submit_lambda(self, lambda_):
        """Submit a new lambda to the vote.

        Submitting a proposal does not imply casting a vote in favour of it.

        Args:
            lambda_(sp.TLambda(sp.TUnit,sp.TList(sp.TOperation))): lambda
                proposed to vote.
        Raises:
            `You are not a member`
        """
        assert self.data.members.contains(sp.sender), "You are not a member"
        self.data.lambdas[self.data.nextId] = lambda_
        self.data.votes[self.data.nextId] = sp.set()
        self.data.nextId += 1

    @sp.entrypoint(new_syntax=True)
    def vote_lambda(self, id):
        """Vote for a lambda.

        Args:
            id(sp.TNat): id of the lambda to vote for.
        Raises:
            `You are not a member`, `The lambda is inactive`, `Lambda not found`

        There is no vote against or pass. If someone disagrees with a lambda
        they can avoid to vote.
        """
        assert self.data.members.contains(sp.sender), "You are not a member"
        assert id >= self.data.inactiveBefore, "The lambda is inactive"
        assert self.data.lambdas.contains(id), "Lambda not found"
        self.data.votes[id].add(sp.sender)
        if sp.len(self.data.votes[id]) >= self.data.required_votes:
            _ = self.data.lambdas[id](())
            self.data.inactiveBefore = self.data.nextId

    @sp.onchain_view()
    def get_lambda(self, id):
        """Return the corresponding lambda.

        Args:
            id (sp.TNat): id of the lambda to get.

        Return:
            pair of the lambda and a boolean showing if the lambda is active.
        """
        sp.result(sp.pair(self.data.lambdas[id], id >= self.data.inactiveBefore))

Ancestors

  • smartpy.Contract

Methods

def submit_lambda(self, lambda_)

Entrypoint. Submit a new lambda to the vote.

Submitting a proposal does not imply casting a vote in favour of it.

Args

lambda_(sp.TLambda(sp.TUnit,sp.TList(sp.TOperation))): lambda proposed to vote.

Raises

You are not a member

Expand source code
@sp.entrypoint(new_syntax=True)
def submit_lambda(self, lambda_):
    """Submit a new lambda to the vote.

    Submitting a proposal does not imply casting a vote in favour of it.

    Args:
        lambda_(sp.TLambda(sp.TUnit,sp.TList(sp.TOperation))): lambda
            proposed to vote.
    Raises:
        `You are not a member`
    """
    assert self.data.members.contains(sp.sender), "You are not a member"
    self.data.lambdas[self.data.nextId] = lambda_
    self.data.votes[self.data.nextId] = sp.set()
    self.data.nextId += 1
def vote_lambda(self, id)

Entrypoint. Vote for a lambda.

Args

id(sp.TNat): id of the lambda to vote for.

Raises

You are not a member, The lambda is inactive, Lambda not found There is no vote against or pass. If someone disagrees with a lambda they can avoid to vote.

Expand source code
@sp.entrypoint(new_syntax=True)
def vote_lambda(self, id):
    """Vote for a lambda.

    Args:
        id(sp.TNat): id of the lambda to vote for.
    Raises:
        `You are not a member`, `The lambda is inactive`, `Lambda not found`

    There is no vote against or pass. If someone disagrees with a lambda
    they can avoid to vote.
    """
    assert self.data.members.contains(sp.sender), "You are not a member"
    assert id >= self.data.inactiveBefore, "The lambda is inactive"
    assert self.data.lambdas.contains(id), "Lambda not found"
    self.data.votes[id].add(sp.sender)
    if sp.len(self.data.votes[id]) >= self.data.required_votes:
        _ = self.data.lambdas[id](())
        self.data.inactiveBefore = self.data.nextId
def get_lambda(self, id)

Onchain view. Return the corresponding lambda.

Args

id : sp.TNat
id of the lambda to get.

Return

pair of the lambda and a boolean showing if the lambda is active.

Expand source code
@sp.onchain_view()
def get_lambda(self, id):
    """Return the corresponding lambda.

    Args:
        id (sp.TNat): id of the lambda to get.

    Return:
        pair of the lambda and a boolean showing if the lambda is active.
    """
    sp.result(sp.pair(self.data.lambdas[id], id >= self.data.inactiveBefore))
class Administrated (admin)
Expand source code
class Administrated(sp.Contract):
        def __init__(self, admin):
            self.init(admin=admin, value=sp.int(0))

        @sp.entrypoint(new_syntax=True)
        def set_value(self, value):
            assert sp.sender == self.data.admin
            self.data.value = value

Ancestors

  • smartpy.Contract

Methods

def set_value(self, value)

Entrypoint.

Expand source code
@sp.entrypoint(new_syntax=True)
def set_value(self, value):
    assert sp.sender == self.data.admin
    self.data.value = value