Mint & Redeem
Minting deposits tGBP and receives wstGBP. Redeeming burns wstGBP and returns
tGBP. Both quote against the live oracle price; mint has no fee, redeem takes a
0.25% haircut. All amounts are 18‑decimal (WAD) uint256.
Addresses used below — wstGBP token 0x57C3571f10767E49C9d7b60feb6c67804783B7aE,
tGBP 0x27f6c8289550fCE67f6B50BeD1F519966aFE5287. See the
Contract Reference for the full ABI.
Mint
mint(uint256 amt) → uint256 _out — amt is the tGBP paid in; _out is the wstGBP minted.
Approve tGBP
The token pulls tGBP via transferFrom, so the caller must first approve the wstGBP
contract as spender of tGBP (exact amount or max).
Call mint(amt)
The contract reads the fee‑adjusted mintcost(), requires amt >= mintcost (else
DustThreshold), computes _out = amt * 1e18 / mintcost (floored), checks
totalSupply + _out <= capacity (else ExceedsCap), pulls the tGBP, mints wstGBP, and
emits ContractCreated(creator, price, amount).
Preview math
out = wdiv(amountIn, mintcost) where wdiv(x, y) = x * 1e18 / y (floored, mirrors the
contract). Because bpsin = 0, mintcost() == navprice().
const WAD = 10n ** 18n
const wdiv = (x: bigint, y: bigint) => (x * WAD) / y
// out = how much wstGBP you receive for `amountIn` tGBP
const out = wdiv(amountIn, mintcost) // both 18-dec bigints
// Reverts on-chain if:
// amountIn < mintcost -> DustThreshold
// totalSupply + out > capacity -> ExceedsCap
// mintable() === false -> mint haltedMint with viem
import { createWalletClient, createPublicClient, http, erc20Abi } from 'viem'
import { mainnet } from 'viem/chains'
import { wstgbpAbi } from './wstgbpAbi'
const WSTGBP = '0x57C3571f10767E49C9d7b60feb6c67804783B7aE'
const TGBP = '0x27f6c8289550fCE67f6B50BeD1F519966aFE5287'
const pub = createPublicClient({ chain: mainnet, transport: http() })
const wallet = createWalletClient({ chain: mainnet, transport: http(), account })
// 1. approve tGBP
await wallet.writeContract({
address: TGBP,
abi: erc20Abi,
functionName: 'approve',
args: [WSTGBP, amountIn],
})
// 2. mint
const { request } = await pub.simulateContract({
address: WSTGBP,
abi: wstgbpAbi,
functionName: 'mint',
args: [amountIn],
account,
})
const hash = await wallet.writeContract(request)Redeem
redeem(uint256 amt) → uint256 _id — amt is the wstGBP burned. Minimum 1 WAD
(1e18); smaller reverts with DustThreshold.
With the current cooldown = 0, redeem is atomic: it burns wstGBP and pays out
tGBP in the same transaction, internally calling exit(_id) for you. The two‑step path
below only matters if a non‑zero cooldown is ever configured.
Atomic redemption (cooldown 0)
The contract reads burncost(), computes _claim = amt * burncost / 1e18, burns the
wstGBP, and immediately settles tGBP. It emits both ContractRedemption(id, date, redeemer, amount) and ClaimProcessed(id, claimer, amount) in the same receipt — decode
ClaimProcessed to confirm the atomic payout.
const wmul = (x: bigint, y: bigint) => (x * y) / WAD
// out = tGBP received for burning `amountIn` wstGBP
const out = wmul(amountIn, burncost)
// Reverts on-chain if:
// amountIn < 1e18 -> DustThreshold (min one whole wstGBP)
// burnable() === false -> redeem haltedconst { request } = await pub.simulateContract({
address: WSTGBP,
abi: wstgbpAbi,
functionName: 'redeem',
args: [amountIn],
account,
})
const hash = await wallet.writeContract(request)
const receipt = await pub.waitForTransactionReceipt({ hash })
// parse logs for ClaimProcessed (atomic) vs only ContractRedemption (queued)Queued redemption (cooldown > 0)
If cooldown() is non‑zero, redeem instead queues a claim: it stores a
Redemption { amount, redeemer, date = now + cooldown }, returns the redemption _id,
and emits only ContractRedemption. Once block.timestamp >= date, anyone can call
exit(id) to settle:
// later, after the cooldown elapses
await wallet.writeContract({
address: WSTGBP,
abi: wstgbpAbi,
functionName: 'exit',
args: [id],
})exit pays min(redemption.amount, gem.balanceOf(token)) — supporting partial claims if
the contract is temporarily underfunded — and emits ClaimProcessed.
Don’t assume cooldown. Read cooldown() on‑chain and branch: zero ⇒ expect
ClaimProcessed in the same receipt; non‑zero ⇒ store the _id from
ContractRedemption and call exit(id) after date.
Reverse quoting (target an exact output)
The contract floors on the forward pass, so to receive at least a target output you must round the input up:
const wmulUp = (x: bigint, y: bigint) => (x * y + WAD - 1n) / WAD
const wdivUp = (x: bigint, y: bigint) => (x * WAD + y - 1n) / y
// smallest tGBP input to mint at least `targetOut` wstGBP
const amountInForMint = wmulUp(targetOut, mintcost)
// smallest wstGBP input to redeem at least `targetOut` tGBP
const amountInForRedeem = wdivUp(targetOut, burncost)Events
| Event | Emitted by | Meaning |
|---|---|---|
ContractCreated(creator, price, amount) | mint | wstGBP minted to creator at price. |
ContractRedemption(id, date, redeemer, amount) | redeem | A redemption was opened (date = claimable time). |
ClaimProcessed(id, claimer, amount) | exit (and atomically inside redeem when cooldown 0) | tGBP paid out for redemption id. |
See Integration Recipes for watching these events and reading contract state in a single multicall.