UNPKG

cotiv2-mcp

Version:

> A plug-and-play MCP tool server to **send COTI**, **transfer BEP-20 tokens**, **deploy tokens**, and **interact with smart contracts** on the **COTI v2 Network (COTI)** — built for **Claude Desktop**, **AI agents**, and **developers.**

228 lines (212 loc) 9.14 kB
import { exec } from "child_process"; import fs from "fs"; import path from "path"; import os from "os"; import { decryptPrivateKey } from "./PrivateAES.js"; import { privateKeyToAccount } from "viem/accounts"; import { publicClient } from "./config.js"; import { supportedChain } from "./chains/index.js"; import { getStorage } from "./lib/storage.js"; const platform = os.platform(); export function buildTxUrl(txHash) { if (!txHash) { return undefined; } const txUrl = `${supportedChain.blockExplorers.default.url}/tx/${txHash}`; return txUrl; } export async function checkTransactionHash(txHash) { const txReceipt = await publicClient.waitForTransactionReceipt({ hash: txHash, retryCount: 300, retryDelay: 100, }); const txUrl = `${supportedChain.blockExplorers.default.url}/tx/${txHash}`; if (txReceipt.status !== "success") { throw new Error(`Please check the transaction results on block explorer, ${txUrl}`); } return txUrl; } export function bigIntReplacer(key, value) { return typeof value === "bigint" ? value.toString() : value; } export async function getPassword(isRetry) { const passwordResp = await showInputBoxWithTerms(isRetry); if (!passwordResp.value) { throw new Error("You did not enter a password."); } if (passwordResp.value.length != 8) { throw new Error("The password must be 8 characters long"); } const password = passwordResp.value; const storage = await getStorage(); const WALLET_PRIVATE_KEY = storage.WALLET_PRIVATE_KEY; if (!WALLET_PRIVATE_KEY) { throw new Error("WALLET_PRIVATE_KEY is not defined"); } const pk = await decryptPrivateKey(WALLET_PRIVATE_KEY, password); const account = privateKeyToAccount(pk); const address = storage.WALLET_ADDRESS; if (!address) { throw new Error("WALLET_ADDRESS is not defined"); } if (account.address != address) { return await getPassword(true); } return passwordResp; } export function showInputBoxWithTerms(isRetry) { let message = "Enter your Wallet Password:"; if (isRetry) { message = "Wrong password, please try again:"; } return new Promise((resolve, reject) => { switch (platform) { case "darwin": // For macOS, we use AppleScript to show a dialog with both input and checkbox // The AppleScript is more complex but allows for a better UX if (isRetry) { message = "❌" + message; } const appleScript = ` tell application "System Events" set userPassword to "" set buttonPressed to "" repeat try set userInput to display dialog "${message}" default answer "" with hidden answer buttons {"cancel", "confirm"} default button "confirm" with icon note set userPassword to text returned of userInput set buttonPressed to button returned of userInput if buttonPressed is "cancel" then exit repeat end if if length of userPassword is 8 then exit repeat end if display dialog "Password must be exactly 8 characters." buttons {"confirm"} default button "confirm" with icon caution on error -- Handle any errors (like when user clicks the red close button) exit repeat end try end repeat if buttonPressed is not "cancel" then set agreeToTerms to button returned of (display dialog "🔒 You will stay signed in for the next hour." buttons {"no", "yes"} default button "no" with icon caution) return userPassword & "============" & agreeToTerms else return "canceled" end if end tell `; exec(`osascript -e '${appleScript}'`, (error, stdout, stderr) => { if (error) { // User cancelled if (error.code === 1 || error.code === 255) { resolve({ value: null, agreed: false }); } else { reject(error); } return; } if (stdout.trim() === "canceled") { reject(new Error("Please enter the password before using ❕")); return; } const [password, agree] = stdout.trim().split("============"); resolve({ value: password, agreed: agree === "yes", }); }); break; case "win32": const winCommand = ` Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing $form = New-Object System.Windows.Forms.Form $form.Text = 'wallet password' $form.Size = New-Object System.Drawing.Size(450,300) $form.StartPosition = 'CenterScreen' $label = New-Object System.Windows.Forms.Label $label.Location = New-Object System.Drawing.Point(10,20) $label.Size = New-Object System.Drawing.Size(380,40) $label.Text = '${message}' $form.Controls.Add($label) # User input label $userLabel = New-Object System.Windows.Forms.Label $userLabel.Location = New-Object System.Drawing.Point(10,70) $userLabel.Size = New-Object System.Drawing.Size(150,20) $userLabel.Text = 'Input Password:' $form.Controls.Add($userLabel) # User input textbox $passwordTextBox = New-Object System.Windows.Forms.TextBox $passwordTextBox.Location = New-Object System.Drawing.Point(160,70) $passwordTextBox.Size = New-Object System.Drawing.Size(250,20) $passwordTextBox.PasswordChar = '*' $form.Controls.Add($passwordTextBox) # Error message label $errorLabel = New-Object System.Windows.Forms.Label $errorLabel.Location = New-Object System.Drawing.Point(160,95) $errorLabel.Size = New-Object System.Drawing.Size(250,20) $errorLabel.ForeColor = [System.Drawing.Color]::Red $errorLabel.Text = '' $form.Controls.Add($errorLabel) $checkbox = New-Object System.Windows.Forms.CheckBox $checkbox.Location = New-Object System.Drawing.Point(10,130) $checkbox.Size = New-Object System.Drawing.Size(350,20) $checkbox.Text = 'You will stay signed in for the next hour.' $form.Controls.Add($checkbox) $button = New-Object System.Windows.Forms.Button $button.Location = New-Object System.Drawing.Point(175,190) $button.Size = New-Object System.Drawing.Size(100,30) $button.Text = 'Confirm' $button.Add_Click({ # Validate password length if ($passwordTextBox.Text.Length -ne 8) { $errorLabel.Text = 'Password must be exactly 8 characters.' } else { $form.DialogResult = [System.Windows.Forms.DialogResult]::OK $form.Close() } }) $form.Controls.Add($button) $form.AcceptButton = $button $form.Add_Shown({$form.Activate()}) [void]$form.ShowDialog() if ($form.DialogResult -eq [System.Windows.Forms.DialogResult]::OK) { $result = @{ agreed = $checkbox.Checked value = $passwordTextBox.Text } $jsonResult = ConvertTo-Json -InputObject $result Write-Output $jsonResult } exit 0 `; const tempScriptPath = path.join(".", "terms_form.ps1"); fs.writeFileSync(tempScriptPath, winCommand); exec(`powershell -ExecutionPolicy Bypass -File "${tempScriptPath}"`, (error, stdout, stderr) => { fs.unlinkSync(tempScriptPath); if (error && error.code !== 1) { resolve({ value: null, agreed: false, }); return; } if (!stdout) { reject(new Error("Please enter the password before using ❕")); return; } const stdoutJSON = JSON.parse(stdout); resolve({ value: stdoutJSON.value, agreed: stdoutJSON.agreed, }); }); break; default: reject(new Error(`Unsupported platform and command-line input is not available: ${platform}`)); } }); }