@polymarket/builder-relayer-client
v0.0.9
Published
Client for Polymarket relayers
Maintainers
Keywords
Readme
builder-relayer-client
TypeScript client library for interacting with Polymarket relayer infrastructure
Installation
pnpm install @polymarket/builder-relayer-clientQuick Start
Basic Setup
import { 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);Transaction Types
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.
With Local Builder Authentication
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);With Remote Builder Authentication
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);Examples
Execute ERC20 Approval Transaction
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);Deploy Safe Contract
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");
}Redeem Positions
CTF (ConditionalTokensFramework) Redeem
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);NegRisk Adapter Redeem
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 Wallet
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.
Derive Deposit Wallet Address
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);Deploy Deposit Wallet
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");
}Execute Deposit Wallet Batch
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);Check Deposit Wallet Deployment
const walletAddress = await client.deriveDepositWalletAddress();
const isDeployed = await client.getDeployed(walletAddress, "WALLET");
console.log("Deposit wallet deployed:", isDeployed);