# `gno.land/p/moul/x/grc20wrap/v0`
**Build a new GRC20 on top of one you do not control.** `Vault` escrows an
existing token and issues its own against it; `Basket` does the same over
several at once. A `Policy` decides the exchange rate in both directions and
whether the wrapped token may move, which is the whole personality of a wrapper.
```go
import (
"gno.land/p/moul/x/grc20wrap/v0"
"gno.land/p/nt/grc20/v0"
)
// In your realm, over any *grc20.Token you can reach (an import, or a
// gno.land/r/nt/grc20reg/v0 lookup):
v := grc20wrap.NewVault(under, grc20wrap.Pool{}, "Pooled FOO", "pFOO", 4, 0, cur)
// The holder approves YOUR realm's address on the underlying first, through
// the underlying realm's own entry point. Then:
shares, err := v.Wrap(0, cur, holder, 1000) // escrow 1000, mint shares
out, err := v.Unwrap(0, cur, holder, shares) // burn shares, release escrow
err = v.Donate(0, cur, patron, 500) // no mint: every share is worth more
```
## The five policies
| policy | wrapped token behaves like |
|---|---|
| `OneToOne` | a 1:1 custody receipt |
| `Ratio{Num, Den}` | the same token re-denominated (`{1000, 1}` adds three decimals) |
| `Soulbound{Base}` | a badge: wrap and unwrap freely, never transferable |
| `Pool` | a share of the escrow; `Donate` pays every holder at once |
| `Fee{Base, WrapBPS, UnwrapBPS}` | a haircut left behind, which over `Pool` pays whoever stays |
They compose: `Fee{Base: Pool{}, UnwrapBPS: 100}` is a pool with a 1% exit fee.
Writing your own means three methods and a name; embed `OneToOne` and override
only what differs.
## Basket: one token backed by two
```go
b := grc20wrap.NewBasket(
[]*grc20.Token{red, blue}, []int64{1, 2},
"Purple", "PURPLE", 4, 0, cur,
)
err := b.Fuse(0, cur, holder, 100) // escrow 100 RED + 200 BLUE, mint 100 PURPLE
err = b.Defuse(0, cur, holder, 40) // burn 40, hand back 40 RED + 80 BLUE
```
Proportions are fixed, and the meta-token is only ever minted against the real
thing, so it cannot drift from its backing or be arbitraged. It has no price
oracle and no rebalancing, because both would mean valuing the legs.
## Three things worth knowing before using it
**Custody is an allowance, never a privilege.** Escrow sits at the host realm's
address and moves through `grc20.RealmTeller`, which grc20 binds eagerly to that
address. A wrapper can only take what a holder approved for it, and can only
ever spend its own realm's balance.
**Errors are returned before anything moves, panics after.** Wrapping is two
ledger writes on two different tokens and gno has no rollback short of a panic,
so each method validates first and returns an error while the world is still
untouched, then panics if a write fails past the point of no return. Aborting
the transaction is the only atomicity available.
**A policy veto binds users, not realms.** `Vault.Move` is the only path a
signing account has, because `MsgCall` cannot build the `realm` argument grc20's
tellers require. A realm holding the wrapped token can always call
`grc20.RealmTeller` and move its own balance. `Soulbound` means "no user can
pass it on", not "it can never move".
**Live demo:** [`r/moul/x/grc20wrapdemo`](https://github.com/moul/gno-contracts/tree/main/r/moul/x/grc20wrapdemo/v0)
· render it at [`/r/moul/x/grc20wrapdemo/v0`](https://gno.land/r/moul/x/grc20wrapdemo/v0).
<!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) -->
---
Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.
**Dependency graph:**

> 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md).
<!-- END GNOCONTRACTS FOOTER -->
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.