mint-day 0.3.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.
@@ -0,0 +1,177 @@
1
+ import { z } from "zod";
2
+ import { ethers } from "ethers";
3
+ import { resolveEns, looksLikeEns } from "../services/ens.js";
4
+ const TOKEN_TYPE_NAMES = ["Identity", "Attestation", "Credential", "Receipt", "Pass"];
5
+ const MINT_FACTORY_ABI = [
6
+ "event Minted(uint256 indexed tokenId, address indexed to, uint8 tokenType, bool soulbound, string tokenURI)",
7
+ "function tokenURI(uint256 tokenId) view returns (string)",
8
+ "function tokenData(uint256 tokenId) view returns (uint8 tokenType, bool soulbound, uint256 mintedAt)",
9
+ "function ownerOf(uint256 tokenId) view returns (address)",
10
+ ];
11
+ // Deploy blocks per chain to avoid scanning from genesis
12
+ const DEPLOY_BLOCKS = {
13
+ 8453: 28000000, // Base mainnet (approximate, update after deploy)
14
+ 84532: 22000000, // Base Sepolia (approximate)
15
+ };
16
+ export const mintCheckSchema = {
17
+ address: z
18
+ .string()
19
+ .optional()
20
+ .describe("Ethereum address to check for mint.day tokens. If omitted, returns global stats."),
21
+ txHash: z
22
+ .string()
23
+ .optional()
24
+ .describe("Transaction hash to look up a specific mint. Returns token details from the tx receipt."),
25
+ };
26
+ export async function handleMintCheck(params, calldataService, provider, contractAddress, chainId) {
27
+ // Tx hash lookup: get token details from a specific transaction
28
+ if (params.txHash) {
29
+ const receipt = await provider.getTransactionReceipt(params.txHash);
30
+ if (!receipt) {
31
+ return {
32
+ content: [{
33
+ type: "text",
34
+ text: JSON.stringify({ error: "Transaction not found." }, null, 2),
35
+ }],
36
+ };
37
+ }
38
+ const contract = new ethers.Contract(contractAddress, MINT_FACTORY_ABI, provider);
39
+ const mintedLogs = receipt.logs
40
+ .filter(log => log.address.toLowerCase() === contractAddress.toLowerCase())
41
+ .map(log => {
42
+ try {
43
+ return contract.interface.parseLog({ topics: [...log.topics], data: log.data });
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ })
49
+ .filter(parsed => parsed?.name === "Minted");
50
+ if (mintedLogs.length === 0) {
51
+ return {
52
+ content: [{
53
+ type: "text",
54
+ text: JSON.stringify({ error: "No mint.day Minted event found in this transaction.", txHash: params.txHash }, null, 2),
55
+ }],
56
+ };
57
+ }
58
+ const tokens = await Promise.all(mintedLogs.map(async (log) => {
59
+ const tokenId = log.args[0].toString();
60
+ const to = log.args[1];
61
+ const tokenType = Number(log.args[2]);
62
+ const soulbound = Boolean(log.args[3]);
63
+ const uri = await contract.tokenURI(tokenId);
64
+ let metadata = {};
65
+ if (uri.startsWith("data:application/json;base64,")) {
66
+ try {
67
+ metadata = JSON.parse(Buffer.from(uri.replace("data:application/json;base64,", ""), "base64").toString());
68
+ }
69
+ catch {
70
+ metadata = { raw: uri };
71
+ }
72
+ }
73
+ return {
74
+ tokenId,
75
+ to,
76
+ tokenType: TOKEN_TYPE_NAMES[tokenType] || "Unknown",
77
+ soulbound,
78
+ metadata,
79
+ };
80
+ }));
81
+ return {
82
+ content: [{
83
+ type: "text",
84
+ text: JSON.stringify({
85
+ txHash: params.txHash,
86
+ blockNumber: receipt.blockNumber,
87
+ status: receipt.status === 1 ? "success" : "failed",
88
+ tokens,
89
+ }, null, 2),
90
+ }],
91
+ };
92
+ }
93
+ // No address: return global stats (replaces mint_status)
94
+ if (!params.address) {
95
+ const total = await calldataService.totalMinted();
96
+ const fee = await calldataService.mintFee();
97
+ return {
98
+ content: [{
99
+ type: "text",
100
+ text: JSON.stringify({ totalMinted: total, mintFee: `${fee} ETH`, chainId }, null, 2),
101
+ }],
102
+ };
103
+ }
104
+ // Resolve ENS names
105
+ let address = params.address;
106
+ if (looksLikeEns(address)) {
107
+ try {
108
+ address = await resolveEns(address);
109
+ }
110
+ catch {
111
+ return {
112
+ content: [{
113
+ type: "text",
114
+ text: JSON.stringify({ error: `Could not resolve ENS name: ${params.address}` }, null, 2),
115
+ }],
116
+ };
117
+ }
118
+ }
119
+ // Validate address
120
+ if (!ethers.isAddress(address)) {
121
+ return {
122
+ content: [{
123
+ type: "text",
124
+ text: JSON.stringify({ error: `Invalid Ethereum address: ${address}` }, null, 2),
125
+ }],
126
+ };
127
+ }
128
+ const contract = new ethers.Contract(contractAddress, MINT_FACTORY_ABI, provider);
129
+ const fromBlock = DEPLOY_BLOCKS[chainId] || 0;
130
+ const filter = contract.filters.Minted(null, address);
131
+ const events = await contract.queryFilter(filter, fromBlock, "latest");
132
+ if (events.length === 0) {
133
+ return {
134
+ content: [{
135
+ type: "text",
136
+ text: JSON.stringify({ address, tokens: [], count: 0 }, null, 2),
137
+ }],
138
+ };
139
+ }
140
+ const tokens = await Promise.all(events.map(async (event) => {
141
+ const log = event;
142
+ const tokenId = log.args[0].toString();
143
+ let currentOwner = null;
144
+ try {
145
+ currentOwner = await contract.ownerOf(tokenId);
146
+ }
147
+ catch {
148
+ currentOwner = null; // burned
149
+ }
150
+ const [tokenType, soulbound, mintedAt] = await contract.tokenData(tokenId);
151
+ const uri = await contract.tokenURI(tokenId);
152
+ let metadata = {};
153
+ if (uri.startsWith("data:application/json;base64,")) {
154
+ const base64 = uri.replace("data:application/json;base64,", "");
155
+ try {
156
+ metadata = JSON.parse(Buffer.from(base64, "base64").toString());
157
+ }
158
+ catch {
159
+ metadata = { raw: uri };
160
+ }
161
+ }
162
+ return {
163
+ tokenId,
164
+ tokenType: TOKEN_TYPE_NAMES[Number(tokenType)] || "Unknown",
165
+ soulbound: Boolean(soulbound),
166
+ mintedAt: new Date(Number(mintedAt) * 1000).toISOString(),
167
+ currentOwner,
168
+ metadata,
169
+ };
170
+ }));
171
+ return {
172
+ content: [{
173
+ type: "text",
174
+ text: JSON.stringify({ address, tokens, count: tokens.length }, null, 2),
175
+ }],
176
+ };
177
+ }
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import { ethers } from "ethers";
3
+ export declare const mintResolveSchema: {
4
+ address: z.ZodString;
5
+ };
6
+ export declare function handleMintResolve(params: {
7
+ address: string;
8
+ }, provider: ethers.JsonRpcProvider, contractAddress: string, chainId: number): Promise<{
9
+ content: {
10
+ type: "text";
11
+ text: string;
12
+ }[];
13
+ }>;
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ import { ethers } from "ethers";
3
+ import { resolveEns, looksLikeEns } from "../services/ens.js";
4
+ const TOKEN_TYPE_NAMES = ["Identity", "Attestation", "Credential", "Receipt", "Pass"];
5
+ const MINT_FACTORY_ABI = [
6
+ "event Minted(uint256 indexed tokenId, address indexed to, uint8 tokenType, bool soulbound, string tokenURI)",
7
+ "function tokenURI(uint256 tokenId) view returns (string)",
8
+ "function tokenData(uint256 tokenId) view returns (uint8 tokenType, bool soulbound, uint256 mintedAt)",
9
+ "function ownerOf(uint256 tokenId) view returns (address)",
10
+ ];
11
+ const DEPLOY_BLOCKS = {
12
+ 8453: 28000000,
13
+ 84532: 22000000,
14
+ };
15
+ export const mintResolveSchema = {
16
+ address: z
17
+ .string()
18
+ .describe("Ethereum address or ENS name to resolve. Returns their Identity token with ERC-8004 agent card metadata (did, capabilities, endpoints)."),
19
+ };
20
+ export async function handleMintResolve(params, provider, contractAddress, chainId) {
21
+ let address = params.address;
22
+ if (looksLikeEns(address)) {
23
+ try {
24
+ address = await resolveEns(address);
25
+ }
26
+ catch {
27
+ return {
28
+ content: [{
29
+ type: "text",
30
+ text: JSON.stringify({ error: `Could not resolve ENS name: ${params.address}` }, null, 2),
31
+ }],
32
+ };
33
+ }
34
+ }
35
+ if (!ethers.isAddress(address)) {
36
+ return {
37
+ content: [{
38
+ type: "text",
39
+ text: JSON.stringify({ error: `Invalid Ethereum address: ${address}` }, null, 2),
40
+ }],
41
+ };
42
+ }
43
+ const contract = new ethers.Contract(contractAddress, MINT_FACTORY_ABI, provider);
44
+ const fromBlock = DEPLOY_BLOCKS[chainId] || 0;
45
+ // Query all Minted events to this address
46
+ const filter = contract.filters.Minted(null, address);
47
+ const events = await contract.queryFilter(filter, fromBlock, "latest");
48
+ // Find Identity tokens (tokenType === 0)
49
+ let identityEvent = null;
50
+ for (const event of events) {
51
+ const log = event;
52
+ if (Number(log.args[2]) === 0) {
53
+ identityEvent = log; // take the latest Identity token
54
+ }
55
+ }
56
+ if (!identityEvent) {
57
+ return {
58
+ content: [{
59
+ type: "text",
60
+ text: JSON.stringify({
61
+ address,
62
+ identity: null,
63
+ message: "No Identity token found for this address.",
64
+ }, null, 2),
65
+ }],
66
+ };
67
+ }
68
+ const tokenId = identityEvent.args[0].toString();
69
+ let currentOwner = null;
70
+ try {
71
+ currentOwner = await contract.ownerOf(tokenId);
72
+ }
73
+ catch {
74
+ currentOwner = null;
75
+ }
76
+ const [, soulbound, mintedAt] = await contract.tokenData(tokenId);
77
+ const uri = await contract.tokenURI(tokenId);
78
+ let metadata = {};
79
+ if (uri.startsWith("data:application/json;base64,")) {
80
+ const base64 = uri.replace("data:application/json;base64,", "");
81
+ try {
82
+ metadata = JSON.parse(Buffer.from(base64, "base64").toString());
83
+ }
84
+ catch {
85
+ metadata = { raw: uri };
86
+ }
87
+ }
88
+ return {
89
+ content: [{
90
+ type: "text",
91
+ text: JSON.stringify({
92
+ address,
93
+ identity: {
94
+ tokenId,
95
+ soulbound: Boolean(soulbound),
96
+ mintedAt: new Date(Number(mintedAt) * 1000).toISOString(),
97
+ currentOwner,
98
+ did: metadata.did || null,
99
+ capabilities: metadata.capabilities || [],
100
+ endpoints: metadata.endpoints || [],
101
+ name: metadata.name || null,
102
+ description: metadata.description || null,
103
+ image: metadata.image || null,
104
+ },
105
+ }, null, 2),
106
+ }],
107
+ };
108
+ }
@@ -0,0 +1,43 @@
1
+ import { z } from "zod";
2
+ import { CalldataService } from "../services/calldata.js";
3
+ export declare const mintSchema: {
4
+ description: z.ZodOptional<z.ZodString>;
5
+ tokenType: z.ZodOptional<z.ZodEnum<{
6
+ Identity: "Identity";
7
+ Attestation: "Attestation";
8
+ Credential: "Credential";
9
+ Receipt: "Receipt";
10
+ Pass: "Pass";
11
+ }>>;
12
+ recipient: z.ZodOptional<z.ZodString>;
13
+ soulbound: z.ZodOptional<z.ZodBoolean>;
14
+ image: z.ZodOptional<z.ZodString>;
15
+ animation_url: z.ZodOptional<z.ZodString>;
16
+ mintId: z.ZodOptional<z.ZodString>;
17
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
18
+ };
19
+ interface SponsoredConfig {
20
+ sponsoredKey: string;
21
+ sponsoredContract: string;
22
+ sponsoredRpc: string;
23
+ sponsoredChain: number;
24
+ fallbackContract: string;
25
+ fallbackRpc: string;
26
+ fallbackChain: number;
27
+ }
28
+ export declare function handleMint(params: {
29
+ description?: string;
30
+ tokenType?: string;
31
+ recipient?: string;
32
+ soulbound?: boolean;
33
+ image?: string;
34
+ animation_url?: string;
35
+ mintId?: string;
36
+ metadata?: Record<string, unknown>;
37
+ }, calldataService: CalldataService, defaultRecipient: string, userKey?: string, sponsored?: SponsoredConfig): Promise<{
38
+ content: {
39
+ type: "text";
40
+ text: string;
41
+ }[];
42
+ }>;
43
+ export {};