templates.inheritance
1import smartpy as sp 2 3# This contract has nothing to do with inheritance in the Python sense. 4# It's about transfers after a person's death. 5 6 7@sp.module 8def main(): 9 class Inheritance(sp.Contract): 10 """Inheritance contract 11 12 An owner deposits a certain amount of coins into a contract and regularly 13 calls the `default` entrypoint to prove that they are still alive. 14 15 If the contract is not called since `alive_delta` seconds (1 years in 16 this example), the heir can withdraw the tez by calling `withdraw`. 17 18 At anytime the owner can deposit some tez by using the `default` entrypoint 19 (or no entrypoint) or withdraw by calling `withdraw`. 20 """ 21 22 def __init__(self, owner, heir, alive_delta, now): 23 """ 24 Args: 25 owner (address): Address that deposits and prove being alive. 26 heir (address): Address that can withdraw if the owner hasn't proved 27 being alive. 28 alive_delta (int): Maximum number of seconds between two calls to `default`. 29 """ 30 self.data.owner = owner 31 self.data.heir = heir 32 self.data.alive_delta = alive_delta 33 self.data.timeout = sp.add_seconds(now, alive_delta) 34 35 @sp.entrypoint 36 def default(self): 37 """Used by the owner to deposit coins and say that they are still alive. 38 39 The `default` entrypoint is also called when doing a transfer without 40 specifying an entrypoint. This is useful when using a simple wallet or 41 an app without the ability to specify an entrypoint. 42 43 Raises: 44 `This entrypoint can only be called by the owner.` 45 """ 46 assert ( 47 sp.sender == self.data.owner 48 ), "This entrypoint can only be called by the owner." 49 50 self.data.timeout = sp.add_seconds(sp.now, self.data.alive_delta) 51 52 @sp.entrypoint 53 def withdraw(self, receiver, amount_): 54 """Used by the owner or the heir to withdraw coins. 55 56 The heir can only withdraw if the last call was made more than 57 `alive_delta` seconds ago. 58 59 Args: 60 receiver (address): Receiver of the withdraw. 61 amount (mutez): Amount withdrawn. 62 Raises: 63 `This entrypoint doesn't accept deposits.` 64 `The owner is still considered alive, you cannot withdraw.` 65 `Only owner or heir can withdraw.` 66 """ 67 assert sp.amount == sp.tez(0), "This entrypoint doesn't accept deposits." 68 if sp.sender == self.data.heir: 69 assert ( 70 sp.now > self.data.timeout 71 ), "The owner is still considered alive, you cannot withdraw." 72 73 else: 74 assert sp.sender == self.data.owner, "Only owner or heir can withdraw." 75 sp.send(receiver, amount_) 76 77 78owner = sp.test_account("owner").address 79heir = sp.test_account("heir").address 80ALIVE_DELTA = 366 * 24 * 3600 # 1 leap year 81 82if "main" in __name__: 83 84 @sp.add_test() 85 def basic_scenario(): 86 """Test of: 87 - origination. 88 - deposit. 89 - owner withdrawal. 90 - alive confirmation. 91 - heir withdrawal before timeout. 92 """ 93 sc = sp.test_scenario("Inheritance basic scenario", main) 94 sc.h1("Basic scenario.") 95 now = sp.timestamp(0) 96 97 sc.h2("Origination.") 98 c1 = main.Inheritance(owner=owner, heir=heir, alive_delta=ALIVE_DELTA, now=now) 99 sc += c1 100 101 sc.h2("Deposit.") 102 c1.default(_sender=owner, _amount=sp.tez(1200), _now=now) 103 sc.verify(c1.balance == sp.tez(1200)) 104 sc.verify(c1.data.timeout == now.add_seconds(ALIVE_DELTA)) 105 106 sc.h2("Owner withdraw.") 107 now = now.add_minutes(1) 108 c1.withdraw(receiver=owner, amount_=sp.tez(200), _sender=owner, _now=now) 109 110 sc.h2("Alive confirmation.") 111 now = now.add_days(360) 112 c1.default(_sender=owner, _now=now) 113 114 sc.h2("Heir withdraw.") 115 now = now.add_days(367) 116 c1.withdraw(receiver=heir, amount_=c1.balance, _sender=heir, _now=now) 117 118 @sp.add_test() 119 def errors_test(): 120 """Test of: 121 - `default`: non-owner calling. 122 - `withdraw`: not allowed calling. 123 - `withdraw`: sending tez. 124 - `withdraw`: heir withdraw before timeout. 125 """ 126 sc = sp.test_scenario("Inheritance errors test", main) 127 sc.h1("Errors tests.") 128 now = sp.timestamp(0) 129 130 sc.h2("Origination.") 131 c1 = main.Inheritance(owner=owner, heir=heir, alive_delta=ALIVE_DELTA, now=now) 132 sc += c1 133 134 sc.h2("Default: non-owner calling.") 135 NOT_ALLOWED = sp.test_account("not_allowed").address 136 c1.default( 137 _sender=NOT_ALLOWED, 138 _now=now, 139 _valid=False, 140 _exception="This entrypoint can only be called by the owner.", 141 ) 142 143 sc.h2("Withdraw: not allowed calling.") 144 c1.withdraw( 145 receiver=NOT_ALLOWED, 146 amount_=c1.balance, 147 _sender=NOT_ALLOWED, 148 _valid=False, 149 _exception="Only owner or heir can withdraw.", 150 ) 151 152 sc.h2("Withdraw: sending tez.") 153 c1.withdraw( 154 receiver=owner, 155 amount_=c1.balance, 156 _sender=owner, 157 _amount=sp.tez(100), 158 _valid=False, 159 _exception="This entrypoint doesn't accept deposits.", 160 ) 161 162 sc.h2("Withdraw: heir withdraw before timeout.") 163 now = now.add_minutes(5) 164 c1.withdraw( 165 receiver=heir, 166 amount_=c1.balance, 167 _sender=heir, 168 _now=now, 169 _valid=False, 170 _exception="The owner is still considered alive, you cannot withdraw.", 171 )
owner =
(("templates/inheritance.py" 78) attr (("templates/inheritance.py" 78) account_of_seed "owner") "address")
heir =
(("templates/inheritance.py" 79) attr (("templates/inheritance.py" 79) account_of_seed "heir") "address")
ALIVE_DELTA =
31622400