# `gno.land/p/moul/x/pair/v0`
One constant-product AMM pair (`x*y=k`, 30 bps to the liquidity providers), as a
pure package, so that the realm holding a pair can be twenty lines of glue.
This is the shared half of the **instance-per-realm** pattern: gno's answer to
the Ethereum factory shape, where an ecosystem is a frontend over thousands of
tiny identical contracts, one per token couple.
| artifact | what it is |
|---|---|
| `p/moul/x/pair/v0` | this package, the whole behaviour, deployed once |
| an instance realm | ~20 lines: two constants, an `init` that registers, one-line re-exports. See `r/moul/x/pairs/aaabbb/v0` |
| `r/moul/x/pairreg/v0` | the registry every instance announces itself to, and the unified frontend over all of them |
| `tools/pairgen` | the factory, which runs on your machine because gno has no on-chain deploy |
## What gno changes about the Ethereum pattern
- **No on-chain factory.** `vm/add_package` is permanently denied to realm code,
so an instance is an `addpkg` transaction signed by a human, priced in gas and
a storage deposit. `tools/pairgen` fills the template and prints the
command.
- **Spawning is still permissionless.** Any address may deploy under
`gno.land/r/<its own g1 address>/**` with no registered username, so anyone can
run their own instance and land in the registry.
- **No clone trick needed.** Ethereum copies bytecode or delegatecalls through an
EIP-1167 proxy; an import stores this package once for every instance.
- **The registry sees live state.** Instances register a `*pair.Pair` pointer, so
one `vm/qrender` renders every instance's real reserves, with no indexer.
- **Nothing attests the code.** No realm can read another package's source, so an
instance's code can only be verified off chain, by diffing it against freshly
generated output. Unforgeable identity, no code attestation: the mirror image
of CREATE2.
## Using it from an instance
```go
var p *pair.Pair
func init(cur realm) {
p = pair.New(keyA, keyB, grc20reg.MustGet(keyA), grc20reg.MustGet(keyB))
pairreg.Register(cross(cur), p)
}
func Swap(cur realm, keyIn string, amountIn, minOut int64) int64 {
return p.Swap(0, cur, keyIn, amountIn, minOut)
}
```
Every state-changing method is shaped `(_ int, rlm realm, ...)`. That is forced,
not stylistic: a pure package cannot declare a crossing function (`func F(cur
realm, ...)` fails to compile there), and the parameter cannot be named `cur`
either. The realm value arrives non-crossing, which is exactly what is wanted:
the frame stays the instance realm, so funds move to and from the **instance's**
address and grc20's `rlm.IsCurrent()` spoof check still passes. `p/moul/x/framelab/v0`
is the 30-line probe that pins this property.
A pure package also cannot import a realm, so this one never resolves a token by
path: the instance passes `*grc20.Token` handles in.
## Economics, and what is deliberately absent
Reserves are stored fields, never derived from `BalanceOf`, which removes
`MINIMUM_LIQUIDITY`, `skim`/`sync` and the first-depositor inflation attack in
one decision. The price: tokens sent directly to an instance's address are
permanently stuck. The first deposit mints `shares = amountA` with no `sqrt`;
later deposits mint `min(A-side, B-side)` with floor division, so an off-ratio
deposit rounds against the depositor. No oracle, no flash swap, no routing, no
protocol fee, no governance.
All amounts are `int64` and the fee denominator is 1000, so every reserve is
capped at `MaxReserve = MaxInt64/1000`. **Unusable with 18-decimal tokens**; six
to nine decimals is the practical band.
Full design study of the economics: [gno-contracts#135](https://github.com/moul/gno-contracts/issues/135).
## When to copy this pattern, and when not to
Uniswap V4 moved from a pair-per-contract factory to a singleton, and
[PR #137](https://github.com/moul/gno-contracts/pull/137) is the same AMM as one
realm holding N pools. An AMM is therefore the contested case, kept here as a
deliberate A/B.
Reach for instance-per-realm when instances have **different owners, trust and
lifecycles** (a user's shop, a DAO, a game table). Keep one realm with N objects
when instances are fungible parts of one shared network (liquidity, an order
book, a global index).
<!-- 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.