TypeScript client library for interacting with Polymarket relayer infrastructure
pnpm install @polymarket/builder-relayer-clientimport { createWalletClient, Hex, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { polygon } from "viem/chains";
import { RelayClient, RelayerTxType } from "@polymarket/builder-relayer-client";
const relayerUrl = process.env.POLYMARKET_RELAYER_URL;
const chainId = parseInt(process.env.CHAIN_ID);
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const wallet = createWalletClient({
account,
chain: polygon,
transport: http(process.env.RPC_URL)
});
// Initialize the client with SAFE transaction type (default)
const client = new RelayClient(relayerUrl, chainId, wallet);
// Or initialize with PROXY transaction type
const proxyClient = new RelayClient(relayerUrl, chainId, wallet, undefined, RelayerTxType.PROXY);The client supports two transaction types via the RelayerTxType enum:
RelayerTxType.SAFE(default): Executes transactions through a Gnosis SafeRelayerTxType.PROXY: Executes transactions for a Polymarket Proxy wallet
The transaction type is specified as the last parameter when creating a RelayClient instance. All examples use the Transaction type - the client automatically converts transactions to the appropriate format (SafeTransaction or ProxyTransaction) based on the RelayerTxType you've configured.
The client also supports Deposit Wallets, which use separate dedicated methods rather than the execute() flow. See Deposit Wallet below.
import { BuilderApiKeyCreds, BuilderConfig } from "@polymarket/builder-signing-sdk";
import { RelayerTxType } from "@polymarket/builder-relayer-client";
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 with SAFE transaction type (default)
const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
// Or initialize with PROXY transaction type
const proxyClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig, RelayerTxType.PROXY);import { BuilderConfig } from "@polymarket/builder-signing-sdk";
import { RelayerTxType } from "@polymarket/builder-relayer-client";
const builderConfig = new BuilderConfig(
{
remoteBuilderConfig: {
url: "http://localhost:3000/sign",
token: `${process.env.MY_AUTH_TOKEN}`
}
},
);
// Initialize with SAFE transaction type (default)
const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
// Or initialize with PROXY transaction type
const proxyClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig, RelayerTxType.PROXY);import { encodeFunctionData, prepareEncodeFunctionData, maxUint256 } from "viem";
import { Transaction, RelayerTxType } from "@polymarket/builder-relayer-client";
const erc20Abi = [
{
"constant": false,
"inputs": [
{"name": "_spender", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "approve",
"outputs": [{"name": "", "type": "bool"}],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
];
const erc20 = prepareEncodeFunctionData({
abi: erc20Abi,
functionName: "approve",
});
function createApprovalTransaction(
tokenAddress: string,
spenderAddress: string
): Transaction {
const calldata = encodeFunctionData({
...erc20,
args: [spenderAddress, maxUint256]
});
return {
to: tokenAddress,
data: calldata,
value: "0"
};
}
// Initialize client with SAFE transaction type (default)
const safeClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
// Or initialize with PROXY transaction type
const proxyClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig, RelayerTxType.PROXY);
// Execute the approval - works with both SAFE and PROXY
const approvalTx = createApprovalTransaction(
"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC
"0x4d97dcd97ec945f40cf65f87097ace5ea0476045" // CTF
);
// Using SAFE client
const safeResponse = await safeClient.execute([approvalTx], "usdc approval on the CTF");
const safeResult = await safeResponse.wait();
console.log("Safe approval completed:", safeResult.transactionHash);
// Using PROXY client
const proxyResponse = await proxyClient.execute([approvalTx], "usdc approval on the CTF");
const proxyResult = await proxyResponse.wait();
console.log("Proxy approval completed:", proxyResult.transactionHash);Note: Safe deployment is only available for
RelayerTxType.SAFE. Proxy wallets are deployed automatically on its first transaction.
// Initialize client with SAFE transaction type (default)
const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
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");
}import { encodeFunctionData, prepareEncodeFunctionData, zeroHash } from "viem";
import { Transaction, RelayerTxType } from "@polymarket/builder-relayer-client";
const ctfRedeemAbi = [
{
"constant": false,
"inputs": [
{"name": "collateralToken", "type": "address"},
{"name": "parentCollectionId", "type": "bytes32"},
{"name": "conditionId", "type": "bytes32"},
{"name": "indexSets", "type": "uint256[]"}
],
"name": "redeemPositions",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
];
const ctf = prepareEncodeFunctionData({
abi: ctfRedeemAbi,
functionName: "redeemPositions",
});
function createCtfRedeemTransaction(
ctfAddress: string,
collateralToken: string,
conditionId: string
): Transaction {
const calldata = encodeFunctionData({
...ctf,
args: [collateralToken, zeroHash, conditionId, [1, 2]]
});
return {
to: ctfAddress,
data: calldata,
value: "0"
};
}
// Initialize client with SAFE transaction type (default)
const safeClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
// Or initialize with PROXY transaction type
const proxyClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig, RelayerTxType.PROXY);
// Execute the redeem - works with both SAFE and PROXY
const ctfAddress = "0x4d97dcd97ec945f40cf65f87097ace5ea0476045";
const usdcAddress = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
const conditionId = "0x..."; // Your condition ID
const redeemTx = createCtfRedeemTransaction(ctfAddress, usdcAddress, conditionId);
// Using SAFE client
const safeResponse = await safeClient.execute([redeemTx], "redeem positions");
const safeResult = await safeResponse.wait();
console.log("Safe redeem completed:", safeResult.transactionHash);
// Using PROXY client
const proxyResponse = await proxyClient.execute([redeemTx], "redeem positions");
const proxyResult = await proxyResponse.wait();
console.log("Proxy redeem completed:", proxyResult.transactionHash);import { encodeFunctionData, prepareEncodeFunctionData } from "viem";
import { Transaction, RelayerTxType } from "@polymarket/builder-relayer-client";
const nrAdapterRedeemAbi = [
{
"inputs": [
{"internalType": "bytes32", "name": "_conditionId", "type": "bytes32"},
{"internalType": "uint256[]", "name": "_amounts", "type": "uint256[]"}
],
"name": "redeemPositions",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];
const nrAdapter = prepareEncodeFunctionData({
abi: nrAdapterRedeemAbi,
functionName: "redeemPositions",
});
function createNrAdapterRedeemTransaction(
adapterAddress: string,
conditionId: string,
redeemAmounts: bigint[] // [yesAmount, noAmount]
): Transaction {
const calldata = encodeFunctionData({
...nrAdapter,
args: [conditionId, redeemAmounts]
});
return {
to: adapterAddress,
data: calldata,
value: "0"
};
}
// Initialize client with SAFE transaction type (default)
const safeClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
// Or initialize with PROXY transaction type
const proxyClient = new RelayClient(relayerUrl, chainId, wallet, builderConfig, RelayerTxType.PROXY);
// Execute the redeem - works with both SAFE and PROXY
const negRiskAdapter = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296";
const conditionId = "0x..."; // Your condition ID
const redeemAmounts = [BigInt(111000000), BigInt(0)]; // [yes tokens, no tokens]
const redeemTx = createNrAdapterRedeemTransaction(negRiskAdapter, conditionId, redeemAmounts);
// Using SAFE client
const safeResponse = await safeClient.execute([redeemTx], "redeem positions");
const safeResult = await safeResponse.wait();
console.log("Safe redeem completed:", safeResult.transactionHash);
// Using PROXY client
const proxyResponse = await proxyClient.execute([redeemTx], "redeem positions");
const proxyResult = await proxyResponse.wait();
console.log("Proxy redeem completed:", proxyResult.transactionHash);Deposit Wallets are UUPS-upgradeable smart contract wallets that support EIP-712 signed batch execution. Unlike Safe and Proxy wallets which use the execute() method, Deposit Wallets have dedicated methods.
You can predict the deposit wallet address before deployment using CREATE2:
const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
const walletAddress = await client.deriveDepositWalletAddress();
console.log("Expected deposit wallet address:", walletAddress);Or use the standalone function directly:
import { deriveDepositWallet } from "@polymarket/builder-relayer-client";
const walletAddress = deriveDepositWallet(ownerAddress, factoryAddress, implementationAddress);const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
const response = await client.deployDepositWallet();
const result = await response.wait();
if (result) {
console.log("Deposit wallet deployed successfully!");
console.log("Transaction Hash:", result.transactionHash);
} else {
console.log("Deposit wallet deployment failed");
}Deposit wallet transactions use DepositWalletCall instead of Transaction. Each call has a target (instead of to), value, and data field. Batches require a walletAddress and a deadline (unix timestamp for signature expiry).
import { encodeFunctionData, prepareEncodeFunctionData, maxUint256 } from "viem";
import { DepositWalletCall } from "@polymarket/builder-relayer-client";
const erc20Abi = [
{
"constant": false,
"inputs": [
{"name": "_spender", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "approve",
"outputs": [{"name": "", "type": "bool"}],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
];
const erc20 = prepareEncodeFunctionData({
abi: erc20Abi,
functionName: "approve",
});
function createApproveCall(
tokenAddress: string,
spenderAddress: string
): DepositWalletCall {
const calldata = encodeFunctionData({
...erc20,
args: [spenderAddress, maxUint256]
});
return {
target: tokenAddress,
value: "0",
data: calldata,
};
}
const client = new RelayClient(relayerUrl, chainId, wallet, builderConfig);
const walletAddress = await client.deriveDepositWalletAddress();
const approveCall = createApproveCall(
"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC
"0x4d97dcd97ec945f40cf65f87097ace5ea0476045" // CTF
);
// Deadline: 4 minutes from now
const deadline = Math.floor(Date.now() / 1000 + 240).toString();
const response = await client.executeDepositWalletBatch([approveCall], walletAddress, deadline);
const result = await response.wait();
console.log("Deposit wallet batch executed:", result.transactionHash);const walletAddress = await client.deriveDepositWalletAddress();
const isDeployed = await client.getDeployed(walletAddress, "WALLET");
console.log("Deposit wallet deployed:", isDeployed);