2026-04-14 21:53:47 +02:00
> ## 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.
2026-02-19 14:31:02 +01:00
# Inventory Management
> Managing outcome token inventory for market making
2026-04-20 01:19:37 +02:00
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.
2026-02-19 14:31:02 +01:00
<Info>
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.
</Info>
** *
2026-04-20 01:19:37 +02:00
## Splitting pUSD into Tokens
2026-02-19 14:31:02 +01:00
2026-04-20 01:19:37 +02:00
Split converts pUSD into equal amounts of YES and NO tokens — creating the inventory you need to quote both sides of a market.
2026-02-19 14:31:02 +01:00
<CodeGroup>
```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";
2026-04-20 01:19:37 +02:00
const pUSD_ADDRESS = "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB";
2026-02-19 14:31:02 +01:00
const ctfInterface = new Interface([
"function splitPosition(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] partition, uint amount)",
]);
2026-04-20 01:19:37 +02:00
// Split $1000 pUSD into YES/NO tokens
const amount = ethers.utils.parseUnits("1000", 6); // pUSD has 6 decimals
2026-02-19 14:31:02 +01:00
const splitTx: Transaction = {
to: CTF_ADDRESS,
data: ctfInterface.encodeFunctionData("splitPosition", [
2026-04-20 01:19:37 +02:00
pUSD_ADDRESS, // collateralToken
2026-02-19 14:31:02 +01:00
ethers.constants.HashZero, // parentCollectionId (always zero for Polymarket)
conditionId, // conditionId from market
[1, 2], // partition: [YES, NO]
amount,
]),
value: "0",
};
2026-04-20 01:19:37 +02:00
const response = await client.execute([splitTx], "Split pUSD into tokens");
2026-02-19 14:31:02 +01:00
const result = await response.wait();
console.log("Split completed:", result?.transactionHash);
` ``
` ``python Python theme={null}
from web3 import Web3
CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"
2026-04-20 01:19:37 +02:00
pUSD_ADDRESS = "0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"
2026-02-19 14:31:02 +01:00
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": []
}]
2026-04-20 01:19:37 +02:00
# Split $1000 pUSD into YES/NO tokens
amount = 1000 * 10**6 # pUSD has 6 decimals
2026-02-19 14:31:02 +01:00
split_tx = {
"to": CTF_ADDRESS,
"data": Web3().eth.contract(
address=CTF_ADDRESS, abi=ctf_abi
).encode_abi(
abi_element_identifier="splitPosition",
args=[
2026-04-20 01:19:37 +02:00
pUSD_ADDRESS,
2026-02-19 14:31:02 +01:00
bytes(32), # parentCollectionId (always zero)
condition_id, # conditionId from market
[1, 2], # partition: [YES, NO]
amount,
]
),
"value": "0"
}
2026-04-20 01:19:37 +02:00
response = client.execute([split_tx], "Split pUSD into tokens")
2026-02-19 14:31:02 +01:00
response.wait()
` ``
2026-03-30 12:53:20 +02:00
` ``rust Rust theme={null}
2026-05-03 14:55:37 +02:00
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};
2026-03-30 12:53:20 +02:00
let ctf_client = CtfClient::new(provider, 137)?;
2026-04-20 01:19:37 +02:00
// Split $1000 pUSD into YES/NO tokens
2026-03-30 12:53:20 +02:00
let request = SplitPositionRequest::builder()
2026-04-20 01:19:37 +02:00
.collateral_token(address!("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"))
2026-03-30 12:53:20 +02:00
.condition_id(condition_id)
.partition(vec![U256::from(1), U256::from(2)])
2026-04-20 01:19:37 +02:00
.amount(U256::from(1000_000_000u64)) // 1000 pUSD (6 decimals)
2026-03-30 12:53:20 +02:00
.build();
let result = ctf_client.split_position(&request).await?;
println!("Split tx: {:?}", result.transaction_hash);
` ``
2026-02-19 14:31:02 +01:00
</CodeGroup>
2026-04-20 01:19:37 +02:00
After splitting 1000 pUSD, you receive 1000 YES tokens and 1000 NO tokens. Your pUSD balance decreases by 1000.
2026-02-19 14:31:02 +01:00
***
2026-04-20 01:19:37 +02:00
## Merging Tokens to pUSD
2026-02-19 14:31:02 +01:00
2026-04-20 01:19:37 +02:00
Merge converts equal amounts of YES and NO tokens back into pUSD — useful for reducing exposure, exiting a market, or freeing up capital.
2026-02-19 14:31:02 +01:00
<CodeGroup>
` ``typescript TypeScript theme={null}
const ctfInterface = new Interface([
"function mergePositions(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] partition, uint amount)",
]);
2026-04-20 01:19:37 +02:00
// Merge 500 YES + 500 NO back to 500 pUSD
2026-02-19 14:31:02 +01:00
const amount = ethers.utils.parseUnits("500", 6);
const mergeTx: Transaction = {
to: CTF_ADDRESS,
data: ctfInterface.encodeFunctionData("mergePositions", [
2026-04-20 01:19:37 +02:00
pUSD_ADDRESS,
2026-02-19 14:31:02 +01:00
ethers.constants.HashZero,
conditionId,
[1, 2],
amount,
]),
value: "0",
};
2026-04-20 01:19:37 +02:00
const response = await client.execute([mergeTx], "Merge tokens to pUSD");
2026-02-19 14:31:02 +01:00
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": []
}]
2026-04-20 01:19:37 +02:00
# Merge 500 YES + 500 NO back to 500 pUSD
2026-02-19 14:31:02 +01:00
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",
2026-04-20 01:19:37 +02:00
args=[pUSD_ADDRESS, bytes(32), condition_id, [1, 2], amount]
2026-02-19 14:31:02 +01:00
),
"value": "0"
}
2026-04-20 01:19:37 +02:00
response = client.execute([merge_tx], "Merge tokens to pUSD")
2026-02-19 14:31:02 +01:00
response.wait()
` ``
2026-03-30 12:53:20 +02:00
` ``rust Rust theme={null}
2026-05-03 14:55:37 +02:00
use polymarket_client_sdk_v2::ctf::types::MergePositionsRequest;
2026-03-30 12:53:20 +02:00
2026-04-20 01:19:37 +02:00
// Merge 500 YES + 500 NO back to 500 pUSD
2026-03-30 12:53:20 +02:00
let request = MergePositionsRequest::builder()
2026-04-20 01:19:37 +02:00
.collateral_token(address!("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"))
2026-03-30 12:53:20 +02:00
.condition_id(condition_id)
.partition(vec![U256::from(1), U256::from(2)])
2026-04-20 01:19:37 +02:00
.amount(U256::from(500_000_000u64)) // 500 pUSD (6 decimals)
2026-03-30 12:53:20 +02:00
.build();
let result = ctf_client.merge_positions(&request).await?;
` ``
2026-02-19 14:31:02 +01:00
</CodeGroup>
2026-04-20 01:19:37 +02:00
After merging 500 of each, your YES and NO balances decrease by 500 and your pUSD balance increases by 500.
2026-02-19 14:31:02 +01:00
***
## Redeeming After Resolution
2026-04-20 01:19:37 +02:00
Once a market resolves, redeem winning tokens for pUSD. Each winning token is worth $1 — losing tokens redeem for $0.
2026-02-19 14:31:02 +01:00
### Check Resolution Status
<CodeGroup>
` ``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);
}
` ``
` ``python Python theme={null}
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"])
` ``
2026-03-30 12:53:20 +02:00
` ``rust Rust theme={null}
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);
}
}
` ``
2026-02-19 14:31:02 +01:00
</CodeGroup>
### Redeem Winning Tokens
<CodeGroup>
` ``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", [
2026-04-20 01:19:37 +02:00
pUSD_ADDRESS,
2026-02-19 14:31:02 +01:00
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",
2026-04-20 01:19:37 +02:00
args=[pUSD_ADDRESS, bytes(32), condition_id, [1, 2]]
2026-02-19 14:31:02 +01:00
),
"value": "0"
}
response = client.execute([redeem_tx], "Redeem winning tokens")
response.wait()
` ``
2026-03-30 12:53:20 +02:00
` ``rust Rust theme={null}
2026-05-03 14:55:37 +02:00
use polymarket_client_sdk_v2::ctf::types::RedeemPositionsRequest;
2026-03-30 12:53:20 +02:00
let request = RedeemPositionsRequest::builder()
2026-04-20 01:19:37 +02:00
.collateral_token(address!("0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB"))
2026-03-30 12:53:20 +02:00
.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?;
` ``
2026-02-19 14:31:02 +01:00
</CodeGroup>
***
## Negative Risk Markets
2026-04-20 01:19:37 +02:00
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:
2026-02-19 14:31:02 +01:00
2026-04-14 21:53:47 +02:00
` ``typescript theme={null}
2026-04-20 01:19:37 +02:00
const NEG_RISK_CTF_EXCHANGE = "0xe2222d279d744050d28e00520010520000310F59";
2026-02-19 14:31:02 +01:00
const NEG_RISK_ADAPTER = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296";
` ``
See [Negative Risk Markets](/advanced/neg-risk) for details on how multi-outcome token mechanics differ.
***
## Inventory Strategies
### Before Quoting
1. Check market metadata via the [Gamma API](/market-data/fetching-markets)
2026-04-20 01:19:37 +02:00
2. Split sufficient pUSD to cover your expected quoting size
2026-02-19 14:31:02 +01:00
3. Set token approvals if not already done (see [Getting Started](/market-makers/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
1. Cancel all open orders in the market
2. Wait for resolution to complete
3. Redeem winning tokens
4. Merge any remaining YES/NO pairs
***
## Batch Operations
Execute multiple inventory operations in a single relayer call for efficiency:
2026-04-14 21:53:47 +02:00
` ``typescript theme={null}
2026-02-19 14:31:02 +01:00
const transactions: Transaction[] = [
// Split on Market A
{
to: CTF_ADDRESS,
data: ctfInterface.encodeFunctionData("splitPosition", [
2026-04-20 01:19:37 +02:00
pUSD_ADDRESS,
2026-02-19 14:31:02 +01:00
ethers.constants.HashZero,
conditionIdA,
[1, 2],
ethers.utils.parseUnits("1000", 6),
]),
value: "0",
},
// Split on Market B
{
to: CTF_ADDRESS,
data: ctfInterface.encodeFunctionData("splitPosition", [
2026-04-20 01:19:37 +02:00
pUSD_ADDRESS,
2026-02-19 14:31:02 +01:00
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();
` ``
** *
## Next Steps
<CardGroup cols={2}>
<Card title="CTF Overview" icon="coins" href="/trading/ctf/overview">
How the Conditional Token Framework works under the hood
</Card>
<Card title="Split Tokens" icon="scissors" href="/trading/ctf/split">
Detailed split function parameters and prerequisites
</Card>
<Card title="Merge Tokens" icon="merge" href="/trading/ctf/merge">
Detailed merge function parameters
</Card>
<Card title="Gasless Transactions" icon="gas-pump" href="/trading/gasless">
Relayer Client setup and configuration
</Card>
</CardGroup>