Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

v0 source realm

Package vault demonstrates a minimal time-locked token vault built on top of the grc20 token package. Deposited token...

Readme View source

gno.land/r/moul/demo/vault

Step 7 and last of the r/moul/demo walkthrough: a time-locked vault over the GRC20 token from grc20.

Withdrawing is deliberately two steps, so that a stolen key does not drain the vault in one transaction:

1func Deposit(cur realm, amount uint, recoverAddress address)  // caller -> vault
2func Unvault(cur realm, amount uint)                          // arm the timer
3func Redeem(cur realm, amount uint)                           // after the lock
4func Recover(cur realm, target address)                       // escape hatch

Unvault moves an amount from vaulted to pending and stamps unlockAt = ChainHeight() + 100. Redeem refuses until the chain passes that height. In between, the depositor (or whoever is watching) can call Recover to push the still-locked balance to the recovery address recorded at deposit time. That is the pattern: a withdrawal announces itself, and the announcement window is when a theft can be reversed.

Deposited tokens live at the realm's own address (cur.Address()), which is what makes the vault custodial rather than an accounting fiction.

What is wrong with it on purpose

This is a demo, and two things would sink it in production:

  • Recover has no caller check. Anyone can call Recover(target) for any target. It only ever pays out to the address the target itself recorded, so it cannot steal, but it is a free denial of service: a bystander can rewind anyone's deposit at will.
  • Only Unvault/Redeem are covered by tests. Recover has none, flagged as a TODO in the test file.

Read it for the two-step withdrawal shape, not as a vault to deploy. (Note that r/moul/x/daily/vault is a different thing entirely: a key-value store, not a token vault.)

Previous: grc20.


Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.

On mainnet: deployment status transactions unique callers deployed revision

Dependency graph:

gno.land/r/moul/demo/vault/v0 dependency graph

⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.

Overview

Package vault demonstrates a minimal time-locked token vault built on top of the grc20 token package. Deposited tokens are held at the realm's own address; withdrawing them is a two-step process: Unvault marks an amount for withdrawal and starts a lock timer, then (once the lock elapses) Redeem moves the tokens back to the depositor.

Variables 1

var Token, privateLedger, vaultAddr, vaulted, pending, unlockAt, recoverOf

 1var (
 2	Token         *tokens.Token
 3	privateLedger *tokens.PrivateLedger
 4	vaultAddr     address // address holding all deposited tokens
 5
 6	vaulted   = map[address]uint{}    // still-locked deposits per owner
 7	pending   = map[address]uint{}    // amount marked for withdrawal
 8	unlockAt  = map[address]int64{}   // block height at which pending unlocks
 9	recoverOf = map[address]address{} // recovery address per depositor
10)
source

Token and FooVault-equivalent state are exported for reading; privateLedger stays unexported (unrestricted mint/burn/transfer).

Functions 5

func Deposit

crossing Action
1func Deposit(cur realm, amount uint, recoverAddress address)
source

Deposit moves amount tokens from the caller into the vault, recording a recovery address for later use.

func Recover

crossing Action
1func Recover(cur realm, target address)
source

Recover sends target's still-locked deposit to its recorded recovery address.

func Redeem

crossing Action
1func Redeem(cur realm, amount uint)
source

Redeem transfers amount of the caller's matured pending withdrawal back to the caller.

func Unvault

crossing Action
1func Unvault(cur realm, amount uint)
source

Unvault marks amount of the caller's deposit for withdrawal and starts the lock timer.

Dependencies

Imports 3

/r/g1magld5ptgzaktqcexxq0pgq5lmdf0kz6re8hlv/demo/vault/v0

Imported by indexer

Source Files 4