@ecadlabs/tezosx-mcp 1.0.0 → 1.0.1

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 (51) hide show
  1. package/README.md +87 -66
  2. package/dist/api.d.ts +3 -0
  3. package/dist/api.js +155 -0
  4. package/dist/config-store.d.ts +15 -0
  5. package/dist/config-store.js +38 -0
  6. package/dist/index.d.ts +0 -6
  7. package/dist/index.js +43 -44
  8. package/dist/live-config.d.ts +32 -0
  9. package/dist/live-config.js +55 -0
  10. package/dist/tools/create_x402_payment.d.ts +2 -2
  11. package/dist/tools/create_x402_payment.js +2 -1
  12. package/dist/tools/fetch_with_x402.d.ts +3 -3
  13. package/dist/tools/fetch_with_x402.js +7 -6
  14. package/dist/tools/get_addresses.d.ts +2 -2
  15. package/dist/tools/get_addresses.js +2 -1
  16. package/dist/tools/get_balance.d.ts +2 -2
  17. package/dist/tools/get_balance.js +2 -1
  18. package/dist/tools/get_dashboard.d.ts +2 -1
  19. package/dist/tools/get_dashboard.js +2 -2
  20. package/dist/tools/get_limits.d.ts +2 -2
  21. package/dist/tools/get_limits.js +2 -1
  22. package/dist/tools/get_operation_history.d.ts +2 -1
  23. package/dist/tools/get_operation_history.js +2 -1
  24. package/dist/tools/index.d.ts +3 -3
  25. package/dist/tools/index.js +15 -24
  26. package/dist/tools/reveal_account.d.ts +2 -1
  27. package/dist/tools/reveal_account.js +2 -1
  28. package/dist/tools/send_xtz.d.ts +2 -9
  29. package/dist/tools/send_xtz.js +33 -15
  30. package/dist/webserver.d.ts +2 -1
  31. package/dist/webserver.js +11 -4
  32. package/frontend/dist/assets/{index-RtTL1nIl.js → index-B-2-_lot.js} +95 -65
  33. package/frontend/dist/assets/index-CTdz8_ps.css +1 -0
  34. package/frontend/dist/index.html +2 -2
  35. package/package.json +2 -1
  36. package/dist/adapters/index.d.ts +0 -37
  37. package/dist/adapters/index.js +0 -57
  38. package/dist/adapters/node.d.ts +0 -18
  39. package/dist/adapters/node.js +0 -35
  40. package/dist/adapters/types.d.ts +0 -52
  41. package/dist/adapters/types.js +0 -25
  42. package/dist/adapters/worker.d.ts +0 -35
  43. package/dist/adapters/worker.js +0 -50
  44. package/dist/server.d.ts +0 -36
  45. package/dist/server.js +0 -80
  46. package/dist/tools/get_address.d.ts +0 -20
  47. package/dist/tools/get_address.js +0 -24
  48. package/dist/worker.bundle.js +0 -134265
  49. package/dist/worker.d.ts +0 -13
  50. package/dist/worker.js +0 -132
  51. package/frontend/dist/assets/index-mSsI3AqQ.css +0 -1
@@ -1,6 +1,6 @@
1
- import { TezosToolkit } from "@taquito/taquito";
2
1
  import z from "zod";
