nano_send
Transfer a specified amount of Nano cryptocurrency from a predefined account to a designated Nano address using the Nano Currency MCP Server tool.
Instructions
Send a specified amount of Nano currency from a predefined account to a destination Nano address.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| amount | Yes | Amount of Nano to send (max 0.05 by default) | |
| destination_address | Yes | Nano address to send the nano to |
Implementation Reference
- nano-currency.js:187-248 (handler)Handler function that executes the nano_send tool: validates inputs, checks balance, generates work, creates send block, processes it via RPC, returns result or error.async function (parameters) { NANO_PRIVATE_KEY_SCHEMA.parse(process.env.NANO_PRIVATE_KEY) try { let amountInRaw = convertNanoToRaw(parameters.amount) let sourceAddress = getAddress() let sourceAddressInfo = await getAccountInfo(sourceAddress) let balanceAfterSend = BigNumber(sourceAddressInfo.balance).minus(amountInRaw).toFixed() if (BigNumber(sourceAddressInfo.balance).lt(amountInRaw)) { throw new Error("Insufficient balance to perform Nano send transaction"); } if (!sourceAddressInfo.frontier) { throw new Error("Source account has no frontier (unopened account)"); } // ----- let work = ( await rpcCall( (process.env.NANO_WORK_GENERATION_URL ? NANO_WORK_GENERATION_URL_KEY : NANO_RPC_URL_KEY), 'work_generate', { hash: sourceAddressInfo.frontier }, 5 * ONE_MINUTE ) ).work z.string({ required_error: `Work is required`, }).refine(work_ => N.validateWork({ work: work_, blockHash: sourceAddressInfo.frontier }), { message: 'Computed Proof-of-Work for Nano transaction is not valid' }).parse(work) // ----- let { block } = N.createBlock(process.env.NANO_PRIVATE_KEY, { representative: sourceAddressInfo.representative, balance: balanceAfterSend, work, link: parameters.destination_address, previous: sourceAddressInfo.frontier }) let processJson = ( await rpcCall( NANO_RPC_URL_KEY, 'process', { json_block: 'true', subtype: 'send', block } ) ) return createTextResponse(JSON.stringify(processJson)) } catch (error) { console.error('[nano_send] Error:', error.message || error); return createErrorResponse(error) } }
- nano-currency.js:168-181 (schema)Zod schema defining input parameters for nano_send: destination_address (validated Nano address) and amount (positive number <= max).const nano_send_parameters = { destination_address: z.string({ required_error: `Destination address is required`, }) .refine(address_ => N.checkAddress(address_), { message: 'Destination address is not valid' }) .describe('Nano address to send the nano to'), amount: z.string({ required_error: `Amount is required`, }) .refine(amount_ => !isNaN(Number(amount_)) && Number(amount_) > 0, { message: 'Amount must be a positive number' }) .transform(amount_ => Number(amount_)) .refine(amount_ => amount_ <= (process.env.NANO_MAX_SEND_AMOUNT || NANO_MAX_SEND_AMOUNT_DEFAULT), { message: 'Maximum send amount exceeded' }) .describe(`Amount of Nano to send (max ${(process.env.NANO_MAX_SEND_AMOUNT || NANO_MAX_SEND_AMOUNT_DEFAULT)} by default)`) }
- nano-currency.js:183-249 (registration)Registration of the nano_send tool using server.tool(), including name, description, schema, and handler function.server.tool( 'nano_send', 'Send a specified amount of Nano currency from a predefined account to a destination Nano address.', nano_send_parameters, async function (parameters) { NANO_PRIVATE_KEY_SCHEMA.parse(process.env.NANO_PRIVATE_KEY) try { let amountInRaw = convertNanoToRaw(parameters.amount) let sourceAddress = getAddress() let sourceAddressInfo = await getAccountInfo(sourceAddress) let balanceAfterSend = BigNumber(sourceAddressInfo.balance).minus(amountInRaw).toFixed() if (BigNumber(sourceAddressInfo.balance).lt(amountInRaw)) { throw new Error("Insufficient balance to perform Nano send transaction"); } if (!sourceAddressInfo.frontier) { throw new Error("Source account has no frontier (unopened account)"); } // ----- let work = ( await rpcCall( (process.env.NANO_WORK_GENERATION_URL ? NANO_WORK_GENERATION_URL_KEY : NANO_RPC_URL_KEY), 'work_generate', { hash: sourceAddressInfo.frontier }, 5 * ONE_MINUTE ) ).work z.string({ required_error: `Work is required`, }).refine(work_ => N.validateWork({ work: work_, blockHash: sourceAddressInfo.frontier }), { message: 'Computed Proof-of-Work for Nano transaction is not valid' }).parse(work) // ----- let { block } = N.createBlock(process.env.NANO_PRIVATE_KEY, { representative: sourceAddressInfo.representative, balance: balanceAfterSend, work, link: parameters.destination_address, previous: sourceAddressInfo.frontier }) let processJson = ( await rpcCall( NANO_RPC_URL_KEY, 'process', { json_block: 'true', subtype: 'send', block } ) ) return createTextResponse(JSON.stringify(processJson)) } catch (error) { console.error('[nano_send] Error:', error.message || error); return createErrorResponse(error) } } )
- nano-currency.js:68-86 (helper)Helper function rpcCall used by nano_send for making RPC calls to Nano node with timeout and error handling.async function rpcCall(envUrl, action, payload, timeout = ONE_MINUTE) { const controller = new AbortController() const timer = setTimeout(() => controller.abort(), timeout) try { const res = await fetch(process.env[envUrl], { ...FETCH_COMMON, signal: controller.signal, body: JSON.stringify({ action, ...payload }), }) if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`) const json = await res.json() if (json.error) throw new Error(`RPC Error: ${json.error}`) return json } catch (error) { throw new Error(`[${envUrl}] ${error.message}`) } finally { clearTimeout(timer) } }
- nano-currency.js:152-163 (helper)Helper function getAccountInfo used by nano_send to fetch source account details before sending.async function getAccountInfo(address) { return ( await rpcCall( NANO_RPC_URL_KEY, 'account_info', { account: address, representative: 'true' } ) ) }