@ecadlabs/tezosx-mcp 1.0.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.
- package/LICENSE +201 -0
- package/README.md +335 -0
- package/dist/adapters/index.d.ts +37 -0
- package/dist/adapters/index.js +57 -0
- package/dist/adapters/node.d.ts +18 -0
- package/dist/adapters/node.js +35 -0
- package/dist/adapters/types.d.ts +52 -0
- package/dist/adapters/types.js +25 -0
- package/dist/adapters/worker.d.ts +35 -0
- package/dist/adapters/worker.js +50 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +144 -0
- package/dist/server.d.ts +36 -0
- package/dist/server.js +80 -0
- package/dist/tools/create_x402_payment.d.ts +27 -0
- package/dist/tools/create_x402_payment.js +55 -0
- package/dist/tools/fetch_with_x402.d.ts +28 -0
- package/dist/tools/fetch_with_x402.js +143 -0
- package/dist/tools/get_address.d.ts +20 -0
- package/dist/tools/get_address.js +24 -0
- package/dist/tools/get_addresses.d.ts +22 -0
- package/dist/tools/get_addresses.js +32 -0
- package/dist/tools/get_balance.d.ts +22 -0
- package/dist/tools/get_balance.js +27 -0
- package/dist/tools/get_dashboard.d.ts +21 -0
- package/dist/tools/get_dashboard.js +29 -0
- package/dist/tools/get_limits.d.ts +22 -0
- package/dist/tools/get_limits.js +61 -0
- package/dist/tools/get_operation_history.d.ts +21 -0
- package/dist/tools/get_operation_history.js +58 -0
- package/dist/tools/index.d.ts +113 -0
- package/dist/tools/index.js +59 -0
- package/dist/tools/parse_x402_requirements.d.ts +23 -0
- package/dist/tools/parse_x402_requirements.js +66 -0
- package/dist/tools/reveal_account.d.ts +34 -0
- package/dist/tools/reveal_account.js +51 -0
- package/dist/tools/send_xtz.d.ts +32 -0
- package/dist/tools/send_xtz.js +86 -0
- package/dist/tools/x402/sign.d.ts +12 -0
- package/dist/tools/x402/sign.js +76 -0
- package/dist/tools/x402/types.d.ts +40 -0
- package/dist/tools/x402/types.js +16 -0
- package/dist/webserver.d.ts +1 -0
- package/dist/webserver.js +10 -0
- package/dist/worker.bundle.js +134265 -0
- package/dist/worker.d.ts +13 -0
- package/dist/worker.js +132 -0
- package/frontend/dist/assets/index-RtTL1nIl.js +257 -0
- package/frontend/dist/assets/index-mSsI3AqQ.css +1 -0
- package/frontend/dist/index.html +16 -0
- package/package.json +70 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { X402ResponseSchema } from "./x402/types.js";
|
|
4
|
+
import { signX402Payment } from "./x402/sign.js";
|
|
5
|
+
import { ensureRevealed } from "./reveal_account.js";
|
|
6
|
+
export const createFetchWithX402Tool = (Tezos) => ({
|
|
7
|
+
name: "tezos_fetch_with_x402",
|
|
8
|
+
config: {
|
|
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).",
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
url: z.string().describe("The URL to fetch"),
|
|
13
|
+
maxAmountMutez: z.string().describe("Maximum amount in mutez willing to pay (e.g., '500000' for 0.5 XTZ)"),
|
|
14
|
+
method: z.string().optional().describe("HTTP method (default: GET)"),
|
|
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."),
|
|
17
|
+
}),
|
|
18
|
+
annotations: {
|
|
19
|
+
readOnlyHint: false,
|
|
20
|
+
destructiveHint: false,
|
|
21
|
+
idempotentHint: false,
|
|
22
|
+
openWorldHint: true,
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
handler: async (params) => {
|
|
26
|
+
const { url, maxAmountMutez, method = "GET", body, recipient } = params;
|
|
27
|
+
const maxAmount = parseInt(maxAmountMutez, 10);
|
|
28
|
+
if (isNaN(maxAmount) || maxAmount <= 0) {
|
|
29
|
+
throw new Error(`Invalid maxAmountMutez: ${maxAmountMutez}. Must be a positive integer.`);
|
|
30
|
+
}
|
|
31
|
+
// Build URL with recipient parameter if specified
|
|
32
|
+
let requestUrl = url;
|
|
33
|
+
if (recipient) {
|
|
34
|
+
const urlObj = new URL(url);
|
|
35
|
+
urlObj.searchParams.set("recipient", recipient);
|
|
36
|
+
requestUrl = urlObj.toString();
|
|
37
|
+
}
|
|
38
|
+
// Initial request config
|
|
39
|
+
const axiosConfig = {
|
|
40
|
+
method,
|
|
41
|
+
url: requestUrl,
|
|
42
|
+
headers: { "Content-Type": "application/json" },
|
|
43
|
+
validateStatus: () => true, // Don't throw on any status code
|
|
44
|
+
};
|
|
45
|
+
if (body && (method === "POST" || method === "PUT")) {
|
|
46
|
+
axiosConfig.data = body;
|
|
47
|
+
}
|
|
48
|
+
const initialResponse = await axios(axiosConfig);
|
|
49
|
+
// If not 402, return the response directly
|
|
50
|
+
if (initialResponse.status !== 402) {
|
|
51
|
+
return {
|
|
52
|
+
content: [{
|
|
53
|
+
type: "text",
|
|
54
|
+
text: JSON.stringify({
|
|
55
|
+
status: initialResponse.status,
|
|
56
|
+
statusText: initialResponse.statusText,
|
|
57
|
+
paymentMade: false,
|
|
58
|
+
body: typeof initialResponse.data === 'string'
|
|
59
|
+
? initialResponse.data
|
|
60
|
+
: JSON.stringify(initialResponse.data)
|
|
61
|
+
}, null, 2)
|
|
62
|
+
}]
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Parse 402 response
|
|
66
|
+
let x402Response;
|
|
67
|
+
try {
|
|
68
|
+
const responseData = typeof initialResponse.data === 'string'
|
|
69
|
+
? JSON.parse(initialResponse.data)
|
|
70
|
+
: initialResponse.data;
|
|
71
|
+
x402Response = X402ResponseSchema.parse(responseData);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw new Error(`Failed to parse x402 response: ${error instanceof Error ? error.message : error}`);
|
|
75
|
+
}
|
|
76
|
+
// Find Tezos payment requirement
|
|
77
|
+
const tezosRequirement = x402Response.paymentRequirements.find(req => req.scheme === "exact-tezos");
|
|
78
|
+
if (!tezosRequirement) {
|
|
79
|
+
throw new Error("No Tezos payment option available in x402 response");
|
|
80
|
+
}
|
|
81
|
+
const requiredAmount = parseInt(tezosRequirement.amount, 10);
|
|
82
|
+
// Check if amount is within limit
|
|
83
|
+
if (requiredAmount > maxAmount) {
|
|
84
|
+
const decimals = tezosRequirement.extra?.decimals ?? 6;
|
|
85
|
+
const requiredFormatted = requiredAmount / Math.pow(10, decimals);
|
|
86
|
+
const maxFormatted = maxAmount / Math.pow(10, decimals);
|
|
87
|
+
throw new Error(`Payment amount ${requiredFormatted} XTZ exceeds maximum allowed ${maxFormatted} XTZ`);
|
|
88
|
+
}
|
|
89
|
+
// Get source address from signer
|
|
90
|
+
const source = await Tezos.signer.publicKeyHash();
|
|
91
|
+
// Validate source has sufficient funds
|
|
92
|
+
const sourceBalance = await Tezos.tz.getBalance(source);
|
|
93
|
+
if (sourceBalance.toNumber() < requiredAmount + 10000) { // Add buffer for fees
|
|
94
|
+
throw new Error(`Insufficient balance. ` +
|
|
95
|
+
`Required: ${requiredAmount + 10000} mutez (including fees), ` +
|
|
96
|
+
`Available: ${sourceBalance.toNumber()} mutez`);
|
|
97
|
+
}
|
|
98
|
+
// Ensure account is revealed before signing
|
|
99
|
+
await ensureRevealed(Tezos);
|
|
100
|
+
// Sign the payment using shared utility
|
|
101
|
+
const signed = await signX402Payment(Tezos, {
|
|
102
|
+
network: tezosRequirement.network,
|
|
103
|
+
amount: requiredAmount,
|
|
104
|
+
recipient: tezosRequirement.recipient,
|
|
105
|
+
});
|
|
106
|
+
// Retry with payment header
|
|
107
|
+
const paidConfig = {
|
|
108
|
+
...axiosConfig,
|
|
109
|
+
headers: {
|
|
110
|
+
...axiosConfig.headers,
|
|
111
|
+
"X-PAYMENT": signed.base64,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
let paidResponse;
|
|
115
|
+
try {
|
|
116
|
+
paidResponse = await axios(paidConfig);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
throw new Error(`Payment signed but request failed: ${error instanceof Error ? error.message : error}`);
|
|
120
|
+
}
|
|
121
|
+
const decimals = tezosRequirement.extra?.decimals ?? 6;
|
|
122
|
+
const amountFormatted = requiredAmount / Math.pow(10, decimals);
|
|
123
|
+
return {
|
|
124
|
+
content: [{
|
|
125
|
+
type: "text",
|
|
126
|
+
text: JSON.stringify({
|
|
127
|
+
status: paidResponse.status,
|
|
128
|
+
statusText: paidResponse.statusText,
|
|
129
|
+
paymentMade: true,
|
|
130
|
+
paymentDetails: {
|
|
131
|
+
amount: tezosRequirement.amount,
|
|
132
|
+
amountFormatted: `${amountFormatted} XTZ`,
|
|
133
|
+
recipient: tezosRequirement.recipient,
|
|
134
|
+
network: tezosRequirement.network,
|
|
135
|
+
},
|
|
136
|
+
body: typeof paidResponse.data === 'string'
|
|
137
|
+
? paidResponse.data
|
|
138
|
+
: JSON.stringify(paidResponse.data)
|
|
139
|
+
}, null, 2)
|
|
140
|
+
}]
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TezosToolkit } from "@taquito/taquito";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
export declare const createGetAddressTool: (Tezos: TezosToolkit) => {
|
|
4
|
+
name: string;
|
|
5
|
+
config: {
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: z.ZodObject<{}, z.z.core.$strip>;
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: boolean;
|
|
11
|
+
destructiveHint: boolean;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
handler: () => Promise<{
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
export const createGetAddressTool = (Tezos) => ({
|
|
3
|
+
name: "tezos_get_address",
|
|
4
|
+
config: {
|
|
5
|
+
title: "Get Tezos Address",
|
|
6
|
+
description: "Returns the currently configured Tezos address",
|
|
7
|
+
inputSchema: z.object({}),
|
|
8
|
+
annotations: {
|
|
9
|
+
readOnlyHint: true,
|
|
10
|
+
destructiveHint: false
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
handler: async () => {
|
|
14
|
+
try {
|
|
15
|
+
const address = await Tezos.signer.publicKeyHash();
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: "text", text: address }]
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new ReferenceError("Failed to get public key from signer.");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TezosToolkit } from "@taquito/taquito";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
export declare const createGetAddressesTool: (Tezos: TezosToolkit, spendingContract: string) => {
|
|
4
|
+
name: string;
|
|
5
|
+
config: {
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: z.ZodObject<{}, z.z.core.$strip>;
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: boolean;
|
|
11
|
+
destructiveHint: boolean;
|
|
12
|
+
idempotentHint: boolean;
|
|
13
|
+
openWorldHint: boolean;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
handler: () => Promise<{
|
|
17
|
+
content: {
|
|
18
|
+
type: "text";
|
|
19
|
+
text: string;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
export const createGetAddressesTool = (Tezos, spendingContract) => ({
|
|
3
|
+
name: "tezos_get_addresses",
|
|
4
|
+
config: {
|
|
5
|
+
title: "Get Tezos Addresses",
|
|
6
|
+
description: "Returns the spending contract address, owner address, and spender address from the contract storage.",
|
|
7
|
+
inputSchema: z.object({}),
|
|
8
|
+
annotations: {
|
|
9
|
+
readOnlyHint: true,
|
|
10
|
+
destructiveHint: false,
|
|
11
|
+
idempotentHint: true,
|
|
12
|
+
openWorldHint: false,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
handler: async () => {
|
|
16
|
+
const contract = await Tezos.contract.at(spendingContract);
|
|
17
|
+
const storage = (await contract.storage());
|
|
18
|
+
const result = {
|
|
19
|
+
contractAddress: spendingContract,
|
|
20
|
+
ownerAddress: storage.owner,
|
|
21
|
+
spenderAddress: storage.spender,
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: JSON.stringify(result, null, 2),
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TezosToolkit } from "@taquito/taquito";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
export declare const createGetBalanceTool: (Tezos: TezosToolkit, spendingContract: string, spendingAddress: string) => {
|
|
4
|
+
name: string;
|
|
5
|
+
config: {
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: z.ZodObject<{}, z.z.core.$strip>;
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: boolean;
|
|
11
|
+
destructiveHint: boolean;
|
|
12
|
+
idempotentHint: boolean;
|
|
13
|
+
openWorldHint: boolean;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
handler: () => Promise<{
|
|
17
|
+
content: {
|
|
18
|
+
type: "text";
|
|
19
|
+
text: string;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
export const createGetBalanceTool = (Tezos, spendingContract, spendingAddress) => ({
|
|
3
|
+
name: "tezos_get_balance",
|
|
4
|
+
config: {
|
|
5
|
+
title: "Get Balances",
|
|
6
|
+
description: "Returns the balance of the spending contract (usable tokens) and spending address (tokens for fees)",
|
|
7
|
+
inputSchema: z.object({}),
|
|
8
|
+
annotations: {
|
|
9
|
+
readOnlyHint: true,
|
|
10
|
+
destructiveHint: false,
|
|
11
|
+
idempotentHint: false,
|
|
12
|
+
openWorldHint: true,
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
handler: async () => {
|
|
16
|
+
try {
|
|
17
|
+
const spendingAddressBalance = (await Tezos.tz.getBalance(spendingAddress)).toString();
|
|
18
|
+
const spendingContractBalance = (await Tezos.tz.getBalance(spendingContract)).toString();
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text: `Spending address balance: ${spendingAddressBalance} mutez. Spending contract balance: ${spendingContractBalance} mutez` }]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw new Error(`Failed to get balance: ${error instanceof Error ? error.message : error}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
export declare const createGetDashboardTool: (spendingContract: string) => {
|
|
3
|
+
name: string;
|
|
4
|
+
config: {
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
inputSchema: z.ZodObject<{}, z.z.core.$strip>;
|
|
8
|
+
annotations: {
|
|
9
|
+
readOnlyHint: boolean;
|
|
10
|
+
destructiveHint: boolean;
|
|
11
|
+
idempotentHint: boolean;
|
|
12
|
+
openWorldHint: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
handler: () => Promise<{
|
|
16
|
+
content: {
|
|
17
|
+
type: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import { DEFAULT_WEB_PORT } from '../index.js';
|
|
3
|
+
export const createGetDashboardTool = (spendingContract) => ({
|
|
4
|
+
name: "tezos_get_dashboard",
|
|
5
|
+
config: {
|
|
6
|
+
title: "Get Contract Dashboard",
|
|
7
|
+
description: "Returns a link to the dashboard where the user can configure their contracts spending limits, spending keys, and deploy a new spending contract.",
|
|
8
|
+
inputSchema: z.object({}),
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: true,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
idempotentHint: true,
|
|
13
|
+
openWorldHint: false,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
handler: async () => {
|
|
17
|
+
const port = process.env.WEB_PORT || DEFAULT_WEB_PORT;
|
|
18
|
+
const network = process.env.TEZOS_NETWORK || 'shadownet';
|
|
19
|
+
const url = `http://localhost:${port}?contract=${spendingContract}&network=${network}`;
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "text",
|
|
24
|
+
text: url,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TezosToolkit } from "@taquito/taquito";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
export declare const createGetLimitsTool: (Tezos: TezosToolkit, spendingContract: string) => {
|
|
4
|
+
name: string;
|
|
5
|
+
config: {
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: z.ZodObject<{}, z.z.core.$strip>;
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: boolean;
|
|
11
|
+
destructiveHint: boolean;
|
|
12
|
+
idempotentHint: boolean;
|
|
13
|
+
openWorldHint: boolean;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
handler: () => Promise<{
|
|
17
|
+
content: {
|
|
18
|
+
type: "text";
|
|
19
|
+
text: string;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
// Constants
|
|
3
|
+
const MUTEZ_PER_TEZ = 1_000_000;
|
|
4
|
+
/** Convert mutez to XTZ */
|
|
5
|
+
const mutezToXtz = (mutez) => mutez / MUTEZ_PER_TEZ;
|
|
6
|
+
export const createGetLimitsTool = (Tezos, spendingContract) => ({
|
|
7
|
+
name: "tezos_get_limits",
|
|
8
|
+
config: {
|
|
9
|
+
title: "Get Spending Limits",
|
|
10
|
+
description: "Returns the spending limits and current usage from the contract: daily limit, per-transaction limit, amount spent today, and time until reset.",
|
|
11
|
+
inputSchema: z.object({}),
|
|
12
|
+
annotations: {
|
|
13
|
+
readOnlyHint: true,
|
|
14
|
+
destructiveHint: false,
|
|
15
|
+
idempotentHint: true,
|
|
16
|
+
openWorldHint: false,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
handler: async () => {
|
|
20
|
+
const contract = await Tezos.contract.at(spendingContract);
|
|
21
|
+
const storage = (await contract.storage());
|
|
22
|
+
const dailyLimitMutez = storage.daily_limit.toNumber();
|
|
23
|
+
const perTxLimitMutez = storage.per_tx_limit.toNumber();
|
|
24
|
+
const spentTodayMutez = storage.spent_today.toNumber();
|
|
25
|
+
const lastReset = new Date(storage.last_reset);
|
|
26
|
+
// Calculate time until next reset (24 hours from last_reset)
|
|
27
|
+
const nextReset = new Date(lastReset.getTime() + 24 * 60 * 60 * 1000);
|
|
28
|
+
const now = new Date();
|
|
29
|
+
const msUntilReset = Math.max(0, nextReset.getTime() - now.getTime());
|
|
30
|
+
const hoursUntilReset = Math.floor(msUntilReset / (1000 * 60 * 60));
|
|
31
|
+
const minutesUntilReset = Math.floor((msUntilReset % (1000 * 60 * 60)) / (1000 * 60));
|
|
32
|
+
const result = {
|
|
33
|
+
dailyLimit: {
|
|
34
|
+
xtz: mutezToXtz(dailyLimitMutez),
|
|
35
|
+
mutez: dailyLimitMutez,
|
|
36
|
+
},
|
|
37
|
+
perTransactionLimit: {
|
|
38
|
+
xtz: mutezToXtz(perTxLimitMutez),
|
|
39
|
+
mutez: perTxLimitMutez,
|
|
40
|
+
},
|
|
41
|
+
spentToday: {
|
|
42
|
+
xtz: mutezToXtz(spentTodayMutez),
|
|
43
|
+
mutez: spentTodayMutez,
|
|
44
|
+
},
|
|
45
|
+
remainingDaily: {
|
|
46
|
+
xtz: mutezToXtz(Math.max(0, dailyLimitMutez - spentTodayMutez)),
|
|
47
|
+
mutez: Math.max(0, dailyLimitMutez - spentTodayMutez),
|
|
48
|
+
},
|
|
49
|
+
lastReset: storage.last_reset,
|
|
50
|
+
timeUntilReset: `${hoursUntilReset}h ${minutesUntilReset}m`,
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: JSON.stringify(result, null, 2),
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
export declare const createGetOperationHistoryTool: (spendingContract: string, tzktApi: string) => {
|
|
3
|
+
name: string;
|
|
4
|
+
config: {
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
inputSchema: z.ZodObject<{}, z.z.core.$strip>;
|
|
8
|
+
annotations: {
|
|
9
|
+
readOnlyHint: boolean;
|
|
10
|
+
destructiveHint: boolean;
|
|
11
|
+
idempotentHint: boolean;
|
|
12
|
+
openWorldHint: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
handler: () => Promise<{
|
|
16
|
+
content: {
|
|
17
|
+
type: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
export const createGetOperationHistoryTool = (spendingContract, tzktApi) => ({
|
|
4
|
+
name: "tezos_get_operation_history",
|
|
5
|
+
config: {
|
|
6
|
+
title: "Get Operation History",
|
|
7
|
+
description: "Returns the last 100 transaction operations for the spending contract from TzKT API, ordered from most recent to oldest.",
|
|
8
|
+
inputSchema: z.object({}),
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: true,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
idempotentHint: true,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
handler: async () => {
|
|
17
|
+
try {
|
|
18
|
+
const url = `${tzktApi}/v1/accounts/${spendingContract}/operations?type=transaction&limit=100`;
|
|
19
|
+
const response = await axios.get(url);
|
|
20
|
+
const allOperations = response.data;
|
|
21
|
+
// Filter to only incoming/outgoing transfers, exclude spend calls
|
|
22
|
+
const operations = allOperations.filter((op) => {
|
|
23
|
+
const entrypoint = op.parameter?.entrypoint;
|
|
24
|
+
const isSpendCall = op.target.address === spendingContract && entrypoint === "spend";
|
|
25
|
+
const isOutgoing = op.sender.address === spendingContract;
|
|
26
|
+
const isIncoming = op.target.address === spendingContract && !isSpendCall;
|
|
27
|
+
return isIncoming || isOutgoing;
|
|
28
|
+
});
|
|
29
|
+
if (operations.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: `No transaction history found for contract ${spendingContract}`,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const formattedOperations = operations.map((op, index) => {
|
|
40
|
+
const amount = (op.amount / 1_000_000).toFixed(6);
|
|
41
|
+
const date = new Date(op.timestamp).toISOString();
|
|
42
|
+
const direction = op.sender.address === spendingContract ? "OUT" : "IN";
|
|
43
|
+
return `${index + 1}. [${direction}] [${date}] ${op.hash}
|
|
44
|
+
From: ${op.sender.address}
|
|
45
|
+
To: ${op.target.address}
|
|
46
|
+
Amount: ${amount} XTZ
|
|
47
|
+
Status: ${op.status}`;
|
|
48
|
+
});
|
|
49
|
+
const summary = `Operation history for ${spendingContract} (${operations.length} transactions):\n\n${formattedOperations.join("\n\n")}`;
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: "text", text: summary }],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(`Failed to fetch operation history: ${error instanceof Error ? error.message : error}`);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { WalletConfig } from "../index.js";
|
|
2
|
+
export declare const createTools: (walletConfig: WalletConfig, tzktApi: string, http: boolean) => ({
|
|
3
|
+
name: string;
|
|
4
|
+
config: {
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
inputSchema: import("zod").ZodObject<{
|
|
8
|
+
network: import("zod").ZodString;
|
|
9
|
+
asset: import("zod").ZodString;
|
|
10
|
+
amount: import("zod").ZodString;
|
|
11
|
+
recipient: import("zod").ZodString;
|
|
12
|
+
}, import("zod/v4/core").$strip>;
|
|
13
|
+
annotations: {
|
|
14
|
+
readOnlyHint: boolean;
|
|
15
|
+
destructiveHint: boolean;
|
|
16
|
+
idempotentHint: boolean;
|
|
17
|
+
openWorldHint: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
handler: (params: any) => Promise<{
|
|
21
|
+
content: {
|
|
22
|
+
type: "text";
|
|
23
|
+
text: string;
|
|
24
|
+
}[];
|
|
25
|
+
}>;
|
|
26
|
+
} | {
|
|
27
|
+
name: string;
|
|
28
|
+
config: {
|
|
29
|
+
title: string;
|
|
30
|
+
description: string;
|
|
31
|
+
inputSchema: import("zod").ZodObject<{}, import("zod/v4/core").$strip>;
|
|
32
|
+
annotations: {
|
|
33
|
+
readOnlyHint: boolean;
|
|
34
|
+
destructiveHint: boolean;
|
|
35
|
+
idempotentHint: boolean;
|
|
36
|
+
openWorldHint: boolean;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
handler: () => Promise<{
|
|
40
|
+
content: {
|
|
41
|
+
type: "text";
|
|
42
|
+
text: string;
|
|
43
|
+
}[];
|
|
44
|
+
}>;
|
|
45
|
+
} | {
|
|
46
|
+
name: string;
|
|
47
|
+
config: {
|
|
48
|
+
title: string;
|
|
49
|
+
description: string;
|
|
50
|
+
inputSchema: import("zod").ZodObject<{
|
|
51
|
+
url: import("zod").ZodString;
|
|
52
|
+
maxAmountMutez: import("zod").ZodString;
|
|
53
|
+
method: import("zod").ZodOptional<import("zod").ZodString>;
|
|
54
|
+
body: import("zod").ZodOptional<import("zod").ZodString>;
|
|
55
|
+
recipient: import("zod").ZodOptional<import("zod").ZodString>;
|
|
56
|
+
}, import("zod/v4/core").$strip>;
|
|
57
|
+
annotations: {
|
|
58
|
+
readOnlyHint: boolean;
|
|
59
|
+
destructiveHint: boolean;
|
|
60
|
+
idempotentHint: boolean;
|
|
61
|
+
openWorldHint: boolean;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
handler: (params: any) => Promise<{
|
|
65
|
+
content: {
|
|
66
|
+
type: "text";
|
|
67
|
+
text: string;
|
|
68
|
+
}[];
|
|
69
|
+
}>;
|
|
70
|
+
} | {
|
|
71
|
+
name: string;
|
|
72
|
+
config: {
|
|
73
|
+
title: string;
|
|
74
|
+
description: string;
|
|
75
|
+
inputSchema: import("zod").ZodObject<{
|
|
76
|
+
responseBody: import("zod").ZodString;
|
|
77
|
+
}, import("zod/v4/core").$strip>;
|
|
78
|
+
annotations: {
|
|
79
|
+
readOnlyHint: boolean;
|
|
80
|
+
destructiveHint: boolean;
|
|
81
|
+
idempotentHint: boolean;
|
|
82
|
+
openWorldHint: boolean;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
handler: (params: any) => Promise<{
|
|
86
|
+
content: {
|
|
87
|
+
type: "text";
|
|
88
|
+
text: string;
|
|
89
|
+
}[];
|
|
90
|
+
}>;
|
|
91
|
+
} | {
|
|
92
|
+
name: string;
|
|
93
|
+
config: {
|
|
94
|
+
title: string;
|
|
95
|
+
description: string;
|
|
96
|
+
inputSchema: import("zod").ZodObject<{
|
|
97
|
+
toAddress: import("zod").ZodString;
|
|
98
|
+
amount: import("zod").ZodNumber;
|
|
99
|
+
}, import("zod/v4/core").$strip>;
|
|
100
|
+
annotations: {
|
|
101
|
+
readOnlyHint: boolean;
|
|
102
|
+
destructiveHint: boolean;
|
|
103
|
+
idempotentHint: boolean;
|
|
104
|
+
openWorldHint: boolean;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
handler: (params: any) => Promise<{
|
|
108
|
+
content: {
|
|
109
|
+
type: "text";
|
|
110
|
+
text: string;
|
|
111
|
+
}[];
|
|
112
|
+
}>;
|
|
113
|
+
})[];
|