zano-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +195 -0
  2. package/dist/clients/daemon.d.ts +6 -0
  3. package/dist/clients/daemon.d.ts.map +1 -0
  4. package/dist/clients/daemon.js +30 -0
  5. package/dist/clients/daemon.js.map +1 -0
  6. package/dist/clients/trade.d.ts +8 -0
  7. package/dist/clients/trade.d.ts.map +1 -0
  8. package/dist/clients/trade.js +33 -0
  9. package/dist/clients/trade.js.map +1 -0
  10. package/dist/clients/wallet.d.ts +8 -0
  11. package/dist/clients/wallet.d.ts.map +1 -0
  12. package/dist/clients/wallet.js +45 -0
  13. package/dist/clients/wallet.js.map +1 -0
  14. package/dist/config.d.ts +30 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +42 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +15 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/logger.d.ts +9 -0
  23. package/dist/logger.d.ts.map +1 -0
  24. package/dist/logger.js +28 -0
  25. package/dist/logger.js.map +1 -0
  26. package/dist/prompts/index.d.ts +4 -0
  27. package/dist/prompts/index.d.ts.map +1 -0
  28. package/dist/prompts/index.js +54 -0
  29. package/dist/prompts/index.js.map +1 -0
  30. package/dist/resources/index.d.ts +4 -0
  31. package/dist/resources/index.d.ts.map +1 -0
  32. package/dist/resources/index.js +60 -0
  33. package/dist/resources/index.js.map +1 -0
  34. package/dist/server.d.ts +4 -0
  35. package/dist/server.d.ts.map +1 -0
  36. package/dist/server.js +22 -0
  37. package/dist/server.js.map +1 -0
  38. package/dist/tools/assets/definitions.d.ts +127 -0
  39. package/dist/tools/assets/definitions.d.ts.map +1 -0
  40. package/dist/tools/assets/definitions.js +43 -0
  41. package/dist/tools/assets/definitions.js.map +1 -0
  42. package/dist/tools/assets/handlers.d.ts +21 -0
  43. package/dist/tools/assets/handlers.d.ts.map +1 -0
  44. package/dist/tools/assets/handlers.js +115 -0
  45. package/dist/tools/assets/handlers.js.map +1 -0
  46. package/dist/tools/daemon/definitions.d.ts +139 -0
  47. package/dist/tools/daemon/definitions.d.ts.map +1 -0
  48. package/dist/tools/daemon/definitions.js +55 -0
  49. package/dist/tools/daemon/definitions.js.map +1 -0
  50. package/dist/tools/daemon/handlers.d.ts +30 -0
  51. package/dist/tools/daemon/handlers.d.ts.map +1 -0
  52. package/dist/tools/daemon/handlers.js +274 -0
  53. package/dist/tools/daemon/handlers.js.map +1 -0
  54. package/dist/tools/register.d.ts +4 -0
  55. package/dist/tools/register.d.ts.map +1 -0
  56. package/dist/tools/register.js +93 -0
  57. package/dist/tools/register.js.map +1 -0
  58. package/dist/tools/swap/definitions.d.ts +103 -0
  59. package/dist/tools/swap/definitions.d.ts.map +1 -0
  60. package/dist/tools/swap/definitions.js +28 -0
  61. package/dist/tools/swap/definitions.js.map +1 -0
  62. package/dist/tools/swap/handlers.d.ts +17 -0
  63. package/dist/tools/swap/handlers.d.ts.map +1 -0
  64. package/dist/tools/swap/handlers.js +98 -0
  65. package/dist/tools/swap/handlers.js.map +1 -0
  66. package/dist/tools/trade/definitions.d.ts +137 -0
  67. package/dist/tools/trade/definitions.d.ts.map +1 -0
  68. package/dist/tools/trade/definitions.js +48 -0
  69. package/dist/tools/trade/definitions.js.map +1 -0
  70. package/dist/tools/trade/handlers.d.ts +23 -0
  71. package/dist/tools/trade/handlers.d.ts.map +1 -0
  72. package/dist/tools/trade/handlers.js +196 -0
  73. package/dist/tools/trade/handlers.js.map +1 -0
  74. package/dist/tools/wallet/definitions.d.ts +158 -0
  75. package/dist/tools/wallet/definitions.d.ts.map +1 -0
  76. package/dist/tools/wallet/definitions.js +73 -0
  77. package/dist/tools/wallet/definitions.js.map +1 -0
  78. package/dist/tools/wallet/handlers.d.ts +28 -0
  79. package/dist/tools/wallet/handlers.d.ts.map +1 -0
  80. package/dist/tools/wallet/handlers.js +214 -0
  81. package/dist/tools/wallet/handlers.js.map +1 -0
  82. package/dist/utils/constants.d.ts +25 -0
  83. package/dist/utils/constants.d.ts.map +1 -0
  84. package/dist/utils/constants.js +19 -0
  85. package/dist/utils/constants.js.map +1 -0
  86. package/dist/utils/formatting.d.ts +14 -0
  87. package/dist/utils/formatting.d.ts.map +1 -0
  88. package/dist/utils/formatting.js +68 -0
  89. package/dist/utils/formatting.js.map +1 -0
  90. package/package.json +43 -0
  91. package/src/clients/daemon.ts +41 -0
  92. package/src/clients/trade.ts +51 -0
  93. package/src/clients/wallet.ts +59 -0
  94. package/src/config.ts +56 -0
  95. package/src/index.ts +20 -0
  96. package/src/logger.ts +33 -0
  97. package/src/prompts/index.ts +80 -0
  98. package/src/resources/index.ts +73 -0
  99. package/src/server.ts +26 -0
  100. package/src/tools/assets/definitions.ts +58 -0
  101. package/src/tools/assets/handlers.ts +140 -0
  102. package/src/tools/daemon/definitions.ts +86 -0
  103. package/src/tools/daemon/handlers.ts +349 -0
  104. package/src/tools/register.ts +194 -0
  105. package/src/tools/swap/definitions.ts +36 -0
  106. package/src/tools/swap/handlers.ts +139 -0
  107. package/src/tools/trade/definitions.ts +67 -0
  108. package/src/tools/trade/handlers.ts +264 -0
  109. package/src/tools/wallet/definitions.ts +92 -0
  110. package/src/tools/wallet/handlers.ts +268 -0
  111. package/src/utils/constants.ts +24 -0
  112. package/src/utils/formatting.ts +78 -0
  113. package/tsconfig.json +19 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatting.js","sourceRoot":"","sources":["../../src/utils/formatting.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "zano-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Zano blockchain - daemon, wallet, asset, swap, and trade tools",
