Overview
The Polymarket Relayer Client allows builders to route onchain transactions through Polymarket’s Polygon relayer infrastructure. This provides several key benefits:
- Gasless Transactions: Polymarket pays for all gas fees on your behalf
- Wallet Deployment: Deploy Safe Wallets explicitly, or use auto-deploying Proxy Wallets for Magic Link users
- Token Approvals: Set allowances for trading tokens
- CTF Operations: Execute Conditional Token Framework (CTF) operations including:
- Splitting positions
- Merging positions
- Redeeming positions
- Converting positions
Wallet Types
The Relayer Client supports two types of wallets:
Safe Wallets (RelayerTxType.SAFE)
- Gnosis Safe-based proxy wallets
- Requires explicit deployment using
client.deploy() before first use
- Default wallet type when not specified
Proxy Wallets (RelayerTxType.PROXY)
- Custom Polymarket proxy wallets
- Auto-deploys on first transaction (no
deploy() call needed)
- Used for Magic Link users who authenticate via email/Google on Polymarket.com
Comparison
| Feature | Safe Wallets | Proxy Wallets |
|---|
| Deployment | Explicit deploy() required | Auto-deployed on first transaction |
| Gas Fees | Paid by Polymarket | Paid by Polymarket |
| Transaction Execution | ✅ | ✅ |
| ERC20 Approvals | ✅ | ✅ |
| CTF Operations | ✅ | ✅ |
All code examples in this guide use the generic Transaction type, which works for both Safe and Proxy wallets. Simply change the RelayerTxType when initializing your client.
Installation
npm install @polymarket/builder-relayer-client
# or
pnpm install @polymarket/builder-relayer-client
Quick Start
Relayer URL
The Polymarket relayer is publicly available at:
https://relayer-v2.polymarket.com/
Basic Setup
The relayer client is available for both TypeScript and Python:
import { ethers } from "ethers";
import { RelayClient, RelayerTxType } from "@polymarket/builder-relayer-client";
import { BuilderApiKeyCreds, BuilderConfig } from "@polymarket/builder-signing-sdk";
const relayerUrl = process.env.POLYMARKET_RELAYER_URL;
const chainId = 137; // Polygon mainnet
// Create wallet
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Configure builder credentials
const builderCreds: BuilderApiKeyCreds = {
key: process.env.BUILDER_API_KEY!,
secret: process.env.BUILDER_SECRET!,
passphrase: process.env.BUILDER_PASS_PHRASE!
};
const builderConfig = new BuilderConfig({
localBuilderCreds: builderCreds
});
// Initialize client
const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
// or explicitly:
const safeClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig, RelayerTxType.SAFE);
// For Proxy Wallets (Magic Link users)
const proxyClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig, RelayerTxType.PROXY);
Remote Signing
For enhanced security, you can use remote signing to keep your builder credentials on a secure server:
import { BuilderConfig } from "@polymarket/builder-signing-sdk";
const builderConfig = new BuilderConfig({
remoteBuilderConfig: {url: "http://localhost:3000/sign"}
});
const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
Core Features
Deploying Safe Wallets
Deploy a Safe wallet for your user with a single call. Polymarket pays the gas fees:
const response = await client.deploy();
const result = await response.wait();
if (result) {
console.log("Safe deployed successfully!");
console.log("Transaction Hash:", result.transactionHash);
console.log("Safe Address:", result.proxyAddress);
} else {
console.log("Safe deployment failed");
}
Note: The deploy() method only works with Safe wallets. Proxy wallets deploy automatically.
Setting Token Approvals
Set token allowances to enable trading. This example approves USDC spending for the Conditional Token Framework:
import { ethers } from "ethers";
import { Interface } from "ethers/lib/utils";
import { Transaction } from "@polymarket/builder-relayer-client";
// Define ERC20 approval interface
const erc20Interface = new Interface([{
"constant": false,
"inputs": [
{"name": "_spender", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "approve",
"outputs": [{"name": "", "type": "bool"}],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}]);
// Create approval transaction
function createApprovalTransaction(
tokenAddress: string,
spenderAddress: string
): Transaction {
return {
to: tokenAddress,
data: erc20Interface.encodeFunctionData("approve", [
spenderAddress,
ethers.constants.MaxUint256
]),
value: "0"
};
}
// Execute the approval
const usdcAddress = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
const ctfAddress = "0x4d97dcd97ec945f40cf65f87097ace5ea0476045";
const approvalTx = createApprovalTransaction(usdcAddress, ctfAddress);
const response = await client.execute(
[approvalTx],
"Approve USDC for CTF"
);
const result = await response.wait();
console.log("Approval completed:", result?.transactionHash);
Monitoring Transaction Status
The relayer client provides built-in transaction monitoring:
// Automatic waiting (recommended)
const response = await client.execute(transactions);
const result = await response.wait();
if (result) {
console.log("Transaction confirmed:", result.transactionHash);
console.log("Transaction state:", result.state);
} else {
console.log("Transaction failed or timed out");
}
Transaction States
Transactions move through the following states:
STATE_NEW: Transaction received by relayer
STATE_EXECUTED: Transaction executed onchain
STATE_MINED: Transaction included in a block
STATE_CONFIRMED: Transaction confirmed (final state)
STATE_FAILED: Transaction failed (terminal state)
STATE_INVALID: Transaction rejected as invalid (terminal state)
CTF Operations
The relayer client enables you to execute Conditional Token Framework operations for your users:
Split Positions
Split collateral tokens into conditional tokens representing different outcomes:
import { Interface } from "ethers/lib/utils";
import { Transaction } from "@polymarket/builder-relayer-client";
const ctfInterface = new Interface([
"function splitPosition(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] partition, uint amount)"
]);
const splitTx: Transaction = {
to: ctfAddress,
data: ctfInterface.encodeFunctionData("splitPosition", [
collateralToken,
parentCollectionId,
conditionId,
partition,
amount
]),
value: "0"
};
const response = await client.execute([splitTx], "Split position");
const result = await response.wait();
Merge Positions
Merge conditional tokens back into collateral:
const ctfInterface = new Interface([
"function mergePositions(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] partition, uint amount)"
]);
const mergeTx: Transaction = {
to: ctfAddress,
data: ctfInterface.encodeFunctionData("mergePositions", [
collateralToken,
parentCollectionId,
conditionId,
partition,
amount
]),
value: "0"
};
const response = await client.execute([mergeTx], "Merge position");
const result = await response.wait();
Redeem Positions
Redeem winning conditional tokens for collateral after market resolution:
const ctfInterface = new Interface([
"function redeemPositions(address collateralToken, bytes32 parentCollectionId, bytes32 conditionId, uint[] indexSets)"
]);
const redeemTx: Transaction = {
to: ctfAddress,
data: ctfInterface.encodeFunctionData("redeemPositions", [
collateralToken,
parentCollectionId,
conditionId,
indexSets
]),
value: "0"
};
const response = await client.execute([redeemTx], "Redeem position");
const result = await response.wait();
Use descriptive metadata to track transaction purposes:
Metadata is limited to 500 characters or less
await client.execute(
transactions,
"User deposit: 100 USDC for market ABC123"
);
TypeScript Types
The relayer client is fully typed. Key types include:
// Transaction type used in all examples (works for both Safe and Proxy wallets)
interface Transaction {
to: string;
data: string;
value: string;
}
// Wallet type selector
enum RelayerTxType {
SAFE = "SAFE",
PROXY = "PROXY"
}
// Transaction states
enum RelayerTransactionState {
STATE_NEW = "STATE_NEW",
STATE_EXECUTED = "STATE_EXECUTED",
STATE_MINED = "STATE_MINED",
STATE_CONFIRMED = "STATE_CONFIRMED",
STATE_FAILED = "STATE_FAILED",
STATE_INVALID = "STATE_INVALID"
}
// Response from relayer
interface RelayerTransaction {
transactionID: string;
transactionHash: string;
from: string;
to: string;
proxyAddress: string;
data: string;
state: string;
type: string;
metadata: string;
createdAt: Date;
updatedAt: Date;
}
Contract Addresses
Polygon Mainnet
- USDC:
0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
- CTF (Conditional Token Framework):
0x4d97dcd97ec945f40cf65f87097ace5ea0476045
- CTF Exchange:
0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E
- Neg Risk CTF Exchange:
0xC5d563A36AE78145C45a50134d48A1215220f80a
These addresses are commonly used for token approvals and CTF operations.
Resources
Full Integration Examples
Complete Next.js applications demonstrating builder integration with various wallet providers:
TypeScript
Python
Additional Resources
Support
If you encounter issues or have questions about using the relayer client, please contact [email protected].