Module baking_swap

Expand source code
import smartpy as sp


class BakingSwap(sp.Contract):
    """A contract that takes tez deposits and pays interest.

    The deposited funds cannot leave the contract, but the administrator can
    delegate them for baking.

    In more detail:

    - The administrator funds the contract with collateral.

    - The administrator publishes an offer: a rate (in basis points) and a
    duration (in days).

    - For each deposit the amount to be paid out and the due date are recorded.
    The corresponding amount of collateral is locked up.

    - At maturity the deposit plus interest can be withdrawn.
    """

    def __init__(self, admin, initialRate, initialDuration):
        """Constructor

        Args:
            admin (sp.TAddress): admin of the contract.
            initialRate (sp.TNat): Basis points to compute the interest.
            initialDuration (sp.TNat): Number of days before a deposit can be
                withdrawn.
        """
        self.init(
            admin=admin,
            collateral=sp.mutez(0),
            ledger={},
            rate=initialRate,
            duration=initialDuration,
        )

    # Admin-only entrypoints

    @sp.entrypoint(new_syntax = True)
    def delegate(self, public_key_hash):
        """Admin-only. Delegate the contract's balance to the admin.

        Args:
            public_key_hash (sp.TKeyHash): public key hash of the admin.
        """
        assert sp.sender == self.data.admin
        assert sp.amount == sp.mutez(0)
        assert sp.sender == sp.to_address(sp.implicit_account(public_key_hash))
        sp.set_delegate(sp.Some(public_key_hash))

    @sp.entrypoint(new_syntax = True)
    def collateralize(self):
        """Admin-only. Provide tez as collateral for interest to be paid."""
        assert sp.sender == self.data.admin
        self.data.collateral += sp.amount

    @sp.entrypoint(new_syntax = True)
    def uncollateralize(self, amount, receiver):
        """Admin-only. Withdraw collateral.

        Transfer `amount` mutez to admin if it doesn't exceed collateral.

        Args:

            amount (sp.TMutez): Amount to be removed from the collateral.
        """
        assert sp.sender == self.data.admin
        # Explicitly fails for insufficient collateral.
        assert amount <= self.data.collateral, "insufficient collateral"
        self.data.collateral -= amount
        sp.send(receiver, amount)

    @sp.entrypoint(new_syntax = True)
    def set_offer(self, rate, duration):
        """Admin-only. Set the current offer: interest rate (in basis points)
        and duration.

        Args:
            rate (sp.TNat): Basis points to compute the interest.
            duration (sp.TNat): Number of days before a deposit can be withdrawn.
        """
        assert sp.sender == self.data.admin
        assert sp.amount == sp.mutez(0)
        self.data.rate = rate
        self.data.duration = duration

    # Permissionless entrypoints

    @sp.entrypoint(new_syntax = True)
    def deposit(self, rate, duration):
        """Deposit tez. The current offer has to be repeated in the parameters.

        Args:
            rate (sp.TNat): Basis points to compute the interest.
            duration (sp.TNat): Number of days before a deposit can be withdrawn.
        """
        assert self.data.rate >= rate
        assert self.data.duration <= duration
        assert not self.data.ledger.contains(sp.sender)

        # Compute interest to be paid.
        interest = sp.split_tokens(sp.amount, self.data.rate, 10_000)
        self.data.collateral -= interest

        # Record the payment to be made.
        self.data.ledger[sp.sender] = sp.record(
            amount=sp.amount + interest,
            due=sp.add_days(sp.now, self.data.duration),
        )

    @sp.entrypoint(new_syntax = True)
    def withdraw(self, receiver):
        """Withdraw tez at maturity."""
        assert sp.amount == sp.mutez(0)
        entry = self.data.ledger.get(sp.sender, error="NoDeposit")
        assert sp.now >= entry.due
        sp.send(receiver, entry.amount)
        del self.data.ledger[sp.sender]


if "templates" not in __name__:
    admin = sp.test_account("Admin")
    voting_powers = {
        admin.public_key_hash: 0,
    }

    @sp.add_test(name="Baking")
    def test():
        scenario = sp.test_scenario()
        scenario.h1("Baking Swap")
        c = BakingSwap(admin.address, 700, 365)
        scenario += c

        c.delegate(admin.public_key_hash).run(sender=admin, voting_powers=voting_powers)

