@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.
- package/README.md +87 -66
- package/dist/api.d.ts +3 -0
- package/dist/api.js +155 -0
- package/dist/config-store.d.ts +15 -0
- package/dist/config-store.js +38 -0
- package/dist/index.d.ts +0 -6
- package/dist/index.js +43 -44
- package/dist/live-config.d.ts +32 -0
- package/dist/live-config.js +55 -0
- package/dist/tools/create_x402_payment.d.ts +2 -2
- package/dist/tools/create_x402_payment.js +2 -1
- package/dist/tools/fetch_with_x402.d.ts +3 -3
- package/dist/tools/fetch_with_x402.js +7 -6
- package/dist/tools/get_addresses.d.ts +2 -2
- package/dist/tools/get_addresses.js +2 -1
- package/dist/tools/get_balance.d.ts +2 -2
- package/dist/tools/get_balance.js +2 -1
- package/dist/tools/get_dashboard.d.ts +2 -1
- package/dist/tools/get_dashboard.js +2 -2
- package/dist/tools/get_limits.d.ts +2 -2
- package/dist/tools/get_limits.js +2 -1
- package/dist/tools/get_operation_history.d.ts +2 -1
- package/dist/tools/get_operation_history.js +2 -1
- package/dist/tools/index.d.ts +3 -3
- package/dist/tools/index.js +15 -24
- package/dist/tools/reveal_account.d.ts +2 -1
- package/dist/tools/reveal_account.js +2 -1
- package/dist/tools/send_xtz.d.ts +2 -9
- package/dist/tools/send_xtz.js +33 -15
- package/dist/webserver.d.ts +2 -1
- package/dist/webserver.js +11 -4
- package/frontend/dist/assets/{index-RtTL1nIl.js → index-B-2-_lot.js} +95 -65
- package/frontend/dist/assets/index-CTdz8_ps.css +1 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +2 -1
- package/dist/adapters/index.d.ts +0 -37
- package/dist/adapters/index.js +0 -57
- package/dist/adapters/node.d.ts +0 -18
- package/dist/adapters/node.js +0 -35
- package/dist/adapters/types.d.ts +0 -52
- package/dist/adapters/types.js +0 -25
- package/dist/adapters/worker.d.ts +0 -35
- package/dist/adapters/worker.js +0 -50
- package/dist/server.d.ts +0 -36
- package/dist/server.js +0 -80
- package/dist/tools/get_address.d.ts +0 -20
- package/dist/tools/get_address.js +0 -24
- package/dist/worker.bundle.js +0 -134265
- package/dist/worker.d.ts +0 -13
- package/dist/worker.js +0 -132
- 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
|
-
|
|
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
|
-
|
|
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 = (
|
|
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.
|
|
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
|
-
|
|
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 {
|
|
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 (
|
|
34
|
+
if (nftRecipientAddress) {
|
|
34
35
|
const urlObj = new URL(url);
|
|
35
|
-
urlObj.searchParams.set("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
|
-
|
|
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 = (
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
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;
|
package/dist/tools/get_limits.js
CHANGED
|
@@ -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 = (
|
|
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
|
-
|
|
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 = (
|
|
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;
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const createTools: (
|
|
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
|
-
|
|
55
|
+
nftRecipientAddress: import("zod").ZodOptional<import("zod").ZodString>;
|
|
56
56
|
}, import("zod/v4/core").$strip>;
|
|
57
57
|
annotations: {
|
|
58
58
|
readOnlyHint: boolean;
|
package/dist/tools/index.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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,
|
|
24
|
+
const withConfigCheck = (tool, liveConfig) => ({
|
|
29
25
|
...tool,
|
|
30
26
|
handler: async (...args) => {
|
|
31
|
-
if (!
|
|
27
|
+
if (!liveConfig.configured) {
|
|
32
28
|
return notConfiguredResponse();
|
|
33
29
|
}
|
|
34
30
|
return tool.handler(...args);
|
|
35
31
|
}
|
|
36
32
|
});
|
|
37
|
-
export const createTools = (
|
|
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(
|
|
45
|
-
createFetchWithX402Tool(
|
|
46
|
-
createGetAddressesTool(
|
|
47
|
-
createGetBalanceTool(
|
|
48
|
-
createGetLimitsTool(
|
|
49
|
-
createGetOperationHistoryTool(
|
|
35
|
+
createCreateX402PaymentTool(liveConfig),
|
|
36
|
+
createFetchWithX402Tool(liveConfig),
|
|
37
|
+
createGetAddressesTool(liveConfig),
|
|
38
|
+
createGetBalanceTool(liveConfig),
|
|
39
|
+
createGetLimitsTool(liveConfig),
|
|
40
|
+
createGetOperationHistoryTool(liveConfig),
|
|
50
41
|
createParseX402RequirementsTool(),
|
|
51
|
-
createRevealAccountTool(
|
|
52
|
-
createSendXtzTool(
|
|
42
|
+
createRevealAccountTool(liveConfig),
|
|
43
|
+
createSendXtzTool(liveConfig),
|
|
53
44
|
];
|
|
54
45
|
if (!http) {
|
|
55
|
-
tools.push(createGetDashboardTool(
|
|
46
|
+
tools.push(createGetDashboardTool(liveConfig));
|
|
56
47
|
}
|
|
57
48
|
// Wrap all tools with config check
|
|
58
|
-
return tools.map(tool => withConfigCheck(tool,
|
|
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: (
|
|
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 = (
|
|
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 {
|
package/dist/tools/send_xtz.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import { TezosToolkit } from "@taquito/taquito";
|
|
2
1
|
import z from "zod";
|
|
3
|
-
|
|
4
|
-
|
|
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;
|
package/dist/tools/send_xtz.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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}`;
|
package/dist/webserver.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
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
|
|
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
|
-
|
|
9
|
-
|
|
15
|
+
app.use(sirv(distPath, { single: true }));
|
|
16
|
+
app.listen(port);
|
|
10
17
|
};
|