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.
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 . 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.
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 );
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.
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 ();
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 — l o s i n g t o k e n s r e d e e m f o r 1 — losing tokens redeem for 1— l os in g t o k e n sre d ee m f or 0.
Check Resolution Status
const market = await clobClient . getMarket ( conditionId );
if ( market . closed ) {
const winningToken = market . tokens . find (( t ) => t . winner );
console . log ( "Winning outcome:" , winningToken ?. outcome );
}
Redeem Winning Tokens
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 ();
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 ();
Next Steps
CTF Overview How the Conditional Token Framework works under the hood
Split Tokens Detailed split function parameters and prerequisites
Merge Tokens Detailed merge function parameters
Gasless Transactions Relayer Client setup and configuration