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

voucher.gno

6.56 Kb · 178 lines
  1package ucs03_zkgm
  2
  3import (
  4	"gno.land/p/demo/tokens/grc20"
  5	z "gno.land/p/onbloc/ibc/union/zkgm"
  6	u256 "gno.land/p/onbloc/math/uint256"
  7)
  8
  9// Voucher bundles a wrapped grc20 token, its private mint/burn ledger,
 10// and the origin token's decimal precision.
 11// It is proxy-owned state held in the Store,
 12// so its mint/burn rights survive impl upgrades.
 13//
 14// Packet amounts are in the origin token's decimals;
 15// the grc20 ledger holds them downscaled to the (possibly capped) ledger decimals,
 16// so they fit int64.
 17// MintScaled / BurnScaled / TransferScaled apply that downscale,
 18// so callers never combine the scale and the ledger by hand;
 19// the raw int64 ops stay reachable via Ledger().
 20//
 21// SECURITY: those methods and Ledger() all hand out the mint/burn capability,
 22// so a *Voucher must stay impl-gated.
 23// The Store is injected and never publicly returned,
 24// so untrusted realms can only read balances via VoucherBalanceOf.
 25type Voucher struct {
 26	token          *grc20.Token
 27	ledger         *grc20.PrivateLedger
 28	originDecimals uint8
 29}
 30
 31// NewVoucher is a crossing-safe constructor,
 32// so the impl realm can build one without tripping the foreign-composite-literal rule.
 33// originDecimals is the origin token's precision;
 34// the ledger token is created at min(originDecimals, ledger cap).
 35func NewVoucher(token *grc20.Token, ledger *grc20.PrivateLedger, originDecimals uint8) *Voucher {
 36	return &Voucher{token: token, ledger: ledger, originDecimals: originDecimals}
 37}
 38
 39// VoucherInfo is a read-only snapshot of a wrapped voucher's grc20 identity
 40// for the voucher-list getter. It carries only scalar fields copied out of
 41// Voucher/Token — no token/ledger handles — so listing vouchers never hands
 42// out the mint/burn capability that *Voucher.Ledger() exposes.
 43type VoucherInfo struct {
 44	Denom          string
 45	Name           string
 46	Symbol         string
 47	OriginDecimals uint8
 48	LedgerDecimals uint8
 49	TotalSupply    int64
 50}
 51
 52// NewVoucherInfo is a crossing-safe constructor,
 53// so the impl realm (and test doubles) can build one without tripping the
 54// foreign-composite-literal rule.
 55func NewVoucherInfo(denom, name, symbol string, originDecimals, ledgerDecimals uint8, totalSupply int64) VoucherInfo {
 56	return VoucherInfo{
 57		Denom:          denom,
 58		Name:           name,
 59		Symbol:         symbol,
 60		OriginDecimals: originDecimals,
 61		LedgerDecimals: ledgerDecimals,
 62		TotalSupply:    totalSupply,
 63	}
 64}
 65
 66// Token returns the read-only token handle (safe: balances, metadata).
 67func (v *Voucher) Token() *grc20.Token { return v.token }
 68
 69// Ledger returns the mint/burn capability. Callers MUST be impl-gated.
 70func (v *Voucher) Ledger() *grc20.PrivateLedger { return v.ledger }
 71
 72// OriginDecimals returns the origin token's precision.
 73// The scalar is copied across the realm boundary, so impl reads are safe.
 74func (v *Voucher) OriginDecimals() uint8 { return v.originDecimals }
 75
 76// ScaleExp is the origin->ledger downscale exponent:
 77// the low-order digits dropped between origin precision and the ledger token's precision.
 78// Derived from both stored decimals, so it needs no global constant,
 79// and clamps to 0 for native/low-decimal tokens.
 80func (v *Voucher) ScaleExp() int {
 81	exp := int(v.originDecimals) - v.token.GetDecimals()
 82	if exp < 0 {
 83		return 0
 84	}
 85
 86	return exp
 87}
 88
 89// ToLedgerAmount narrows an origin-precision amount to this voucher's int64 ledger amount,
 90// rejecting sub-precision dust and int64 overflow.
 91// MintScaled, BurnScaled, TransferScaled and the impl's pre-flight checks all route through it.
 92func (v *Voucher) ToLedgerAmount(amount *u256.Uint) (int64, error) {
 93	return z.ScaleDownToInt64(amount, v.ScaleExp())
 94}
 95
 96// MintScaled credits an origin-precision amount, downscaled to the ledger.
 97// Carries the mint capability; callers MUST be impl-gated.
 98func (v *Voucher) MintScaled(receiver address, amount *u256.Uint) error {
 99	n, err := v.ToLedgerAmount(amount)
100	if err != nil {
101		return err
102	}
103
104	return v.ledger.Mint(receiver, n)
105}
106
107// BurnScaled debits an origin-precision amount, downscaled to the ledger.
108// Carries the burn capability; callers MUST be impl-gated.
109func (v *Voucher) BurnScaled(holder address, amount *u256.Uint) error {
110	n, err := v.ToLedgerAmount(amount)
111	if err != nil {
112		return err
113	}
114
115	return v.ledger.Burn(holder, n)
116}
117
118// TransferScaled moves an origin-precision amount between holders, downscaled to the ledger.
119// Carries the ledger capability; callers MUST be impl-gated.
120func (v *Voucher) TransferScaled(from, to address, amount *u256.Uint) error {
121	n, err := v.ToLedgerAmount(amount)
122	if err != nil {
123		return err
124	}
125
126	return v.ledger.Transfer(from, to, n)
127}
128
129// Approve grants spender an allowance on holder's ledger-precision balance.
130// Unlike MintScaled/BurnScaled/TransferScaled, the amount is not scaled from
131// origin precision: it is a self-service action on the holder's own balance,
132// matching the ledger units VoucherBalanceOf already reads.
133// Carries the ledger's Approve capability; callers MUST be impl-gated.
134func (v *Voucher) Approve(_ int, rlm realm, spender address, amount int64) error {
135	teller := v.ledger.ImpersonateTeller(rlm.Previous().Address())
136	return teller.Approve(0, rlm, spender, amount)
137}
138
139// Transfer moves amount of holder's ledger-precision balance to another
140// address. Same scale as Approve: no origin-precision scaling.
141// Carries the ledger's Transfer capability; callers MUST be impl-gated.
142func (v *Voucher) Transfer(_ int, rlm realm, to address, amount int64) error {
143	teller := v.ledger.ImpersonateTeller(rlm.Previous().Address())
144	return teller.Transfer(0, rlm, to, amount)
145}
146
147// VoucherApprove lets the caller grant spender an allowance on their own
148// wrapped-voucher balance for ibcDenom, so the voucher can be used through
149// GRC20-composable flows (e.g. Gnoswap) that rely on TransferFrom.
150func VoucherApprove(cur realm, ibcDenom string, spender address, amount int64) {
151	assertIsNotPaused()
152
153	if err := mustGetImpl().VoucherApprove(0, cur, ibcDenom, spender, amount); err != nil {
154		panic(err)
155	}
156}
157
158// VoucherTransfer lets the caller move their own wrapped-voucher balance for
159// ibcDenom directly to another address.
160func VoucherTransfer(cur realm, ibcDenom string, to address, amount int64) {
161	assertIsNotPaused()
162
163	if err := mustGetImpl().VoucherTransfer(0, cur, ibcDenom, to, amount); err != nil {
164		panic(err)
165	}
166}
167
168// VoucherBalanceOf returns addr's wrapped-voucher balance for ibcDenom, or 0 if none.
169// It reads the proxy-owned Store,
170// so the balance survives impl upgrades and does not depend on grc20reg's key.
171func VoucherBalanceOf(ibcDenom string, addr address) int64 {
172	vou, ok := store.GetVoucher(ibcDenom)
173	if !ok {
174		return 0
175	}
176
177	return vou.Token().BalanceOf(addr)
178}