sp.add_compilation_target(
    "bakingSwap", BakingSwap(sp.test_account("admin").address, 700, 365)
)

Classes

class BakingSwap (admin, initialRate, initialDuration)

A contract that takes tez deposits and pays interest.

The deposited funds cannot leave the contract, but the administrator can delegate them for baking.

In more detail:

  • The administrator funds the contract with collateral.

  • The administrator publishes an offer: a rate (in basis points) and a duration (in days).

  • For each deposit the amount to be paid out and the due date are recorded. The corresponding amount of collateral is locked up.

  • At maturity the deposit plus interest can be withdrawn.

Constructor

Args

admin : sp.TAddress
admin of the contract.
initialRate : sp.TNat
Basis points to compute the interest.
initialDuration : sp.TNat
Number of days before a deposit can be withdrawn.
Expand source code
class BakingSwap(sp.Contract):
    """A contract that takes tez deposits and pays interest.

    The deposited funds cannot leave the contract, but the administrator can
    delegate them for baking.

    In more detail:

    - The administrator funds the contract with collateral.

    - The administrator publishes an offer: a rate (in basis points) and a
    duration (in days).

    - For each deposit the amount to be paid out and the due date are recorded.
    The corresponding amount of collateral is locked up.

    - At maturity the deposit plus interest can be withdrawn.
    """

    def __init__(self, admin, initialRate, initialDuration):
        """Constructor

        Args:
            admin (sp.TAddress): admin of the contract.
            initialRate (sp.TNat): Basis points to compute the interest.
            initialDuration (sp.TNat): Number of days before a deposit can be
                withdrawn.
        """
        self.init(
            admin=admin,
            collateral=sp.mutez(0),
            ledger={},
            rate=initialRate,
            duration=initialDuration,
        )

    # Admin-only entrypoints

    @sp.entrypoint(new_syntax = True)
    def delegate(self, public_key_hash):
        """Admin-only. Delegate the contract's balance to the admin.

        Args:
            public_key_hash (sp.TKeyHash): public key hash of the admin.
        """
        assert sp.sender == self.data.admin
        assert sp.amount == sp.mutez(0)
        assert sp.sender == sp.to_address(sp.implicit_account(public_key_hash))
        sp.set_delegate(sp.Some(public_key_hash))

    @sp.entrypoint(new_syntax = True)
    def collateralize(self):
        """Admin-only. Provide tez as collateral for interest to be paid."""
        assert sp.sender == self.data.admin
        self.data.collateral += sp.amount

    @sp.entrypoint(new_syntax = True)
    def uncollateralize(self, amount, receiver):
        """Admin-only. Withdraw collateral.

        Transfer `amount` mutez to admin if it doesn't exceed collateral.

        Args:

            amount (sp.TMutez): Amount to be removed from the collateral.
        """
        assert sp.sender == self.data.admin
        # Explicitly fails for insufficient collateral.
        assert amount <= self.data.collateral, "insufficient collateral"
        self.data.collateral -= amount
        sp.send(receiver, amount)

    @sp.entrypoint(new_syntax = True)
    def set_offer(self, rate, duration):
        """Admin-only. Set the current offer: interest rate (in basis points)
        and duration.

        Args:
            rate (sp.TNat): Basis points to compute the interest.
            duration (sp.TNat): Number of days before a deposit can be withdrawn.
        """
        assert sp.sender == self.data.admin
        assert sp.amount == sp.mutez(0)
        self.data.rate = rate
        self.data.duration = duration

    # Permissionless entrypoints

    @sp.entrypoint(new_syntax = True)
    def deposit(self, rate, duration):
        """Deposit tez. The current offer has to be repeated in the parameters.

        Args:
            rate (sp.TNat): Basis points to compute the interest.
            duration (sp.TNat): Number of days before a deposit can be withdrawn.
        """
        assert self.data.rate >= rate
        assert self.data.duration <= duration
        assert not self.data.ledger.contains(sp.sender)

        # Compute interest to be paid.
        interest = sp.split_tokens(sp.amount, self.data.rate, 10_000)
        self.data.collateral -= interest

        # Record the payment to be made.
        self.data.ledger[sp.sender] = sp.record(
            amount=sp.amount + interest,
            due=sp.add_days(sp.now, self.data.duration),
        )

    @sp.entrypoint(new_syntax = True)
    def withdraw(self, receiver):
        """Withdraw tez at maturity."""
        assert sp.amount == sp.mutez(0)
        entry = self.data.ledger.get(sp.sender, error="NoDeposit")
        assert sp.now >= entry.due
        sp.send(receiver, entry.amount)
        del self.data.ledger[sp.sender]

