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.
- package/README.md +166 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +78 -0
- package/dist/services/calldata.d.ts +12 -0
- package/dist/services/calldata.js +61 -0
- package/dist/services/classifier.d.ts +2 -0
- package/dist/services/classifier.js +81 -0
- package/dist/services/ens.d.ts +3 -0
- package/dist/services/ens.js +32 -0
- package/dist/services/image-upload.d.ts +11 -0
- package/dist/services/image-upload.js +70 -0
- package/dist/services/metadata.d.ts +2 -0
- package/dist/services/metadata.js +119 -0
- package/dist/tools/mint-check.d.ts +16 -0
- package/dist/tools/mint-check.js +177 -0
- package/dist/tools/mint-resolve.d.ts +13 -0
- package/dist/tools/mint-resolve.js +108 -0
- package/dist/tools/mint.d.ts +43 -0
- package/dist/tools/mint.js +425 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.js +9 -0
- package/package.json +41 -0
|
@@ -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 {};
|