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 at
the protocol’s own oracle prices: buys execute at wstGBP.mintcost(), sells at
wstGBP.burncost(). That gives the pool effectively infinite depth inside the
wrapper’s own ~25bps bid/ask band — the spread is the wrapper’s redeem fee, with no
extra hook fee — and both prices ratchet up as NAV accrues.
Two properties follow directly from the design:
- No LP.
beforeAddLiquidityreverts — the pool never holds AMM liquidity; every swap is a wrappermintorredeem. - No capital, no owner. The hook is ownerless, holds no funds, and adds no fee, admin, or pause of its own — it wraps the swap’s own tokens.
The hook is a pure pass-through to the wstGBP wrapper: the execution price is whatever
mintcost()/burncost() return at that block, and swaps are subject to the same
on-chain governance surface as direct mint/redeem — the oracle price, market open/close,
capacity, cooldown, and compliance gates described in the
Contract Reference. Always pass real slippage bounds.
Deployed contracts — Ethereum mainnet (chainId 1)
All four contracts are ownerless and hold no capital.
| Contract | Purpose | Address |
|---|---|---|
WsgemBackstopHook | The v4 hook | 0xfE36B48c9c0240991E4CEf006a2445F2ff524888 |
WsgemSwapRouter | Settle-first v4 swap router | 0x21734507fDca48A3b4e8C496280b63a37D3bD0C8 |
WsgemQuoter | On-chain quotes & executability preview | 0x9B409f87aeaADBE912632b1E4de855B6aFCc71Ee |
WsgemDirectAdapter | Aggregator / solver adapter (no pool) | 0xBE402d34f31133B1Dc00277f24F8ce2d975CBe23 |
Uniswap v4 PoolManager | Canonical v4 singleton | 0x000000000004444c5dc75cB358380D2e3dE08A90 |
Pool id & canonical PoolKey
Uniswap v4 pools have no address — the pool lives inside the PoolManager singleton,
keyed by poolId = keccak256(abi.encode(PoolKey)):
| Field | Value |
|---|---|
currency0 | tGBP — 0x27f6c8289550fCE67f6B50BeD1F519966aFE5287 |
currency1 | wstGBP — 0x57C3571f10767E49C9d7b60feb6c67804783B7aE |
fee | 0 |
tickSpacing | 1 |
hooks | 0xfE36B48c9c0240991E4CEf006a2445F2ff524888 |
| poolId | 0xdb21c31f461611ebeeab8af1280c77a82bb81725e1bf9d6093fbbc207a375ce5 |
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— buy wstGBP (pay tGBP; executes as a wrappermintatmintcost()).zeroForOne == false— sell wstGBP (receive tGBP; executes as a wrapperredeematburncost()).
Both tokens are 18 decimals; all prices are WAD (1e18) tGBP-per-wstGBP.
Swapping on v4 — 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 — 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.
Aggregators & solvers — WsgemDirectAdapter
WsgemDirectAdapter is a standalone, ownerless approve → 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 — 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.
What integrators should monitor
All of these are public on-chain reads on wstGBP (0x57C3…B7aE); they gate the pool,
the router, and the adapter alike:
| Read | Why 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. |
Compliance gating 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 compliance-checked.
Choosing a venue
| You are | Use |
|---|---|
| Routing v4-native flow | WsgemSwapRouter against the canonical PoolKey |
| A DEX aggregator or CoW solver | WsgemDirectAdapter (approve + swap) |
| Quoting | WsgemQuoter, the adapter’s quote views, or off-chain mintcost()/burncost() math |
| Minting/redeeming your own funds | Direct 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.