Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}