3
- export declare const createFetchWithX402Tool: (Tezos: TezosToolkit) => {
2
+ import type { LiveConfig } from "../live-config.js";
3
+ export declare const createFetchWithX402Tool: (config: LiveConfig) => {
4
4
  name: string;
5
5
  config: {
6
6
  title: string;
@@ -10,7 +10,7 @@ export declare const createFetchWithX402Tool: (Tezos: TezosToolkit) => {
10
10
  maxAmountMutez: z.ZodString;
11
11
  method: z.ZodOptional<z.ZodString>;
12
12
  body: z.ZodOptional<z.ZodString>;
13
- recipient: z.ZodOptional<z.ZodString>;
13
+ nftRecipientAddress: z.ZodOptional<z.ZodString>;
14
14
  }, z.z.core.$strip>;
15
15
  annotations: {
16
16
  readOnlyHint: boolean;
@@ -3,17 +3,17 @@ import axios from "axios";
3
3
  import { X402ResponseSchema } from "./x402/types.js";
4
4
  import { signX402Payment } from "./x402/sign.js";
5
5
  import { ensureRevealed } from "./reveal_account.js";
6
- export const createFetchWithX402Tool = (Tezos) => ({
6
+ export const createFetchWithX402Tool = (config) => ({
7
7
  name: "tezos_fetch_with_x402",
8
8
  config: {
9
9
  title: "Fetch with x402 Payment",
10
- description: "Fetches a URL and automatically handles x402 payment requirements. If the server returns 402, it parses the requirements, creates a signed payment, and retries the request with the X-PAYMENT header. IMPORTANT: For NFT minting endpoints, always set 'recipient' to the user's wallet address so they receive the NFT (not the spending account).",
10
+ description: "Fetches a URL and automatically handles x402 payment requirements. If the server returns 402, it parses the requirements, creates a signed payment, and retries the request with the X-PAYMENT header. WARNING: For NFT minting endpoints (URLs containing '/mint' or otherwise going to a minter), you MUST set 'nftRecipientAddress' to the user's wallet address, otherwise the NFT will be sent to the spender account instead of the user.",
11
11
  inputSchema: z.object({
12
12
  url: z.string().describe("The URL to fetch"),
13
13
  maxAmountMutez: z.string().describe("Maximum amount in mutez willing to pay (e.g., '500000' for 0.5 XTZ)"),
14
14
  method: z.string().optional().describe("HTTP method (default: GET)"),
15
15
  body: z.string().optional().describe("Request body for POST/PUT requests"),
16
- recipient: z.string().optional().describe("Address to receive any minted assets (NFTs). IMPORTANT: For /mint endpoints, set this to the user's wallet address. If omitted, defaults to the spending account (payer), which is usually not desired."),
16
+ nftRecipientAddress: z.string().optional().describe("REQUIRED FOR MINTING: The user's wallet address (tz1...) to receive minted NFTs. If omitted, NFTs go to the spender account, not the user."),
17
17
  }),
18
18
  annotations: {
19
19
  readOnlyHint: false,
@@ -23,16 +23,17 @@ export const createFetchWithX402Tool = (Tezos) => ({
23
23
  }
24
24
  },
25
25
  handler: async (params) => {
26
- const { url, maxAmountMutez, method = "GET", body, recipient } = params;
26
+ const { Tezos } = config;
27
+ const { url, maxAmountMutez, method = "GET", body, nftRecipientAddress } = params;
27
28
  const maxAmount = parseInt(maxAmountMutez, 10);
28
29
  if (isNaN(maxAmount) || maxAmount <= 0) {
29
30
  throw new Error(`Invalid maxAmountMutez: ${maxAmountMutez}. Must be a positive integer.`);
30
31
  }
31
32
  // Build URL with recipient parameter if specified
32
33
  let requestUrl = url;
33
- if (recipient) {
34
+ if (nftRecipientAddress) {
34
35
  const urlObj = new URL(url);
35
- urlObj.searchParams.set("recipient", recipient);
36
+ urlObj.searchParams.set("recipient", nftRecipientAddress);
36
37
  requestUrl = urlObj.toString();
37
38
  }
38
39
  // Initial request config
@@ -1,6 +1,6 @@
1
- import { TezosToolkit } from "@taquito/taquito";
2
1
  import z from "zod";
3
- export declare const createGetAddressesTool: (Tezos: TezosToolkit, spendingContract: string) => {
2
+ import type { LiveConfig } from "../live-config.js";
3
+ export declare const createGetAddressesTool: (config: LiveConfig) => {
4
4
  name: string;
5
5
  config: {
6
6
  title: string;
@@ -1,5 +1,5 @@
1
1
  import z from "zod";
2
- export const createGetAddressesTool = (Tezos, spendingContract) => ({
2
+ export const createGetAddressesTool = (config) => ({
3
3
  name: "tezos_get_addresses",
4
4
  config: {
5
5
  title: "Get Tezos Addresses",
@@ -13,6 +13,7 @@ export const createGetAddressesTool = (Tezos, spendingContract) => ({
13
13
  },
14
14
  },
15
15
  handler: async () => {
16
+ const { Tezos, spendingContract } = config;
16
17
  const contract = await Tezos.contract.at(spendingContract);
17
18
  const storage = (await contract.storage());
18
19
  const result = {
@@ -1,6 +1,6 @@
1
- import { TezosToolkit } from "@taquito/taquito";
2
1
  import z from "zod";
3
- export declare const createGetBalanceTool: (Tezos: TezosToolkit, spendingContract: string, spendingAddress: string) => {
2
+ import type { LiveConfig } from "../live-config.js";
3
+ export declare const createGetBalanceTool: (config: LiveConfig) => {
4
4
  name: string;
5
5
  config: {
6
6
  title: string;
@@ -1,5 +1,5 @@
1
1
  import z from "zod";
2
- export const createGetBalanceTool = (Tezos, spendingContract, spendingAddress) => ({
2
+ export const createGetBalanceTool = (config) => ({
3
3
  name: "tezos_get_balance",
4
4
  config: {
5
5
  title: "Get Balances",
@@ -14,6 +14,7 @@ export const createGetBalanceTool = (Tezos, spendingContract, spendingAddress) =
14
14
  },
15
15
  handler: async () => {
16
16
  try {
17
+ const { Tezos, spendingContract, spendingAddress } = config;
17
18
  const spendingAddressBalance = (await Tezos.tz.getBalance(spendingAddress)).toString();
18
19
  const spendingContractBalance = (await Tezos.tz.getBalance(spendingContract)).toString();
19
20
  return {
@@ -1,5 +1,6 @@
1
1
  import z from "zod";
2
- export declare const createGetDashboardTool: (spendingContract: string) => {
2
+ import type { LiveConfig } from "../live-config.js";
3
+ export declare const createGetDashboardTool: (config: LiveConfig) => {
3
4
  name: string;
4
5
  config: {
5
6
  title: string;
@@ -1,6 +1,6 @@
1
1
  import z from "zod";
2
2
  import { DEFAULT_WEB_PORT } from '../index.js';
3
- export const createGetDashboardTool = (spendingContract) => ({
3
+ export const createGetDashboardTool = (config) => ({
4
4
  name: "tezos_get_dashboard",
5
5
  config: {
6
6
  title: "Get Contract Dashboard",
@@ -16,7 +16,7 @@ export const createGetDashboardTool = (spendingContract) => ({
16
16
  handler: async () => {
17
17
  const port = process.env.WEB_PORT || DEFAULT_WEB_PORT;
18
18
  const network = process.env.TEZOS_NETWORK || 'shadownet';
19
- const url = `http://localhost:${port}?contract=${spendingContract}&network=${network}`;
19
+ const url = `http://localhost:${port}?contract=${config.spendingContract}&network=${network}`;
20
20
  return {
21
21
  content: [
22
22
  {
@@ -1,6 +1,6 @@
1
- import { TezosToolkit } from "@taquito/taquito";
2
1
  import z from "zod";
3
- export declare const createGetLimitsTool: (Tezos: TezosToolkit, spendingContract: string) => {
2
+ import type { LiveConfig } from "../live-config.js";
3
+ export declare const createGetLimitsTool: (config: LiveConfig) => {
4
4
  name: string;
5
5
  config: {
6
6
  title: string;
@@ -3,7 +3,7 @@ import z from "zod";
3
3
  const MUTEZ_PER_TEZ = 1_000_000;
4
4
  /** Convert mutez to XTZ */
5
5
  const mutezToXtz = (mutez) => mutez / MUTEZ_PER_TEZ;
6
- export const createGetLimitsTool = (Tezos, spendingContract) => ({
6
+ export const createGetLimitsTool = (config) => ({
7
7
  name: "tezos_get_limits",
8
8
  config: {
9
9
  title: "Get Spending Limits",
@@ -17,6 +17,7 @@ export const createGetLimitsTool = (Tezos, spendingContract) => ({
17
17
  },
18
18
  },
19
19
  handler: async () => {
20
+ const { Tezos, spendingContract } = config;
20
21
  const contract = await Tezos.contract.at(spendingContract);
21
22
  const storage = (await contract.storage());
22
23
  const dailyLimitMutez = storage.daily_limit.toNumber();
@@ -1,5 +1,6 @@
1
1
  import z from "zod";
2
- export declare const createGetOperationHistoryTool: (spendingContract: string, tzktApi: string) => {
2
+ import type { LiveConfig } from "../live-config.js";
3
+ export declare const createGetOperationHistoryTool: (config: LiveConfig) => {
3
4
  name: string;
4
5
  config: {
5
6
  title: string;
@@ -1,6 +1,6 @@
1
1
  import axios from "axios";
2
2
  import z from "zod";
3
- export const createGetOperationHistoryTool = (spendingContract, tzktApi) => ({
3
+ export const createGetOperationHistoryTool = (config) => ({
4
4
  name: "tezos_get_operation_history",
5
5
  config: {
6
6
  title: "Get Operation History",
@@ -15,6 +15,7 @@ export const createGetOperationHistoryTool = (spendingContract, tzktApi) => ({
15
15
  },
16
16
  handler: async () => {
17
17
  try {
18
+ const { spendingContract, tzktApi } = config;
18
19
  const url = `${tzktApi}/v1/accounts/${spendingContract}/operations?type=transaction&limit=100`;
19
20
  const response = await axios.get(url);
20
21
  const allOperations = response.data;
@@ -1,5 +1,5 @@
1
- import { WalletConfig } from "../index.js";
2
- export declare const createTools: (walletConfig: WalletConfig, tzktApi: string, http: boolean) => ({
1
+ import type { LiveConfig } from "../live-config.js";
2
+ export declare const createTools: (liveConfig: LiveConfig, http: boolean) => ({
3
3
  name: string;
4
4
  config: {
5
5
  title: string;
@@ -52,7 +52,7 @@ export declare const createTools: (walletConfig: WalletConfig, tzktApi: string,
52
52
  maxAmountMutez: import("zod").ZodString;
53
53
  method: import("zod").ZodOptional<import("zod").ZodString>;
54
54
  body: import("zod").ZodOptional<import("zod").ZodString>;
55
- recipient: import("zod").ZodOptional<import("zod").ZodString>;
55
+ nftRecipientAddress: import("zod").ZodOptional<import("zod").ZodString>;
56
56
  }, import("zod/v4/core").$strip>;
57
57
  annotations: {
58
58
  readOnlyHint: boolean;
@@ -8,16 +8,12 @@ import { createParseX402RequirementsTool } from "./parse_x402_requirements.js";
8
8
  import { createRevealAccountTool } from "./reveal_account.js";
9
9
  import { createSendXtzTool } from "./send_xtz.js";
10
10
  import { createGetDashboardTool } from "./get_dashboard.js";
11
- const getNotConfiguredMessage = () => `Wallet not configured. Please set the following environment variables and restart the MCP server:
12
-
13
- 1. SPENDING_PRIVATE_KEY - Your spending key (starts with edsk, spsk, or p2sk)
14
- 2. SPENDING_CONTRACT - Your spending contract address (starts with KT1)
11
+ const getNotConfiguredMessage = () => `Wallet not configured.
15
12
 
16
13
  To get started:
17
14
  1. Open the dashboard at http://localhost:${process.env.WEB_PORT || '13205'}
18
15
  2. Connect your wallet and deploy a spending contract
19
- 3. Add the environment variables to your MCP client configuration
20
- 4. Restart the MCP server`;
16
+ 3. The MCP server will be configured automatically no manual setup needed`;
21
17
  const notConfiguredResponse = () => ({
22
18
  content: [{
23
19
  type: "text",
@@ -25,35 +21,30 @@ const notConfiguredResponse = () => ({
25
21
  }]
26
22
  });
27
23
  // Wraps a tool's handler to check config before execution
28
- const withConfigCheck = (tool, walletConfig) => ({
24
+ const withConfigCheck = (tool, liveConfig) => ({
29
25
  ...tool,
30
26
  handler: async (...args) => {
31
- if (!walletConfig) {
27
+ if (!liveConfig.configured) {
32
28
  return notConfiguredResponse();
33
29
  }
34
30
  return tool.handler(...args);
35
31
  }
36
32
  });
37
- export const createTools = (walletConfig, tzktApi, http) => {
38
- // Create a mock Tezos toolkit for tool creation when config is missing
39
- // The actual handlers will be blocked by withConfigCheck
40
- const Tezos = walletConfig?.Tezos ?? {};
41
- const spendingContract = walletConfig?.spendingContract ?? '';
42
- const spendingAddress = walletConfig?.spendingAddress ?? '';
33
+ export const createTools = (liveConfig, http) => {
43
34
  const tools = [
44
- createCreateX402PaymentTool(Tezos),
45
- createFetchWithX402Tool(Tezos),
46
- createGetAddressesTool(Tezos, spendingContract),
47
- createGetBalanceTool(Tezos, spendingContract, spendingAddress),
48
- createGetLimitsTool(Tezos, spendingContract),
49
- createGetOperationHistoryTool(spendingContract, tzktApi),
35
+ createCreateX402PaymentTool(liveConfig),
36
+ createFetchWithX402Tool(liveConfig),
37
+ createGetAddressesTool(liveConfig),
38
+ createGetBalanceTool(liveConfig),
39
+ createGetLimitsTool(liveConfig),
40
+ createGetOperationHistoryTool(liveConfig),
50
41
  createParseX402RequirementsTool(),
51
- createRevealAccountTool(Tezos),
52
- createSendXtzTool(Tezos, spendingContract, spendingAddress),
42
+ createRevealAccountTool(liveConfig),
43
+ createSendXtzTool(liveConfig),
53
44
  ];
54
45
  if (!http) {
55
- tools.push(createGetDashboardTool(spendingContract));
46
+ tools.push(createGetDashboardTool(liveConfig));
56
47
  }
57
48
  // Wrap all tools with config check
58
- return tools.map(tool => withConfigCheck(tool, walletConfig));
49
+ return tools.map(tool => withConfigCheck(tool, liveConfig));
59
50
  };
@@ -1,5 +1,6 @@
1
1
  import { TezosToolkit } from "@taquito/taquito";
2
2
  import z from "zod";
3
+ import type { LiveConfig } from "../live-config.js";
3
4
  /**
4
5
  * Check if an account is revealed on the Tezos network.
5
6
  */
@@ -12,7 +13,7 @@ export declare function ensureRevealed(Tezos: TezosToolkit): Promise<{
12
13
  wasRevealed: boolean;
13
14
  opHash?: string;
14
15
  }>;
15
- export declare const createRevealAccountTool: (Tezos: TezosToolkit) => {
16
+ export declare const createRevealAccountTool: (config: LiveConfig) => {
16
17
  name: string;
17
18
  config: {
18
19
  title: string;
@@ -20,7 +20,7 @@ export async function ensureRevealed(Tezos) {
20
20
  await op.confirmation(1);
21
21
  return { wasRevealed: true, opHash: op.hash };
22
22
  }
23
- export const createRevealAccountTool = (Tezos) => ({
23
+ export const createRevealAccountTool = (config) => ({
24
24
  name: "tezos_reveal_account",
25
25
  config: {
26
26
  title: "Reveal Account",
@@ -34,6 +34,7 @@ export const createRevealAccountTool = (Tezos) => ({
34
34
  }
35
35
  },
36
36
  handler: async () => {
37
+ const { Tezos } = config;
37
38
  const address = await Tezos.signer.publicKeyHash();
38
39
  const result = await ensureRevealed(Tezos);
39
40
  return {
@@ -1,13 +1,6 @@
1
- import { TezosToolkit } from "@taquito/taquito";
2
1
  import z from "zod";
3
- /**
4
- * MCP tool for sending XTZ via a spending contract.
5
- *
6
- * @param Tezos - Configured TezosToolkit instance (with signer set to spender key)
7
- * @param spendingContract - Address of the spending-limited wallet contract
8
- * @param spendingAddress - Address of the spender account (for fee payments)
9
- */
10
- export declare const createSendXtzTool: (Tezos: TezosToolkit, spendingContract: string, spendingAddress: string) => {
2
+ import type { LiveConfig } from "../live-config.js";
3
+ export declare const createSendXtzTool: (config: LiveConfig) => {
11
4
  name: string;
12
5
  config: {
13
6
  title: string;
@@ -1,8 +1,11 @@
1
1
  import z from "zod";
2
+ import { ensureRevealed } from "./reveal_account.js";
2
3
  // Constants
3
4
  const MUTEZ_PER_TEZ = 1_000_000;
4
5
  const CONFIRMATIONS_TO_WAIT = 3;
5
6
  const TZKT_BASE_URL = "https://shadownet.tzkt.io";
7
+ const SPENDER_TOP_UP_THRESHOLD = 250_000; // 0.25 XTZ
8
+ const SPENDER_TOP_UP_TARGET = 500_000; // 0.5 XTZ
6
9
  // Types
7
10
  const inputSchema = z.object({
8
11
  toAddress: z.string().describe("The address to send Tez to."),
@@ -13,14 +16,9 @@ const inputSchema = z.object({
13
16
  const xtzToMutez = (xtz) => xtz * MUTEZ_PER_TEZ;
14
17
  /** Format mutez for display */
15
18
  const formatMutez = (mutez) => `${mutez} mutez`;
16
- /**
17
- * MCP tool for sending XTZ via a spending contract.
18
- *
19
- * @param Tezos - Configured TezosToolkit instance (with signer set to spender key)
20
- * @param spendingContract - Address of the spending-limited wallet contract
21
- * @param spendingAddress - Address of the spender account (for fee payments)
22
- */
23
- export const createSendXtzTool = (Tezos, spendingContract, spendingAddress) => ({
19
+ // Cache the last-verified spender so we only hit the chain when config changes
20
+ let verifiedSpender = null;
21
+ export const createSendXtzTool = (config) => ({
24
22
  name: "tezos_send_xtz",
25
23
  config: {
26
24
  title: "Send Tez",
@@ -35,6 +33,21 @@ export const createSendXtzTool = (Tezos, spendingContract, spendingAddress) => (
35
33
  },
36
34
  handler: async (params) => {
37
35
  params = params;
36
+ const { Tezos, spendingContract, spendingAddress } = config;
37
+ // Verify the server's signer matches the on-chain spender (only when config changes)
38
+ const needsVerify = !verifiedSpender
39
+ || verifiedSpender.address !== spendingAddress
40
+ || verifiedSpender.contract !== spendingContract;
41
+ if (needsVerify) {
42
+ const c = await Tezos.contract.at(spendingContract);
43
+ const storage = await c.storage();
44
+ if (storage.spender !== spendingAddress) {
45
+ throw new Error(`Spender mismatch: the server's signing key (${spendingAddress}) does not match ` +
46
+ `the contract's spender (${storage.spender}). ` +
47
+ `Please regenerate the spender key from the dashboard.`);
48
+ }
49
+ verifiedSpender = { address: spendingAddress, contract: spendingContract };
50
+ }
38
51
  // Validate spender has funds for fees
39
52
  const spenderBalance = await Tezos.tz.getBalance(spendingAddress);
40
53
  if (spenderBalance.isZero()) {
@@ -49,11 +62,19 @@ export const createSendXtzTool = (Tezos, spendingContract, spendingAddress) => (
49
62
  `Requested: ${formatMutez(amountMutez)}, ` +
50
63
  `Available: ${formatMutez(contractBalance.toNumber())}`);
51
64
  }
52
- // Prepare contract call
65
+ // Taquito auto-reveals on send(), but the estimate step below
66
+ // simulates via RPC without handling revelation — so we reveal first.
67
+ await ensureRevealed(Tezos);
68
+ // Top up spender from contract if balance is low
69
+ const spenderMutez = spenderBalance.toNumber();
70
+ const feeRebate = spenderMutez < SPENDER_TOP_UP_THRESHOLD
71
+ ? SPENDER_TOP_UP_TARGET - spenderMutez
72
+ : 0;
53
73
  const contract = await Tezos.contract.at(spendingContract);
54
74
  const contractCall = contract.methodsObject.spend({
55
75
  recipient: params.toAddress,
56
76
  amount: amountMutez,
77
+ fee_rebate: feeRebate,
57
78
  });
58
79
  // Estimate fees
59
80
  let estimate;
@@ -63,19 +84,16 @@ export const createSendXtzTool = (Tezos, spendingContract, spendingAddress) => (
63
84
  catch (err) {
64
85
  const message = err instanceof Error ? err.message : String(err);
65
86
  if (message.includes("balance_too_low")) {
66
- throw new Error(`Spender balance (${formatMutez(spenderBalance.toNumber())}) ` +
87
+ throw new Error(`Spender balance (${formatMutez(spenderMutez)}) ` +
67
88
  `is too low to cover fees. Please fund the spending address.`);
68
89
  }
69
90
  throw err;
70
91
  }
71
- // Run another fee check because the first check is only estimating the fees, but still needs tez in the account to estimate.
72
- // This check is checking against the actual estimated fee value.
73
- if (spenderBalance.toNumber() < estimate.totalCost) {
92
+ if (spenderMutez < estimate.totalCost) {
74
93
  throw new Error(`Spender balance too low for fees. ` +
75
94
  `Required: ${formatMutez(estimate.totalCost)}, ` +
76
- `Available: ${formatMutez(spenderBalance.toNumber())}`);
95
+ `Available: ${formatMutez(spenderMutez)}`);
77
96
  }
78
- // Execute transaction
79
97
  const operation = await contractCall.send();
80
98
  await operation.confirmation(CONFIRMATIONS_TO_WAIT);
81
99
  const tzktUrl = `${TZKT_BASE_URL}/${operation.hash}`;
@@ -1 +1,2 @@
1
- export declare const startWebServer: (port: number) => void;
1
+ import { type Router } from "express";
2
+ export declare const startWebServer: (port: number, apiRouter?: Router) => void;
package/dist/webserver.js CHANGED
@@ -1,10 +1,17 @@
1
- import { createServer } from "http";
1
+ import express from "express";
2
2
  import { fileURLToPath } from "url";
3
3
  import { join } from "path";
4
4
  import sirv from "sirv";
5
5
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
6
- export const startWebServer = (port) => {
6
+ export const startWebServer = (port, apiRouter) => {
7
+ const app = express();
8
+ app.use(express.json());
9
+ // API routes (config endpoints)
10
+ if (apiRouter) {
11
+ app.use(apiRouter);
12
+ }
13
+ // Static files + SPA fallback
7
14
  const distPath = join(__dirname, "../frontend/dist");
8
- const serve = sirv(distPath, { single: true });
9
- createServer(serve).listen(port);
15
+ app.use(sirv(distPath, { single: true }));
16
+ app.listen(port);
10
17
  };