Ancestors

  • smartpy.Contract

Methods

def delegate(self, public_key_hash)

Entrypoint. Admin-only. Delegate the contract's balance to the admin.

Args

public_key_hash : sp.TKeyHash
public key hash of the admin.
Expand source code
@sp.entrypoint(new_syntax = True)
def delegate(self, public_key_hash):
    """Admin-only. Delegate the contract's balance to the admin.

    Args:
        public_key_hash (sp.TKeyHash): public key hash of the admin.
    """
    assert sp.sender == self.data.admin
    assert sp.amount == sp.mutez(0)
    assert sp.sender == sp.to_address(sp.implicit_account(public_key_hash))
    sp.set_delegate(sp.Some(public_key_hash))
def collateralize(self)

Entrypoint. Admin-only. Provide tez as collateral for interest to be paid.

Expand source code
@sp.entrypoint(new_syntax = True)
def collateralize(self):
    """Admin-only. Provide tez as collateral for interest to be paid."""
    assert sp.sender == self.data.admin
    self.data.collateral += sp.amount
def uncollateralize(self, amount, receiver)

Entrypoint. Admin-only. Withdraw collateral.

Transfer amount mutez to admin if it doesn't exceed collateral.

Args

amount : sp.TMutez
Amount to be removed from the collateral.
Expand source code
@sp.entrypoint(new_syntax = True)
def uncollateralize(self, amount, receiver):
    """Admin-only. Withdraw collateral.

    Transfer `amount` mutez to admin if it doesn't exceed collateral.

    Args:

        amount (sp.TMutez): Amount to be removed from the collateral.
    """
    assert sp.sender == self.data.admin
    # Explicitly fails for insufficient collateral.
    assert amount <= self.data.collateral, "insufficient collateral"
    self.data.collateral -= amount
    sp.send(receiver, amount)
def set_offer(self, rate, duration)

Entrypoint. Admin-only. Set the current offer: interest rate (in basis points) and duration.

Args

rate : sp.TNat
Basis points to compute the interest.
duration : sp.TNat
Number of days before a deposit can be withdrawn.
Expand source code
@sp.entrypoint(new_syntax = True)
def set_offer(self, rate, duration):
    """Admin-only. Set the current offer: interest rate (in basis points)
    and duration.

    Args:
        rate (sp.TNat): Basis points to compute the interest.
        duration (sp.TNat): Number of days before a deposit can be withdrawn.
    """
    assert sp.sender == self.data.admin
    assert sp.amount == sp.mutez(0)
    self.data.rate = rate
    self.data.duration = duration
def deposit(self, rate, duration)

Entrypoint. Deposit tez. The current offer has to be repeated in the parameters.

Args

rate : sp.TNat
Basis points to compute the interest.
duration : sp.TNat
Number of days before a deposit can be withdrawn.
Expand source code
@sp.entrypoint(new_syntax = True)
def deposit(self, rate, duration):
    """Deposit tez. The current offer has to be repeated in the parameters.

    Args:
        rate (sp.TNat): Basis points to compute the interest.
        duration (sp.TNat): Number of days before a deposit can be withdrawn.
    """
    assert self.data.rate >= rate
    assert self.data.duration <= duration
    assert not self.data.ledger.contains(sp.sender)

    # Compute interest to be paid.
    interest = sp.split_tokens(sp.amount, self.data.rate, 10_000)
    self.data.collateral -= interest

    # Record the payment to be made.
    self.data.ledger[sp.sender] = sp.record(
        amount=sp.amount + interest,
        due=sp.add_days(sp.now, self.data.duration),
    )
def withdraw(self, receiver)

Entrypoint. Withdraw tez at maturity.

Expand source code
@sp.entrypoint(new_syntax = True)
def withdraw(self, receiver):
    """Withdraw tez at maturity."""
    assert sp.amount == sp.mutez(0)
    entry = self.data.ledger.get(sp.sender, error="NoDeposit")
    assert sp.now >= entry.due
    sp.send(receiver, entry.amount)
    del self.data.ledger[sp.sender]