Skip to Content
wstGBP is live on Ethereum mainnet. All addresses in these docs are verifiable on-chain.
GuidesUniswap v4 & Aggregators

Uniswap v4 & Aggregators

wstGBP trades on-chain through a tGBP/wstGBP Uniswap v4 pool whose hook (WsgemBackstopHook) routes every swap through the wrapper’s atomic mint/redeem: buys execute at wstGBP.mintcost(), sells at wstGBP.burncost(). Depth inside the wrapper’s own ~25bps bid/ask band (the spread is the wrapper’s redeem fee) is bounded by mint capacity and the wrapper’s tGBP balance rather than by pool liquidity, and both prices ratchet up as NAV accrues.

The design has two consequences:

  • There is no LP. beforeAddLiquidity reverts, so every swap is a wrapper mint or redeem, never AMM liquidity.
  • There is no capital and no owner. The hook is ownerless, holds no funds, and adds no fee, admin, or pause of its own.

Swaps are subject to the same on-chain governance surface as direct mint/redeem: the oracle price, market open/close, capacity, cooldown, and ban-list screening described in the Contract Reference. Quotes are point-in-time; always pass real slippage bounds.

Deployed contracts on Ethereum mainnet (chainId 1)

All five contracts are ownerless and hold no capital.

ContractPurposeAddress
WsgemBackstopHookThe v4 hook0xfE36B48c9c0240991E4CEf006a2445F2ff524888
WsgemSwapRouterSettle-first v4 swap router0x21734507fDca48A3b4e8C496280b63a37D3bD0C8
WsgemQuoterOn-chain quotes & executability preview0x9B409f87aeaADBE912632b1E4de855B6aFCc71Ee
WsgemDirectAdapterAggregator / solver adapter (no pool)0xBE402d34f31133B1Dc00277f24F8ce2d975CBe23
WsgemHookHelperCoW Swap order-hook wrap/unwrap target0x4F93a2E29B0AA75875Ab922d780B6dc59b415B6A
Uniswap v4 PoolManagerCanonical v4 singleton0x000000000004444c5dc75cB358380D2e3dE08A90

Solidity source for the backstop hook and its periphery (the router, quoter, adapter, and hook helper), along with their tests and deployment scripts, is public at Arb-Capital/wstgbp-univ4-hook .

Pool id & canonical PoolKey

Uniswap v4 pools have no address. The pool lives inside the PoolManager singleton, keyed by poolId = keccak256(abi.encode(PoolKey)):

FieldValue
currency0tGBP (0x27f6c8289550fCE67f6B50BeD1F519966aFE5287)
currency1wstGBP (0x57C3571f10767E49C9d7b60feb6c67804783B7aE)
fee0
tickSpacing1
hooks0xfE36B48c9c0240991E4CEf006a2445F2ff524888
poolId0xdb21c31f461611ebeeab8af1280c77a82bb81725e1bf9d6093fbbc207a375ce5

Pin the canonical PoolKey. The router is intentionally generic over PoolKey, and the hook validates only the two currencies, not the fee, tick spacing, or hook address. Integrators, bots, and frontends must hardcode or validate the exact key above and never route through a user- or route-supplied key. (The quoter needs no key; it is bound to the wrapper at construction.)

Swap direction

tGBP < wstGBP numerically, so currency0 = tGBP, currency1 = wstGBP:

  • zeroForOne == true buys wstGBP: pay tGBP, executed as a wrapper mint at mintcost().
  • zeroForOne == false sells wstGBP: receive tGBP, executed as a wrapper redeem at burncost().

Both tokens are 18 decimals; all prices are WAD (1e18) tGBP-per-wstGBP.

Swapping on v4 with WsgemSwapRouter

v4 swaps against this hook must be settle-first (pay the input before the swap executes). Route through WsgemSwapRouter or any settle-first solver; a plain swap-then-settle v4 router will revert.

