For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 → createPosition with viem) and 04-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

Shrink without closing (owner-initiated):
4. Call requestPartialClose() ─────► Detect event
                                     Sell the slice on the exchange
                                     Repay proportional capital, finalize on-chain
                                     ──► position stays open at reduced size

Other exits (no user action needed):
  Market resolves ─────────────────► Settle all positions
  Price breaches threshold ────────► Liquidate position

See 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)

  1. Backend detects the PositionCreated event

  2. Places an exchange order for prediction market tokens

  3. Finalizes on-chain via finalizeOpen

  4. Emits position.opened over 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.


Partially closing a position

To shrink a position without closing it entirely, the owner calls requestPartialClose(positionKey, closeTokenUnits), naming how many position tokens to sell. The position stays open at a reduced size — see Partial Close for the full flow, status behaviour, and WebSocket events.

After it confirms, the backend withdraws the slice, sells it, repays the proportional capital, and emits the position.partial_close_* events. requestPartialClose is the only call you make — the backend handles initiate, finalize, and abort. (Gated and rolling out — see Partial Close.)


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 at risk.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