Skip to Content
wstGBP is live on Ethereum mainnet · all addresses in these docs are verifiable on-chain
GuidesMint & Redeem

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 _outamt 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 halted

Mint 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 _idamt 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 halted
const { 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

EventEmitted byMeaning
ContractCreated(creator, price, amount)mintwstGBP minted to creator at price.
ContractRedemption(id, date, redeemer, amount)redeemA 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.

Last updated on