Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.swaps.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Some Polymarket workflow endpoints accept a signatureByEvmEoa field — an EIP-712 signature produced by the user’s home EOA that authorizes the request. This page describes the typed-data schemas, the signing flow, and a working viem example.

When a signature is required

EndpointSignature requirement
POST /workflows/polymarket/placeOrderAlways required. The signature gates every direct order.
POST /workflows/polymarket/redeemPositionRequired when the request includes dstWalletAddress (proceeds going to a wallet other than the EOA). Omit dstWalletAddress to skip signing — for now.
POST /workflows/polymarket/sellPositionSame as redeemPosition.
When dstWalletAddress is omitted on redeem/sell, proceeds are delivered to the caller’s evmEoa and no signature is currently needed.
Upcoming change. signatureByEvmEoa + expiration will soon be required on every redeemPosition and sellPosition request, regardless of whether dstWalletAddress is set. Today the signature is only enforced when dstWalletAddress is present so existing integrations keep working — but partners should plan to sign every redeem/sell call ahead of the cutover.

Expiration window

expiration is a Unix timestamp in seconds. The backend rejects:
  • expirations in the past,
  • expirations more than 300 seconds (5 minutes) in the future,
  • non-integer or non-finite values.
Compute it close to send time, e.g. Math.floor(Date.now() / 1000) + 60.

Typed-data schemas

All three schemas use the same EIP-712 domain shape — version: '1', chainId: 137 (Polygon). Only the domain name and the primaryType change. Address- and chainId-shaped fields are signed as string so absent optionals can be represented as the empty string "". When an optional is omitted from the request body, sign it as "" (not "0x0000..." or "0").

PlaceOrder — used by placeOrder

const domain = {
  name: 'BoxPolymarketPlaceOrder',
  version: '1',
  chainId: 137,
};

const types = {
  PlaceOrder: [
    { name: 'tokenID', type: 'string' },
    { name: 'side', type: 'string' },
    { name: 'orderType', type: 'string' },
    { name: 'amount', type: 'uint256' },
    { name: 'tickSize', type: 'string' },
    { name: 'negRisk', type: 'bool' },
    { name: 'slippage', type: 'uint256' },
    { name: 'expiration', type: 'uint256' },
  ],
};

RedeemPosition — used by redeemPosition

const domain = {
  name: 'BoxPolymarketRedeemPosition',
  version: '1',
  chainId: 137,
};

const types = {
  RedeemPosition: [
    { name: 'conditionId', type: 'string' },
    { name: 'assetId', type: 'string' },
    { name: 'dstTokenAddress', type: 'string' },
    { name: 'dstTokenChainId', type: 'string' },
    { name: 'dstWalletAddress', type: 'string' },
    { name: 'expiration', type: 'uint256' },
  ],
};

SellPosition — used by sellPosition

const domain = {
  name: 'BoxPolymarketSellPosition',
  version: '1',
  chainId: 137,
};

const types = {
  SellPosition: [
    { name: 'proxyWallet', type: 'string' },
    { name: 'tokenID', type: 'string' },
    { name: 'side', type: 'string' },
    { name: 'orderType', type: 'string' },
    { name: 'tickSize', type: 'string' },
    { name: 'negRisk', type: 'bool' },
    { name: 'amount', type: 'uint256' },
    { name: 'slippage', type: 'uint256' },
    { name: 'dstTokenAddress', type: 'string' },
    { name: 'dstTokenChainId', type: 'string' },
    { name: 'dstWalletAddress', type: 'string' },
    { name: 'expiration', type: 'uint256' },
  ],
};

Building the message

A few field-specific rules apply when assembling the message object:
  • Amount scaling. For PlaceOrder and SellPosition, amount is the USD amount scaled to 6 decimals: BigInt(Math.floor(amount * 1_000_000)). The request body still carries the un-scaled number (e.g. 2.5); the scaling applies only to the signed value.
  • uint256 fields. slippage and expiration must be bigint when signing (BigInt(slippage), BigInt(expiration)).
  • Empty-string optionals. Map omitted optionals to "":
    • dstToken absent → dstTokenAddress: "", dstTokenChainId: "".
    • proxyWallet absent (sell) → proxyWallet: "".
  • Chain ID as string. When dstToken is present, sign dstTokenChainId: String(dstToken.chainId).

Example — sign and submit a placeOrder

import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(privateKey);

const orderRequest = {
  side: 'BUY',
  tokenID: '902348896171994436737091508776752646...',
  orderType: 'FOK',
  amount: 2,
  tickSize: '0.01',
  negRisk: true,
  slippage: 100,
};

const expiration = Math.floor(Date.now() / 1000) + 60; // 60s in the future

const signatureByEvmEoa = await account.signTypedData({
  domain: {
    name: 'BoxPolymarketPlaceOrder',
    version: '1',
    chainId: 137,
  },
  types: {
    PlaceOrder: [
      { name: 'tokenID', type: 'string' },
      { name: 'side', type: 'string' },
      { name: 'orderType', type: 'string' },
      { name: 'amount', type: 'uint256' },
      { name: 'tickSize', type: 'string' },
      { name: 'negRisk', type: 'bool' },
      { name: 'slippage', type: 'uint256' },
      { name: 'expiration', type: 'uint256' },
    ],
  },
  primaryType: 'PlaceOrder',
  message: {
    tokenID: orderRequest.tokenID,
    side: orderRequest.side,
    orderType: orderRequest.orderType,
    amount: BigInt(Math.floor(orderRequest.amount * 1_000_000)),
    tickSize: orderRequest.tickSize,
    negRisk: orderRequest.negRisk,
    slippage: BigInt(orderRequest.slippage),
    expiration: BigInt(expiration),
  },
});

await fetch('https://api-v2.swaps.xyz/api/workflows/polymarket/placeOrder', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': API_KEY,
  },
  body: JSON.stringify({
    evmEoa: account.address,
    orderRequest,
    expiration,
    signatureByEvmEoa,
  }),
});
The same pattern applies to redeemPosition and sellPosition — swap the domain name, primaryType, and types for the matching schema and include the additional dstTokenAddress, dstTokenChainId, and dstWalletAddress fields (using "" when their request-body counterpart is omitted).