On-Chain Integration
How to submit on-chain transactions to open and close leveraged positions, and what the backend handles automatically after each step.
Covers the on-chain transactions your integration must submit. For quote parameters and field mappings, see the API Reference. For the full flow end-to-end, see the Quickstart.
Runnable examples:
03-open-position-onchain.ts(approve → verify signature →createPositionwith viem) and04-positions-and-close.ts(list positions, request close). Both are type-checked against the SDK in CI.
Position lifecycle
Two user-initiated on-chain transactions. Everything else is handled by the Multiply backend.
User action Backend (automatic)
─────────── ───────────────────
1. Approve pUSD
2. Call createPosition() ──────────► Detect event
Place exchange order
Finalize on-chain
──► position.opened WebSocket event
3. Call requestClose() ────────────► Detect event
Sell tokens on exchange
Distribute proceeds on-chain
──► position.closed WebSocket event
Other exits (no user action needed):
Market resolves ─────────────────► Settle all positions
Price breaches threshold ────────► Liquidate positionSee WebSocket Events for event payloads.
Wallet types
The vault doesn't care whether msg.sender is an EOA or smart-contract wallet:
Direct EOA — covered below.
Polymarket Safe, Polymarket Proxy, Polymarket Deposit Wallet — smart-contract wallets provisioned by Polymarket. Deposit wallets are the default for new API users and use a different opening flow. See Smart Wallet Integration.
Critical: The wallet_address you pass to POST /prediction-markets/quotes must be the address that will be msg.sender on-chain. For smart-contract wallets that's the contract address, not the owner EOA. The wrong address fails the on-chain signature check.
Opening a position (Direct EOA)
1. Approve pUSD
The vault pulls pUSD from the caller via transferFrom. Approve the vault to spend the required amount first.
2. Call createPosition
All parameters come directly from the quote response.
3. What happens next (automatic)
Backend detects the
PositionCreatedeventPlaces an exchange order for prediction market tokens
Finalizes on-chain via
finalizeOpenEmits
position.openedover WebSocket
If the exchange order fails, the backend reverts the position, refunds collateral + origination fee + venue fee, and emits position.reverted.
Closing a position
The position owner calls requestClose on the vault. The positionKey is the on_chain_position_key field from GET /positions.
Only the position owner (the wallet that called createPosition) can call requestClose. The example above is the Direct EOA path — for smart-contract wallets, wrap requestClose in the wallet's batch (Smart Wallet Integration).
After the transaction confirms, the backend sells tokens, distributes proceeds, and emits position.closed over WebSocket.
Other exit paths
No user action required for these — the backend handles them automatically.
Settlement: When a market resolves, all positions are settled. Emits
position.settled.Liquidation: When margin health approaches zero, the position is liquidated. Emits
position.liquidated. The liquidation price is on every position object atrisk.current_liquidation_price_usd.
Contract ABIs
Vault
The full canonical vault ABI — every external function, event, and custom error — is published in the official UI repo:
canonical-leveraged-prediction-vault-v1-abi.json
Use it directly so your client can decode revert reasons (e.g. InsufficientCapital, SignatureExpired, UserCapitalExceeded) into named errors instead of raw selectors. The two methods you'll typically call:
The vault address is returned in every quote response as polygon_vault_contract_address, and from GET /prediction-markets/contract-info.
Last updated

