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

chore: add p/grc/exos/vault composability example #334

Merged
merged 13 commits into from
Sep 13, 2022
Merged
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
6 changes: 3 additions & 3 deletions examples/gno.land/p/grc/exts/token_metadata.gno
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package exts

type TokenMetadata interface {
// Returns the name of the token.
Name() string
GetName() string

// Returns the symbol of the token, usually a shorter version of the
// name.
Symbol() string
GetSymbol() string

// Returns the decimals places of the token.
Decimals() uint
GetDecimals() uint
}
8 changes: 8 additions & 0 deletions examples/gno.land/p/grc/exts/vault/errors.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package vault

import "errors"

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

import (
"std"

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

// Vault is a GRC20 compatible token with vault features.
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.AdminToken) Vault {
return &impl{
adminToken: adminToken,
users: avl.NewMutTree(),
}
}

type impl struct {
adminToken *grc20.AdminToken
users *avl.MutTree // 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, err := v.adminToken.BalanceOf(caller)
if err != nil {
return err
}
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
}
79 changes: 79 additions & 0 deletions examples/gno.land/p/grc/exts/vault/vault_filetest.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package main

import (
"std"
"time"

"gno.land/p/grc/exts/vault"
"gno.land/p/grc/grc20"
"gno.land/p/testutils"
"gno.land/p/ufmt"
)

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

// create a fooAdminToken + fooToken (GRC20) pair.
fooAdminToken := grc20.NewAdminToken("Foo", "FOO", 4)
fooAdminToken.Mint(alice, 1000)
fooToken := fooAdminToken.GRC20()

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

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

// 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 tokens.
checkErr(v.Unvault(200))
printBalances()

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

// alice redeems 200 tokens.
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
3 changes: 3 additions & 0 deletions examples/gno.land/p/grc/exts/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.
Loading