PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/grc20wrap/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / grc20wrap
Files
8 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/grc20wrap/v0
gno
0.9

Files (8)

  • README.mdmarkdown
  • gnomod.tomltoml
  • basket.gnogno
  • policy.gnogno
  • wrap.gnogno
  • basket_test.gnogno
  • policy_test.gnogno
  • wrap_test.gnogno
  • policy.gnogno
    1package grc20wrap23import "gno.land/p/nt/ufmt/v0"45// Policy is a wrapper's personality. It sets the exchange rate in both6// directions and can veto a move of the wrapped token.7//8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    // Every method receives the vault, so a policy can price against live state -
    9// escrow, supply, custody - instead of a frozen constant. Embed OneToOne to
    10// write a policy that only overrides what it cares about.
    11type Policy interface {
    12 // WrapRate returns how many wrapped units `in` underlying units mint.
    13 // It is called BEFORE the escrow moves, so v.Held() excludes `in`.
    14 WrapRate(v *Vault, in int64) (int64, error)
    15
    16 // UnwrapRate returns how many underlying units `in` wrapped units
    17 // release. It is called before the burn, so v.Supply() includes `in`.
    18 UnwrapRate(v *Vault, in int64) (int64, error)
    19
    20 // CanMove vetoes a transfer of the wrapped token between two accounts.
    21 CanMove(v *Vault, from, to address, amount int64) error
    22
    23 // Name labels the policy in a catalogue. Short, lowercase.
    24 Name() string
    25}
    26
    27// OneToOne is the boring wrapper: one underlying unit in, one wrapped unit
    28// out, and no restriction on who may hold the result. Useful on its own as a
    29// pure custody receipt, and as the embedded base of every policy below.
    30type OneToOne struct{}
    31
    32func (OneToOne) WrapRate(v *Vault, in int64) (int64, error) { return in, nil }
    33
    34func (OneToOne) UnwrapRate(v *Vault, in int64) (int64, error) { return in, nil }
    35
    36func (OneToOne) CanMove(v *Vault, from, to address, amount int64) error { return nil }
    37
    38func (OneToOne) Name() string { return "1:1" }
    39
    40// Ratio re-denominates: Num wrapped units per Den underlying units. Both terms
    41// must be positive.
    42//
    43// Ratio{Num: 1000, Den: 1} turns one underlying unit into a thousand wrapped
    44// ones, which is how you give a token three more decimals without touching the
    45// realm that issued it. The reverse direction divides, so a redemption that
    46// rounds to zero is refused (ErrDust) rather than silently burning value: with
    47// Num=1000, unwrapping 999 wrapped units returns nothing and is rejected.
    48type Ratio struct {
    49 OneToOne
    50 Num int64
    51 Den int64
    52}
    53
    54func (r Ratio) WrapRate(v *Vault, in int64) (int64, error) {
    55 if r.Num <= 0 || r.Den <= 0 {
    56 return 0, ErrBadRatio
    57 }
    58 return mulDiv(in, r.Num, r.Den)
    59}
    60
    61func (r Ratio) UnwrapRate(v *Vault, in int64) (int64, error) {
    62 if r.Num <= 0 || r.Den <= 0 {
    63 return 0, ErrBadRatio
    64 }
    65 return mulDiv(in, r.Den, r.Num)
    66}
    67
    68func (r Ratio) Name() string { return ufmt.Sprintf("%d:%d", r.Num, r.Den) }
    69
    70// Soulbound wraps another policy and refuses every transfer of the wrapped
    71// token. Rates are whatever Base says; a nil Base means OneToOne.
    72//
    73// The result is an account-bound receipt: anyone can wrap into it and anyone
    74// can unwrap back out, but the wrapped unit itself never changes hands. That
    75// makes it a membership badge, a proof of deposit, or a vote weight that
    76// cannot be rented - while the underlying stays fully liquid one step away.
    77//
    78// The veto binds signing users only; see the package doc.
    79type Soulbound struct {
    80 Base Policy
    81}
    82
    83func (s Soulbound) base() Policy {
    84 if s.Base == nil {
    85 return OneToOne{}
    86 }
    87 return s.Base
    88}
    89
    90func (s Soulbound) WrapRate(v *Vault, in int64) (int64, error) {
    91 return s.base().WrapRate(v, in)
    92}
    93
    94func (s Soulbound) UnwrapRate(v *Vault, in int64) (int64, error) {
    95 return s.base().UnwrapRate(v, in)
    96}
    97
    98func (s Soulbound) CanMove(v *Vault, from, to address, amount int64) error {
    99 return ErrSoulbound
    100}
    101
    102func (s Soulbound) Name() string { return "soulbound/" + s.base().Name() }
    103
    104// Pool prices in shares instead of units: the wrapped token is a claim on a
    105// fraction of the escrow rather than on a fixed amount.
    106//
    107// wrap: shares = in * supply / held (1:1 while the pool is empty)
    108// unwrap: units = in * held / supply
    109//
    110// Nothing changes until someone calls Vault.Donate, which adds escrow without
    111// minting shares. Every outstanding share is then worth more, permanently and
    112// for everyone at once. That is the whole yield-bearing-token pattern in two
    113// lines of arithmetic: a fee sink, a staking reward, an airdrop to holders, all
    114// the same operation.
    115//
    116// Two honest caveats:
    117//
    118// - Division truncates, so a share is always worth marginally less than its
    119// exact fraction. The remainder stays in the pool, which is the safe
    120// direction: a vault can never promise more than it holds.
    121// - A first depositor can wrap one unit, donate a large amount, and make
    122// every later deposit smaller than one share round to zero in their favour
    123// (the ERC-4626 inflation attack). Seed the pool at creation, or keep the
    124// wrapped token's decimals well above the underlying's, before using this
    125// anywhere real.
    126type Pool struct {
    127 OneToOne
    128}
    129
    130func (Pool) WrapRate(v *Vault, in int64) (int64, error) {
    131 supply, held := v.Supply(), v.Held()
    132 if supply == 0 || held == 0 {
    133 return in, nil
    134 }
    135 return mulDiv(in, supply, held)
    136}
    137
    138func (Pool) UnwrapRate(v *Vault, in int64) (int64, error) {
    139 supply := v.Supply()
    140 if supply == 0 {
    141 return 0, ErrDust
    142 }
    143 return mulDiv(in, v.Held(), supply)
    144}
    145
    146func (Pool) Name() string { return "pool" }
    147
    148// Fee takes a basis-point haircut in both directions on top of Base (nil means
    149// OneToOne). 100 BPS is 1%.
    150//
    151// The haircut is not paid to anyone: it stays escrowed. Over a plain base that
    152// only strands value, so Fee is meant to sit over Pool, where the stranded
    153// units raise what every remaining share redeems for. The wrapper then pays its
    154// holders out of its own turnover, and paying twice - in and out - costs more
    155// than holding.
    156type Fee struct {
    157 Base Policy
    158 WrapBPS int64
    159 UnwrapBPS int64
    160}
    161
    162func (f Fee) base() Policy {
    163 if f.Base == nil {
    164 return OneToOne{}
    165 }
    166 return f.Base
    167}
    168
    169// cut removes bps basis points from amount.
    170func cut(amount, bps int64) (int64, error) {
    171 if bps < 0 || bps > 10000 {
    172 return 0, ErrBadBPS
    173 }
    174 taken, err := mulDiv(amount, bps, 10000)
    175 if err != nil {
    176 return 0, err
    177 }
    178 return amount - taken, nil
    179}
    180
    181func (f Fee) WrapRate(v *Vault, in int64) (int64, error) {
    182 out, err := f.base().WrapRate(v, in)
    183 if err != nil {
    184 return 0, err
    185 }
    186 return cut(out, f.WrapBPS)
    187}
    188
    189func (f Fee) UnwrapRate(v *Vault, in int64) (int64, error) {
    190 out, err := f.base().UnwrapRate(v, in)
    191 if err != nil {
    192 return 0, err
    193 }
    194 return cut(out, f.UnwrapBPS)
    195}
    196
    197func (f Fee) CanMove(v *Vault, from, to address, amount int64) error {
    198 return f.base().CanMove(v, from, to, amount)
    199}
    200
    201func (f Fee) Name() string {
    202 return ufmt.Sprintf("fee(%d/%dbps)/%s", f.WrapBPS, f.UnwrapBPS, f.base().Name())
    203}
    204