5
+ "type": "module",
6
+ "bin": {
7
+ "zano-mcp": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsx src/index.ts",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "zano",
18
+ "mcp",
19
+ "blockchain",
20
+ "cryptocurrency",
21
+ "model-context-protocol",
22
+ "ionic-swap",
23
+ "dex"
24
+ ],
25
+ "author": "PRavaga",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/PRavaga/zano-mcp.git"
30
+ },
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ },
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.25.0",
36
+ "zod": "^3.24.0"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.7.0",
40
+ "tsx": "^4.19.0",
41
+ "@types/node": "^22.0.0"
42
+ }
43
+ }
@@ -0,0 +1,41 @@
1
+ import { logger } from "../logger.js";
2
+
3
+ export class DaemonClient {
4
+ private url: string;
5
+
6
+ constructor(url: string) {
7
+ this.url = url;
8
+ }
9
+
10
+ async call<T = unknown>(method: string, params: Record<string, unknown> = {}): Promise<T> {
11
+ const body = JSON.stringify({
12
+ jsonrpc: "2.0",
13
+ id: 0,
14
+ method,
15
+ params,
16
+ });
17
+
18
+ logger.debug(`Daemon RPC: ${method}`, params);
19
+
20
+ const res = await fetch(this.url, {
21
+ method: "POST",
22
+ headers: { "Content-Type": "application/json" },
23
+ body,
24
+ });
25
+
26
+ if (!res.ok) {
27
+ throw new Error(`Daemon RPC HTTP ${res.status}: ${res.statusText}`);
28
+ }
29
+
30
+ const json = (await res.json()) as {
31
+ result?: T;
32
+ error?: { code: number; message: string };
33
+ };
34
+
35
+ if (json.error) {
36
+ throw new Error(`Daemon RPC error: ${json.error.message} (code: ${json.error.code})`);
37
+ }
38
+
39
+ return json.result as T;
40
+ }
41
+ }
@@ -0,0 +1,51 @@
1
+ import { logger } from "../logger.js";
2
+
3
+ export class TradeClient {
4
+ private baseUrl: string;
5
+ private token?: string;
6
+
7
+ constructor(baseUrl: string, token?: string) {
8
+ this.baseUrl = baseUrl.replace(/\/$/, "");
9
+ this.token = token;
10
+ }
11
+
12
+ setToken(token: string): void {
13
+ this.token = token;
14
+ }
15
+
16
+ async post<T = unknown>(
17
+ path: string,
18
+ data: Record<string, unknown> = {},
19
+ requireAuth = false,
20
+ ): Promise<T> {
21
+ if (requireAuth && !this.token) {
22
+ throw new Error("Trade API authentication required. Set ZANO_TRADE_TOKEN or call dex_authenticate first.");
23
+ }
24
+
25
+ const payload = requireAuth ? { ...data, token: this.token } : data;
26
+
27
+ logger.debug(`Trade API: POST ${path}`, data);
28
+
29
+ const res = await fetch(`${this.baseUrl}${path}`, {
30
+ method: "POST",
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify(payload),
33
+ });
34
+
35
+ if (!res.ok) {
36
+ throw new Error(`Trade API HTTP ${res.status}: ${res.statusText}`);
37
+ }
38
+
39
+ const json = (await res.json()) as {
40
+ success: boolean;
41
+ data?: T;
42
+ error?: string;
43
+ };
44
+
45
+ if (!json.success) {
46
+ throw new Error(`Trade API error: ${json.error || json.data || "Unknown error"}`);
47
+ }
48
+
49
+ return json.data as T;
50
+ }
51
+ }
@@ -0,0 +1,59 @@
1
+ import { logger } from "../logger.js";
2
+
3
+ export class WalletClient {
4
+ private url: string;
5
+ private auth?: string;
6
+
7
+ constructor(url: string, auth?: string) {
8
+ this.url = url;
9
+ this.auth = auth;
10
+ }
11
+
12
+ async call<T = unknown>(method: string, params: Record<string, unknown> = {}): Promise<T> {
13
+ const body = JSON.stringify({
14
+ jsonrpc: "2.0",
15
+ id: 0,
16
+ method,
17
+ params,
18
+ });
19
+
20
+ logger.debug(`Wallet RPC: ${method}`, params);
21
+
22
+ const headers: Record<string, string> = {
23
+ "Content-Type": "application/json",
24
+ };
25
+
26
+ if (this.auth) {
27
+ headers["Zano-Access-Token"] = this.generateAccessToken(body);
28
+ }
29
+
30
+ const res = await fetch(this.url, {
31
+ method: "POST",
32
+ headers,
33
+ body,
34
+ });
35
+
36
+ if (!res.ok) {
37
+ throw new Error(`Wallet RPC HTTP ${res.status}: ${res.statusText}`);
38
+ }
39
+
40
+ const json = (await res.json()) as {
41
+ result?: T;
42
+ error?: { code: number; message: string };
43
+ };
44
+
45
+ if (json.error) {
46
+ throw new Error(`Wallet RPC error: ${json.error.message} (code: ${json.error.code})`);
47
+ }
48
+
49
+ return json.result as T;
50
+ }
51
+
52
+ private generateAccessToken(httpBody: string): string {
53
+ // JWT-based auth for wallet RPC
54
+ // Uses SHA-256 hash of the body as part of the token payload
55
+ // For now, return the auth secret directly as a simple bearer token
56
+ // Full JWT implementation would require a JWT library
57
+ return this.auth || "";
58
+ }
59
+ }
package/src/config.ts ADDED
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ import { DEFAULT_PORTS, TRADE_API_URL } from "./utils/constants.js";
3
+ import type { LogLevel } from "./logger.js";
4
+
5
+ export const configSchema = z.object({
6
+ daemonUrl: z.string().url(),
7
+ walletUrl: z.string().url().optional(),
8
+ walletAuth: z.string().optional(),
9
+ tradeUrl: z.string().url(),
10
+ tradeToken: z.string().optional(),
11
+ network: z.enum(["mainnet", "testnet"]),
12
+ logLevel: z.enum(["debug", "info", "warn", "error"]),
13
+ });
14
+
15
+ export type Config = z.infer<typeof configSchema>;
16
+
17
+ export function loadConfig(cliArgs: Record<string, string | undefined>): Config {
18
+ const network = (cliArgs.network || process.env.ZANO_NETWORK || "mainnet") as
19
+ | "mainnet"
20
+ | "testnet";
21
+ const ports = DEFAULT_PORTS[network] || DEFAULT_PORTS.mainnet;
22
+ const defaultDaemonUrl = `http://127.0.0.1:${ports.daemon}/json_rpc`;
23
+
24
+ const raw = {
25
+ daemonUrl:
26
+ cliArgs["daemon-url"] ||
27
+ process.env.ZANO_DAEMON_URL ||
28
+ defaultDaemonUrl,
29
+ walletUrl:
30
+ cliArgs["wallet-url"] || process.env.ZANO_WALLET_URL || undefined,
31
+ walletAuth:
32
+ cliArgs["wallet-auth"] || process.env.ZANO_WALLET_AUTH || undefined,
33
+ tradeUrl:
34
+ cliArgs["trade-url"] || process.env.ZANO_TRADE_URL || TRADE_API_URL,
35
+ tradeToken:
36
+ cliArgs["trade-token"] || process.env.ZANO_TRADE_TOKEN || undefined,
37
+ network,
38
+ logLevel: (cliArgs["log-level"] ||
39
+ process.env.ZANO_LOG_LEVEL ||
40
+ "info") as LogLevel,
41
+ };
42
+
43
+ return configSchema.parse(raw);
44
+ }
45
+
46
+ export function parseCliArgs(argv: string[]): Record<string, string> {
47
+ const args: Record<string, string> = {};
48
+ for (let i = 0; i < argv.length; i++) {
49
+ const arg = argv[i];
50
+ if (arg.startsWith("--") && i + 1 < argv.length) {
51
+ const key = arg.slice(2);
52
+ args[key] = argv[++i];
53
+ }
54
+ }
55
+ return args;
56
+ }
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { loadConfig, parseCliArgs } from "./config.js";
5
+ import { setLogLevel } from "./logger.js";
6
+ import { logger } from "./logger.js";
7
+ import { createServer } from "./server.js";
8
+
9
+ const cliArgs = parseCliArgs(process.argv.slice(2));
10
+ const config = loadConfig(cliArgs);
11
+ setLogLevel(config.logLevel);
12
+
13
+ logger.info("Starting Zano MCP server...");
14
+
15
+ const server = createServer(config);
16
+ const transport = new StdioServerTransport();
17
+
18
+ await server.connect(transport);
19
+
20
+ logger.info("Zano MCP server running on stdio");
package/src/logger.ts ADDED
@@ -0,0 +1,33 @@
1
+ export type LogLevel = "debug" | "info" | "warn" | "error";
2
+
3
+ const LEVELS: Record<LogLevel, number> = {
4
+ debug: 0,
5
+ info: 1,
6
+ warn: 2,
7
+ error: 3,
8
+ };
9
+
10
+ let currentLevel: LogLevel = "info";
11
+
12
+ export function setLogLevel(level: LogLevel): void {
13
+ currentLevel = level;
14
+ }
15
+
16
+ function shouldLog(level: LogLevel): boolean {
17
+ return LEVELS[level] >= LEVELS[currentLevel];
18
+ }
19
+
20
+ function log(level: LogLevel, ...args: unknown[]): void {
21
+ if (!shouldLog(level)) return;
22
+ const ts = new Date().toISOString();
23
+ const prefix = `[${ts}] [${level.toUpperCase()}]`;
24
+ // All logging goes to stderr to avoid interfering with JSON-RPC on stdout
25
+ console.error(prefix, ...args);
26
+ }
27
+
28
+ export const logger = {
29
+ debug: (...args: unknown[]) => log("debug", ...args),
30
+ info: (...args: unknown[]) => log("info", ...args),
31
+ warn: (...args: unknown[]) => log("warn", ...args),
32
+ error: (...args: unknown[]) => log("error", ...args),
33
+ };
@@ -0,0 +1,80 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { Config } from "../config.js";
3
+ import { logger } from "../logger.js";
4
+ import { z } from "zod";
5
+
6
+ export function registerPrompts(server: McpServer, config: Config): void {
7
+ server.prompt(
8
+ "check-network",
9
+ "Check Zano network status - calls get_network_info and formats a report",
10
+ {},
11
+ async () => ({
12
+ messages: [
13
+ {
14
+ role: "user",
15
+ content: {
16
+ type: "text",
17
+ text: "Use the get_network_info tool to check the current Zano network status. Report the block height, sync status, difficulty, hashrate, connection count, and pool size. Flag anything unusual (low connections, sync lag, empty pool).",
18
+ },
19
+ },
20
+ ],
21
+ }),
22
+ );
23
+
24
+ server.prompt(
25
+ "analyze-order-book",
26
+ "Analyze the DEX order book for a trading pair",
27
+ { pair_id: z.string().describe("Trading pair ID (e.g. 643 for FUSD)") },
28
+ async ({ pair_id }) => ({
29
+ messages: [
30
+ {
31
+ role: "user",
32
+ content: {
33
+ type: "text",
34
+ text: `Use the get_order_book tool with pairId ${pair_id} and the get_trading_pair tool with id ${pair_id}. Analyze the order book depth, bid/ask spread, instant vs non-instant orders, and top traders. Summarize liquidity and any arbitrage opportunities.`,
35
+ },
36
+ },
37
+ ],
38
+ }),
39
+ );
40
+
41
+ server.prompt(
42
+ "explain-transaction",
43
+ "Explain a Zano transaction in human-readable terms",
44
+ { tx_hash: z.string().describe("Transaction hash") },
45
+ async ({ tx_hash }) => ({
46
+ messages: [
47
+ {
48
+ role: "user",
49
+ content: {
50
+ type: "text",
51
+ text: `Use the get_transaction tool with tx_hash "${tx_hash}". Explain the transaction in human-readable terms: what happened, how much was sent, the fee, confirmations, and any asset operations.`,
52
+ },
53
+ },
54
+ ],
55
+ }),
56
+ );
57
+
58
+ server.prompt(
59
+ "swap-calculator",
60
+ "Calculate what an ionic swap would look like between two assets",
61
+ {
62
+ from_asset: z.string().describe("Source asset ticker or ID"),
63
+ to_asset: z.string().describe("Destination asset ticker or ID"),
64
+ amount: z.string().describe("Amount to swap (human-readable)"),
65
+ },
66
+ async ({ from_asset, to_asset, amount }) => ({
67
+ messages: [
68
+ {
69
+ role: "user",
70
+ content: {
71
+ type: "text",
72
+ text: `I want to swap ${amount} ${from_asset} for ${to_asset}. Look up both assets using get_asset_info if needed. Calculate the swap parameters and explain what the ionic swap proposal would contain, including atomic amounts, fees, and the expected outcome.`,
73
+ },
74
+ },
75
+ ],
76
+ }),
77
+ );
78
+
79
+ logger.info("Prompts registered (4 prompts)");
80
+ }
@@ -0,0 +1,73 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { Config } from "../config.js";
3
+ import { ASSET_WHITELIST_URLS, PUBLIC_NODES, DEFAULT_PORTS } from "../utils/constants.js";
4
+ import { logger } from "../logger.js";
5
+
6
+ export function registerResources(server: McpServer, config: Config): void {
7
+ server.resource(
8
+ "network-info",
9
+ "zano://network/info",
10
+ {
11
+ description: "Current Zano network configuration",
12
+ mimeType: "application/json",
13
+ },
14
+ async () => {
15
+ const ports = DEFAULT_PORTS[config.network];
16
+ const info = {
17
+ network: config.network,
18
+ daemonUrl: config.daemonUrl,
19
+ walletConfigured: !!config.walletUrl,
20
+ tradeAuthenticated: !!config.tradeToken,
21
+ defaultPorts: ports,
22
+ publicNode: PUBLIC_NODES[config.network],
23
+ };
24
+ return {
25
+ contents: [
26
+ {
27
+ uri: "zano://network/info",
28
+ mimeType: "application/json",
29
+ text: JSON.stringify(info, null, 2),
30
+ },
31
+ ],
32
+ };
33
+ },
34
+ );
35
+
36
+ server.resource(
37
+ "asset-whitelist",
38
+ "zano://assets/whitelist",
39
+ {
40
+ description: "Official Zano asset whitelist",
41
+ mimeType: "application/json",
42
+ },
43
+ async () => {
44
+ try {
45
+ const url = ASSET_WHITELIST_URLS[config.network];
46
+ const res = await fetch(url);
47
+ const data = await res.text();
48
+ return {
49
+ contents: [
50
+ {
51
+ uri: "zano://assets/whitelist",
52
+ mimeType: "application/json",
53
+ text: data,
54
+ },
55
+ ],
56
+ };
57
+ } catch (e) {
58
+ logger.error("Failed to fetch asset whitelist:", e);
59
+ return {
60
+ contents: [
61
+ {
62
+ uri: "zano://assets/whitelist",
63
+ mimeType: "application/json",
64
+ text: JSON.stringify({ error: "Failed to fetch whitelist" }),
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ },
70
+ );
71
+
72
+ logger.info("Resources registered (2 resources)");
73
+ }
package/src/server.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { Config } from "./config.js";
3
+ import { registerAllTools } from "./tools/register.js";
4
+ import { registerResources } from "./resources/index.js";
5
+ import { registerPrompts } from "./prompts/index.js";
6
+ import { logger } from "./logger.js";
7
+
8
+ export function createServer(config: Config): McpServer {
9
+ const server = new McpServer({
10
+ name: "zano-mcp",
11
+ version: "0.1.0",
12
+ });
13
+
14
+ registerAllTools(server, config);
15
+ registerResources(server, config);
16
+ registerPrompts(server, config);
17
+
18
+ logger.info("Zano MCP server created", {
19
+ daemon: config.daemonUrl,
20
+ wallet: config.walletUrl ? "configured" : "disabled",
21
+ trade: config.tradeToken ? "authenticated" : "public only",
22
+ network: config.network,
23
+ });
24
+
25
+ return server;
26
+ }
@@ -0,0 +1,58 @@
1
+ import { z } from "zod";
2
+
3
+ export const DeployAssetShape = {
4
+ ticker: z.string().describe("Asset ticker symbol (e.g. 'MYTOKEN')"),
5
+ full_name: z.string().describe("Full asset name"),
6
+ total_max_supply: z.string().describe("Maximum supply in human-readable units"),
7
+ current_supply: z.string().describe("Initial supply to mint in human-readable units"),
8
+ decimal_point: z.number().int().min(0).max(18).default(12).describe("Decimal places"),
9
+ meta_info: z.string().optional().describe("JSON metadata string"),
10
+ hidden_supply: z.boolean().optional().default(false).describe("Whether to hide supply info"),
11
+ };
12
+
13
+ export const EmitAssetShape = {
14
+ asset_id: z.string().describe("Asset ID to mint"),
15
+ amount: z.string().describe("Amount to mint in human-readable units"),
16
+ };
17
+
18
+ export const BurnAssetShape = {
19
+ asset_id: z.string().describe("Asset ID to burn"),
20
+ amount: z.string().describe("Amount to burn in human-readable units"),
21
+ };
22
+
23
+ export const UpdateAssetShape = {
24
+ asset_id: z.string().describe("Asset ID to update"),
25
+ ticker: z.string().optional().describe("New ticker"),
26
+ full_name: z.string().optional().describe("New full name"),
27
+ meta_info: z.string().optional().describe("New metadata"),
28
+ };
29
+
30
+ export const TransferAssetOwnershipShape = {
31
+ asset_id: z.string().describe("Asset ID"),
32
+ new_owner: z.string().describe("New owner's public key"),
33
+ };
34
+
35
+ export const WhitelistAssetShape = {
36
+ asset_id: z.string().describe("Asset ID to add to whitelist"),
37
+ };
38
+
39
+ export const RemoveAssetFromWhitelistShape = {
40
+ asset_id: z.string().describe("Asset ID to remove from whitelist"),
41
+ };
42
+
43
+ // Schemas and types
44
+ export const DeployAssetSchema = z.object(DeployAssetShape);
45
+ export const EmitAssetSchema = z.object(EmitAssetShape);
46
+ export const BurnAssetSchema = z.object(BurnAssetShape);
47
+ export const UpdateAssetSchema = z.object(UpdateAssetShape);
48
+ export const TransferAssetOwnershipSchema = z.object(TransferAssetOwnershipShape);
49
+ export const WhitelistAssetSchema = z.object(WhitelistAssetShape);
50
+ export const RemoveAssetFromWhitelistSchema = z.object(RemoveAssetFromWhitelistShape);
51
+
52
+ export type DeployAssetInput = z.infer<typeof DeployAssetSchema>;
53
+ export type EmitAssetInput = z.infer<typeof EmitAssetSchema>;
54
+ export type BurnAssetInput = z.infer<typeof BurnAssetSchema>;
55
+ export type UpdateAssetInput = z.infer<typeof UpdateAssetSchema>;
56
+ export type TransferAssetOwnershipInput = z.infer<typeof TransferAssetOwnershipSchema>;
57
+ export type WhitelistAssetInput = z.infer<typeof WhitelistAssetSchema>;
58
+ export type RemoveAssetFromWhitelistInput = z.infer<typeof RemoveAssetFromWhitelistSchema>;