Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: r/demo/vault #2542

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/gno.land/r/demo/tests/test20/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module gno.land/r/demo/tests/test20

require gno.land/p/demo/grc/grc20 v0.0.0-latest
18 changes: 18 additions & 0 deletions examples/gno.land/r/demo/tests/test20/test20.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Package test20 implements a deliberately insecure ERC20 token for testing purposes.
// The Test20 token allows anyone to mint any amount of tokens to any address, making
// it unsuitable for production use. The primary goal of this package is to facilitate
// testing and experimentation without any security measures or restrictions.
//
// WARNING: This token is highly insecure and should not be used in any
// production environment. It is intended solely for testing and
// educational purposes.
package test20

import "gno.land/p/demo/grc/grc20"

var (
Banker = grc20.NewBanker("Test20", "TST", 4)
Token = Banker.Token()
)

// XXX func init() { grc20reg.Register(Pub, "") } // Depends on #2516
6 changes: 6 additions & 0 deletions examples/gno.land/r/demo/vault/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module gno.land/r/demo/vault

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/demo/grc/grc20 v0.0.0-latest
)
8 changes: 8 additions & 0 deletions examples/gno.land/r/demo/vault/types.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package vault

import "errors"

var (
ErrTooEarlyToRedeem = errors.New("too early to redeem")
ErrNoSuchVault = errors.New("no such vault")
)
121 changes: 121 additions & 0 deletions examples/gno.land/r/demo/vault/vault.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package vault

import (
"std"

"gno.land/p/demo/avl"
"gno.land/p/demo/grc/grc20"
)

type Vault interface {
Deposit(amount uint, recovery std.Address, lockDuration uint) error
Unvault(amount uint) error
Recover(dest std.Address) error
Redeem() error
}

func New(adminToken *grc20.Banker) Vault {
return &impl{
adminToken: adminToken,
users: avl.Tree{},
}
}

type impl struct {
adminToken *grc20.Banker
users avl.Tree // std.Address -> userVault
}

type userVault struct {
// constructor parameters.
recover std.Address
lockDuration uint

// internal parameters.
owner std.Address
redeemMinHeight int64
unvaultedAmount uint
}

func (v *impl) Deposit(amount uint, recover std.Address, lockDuration uint) error {
caller := std.GetOrigCaller()
pkgAddr := std.GetOrigPkgAddr()

uv := userVault{
lockDuration: lockDuration,
redeemMinHeight: 0, // will be set in Unvault.
unvaultedAmount: 0, // will be increased in Unvault, zeroed in Redeem.
owner: caller,
}

// deposit.
err := v.adminToken.Transfer(caller, pkgAddr, uint64(amount))
if err != nil {
return err
}
v.users.Set(caller.String(), &uv)

return nil
}

func (v *impl) Unvault(amount uint) error {
caller := std.GetOrigCaller()
uv, err := v.getUserVault(caller)
if err != nil {
return err
}

balance := v.adminToken.BalanceOf(caller)
if balance < uint64(amount) {
return grc20.ErrInsufficientBalance
}

println("AAA1", std.GetHeight(), uv.redeemMinHeight, uv.lockDuration)
uv.redeemMinHeight = std.GetHeight() + int64(uv.lockDuration)
uv.unvaultedAmount += amount
v.users.Set(caller.String(), uv)
println("AAA2", std.GetHeight(), uv.redeemMinHeight, uv.lockDuration)
return nil
}

func (v *impl) Redeem() error {
pkgAddr := std.GetOrigPkgAddr()
caller := std.GetOrigCaller()
uv, err := v.getUserVault(caller)
if err != nil {
return err
}

println("AAA3", std.GetHeight(), uv.redeemMinHeight, uv.lockDuration)
if std.GetHeight() < uv.redeemMinHeight {
return ErrTooEarlyToRedeem
}
// TODO: check balance. (should be optional, but let's be sure).
// TODO: check height.

// transfer token.
err = v.adminToken.Transfer(pkgAddr, caller, uint64(uv.unvaultedAmount))
if err != nil {
return err
}

uv.unvaultedAmount = 0
// TODO: if balance == 0 -> destroy?
return nil
}

func (v *impl) Recover(dest std.Address) error {
// TODO: assert caller (recovery).
// TODO: trasfertToken.
// TODO: destroy?
return nil
}

func (v *impl) getUserVault(address std.Address) (*userVault, error) {
uvI, exists := v.users.Get(address.String())
if !exists {
return nil, ErrNoSuchVault
}
uv := uvI.(*userVault)
return uv, nil
}
3 changes: 3 additions & 0 deletions examples/gno.land/r/demo/vault/vault_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package vault

// TODO: unit tests, edge cases.
78 changes: 78 additions & 0 deletions examples/gno.land/r/demo/vault/z1_filetest.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"std"

"gno.land/p/demo/testutils"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/tests/test20"
"gno.land/r/demo/vault"
)

func main() {
var (
alice = testutils.TestAddress("alice")
bob = testutils.TestAddress("bob") // recovery request address (cold wallet).
charly = testutils.TestAddress("charly") // recovery dest.
pkgaddr = std.GetOrigPkgAddr()
t20token = test20.Token
)

printBalances := func() {
aliceBalance := t20token.BalanceOf(alice)
bobBalance := t20token.BalanceOf(bob)
charlyBalance := t20token.BalanceOf(charly)
pkgBalance := t20token.BalanceOf(pkgaddr)
height := std.GetHeight()
println(ufmt.Sprintf(
"balances: alice=%d, bob=%d, charly=%d, pkg=%d, height=%d",
aliceBalance, bobBalance, charlyBalance, pkgBalance, height))
}

// create a vault for t20token.
v := vault.New(t20token)
printBalances()

return // XXX

// alice deposits 300 with an unlock duration of 5 blocks.
std.TestSetOrigCaller(alice)
lockAmount := uint(300)
lockDuration := uint(5)
checkErr(v.Deposit(lockAmount, bob, lockDuration))
printBalances()

// alice calls unvault for 200 t20tokens.
checkErr(v.Unvault(200))
printBalances()

// alice waits for few blocks.
std.TestSkipHeights(int64(lockDuration) + 1)
printBalances()

// alice redeems 200 t20tokens.
checkErr(v.Redeem())
printBalances()

// bob instantly recover everything in the wallet.
std.TestSetOrigCaller(bob)
checkErr(v.Recover(charly))
printBalances()
}

func checkErr(err error) {
if err != nil {
panic(err)
}
}

// Output:
// balances: alice=1000, bob=0, charly=0, pkg=0, height=123
// balances: alice=700, bob=0, charly=0, pkg=300, height=123
// AAA1 123 0 5
// AAA2 123 128 5
// balances: alice=700, bob=0, charly=0, pkg=300, height=123
// balances: alice=700, bob=0, charly=0, pkg=300, height=129
// AAA3 129 128 5
// balances: alice=900, bob=0, charly=0, pkg=100, height=129
// balances: alice=900, bob=0, charly=0, pkg=100, height=129
Loading