Skip to content

Instantly share code, notes, and snippets.

@L-Kov
Created January 24, 2025 18:42
Show Gist options
  • Save L-Kov/a80d6d8965ea0813287e26e52c5f27aa to your computer and use it in GitHub Desktop.
Save L-Kov/a80d6d8965ea0813287e26e52c5f27aa to your computer and use it in GitHub Desktop.
/*******************************************************
* EXAMPLE: BATCH TRANSFER OF POLYMARKET OUTCOME TOKENS
* ----------------------------------------------
* 1) Fetch outcome-token positions from the data API
* 2) Encode a batch-transfer transaction
* 3) Submit that transaction via the Polymarket Proxy
*******************************************************/
import { BigNumber, Contract, ethers, Wallet } from "ethers";
import { Interface } from "ethers/lib/utils";
import { TransactionResponse } from "@ethersproject/abstract-provider";
import axios from "axios";
// =============== Replace these with your desired values ==========================
const RPC_URL = "https://your-preferred-rpc-url";
const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY";
const PROXY_ADDRESS = "0xYourPolymarketProxy"; // 'from' address
const DESTINATION_ADDRESS = "0xDestination"; // 'to' address
const DATA_API_URL = "https://data-api.polymarket.com/positions?sizeThreshold=1.0";
const GAS_PRICE = "100000000000"; // Example: 100 gwei
// =================================================================================
// Polymarket proxy factory
const PROXY_WALLET_FACTORY_ADDRESS = "0xaB45c5A4B0c941a2F231C04C3f49182e1A254052";
// Polymarket conditional tokens framework
const CONDITIONAL_TOKENS_FRAMEWORK_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045";
// The ABI of Polymarket’s proxy factory
const proxyFactoryAbi = [
{
"constant": false,
"inputs": [
{
"components": [
{ "name": "typeCode", "type": "uint8" },
{ "name": "to", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "data", "type": "bytes" }
],
"name": "calls",
"type": "tuple[]"
}
],
"name": "proxy",
"outputs": [
{
"name": "returnValues",
"type": "bytes[]"
}
],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
// ...other items truncated for brevity—only `proxy` is needed to do calls
];
// Minimal ABI for ERC1155's safeBatchTransferFrom
const ERC1155_INTERFACE = new Interface([
{
"inputs": [
{ "internalType": "address", "name": "from", "type": "address" },
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "uint256[]", "name": "ids", "type": "uint256[]" },
{ "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"name": "safeBatchTransferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]);
/**
* Encode the data for an ERC1155 batch transfer
*/
function encodeErc1155BatchTransferFrom(
from: string,
to: string,
ids: string[],
amounts: BigNumber[]
): string {
return ERC1155_INTERFACE.encodeFunctionData("safeBatchTransferFrom", [
from,
to,
ids,
amounts,
ethers.constants.HashZero // 'data' param
]);
}
// Simple interface for the API response
interface Position {
size: number;
asset: string; // the outcome token ID
}
/**
* Fetch positions from the Polymarket data API for a given address
*/
async function getPositions(proxyAddress: string): Promise<Position[]> {
console.log(`Fetching positions for ${proxyAddress}...`);
const url = `${DATA_API_URL}&user=${proxyAddress}`;
const { data } = await axios.get(url);
// Just extracting asset ID & size from each response item
const positions: Position[] = data.map((d: any) => ({
asset: d.asset,
size: d.size,
}));
return positions;
}
/**
* Main script
*/
async function main() {
console.log(`Starting...`);
// Setup signer/provider
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const wallet = new Wallet(PRIVATE_KEY).connect(provider);
console.log(`Signer address: ${wallet.address}`);
// Create contract instance for the Polymarket proxy factory
const factory = new Contract(PROXY_WALLET_FACTORY_ADDRESS, proxyFactoryAbi, wallet);
// 1) Fetch all outcome-token positions from the proxy wallet
const positions = await getPositions(PROXY_ADDRESS);
const ids = positions.map((p) => p.asset); // array of token IDs
const amounts = positions.map((p) => {
// Polymarket outcome tokens typically have 6 decimals
// Adjust if your outcome tokens have different decimals
return ethers.utils.parseUnits(`${p.size}`, 6);
});
if (ids.length === 0) {
console.log("No outcome-token positions found. Exiting...");
return;
}
console.log(`Preparing to batch-transfer ${ids.length} tokens...`);
// 2) Encode a *batch* transfer call
const data = encodeErc1155BatchTransferFrom(
PROXY_ADDRESS, // from the proxy
DESTINATION_ADDRESS,
ids,
amounts
);
// 3) Execute the call via the proxy factory
// typeCode = "1" => standard CALL, to = conditional tokens, data = our batch xfer
const proxyTxn = {
to: CONDITIONAL_TOKENS_FRAMEWORK_ADDRESS,
typeCode: "1",
data: data,
value: "0",
};
console.log("Sending transaction via Polymarket proxy factory...");
const tx: TransactionResponse = await factory.proxy([proxyTxn], {
gasPrice: GAS_PRICE
});
console.log(`Transaction broadcast. Hash: ${tx.hash}`);
await tx.wait();
console.log(`Success!`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment