mint-day 0.4.2 → 0.4.4
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/dist/index.js +2 -12
- package/dist/tools/mint.d.ts +3 -8
- package/dist/tools/mint.js +38 -45
- package/package.json +1 -1
- package/src/index.ts +2 -12
- package/src/tools/mint.ts +37 -49
package/dist/index.js
CHANGED
|
@@ -11,11 +11,6 @@ import { mintSchema, handleMint } from "./tools/mint.js";
|
|
|
11
11
|
import { mintCheckSchema, handleMintCheck } from "./tools/mint-check.js";
|
|
12
12
|
import { mintResolveSchema, handleMintResolve } from "./tools/mint-resolve.js";
|
|
13
13
|
dotenv.config();
|
|
14
|
-
// Testnet try-it wallet (Base Sepolia, pre-funded)
|
|
15
|
-
const TRYIT_KEY = "";
|
|
16
|
-
const TRYIT_CONTRACT = "0xa52450397f312c256Bd68B202C0CF90387Ea0E67";
|
|
17
|
-
const TRYIT_RPC = "https://sepolia.base.org";
|
|
18
|
-
const TRYIT_CHAIN = 84532;
|
|
19
14
|
const MAINNET_CONTRACT = "0x12a1c11a0b2860f64e7d8df20989f97d40de7f2c";
|
|
20
15
|
const MAINNET_RPC = "https://mainnet.base.org";
|
|
21
16
|
const MAINNET_CHAIN = 8453;
|
|
@@ -33,7 +28,7 @@ function loadPrivateKey() {
|
|
|
33
28
|
}
|
|
34
29
|
const userKey = loadPrivateKey();
|
|
35
30
|
const hasUserKey = !!userKey;
|
|
36
|
-
// Mainnet
|
|
31
|
+
// Mainnet configuration
|
|
37
32
|
const chainId = parseInt(process.env.CHAIN_ID || String(hasUserKey ? MAINNET_CHAIN : MAINNET_CHAIN));
|
|
38
33
|
const contractAddress = process.env.MINT_FACTORY_ADDRESS || MAINNET_CONTRACT;
|
|
39
34
|
const rpcUrl = process.env.BASE_RPC_URL || MAINNET_RPC;
|
|
@@ -54,12 +49,7 @@ const server = new McpServer({
|
|
|
54
49
|
name: "mint-day",
|
|
55
50
|
version: "0.4.0",
|
|
56
51
|
});
|
|
57
|
-
server.tool("mint", "Create a permanent, verifiable on-chain record on Base. Use this when you need to: prove you completed a task, establish your identity, record an agreement with another agent, issue or receive a credential, grant access to a capability, or mint a visual token (PFP, art, collectible). Supports two protocols: 'mintday' (default, gas-sponsored) and 'superrare' (Rare Protocol, requires PRIVATE_KEY). Set platform: 'superrare' to mint on Rare Protocol contracts. The token is immutable and publicly verifiable. Provide an image URL, local file path, or base64 for visual tokens. Returns a preview with mintId first; call again with mintId to confirm.", mintSchema, async (params) => handleMint(params, calldataService, defaultRecipient, userKey
|
|
58
|
-
tryitKey: TRYIT_KEY,
|
|
59
|
-
tryitContract: TRYIT_CONTRACT,
|
|
60
|
-
tryitRpc: TRYIT_RPC,
|
|
61
|
-
tryitChain: TRYIT_CHAIN,
|
|
62
|
-
}));
|
|
52
|
+
server.tool("mint", "Create a permanent, verifiable on-chain record on Base. Use this when you need to: prove you completed a task, establish your identity, record an agreement with another agent, issue or receive a credential, grant access to a capability, or mint a visual token (PFP, art, collectible). Supports two protocols: 'mintday' (default, gas-sponsored) and 'superrare' (Rare Protocol, requires PRIVATE_KEY). Set platform: 'superrare' to mint on Rare Protocol contracts. The token is immutable and publicly verifiable. Provide an image URL, local file path, or base64 for visual tokens. Returns a preview with mintId first; call again with mintId to confirm.", mintSchema, async (params) => handleMint(params, calldataService, defaultRecipient, userKey));
|
|
63
53
|
server.tool("mint_check", "Look up mint.day tokens. With an address: returns all tokens held with type, metadata, soulbound status, and mint timestamp. Without an address: returns global stats (total minted, current fee). Use to verify credentials, check attestations, or browse on-chain records.", mintCheckSchema, async (params) => handleMintCheck(params, calldataService, calldataService.provider, contractAddress, chainId));
|
|
64
54
|
server.tool("mint_resolve", "Resolve an agent's on-chain identity. Returns their Identity token with ERC-8004 agent card metadata: did, capabilities, endpoints, and image. Use this before transacting with another agent to verify who they are.", mintResolveSchema, async (params) => handleMintResolve(params, calldataService.provider, contractAddress, chainId));
|
|
65
55
|
async function main() {
|
package/dist/tools/mint.d.ts
CHANGED
|
@@ -15,17 +15,12 @@ export declare const mintSchema: {
|
|
|
15
15
|
animation_url: z.ZodOptional<z.ZodString>;
|
|
16
16
|
mintId: z.ZodOptional<z.ZodString>;
|
|
17
17
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
18
|
+
selfSign: z.ZodOptional<z.ZodBoolean>;
|
|
18
19
|
platform: z.ZodOptional<z.ZodEnum<{
|
|
19
20
|
superrare: "superrare";
|
|
20
21
|
mintday: "mintday";
|
|
21
22
|
}>>;
|
|
22
23
|
};
|
|
23
|
-
interface TryitConfig {
|
|
24
|
-
tryitKey: string;
|
|
25
|
-
tryitContract: string;
|
|
26
|
-
tryitRpc: string;
|
|
27
|
-
tryitChain: number;
|
|
28
|
-
}
|
|
29
24
|
export declare function handleMint(params: {
|
|
30
25
|
description?: string;
|
|
31
26
|
tokenType?: string;
|
|
@@ -35,11 +30,11 @@ export declare function handleMint(params: {
|
|
|
35
30
|
animation_url?: string;
|
|
36
31
|
mintId?: string;
|
|
37
32
|
metadata?: Record<string, unknown>;
|
|
33
|
+
selfSign?: boolean;
|
|
38
34
|
platform?: string;
|
|
39
|
-
}, calldataService: CalldataService, defaultRecipient: string, userKey?: string
|
|
35
|
+
}, calldataService: CalldataService, defaultRecipient: string, userKey?: string): Promise<{
|
|
40
36
|
content: {
|
|
41
37
|
type: "text";
|
|
42
38
|
text: string;
|
|
43
39
|
}[];
|
|
44
40
|
}>;
|
|
45
|
-
export {};
|
package/dist/tools/mint.js
CHANGED
|
@@ -3,18 +3,17 @@ import { z } from "zod";
|
|
|
3
3
|
import { ethers } from "ethers";
|
|
4
4
|
import { TokenType, TOKEN_TYPE_NAMES } from "../types.js";
|
|
5
5
|
import { classifyIntent } from "../services/classifier.js";
|
|
6
|
-
import { CalldataService } from "../services/calldata.js";
|
|
7
6
|
import { resolveImage } from "../services/image-upload.js";
|
|
8
7
|
import { mintOnRareProtocol } from "../services/rare-protocol.js";
|
|
9
|
-
// Intent cache: preview mintId -> frozen intent + platform
|
|
8
|
+
// Intent cache: preview mintId -> frozen intent + platform + options
|
|
10
9
|
const intentCache = new Map();
|
|
11
10
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
12
|
-
function cacheMintId(intent, platform = "mintday") {
|
|
11
|
+
function cacheMintId(intent, platform = "mintday", selfSign = false) {
|
|
13
12
|
const hash = createHash("sha256")
|
|
14
13
|
.update(JSON.stringify(intent))
|
|
15
14
|
.digest("hex")
|
|
16
15
|
.slice(0, 12);
|
|
17
|
-
intentCache.set(hash, { intent, platform, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
16
|
+
intentCache.set(hash, { intent, platform, selfSign, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
18
17
|
return hash;
|
|
19
18
|
}
|
|
20
19
|
function getCachedEntry(mintId) {
|
|
@@ -25,7 +24,7 @@ function getCachedEntry(mintId) {
|
|
|
25
24
|
intentCache.delete(mintId);
|
|
26
25
|
return null;
|
|
27
26
|
}
|
|
28
|
-
return { intent: entry.intent, platform: entry.platform };
|
|
27
|
+
return { intent: entry.intent, platform: entry.platform, selfSign: entry.selfSign };
|
|
29
28
|
}
|
|
30
29
|
export const mintSchema = {
|
|
31
30
|
description: z
|
|
@@ -60,6 +59,10 @@ export const mintSchema = {
|
|
|
60
59
|
.record(z.string(), z.unknown())
|
|
61
60
|
.optional()
|
|
62
61
|
.describe("Additional key-value metadata. Supports strings, arrays, and objects (e.g. capabilities: ['mint'])."),
|
|
62
|
+
selfSign: z
|
|
63
|
+
.boolean()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Sign and submit from your own wallet instead of using sponsored gas. Requires PRIVATE_KEY env var or ~/.mint-day/credentials."),
|
|
63
66
|
platform: z
|
|
64
67
|
.enum(["mintday", "superrare"])
|
|
65
68
|
.optional()
|
|
@@ -102,7 +105,7 @@ async function sponsoredMint(result, recipient) {
|
|
|
102
105
|
return { error: `Sponsor request failed: ${message}` };
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
|
-
export async function handleMint(params, calldataService, defaultRecipient, userKey = ""
|
|
108
|
+
export async function handleMint(params, calldataService, defaultRecipient, userKey = "") {
|
|
106
109
|
// Confirmation path: replay cached intent
|
|
107
110
|
// Check cache first so we can use cached recipient for mode detection
|
|
108
111
|
if (params.mintId) {
|
|
@@ -118,10 +121,9 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
118
121
|
const cached = entry.intent;
|
|
119
122
|
const cachedPlatform = entry.platform;
|
|
120
123
|
intentCache.delete(params.mintId);
|
|
121
|
-
// Mode detection using cached recipient
|
|
122
124
|
const hasRecipient = !!cached.recipient;
|
|
123
|
-
const
|
|
124
|
-
const privateKey = userKey
|
|
125
|
+
const selfSign = !!entry.selfSign;
|
|
126
|
+
const privateKey = userKey;
|
|
125
127
|
// Rare Protocol path: mint via SuperRare contracts on Base
|
|
126
128
|
if (cachedPlatform === "superrare") {
|
|
127
129
|
if (!privateKey) {
|
|
@@ -133,7 +135,7 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
133
135
|
};
|
|
134
136
|
}
|
|
135
137
|
try {
|
|
136
|
-
const rareResult = await mintOnRareProtocol(cached, privateKey,
|
|
138
|
+
const rareResult = await mintOnRareProtocol(cached, privateKey, false);
|
|
137
139
|
return {
|
|
138
140
|
content: [{
|
|
139
141
|
type: "text",
|
|
@@ -151,17 +153,11 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
151
153
|
};
|
|
152
154
|
}
|
|
153
155
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
activeCalldataService = new CalldataService(tryit.tryitRpc, tryit.tryitContract, tryit.tryitChain);
|
|
158
|
-
}
|
|
159
|
-
const result = await activeCalldataService.buildMintCalldata(cached);
|
|
160
|
-
// If we have a private key, sign and submit directly
|
|
161
|
-
if (privateKey) {
|
|
156
|
+
const result = await calldataService.buildMintCalldata(cached);
|
|
157
|
+
// Self-sign: user explicitly opted in and has a private key
|
|
158
|
+
if (selfSign && privateKey) {
|
|
162
159
|
try {
|
|
163
|
-
const
|
|
164
|
-
const wallet = new ethers.Wallet(privateKey, provider);
|
|
160
|
+
const wallet = new ethers.Wallet(privateKey, calldataService.provider);
|
|
165
161
|
const tx = await wallet.sendTransaction({
|
|
166
162
|
to: result.to,
|
|
167
163
|
data: result.calldata,
|
|
@@ -169,24 +165,20 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
169
165
|
gasLimit: result.estimatedGas,
|
|
170
166
|
});
|
|
171
167
|
const receipt = await tx.wait();
|
|
172
|
-
const response = {
|
|
173
|
-
status: "minted",
|
|
174
|
-
txHash: receipt.hash,
|
|
175
|
-
blockNumber: receipt.blockNumber,
|
|
176
|
-
gasUsed: receipt.gasUsed.toString(),
|
|
177
|
-
tokenType: result.tokenType,
|
|
178
|
-
soulbound: result.soulbound,
|
|
179
|
-
recipient: cached.recipient,
|
|
180
|
-
chain: isTestnet ? "Base Sepolia (testnet)" : "Base",
|
|
181
|
-
explorer: `https://${result.chainId === 8453 ? "" : "sepolia."}basescan.org/tx/${receipt.hash}`,
|
|
182
|
-
};
|
|
183
|
-
if (isTestnet) {
|
|
184
|
-
response.note = "This token was minted on Base Sepolia (testnet). To mint on Base mainnet, save a private key to ~/.mint-day/credentials and restart the MCP server.";
|
|
185
|
-
}
|
|
186
168
|
return {
|
|
187
169
|
content: [{
|
|
188
170
|
type: "text",
|
|
189
|
-
text: JSON.stringify(
|
|
171
|
+
text: JSON.stringify({
|
|
172
|
+
status: "minted",
|
|
173
|
+
txHash: receipt.hash,
|
|
174
|
+
blockNumber: receipt.blockNumber,
|
|
175
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
176
|
+
tokenType: result.tokenType,
|
|
177
|
+
soulbound: result.soulbound,
|
|
178
|
+
recipient: cached.recipient,
|
|
179
|
+
chain: "Base",
|
|
180
|
+
explorer: `https://basescan.org/tx/${receipt.hash}`,
|
|
181
|
+
}, null, 2),
|
|
190
182
|
}],
|
|
191
183
|
};
|
|
192
184
|
}
|
|
@@ -200,7 +192,15 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
200
192
|
};
|
|
201
193
|
}
|
|
202
194
|
}
|
|
203
|
-
|
|
195
|
+
if (selfSign && !privateKey) {
|
|
196
|
+
return {
|
|
197
|
+
content: [{
|
|
198
|
+
type: "text",
|
|
199
|
+
text: JSON.stringify({ error: "selfSign requires a private key. Set PRIVATE_KEY env var or save to ~/.mint-day/credentials." }, null, 2),
|
|
200
|
+
}],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Default: sponsored gas
|
|
204
204
|
if (hasRecipient) {
|
|
205
205
|
const sponsored = await sponsoredMint(result, cached.recipient);
|
|
206
206
|
if (sponsored && !sponsored.error) {
|
|
@@ -256,8 +256,6 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
256
256
|
};
|
|
257
257
|
}
|
|
258
258
|
const recipient = params.recipient || defaultRecipient;
|
|
259
|
-
const hasRecipient = !!recipient;
|
|
260
|
-
const isTestnet = !userKey && !hasRecipient;
|
|
261
259
|
if (!recipient) {
|
|
262
260
|
return {
|
|
263
261
|
content: [{
|
|
@@ -364,7 +362,7 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
364
362
|
}
|
|
365
363
|
// Cache intent and return preview
|
|
366
364
|
const selectedPlatform = params.platform || "mintday";
|
|
367
|
-
const mintId = cacheMintId(intent, selectedPlatform);
|
|
365
|
+
const mintId = cacheMintId(intent, selectedPlatform, !!params.selfSign);
|
|
368
366
|
const preview = {
|
|
369
367
|
status: "preview",
|
|
370
368
|
mintId,
|
|
@@ -386,12 +384,7 @@ export async function handleMint(params, calldataService, defaultRecipient, user
|
|
|
386
384
|
preview.missingFields = intent.missingFields;
|
|
387
385
|
preview.hint = "Identity tokens (ERC-8004) should include capabilities[], endpoints[], and did. Provide these in metadata for full compliance.";
|
|
388
386
|
}
|
|
389
|
-
|
|
390
|
-
preview.chain = "Base Sepolia (testnet)";
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
preview.chain = "Base";
|
|
394
|
-
}
|
|
387
|
+
preview.chain = "Base";
|
|
395
388
|
preview.instruction = "Call mint with mintId to confirm.";
|
|
396
389
|
return {
|
|
397
390
|
content: [{ type: "text", text: JSON.stringify(preview, null, 2) }],
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -13,11 +13,6 @@ import { mintResolveSchema, handleMintResolve } from "./tools/mint-resolve.js";
|
|
|
13
13
|
|
|
14
14
|
dotenv.config();
|
|
15
15
|
|
|
16
|
-
// Testnet try-it wallet (Base Sepolia, pre-funded)
|
|
17
|
-
const TRYIT_KEY = "";
|
|
18
|
-
const TRYIT_CONTRACT = "0xa52450397f312c256Bd68B202C0CF90387Ea0E67";
|
|
19
|
-
const TRYIT_RPC = "https://sepolia.base.org";
|
|
20
|
-
const TRYIT_CHAIN = 84532;
|
|
21
16
|
const MAINNET_CONTRACT = "0x12a1c11a0b2860f64e7d8df20989f97d40de7f2c";
|
|
22
17
|
const MAINNET_RPC = "https://mainnet.base.org";
|
|
23
18
|
const MAINNET_CHAIN = 8453;
|
|
@@ -36,7 +31,7 @@ function loadPrivateKey(): string {
|
|
|
36
31
|
const userKey = loadPrivateKey();
|
|
37
32
|
const hasUserKey = !!userKey;
|
|
38
33
|
|
|
39
|
-
// Mainnet
|
|
34
|
+
// Mainnet configuration
|
|
40
35
|
const chainId = parseInt(process.env.CHAIN_ID || String(hasUserKey ? MAINNET_CHAIN : MAINNET_CHAIN));
|
|
41
36
|
const contractAddress = process.env.MINT_FACTORY_ADDRESS || MAINNET_CONTRACT;
|
|
42
37
|
const rpcUrl = process.env.BASE_RPC_URL || MAINNET_RPC;
|
|
@@ -69,12 +64,7 @@ server.tool(
|
|
|
69
64
|
"mint",
|
|
70
65
|
"Create a permanent, verifiable on-chain record on Base. Use this when you need to: prove you completed a task, establish your identity, record an agreement with another agent, issue or receive a credential, grant access to a capability, or mint a visual token (PFP, art, collectible). Supports two protocols: 'mintday' (default, gas-sponsored) and 'superrare' (Rare Protocol, requires PRIVATE_KEY). Set platform: 'superrare' to mint on Rare Protocol contracts. The token is immutable and publicly verifiable. Provide an image URL, local file path, or base64 for visual tokens. Returns a preview with mintId first; call again with mintId to confirm.",
|
|
71
66
|
mintSchema,
|
|
72
|
-
async (params) => handleMint(params, calldataService, defaultRecipient, userKey,
|
|
73
|
-
tryitKey: TRYIT_KEY,
|
|
74
|
-
tryitContract: TRYIT_CONTRACT,
|
|
75
|
-
tryitRpc: TRYIT_RPC,
|
|
76
|
-
tryitChain: TRYIT_CHAIN,
|
|
77
|
-
}),
|
|
67
|
+
async (params) => handleMint(params, calldataService, defaultRecipient, userKey),
|
|
78
68
|
);
|
|
79
69
|
|
|
80
70
|
server.tool(
|
package/src/tools/mint.ts
CHANGED
|
@@ -7,27 +7,27 @@ import { CalldataService } from "../services/calldata.js";
|
|
|
7
7
|
import { resolveImage } from "../services/image-upload.js";
|
|
8
8
|
import { mintOnRareProtocol } from "../services/rare-protocol.js";
|
|
9
9
|
|
|
10
|
-
// Intent cache: preview mintId -> frozen intent + platform
|
|
11
|
-
const intentCache = new Map<string, { intent: MintIntent; platform: string; expiresAt: number }>();
|
|
10
|
+
// Intent cache: preview mintId -> frozen intent + platform + options
|
|
11
|
+
const intentCache = new Map<string, { intent: MintIntent; platform: string; selfSign: boolean; expiresAt: number }>();
|
|
12
12
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
13
13
|
|
|
14
|
-
function cacheMintId(intent: MintIntent, platform: string = "mintday"): string {
|
|
14
|
+
function cacheMintId(intent: MintIntent, platform: string = "mintday", selfSign: boolean = false): string {
|
|
15
15
|
const hash = createHash("sha256")
|
|
16
16
|
.update(JSON.stringify(intent))
|
|
17
17
|
.digest("hex")
|
|
18
18
|
.slice(0, 12);
|
|
19
|
-
intentCache.set(hash, { intent, platform, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
19
|
+
intentCache.set(hash, { intent, platform, selfSign, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
20
20
|
return hash;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function getCachedEntry(mintId: string): { intent: MintIntent; platform: string } | null {
|
|
23
|
+
function getCachedEntry(mintId: string): { intent: MintIntent; platform: string; selfSign: boolean } | null {
|
|
24
24
|
const entry = intentCache.get(mintId);
|
|
25
25
|
if (!entry) return null;
|
|
26
26
|
if (Date.now() > entry.expiresAt) {
|
|
27
27
|
intentCache.delete(mintId);
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
|
-
return { intent: entry.intent, platform: entry.platform };
|
|
30
|
+
return { intent: entry.intent, platform: entry.platform, selfSign: entry.selfSign };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export const mintSchema = {
|
|
@@ -63,19 +63,16 @@ export const mintSchema = {
|
|
|
63
63
|
.record(z.string(), z.unknown())
|
|
64
64
|
.optional()
|
|
65
65
|
.describe("Additional key-value metadata. Supports strings, arrays, and objects (e.g. capabilities: ['mint'])."),
|
|
66
|
+
selfSign: z
|
|
67
|
+
.boolean()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Sign and submit from your own wallet instead of using sponsored gas. Requires PRIVATE_KEY env var or ~/.mint-day/credentials."),
|
|
66
70
|
platform: z
|
|
67
71
|
.enum(["mintday", "superrare"])
|
|
68
72
|
.optional()
|
|
69
73
|
.describe("Which protocol to mint on. 'mintday' uses the MintFactory contract (default). 'superrare' mints via Rare Protocol on Base."),
|
|
70
74
|
};
|
|
71
75
|
|
|
72
|
-
interface TryitConfig {
|
|
73
|
-
tryitKey: string;
|
|
74
|
-
tryitContract: string;
|
|
75
|
-
tryitRpc: string;
|
|
76
|
-
tryitChain: number;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
76
|
const SPONSOR_URL = process.env.SPONSOR_URL || "https://www.mint.day/api/sponsor";
|
|
80
77
|
|
|
81
78
|
async function sponsoredMint(result: { to: string; calldata: string; value: string; estimatedGas: number; tokenType: string; soulbound: boolean; chainId: number }, recipient: string): Promise<Record<string, unknown> | null> {
|
|
@@ -126,12 +123,12 @@ export async function handleMint(
|
|
|
126
123
|
animation_url?: string;
|
|
127
124
|
mintId?: string;
|
|
128
125
|
metadata?: Record<string, unknown>;
|
|
126
|
+
selfSign?: boolean;
|
|
129
127
|
platform?: string;
|
|
130
128
|
},
|
|
131
129
|
calldataService: CalldataService,
|
|
132
130
|
defaultRecipient: string,
|
|
133
131
|
userKey: string = "",
|
|
134
|
-
tryit: TryitConfig = { tryitKey: "", tryitContract: "", tryitRpc: "", tryitChain: 84532 },
|
|
135
132
|
) {
|
|
136
133
|
// Confirmation path: replay cached intent
|
|
137
134
|
// Check cache first so we can use cached recipient for mode detection
|
|
@@ -149,10 +146,9 @@ export async function handleMint(
|
|
|
149
146
|
const cachedPlatform = entry.platform;
|
|
150
147
|
intentCache.delete(params.mintId);
|
|
151
148
|
|
|
152
|
-
// Mode detection using cached recipient
|
|
153
149
|
const hasRecipient = !!cached.recipient;
|
|
154
|
-
const
|
|
155
|
-
const privateKey = userKey
|
|
150
|
+
const selfSign = !!entry.selfSign;
|
|
151
|
+
const privateKey = userKey;
|
|
156
152
|
|
|
157
153
|
// Rare Protocol path: mint via SuperRare contracts on Base
|
|
158
154
|
if (cachedPlatform === "superrare") {
|
|
@@ -165,7 +161,7 @@ export async function handleMint(
|
|
|
165
161
|
};
|
|
166
162
|
}
|
|
167
163
|
try {
|
|
168
|
-
const rareResult = await mintOnRareProtocol(cached, privateKey,
|
|
164
|
+
const rareResult = await mintOnRareProtocol(cached, privateKey, false);
|
|
169
165
|
return {
|
|
170
166
|
content: [{
|
|
171
167
|
type: "text" as const,
|
|
@@ -183,18 +179,12 @@ export async function handleMint(
|
|
|
183
179
|
}
|
|
184
180
|
}
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
let activeCalldataService = calldataService;
|
|
188
|
-
if (isTestnet) {
|
|
189
|
-
activeCalldataService = new CalldataService(tryit.tryitRpc, tryit.tryitContract, tryit.tryitChain);
|
|
190
|
-
}
|
|
191
|
-
const result = await activeCalldataService.buildMintCalldata(cached);
|
|
182
|
+
const result = await calldataService.buildMintCalldata(cached);
|
|
192
183
|
|
|
193
|
-
//
|
|
194
|
-
if (privateKey) {
|
|
184
|
+
// Self-sign: user explicitly opted in and has a private key
|
|
185
|
+
if (selfSign && privateKey) {
|
|
195
186
|
try {
|
|
196
|
-
const
|
|
197
|
-
const wallet = new ethers.Wallet(privateKey, provider);
|
|
187
|
+
const wallet = new ethers.Wallet(privateKey, calldataService.provider);
|
|
198
188
|
const tx = await wallet.sendTransaction({
|
|
199
189
|
to: result.to,
|
|
200
190
|
data: result.calldata,
|
|
@@ -202,7 +192,10 @@ export async function handleMint(
|
|
|
202
192
|
gasLimit: result.estimatedGas,
|
|
203
193
|
});
|
|
204
194
|
const receipt = await tx.wait();
|
|
205
|
-
|
|
195
|
+
return {
|
|
196
|
+
content: [{
|
|
197
|
+
type: "text" as const,
|
|
198
|
+
text: JSON.stringify({
|
|
206
199
|
status: "minted",
|
|
207
200
|
txHash: receipt!.hash,
|
|
208
201
|
blockNumber: receipt!.blockNumber,
|
|
@@ -210,16 +203,9 @@ export async function handleMint(
|
|
|
210
203
|
tokenType: result.tokenType,
|
|
211
204
|
soulbound: result.soulbound,
|
|
212
205
|
recipient: cached.recipient,
|
|
213
|
-
chain:
|
|
214
|
-
explorer: `https
|
|
215
|
-
}
|
|
216
|
-
if (isTestnet) {
|
|
217
|
-
response.note = "This token was minted on Base Sepolia (testnet). To mint on Base mainnet, save a private key to ~/.mint-day/credentials and restart the MCP server.";
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
content: [{
|
|
221
|
-
type: "text" as const,
|
|
222
|
-
text: JSON.stringify(response, null, 2),
|
|
206
|
+
chain: "Base",
|
|
207
|
+
explorer: `https://basescan.org/tx/${receipt!.hash}`,
|
|
208
|
+
}, null, 2),
|
|
223
209
|
}],
|
|
224
210
|
};
|
|
225
211
|
} catch (err: unknown) {
|
|
@@ -233,7 +219,16 @@ export async function handleMint(
|
|
|
233
219
|
}
|
|
234
220
|
}
|
|
235
221
|
|
|
236
|
-
|
|
222
|
+
if (selfSign && !privateKey) {
|
|
223
|
+
return {
|
|
224
|
+
content: [{
|
|
225
|
+
type: "text" as const,
|
|
226
|
+
text: JSON.stringify({ error: "selfSign requires a private key. Set PRIVATE_KEY env var or save to ~/.mint-day/credentials." }, null, 2),
|
|
227
|
+
}],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Default: sponsored gas
|
|
237
232
|
if (hasRecipient) {
|
|
238
233
|
const sponsored = await sponsoredMint(result, cached.recipient);
|
|
239
234
|
if (sponsored && !sponsored.error) {
|
|
@@ -292,9 +287,6 @@ export async function handleMint(
|
|
|
292
287
|
}
|
|
293
288
|
|
|
294
289
|
const recipient = params.recipient || defaultRecipient;
|
|
295
|
-
const hasRecipient = !!recipient;
|
|
296
|
-
const isTestnet = !userKey && !hasRecipient;
|
|
297
|
-
|
|
298
290
|
if (!recipient) {
|
|
299
291
|
return {
|
|
300
292
|
content: [{
|
|
@@ -402,7 +394,7 @@ export async function handleMint(
|
|
|
402
394
|
|
|
403
395
|
// Cache intent and return preview
|
|
404
396
|
const selectedPlatform = params.platform || "mintday";
|
|
405
|
-
const mintId = cacheMintId(intent, selectedPlatform);
|
|
397
|
+
const mintId = cacheMintId(intent, selectedPlatform, !!params.selfSign);
|
|
406
398
|
|
|
407
399
|
const preview: Record<string, unknown> = {
|
|
408
400
|
status: "preview",
|
|
@@ -423,11 +415,7 @@ export async function handleMint(
|
|
|
423
415
|
preview.missingFields = intent.missingFields;
|
|
424
416
|
preview.hint = "Identity tokens (ERC-8004) should include capabilities[], endpoints[], and did. Provide these in metadata for full compliance.";
|
|
425
417
|
}
|
|
426
|
-
|
|
427
|
-
preview.chain = "Base Sepolia (testnet)";
|
|
428
|
-
} else {
|
|
429
|
-
preview.chain = "Base";
|
|
430
|
-
}
|
|
418
|
+
preview.chain = "Base";
|
|
431
419
|
preview.instruction = "Call mint with mintId to confirm.";
|
|
432
420
|
|
|
433
421
|
return {
|