Created
January 24, 2025 18:42
-
-
Save L-Kov/a80d6d8965ea0813287e26e52c5f27aa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************* | |
* 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