Module multisig

Expand source code
# Two Level Multisig - Example for illustrative purposes only.

import smartpy as sp

# MultiSigFactory is a two level multisig factory contract
# - two level because to validate a property, we can use several groups of participants and not only one group,
# - and a factory because it can hold several such two level multisig contracts.

class MultiSigFactory(sp.Contract):
    def __init__(self):
        self.init(multisigs = sp.big_map(), nbMultisigs = 0)

    @sp.entrypoint(new_syntax = True)
    def build(self, params):

        self.data.multisigs[self.data.nbMultisigs] = params.contract
        self.data.nbMultisigs += 1

    @sp.entrypoint
    def sign(self, id, contractId, contractName):
        sp.verify(id == sp.sender)
        sp.set_type(contractName, sp.TString)
        contract = self.data.multisigs[contractId]
        sp.verify(contractName == contract.name)
        sp.set_type(contract.weight, sp.TInt)
        sp.set_type(contract.groupsOK, sp.TInt)
        with sp.for_("group", contract.groups) as group:
            with sp.for_("participant", group.participants) as participant:
                with sp.if_(participant.id == id):
                    sp.verify(~ participant.hasVoted)
                    participant.hasVoted = True
                    sp.set_type(group.weight, sp.TInt)
                    group.weight += participant.weight
                    group.voters += 1
                    with sp.if_(~group.ok & (group.thresholdVoters <= group.voters) & (group.thresholdWeight <= group.weight)):
                        group.ok = True
                        contract.weight += group.contractWeight
                        contract.groupsOK += 1
                        with sp.if_(~contract.ok & (contract.thresholdGroupsOK <= contract.groupsOK) & (contract.thresholdWeight <= contract.weight)):
                            contract.ok = True
                            self.onOK(contract)

    def onOK(self, contract):
        pass

# MultiSigFactoryWithPayment inherits from MultiSigFactory and adds a
# payment functionality

class MultiSigFactoryWithPayment(MultiSigFactory):
    def onOK(self, contract):
        sp.send(contract.owner, contract.amount)

def addMultiSig(c, thresholdWeight, thresholdGroupsOK):
    # tgroup = c.getStorageType().go('multisigs').go('list').go('groups').go('list')
    # tparticipant = tgroup.go('participants').go('list')
    def group(contractWeight, thresholdWeight, thresholdVoters, participants):
        participants = [sp.record(hasVoted = False, weight = weight, id = id) for (id, weight) in participants]
        return sp.record(weight          = 0,
                         voters          = 0,
                         contractWeight  = contractWeight,
                         thresholdWeight = thresholdWeight,
                         thresholdVoters = thresholdVoters,
                         participants    = participants,
                         ok              = False
        )
    p1 = sp.address("tz1NFevnqBrtcZTZTeKP2YBBjsPs9bih5i3J")
    p2 = sp.address("tz1ZRjMiF9K9n3S9AcUrTGUzR2okS7dn9KXS")
    p3 = sp.address("tz1NLJRAAwYdggijWz9EFtX5Dgs95BLfD6mP")
    g1 = group(5, 5 , 2, [(p1, 2), (p2, 8), (p3, 1)])
    g2 = group(7, 5 , 1, [(p1, 7), (p2, 8)])
    g3 = group(7, 10, 1, [(p3, 10)])
    contract = sp.record(name              = "demo",
                         thresholdGroupsOK = thresholdGroupsOK,
                         groupsOK          = 0,
                         thresholdWeight   = thresholdWeight,
                         weight            = 0,
                         groups            = sp.list([g1, g2, g3]),
                         ok                = False)
    return c.build(contract = contract)

# Tests
@sp.add_test(name = "MultiSig")
def test():
    c1 = MultiSigFactory()

    alice    = sp.test_account("Alice")
    bob      = sp.test_account("Rob")
    charlie  = sp.test_account("Charlie")

    scenario = sp.test_scenario()
    scenario.h1("Multi Sig")
    scenario.h2("Contract")
    scenario.h3("Simple multisig factories")
    scenario += c1
    scenario.h2("First: define a simple multisig")
    scenario += addMultiSig(c1, thresholdWeight = 10, thresholdGroupsOK = 2)
    scenario.h2("Message execution")
    scenario.h3("A first move")
    c1.sign(id = alice.address, contractId = 0, contractName = "demo").run(sender = alice)
    c1.sign(id = bob.address, contractId = 0, contractName = "demo").run(sender = bob)

    scenario.h2("First: define a simple multi-sig")
    scenario += addMultiSig(c1, thresholdWeight = 10, thresholdGroupsOK = 3)
    scenario.h2("Message execution")
    scenario.h3("A first move")
    c1.sign(id = alice.address, contractId = 1, contractName = "demo").run(sender = alice)
    c1.sign(id = bob.address, contractId = 1, contractName = "demo").run(sender = bob)
    scenario.h4("We need a third vote")
    c1.sign(id = charlie.address, contractId = 1, contractName = "demo").run(sender = charlie)
    scenario.h4("Final state")
    scenario.show(c1.data)

    scenario.h3("Multisig factories with payments")
    c2 = MultiSigFactoryWithPayment()
    scenario += c2

sp.add_compilation_target("multisig", MultiSigFactoryWithPayment())

Functions

def addMultiSig(c, thresholdWeight, thresholdGroupsOK)
Expand source code
def addMultiSig(c, thresholdWeight, thresholdGroupsOK):
    # tgroup = c.getStorageType().go('multisigs').go('list').go('groups').go('list')
    # tparticipant = tgroup.go('participants').go('list')
    def group(contractWeight, thresholdWeight, thresholdVoters, participants):
        participants = [sp.record(hasVoted = False, weight = weight, id = id) for (id, weight) in participants]
        return sp.record(weight          = 0,
                         voters          = 0,
                         contractWeight  = contractWeight,
                         thresholdWeight = thresholdWeight,
                         thresholdVoters = thresholdVoters,
                         participants    = participants,
                         ok              = False
        )
    p1 = sp.address("tz1NFevnqBrtcZTZTeKP2YBBjsPs9bih5i3J")
    p2 = sp.address("tz1ZRjMiF9K9n3S9AcUrTGUzR2okS7dn9KXS")
    p3 = sp.address("tz1NLJRAAwYdggijWz9EFtX5Dgs95BLfD6mP")
    g1 = group(5, 5 , 2, [(p1, 2), (p2, 8), (p3, 1)])
    g2 = group(7, 5 , 1, [(p1, 7), (p2, 8)])
    g3 = group(7, 10, 1, [(p3, 10)])
    contract = sp.record(name              = "demo",
                         thresholdGroupsOK = thresholdGroupsOK,
                         groupsOK          = 0,
                         thresholdWeight   = thresholdWeight,
                         weight            = 0,
                         groups            = sp.list([g1, g2, g3]),
                         ok                = False)
    return c.build(contract = contract)

Classes

class MultiSigFactory
Expand source code
class MultiSigFactory(sp.Contract):
    def __init__(self):
        self.init(multisigs = sp.big_map(), nbMultisigs = 0)

    @sp.entrypoint(new_syntax = True)
    def build(self, params):

        self.data.multisigs[self.data.nbMultisigs] = params.contract
        self.data.nbMultisigs += 1

    @sp.entrypoint
    def sign(self, id, contractId, contractName):
        sp.verify(id == sp.sender)
        sp.set_type(contractName, sp.TString)
        contract = self.data.multisigs[contractId]
        sp.verify(contractName == contract.name)
        sp.set_type(contract.weight, sp.TInt)
        sp.set_type(contract.groupsOK, sp.TInt)
        with sp.for_("group", contract.groups) as group:
            with sp.for_("participant", group.participants) as participant:
                with sp.if_(participant.id == id):
                    sp.verify(~ participant.hasVoted)
                    participant.hasVoted = True
                    sp.set_type(group.weight, sp.TInt)
                    group.weight += participant.weight
                    group.voters += 1
                    with sp.if_(~group.ok & (group.thresholdVoters <= group.voters) & (group.thresholdWeight <= group.weight)):
                        group.ok = True
                        contract.weight += group.contractWeight
                        contract.groupsOK += 1
                        with sp.if_(~contract.ok & (contract.thresholdGroupsOK <= contract.groupsOK) & (contract.thresholdWeight <= contract.weight)):
                            contract.ok = True
                            self.onOK(contract)

    def onOK(self, contract):
        pass

Ancestors

  • smartpy.Contract

Subclasses

Methods

def build(self, params)

Entrypoint.

Expand source code
@sp.entrypoint(new_syntax = True)
def build(self, params):

    self.data.multisigs[self.data.nbMultisigs] = params.contract
    self.data.nbMultisigs += 1
def sign(self, id, contractId, contractName)

Entrypoint.

Expand source code
@sp.entrypoint
def sign(self, id, contractId, contractName):
    sp.verify(id == sp.sender)
    sp.set_type(contractName, sp.TString)
    contract = self.data.multisigs[contractId]
    sp.verify(contractName == contract.name)
    sp.set_type(contract.weight, sp.TInt)
    sp.set_type(contract.groupsOK, sp.TInt)
    with sp.for_("group", contract.groups) as group:
        with sp.for_("participant", group.participants) as participant:
            with sp.if_(participant.id == id):
                sp.verify(~ participant.hasVoted)
                participant.hasVoted = True
                sp.set_type(group.weight, sp.TInt)
                group.weight += participant.weight
                group.voters += 1
                with sp.if_(~group.ok & (group.thresholdVoters <= group.voters) & (group.thresholdWeight <= group.weight)):
                    group.ok = True
                    contract.weight += group.contractWeight
                    contract.groupsOK += 1
                    with sp.if_(~contract.ok & (contract.thresholdGroupsOK <= contract.groupsOK) & (contract.thresholdWeight <= contract.weight)):
                        contract.ok = True
                        self.onOK(contract)
def onOK(self, contract)
Expand source code
def onOK(self, contract):
    pass
class MultiSigFactoryWithPayment
Expand source code
class MultiSigFactoryWithPayment(MultiSigFactory):
    def onOK(self, contract):
        sp.send(contract.owner, contract.amount)

Ancestors

Methods

def onOK(self, contract)
Expand source code
def onOK(self, contract):
    sp.send(contract.owner, contract.amount)

Inherited members