Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}