UNPKG

plugin-coti

Version:

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

1,021 lines (1,013 loc) 127 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"; import { Wallet, getDefaultProvider, CotiNetwork } from "@coti-io/coti-ethers"; var CotiWalletProvider = class { wallet; provider; runtime; aesKey; constructor(runtime, config) { this.runtime = runtime; this.provider = getDefaultProvider(CotiNetwork.Testnet); const privateKey = config.privateKey || process.env.COTI_PRIVATE_KEY; if (!privateKey) { throw new Error("COTI private key not provided"); } this.aesKey = config.aesKey || process.env.COTI_AES_KEY || ""; if (!this.aesKey) { elizaLogger.warn("COTI AES key not provided. Private token operations may not work properly."); } this.wallet = new Wallet(privateKey, this.provider); if (this.aesKey) { this.wallet.setAesKey(this.aesKey); } elizaLogger.info(`COTI Wallet initialized with address: ${this.wallet.address}`); } getAddress() { return this.wallet.address; } getAesKey() { return this.aesKey; } async getBalance(address) { const targetAddress = address || this.wallet.address; return await this.provider.getBalance(targetAddress); } async transfer(to, amount) { const amountWei = ethers.utils.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.utils.parseEther(amount); const gasEstimate = await this.wallet.estimateGas({ to, value: amountWei }); return ethers.BigNumber.from(gasEstimate.toString()); } 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.utils.formatEther(balance)} COTI`, values: { address, balance: ethers.utils.formatEther(balance), balanceWei: balance.toString() }, data: { address, balance: ethers.utils.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.utils.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.utils.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.utils.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.utils.formatEther(balance)} COTI`; if (callback) { await callback({ text: responseText, content: { success: true, accountAddress, balance: ethers2.utils.formatEther(balance), balanceWei: balance.toString() } }); } return { text: responseText, success: true, data: { accountAddress, balance: ethers2.utils.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.utils.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.utils.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.utils.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.utils.parseEther(amount); if (balance.lt(amountWei)) { throw new Error(`Insufficient balance. Available: ${ethers3.utils.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.utils.formatEther(balance), balanceWei: balance.toString(), transactionCount, network: { name: network.name, chainId: network.chainId.toString() } }; const responseText = `COTI Account Information: Address: ${address} Balance: ${ethers4.utils.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.getGasPrice(); let walletInfo = { address, network: { name: network.name, chainId: network.chainId.toString(), blockNumber, gasPrice: ethers5.utils.formatUnits(gasPrice, "gwei") } }; let responseText = `COTI Wallet Information: Address: ${address} Network: ${network.name} (Chain ID: ${network.chainId}) Current Block: ${blockNumber} Gas Price: ${ethers5.utils.formatUnits(gasPrice, "gwei")} gwei`; if (includeBalance) { const balance = await walletProvider.getBalance(); const transactionCount = await provider.getTransactionCount(address); walletInfo.balance = ethers5.utils.formatEther(balance); walletInfo.balanceWei = balance.toString(); walletInfo.transactionCount = transactionCount; responseText += ` Balance: ${ethers5.utils.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 { inputs: [ { internalType: "string", name: "name_", type: "string" }, { internalType: "string", name: "symbol_", type: "string" } ], stateMutability: "nonpayable", type: "constructor" }, // Standard ERC721 functions "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)", // COTI private mint function { inputs: [ { internalType: "address", name: "to", type: "address" }, { components: [ { components: [ { internalType: "ctUint64[]", name: "value", type: "uint256[]" } ], internalType: "struct ctString", name: "ciphertext", type: "tuple" }, { internalType: "bytes[]", name: "signature", type: "bytes[]" } ], internalType: "struct itString", name: "itTokenURI", type: "tuple" } ], name: "mint", outputs: [], stateMutability: "nonpayable", type: "function" }, // Token URI function { inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }], name: "tokenURI", outputs: [ { components: [ { internalType: "ctUint64[]", name: "value", type: "uint256[]" } ], internalType: "struct ctString", name: "", type: "tuple" } ], stateMutability: "view", type: "function" } ]; var ERC721_BYTECODE = "0x60806040523480156200001157600080fd5b5060405162001eeb38038062001eeb833981016040819052620000349162000123565b818160006200004483826200021c565b5060016200005382826200021c565b5050505050620002e8565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200008657600080fd5b81516001600160401b0380821115620000a357620000a36200005e565b604051601f8301601f19908116603f01168101908282118183101715620000ce57620000ce6200005e565b81604052838152602092508683858801011115620000eb57600080fd5b600091505b838210156200010f5785820183015181830184015290820190620000f0565b600093810190920192909252949350505050565b600080604083850312156200013757600080fd5b82516001600160401b03808211156200014f57600080fd5b6200015d8683870162000074565b935060208501519150808211156200017457600080fd5b50620001838582860162000074565b9150509250929050565b600181811c90821680620001a257607f821691505b602082108103620001c357634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200021757600081815260208120601f850160051c81016020861015620001f25750805b601f850160051c820191505b818110156200021357828155600101620001fe565b5050505b505050565b81516001600160401b038111156200023857620002386200005e565b62000250816200024984546200018d565b84620001c9565b602080601f8311600181146200028857600084156200026f5750858301515b600019600386901b1c1916600185901b17855562000213565b600085815260208120601f198616915b82811015620002b95788860151825594840194600190910190840162000298565b5085821015620002d85787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b611bf380620002f86000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80636352211e11610097578063a22cb46511610066578063a22cb465146101f0578063b88d4fde14610203578063c87b56dd14610216578063e985e9c51461023657600080fd5b80636352211e146101af57806368862e1b146101c257806370a08231146101d557806395d89b41146101e857600080fd5b8063095ea7b3116100d3578063095ea7b31461016257806318160ddd1461017757806323b872dd1461018957806342842e0e1461019c57600080fd5b806301ffc9a7146100fa57806306fdde0314610122578063081812fc14610137575b600080fd5b61010d61010836600461153e565b610272565b60405190151581526020015b60405180910390f35b61012a6102ad565b60405161011991906115a1565b61014a6101453660046115b4565b61033f565b6040516001600160a01b039091168152602001610119565b6101756101703660046115e9565b610368565b005b6007545b604051908152602001610119565b610175610197366004611613565b610377565b6101756101aa366004611613565b610420565b61014a6101bd3660046115b4565b610440565b6101756101d036600461164f565b61044b565b61017b6101e33660046116a4565b610480565b61012a6104e1565b6101756101fe3660046116bf565b6104f0565b6101756102113660046117f3565b6104fb565b6102296102243660046115b4565b610512565b604051610119919061185b565b61010d6102443660046118ab565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b60007fffffffff00000000000000000000000000000000000000000000000000000000821615806102a757506102a78261058f565b92915050565b6060600080546102bc906118de565b80601f01602080910402602001604051908101604052809291908181526020018280546102e8906118de565b80156103355780601f1061030a57610100808354040283529160200191610335565b820191906000526020600020905b81548152906001019060200180831161031857829003601f168201915b5050505050905090565b600061034a82610626565b506000828152600460205260409020546001600160a01b03166102a7565b610373828233610678565b5050565b6001600160a01b0382166103a657604051633250574960e11b8152600060048201526024015b60405180910390fd5b60006103b3838333610685565b9050836001600160a01b0316816001600160a01b03161461041a576040517f64283d7b0000000000000000000000000000000000000000000000000000000081526001600160a01b038086166004830152602482018490528216604482015260640161039d565b50505050565b61043b838383604051806020016040528060008152506104fb565b505050565b60006102a782610626565b600754610458838261071b565b610463338284610799565b600160076000828254610476919061192e565b9091555050505050565b60006001600160a01b0382166104c5576040517f89c62b640000000000000000000000000000000000000000000000000000000081526000600482015260240161039d565b506001600160a01b031660009081526003602052604090205490565b6060600180546102bc906118de565b6103733383836107bb565b610506848484610377565b61041a84848484610891565b604080516020808201835260608252600084815260068252839020835160019091018054808402830186018652928201838152939491939092849284919084018282801561057f57602002820191906000526020600020905b81548152602001906001019080831161056b575b5050505050815250509050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f80ac58cd0000000000000000000000000000000000000000000000000000000014806102a757507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146102a7565b6000818152600260205260408120546001600160a01b0316806102a7576040517f7e2732890000000000000000000000000000000000000000000000000000000081526004810184905260240161039d565b61043b8383836001610a04565b60008281526006602090815260408083208151815480850282018401845293810184815285946106f49492939284929184918401828280156106e657602002820191906000526020600020905b8154815260200190600101908083116106d2575b505050505081525050610b5a565b90506000610703868686610c2a565b90506107128686846000610d3b565b95945050505050565b6001600160a01b03821661074557604051633250574960e11b81526000600482015260240161039d565b600061075383836000610685565b90506001600160a01b0381161561043b576040517f73c6ac6e0000000000000000000000000000000000000000000000000000000081526000600482015260240161039d565b60006107ac6107a7836119ea565b610e2a565b905061041a8484836001610d3b565b6001600160a01b038216610806576040517f5b08ba180000000000000000000000000000000000000000000000000000000081526001600160a01b038316600482015260240161039d565b6001600160a01b0383811660008181526005602090815260408083209487168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6001600160a01b0383163b1561041a576040517f150b7a020000000000000000000000000000000000000000000000000000000081526001600160a01b0384169063150b7a02906108ec903390889087908790600401611ada565b6020604051808303816000875af1925050508015610927575060408051601f3d908101601f1916820190925261092491810190611b16565b60015b610990573d808015610955576040519150601f19603f3d011682016040523d82523d6000602084013e61095a565b606091505b50805160000361098857604051633250574960e11b81526001600160a01b038516600482015260240161039d565b805181602001fd5b7fffffffff0000000000000000000000000000000000000000000000000000000081167f150b7a0200000000000000000000000000000000000000000000000000000000146109fd57604051633250574960e11b81526001600160a01b038516600482015260240161039d565b5050505050565b8080610a1857506001600160a01b03821615155b15610b12576000610a2884610626565b90506001600160a01b03831615801590610a545750826001600160a01b0316816001600160a01b031614155b8015610a8657506001600160a01b0380821660009081526005602090815260408083209387168352929052205460ff16155b15610ac8576040517fa9fbf51f0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161039d565b8115610b105783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b5050600090815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6040805160208101909152606081528151516040805160208101909152600090808367ffffffffffffffff811115610b9457610b946116f0565b604051908082528060200260200182016040528015610bbd578160200160208202803683370190505b509052905060005b82811015610c2257610bf385600001518281518110610be657610be6611b33565b6020026020010151610faa565b8251805183908110610c0757610c07611b33565b6020908102919091010152610c1b81611b49565b9050610bc5565b509392505050565b6000828152600260205260408120546001600160a01b0390811690831615610c5757610c57818486611047565b6001600160a01b03811615610c9557610c74600085600080610a04565b6001600160a01b038116600090815260036020526040902080546000190190555b6001600160a01b03851615610cc4576001600160a01b0385166000908152600360205260409020805460010190555b60008481526002602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b6000610d4684610440565b6001600160a01b031603610d89576040517f868439130000000000000000000000000000000000000000000000000000000081526004810184905260240161039d565b6000610d9583866110dd565b90508115610df457600084815260066020908152604090912082518051805185948492610dc892849291909101906114ad565b5050506020828101518051805191926001850192610de992849201906114ad565b5050509050506109fd565b60208082015160008681526006835260409020815180519293600190920192610e2092849201906114ad565b5050505050505050565b604080516020810190915260608152602082015151825151518114610eab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f4d50435f434f52453a20494e56414c49445f494e5055545f5445585400000000604482015260640161039d565b600060405180602001604052808367ffffffffffffffff811115610ed157610ed16116f0565b604051908082528060200260200182016040528015610efa578160200160208202803683370190505b50905260408051808201909152600081526060602082015290915060005b83811015610fa057855151805182908110610f3557610f35611b33565b6020908102919091018101518352860151805182908110610f5857610f58611b33565b60200260200101518260200181905250610f7182611128565b8351805183908110610f8557610f85611b33565b6020908102919091010152610f9981611b49565b9050610f18565b5090949350505050565b6000606463d2c135e560045b60f81b846040518363ffffffff1660e01b81526004016110049291907fff00000000000000000000000000000000000000000000000000000000000000929092168252602082015260400190565b6020604051808303816000875af1158015611023573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102a79190611b63565b611052838383611191565b61043b576001600160a01b038316611099576040517f7e2732890000000000000000000000000000000000000000000000000000000081526004810182905260240161039d565b6040517f177e802f0000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024810182905260440161039d565b6111086040805160608082018352818301818152825282516020818101909452908152909182015290565b61111183611217565b815261111d83836112df565b602082015292915050565b805160208201516040517fe4f36e1000000000000000000000000000000000000000000000000000000000815260009260649263e4f36e1092611004927f0400000000000000000000000000000000000000000000000000000000000000929091600401611b7c565b60006001600160a01b0383161580159061120f5750826001600160a01b0316846001600160a01b031614806111eb57506001600160a01b0380851660009081526005602090815260408083209387168352929052205460ff165b8061120f57506000828152600460205260409020546001600160a01b038481169116145b949350505050565b6040805160208101909152606081528151516040805160208101909152600090808367ffffffffffffffff811115611251576112516116f0565b60405190808252806020026020018201604052801561127a578160200160208202803683370190505b509052905060005b82811015610c22576112b0856000015182815181106112a3576112a3611b33565b60200260200101516113b1565b82518051839081106112c4576112c4611b33565b60209081029190910101526112d881611b49565b9050611282565b6040805160208101909152606081528251516040805160208101909152600090808367ffffffffffffffff811115611319576113196116f0565b604051908082528060200260200182016040528015611342578160200160208202803683370190505b509052905060005b828110156113a8576113798660000151828151811061136b5761136b611b33565b6020026020010151866113c1565b825180518390811061138d5761138d611b33565b60209081029190910101526113a181611b49565b905061134a565b50949350505050565b6000606463c50c9c026004610fb6565b60408051606083901b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208201528151601481830301815260348201928390527f3c6f0e6800000000000000000000000000000000000000000000000000000000909252600091606491633c6f0e6891611463917f0400000000000000000000000000000000000000000000000000000000000000918891603801611b7c565b6020604051808303816000875af1158015611482573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a69190611b63565b9392505050565b8280548282559060005260206000209081019282156114e8579160200282015b828111156114e85782518255916020019190600101906114cd565b506114f49291506114f8565b5090565b5b808211156114f457600081556001016114f9565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461153b57600080fd5b50565b60006020828403121561155057600080fd5b81356114a68161150d565b6000815180845260005b8181101561158157602081850181015186830182015201611565565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006114a6602083018461155b565b6000602082840312156115c657600080fd5b5035919050565b80356001600160a01b03811681146115e457600080fd5b919050565b600080604083850312156115fc57600080fd5b611605836115cd565b946020939093013593505050565b60008060006060848603121561162857600080fd5b611631846115cd565b925061163f602085016115cd565b9150604084013590509250925092565b6000806040838503121561166257600080fd5b61166b836115cd565b9150602083013567ffffffffffffffff81111561168757600080fd5b83016040818603121561169957600080fd5b809150509250929050565b6000602082840312156116b657600080fd5b6114a6826115cd565b600080604083850312156116d257600080fd5b6116db836115cd565b91506020830135801515811461169957600080fd5b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715611729576117296116f0565b60405290565b6040516020810167ffffffffffffffff81118282101715611729576117296116f0565b604051601f8201601f1916810167ffffffffffffffff8111828210171561177b5761177b6116f0565b604052919050565b600082601f83011261179457600080fd5b813567ffffffffffffffff8111156117ae576117ae6116f0565b6117c16020601f19601f84011601611752565b8181528460208386010111156117d657600080fd5b816020850160208301376000918101602001919091529392505050565b6000806000806080858703121561180957600080fd5b611812856115cd565b9350611820602086016115cd565b925060408501359150606085013567ffffffffffffffff81111561184357600080fd5b61184f87828801611783565b91505092959194509250565b6020808252825182820182905280516040840181905260009291820190839060608601905b808310156118a05783518252928401926001929092019190840190611880565b509695505050505050565b600080604083850312156118be57600080fd5b6118c7836115cd565b91506118d5602084016115cd565b90509250929050565b600181811c908216806118f257607f821691505b60208210810361191257634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b808201808211156102a7576102a7611918565b600067ffffffffffffffff82111561195b5761195b6116f0565b5060051b60200190565b600082601f83011261197657600080fd5b8135602061198b61198683611941565b611752565b82815260059290921b840181019181810190868411156119aa57600080fd5b8286015b848110156118a057803567ffffffffffffffff8111156119ce5760008081fd5b6119dc8986838b0101611783565b8452509183019183016119ae565b6000604082360312156119fc57600080fd5b611a04611706565b823567ffffffffffffffff80821115611a1c57600080fd5b81850191506020808336031215611a3257600080fd5b611a3a61172f565b833583811115611a4957600080fd5b939093019236601f850112611a5d57600080fd5b8335611a6b61198682611941565b81815260059190911b85018301908381019036831115611a8a57600080fd5b958401955b82871015611aa857863582529584019590840190611a8f565b83525050845285810135925081831115611ac157600080fd5b611acd36848801611965565b9084015250909392505050565b60006001600160a01b03808716835280861660208401525083604083015260806060830152611b0c608083018461155b565b9695505050505050565b600060208284031215611b2857600080fd5b81516114a68161150d565b634e487b7160e01b600052603260045260246000fd5b60006000198203611b5c57611b5c611918565b5060010190565b600060208284031215611b7557600080fd5b5051919050565b7fff0000000000000000000000000000000000000000000000000000000000000084168152826020820152606060408201526000610712606083018461155b56fea264697066735822122032cc9569a57d1b107f163af7fd375b58451772f82279883207242aa0372e125964736f6c63430008130033"; 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[d]?|called)\s*['""]?([^'",]+)['""]?/i); const symbolMatch = text.match(/(?:symbol|ticker)\s*['""]?([^'",\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] || symbolMatch?.[3] || "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[d]?|called)\s*['""]?([^'",]+)['""]?/i); const symbolMatch = text.match(/(?:symbol|ticker)\s*['""]?([^'",\s]+)['""]?|\(([^)]+)\)/i); elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT handler - nameMatch: ${nameMatch?.[1] || "none"}`); elizaLogger6.info(`DEPLOY_PRIVATE_ERC721_CONTRACT handler - symbolMatch: ${symbolMatch?.[1] || symbolMatch?.[2] || "none"}`); if (!nameMatch || !symbolMatch) { throw new Error("Could not extract name and symbol from message"); } params = { name: nameMatch[1].trim(), symbol: (symbolMatch[1] || symbolMatch[2]).trim() }; } const walletProvider = initCotiWalletProvider(runtime); const wallet = walletProvider.getWallet(); const factory = new ethers6.ContractFactory( ERC721_ABI, ERC721_BYTECODE ); const deployTx = factory.getDeployTransaction( params.name, params.symbol ); const tx = await wallet.sendTransaction({ data: deployTx.data, gasLimit: params.gasLimit || 2e6 }); const receipt = await tx.wait(); if (!receipt) { throw new Error("Deployment transaction failed to be mined"); } const contractAddress = receipt.contractAddress; if (!contractAddress) { throw new Error("Could not determine contract address from transaction receipt"); } const responseText = `Successfully deployed private ERC721 contract: ${params.name} (${params.symbol}) Contract Address: ${contractAddress} Transaction Hash: ${tx.hash}`; if (callback) { await callback({ text: responseText, content: { success: true, contractAddress, transactionHash: tx.hash, name: params.name, symbol: params.symbol } }); } return { text: responseText, success: true, data: { contractAddress, transactionHash: tx.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"; import { buildStringInputText } from "@coti-io/coti-sdk-typescript"; 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 = [ // COTI private mint function { inputs: [ { internalType: "address", name: "to", type: "address" }, { components: [ { components: [ { internalType: "ctUint64[]", name: "value", type: "uint256[]" } ], internalType: "struct ctString", name: "ciphertext", type: "tuple" }, { internalType: "bytes[]", name: "signature", type: "bytes[]" } ], internalType: "struct itString", name: "itTokenURI", type: "tuple" } ], name: "mint", outputs: [], stateMutability: "nonpayable", type: "function" }, // Standard view functions "function tokenURI(uint256 tokenId) view returns (tuple(uint256[]))", "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", "MINT_PRIVATE_ERC721_TOKEN", "MINT_PRIVATE_ERC721", "CREATE_PRIVATE_NFT" ], 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(`\u{1F50D} MINT_PRIVATE_ERC721_TOKEN validation called!`); 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("\u{1F680} MINT_PRIVATE_ERC721_TOKEN handler called!"); elizaLogger7.info(`Message content: ${JSON.stringify(message.content, null, 2)}`); 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 provider = new ethers7.providers.JsonRpcProvider(process.env.COTI_RPC_URL || "https://testnet.coti.io/rpc"); const contractReader = new ethers7.Contract( params.tokenAddress, ERC721_ABI2, provider ); const contractWriter = new ethers7.Contract( params.tokenAddress, ERC721_ABI2, wallet ); elizaLogger7.info(`Minting NFT to ${params.toAddress} with URI: ${params.tokenUri}`); const mintSelector = contractReader.interface.getSighash("mint(address,(bytes,bytes[]))"); elizaLogger7.info(`Mint function selector: ${mintSelector}`); const aesKey = walletProvider.getAesKey(); if (!aesKey) { throw new Error("AES key not found in wallet provider. Cannot encrypt token URI for private NFT minting."); } elizaLogger7.info("Encrypting token URI for private NFT minting..."); const encryptedInputText = buildStringInputText( params.tokenUri, { wallet, userKey: aesKey }, params.tokenAddress, mintSelector ); elizaLogger7.info("Token URI encrypted successfully. Proceeding with mint transaction..."); const tx = await contractWriter.mint( params.toAddress, encryptedInputText, { 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) { elizaLogge