Updated 55 files with latest documentation changes
12 KiB
Documentation Index
Fetch the complete documentation index at: https://docs.polymarket.com/llms.txt Use this file to discover all available pages before exploring further.
Inventory Management
Managing outcome token inventory for market making
Market makers need outcome tokens on both sides to quote a market. The three core inventory operations are splitting pUSD into YES/NO token pairs, merging pairs back into pUSD, and redeeming winning tokens after resolution — all executed gaslessly through the Relayer Client.
For a full breakdown of how the Conditional Token Framework works, see [CTF Overview](/trading/ctf/overview). This page focuses on the MM workflow using the Relayer Client.Splitting pUSD into Tokens
Split converts pUSD into equal amounts of YES and NO tokens — creating the inventory you need to quote both sides of a market.
```typescript TypeScript theme={null} import { ethers } from "ethers"; import { Interface } from "ethers/lib/utils"; import { RelayClient, Transaction } from "@polymarket/builder-relayer-client";const CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"; const pUSD_ADDRESS = "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB";
const ctfInterface = new Interface([ "function splitPosition(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] partition, uint amount)", ]);
// Split $1000 pUSD into YES/NO tokens const amount = ethers.utils.parseUnits("1000", 6); // pUSD has 6 decimals
const splitTx: Transaction = { to: CTF_ADDRESS, data: ctfInterface.encodeFunctionData("splitPosition", [ pUSD_ADDRESS, // collateralToken ethers.constants.HashZero, // parentCollectionId (always zero for Polymarket) conditionId, // conditionId from market [1, 2], // partition: [YES, NO] amount, ]), value: "0", };
const response = await client.execute([splitTx], "Split pUSD into tokens"); const result = await response.wait(); console.log("Split completed:", result?.transactionHash);
```python Python theme={null}
from web3 import Web3
CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"
pUSD_ADDRESS = "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"
ctf_abi = [{
"name": "splitPosition",
"type": "function",
"inputs": [
{"name": "collateralToken", "type": "address"},
{"name": "parentCollectionId", "type": "bytes32"},
{"name": "conditionId", "type": "bytes32"},
{"name": "partition", "type": "uint256[]"},
{"name": "amount", "type": "uint256"}
],
"outputs": []
}]
# Split $1000 pUSD into YES/NO tokens
amount = 1000 * 10**6 # pUSD has 6 decimals
split_tx = {
"to": CTF_ADDRESS,
"data": Web3().eth.contract(
address=CTF_ADDRESS, abi=ctf_abi
).encode_abi(
abi_element_identifier="splitPosition",
args=[
pUSD_ADDRESS,
bytes(32), # parentCollectionId (always zero)
condition_id, # conditionId from market
[1, 2], # partition: [YES, NO]
amount,
]
),
"value": "0"
}
response = client.execute([split_tx], "Split pUSD into tokens")
response.wait()
use polymarket_client_sdk_v2::ctf::Client as CtfClient;
use polymarket_client_sdk_v2::ctf::types::SplitPositionRequest;
use polymarket_client_sdk_v2::types::{U256, address};
let ctf_client = CtfClient::new(provider, 137)?;
// Split $1000 pUSD into YES/NO tokens
let request = SplitPositionRequest::builder()
.collateral_token(address!("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"))
.condition_id(condition_id)
.partition(vec![U256::from(1), U256::from(2)])
.amount(U256::from(1000_000_000u64)) // 1000 pUSD (6 decimals)
.build();
let result = ctf_client.split_position(&request).await?;
println!("Split tx: {:?}", result.transaction_hash);
After splitting 1000 pUSD, you receive 1000 YES tokens and 1000 NO tokens. Your pUSD balance decreases by 1000.
Merging Tokens to pUSD
Merge converts equal amounts of YES and NO tokens back into pUSD — useful for reducing exposure, exiting a market, or freeing up capital.
```typescript TypeScript theme={null} const ctfInterface = new Interface([ "function mergePositions(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] partition, uint amount)", ]);// Merge 500 YES + 500 NO back to 500 pUSD const amount = ethers.utils.parseUnits("500", 6);
const mergeTx: Transaction = { to: CTF_ADDRESS, data: ctfInterface.encodeFunctionData("mergePositions", [ pUSD_ADDRESS, ethers.constants.HashZero, conditionId, [1, 2], amount, ]), value: "0", };
const response = await client.execute([mergeTx], "Merge tokens to pUSD"); await response.wait();
```python Python theme={null}
merge_abi = [{
"name": "mergePositions",
"type": "function",
"inputs": [
{"name": "collateralToken", "type": "address"},
{"name": "parentCollectionId", "type": "bytes32"},
{"name": "conditionId", "type": "bytes32"},
{"name": "partition", "type": "uint256[]"},
{"name": "amount", "type": "uint256"}
],
"outputs": []
}]
# Merge 500 YES + 500 NO back to 500 pUSD
amount = 500 * 10**6
merge_tx = {
"to": CTF_ADDRESS,
"data": Web3().eth.contract(
address=CTF_ADDRESS, abi=merge_abi
).encode_abi(
abi_element_identifier="mergePositions",
args=[pUSD_ADDRESS, bytes(32), condition_id, [1, 2], amount]
),
"value": "0"
}
response = client.execute([merge_tx], "Merge tokens to pUSD")
response.wait()
use polymarket_client_sdk_v2::ctf::types::MergePositionsRequest;
// Merge 500 YES + 500 NO back to 500 pUSD
let request = MergePositionsRequest::builder()
.collateral_token(address!("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"))
.condition_id(condition_id)
.partition(vec![U256::from(1), U256::from(2)])
.amount(U256::from(500_000_000u64)) // 500 pUSD (6 decimals)
.build();
let result = ctf_client.merge_positions(&request).await?;
After merging 500 of each, your YES and NO balances decrease by 500 and your pUSD balance increases by 500.
Redeeming After Resolution
Once a market resolves, redeem winning tokens for pUSD. Each winning token is worth $1 — losing tokens redeem for $0.
Check Resolution Status
```typescript TypeScript theme={null} const market = await clobClient.getMarket(conditionId); if (market.closed) { const winningToken = market.tokens.find((t) => t.winner); console.log("Winning outcome:", winningToken?.outcome); } ```market = clob_client.get_market(condition_id)
if market.get("closed"):
winning = next(t for t in market["tokens"] if t.get("winner"))
print("Winning outcome:", winning["outcome"])
let market = clob_client.market(condition_id).await?;
if market.closed {
if let Some(winner) = market.tokens.iter().find(|t| t.winner) {
println!("Winning outcome: {}", winner.outcome);
}
}
Redeem Winning Tokens
```typescript TypeScript theme={null} const ctfInterface = new Interface([ "function redeemPositions(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] indexSets)", ]);const redeemTx: Transaction = { to: CTF_ADDRESS, data: ctfInterface.encodeFunctionData("redeemPositions", [ pUSD_ADDRESS, ethers.constants.HashZero, conditionId, [1, 2], // Redeem both YES and NO (only winners pay out) ]), value: "0", };
const response = await client.execute([redeemTx], "Redeem winning tokens"); await response.wait();
```python Python theme={null}
redeem_abi = [{
"name": "redeemPositions",
"type": "function",
"inputs": [
{"name": "collateralToken", "type": "address"},
{"name": "parentCollectionId", "type": "bytes32"},
{"name": "conditionId", "type": "bytes32"},
{"name": "indexSets", "type": "uint256[]"}
],
"outputs": []
}]
redeem_tx = {
"to": CTF_ADDRESS,
"data": Web3().eth.contract(
address=CTF_ADDRESS, abi=redeem_abi
).encode_abi(
abi_element_identifier="redeemPositions",
args=[pUSD_ADDRESS, bytes(32), condition_id, [1, 2]]
),
"value": "0"
}
response = client.execute([redeem_tx], "Redeem winning tokens")
response.wait()
use polymarket_client_sdk_v2::ctf::types::RedeemPositionsRequest;
let request = RedeemPositionsRequest::builder()
.collateral_token(address!("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"))
.condition_id(condition_id)
.index_sets(vec![U256::from(1), U256::from(2)]) // Redeem both (only winners pay)
.build();
let result = ctf_client.redeem_positions(&request).await?;
Negative Risk Markets
Multi-outcome markets use the Neg Risk CTF Exchange and Neg Risk Adapter. Split and merge work the same way, but use different contract addresses:
const NEG_RISK_CTF_EXCHANGE = "0xe2222d279d744050d28e00520010520000310F59";
const NEG_RISK_ADAPTER = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296";
See Negative Risk Markets for details on how multi-outcome token mechanics differ.
Inventory Strategies
Before Quoting
- Check market metadata via the Gamma API
- Split sufficient pUSD to cover your expected quoting size
- Set token approvals if not already done (see Getting Started)
During Trading
- Skew quotes when inventory becomes imbalanced on one side
- Merge excess tokens to free up capital for other markets
- Split more when inventory on either side runs low
After Resolution
- Cancel all open orders in the market
- Wait for resolution to complete
- Redeem winning tokens
- Merge any remaining YES/NO pairs
Batch Operations
Execute multiple inventory operations in a single relayer call for efficiency:
const transactions: Transaction[] = [
// Split on Market A
{
to: CTF_ADDRESS,
data: ctfInterface.encodeFunctionData("splitPosition", [
pUSD_ADDRESS,
ethers.constants.HashZero,
conditionIdA,
[1, 2],
ethers.utils.parseUnits("1000", 6),
]),
value: "0",
},
// Split on Market B
{
to: CTF_ADDRESS,
data: ctfInterface.encodeFunctionData("splitPosition", [
pUSD_ADDRESS,
ethers.constants.HashZero,
conditionIdB,
[1, 2],
ethers.utils.parseUnits("1000", 6),
]),
value: "0",
},
];
const response = await client.execute(transactions, "Batch inventory setup");
await response.wait();