@theschein/plugin-polymarket
Version:
ElizaOS plugin for Polymarket prediction markets
458 lines (433 loc) • 16.2 kB
JavaScript
// src/actions/approveUSDC.ts
import {
logger
} from "@elizaos/core";
import { ethers } from "ethers";
// src/utils/actionHelpers.ts
function contentToActionResult(content, success = true) {
return {
text: content.text || "",
success,
data: content,
values: {
success,
...content.values || {}
}
};
}
function createErrorResult(error, data) {
const errorMessage = error instanceof Error ? error.message : error;
return {
text: `Error: ${errorMessage}`,
success: false,
error: error instanceof Error ? error : new Error(errorMessage),
data: data || {},
values: {
success: false,
error: errorMessage
}
};
}
// src/actions/approveUSDC.ts
var USDC_NATIVE_ADDRESS = "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359";
var USDC_BRIDGED_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
var CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045";
var EXCHANGE_ADDRESS = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E";
var NEG_RISK_EXCHANGE_ADDRESS = "0xC5d563A36AE78145C45a50134d48A1215220f80a";
var ERC20_ABI = [
"function approve(address spender, uint256 amount) returns (bool)",
"function allowance(address owner, address spender) view returns (uint256)",
"function balanceOf(address owner) view returns (uint256)",
"function decimals() view returns (uint8)"
];
var CTF_ABI = [
"function setApprovalForAll(address operator, bool approved)",
"function isApprovedForAll(address owner, address operator) view returns (bool)"
];
var approveUSDCAction = {
name: "APPROVE_USDC",
similes: [
"APPROVE_USDC",
"SET_USDC_APPROVAL",
"ENABLE_TRADING",
"SETUP_APPROVALS",
"APPROVE_TOKENS",
"ALLOW_SPENDING",
"SET_ALLOWANCE",
"ENABLE_USDC",
"APPROVE_POLYMARKET",
"SETUP_TRADING"
],
description: "Approve USDC spending for Polymarket trading contracts (CTF, Exchange)",
validate: async (runtime, message, state) => {
logger.info(`[approveUSDCAction] Validate called for message: "${message.content?.text}"`);
const messageText = message.content?.text?.toLowerCase() || "";
const approvalKeywords = [
"approve",
"approval",
"allowance",
"enable trading",
"setup trading",
"allow spending",
"usdc",
"set approval",
"authorize"
];
const hasApprovalIntent = approvalKeywords.some((keyword) => messageText.includes(keyword));
if (!hasApprovalIntent) {
logger.info("[approveUSDCAction] No approval keywords found");
return false;
}
const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY") || runtime.getSetting("POLYMARKET_PRIVATE_KEY") || runtime.getSetting("PRIVATE_KEY");
if (!privateKey) {
logger.warn("[approveUSDCAction] No private key found");
return false;
}
logger.info("[approveUSDCAction] Validation passed");
return true;
},
handler: async (runtime, message, state, options, callback) => {
logger.info("[approveUSDCAction] Handler called!");
try {
const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY") || runtime.getSetting("POLYMARKET_PRIVATE_KEY") || runtime.getSetting("PRIVATE_KEY");
if (!privateKey) {
const errorMessage = "Private key is required for USDC approval";
logger.error(`[approveUSDCAction] ${errorMessage}`);
return createErrorResult(errorMessage);
}
const rpcUrl = runtime.getSetting("POLYGON_RPC_URL") || "https://polygon-rpc.com";
const provider = new ethers.JsonRpcProvider(rpcUrl);
const formattedPrivateKey = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
const wallet = new ethers.Wallet(formattedPrivateKey, provider);
logger.info(`[approveUSDCAction] Using wallet: ${wallet.address}`);
if (callback) {
const startContent = {
text: `\u{1F527} **Setting up USDC Approvals**
**Wallet Address**: ${wallet.address}
**Network**: Polygon (Chain ID: 137)
**Approval Process:**
1. Check current allowances
2. Approve USDC for Conditional Tokens Framework
3. Approve USDC for CTF Exchange
4. Approve USDC for Neg Risk Exchange
5. Set CTF approval for both Exchanges
Checking current approvals...`,
actions: ["APPROVE_USDC"],
data: { walletAddress: wallet.address, step: "starting" }
};
await callback(startContent);
}
const usdcNativeContract = new ethers.Contract(
USDC_NATIVE_ADDRESS,
ERC20_ABI,
wallet
);
const usdcBridgedContract = new ethers.Contract(
USDC_BRIDGED_ADDRESS,
ERC20_ABI,
wallet
);
const ctfContract = new ethers.Contract(CTF_ADDRESS, CTF_ABI, wallet);
const nativeBalance = await usdcNativeContract.balanceOf(wallet.address);
const bridgedBalance = await usdcBridgedContract.balanceOf(
wallet.address
);
const useNativeUSDC = nativeBalance > bridgedBalance;
const usdcContract = useNativeUSDC ? usdcNativeContract : usdcBridgedContract;
const usdcAddress = useNativeUSDC ? USDC_NATIVE_ADDRESS : USDC_BRIDGED_ADDRESS;
const usdcType = useNativeUSDC ? "Native USDC" : "USDC.e (Bridged)";
logger.info(`[approveUSDCAction] Using ${usdcType} (${ethers.formatUnits(useNativeUSDC ? nativeBalance : bridgedBalance, 6)} USDC)`);
const [
usdcForCTFAllowance,
usdcForExchangeAllowance,
usdcForNegRiskAllowance,
ctfForExchangeApproval,
ctfForNegRiskApproval
] = await Promise.all([
usdcContract.allowance(wallet.address, CTF_ADDRESS),
usdcContract.allowance(wallet.address, EXCHANGE_ADDRESS),
usdcContract.allowance(wallet.address, NEG_RISK_EXCHANGE_ADDRESS),
ctfContract.isApprovedForAll(wallet.address, EXCHANGE_ADDRESS),
ctfContract.isApprovedForAll(wallet.address, NEG_RISK_EXCHANGE_ADDRESS)
]);
const currentStatus = {
usdcForCTF: usdcForCTFAllowance > 0,
usdcForExchange: usdcForExchangeAllowance > 0,
ctfForExchange: ctfForExchangeApproval,
usdcForNegRisk: usdcForNegRiskAllowance > 0,
ctfForNegRisk: ctfForNegRiskApproval
};
if (callback) {
const statusContent = {
text: `\u{1F4CA} **Current Approval Status**
**USDC Contract**: ${usdcType}
**Balance**: ${ethers.formatUnits(useNativeUSDC ? nativeBalance : bridgedBalance, 6)} USDC
**Current Allowances:**
\u2022 USDC \u2192 CTF: ${currentStatus.usdcForCTF ? "\u2705 Approved" : "\u274C Not Approved"}
\u2022 USDC \u2192 CTF Exchange: ${currentStatus.usdcForExchange ? "\u2705 Approved" : "\u274C Not Approved"}
\u2022 USDC \u2192 Neg Risk Exchange: ${currentStatus.usdcForNegRisk ? "\u2705 Approved" : "\u274C Not Approved"}
\u2022 CTF \u2192 CTF Exchange: ${currentStatus.ctfForExchange ? "\u2705 Approved" : "\u274C Not Approved"}
\u2022 CTF \u2192 Neg Risk Exchange: ${currentStatus.ctfForNegRisk ? "\u2705 Approved" : "\u274C Not Approved"}
${currentStatus.usdcForCTF && currentStatus.usdcForExchange && currentStatus.ctfForExchange && currentStatus.usdcForNegRisk && currentStatus.ctfForNegRisk ? "\u2705 All approvals already set - ready for trading!" : "Setting missing approvals..."}`,
actions: ["APPROVE_USDC"],
data: {
currentStatus,
usdcType,
balance: ethers.formatUnits(
useNativeUSDC ? nativeBalance : bridgedBalance,
6
)
}
};
await callback(statusContent);
}
if (currentStatus.usdcForCTF && currentStatus.usdcForExchange && currentStatus.ctfForExchange && currentStatus.usdcForNegRisk && currentStatus.ctfForNegRisk) {
const successContent2 = {
text: `\u2705 **All Approvals Already Set**
**USDC Contract**: ${usdcType}
**Wallet Address**: ${wallet.address}
**\u2705 Ready for Trading:**
\u2022 USDC \u2192 CTF: Approved
\u2022 USDC \u2192 CTF Exchange: Approved
\u2022 USDC \u2192 Neg Risk Exchange: Approved
\u2022 CTF \u2192 CTF Exchange: Approved
\u2022 CTF \u2192 Neg Risk Exchange: Approved
You can now place orders on Polymarket!`,
actions: ["APPROVE_USDC"],
data: {
success: true,
allApprovalsSet: true,
walletAddress: wallet.address,
usdcType,
approvalStatus: currentStatus
}
};
if (callback) {
await callback(successContent2);
}
return contentToActionResult(successContent2);
}
const transactions = [];
const gasLimit = 1e5;
if (!currentStatus.usdcForCTF) {
logger.info("[approveUSDCAction] Setting USDC allowance for CTF...");
if (callback) {
const step1Content = {
text: `\u{1F527} **Step 1/5: Approving USDC for CTF**
Setting unlimited allowance for Conditional Tokens Framework...
**Contract**: ${CTF_ADDRESS}`,
actions: ["APPROVE_USDC"],
data: { step: 1, contract: "CTF" }
};
await callback(step1Content);
}
const tx1 = await usdcContract.approve(CTF_ADDRESS, ethers.MaxUint256, {
gasLimit
});
const receipt1 = await tx1.wait();
transactions.push({ step: 1, contract: "CTF", txHash: receipt1.hash });
logger.info(`[approveUSDCAction] CTF approval tx: ${receipt1.hash}`);
}
if (!currentStatus.usdcForExchange) {
logger.info(`[approveUSDCAction] Setting USDC allowance for Exchange...`);
if (callback) {
const step2Content = {
text: `\u{1F527} **Step 2/5: Approving USDC for CTF Exchange**
Setting unlimited allowance for CTF Exchange...
**Contract**: ${EXCHANGE_ADDRESS}`,
actions: ["APPROVE_USDC"],
data: { step: 2, contract: "Exchange" }
};
await callback(step2Content);
}
const tx2 = await usdcContract.approve(
EXCHANGE_ADDRESS,
ethers.MaxUint256,
{ gasLimit }
);
const receipt2 = await tx2.wait();
transactions.push({
step: 2,
contract: "Exchange",
txHash: receipt2.hash
});
logger.info(`[approveUSDCAction] Exchange approval tx: ${receipt2.hash}`);
}
if (!currentStatus.ctfForExchange) {
logger.info("[approveUSDCAction] Setting CTF approval for Exchange...");
if (callback) {
const step3Content = {
text: `\u{1F527} **Step 3/5: Approving CTF for CTF Exchange**
Setting approval for all CTF tokens on Exchange...
**Contract**: ${EXCHANGE_ADDRESS}`,
actions: ["APPROVE_USDC"],
data: { step: 3, contract: "CTF for Exchange" }
};
await callback(step3Content);
}
const tx3 = await ctfContract.setApprovalForAll(
EXCHANGE_ADDRESS,
true,
{ gasLimit }
);
const receipt3 = await tx3.wait();
transactions.push({
step: 3,
contract: "CTF for Exchange",
txHash: receipt3.hash
});
logger.info(`[approveUSDCAction] CTF approval tx: ${receipt3.hash}`);
}
if (!currentStatus.usdcForNegRisk) {
logger.info(`[approveUSDCAction] Setting USDC allowance for Neg Risk Exchange...`);
if (callback) {
const step4Content = {
text: `\u{1F527} **Step 4/5: Approving USDC for Neg Risk Exchange**
Setting unlimited allowance for Neg Risk Exchange...
**Contract**: ${NEG_RISK_EXCHANGE_ADDRESS}`,
actions: ["APPROVE_USDC"],
data: { step: 4, contract: "Neg Risk Exchange" }
};
await callback(step4Content);
}
const tx4 = await usdcContract.approve(
NEG_RISK_EXCHANGE_ADDRESS,
ethers.MaxUint256,
{ gasLimit }
);
const receipt4 = await tx4.wait();
transactions.push({
step: 4,
contract: "Neg Risk Exchange",
txHash: receipt4.hash
});
logger.info(`[approveUSDCAction] Neg Risk Exchange approval tx: ${receipt4.hash}`);
}
if (!currentStatus.ctfForNegRisk) {
logger.info(`[approveUSDCAction] Setting CTF approval for Neg Risk Exchange...`);
if (callback) {
const step5Content = {
text: `\u{1F527} **Step 5/5: Approving CTF for Neg Risk Exchange**
Setting approval for all CTF tokens on Neg Risk Exchange...
**Contract**: ${NEG_RISK_EXCHANGE_ADDRESS}`,
actions: ["APPROVE_USDC"],
data: { step: 5, contract: "CTF for Neg Risk Exchange" }
};
await callback(step5Content);
}
const tx5 = await ctfContract.setApprovalForAll(
NEG_RISK_EXCHANGE_ADDRESS,
true,
{ gasLimit }
);
const receipt5 = await tx5.wait();
transactions.push({
step: 5,
contract: "CTF for Neg Risk Exchange",
txHash: receipt5.hash
});
logger.info(`[approveUSDCAction] CTF approval for Neg Risk Exchange tx: ${receipt5.hash}`);
}
const successContent = {
text: `\u{1F389} **USDC Approvals Successfully Set!**
**Wallet Address**: ${wallet.address}
**USDC Contract**: ${usdcType}
**Network**: Polygon
**\u2705 Completed Approvals:**
${transactions.map((tx) => `\u2022 Step ${tx.step} - ${tx.contract}: [${tx.txHash.slice(0, 10)}...](https://polygonscan.com/tx/${tx.txHash})`).join("\n")}
**\u2705 Trading Ready:**
\u2022 USDC \u2192 CTF: \u2705 Approved
\u2022 USDC \u2192 CTF Exchange: \u2705 Approved
\u2022 USDC \u2192 Neg Risk Exchange: \u2705 Approved
\u2022 CTF \u2192 CTF Exchange: \u2705 Approved
\u2022 CTF \u2192 Neg Risk Exchange: \u2705 Approved
\u{1F680} **You can now place orders on Polymarket!**`,
actions: ["APPROVE_USDC"],
data: {
success: true,
walletAddress: wallet.address,
usdcType,
transactions,
finalStatus: {
usdcForCTF: true,
usdcForExchange: true,
ctfForExchange: true,
usdcForNegRisk: true,
ctfForNegRisk: true
}
}
};
if (callback) {
await callback(successContent);
}
return contentToActionResult(successContent);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
logger.error(`[approveUSDCAction] Error: ${error}`);
const errorContent = {
text: `\u274C **USDC Approval Failed**
**Error**: ${errorMessage}
This could be due to:
\u2022 Insufficient ETH for gas fees
\u2022 Network connectivity issues
\u2022 Invalid private key configuration
\u2022 RPC provider problems
**Please check:**
1. You have enough MATIC for gas fees
2. Your private key is correctly configured
3. Network connection is stable
4. Try again in a few moments
**Configuration:**
- WALLET_PRIVATE_KEY or POLYMARKET_PRIVATE_KEY must be set
- Sufficient MATIC balance for gas fees required`,
actions: ["APPROVE_USDC"],
data: {
error: errorMessage,
success: false
}
};
if (callback) {
await callback(errorContent);
}
return createErrorResult(errorMessage);
}
},
examples: [
[
{
name: "{{user1}}",
content: {
text: "I need to approve USDC for Polymarket trading"
}
},
{
name: "{{user2}}",
content: {
text: "I'll set up the necessary USDC approvals for Polymarket trading. This will approve USDC spending for the CTF and Exchange contracts...",
action: "APPROVE_USDC"
}
}
],
[
{
name: "{{user1}}",
content: {
text: "Set up trading approvals for my wallet"
}
},
{
name: "{{user2}}",
content: {
text: "I'll configure the required approvals for Polymarket trading. Setting up USDC allowances...",
action: "APPROVE_USDC"
}
}
]
]
};
export {
contentToActionResult,
createErrorResult,
approveUSDCAction
};
//# sourceMappingURL=chunk-SGIRZUJP.js.map