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
  • basket.gnogno
    1package grc20wrap23import (4	"math/overflow"56	"gno.land/p/nt/grc20/v0"7	"gno.land/p/nt/seqid/v0"8

    Functions

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

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

    "gno.land/p/nt/ufmt/v0"
    9)
    10
    11// leg is one component of a Basket.
    12type leg struct {
    13 tok *grc20.Token
    14 teller grc20.Teller
    15 per int64 // units escrowed per meta unit
    16 held int64 // units escrowed so far
    17}
    18
    19// Basket issues ONE meta-token over several underlying GRC20s at fixed
    20// proportions. Minting a meta unit escrows every leg's `per` amount; redeeming
    21// hands all of them back.
    22//
    23// A two-leg basket is the interesting one: it makes a pair tradable, quotable
    24// and transferable as a single object, without a price oracle, an AMM or any
    25// notion of what the legs are worth. The basket never values anything - it only
    26// ever swaps a fixed bundle for a receipt and back, so it cannot be arbitraged
    27// or drained. What it CAN do is let the market price the bundle, which is
    28// exactly how an index token works.
    29//
    30// Proportions are fixed at creation. Rebalancing would mean revaluing the legs,
    31// which needs a price, which is a different contract.
    32type Basket struct {
    33 legs []*leg
    34 home address
    35 tok *grc20.Token
    36 led *grc20.PrivateLedger
    37}
    38
    39// NewBasket issues a meta-token over tokens[i], escrowing parts[i] units of
    40// each per meta unit.
    41//
    42// Both slices must be the same length, hold at least two entries, name no token
    43// twice, and every part must be positive. As with NewVault, pass the owning
    44// realm's own cur: escrow lands at that realm's address.
    45func NewBasket(tokens []*grc20.Token, parts []int64, name, symbol string, decimals int, id seqid.ID, rlm realm) *Basket {
    46 if len(tokens) < 2 || len(tokens) != len(parts) {
    47 panic(ErrBadLegs)
    48 }
    49 legs := make([]*leg, 0, len(tokens))
    50 for i, t := range tokens {
    51 if t == nil {
    52 panic(ErrNilToken)
    53 }
    54 if parts[i] <= 0 {
    55 panic(ErrBadLegs)
    56 }
    57 for _, seen := range legs {
    58 if seen.tok.ID() == t.ID() {
    59 panic(ErrBadLegs)
    60 }
    61 }
    62 legs = append(legs, &leg{
    63 tok: t,
    64 teller: t.RealmTeller(0, rlm),
    65 per: parts[i],
    66 })
    67 }
    68 tok, led := grc20.NewToken(name, symbol, decimals, id, rlm)
    69 return &Basket{
    70 legs: legs,
    71 home: rlm.Address(),
    72 tok: tok,
    73 led: led,
    74 }
    75}
    76
    77// Token is the meta-token this basket issues.
    78func (b *Basket) Token() *grc20.Token { return b.tok }
    79
    80// Home is the escrow account: the host realm's address.
    81func (b *Basket) Home() address { return b.home }
    82
    83// Legs is the number of components.
    84func (b *Basket) Legs() int { return len(b.legs) }
    85
    86// Leg returns the i-th component: its token, the units escrowed per meta unit,
    87// and the units escrowed so far.
    88func (b *Basket) Leg(i int) (*grc20.Token, int64, int64) {
    89 l := b.legs[i]
    90 return l.tok, l.per, l.held
    91}
    92
    93// Solvent reports whether every leg's escrow account still covers what the
    94// basket recorded taking in.
    95func (b *Basket) Solvent() bool {
    96 for _, l := range b.legs {
    97 if l.tok.BalanceOf(b.home) < l.held {
    98 return false
    99 }
    100 }
    101 return true
    102}
    103
    104// Fuse escrows per*units of every leg from `from` and mints `units` of the meta
    105// token to it.
    106//
    107// `from` must have approved b.Home() on EVERY leg first. The whole basket is
    108// priced and checked before the first transfer, so a caller short on leg two
    109// gets an error with leg one untouched.
    110func (b *Basket) Fuse(_ int, rlm realm, from address, units int64) error {
    111 if units <= 0 {
    112 return ErrInvalidAmount
    113 }
    114 need := make([]int64, len(b.legs))
    115 for i, l := range b.legs {
    116 n, ok := overflow.Mul64(l.per, units)
    117 if !ok {
    118 return ErrOverflow
    119 }
    120 if l.tok.BalanceOf(from) < n {
    121 return ErrShortBalance
    122 }
    123 if l.tok.Allowance(from, b.home) < n {
    124 return ErrShortAllowance
    125 }
    126 need[i] = n
    127 }
    128 // Past here every leg was checked, so a failure is an invariant
    129 // violation and the transaction must not stand.
    130 for i, l := range b.legs {
    131 if err := l.teller.TransferFrom(0, rlm, from, b.home, need[i]); err != nil {
    132 panic(err)
    133 }
    134 l.held += need[i]
    135 }
    136 if err := b.led.Mint(from, units); err != nil {
    137 panic(err)
    138 }
    139 return nil
    140}
    141
    142// Defuse burns `units` of the meta token held by `to` and returns every leg's
    143// share of the escrow to it.
    144func (b *Basket) Defuse(_ int, rlm realm, to address, units int64) error {
    145 if units <= 0 {
    146 return ErrInvalidAmount
    147 }
    148 give := make([]int64, len(b.legs))
    149 for i, l := range b.legs {
    150 n, ok := overflow.Mul64(l.per, units)
    151 if !ok {
    152 return ErrOverflow
    153 }
    154 if n > l.held {
    155 return ErrUnbacked
    156 }
    157 give[i] = n
    158 }
    159 if err := b.led.Burn(to, units); err != nil {
    160 return err
    161 }
    162 for i, l := range b.legs {
    163 l.held -= give[i]
    164 if err := l.teller.Transfer(0, rlm, to, give[i]); err != nil {
    165 panic(err)
    166 }
    167 }
    168 return nil
    169}
    170
    171// Move transfers meta units between two accounts.
    172func (b *Basket) Move(from, to address, amount int64) error {
    173 if amount <= 0 {
    174 return ErrInvalidAmount
    175 }
    176 return b.led.Transfer(from, to, amount)
    177}
    178
    179// Allow sets `spender`'s allowance over `owner`'s meta balance.
    180func (b *Basket) Allow(owner, spender address, amount int64) error {
    181 return b.led.Approve(owner, spender, amount)
    182}
    183
    184// MoveFrom spends `spender`'s allowance over `owner`'s meta balance.
    185func (b *Basket) MoveFrom(spender, owner, to address, amount int64) error {
    186 if amount <= 0 {
    187 return ErrInvalidAmount
    188 }
    189 return b.led.TransferFrom(owner, spender, to, amount)
    190}
    191
    192// Summary renders the basket as a markdown block.
    193func (b *Basket) Summary() string {
    194 s := ufmt.Sprintf("**%s** (%s) - meta-token over %d legs\n\n",
    195 b.tok.GetName(), b.tok.GetSymbol(), len(b.legs))
    196 s += ufmt.Sprintf("- meta supply: %d\n", b.tok.TotalSupply())
    197 for _, l := range b.legs {
    198 s += ufmt.Sprintf("- leg %s: %d per unit, %d escrowed\n",
    199 l.tok.GetSymbol(), l.per, l.held)
    200 }
    201 if b.Solvent() {
    202 s += "- solvent: yes\n"
    203 } else {
    204 s += "- solvent: **NO**\n"
    205 }
    206 return s
    207}
    208