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  }