Polymarket Agent with WaaP CLI
What are we cooking?
An AI-friendly Node.js script that interacts with Polymarket using the WaaP CLI.
This recipe demonstrates how an agent can interact with Polymarket’s orderbook and smart contracts using WaaP wallet programmatically, without ever needing a raw private key in its .env file.
The script will:
- Fetch live prediction markets from the Polymarket Gamma API.
- Enable the agent to choose a side (YES/NO) on a market.
- Sign a Polymarket EIP-712 order using
waap-cli sign-typed-data. - Submit the signed order to the Polymarket CLOB (Central Limit Order Book).
Key Components
- WaaP CLI - For securely signing EIP-712 orders and broadcasting EVM transactions behind 2PC-MPC.
- Polymarket Gamma API - For discovering markets.
- Polymarket CLOB API - For placing Maker/Taker bets.
- Polygon Network - The preferred chain for Polymarket.
Project Setup
First, let’s create a new Javascript/Typescript project and ensure the WaaP CLI is installed.
mkdir waap-polymarket-agent && cd waap-polymarket-agent
npm init -y
npm install -g @human.tech/waap-cli@latestSetup a WaaP wallet for your agent
You can use your email for multiple agent wallets by appending a suffix to the email address with + (i.e: youremail+agent007@example.com or youremail+agent008@example.com)
# Create an account for your agent
waap-cli signup --email youremail+agent007@example.com --password '12345678!'
# Or login to an existing one
waap-cli login --email youremail+agent007@example.com --password '12345678!'
# Get the wallet address, so you can fund it with USDC
waap-cli whoami
# then check USDC balance using waap-cli eth_call on Polygon RPC (replace <YOUR_ADDRESS_WITHOUT_0X> below)
waap-cli request eth_call '[{"to":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","data":"0x70a08231000000000000000000000000<YOUR_ADDRESS_WITHOUT_0X>"},"latest"]' --chain-id 137
# also check POL balance on Polygon (using a public RPC)
waap-cli request eth_getBalance '["0xYourAddress","latest"]' --chain-id 137The Recipe Workflow
1. Fetching Markets (Gamma API)
To bet on Polymarket, we first need to know what markets are available. Polymarket’s Gamma API provides this data.
async function getActiveMarkets() {
console.log("Fetching top Polymarket events...");
// Fetch active events from Polymarket Gamma API
const response = await fetch('https://gamma-api.polymarket.com/events?active=true&closed=false&limit=5');
const data = await response.json();
data.forEach((event, index) => {
console.log(`[${index}] ${event.title}`);
});
return data;
}2. Signing an Order with WaaP CLI
Polymarket’s CLOB uses off-chain EIP-712 signatures for placing limit and market orders. Instead of importing ethers and exposing our private key, our agent script simply shells out to waap-cli sign-typed-data.
Here is a helper function to wrap the WaaP CLI:
import { execSync } from 'child_process';
import { createHmac } from 'crypto';
function signWithWaaP(typedData) {
// Convert the JavaScript object to a JSON string
const dataString = JSON.stringify(typedData).replace(/'/g, "'\\''");
// Use WaaP CLI to sign the EIP-712 payload behind the 2PC-MPC enclave
const signature = execSync(`waap-cli sign-typed-data --data '${dataString}'`, { encoding: 'utf-8' });
return signature.trim(); // Returns 0x...
}3. Submitting the Order (CLOB API Credentials Required)
With our secure signature in hand, we can submit the order to Polymarket’s matching engine, but this step requires Polymarket CLOB API credentials and signed headers.
Set credentials via environment variables (never hardcode in source files):
export POLYMARKET_API_KEY="your_api_key"
export POLYMARKET_API_PASSPHRASE="your_api_passphrase"
export POLYMARKET_API_SECRET="your_api_secret"function generatePmApiSign({ apiSecret, timestamp, method, path, body }) {
// Polymarket L2 signature: base64url(HMAC_SHA256(base64url_decode(secret), timestamp + method + path + body))
const normalizedSecret = apiSecret
.replace(/-/g, '+')
.replace(/_/g, '/')
.replace(/[^A-Za-z0-9+/=]/g, '');
const secretKey = Buffer.from(normalizedSecret, 'base64');
const message = `${timestamp}${String(method).toUpperCase()}${path}${body ?? ''}`;
const digestBase64 = createHmac('sha256', secretKey)
.update(message, 'utf8')
.digest('base64');
// Convert to URL-safe base64 (keep "=" padding, matching Polymarket clients)
return digestBase64.replace(/\+/g, '-').replace(/\//g, '_');
}
async function placeBet(marketId, side, amountUSDC) {
const whoamiOutput = execSync('waap-cli whoami', { encoding: 'utf-8' }).trim();
const walletAddress = (whoamiOutput.match(/0x[a-fA-F0-9]{40}/) || [whoamiOutput])[0];
// 1. Construct the Polymarket Order conforming to their EIP-712 schema
const orderData = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" }
],
Order: [
{ name: "salt", type: "uint256" },
{ name: "maker", type: "address" },
{ name: "signer", type: "address" },
{ name: "tokenId", type: "uint256" },
{ name: "makerAmount", type: "uint256" },
{ name: "takerAmount", type: "uint256" },
{ name: "expiration", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "feeRateBps", type: "uint256" },
{ name: "side", type: "uint8" },
{ name: "signatureType", type: "uint8" }
]
},
domain: {
name: "Polymarket CTF Exchange",
version: "1",
chainId: 137,
verifyingContract: "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"
},
primaryType: "Order",
message: {
salt: Math.floor(Math.random() * 1000000000),
maker: walletAddress,
signer: walletAddress,
tokenId: marketId, // The YES or NO token ID
makerAmount: amountUSDC * 1e6, // USDC uses 6 decimals
takerAmount: amountUSDC * 1e6,
expiration: 0,
nonce: 0,
feeRateBps: 0,
side: side === "BUY" ? 0 : 1,
signatureType: 0
}
};
// 2. The Agent requests a signature from WaaP
console.log("Requesting signature from WaaP enclave...");
const signature = signWithWaaP(orderData);
console.log("Signed successfully:", signature);
// 3. Post the order to Polymarket CLOB
// Polymarket requires L2 API credentials and a per-request signed header.
// See: https://docs.polymarket.com/#authentication
const apiKey = process.env.POLYMARKET_API_KEY;
const apiPassphrase = process.env.POLYMARKET_API_PASSPHRASE;
const apiSecret = process.env.POLYMARKET_API_SECRET;
const timestamp = Date.now().toString();
if (!apiKey || !apiPassphrase || !apiSecret) {
throw new Error('Missing Polymarket credentials. Set POLYMARKET_API_KEY, POLYMARKET_API_PASSPHRASE, and POLYMARKET_API_SECRET.');
}
const requestPath = '/order';
const requestMethod = 'POST';
// Important: sign the exact API path only (e.g., "/order"), not the full URL.
const requestBody = JSON.stringify({
order: orderData.message,
owner: walletAddress,
signature: signature
});
// Compute PM-API-SIGN from timestamp + request details using your API secret.
const pmApiSign = generatePmApiSign({
apiSecret,
timestamp,
method: requestMethod,
path: requestPath,
body: requestBody
});
const response = await fetch('https://clob.polymarket.com/order', {
method: requestMethod,
headers: {
'Content-Type': 'application/json',
'PM-API-KEY': apiKey,
'PM-API-PASSPHRASE': apiPassphrase,
'PM-API-TIMESTAMP': timestamp,
'PM-API-SIGN': pmApiSign
},
body: requestBody
});
const responseData = await response.json();
console.log("Order submitted to Polymarket:", responseData);
}4. Running the Agent Programmatically
Putting it all together:
// index.js
async function runAgent() {
const markets = await getActiveMarkets();
// For demo, we select the first active market and bet 5 USDC
const selectedMarket = markets[0];
const yesTokenId = selectedMarket.tokens[0].token_id;
await placeBet(yesTokenId, "BUY", 5);
}
runAgent();Run the script:
node index.jsOn-Chain Transactions (Approvals)
Before your agent can trade on Polymarket, it must approve the Polymarket Exchange contract to spend its USDC on Polygon.
Instead of dealing with EIP-712 orders, this requires a standard smart contract transaction (send-tx). WaaP CLI handles this instantly:
# Approve Polymarket router to spend 100 USDC on Polygon (Chain ID 137)
waap-cli send-tx \
--to 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 \
--value 0 \
--data 0x095ea7b30000000000000000000000004bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E0000000000000000000000000000000000000000000000000000000005F5E100 \
--chain-id 137(0x2791... is Polygon USDC, 0x095ea7b3 is approve, 0x4bFb... is the CTF Exchange, and 0x05F5E100 is 100M micro-USDC).
Next Steps
Now that your agent can fetch markets and securely sign trades:
- Add an LLM provider (OpenAI, Anthropic) to analyze news and pick the winning side.
- Set up a cron task to run this script daily.
- Set daily spend limit (
waap-cli policy set --daily-spend-limit 100) - Enable 2FA for this agent account (
waap-cli 2fa enable) to require agent to get approval for large trades or high-risk actions.
Related
- WaaP CLI and Skills — CLI command reference and agent workflows
- Permission Tokens — Pre-approved spending scopes for automated flows
- Send Transactions — Transaction lifecycle, async flow, and events