function swapExactInput( PoolKey calldata key, bool zeroForOne, uint256 amountIn, uint256 minAmountOut, address recipient, // address(0) => msg.sender uint256 deadline ) external returns (uint256 amountOut); function swapExactOutput( PoolKey calldata key, bool zeroForOne, uint256 amountOut, uint256 maxAmountIn, // surplus is refunded address recipient, uint256 deadline ) external returns (uint256 amountIn);

Both have Permit2 variants (swapExactInputPermit2 / swapExactOutputPermit2) that fund the swap from a Permit2 SignatureTransfer instead of a router approval; the permit’s token must be the input currency and its deadline is the swap deadline.

The router enforces minAmountOut (exact-input), maxAmountIn (exact-output), and full delivery of the exact output: a swap reverts rather than silently delivering less than agreed. Quotes are point-in-time and the oracle ratchets between quote and execution, so never send minAmountOut = 0.

Quoting with WsgemQuoter

Pre-flight swaps with the quoter rather than simulating reverts:

function quoteExactInput(bool zeroForOne, uint256 amountIn) external view returns (uint256 amountOut); function quoteExactOutput(bool zeroForOne, uint256 amountOut) external view returns (uint256 amountIn); // amountSpecified: negative = exact-input, positive = exact-output (PoolManager convention) function previewSwap(bool zeroForOne, int256 amountSpecified) external view returns (uint256 amountIn, uint256 amountOut, bool executable, string memory reason);

previewSwap reports the live blockers instead of reverting: market closed, dust threshold, capacity exceeded, wrapper underfunded, redeem cooldown active, or oracle paused. Quoter output matches execution exactly.

Off-chain, quote directly from wstGBP.mintcost() / burncost() with the same WAD math as direct mint/redeem (see Mint & Redeem).

WsgemDirectAdapter for aggregators and solvers

WsgemDirectAdapter is a standalone, ownerless approve-then-swap contract that calls wstGBP.mint/redeem directly: no pool, no v4 callback, ordinary swap-then-settle semantics. DEX aggregators (Odos, LI.FI, Paraswap) and CoW Protocol solvers can call it like any swap contract; no settle-first router is needed. Prices and guards are identical to the hook’s.

// tokenIn == tGBP buys wstGBP (mint); tokenIn == wstGBP sells (redeem) function swapExactInput( address tokenIn, uint256 amountIn, uint256 minAmountOut, address recipient, // address(0) => msg.sender uint256 deadline ) external returns (uint256 amountOut); function swapExactOutput( address tokenIn, uint256 amountOut, uint256 maxAmountIn, // only the computed exact input is pulled address recipient, uint256 deadline ) external returns (uint256 amountIn); function quoteExactInput(address tokenIn, uint256 amountIn) external view returns (uint256 amountOut); function quoteExactOutput(address tokenIn, uint256 amountOut) external view returns (uint256 amountIn);

Permit2 variants (swapExactInputPermit2 / swapExactOutputPermit2) are available here too. The adapter is a pure price-taker with no price bounds of its own, so pass real slippage bounds.

CoW Hooks  are user pre/post-interactions, not a liquidity source. Giving CoW solvers access to this venue means route integration  of the adapter; attaching a wrap/unwrap action to a user’s order is what WsgemHookHelper (below) is for.

CoW Swap order hooks with WsgemHookHelper

CoW Swap hooks  are {target, callData, gasLimit} entries in an order’s appData, executed by the public HooksTrampoline, which holds no funds and is callable by anyone. The adapter can’t be a hook target (it pulls from msg.sender), so WsgemHookHelper fills that gap: an ownerless wrap/unwrap target whose proceeds are hard-wired back to the user, at the same mintcost()/burncost() oracle prices as every other venue.

// POST-hook, after an order that buys tGBP: wrap the proceeds into wstGBP. // Sweeps min(balance, allowance) of the owner's tGBP; all wstGBP goes to owner. function wrapAll(address owner, uint256 minAmountOut) external returns (uint256 amountOut); // PRE-hook, before an order that sells tGBP: redeem the owner's wstGBP back to tGBP. function unwrap(address owner, uint256 amountIn, uint256 minAmountOut) external returns (uint256 amountOut); function unwrapAll(address owner, uint256 minAmountOut) external returns (uint256 amountOut);

The security model is owner-bound: anyone may call every function, but funds only ever move from the owner back to the owner, capped by the owner’s ERC-20 allowance to the helper. The worst an arbitrary caller can do is trigger a conversion of the approved amount at the same mintcost()/burncost() oracle price as every other venue, delivered to the owner. That is bounded griefing (the wrapper’s ~25bps bid/ask spread on a forced round-trip), never extraction.

Integration notes:

  • Grant exact-amount approvals to the helper, per order. wrapAll sweeps min(balance, allowance) because post-hook proceeds vary with order surplus.
  • minAmountOut protects the signed hook against oracle movement between order signing and execution; set it from a quote, as with any swap.
  • Hook execution is a weak guarantee (solver social consensus): an order can settle even if its hook was skipped. A skipped wrapAll leaves the owner holding tGBP plus a revocable approval; a skipped unwrap leaves the order unable to settle for lack of the sell token. Either way, nothing is lost.
  • Unwraps face the same sell-side gates as every venue: they revert if wstGBP.cooldown() != 0 (RedeemCooldownActive) or the wrapper lacks tGBP (WrapperUnderfunded). There are no partial fills and no deferred payouts.
  • Each conversion emits Wrap(owner, caller, amountIn, amountOut) or Unwrap(owner, caller, amountIn, amountOut), where caller is the executor, never the payer.

Hook payload snippets live in Swap Recipes.

What integrators should monitor

All of these are public on-chain reads on wstGBP (0x57C3...B7aE); they gate the pool, the router, the adapter, and the hook helper alike:

ReadWhy it matters
mintcost() / burncost()The live execution prices (ratchet up as NAV accrues).
mintable() / burnable()Market open/close. Closed mint reverts buys; closed burn reverts sells.
cooldown()Must be 0 for sells; non-zero makes sells revert (RedeemCooldownActive) rather than queue a deferred payout. Buys are unaffected.
capacity() vs totalSupply()Remaining buy headroom. A buy past capacity reverts (ExceedsCap).
tGBP.balanceOf(wstGBP)Sell-side funding depth. Sells past it revert (WrapperUnderfunded), never partially fill.

Ban-list screening applies to swaps as it does to direct mint/redeem: the swap recipient must not be banned on tGBP (see Contract Reference). Buys settle wstGBP through the PoolManager to the recipient, and every leg is screened.

Choosing a venue

You areUse
Routing v4-native flowWsgemSwapRouter against the canonical PoolKey
A DEX aggregator or CoW solverWsgemDirectAdapter (approve + swap)
Attaching wrap/unwrap hooks to a CoW Swap orderWsgemHookHelper (exact approval + hook in appData)
QuotingWsgemQuoter, the adapter’s quote views, or off-chain mintcost()/burncost() math
Minting/redeeming your own fundsDirect mint/redeem (cheapest gas for the identical price)

Copy-paste viem snippets for all of the above (quoting, router swaps, adapter swaps, and venue monitoring) live in Swap Recipes.

Last updated on