github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/grc/exts/vault/vault.gno (about) 1 package vault 2 3 import ( 4 "std" 5 6 "gno.land/p/demo/avl" 7 "gno.land/p/demo/grc/grc20" 8 ) 9 10 // Vault is a GRC20 compatible token with vault features. 11 type Vault interface { 12 Deposit(amount uint, recovery std.Address, lockDuration uint) error 13 Unvault(amount uint) error 14 Recover(dest std.Address) error 15 Redeem() error 16 } 17 18 func New(adminToken *grc20.AdminToken) Vault { 19 return &impl{ 20 adminToken: adminToken, 21 users: avl.Tree{}, 22 } 23 } 24 25 type impl struct { 26 adminToken *grc20.AdminToken 27 users avl.Tree // std.Address -> userVault 28 } 29 30 type userVault struct { 31 // constructor parameters. 32 recover std.Address 33 lockDuration uint 34 35 // internal parameters. 36 owner std.Address 37 redeemMinHeight int64 38 unvaultedAmount uint 39 } 40 41 func (v *impl) Deposit(amount uint, recover std.Address, lockDuration uint) error { 42 caller := std.GetOrigCaller() 43 pkgAddr := std.GetOrigPkgAddr() 44 45 uv := userVault{ 46 lockDuration: lockDuration, 47 redeemMinHeight: 0, // will be set in Unvault. 48 unvaultedAmount: 0, // will be increased in Unvault, zeroed in Redeem. 49 owner: caller, 50 } 51 52 // deposit. 53 err := v.adminToken.Transfer(caller, pkgAddr, uint64(amount)) 54 if err != nil { 55 return err 56 } 57 v.users.Set(caller.String(), &uv) 58 59 return nil 60 } 61 62 func (v *impl) Unvault(amount uint) error { 63 caller := std.GetOrigCaller() 64 uv, err := v.getUserVault(caller) 65 if err != nil { 66 return err 67 } 68 69 balance, err := v.adminToken.BalanceOf(caller) 70 if err != nil { 71 return err 72 } 73 if balance < uint64(amount) { 74 return grc20.ErrInsufficientBalance 75 } 76 77 println("AAA1", std.GetHeight(), uv.redeemMinHeight, uv.lockDuration) 78 uv.redeemMinHeight = std.GetHeight() + int64(uv.lockDuration) 79 uv.unvaultedAmount += amount 80 v.users.Set(caller.String(), uv) 81 println("AAA2", std.GetHeight(), uv.redeemMinHeight, uv.lockDuration) 82 return nil 83 } 84 85 func (v *impl) Redeem() error { 86 pkgAddr := std.GetOrigPkgAddr() 87 caller := std.GetOrigCaller() 88 uv, err := v.getUserVault(caller) 89 if err != nil { 90 return err 91 } 92 93 println("AAA3", std.GetHeight(), uv.redeemMinHeight, uv.lockDuration) 94 if std.GetHeight() < uv.redeemMinHeight { 95 return ErrTooEarlyToRedeem 96 } 97 // TODO: check balance. (should be optional, but let's be sure). 98 // TODO: check height. 99 100 // transfer token. 101 err = v.adminToken.Transfer(pkgAddr, caller, uint64(uv.unvaultedAmount)) 102 if err != nil { 103 return err 104 } 105 106 uv.unvaultedAmount = 0 107 // TODO: if balance == 0 -> destroy? 108 return nil 109 } 110 111 func (v *impl) Recover(dest std.Address) error { 112 // TODO: assert caller (recovery). 113 // TODO: trasfertToken. 114 // TODO: destroy? 115 return nil 116 } 117 118 func (v *impl) getUserVault(address std.Address) (*userVault, error) { 119 uvI, exists := v.users.Get(address.String()) 120 if !exists { 121 return nil, ErrNoSuchVault 122 } 123 uv := uvI.(*userVault) 124 return uv, nil 125 }