Reference
Trading router
Trading deploys one canonical factory and one canonical router. The router handles entry and liquidity operations, share-transfer exits and complete-set redemption, and permit-assisted LP removal.
Entrypoints
TwoWayConstantProductRouter(factory) fixes the only eligible factory. All user mutations enforce a deadline and a reentrancy guard. The router's receiver callbacks accept only the active pool's canonical share token and universe IDs. receive() accepts ETH only during redemption and only from the expected SecurityPool or its canonical portfolio settlement engine.
enterPosition(pair, longOutcome, minLongSharesOut, recipient, deadline)is payable. It creates actual complete-set shares frommsg.value, verifies equal Invalid/Yes/No deltas, swaps all opposite shares, and delivers long shares plus Invalid. It reverts withMinimum long sharesbelowminLongSharesOut. It returnsEnterResult:ethSpent,completeSetShares,oppositeSharesSwapped,additionalLongShares,totalLongShares,invalidInsurance,feeAmount,conditionalYesBpsBefore, andconditionalYesBpsAfter.createPairAndInitializeWithEth(pool, conditionalYesBpsValue, minLiquidity, recipient, deadline)creates or reuses the deterministic pair and requires it to be uninitialized (Pair already initialized) before atomically seeding it at a Conditional Yes price strictly between 0 and 10,000 basis points.initializeWithEth(pair, conditionalYesBpsValue, minLiquidity, recipient, deadline)initializes an already created canonical pair.addLiquidityWithEth(pair, maxYesUsed, maxNoUsed, minLiquidity, recipient, deadline)creates complete sets, deposits the authoritative proportional maximum, and returns Invalid and unused shares. It reverts withLiquidity price slippagewhenyesUsed > maxYesUsedornoUsed > maxNoUsed.minLiquidityalone does not bound the price: a swap toward even odds before the call raises both the deposit of the side with the smaller reserve and the minted LP.removeLiquidityWithPermit(pair, liquidity, minYesOut, minNoOut, recipient, deadline, v, r, s)calls the pair'spermitfor the caller as owner, the router as spender, exactlyliquidityas value, and the samedeadline; the signature must cover those values. A failing permit is ignored when the router's allowance already coversliquidity; otherwise the call reverts withLP permit or allowance. The router then pulls the LP and returns raw Yes/No directly to the recipient.factory()exposes the immutable factory.onERC1155Received,onERC1155BatchReceived, andsupportsInterfaceexist only for the receive-based operations described below and for the pair callbacks inside an operation.
The payable entrypoints require nonzero msg.value (ETH input is zero). Every entrypoint requires a recipient that is neither zero nor the router (Invalid recipient) and block.timestamp ≤ deadline (Deadline expired); those that take a pair require it to be the factory's canonical pair for its SecurityPool (Unrecognized pair, Noncanonical pair). The three liquidity entrypoints that take ETH return LiquidityResult: pair, completeSetShares, yesUsed, noUsed, yesReturned, noReturned, invalidInsurance, and liquidity.
Every successful share-custody operation, including the receive-based operations below, asserts that the router's Invalid, Yes, and No balances equal their starting values, preserving pre-existing forced shares.
Exits and complete-set redemption by share transfer
The router has no exitPosition or redeemCompleteSet entrypoint. Users exit and redeem by calling the ShareToken's safeBatchTransferFrom with themselves as both caller and owner, the router as recipient, and an encoded request in the data field. No setApprovalForAll is required. The request is the ABI-encoded ReceiveRequest struct with fields in this order: version (must equal CALLBACK_VERSION, currently 1), operation (0 ExitPosition, 1 RedeemCompleteSet), shareToken, securityPool, pair, universeId, questionId, invalidTokenId, yesTokenId, noTokenId, longOutcome, completeSetShares, maxLongSharesIn, minEthOut, payoutRecipient, refundRecipient, and deadline. encodeReceiveRequest produces it. The router accepts only owner-initiated top-level transfers (operator == from); internal pair callbacks are distinguished from top-level user callbacks.
-
Insured exit. The transfer carries exactly two IDs in the order
[invalidTokenId, long token ID]with the values[q, maxLongSharesIn], whereqiscompleteSetShares, andlongOutcomeis Yes (1) or No (2). The router buys exactlyqopposite shares with an exact-output swap, redeemsqcomplete sets at the pool, and returns any unused long shares to the bound refund recipient. It emitsPositionExitedByTransfer. - Complete-set redemption. The transfer carries exactly three IDs in the order
[invalidTokenId, yesTokenId, noTokenId], each with the valuecompleteSetShares;longOutcomemust beNone(3) andmaxLongSharesInzero. The router redeems them at the exact canonical SecurityPool and emitsCompleteSetRedeemedByTransfer.
Both operations require completeSetShares > 0, a payoutRecipient and a refundRecipient that are neither zero nor the router, and canonical pool, share-token, universe, question and outcome IDs. An insured exit requires the factory's canonical pair. Complete-set redemption permits a zero pair; a supplied nonzero pair must still be canonical. A child with no trading pair can therefore redeem directly through this transfer route.
Both operations measure the ETH and native REP produced by redemption and forward exactly those deltas to the payout recipient. ETH must meet minEthOut, and at least one asset must be positive. A REP-only basket therefore accepts a zero ETH minimum. Pre-existing ETH and REP in the router are excluded from the payout; minEthOut protects only the ETH component.
For LP removal through the router, Safes and integrations without signature support may use ordinary exact allowances instead of a permit. The first-party UI calls the pair directly and needs no router allowance.