contract.gno
3.02 Kb · 98 lines
1// Package vault demonstrates a minimal time-locked token vault built on top of
2// the grc20 token package. Deposited tokens are held at the realm's own
3// address; withdrawing them is a two-step process: Unvault marks an amount for
4// withdrawal and starts a lock timer, then (once the lock elapses) Redeem moves
5// the tokens back to the depositor.
6package vault
7
8import (
9 "chain/runtime"
10 "chain/runtime/unsafe"
11
12 tokens "gno.land/p/nt/grc20/v0"
13)
14
15// test1 receives the initial mint at construction.
16var test1 = address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5")
17
18// lockDuration is the number of blocks that must pass between Unvault and
19// Redeem.
20const lockDuration = int64(100)
21
22// Token and FooVault-equivalent state are exported for reading; privateLedger
23// stays unexported (unrestricted mint/burn/transfer).
24var (
25 Token *tokens.Token
26 privateLedger *tokens.PrivateLedger
27 vaultAddr address // address holding all deposited tokens
28
29 vaulted = map[address]uint{} // still-locked deposits per owner
30 pending = map[address]uint{} // amount marked for withdrawal
31 unlockAt = map[address]int64{} // block height at which pending unlocks
32 recoverOf = map[address]address{} // recovery address per depositor
33)
34
35func init(cur realm) {
36 // generate the token and mint some tokens to test1.
37 Token, privateLedger = tokens.NewToken("Foo Token", "FOO", 4, 0, cur)
38 privateLedger.Mint(test1, 100000000)
39
40 // the vault holds deposited tokens at the realm's own address.
41 vaultAddr = cur.Address()
42}
43
44func MyBalance() int64 {
45 return Token.BalanceOf(unsafe.OriginCaller())
46}
47
48// Deposit moves amount tokens from the caller into the vault, recording a
49// recovery address for later use.
50func Deposit(cur realm, amount uint, recoverAddress address) {
51 caller := unsafe.OriginCaller()
52 if err := privateLedger.Transfer(caller, vaultAddr, int64(amount)); err != nil {
53 panic(err)
54 }
55 vaulted[caller] += amount
56 recoverOf[caller] = recoverAddress
57}
58
59// Recover sends target's still-locked deposit to its recorded recovery address.
60func Recover(cur realm, target address) {
61 amount := vaulted[target]
62 if amount == 0 {
63 return
64 }
65 dest := recoverOf[target]
66 if err := privateLedger.Transfer(vaultAddr, dest, int64(amount)); err != nil {
67 panic(err)
68 }
69 vaulted[target] = 0
70}
71
72// Unvault marks amount of the caller's deposit for withdrawal and starts the
73// lock timer.
74func Unvault(cur realm, amount uint) {
75 caller := unsafe.OriginCaller()
76 if vaulted[caller] < amount {
77 panic("insufficient vaulted balance")
78 }
79 vaulted[caller] -= amount
80 pending[caller] += amount
81 unlockAt[caller] = runtime.ChainHeight() + lockDuration
82}
83
84// Redeem transfers amount of the caller's matured pending withdrawal back to
85// the caller.
86func Redeem(cur realm, amount uint) {
87 caller := unsafe.OriginCaller()
88 if pending[caller] < amount {
89 panic("insufficient pending balance")
90 }
91 if runtime.ChainHeight() < unlockAt[caller] {
92 panic("still locked")
93 }
94 if err := privateLedger.Transfer(vaultAddr, caller, int64(amount)); err != nil {
95 panic(err)
96 }
97 pending[caller] -= amount
98}