UNPKG

plugin-my

Version:

COTI blockchain plugin for ElizaOS - enables private token operations and encrypted transactions

1,295 lines (1,284 loc) 91 kB
// src/actions/getNativeBalance.ts import { elizaLogger as elizaLogger2 } from "@elizaos/core"; // src/providers/wallet.ts import { elizaLogger } from "@elizaos/core"; import { ethers } from "ethers"; var CotiWalletProvider = class { wallet; provider; runtime; constructor(runtime, config) { this.runtime = runtime; const rpcUrl = config.rpcUrl || process.env.COTI_RPC_URL || "https://testnet.coti.io/rpc"; this.provider = new ethers.JsonRpcProvider(rpcUrl); const privateKey = config.privateKey || process.env.COTI_PRIVATE_KEY; if (!privateKey) { throw new Error("COTI private key not provided"); } this.wallet = new ethers.Wallet(privateKey, this.provider); elizaLogger.info(`COTI Wallet initialized with address: ${this.wallet.address}`); } getAddress() { return this.wallet.address; } async getBalance(address) { const targetAddress = address || this.wallet.address; return await this.provider.getBalance(targetAddress); } async transfer(to, amount) { const amountWei = ethers.parseEther(amount); elizaLogger.info(`Transferring ${amount} COTI to ${to}`); const tx = await this.wallet.sendTransaction({ to, value: amountWei }); elizaLogger.info(`Transaction sent: ${tx.hash}`); return tx; } async getTransactionReceipt(txHash) { return await this.provider.getTransactionReceipt(txHash); } async estimateGas(to, amount) { const amountWei = ethers.parseEther(amount); return await this.wallet.estimateGas({ to, value: amountWei }); } getProvider() { return this.provider; } getWallet() { return this.wallet; } }; var walletProviderInstance = null; function initCotiWalletProvider(runtime) { if (!walletProviderInstance) { const config = { privateKey: process.env.COTI_PRIVATE_KEY, rpcUrl: process.env.COTI_RPC_URL, networkId: parseInt(process.env.COTI_NETWORK_ID || "7701") }; walletProviderInstance = new CotiWalletProvider(runtime, config); } return walletProviderInstance; } var cotiWalletProvider = { name: "COTI_WALLET_PROVIDER", description: "Provides COTI wallet functionality for blockchain operations", get: async (runtime, message, state) => { try { const walletProvider = initCotiWalletProvider(runtime); const address = walletProvider.getAddress(); const balance = await walletProvider.getBalance(); return { text: `COTI Wallet Address: ${address}, Balance: ${ethers.formatEther(balance)} COTI`, values: { address, balance: ethers.formatEther(balance), balanceWei: balance.toString() }, data: { address, balance: ethers.formatEther(balance), balanceWei: balance.toString() } }; } catch (error) { elizaLogger.error("Error in COTI wallet provider:", error); return { text: `Error accessing COTI wallet: ${error instanceof Error ? error.message : String(error)}`, values: {}, data: { error: error instanceof Error ? error.message : String(error) } }; } } }; // src/types/index.ts import { z } from "zod"; var GetNativeBalanceSchema = z.object({ accountAddress: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address format") }); var TransferNativeSchema = z.object({ to: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid recipient address format"), amount: z.string().min(1, "Amount must be specified") }); var WalletInfoSchema = z.object({ includeBalance: z.boolean().optional().default(true) }); // src/actions/getNativeBalance.ts import { ethers as ethers2 } from "ethers"; var getNativeBalanceAction = { name: "GET_NATIVE_BALANCE", similes: [ "CHECK_COTI_BALANCE", "GET_COTI_BALANCE", "NATIVE_BALANCE", "BALANCE_CHECK", "CHECK_BALANCE", "SHOW_BALANCE", "GET_BALANCE", "BALANCE_OF", "CHECK_ADDRESS_BALANCE", "GET_ADDRESS_BALANCE" ], description: "Get the native COTI token balance of a COTI blockchain account. Returns the account balance in COTI tokens.", validate: async (runtime, message) => { try { const text = message.content?.text || ""; elizaLogger2.info(`GET_NATIVE_BALANCE validation - text: "${text}"`); const isBalanceQuery = /\b(balance|check.*balance|get.*balance|show.*balance|help.*check.*balance)\b/i.test(text); elizaLogger2.info(`GET_NATIVE_BALANCE validation - isBalanceQuery: ${isBalanceQuery}`); if (message.content?.accountAddress) { const params = GetNativeBalanceSchema.parse(message.content); elizaLogger2.info(`GET_NATIVE_BALANCE validation - accountAddress field found: ${params.accountAddress}`); return ethers2.isAddress(params.accountAddress); } const addressMatch = text.match(/0x[a-fA-F0-9]{40}/); elizaLogger2.info(`GET_NATIVE_BALANCE validation - addressMatch: ${addressMatch?.[0] || "none"}`); if (addressMatch && isBalanceQuery) { const isValid = ethers2.isAddress(addressMatch[0]); elizaLogger2.info(`GET_NATIVE_BALANCE validation - address valid: ${isValid}, returning true`); return isValid; } if (isBalanceQuery && !addressMatch) { try { const walletProvider = initCotiWalletProvider(runtime); const hasAddress = walletProvider.getAddress() !== ""; elizaLogger2.info(`GET_NATIVE_BALANCE validation - general balance query, has wallet: ${hasAddress}`); return hasAddress; } catch { elizaLogger2.info(`GET_NATIVE_BALANCE validation - wallet provider failed`); return false; } } elizaLogger2.info(`GET_NATIVE_BALANCE validation - returning false`); return false; } catch (error) { elizaLogger2.error(`GET_NATIVE_BALANCE validation error:`, error); return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger2.info("Starting GET_NATIVE_BALANCE action"); let accountAddress; if (message.content?.accountAddress) { const params = GetNativeBalanceSchema.parse(message.content); accountAddress = params.accountAddress; } else { const text = message.content?.text || ""; const addressMatch = text.match(/0x[a-fA-F0-9]{40}/); if (addressMatch) { accountAddress = addressMatch[0]; } else { const walletProvider2 = initCotiWalletProvider(runtime); accountAddress = walletProvider2.getAddress(); } } if (!ethers2.isAddress(accountAddress)) { throw new Error("Invalid account address"); } const walletProvider = initCotiWalletProvider(runtime); const balance = await walletProvider.getBalance(accountAddress); const responseText = `COTI Balance for ${accountAddress}: ${ethers2.formatEther(balance)} COTI`; if (callback) { await callback({ text: responseText, content: { success: true, accountAddress, balance: ethers2.formatEther(balance), balanceWei: balance.toString() } }); } return { text: responseText, success: true, data: { accountAddress, balance: ethers2.formatEther(balance), balanceWei: balance.toString() } }; } catch (error) { elizaLogger2.error("Error in GET_NATIVE_BALANCE action:", error); const errorMessage = `Failed to get COTI balance: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "What's my COTI balance?" } }, { name: "{{user2}}", content: { text: "I'll check your COTI balance for you. Let me retrieve your account information.", action: "GET_NATIVE_BALANCE" } } ], [ { name: "{{user1}}", content: { text: "Check balance for 0x1234567890123456789012345678901234567890" } }, { name: "{{user2}}", content: { text: "I'll check the COTI balance for that address.", action: "GET_NATIVE_BALANCE" } } ] ] }; // src/actions/transferNative.ts import { elizaLogger as elizaLogger3 } from "@elizaos/core"; import { ethers as ethers3 } from "ethers"; var transferNativeAction = { name: "TRANSFER_NATIVE", similes: [ "SEND_COTI", "TRANSFER_COTI", "SEND_TOKENS", "TRANSFER_TOKENS", "SEND_NATIVE" ], description: "Transfer native COTI tokens to another address on the COTI blockchain", validate: async (runtime, message) => { try { if (message.content?.to && message.content?.amount) { const params = TransferNativeSchema.parse(message.content); return ethers3.isAddress(params.to) && parseFloat(params.amount) > 0; } const text = message.content?.text || ""; const transferMatch = text.match(/(?:send|transfer)\s+(\d+(?:\.\d+)?)\s+(?:coti\s+)?to\s+(0x[a-fA-F0-9]{40})/i); if (transferMatch) { const amount = transferMatch[1]; const address = transferMatch[2]; return ethers3.isAddress(address) && parseFloat(amount) > 0; } return false; } catch { return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger3.info("Starting TRANSFER_NATIVE action"); let to; let amount; if (message.content?.to && message.content?.amount) { const params = TransferNativeSchema.parse(message.content); to = params.to; amount = params.amount; } else { const text = message.content?.text || ""; const transferMatch = text.match(/(?:send|transfer)\s+(\d+(?:\.\d+)?)\s+(?:coti\s+)?to\s+(0x[a-fA-F0-9]{40})/i); if (!transferMatch) { throw new Error("Could not parse transfer parameters from message"); } amount = transferMatch[1]; to = transferMatch[2]; } if (!ethers3.isAddress(to)) { throw new Error("Invalid recipient address"); } if (parseFloat(amount) <= 0) { throw new Error("Amount must be greater than 0"); } const walletProvider = initCotiWalletProvider(runtime); const fromAddress = walletProvider.getAddress(); const balance = await walletProvider.getBalance(); const amountWei = ethers3.parseEther(amount); if (balance < amountWei) { throw new Error(`Insufficient balance. Available: ${ethers3.formatEther(balance)} COTI, Required: ${amount} COTI`); } const estimatedGas = await walletProvider.estimateGas(to, amount); elizaLogger3.info(`Estimated gas: ${estimatedGas.toString()}`); const tx = await walletProvider.transfer(to, amount); const responseText = `Successfully transferred ${amount} COTI to ${to}. Transaction hash: ${tx.hash}`; if (callback) { await callback({ text: responseText, content: { success: true, transactionHash: tx.hash, from: fromAddress, to, amount } }); } return { text: responseText, success: true, data: { transactionHash: tx.hash, from: fromAddress, to, amount, amountWei: amountWei.toString(), estimatedGas: estimatedGas.toString() } }; } catch (error) { elizaLogger3.error("Error in TRANSFER_NATIVE action:", error); const errorMessage = `Failed to transfer COTI: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Send 10 COTI to 0x1234567890123456789012345678901234567890" } }, { name: "{{user2}}", content: { text: "I'll transfer 10 COTI tokens to that address for you.", action: "TRANSFER_NATIVE" } } ], [ { name: "{{user1}}", content: { text: "Transfer 5.5 COTI to alice" } }, { name: "{{user2}}", content: { text: "I'll send 5.5 COTI tokens to alice's address.", action: "TRANSFER_NATIVE" } } ] ] }; // src/actions/listAccounts.ts import { elizaLogger as elizaLogger4 } from "@elizaos/core"; import { ethers as ethers4 } from "ethers"; var listAccountsAction = { name: "LIST_ACCOUNTS", similes: [ "SHOW_ACCOUNTS", "GET_ACCOUNTS", "LIST_WALLETS", "SHOW_WALLETS", "MY_ACCOUNTS" ], description: "List all configured COTI accounts and their information", validate: async (runtime, message) => { try { const walletProvider = initCotiWalletProvider(runtime); return walletProvider.getAddress() !== ""; } catch { return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger4.info("Starting LIST_ACCOUNTS action"); const walletProvider = initCotiWalletProvider(runtime); const address = walletProvider.getAddress(); const balance = await walletProvider.getBalance(); const provider = walletProvider.getProvider(); const transactionCount = await provider.getTransactionCount(address); const network = await provider.getNetwork(); const accountInfo = { address, balance: ethers4.formatEther(balance), balanceWei: balance.toString(), transactionCount, network: { name: network.name, chainId: network.chainId.toString() } }; const responseText = `COTI Account Information: Address: ${address} Balance: ${ethers4.formatEther(balance)} COTI Transaction Count: ${transactionCount} Network: ${network.name} (Chain ID: ${network.chainId})`; if (callback) { await callback({ text: responseText, content: { success: true, accounts: [accountInfo] } }); } return { text: responseText, success: true, data: { accounts: [accountInfo], totalAccounts: 1 } }; } catch (error) { elizaLogger4.error("Error in LIST_ACCOUNTS action:", error); const errorMessage = `Failed to list COTI accounts: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Show me my COTI accounts" } }, { name: "{{user2}}", content: { text: "I'll list all your configured COTI accounts.", action: "LIST_ACCOUNTS" } } ], [ { name: "{{user1}}", content: { text: "What accounts do I have?" } }, { name: "{{user2}}", content: { text: "Let me show you your COTI account information.", action: "LIST_ACCOUNTS" } } ] ] }; // src/actions/walletInfo.ts import { elizaLogger as elizaLogger5 } from "@elizaos/core"; import { ethers as ethers5 } from "ethers"; var walletInfoAction = { name: "WALLET_INFO", similes: [ "GET_WALLET_INFO", "WALLET_DETAILS", "WALLET_STATUS", "ACCOUNT_INFO", "WALLET_SUMMARY" ], description: "Get detailed information about the COTI wallet including address, balance, and network details", validate: async (runtime, message) => { try { const walletProvider = initCotiWalletProvider(runtime); return walletProvider.getAddress() !== ""; } catch { return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger5.info("Starting WALLET_INFO action"); let includeBalance = true; if (message.content?.includeBalance !== void 0) { const params = WalletInfoSchema.parse(message.content); includeBalance = params.includeBalance; } const walletProvider = initCotiWalletProvider(runtime); const address = walletProvider.getAddress(); const provider = walletProvider.getProvider(); const network = await provider.getNetwork(); const blockNumber = await provider.getBlockNumber(); const gasPrice = await provider.getFeeData().then((fee) => fee.gasPrice || BigInt(0)); let walletInfo = { address, network: { name: network.name, chainId: network.chainId.toString(), blockNumber, gasPrice: ethers5.formatUnits(gasPrice, "gwei") } }; let responseText = `COTI Wallet Information: Address: ${address} Network: ${network.name} (Chain ID: ${network.chainId}) Current Block: ${blockNumber} Gas Price: ${ethers5.formatUnits(gasPrice, "gwei")} gwei`; if (includeBalance) { const balance = await walletProvider.getBalance(); const transactionCount = await provider.getTransactionCount(address); walletInfo.balance = ethers5.formatEther(balance); walletInfo.balanceWei = balance.toString(); walletInfo.transactionCount = transactionCount; responseText += ` Balance: ${ethers5.formatEther(balance)} COTI Transaction Count: ${transactionCount}`; } if (callback) { await callback({ text: responseText, content: { success: true, walletInfo } }); } return { text: responseText, success: true, data: { walletInfo } }; } catch (error) { elizaLogger5.error("Error in WALLET_INFO action:", error); const errorMessage = `Failed to get wallet information: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Show me my wallet information" } }, { name: "{{user2}}", content: { text: "I'll get your COTI wallet details for you.", action: "WALLET_INFO" } } ], [ { name: "{{user1}}", content: { text: "What's my wallet status?" } }, { name: "{{user2}}", content: { text: "Let me check your COTI wallet status and information.", action: "WALLET_INFO" } } ] ] }; // src/actions/deployPrivateErc721Contract.ts import { elizaLogger as elizaLogger6 } from "@elizaos/core"; import { ethers as ethers6 } from "ethers"; import { z as z2 } from "zod"; var DeployPrivateERC721Schema = z2.object({ name: z2.string().describe("Name of the NFT collection"), symbol: z2.string().describe("Symbol of the NFT collection"), gasLimit: z2.number().optional().describe("Optional gas limit") }); var ERC721_ABI = [ "constructor(string name, string symbol)", "function name() view returns (string)", "function symbol() view returns (string)", "function totalSupply() view returns (uint256)", "function balanceOf(address owner) view returns (uint256)", "function ownerOf(uint256 tokenId) view returns (address)", "function mint(address to, string uri) returns (uint256)", "function tokenURI(uint256 tokenId) view returns (string)" ]; var ERC721_BYTECODE = "0x608060405234801561001057600080fd5b50604051610c38380380610c388339818101604052810190610032919061007a565b818181600090805190602001906100499291906100ed565b5080600190805190602001906100609291906100ed565b50505050505061019c565b600080fd5b600080fd5b600080fd5b600080fd5b6000815190506100848161016f565b92915050565b6000602082840312156100a0576100a061006a565b5b60006100ae84828501610075565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061012557607f821691505b602082108103610138576101376100de565b5b50919050565b610a8c8061014b6000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80636352211e1161005b5780636352211e1461013157806370a082311461016157806395d89b4114610191578063a22cb465146101af57610088565b806301ffc9a71461008d57806306fdde03146100bd578063081812fc146100db57806323b872dd1461010b575b600080fd5b6100a760048036038101906100a29190610678565b6101cb565b6040516100b491906106c0565b60405180910390f35b6100c56102ad565b6040516100d29190610774565b60405180910390f35b6100f560048036038101906100f091906107cc565b61033f565b60405161010291906107fa565b60405180910390f35b61012560048036038101906101209190610841565b610385565b005b61014b600480360381019061014691906107cc565b6103e5565b60405161015891906107fa565b60405180910390f35b61017b60048036038101906101769190610894565b610496565b60405161018891906108d0565b60405180910390f35b61019961054d565b6040516101a69190610774565b60405180910390f35b6101c960048036038101906101c49190610917565b6105df565b005b60007f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061029657507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b806102a657506102a5826105f5565b5b9050919050565b6060600080546102bc90610986565b80601f01602080910402602001604051908101604052809291908181526020018280546102e890610986565b80156103355780601f1061030a57610100808354040283529160200191610335565b820191906000526020600020905b81548152906001019060200180831161031857829003601f168201915b5050505050905090565b600061034a8261065f565b61038957816040517fcf4700e400000000000000000000000000000000000000000000000000000000815260040161038091906108d0565b60405180910390fd5b6004600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036103f457600080fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361042d57600080fd5b61043883838361066a565b505050565b6000806002600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361048d57600080fd5b80915050919050565b60008073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036104d557600080fd5b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606001805461055c90610986565b80601f016020809104026020016040519081016040528092919081815260200182805461058890610986565b80156105d55780601f106105aa576101008083540402835291602001916105d5565b820191906000526020600020905b8154815290600101906020018083116105b857829003601f168201915b5050505050905090565b6105f182826105ec610674565b61067c565b5050565b60007f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b600080823b905060008111915050919050565b505050565b600033905090565b505050565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6106b581610680565b81146106c057600080fd5b50565b6000813590506106d2816106ac565b92915050565b6000602082840312156106ee576106ed610687565b5b60006106fc848285016106c3565b91505092915050565b60008115159050919050565b61071a81610705565b82525050565b60006020820190506107356000830184610711565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561077557808201518184015260208101905061075a565b60008484015250505050565b6000601f19601f8301169050919050565b600061079d8261073b565b6107a78185610746565b93506107b7818560208601610757565b6107c081610781565b840191505092915050565b600060208201905081810360008301526107e58184610792565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610818826107ed565b9050919050565b6108288161080d565b82525050565b6000602082019050610843600083018461081f565b92915050565b60008060006060848603121561086257610861610687565b5b60006108708682870161082e565b93505060206108818682870161082e565b925050604061089286828701610839565b9150509250925092565b6000602082840312156108b2576108b1610687565b5b60006108c08482850161082e565b91505092915050565b6000819050919050565b6108dc816108c9565b82525050565b60006020820190506108f760008301846108d3565b92915050565b61090681610705565b811461091157600080fd5b50565b600081359050610923816108fd565b92915050565b600080604083850312156109405761093f610687565b5b600061094e8582860161082e565b925050602061095f85828601610914565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806109ae57607f821691505b6020821081036109c1576109c0610969565b5b5091905056fea26469706673582212208c8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b8b64736f6c63430008110033"; var deployPrivateERC721ContractAction = { name: "DEPLOY_PRIVATE_ERC721_CONTRACT", similes: [ "DEPLOY_ERC721", "CREATE_NFT", "DEPLOY_NFT", "CREATE_PRIVATE_NFT", "DEPLOY_PRIVATE_NFT", "CREATE_NFT_COLLECTION", "DEPLOY_NFT_COLLECTION" ], description: "Deploy a new private ERC721 NFT contract on the COTI blockchain. This creates a new private NFT collection with the specified name and symbol.", validate: async (runtime, message) => { try { const text = message.content?.text || ""; elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT validation - text: "${text}"`); const isDeployQuery = /\b(deploy|create|new).*\b(nft|erc721|collection)\b/i.test(text); elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT validation - isDeployQuery: ${isDeployQuery}`); if (message.content?.name && message.content?.symbol) { elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT validation - structured params found`); return true; } const nameMatch = text.match(/name\s*['""]([^'"]*)['""]|called\s*['""]([^'"]*)['"]/i); const symbolMatch = text.match(/symbol\s*['""]([^'"]*)['""]|\(([^)]+)\)/i); elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT validation - nameMatch: ${nameMatch?.[1] || nameMatch?.[2] || "none"}`); elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT validation - symbolMatch: ${symbolMatch?.[1] || symbolMatch?.[2] || "none"}`); if (isDeployQuery && nameMatch && symbolMatch) { elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT validation - returning true`); return true; } elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT validation - returning false`); return false; } catch (error) { elizaLogger6.error(`DEPLOY_PRIVATE_ERC721_CONTRACT validation error:`, error); return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger6.info("Starting DEPLOY_PRIVATE_ERC721_CONTRACT action"); let params; if (message.content?.name && message.content?.symbol) { params = DeployPrivateERC721Schema.parse(message.content); } else { const text = message.content?.text || ""; const nameMatch = text.match(/name\s*['""]([^'"]*)['""]|called\s*['""]([^'"]*)['"]/i); const symbolMatch = text.match(/symbol\s*['""]([^'"]*)['""]|\(([^)]+)\)/i); if (!nameMatch || !symbolMatch) { throw new Error("Could not extract name and symbol from message"); } params = { name: (nameMatch[1] || nameMatch[2]).trim(), symbol: (symbolMatch[1] || symbolMatch[2]).trim() }; } const walletProvider = initCotiWalletProvider(runtime); const wallet = walletProvider.getWallet(); const factory = new ethers6.ContractFactory( ERC721_ABI, ERC721_BYTECODE, wallet ); elizaLogger6.info(`Deploying NFT contract with name: ${params.name}, symbol: ${params.symbol}`); const contract = await factory.deploy( params.name, params.symbol, { gasLimit: params.gasLimit || 2e6 } ); const deploymentTransaction = contract.deploymentTransaction(); if (!deploymentTransaction) { throw new Error("Deployment transaction not found"); } const contractAddress = await contract.getAddress(); const receipt = await deploymentTransaction.wait(); const responseText = `Successfully deployed private ERC721 contract: ${params.name} (${params.symbol}) Contract Address: ${contractAddress} Transaction Hash: ${deploymentTransaction.hash}`; if (callback) { await callback({ text: responseText, content: { success: true, contractAddress, transactionHash: deploymentTransaction.hash, name: params.name, symbol: params.symbol } }); } return { text: responseText, success: true, data: { contractAddress, transactionHash: deploymentTransaction.hash, name: params.name, symbol: params.symbol } }; } catch (error) { elizaLogger6.error("Error in DEPLOY_PRIVATE_ERC721_CONTRACT action:", error); const errorMessage = `Failed to deploy private ERC721 contract: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Deploy a new NFT collection called 'My Art Gallery' with symbol 'ART'" } }, { name: "{{user2}}", content: { text: "I'll deploy a private ERC721 NFT collection for you on the COTI blockchain.", action: "DEPLOY_PRIVATE_ERC721_CONTRACT" } } ] ] }; // src/actions/mintPrivateErc721Token.ts import { elizaLogger as elizaLogger7 } from "@elizaos/core"; import { ethers as ethers7 } from "ethers"; import { z as z3 } from "zod"; var MintPrivateERC721Schema = z3.object({ tokenAddress: z3.string().describe("ERC721 token contract address"), toAddress: z3.string().describe("Address to receive the minted NFT"), tokenUri: z3.string().describe("URI for the token metadata"), gasLimit: z3.number().optional().describe("Optional gas limit") }); var ERC721_ABI2 = [ "function mint(address to, string uri) returns (uint256)", "function tokenURI(uint256 tokenId) view returns (string)", "function ownerOf(uint256 tokenId) view returns (address)", "function totalSupply() view returns (uint256)" ]; var mintPrivateErc721TokenAction = { name: "MINT_PRIVATE_ERC721_TOKEN", similes: [ "MINT_ERC721", "MINT_NFT", "CREATE_NFT", "MINT_PRIVATE_NFT", "ISSUE_NFT", "CREATE_TOKEN", "MINT_TOKEN" ], description: "Mint a new private ERC721 NFT token on the COTI blockchain. This creates a new NFT with the specified metadata URI and assigns it to the recipient address.", validate: async (runtime, message) => { try { const text = message.content?.text || ""; elizaLogger7.info(`MINT_PRIVATE_ERC721_TOKEN validation - text: "${text}"`); const isMintQuery = /\b(mint|create).*\b(nft|token)\b/i.test(text); elizaLogger7.info(`MINT_PRIVATE_ERC721_TOKEN validation - isMintQuery: ${isMintQuery}`); if (message.content?.tokenAddress && message.content?.toAddress && message.content?.tokenUri) { elizaLogger7.info(`MINT_PRIVATE_ERC721_TOKEN validation - structured params found`); return true; } const addressMatches = text.match(/0x[a-fA-F0-9]{40}/g); const uriMatch = text.match(/https?:\/\/[^\s]+/i); elizaLogger7.info(`MINT_PRIVATE_ERC721_TOKEN validation - addresses found: ${addressMatches?.length || 0}`); elizaLogger7.info(`MINT_PRIVATE_ERC721_TOKEN validation - URI found: ${!!uriMatch}`); if (isMintQuery && addressMatches && addressMatches.length >= 2 && uriMatch) { elizaLogger7.info(`MINT_PRIVATE_ERC721_TOKEN validation - returning true`); return true; } elizaLogger7.info(`MINT_PRIVATE_ERC721_TOKEN validation - returning false`); return false; } catch (error) { elizaLogger7.error(`MINT_PRIVATE_ERC721_TOKEN validation error:`, error); return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger7.info("Starting MINT_PRIVATE_ERC721_TOKEN action"); let params; if (message.content?.tokenAddress && message.content?.toAddress && message.content?.tokenUri) { params = MintPrivateERC721Schema.parse(message.content); } else { const text = message.content?.text || ""; const addressMatches = text.match(/0x[a-fA-F0-9]{40}/g); const uriMatch = text.match(/https?:\/\/[^\s]+/i); if (!addressMatches || addressMatches.length < 2 || !uriMatch) { throw new Error("Could not extract required parameters from message"); } params = { tokenAddress: addressMatches[0], toAddress: addressMatches[1], tokenUri: uriMatch[0] }; } const walletProvider = initCotiWalletProvider(runtime); const wallet = walletProvider.getWallet(); const contract = new ethers7.Contract( params.tokenAddress, ERC721_ABI2, wallet ); elizaLogger7.info(`Minting NFT to ${params.toAddress} with URI: ${params.tokenUri}`); const tx = await contract.mint( params.toAddress, params.tokenUri, { gasLimit: params.gasLimit || 5e5 } ); const receipt = await tx.wait(); let tokenId = "N/A"; if (receipt.logs && receipt.logs.length > 0) { try { const transferEvent = receipt.logs.find( (log) => log.topics && log.topics[0] === "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" ); if (transferEvent && transferEvent.topics && transferEvent.topics[3]) { tokenId = parseInt(transferEvent.topics[3], 16).toString(); } } catch (e) { elizaLogger7.warn("Could not extract token ID from logs:", e); } } const responseText = `Successfully minted private ERC721 NFT! Token ID: ${tokenId} Contract: ${params.tokenAddress} Recipient: ${params.toAddress} Metadata URI: ${params.tokenUri} Transaction Hash: ${tx.hash}`; if (callback) { await callback({ text: responseText, content: { success: true, tokenId, tokenAddress: params.tokenAddress, toAddress: params.toAddress, tokenUri: params.tokenUri, transactionHash: tx.hash } }); } return { text: responseText, success: true, data: { tokenId, tokenAddress: params.tokenAddress, toAddress: params.toAddress, tokenUri: params.tokenUri, transactionHash: tx.hash } }; } catch (error) { elizaLogger7.error("Error in MINT_PRIVATE_ERC721_TOKEN action:", error); const errorMessage = `Failed to mint private ERC721 token: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Mint an NFT to alice with metadata https://example.com/art/1.json" } }, { name: "{{user2}}", content: { text: "I'll mint a private NFT with encrypted metadata for you.", action: "MINT_PRIVATE_ERC721_TOKEN" } } ] ] }; // src/actions/transferPrivateErc721Token.ts import { elizaLogger as elizaLogger8 } from "@elizaos/core"; import { ethers as ethers8 } from "ethers"; import { z as z4 } from "zod"; var TransferPrivateERC721Schema = z4.object({ tokenAddress: z4.string().describe("ERC721 token contract address"), recipientAddress: z4.string().describe("Recipient COTI address"), tokenId: z4.string().describe("ID of the NFT token to transfer"), useSafeTransfer: z4.boolean().optional().describe("Use safeTransferFrom instead of transferFrom"), gasLimit: z4.number().optional().describe("Optional gas limit") }); var ERC721_ABI3 = [ "function transferFrom(address from, address to, uint256 tokenId)", "function safeTransferFrom(address from, address to, uint256 tokenId)", "function ownerOf(uint256 tokenId) view returns (address)", "function approve(address to, uint256 tokenId)", "function getApproved(uint256 tokenId) view returns (address)" ]; var transferPrivateErc721TokenAction = { name: "TRANSFER_PRIVATE_ERC721_TOKEN", similes: [ "TRANSFER_ERC721", "TRANSFER_NFT", "SEND_NFT", "TRANSFER_PRIVATE_NFT", "SEND_TOKEN", "TRANSFER_TOKEN" ], description: "Transfer a private ERC721 NFT token to another address on the COTI blockchain.", validate: async (runtime, message) => { try { const text = message.content?.text || ""; elizaLogger8.info(`TRANSFER_PRIVATE_ERC721_TOKEN validation - text: "${text}"`); const isTransferQuery = /\b(transfer|send).*\b(nft|token)\b/i.test(text); elizaLogger8.info(`TRANSFER_PRIVATE_ERC721_TOKEN validation - isTransferQuery: ${isTransferQuery}`); if (message.content?.tokenAddress && message.content?.recipientAddress && message.content?.tokenId) { elizaLogger8.info(`TRANSFER_PRIVATE_ERC721_TOKEN validation - structured params found`); return true; } const addressMatches = text.match(/0x[a-fA-F0-9]{40}/g); const tokenIdMatch = text.match(/token\s*id\s*(\d+)|id\s*(\d+)/i); elizaLogger8.info(`TRANSFER_PRIVATE_ERC721_TOKEN validation - addresses found: ${addressMatches?.length || 0}`); elizaLogger8.info(`TRANSFER_PRIVATE_ERC721_TOKEN validation - tokenId found: ${!!(tokenIdMatch?.[1] || tokenIdMatch?.[2])}`); if (isTransferQuery && addressMatches && addressMatches.length >= 2 && tokenIdMatch) { elizaLogger8.info(`TRANSFER_PRIVATE_ERC721_TOKEN validation - returning true`); return true; } elizaLogger8.info(`TRANSFER_PRIVATE_ERC721_TOKEN validation - returning false`); return false; } catch (error) { elizaLogger8.error(`TRANSFER_PRIVATE_ERC721_TOKEN validation error:`, error); return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger8.info("Starting TRANSFER_PRIVATE_ERC721_TOKEN action"); let params; if (message.content?.tokenAddress && message.content?.recipientAddress && message.content?.tokenId) { params = TransferPrivateERC721Schema.parse(message.content); } else { const text = message.content?.text || ""; const addressMatches = text.match(/0x[a-fA-F0-9]{40}/g); const tokenIdMatch = text.match(/token\s*id\s*(\d+)|id\s*(\d+)/i); if (!addressMatches || addressMatches.length < 2 || !tokenIdMatch) { throw new Error("Could not extract required parameters from message"); } params = { tokenAddress: addressMatches[0], recipientAddress: addressMatches[1], tokenId: tokenIdMatch[1] || tokenIdMatch[2], useSafeTransfer: false }; } const walletProvider = initCotiWalletProvider(runtime); const wallet = walletProvider.getWallet(); const fromAddress = walletProvider.getAddress(); const contract = new ethers8.Contract( params.tokenAddress, ERC721_ABI3, wallet ); elizaLogger8.info(`Transferring NFT token ${params.tokenId} from ${fromAddress} to ${params.recipientAddress}`); const owner = await contract.ownerOf(params.tokenId); if (owner.toLowerCase() !== fromAddress.toLowerCase()) { throw new Error(`You don't own token ${params.tokenId}. Current owner: ${owner}`); } const transferMethod = params.useSafeTransfer ? "safeTransferFrom" : "transferFrom"; const tx = await contract[transferMethod]( fromAddress, params.recipientAddress, params.tokenId, { gasLimit: params.gasLimit || 3e5 } ); const receipt = await tx.wait(); const responseText = `Successfully transferred private ERC721 NFT! Token ID: ${params.tokenId} Contract: ${params.tokenAddress} From: ${fromAddress} To: ${params.recipientAddress} Transaction Hash: ${tx.hash}`; if (callback) { await callback({ text: responseText, content: { success: true, tokenId: params.tokenId, tokenAddress: params.tokenAddress, from: fromAddress, to: params.recipientAddress, transactionHash: tx.hash } }); } return { text: responseText, success: true, data: { tokenId: params.tokenId, tokenAddress: params.tokenAddress, from: fromAddress, to: params.recipientAddress, transactionHash: tx.hash } }; } catch (error) { elizaLogger8.error("Error in TRANSFER_PRIVATE_ERC721_TOKEN action:", error); const errorMessage = `Failed to transfer private ERC721 token: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Transfer NFT token ID 123 to bob" } }, { name: "{{user2}}", content: { text: "I'll transfer that NFT to the specified address.", action: "TRANSFER_PRIVATE_ERC721_TOKEN" } } ] ] }; // src/actions/getPrivateErc721TokenUri.ts import { elizaLogger as elizaLogger9 } from "@elizaos/core"; import { ethers as ethers9 } from "ethers"; import { z as z5 } from "zod"; var GetPrivateERC721TokenURISchema = z5.object({ tokenAddress: z5.string().describe("ERC721 token contract address"), tokenId: z5.string().describe("ID of the NFT token") }); var ERC721_ABI4 = [ "function tokenURI(uint256 tokenId) view returns (string)", "function ownerOf(uint256 tokenId) view returns (address)", "function name() view returns (string)", "function symbol() view returns (string)" ]; var getPrivateErc721TokenUriAction = { name: "GET_PRIVATE_ERC721_TOKEN_URI", similes: [ "GET_TOKEN_URI", "GET_NFT_URI", "GET_METADATA_URI", "GET_NFT_METADATA", "SHOW_TOKEN_URI", "TOKEN_METADATA" ], description: "Get the metadata URI for a private ERC721 NFT token on the COTI blockchain.", validate: async (runtime, message) => { try { const text = message.content?.text || ""; elizaLogger9.info(`GET_PRIVATE_ERC721_TOKEN_URI validation - text: "${text}"`); const isUriQuery = /\b(uri|metadata|get.*metadata|show.*uri)\b/i.test(text); elizaLogger9.info(`GET_PRIVATE_ERC721_TOKEN_URI validation - isUriQuery: ${isUriQuery}`); if (message.content?.tokenAddress && message.content?.tokenId) { elizaLogger9.info(`GET_PRIVATE_ERC721_TOKEN_URI validation - structured params found`); return true; } const addressMatch = text.match(/0x[a-fA-F0-9]{40}/); const tokenIdMatch = text.match(/token\s*id\s*(\d+)|id\s*(\d+)/i); elizaLogger9.info(`GET_PRIVATE_ERC721_TOKEN_URI validation - address found: ${!!addressMatch}`); elizaLogger9.info(`GET_PRIVATE_ERC721_TOKEN_URI validation - tokenId found: ${!!(tokenIdMatch?.[1] || tokenIdMatch?.[2])}`); if (isUriQuery && addressMatch && tokenIdMatch) { elizaLogger9.info(`GET_PRIVATE_ERC721_TOKEN_URI validation - returning true`); return true; } elizaLogger9.info(`GET_PRIVATE_ERC721_TOKEN_URI validation - returning false`); return false; } catch (error) { elizaLogger9.error(`GET_PRIVATE_ERC721_TOKEN_URI validation error:`, error); return false; } }, handler: async (runtime, message, state, options = {}, callback) => { try { elizaLogger9.info("Starting GET_PRIVATE_ERC721_TOKEN_URI action"); let params; if (message.content?.tokenAddress && message.content?.tokenId) { params = GetPrivateERC721TokenURISchema.parse(message.content); } else { const text = message.content?.text || ""; const addressMatch = text.match(/0x[a-fA-F0-9]{40}/); const tokenIdMatch = text.match(/token\s*id\s*(\d+)|id\s*(\d+)/i); if (!addressMatch || !tokenIdMatch) { throw new Error("Could not extract required parameters from message"); } params = { tokenAddress: addressMatch[0], tokenId: tokenIdMatch[1] || tokenIdMatch[2] }; } const walletProvider = initCotiWalletProvider(runtime); const provider = walletProvider.getProvider(); const contract = new ethers9.Contract( params.tokenAddress, ERC721_ABI4, provider ); elizaLogger9.info(`Getting token URI for token ${params.tokenId} from contract ${params.tokenAddress}`); const tokenUri = await contract.tokenURI(params.tokenId); const owner = await contract.ownerOf(params.tokenId); let name = "Unknown"; let symbol = "Unknown"; try { name = await contract.name(); symbol = await contract.symbol(); } catch (e) { elizaLogger9.warn("Could not get contract name/symbol:", e); } const responseText = `Private ERC721 Token Information: Contract: ${name} (${symbol}) Contract Address: ${params.tokenAddress} Token ID: ${params.tokenId} Owner: ${owner} Metadata URI: ${tokenUri}`; if (callback) { await callback({ text: responseText, content: { success: true, tokenAddress: params.tokenAddress, tokenId: params.tokenId, tokenUri, owner, contractName: name, contractSymbol: symbol } }); } return { text: responseText, success: true, data: { tokenAddress: params.tokenAddress, tokenId: params.tokenId, tokenUri, owner, contractName: name, contractSymbol: symbol } }; } catch (error) { elizaLogger9.error("Error in GET_PRIVATE_ERC721_TOKEN_URI action:", error); const errorMessage = `Failed to get private ERC721 token URI: ${error instanceof Error ? error.message : String(error)}`; if (callback) { await callback({ text: errorMessage, content: { success: false, error: errorMessage } }); } return { text: errorMessage, success: false, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Get the metadata URI for NFT token 456" } }, { name: "{{user2}}", content: { text: "I'll retrieve and decrypt the private metadata URI for that NFT.", action: "GET_PRIVATE_ERC721_TOKEN_URI" } } ] ] }; // src/actions/deployPrivateErc20Contract.ts import { elizaLogger as elizaLogger10 } from "@elizaos/core"; import { ether