@artblocks/abx-cli 0.1.0-alpha.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 +21 -0
- package/assets/renderer-scaffold/README.md +59 -0
- package/assets/renderer-scaffold/foundry.toml +15 -0
- package/assets/renderer-scaffold/remappings.txt +3 -0
- package/assets/renderer-scaffold/script/Deploy.s.sol +23 -0
- package/assets/renderer-scaffold/src/MyRenderer.sol +123 -0
- package/assets/renderer-scaffold/src/MyTraits.sol +75 -0
- package/assets/renderer-scaffold/src/interfaces/IAbxFieldRenderer.sol +32 -0
- package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +26 -0
- package/assets/renderer-scaffold/test/MyRenderer.t.sol +110 -0
- package/dist/config.d.ts +69 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +184 -0
- package/dist/config.js.map +1 -0
- package/dist/deps.d.ts +46 -0
- package/dist/deps.d.ts.map +1 -0
- package/dist/deps.js +90 -0
- package/dist/deps.js.map +1 -0
- package/dist/flags.d.ts +25 -0
- package/dist/flags.d.ts.map +1 -0
- package/dist/flags.js +34 -0
- package/dist/flags.js.map +1 -0
- package/dist/inspect.d.ts +48 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +184 -0
- package/dist/inspect.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +5102 -0
- package/dist/main.js.map +1 -0
- package/dist/migrate.d.ts +65 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +180 -0
- package/dist/migrate.js.map +1 -0
- package/dist/mintpage.d.ts +46 -0
- package/dist/mintpage.d.ts.map +1 -0
- package/dist/mintpage.js +461 -0
- package/dist/mintpage.js.map +1 -0
- package/dist/onchain-uri.d.ts +97 -0
- package/dist/onchain-uri.d.ts.map +1 -0
- package/dist/onchain-uri.js +243 -0
- package/dist/onchain-uri.js.map +1 -0
- package/dist/ownerops.d.ts +195 -0
- package/dist/ownerops.d.ts.map +1 -0
- package/dist/ownerops.js +1270 -0
- package/dist/ownerops.js.map +1 -0
- package/dist/provision.d.ts +86 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +372 -0
- package/dist/provision.js.map +1 -0
- package/dist/remote.d.ts +58 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +54 -0
- package/dist/remote.js.map +1 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +125 -0
- package/dist/schema.js.map +1 -0
- package/dist/series-traits.d.ts +30 -0
- package/dist/series-traits.d.ts.map +1 -0
- package/dist/series-traits.js +103 -0
- package/dist/series-traits.js.map +1 -0
- package/dist/signer.d.ts +80 -0
- package/dist/signer.d.ts.map +1 -0
- package/dist/signer.js +520 -0
- package/dist/signer.js.map +1 -0
- package/dist/upload.d.ts +28 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +41 -0
- package/dist/upload.js.map +1 -0
- package/package.json +55 -0
- package/skill/SKILL.md +304 -0
- package/skill/reference/code-projects.md +211 -0
- package/skill/reference/hosting.md +138 -0
- package/skill/reference/operating.md +116 -0
- package/skill/reference/setup.md +36 -0
- package/skill/reference/troubleshooting.md +28 -0
package/dist/mintpage.js
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mint-page scaffolding — a **self-contained** Next.js starter a creator stands up to sell a drop,
|
|
3
|
+
* compatible with the shared {AbxFixedPriceMinter}. The sibling of `deploy-resolver`: a buildable
|
|
4
|
+
* artifact the creator OWNS and customizes (with the agent's help), not a service we run.
|
|
5
|
+
*
|
|
6
|
+
* SELF-CONTAINED + BACKEND-FREE by design:
|
|
7
|
+
* - Reads live sale state (price · sold/allocation · paused · supply) straight from chain, and each
|
|
8
|
+
* token's image/name from `tokenURI(id)` — no server, no database. Fully-on-chain collections need
|
|
9
|
+
* nothing else; off-chain ones rely on their `tokenURI` resolving (same as any marketplace).
|
|
10
|
+
* - Wallet connect + mint use viem + the injected browser wallet (`window.ethereum`) — NO API keys,
|
|
11
|
+
* no WalletConnect projectId. The README shows how to swap in RainbowKit for multi-wallet.
|
|
12
|
+
* - Reads use a PUBLIC RPC (never the creator's keyed endpoint — that would leak a secret into a
|
|
13
|
+
* `NEXT_PUBLIC_` var); the mint tx uses the wallet's own RPC. Config rides `NEXT_PUBLIC_*` env vars,
|
|
14
|
+
* prefilled by the CLI into `.env.local`.
|
|
15
|
+
*
|
|
16
|
+
* V1 scope (deliberately minimal — customize from here): ETH sales, one mint per click, a paginated
|
|
17
|
+
* gallery of minted tokens. ERC-20 sales (need an `approve` step) are flagged as a customization.
|
|
18
|
+
*
|
|
19
|
+
* Pure module: returns the artifact files + the exact next-step commands. The CLI writes the files and
|
|
20
|
+
* prints the steps; `vercel` / `next dev` run in the creator's own account/machine — they own that step.
|
|
21
|
+
*/
|
|
22
|
+
/** Public, keyless RPCs safe to embed in a public site. Extend as chains are added. */
|
|
23
|
+
export const PUBLIC_RPC = {
|
|
24
|
+
1: 'https://ethereum-rpc.publicnode.com',
|
|
25
|
+
11155111: 'https://ethereum-sepolia-rpc.publicnode.com',
|
|
26
|
+
};
|
|
27
|
+
const slug = (s) => s
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
30
|
+
.replace(/^-+|-+$/g, '') || 'abx';
|
|
31
|
+
// ── file contents ─────────────────────────────────────────────────────────────
|
|
32
|
+
// NOTE: emitted TS/TSX below uses single-quote strings + concatenation and avoids backticks and
|
|
33
|
+
// `${}` on purpose — so these Node template literals only interpolate SCAFFOLD-time values, never
|
|
34
|
+
// browser-runtime ones. Keep it that way when editing.
|
|
35
|
+
function packageJson(o) {
|
|
36
|
+
return JSON.stringify({
|
|
37
|
+
name: slug(o.collectionName) + '-mint',
|
|
38
|
+
private: true,
|
|
39
|
+
version: '0.1.0',
|
|
40
|
+
scripts: { dev: 'next dev', build: 'next build', start: 'next start' },
|
|
41
|
+
dependencies: {
|
|
42
|
+
next: '^15.0.3',
|
|
43
|
+
react: '^19.0.0',
|
|
44
|
+
'react-dom': '^19.0.0',
|
|
45
|
+
viem: '^2.21.0',
|
|
46
|
+
},
|
|
47
|
+
devDependencies: {
|
|
48
|
+
typescript: '^5.6.0',
|
|
49
|
+
'@types/node': '^22.0.0',
|
|
50
|
+
'@types/react': '^19.0.0',
|
|
51
|
+
'@types/react-dom': '^19.0.0',
|
|
52
|
+
},
|
|
53
|
+
}, null, 2);
|
|
54
|
+
}
|
|
55
|
+
const NEXT_CONFIG = `/** @type {import('next').NextConfig} */
|
|
56
|
+
// Images render via a plain <img> from data:/http(s) URIs, so the built-in optimizer is off
|
|
57
|
+
// (no per-domain allowlist to maintain). Customize freely.
|
|
58
|
+
const nextConfig = {images: {unoptimized: true}};
|
|
59
|
+
export default nextConfig;
|
|
60
|
+
`;
|
|
61
|
+
const TSCONFIG = JSON.stringify({
|
|
62
|
+
compilerOptions: {
|
|
63
|
+
target: 'ES2020',
|
|
64
|
+
lib: ['dom', 'dom.iterable', 'esnext'],
|
|
65
|
+
allowJs: true,
|
|
66
|
+
skipLibCheck: true,
|
|
67
|
+
strict: true,
|
|
68
|
+
noEmit: true,
|
|
69
|
+
esModuleInterop: true,
|
|
70
|
+
module: 'esnext',
|
|
71
|
+
moduleResolution: 'bundler',
|
|
72
|
+
resolveJsonModule: true,
|
|
73
|
+
isolatedModules: true,
|
|
74
|
+
jsx: 'preserve',
|
|
75
|
+
incremental: true,
|
|
76
|
+
plugins: [{ name: 'next' }],
|
|
77
|
+
paths: { '@/*': ['./*'] },
|
|
78
|
+
},
|
|
79
|
+
include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
|
|
80
|
+
exclude: ['node_modules'],
|
|
81
|
+
}, null, 2);
|
|
82
|
+
const NEXT_ENV = `/// <reference types="next" />
|
|
83
|
+
/// <reference types="next/image-types/global" />
|
|
84
|
+
// NOTE: This file should not be edited. See https://nextjs.org/docs/basic-features/typescript
|
|
85
|
+
`;
|
|
86
|
+
const GITIGNORE = `/node_modules
|
|
87
|
+
/.next
|
|
88
|
+
/out
|
|
89
|
+
.env.local
|
|
90
|
+
.DS_Store
|
|
91
|
+
*.log
|
|
92
|
+
.vercel
|
|
93
|
+
`;
|
|
94
|
+
function envFile(o) {
|
|
95
|
+
return ('NEXT_PUBLIC_CHAIN_ID=' +
|
|
96
|
+
o.chainId +
|
|
97
|
+
'\nNEXT_PUBLIC_CHAIN_NAME=' +
|
|
98
|
+
o.chainName +
|
|
99
|
+
'\n# PUBLIC, keyless RPC (embedded in the public site — never put a keyed endpoint here).\n' +
|
|
100
|
+
'NEXT_PUBLIC_RPC_URL=' +
|
|
101
|
+
o.rpcUrl +
|
|
102
|
+
'\nNEXT_PUBLIC_TOKEN=' +
|
|
103
|
+
o.token +
|
|
104
|
+
'\nNEXT_PUBLIC_MINTER=' +
|
|
105
|
+
o.minter +
|
|
106
|
+
'\nNEXT_PUBLIC_COLLECTION_NAME=' +
|
|
107
|
+
o.collectionName +
|
|
108
|
+
'\nNEXT_PUBLIC_EXPLORER=' +
|
|
109
|
+
(o.explorer ?? '') +
|
|
110
|
+
'\n');
|
|
111
|
+
}
|
|
112
|
+
const LIB_CONFIG = `// Config from NEXT_PUBLIC_* env (prefilled by \`abx mint-page\` into .env.local; set the same in Vercel).
|
|
113
|
+
export const CHAIN_ID = Number(process.env.NEXT_PUBLIC_CHAIN_ID || '11155111');
|
|
114
|
+
export const CHAIN_NAME = process.env.NEXT_PUBLIC_CHAIN_NAME || 'Sepolia';
|
|
115
|
+
export const RPC_URL = process.env.NEXT_PUBLIC_RPC_URL || 'https://ethereum-sepolia-rpc.publicnode.com';
|
|
116
|
+
export const TOKEN = (process.env.NEXT_PUBLIC_TOKEN || '') as \`0x\${string}\`;
|
|
117
|
+
export const MINTER = (process.env.NEXT_PUBLIC_MINTER || '') as \`0x\${string}\`;
|
|
118
|
+
export const COLLECTION_NAME = process.env.NEXT_PUBLIC_COLLECTION_NAME || 'ABX Collection';
|
|
119
|
+
export const EXPLORER = process.env.NEXT_PUBLIC_EXPLORER || '';
|
|
120
|
+
export const PAGE_SIZE = 12;
|
|
121
|
+
`;
|
|
122
|
+
const LIB_CHAIN = `import {defineChain} from 'viem';
|
|
123
|
+
import {CHAIN_ID, CHAIN_NAME, RPC_URL} from './config';
|
|
124
|
+
|
|
125
|
+
// A minimal viem chain built from config — no dependency on viem/chains matching the id.
|
|
126
|
+
export const chain = defineChain({
|
|
127
|
+
id: CHAIN_ID,
|
|
128
|
+
name: CHAIN_NAME,
|
|
129
|
+
nativeCurrency: {name: 'Ether', symbol: 'ETH', decimals: 18},
|
|
130
|
+
rpcUrls: {default: {http: [RPC_URL]}},
|
|
131
|
+
});
|
|
132
|
+
`;
|
|
133
|
+
const LIB_ABI = `// Minimal ABIs — only what the mint page reads/writes.
|
|
134
|
+
export const minterAbi = [
|
|
135
|
+
{
|
|
136
|
+
type: 'function',
|
|
137
|
+
name: 'sales',
|
|
138
|
+
stateMutability: 'view',
|
|
139
|
+
inputs: [{name: 'token', type: 'address'}],
|
|
140
|
+
outputs: [
|
|
141
|
+
{name: 'configured', type: 'bool'},
|
|
142
|
+
{name: 'paymentToken', type: 'address'},
|
|
143
|
+
{name: 'price', type: 'uint256'},
|
|
144
|
+
{name: 'allocation', type: 'uint256'},
|
|
145
|
+
{name: 'sold', type: 'uint256'},
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
{type: 'function', name: 'purchase', stateMutability: 'payable', inputs: [{name: 'token', type: 'address'}], outputs: [{name: 'tokenId', type: 'uint256'}]},
|
|
149
|
+
{type: 'function', name: 'purchaseTo', stateMutability: 'payable', inputs: [{name: 'token', type: 'address'}, {name: 'to', type: 'address'}], outputs: [{name: 'tokenId', type: 'uint256'}]},
|
|
150
|
+
] as const;
|
|
151
|
+
|
|
152
|
+
export const tokenAbi = [
|
|
153
|
+
{type: 'function', name: 'name', stateMutability: 'view', inputs: [], outputs: [{type: 'string'}]},
|
|
154
|
+
{type: 'function', name: 'totalSupply', stateMutability: 'view', inputs: [], outputs: [{type: 'uint256'}]},
|
|
155
|
+
{type: 'function', name: 'maxInvocations', stateMutability: 'view', inputs: [], outputs: [{type: 'uint256'}]},
|
|
156
|
+
{type: 'function', name: 'paused', stateMutability: 'view', inputs: [], outputs: [{type: 'bool'}]},
|
|
157
|
+
{type: 'function', name: 'tokenURI', stateMutability: 'view', inputs: [{name: 'id', type: 'uint256'}], outputs: [{type: 'string'}]},
|
|
158
|
+
] as const;
|
|
159
|
+
`;
|
|
160
|
+
const LIB_META = `// Read a token's image + name from tokenURI(id), directly from chain. Handles data: URIs
|
|
161
|
+
// (on-chain tokens) and http/ipfs/ar locators (off-chain custody). No backend.
|
|
162
|
+
import {createPublicClient, http} from 'viem';
|
|
163
|
+
import {chain} from './chain';
|
|
164
|
+
import {TOKEN} from './config';
|
|
165
|
+
import {tokenAbi} from './abi';
|
|
166
|
+
|
|
167
|
+
export const publicClient = createPublicClient({chain, transport: http()});
|
|
168
|
+
|
|
169
|
+
export function toGateway(u: string): string {
|
|
170
|
+
if (!u) return '';
|
|
171
|
+
if (u.startsWith('ipfs://')) return 'https://ipfs.io/ipfs/' + u.slice(7);
|
|
172
|
+
if (u.startsWith('ar://')) return 'https://arweave.net/' + u.slice(5);
|
|
173
|
+
return u;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function fetchJson(uri: string): Promise<any> {
|
|
177
|
+
if (uri.startsWith('data:')) {
|
|
178
|
+
const comma = uri.indexOf(',');
|
|
179
|
+
const header = uri.slice(5, comma);
|
|
180
|
+
const body = uri.slice(comma + 1);
|
|
181
|
+
const text = header.includes('base64') ? atob(body) : decodeURIComponent(body);
|
|
182
|
+
return JSON.parse(text);
|
|
183
|
+
}
|
|
184
|
+
const res = await fetch(toGateway(uri));
|
|
185
|
+
return res.json();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface TokenMeta {
|
|
189
|
+
id: number;
|
|
190
|
+
name: string;
|
|
191
|
+
image: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function readTokenMeta(id: number): Promise<TokenMeta> {
|
|
195
|
+
const uri = (await publicClient.readContract({address: TOKEN, abi: tokenAbi, functionName: 'tokenURI', args: [BigInt(id)]})) as string;
|
|
196
|
+
const json = await fetchJson(uri);
|
|
197
|
+
return {id, name: json.name || '#' + id, image: toGateway(json.image || '')};
|
|
198
|
+
}
|
|
199
|
+
`;
|
|
200
|
+
const APP_LAYOUT = `import './globals.css';
|
|
201
|
+
import type {ReactNode} from 'react';
|
|
202
|
+
import {COLLECTION_NAME} from '../lib/config';
|
|
203
|
+
|
|
204
|
+
export const metadata = {title: COLLECTION_NAME + ' — Mint', description: 'Mint ' + COLLECTION_NAME};
|
|
205
|
+
|
|
206
|
+
export default function RootLayout({children}: {children: ReactNode}) {
|
|
207
|
+
return (
|
|
208
|
+
<html lang="en">
|
|
209
|
+
<body>{children}</body>
|
|
210
|
+
</html>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
const APP_GLOBALS = `:root { color-scheme: dark; }
|
|
215
|
+
* { box-sizing: border-box; }
|
|
216
|
+
body {
|
|
217
|
+
margin: 0;
|
|
218
|
+
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
|
219
|
+
background: #0b0b0f;
|
|
220
|
+
color: #e8e8ea;
|
|
221
|
+
}
|
|
222
|
+
.wrap { max-width: 960px; margin: 0 auto; padding: 32px 20px 80px; }
|
|
223
|
+
h1 { font-size: 28px; margin: 0 0 4px; }
|
|
224
|
+
.muted { color: #9a9aa2; }
|
|
225
|
+
.card { background: #15151c; border: 1px solid #26262f; border-radius: 14px; padding: 20px; }
|
|
226
|
+
.row { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; justify-content: space-between; }
|
|
227
|
+
button {
|
|
228
|
+
font: inherit; font-weight: 600; cursor: pointer;
|
|
229
|
+
background: #6d5efc; color: #fff; border: 0; border-radius: 10px; padding: 12px 20px;
|
|
230
|
+
}
|
|
231
|
+
button:disabled { background: #2a2a35; color: #6b6b75; cursor: not-allowed; }
|
|
232
|
+
button.ghost { background: transparent; border: 1px solid #3a3a46; color: #e8e8ea; }
|
|
233
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; margin-top: 20px; }
|
|
234
|
+
.tile { background: #15151c; border: 1px solid #26262f; border-radius: 12px; overflow: hidden; }
|
|
235
|
+
.tile img { width: 100%; aspect-ratio: 1; object-fit: cover; display: block; background: #0e0e13; }
|
|
236
|
+
.tile .label { padding: 10px 12px; font-size: 14px; }
|
|
237
|
+
.pager { display: flex; gap: 10px; align-items: center; justify-content: center; margin-top: 24px; }
|
|
238
|
+
a { color: #a99bff; }
|
|
239
|
+
.stat { font-size: 15px; }
|
|
240
|
+
.stat b { color: #fff; }
|
|
241
|
+
.pill { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
|
|
242
|
+
.pill.open { background: #12351f; color: #57d98a; }
|
|
243
|
+
.pill.closed { background: #351212; color: #e88; }
|
|
244
|
+
`;
|
|
245
|
+
const APP_PAGE = `import Mint from './mint-client';
|
|
246
|
+
export default function Page() {
|
|
247
|
+
return <Mint />;
|
|
248
|
+
}
|
|
249
|
+
`;
|
|
250
|
+
// The whole UI in one client component: live sale state + paginated gallery + connect/mint.
|
|
251
|
+
const APP_MINT_CLIENT = `'use client';
|
|
252
|
+
import {useCallback, useEffect, useState} from 'react';
|
|
253
|
+
import {createWalletClient, custom, formatEther} from 'viem';
|
|
254
|
+
import {chain} from '../lib/chain';
|
|
255
|
+
import {CHAIN_ID, CHAIN_NAME, COLLECTION_NAME, EXPLORER, MINTER, PAGE_SIZE, TOKEN} from '../lib/config';
|
|
256
|
+
import {minterAbi, tokenAbi} from '../lib/abi';
|
|
257
|
+
import {publicClient, readTokenMeta, type TokenMeta} from '../lib/meta';
|
|
258
|
+
|
|
259
|
+
const ZERO = '0x0000000000000000000000000000000000000000';
|
|
260
|
+
|
|
261
|
+
interface Sale {
|
|
262
|
+
configured: boolean;
|
|
263
|
+
paymentToken: string;
|
|
264
|
+
price: bigint;
|
|
265
|
+
allocation: bigint;
|
|
266
|
+
sold: bigint;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export default function Mint() {
|
|
270
|
+
const [sale, setSale] = useState<Sale | null>(null);
|
|
271
|
+
const [supply, setSupply] = useState(0);
|
|
272
|
+
const [maxInv, setMaxInv] = useState(0);
|
|
273
|
+
const [paused, setPaused] = useState(false);
|
|
274
|
+
const [account, setAccount] = useState<string>('');
|
|
275
|
+
const [page, setPage] = useState(0);
|
|
276
|
+
const [tiles, setTiles] = useState<TokenMeta[]>([]);
|
|
277
|
+
const [busy, setBusy] = useState(false);
|
|
278
|
+
const [msg, setMsg] = useState('');
|
|
279
|
+
|
|
280
|
+
const refresh = useCallback(async () => {
|
|
281
|
+
const s = (await publicClient.readContract({address: MINTER, abi: minterAbi, functionName: 'sales', args: [TOKEN]})) as any;
|
|
282
|
+
setSale({configured: s[0], paymentToken: s[1], price: s[2], allocation: s[3], sold: s[4]});
|
|
283
|
+
const [ts, mx, pz] = await Promise.all([
|
|
284
|
+
publicClient.readContract({address: TOKEN, abi: tokenAbi, functionName: 'totalSupply'}),
|
|
285
|
+
publicClient.readContract({address: TOKEN, abi: tokenAbi, functionName: 'maxInvocations'}),
|
|
286
|
+
publicClient.readContract({address: TOKEN, abi: tokenAbi, functionName: 'paused'}),
|
|
287
|
+
]);
|
|
288
|
+
setSupply(Number(ts));
|
|
289
|
+
setMaxInv(Number(mx));
|
|
290
|
+
setPaused(Boolean(pz));
|
|
291
|
+
}, []);
|
|
292
|
+
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
refresh().catch((e) => setMsg(String(e)));
|
|
295
|
+
}, [refresh]);
|
|
296
|
+
|
|
297
|
+
// Load the visible page of minted tokens (newest first). Only this page's tokenURIs are fetched.
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
let live = true;
|
|
300
|
+
(async () => {
|
|
301
|
+
const ids: number[] = [];
|
|
302
|
+
const start = page * PAGE_SIZE;
|
|
303
|
+
for (let i = 0; i < PAGE_SIZE; i++) {
|
|
304
|
+
const id = supply - 1 - start - i;
|
|
305
|
+
if (id < 0) break;
|
|
306
|
+
ids.push(id);
|
|
307
|
+
}
|
|
308
|
+
const metas = await Promise.all(ids.map((id) => readTokenMeta(id).catch(() => ({id, name: '#' + id, image: ''}))));
|
|
309
|
+
if (live) setTiles(metas);
|
|
310
|
+
})();
|
|
311
|
+
return () => {
|
|
312
|
+
live = false;
|
|
313
|
+
};
|
|
314
|
+
}, [page, supply]);
|
|
315
|
+
|
|
316
|
+
async function connect() {
|
|
317
|
+
const eth = (window as any).ethereum;
|
|
318
|
+
if (!eth) {
|
|
319
|
+
setMsg('No injected wallet found — install MetaMask (or another browser wallet).');
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const accts = (await eth.request({method: 'eth_requestAccounts'})) as string[];
|
|
323
|
+
setAccount(accts[0] || '');
|
|
324
|
+
try {
|
|
325
|
+
await eth.request({method: 'wallet_switchEthereumChain', params: [{chainId: '0x' + CHAIN_ID.toString(16)}]});
|
|
326
|
+
} catch (e) {
|
|
327
|
+
/* user can switch manually; the mint call will surface a wrong-network error */
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function mint() {
|
|
332
|
+
if (!sale) return;
|
|
333
|
+
const eth = (window as any).ethereum;
|
|
334
|
+
if (!eth || !account) return connect();
|
|
335
|
+
setBusy(true);
|
|
336
|
+
setMsg('');
|
|
337
|
+
try {
|
|
338
|
+
const wallet = createWalletClient({account: account as any, chain, transport: custom(eth)});
|
|
339
|
+
const hash = await wallet.writeContract({address: MINTER, abi: minterAbi, functionName: 'purchase', args: [TOKEN], value: sale.price, account: account as any, chain});
|
|
340
|
+
setMsg('Minting… tx ' + hash.slice(0, 10) + '…');
|
|
341
|
+
await publicClient.waitForTransactionReceipt({hash});
|
|
342
|
+
setMsg('Minted! ✦');
|
|
343
|
+
setPage(0);
|
|
344
|
+
await refresh();
|
|
345
|
+
} catch (e: any) {
|
|
346
|
+
setMsg(e && e.shortMessage ? e.shortMessage : String(e && e.message ? e.message : e));
|
|
347
|
+
} finally {
|
|
348
|
+
setBusy(false);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const isEth = !sale || sale.paymentToken === ZERO;
|
|
353
|
+
const soldOut = !!sale && (sale.sold >= sale.allocation || (maxInv > 0 && supply >= maxInv));
|
|
354
|
+
const pages = Math.max(1, Math.ceil(supply / PAGE_SIZE));
|
|
355
|
+
const canMint = !!sale && sale.configured && !paused && !soldOut && isEth && !busy;
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<main className="wrap">
|
|
359
|
+
<div className="row" style={{marginBottom: 20}}>
|
|
360
|
+
<div>
|
|
361
|
+
<h1>{COLLECTION_NAME}</h1>
|
|
362
|
+
<div className="muted">
|
|
363
|
+
{EXPLORER ? <a href={EXPLORER + '/address/' + TOKEN} target="_blank" rel="noreferrer">contract ↗</a> : TOKEN}
|
|
364
|
+
{' · '}
|
|
365
|
+
{CHAIN_NAME}
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
{account ? <span className="muted">{account.slice(0, 6) + '…' + account.slice(-4)}</span> : <button className="ghost" onClick={connect}>Connect wallet</button>}
|
|
369
|
+
</div>
|
|
370
|
+
|
|
371
|
+
<div className="card">
|
|
372
|
+
<div className="row">
|
|
373
|
+
<div className="stat">
|
|
374
|
+
{sale ? <>Price <b>{isEth ? formatEther(sale.price) + ' ETH' : sale.price.toString() + ' units'}</b></> : 'Loading…'}
|
|
375
|
+
{sale ? <> · Sold <b>{sale.sold.toString()}/{sale.allocation.toString()}</b></> : null}
|
|
376
|
+
{maxInv > 0 ? <> · Supply <b>{supply}/{maxInv}</b></> : null}
|
|
377
|
+
{' '}
|
|
378
|
+
<span className={'pill ' + (paused || soldOut ? 'closed' : 'open')}>{soldOut ? 'sold out' : paused ? 'paused' : 'open'}</span>
|
|
379
|
+
</div>
|
|
380
|
+
<button onClick={mint} disabled={!canMint}>
|
|
381
|
+
{busy ? 'Minting…' : soldOut ? 'Sold out' : paused ? 'Paused' : !isEth ? 'ERC-20 — see README' : account ? 'Mint' : 'Connect & mint'}
|
|
382
|
+
</button>
|
|
383
|
+
</div>
|
|
384
|
+
{msg ? <div className="muted" style={{marginTop: 12}}>{msg}</div> : null}
|
|
385
|
+
{sale && !sale.configured ? <div className="muted" style={{marginTop: 12}}>No sale configured yet for this collection.</div> : null}
|
|
386
|
+
</div>
|
|
387
|
+
|
|
388
|
+
<div className="grid">
|
|
389
|
+
{tiles.map((t) => (
|
|
390
|
+
<div className="tile" key={t.id}>
|
|
391
|
+
{t.image ? <img src={t.image} alt={t.name} /> : <div style={{aspectRatio: '1', background: '#0e0e13'}} />}
|
|
392
|
+
<div className="label">{t.name}</div>
|
|
393
|
+
</div>
|
|
394
|
+
))}
|
|
395
|
+
</div>
|
|
396
|
+
{supply === 0 ? <p className="muted" style={{textAlign: 'center', marginTop: 24}}>No tokens minted yet — be the first.</p> : null}
|
|
397
|
+
|
|
398
|
+
{pages > 1 ? (
|
|
399
|
+
<div className="pager">
|
|
400
|
+
<button className="ghost" onClick={() => setPage((p) => Math.max(0, p - 1))} disabled={page === 0}>Prev</button>
|
|
401
|
+
<span className="muted">Page {page + 1} / {pages}</span>
|
|
402
|
+
<button className="ghost" onClick={() => setPage((p) => Math.min(pages - 1, p + 1))} disabled={page >= pages - 1}>Next</button>
|
|
403
|
+
</div>
|
|
404
|
+
) : null}
|
|
405
|
+
</main>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
`;
|
|
409
|
+
function readme(o) {
|
|
410
|
+
return ('# ' +
|
|
411
|
+
o.collectionName +
|
|
412
|
+
' — mint page\n\n' +
|
|
413
|
+
'A self-contained Next.js mint site for the ABX collection `' +
|
|
414
|
+
o.token +
|
|
415
|
+
'`, wired to the shared fixed-price minter `' +
|
|
416
|
+
o.minter +
|
|
417
|
+
'` on ' +
|
|
418
|
+
o.chainName +
|
|
419
|
+
'. Generated by `abx mint-page`.\n\n' +
|
|
420
|
+
'## Run it locally\n\n' +
|
|
421
|
+
'```bash\nnpm install\nnpm run dev # http://localhost:3000\n```\n\n' +
|
|
422
|
+
'Config lives in `.env.local` (already prefilled). It reads sale state + each token\'s image straight from chain — no backend.\n\n' +
|
|
423
|
+
'## Deploy (Vercel — recommended)\n\n' +
|
|
424
|
+
'```bash\nnpm i -g vercel\nvercel # first run links/creates the project\nvercel --prod # ship it\n```\n\n' +
|
|
425
|
+
'In the Vercel dashboard set the same `NEXT_PUBLIC_*` vars from `.env.local` (Project → Settings → Environment Variables), then redeploy. Any Next.js host works; Vercel is the least-setup path.\n\n' +
|
|
426
|
+
'## Customize (this is a starting point)\n\n' +
|
|
427
|
+
'- **Design** — it is plain React + one `app/globals.css`. Restyle freely; ask your agent for a bespoke layout per drop.\n' +
|
|
428
|
+
'- **Multi-wallet** — V1 uses the injected wallet (`window.ethereum`) with zero config. For WalletConnect/Coinbase/etc., swap in [RainbowKit](https://rainbowkit.com) + wagmi (needs a WalletConnect projectId).\n' +
|
|
429
|
+
'- **ERC-20 sales** — the mint button handles ETH. For an ERC-20-priced sale, add an `approve(minter, price)` step before `purchase` (see `lib/abi.ts`).\n' +
|
|
430
|
+
'- **RPC** — `NEXT_PUBLIC_RPC_URL` is a public endpoint (may rate-limit). Use your own **keyless** endpoint if you like; never put a secret-keyed URL in a `NEXT_PUBLIC_` var — it ships to the browser.\n' +
|
|
431
|
+
'- **Gallery** — shows minted tokens newest-first, ' +
|
|
432
|
+
'paginated. It reads `tokenURI(id)`; on-chain collections resolve with no backend, off-chain ones need their resolver/gateway reachable.\n');
|
|
433
|
+
}
|
|
434
|
+
export function mintPageArtifact(o) {
|
|
435
|
+
return {
|
|
436
|
+
files: [
|
|
437
|
+
{ path: 'package.json', content: packageJson(o) },
|
|
438
|
+
{ path: 'next.config.mjs', content: NEXT_CONFIG },
|
|
439
|
+
{ path: 'tsconfig.json', content: TSCONFIG },
|
|
440
|
+
{ path: 'next-env.d.ts', content: NEXT_ENV },
|
|
441
|
+
{ path: '.gitignore', content: GITIGNORE },
|
|
442
|
+
{ path: '.env.local', content: envFile(o) },
|
|
443
|
+
{ path: '.env.example', content: envFile({ ...o, token: '0xYourCollection', minter: o.minter }) },
|
|
444
|
+
{ path: 'lib/config.ts', content: LIB_CONFIG },
|
|
445
|
+
{ path: 'lib/chain.ts', content: LIB_CHAIN },
|
|
446
|
+
{ path: 'lib/abi.ts', content: LIB_ABI },
|
|
447
|
+
{ path: 'lib/meta.ts', content: LIB_META },
|
|
448
|
+
{ path: 'app/globals.css', content: APP_GLOBALS },
|
|
449
|
+
{ path: 'app/layout.tsx', content: APP_LAYOUT },
|
|
450
|
+
{ path: 'app/page.tsx', content: APP_PAGE },
|
|
451
|
+
{ path: 'app/mint-client.tsx', content: APP_MINT_CLIENT },
|
|
452
|
+
{ path: 'README.md', content: readme(o) },
|
|
453
|
+
],
|
|
454
|
+
steps: [
|
|
455
|
+
'npm install',
|
|
456
|
+
'npm run dev # preview at http://localhost:3000',
|
|
457
|
+
'npm i -g vercel && vercel --prod # deploy (set the NEXT_PUBLIC_* vars in the Vercel dashboard)',
|
|
458
|
+
],
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
//# sourceMappingURL=mintpage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mintpage.js","sourceRoot":"","sources":["../src/mintpage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type Address, type GeneratorReadClient, type GeneratorStatus, type Hex, type ParsedDependencyRef, type ProjectState } from '@artblocks/abx-sdk';
|
|
2
|
+
import type { DepCheck } from './deps.js';
|
|
3
|
+
/** The self-referential key: the CSV lists it too (the resolver's tokenData carries every
|
|
4
|
+
* evented param — including `params.keys` itself — so the generator must list it to stay
|
|
5
|
+
* byte-aligned; inputsHash parity is load-bearing). */
|
|
6
|
+
export declare const PARAMS_KEYS = "params.keys";
|
|
7
|
+
/** ≤ this many chars rides as a literal readable-ASCII bytes32; longer takes the data path. */
|
|
8
|
+
export declare const PARAMS_KEYS_LITERAL_MAX = 31;
|
|
9
|
+
/**
|
|
10
|
+
* Compose the `params.keys` CSV: every param key the deploy writes ∪ the `--schema` keys ∪
|
|
11
|
+
* `params.keys` itself — deduped, sorted lexicographically, comma-joined. Sorted because the
|
|
12
|
+
* canonical tokenData serialization sorts keys; deterministic composition keeps the value
|
|
13
|
+
* reproducible from the same flags.
|
|
14
|
+
*/
|
|
15
|
+
export declare function composeParamsKeys(writtenParamKeys: string[], schemaKeys: string[]): string;
|
|
16
|
+
/** Whether a CSV fits the literal bytes32 lane (printable ASCII, ≤ 31 bytes). */
|
|
17
|
+
export declare function paramsKeysIsLiteral(csv: string): boolean;
|
|
18
|
+
/** The `params.keys` write leg: `setContractParam` (literal bytes32) when the CSV fits,
|
|
19
|
+
* else `setContractParamData` (one blob; hash evented — the generator reads it back whole). */
|
|
20
|
+
export declare function paramsKeysCall(csv: string): {
|
|
21
|
+
call: Hex;
|
|
22
|
+
encoding: 'literal' | 'data';
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The `--onchain-uri` legs of the setup multicall, in order:
|
|
26
|
+
* 1. collection-scope `animation_url` field → representation `renderer`,
|
|
27
|
+
* value `abi.encode(generator)` (the canonical generator computes the live view on-chain);
|
|
28
|
+
* 2. the `params.keys` contract param (the generator's tokenData enumeration seam);
|
|
29
|
+
* 3. `setTokenURIRenderer(metadataRenderer)` — tokenURI resolves ON-CHAIN from here on;
|
|
30
|
+
* 4. `setContractURIRenderer(metadataRenderer)` — contractURI too (parity with the 1/1 lane).
|
|
31
|
+
* Placed BEFORE the mint legs so every token is born with the on-chain lane fully configured.
|
|
32
|
+
*/
|
|
33
|
+
export declare function onchainUriSetupCalls(args: {
|
|
34
|
+
generator: Address;
|
|
35
|
+
metadataRenderer: Address;
|
|
36
|
+
paramsKeys: string;
|
|
37
|
+
}): {
|
|
38
|
+
calls: Hex[];
|
|
39
|
+
paramsKeysEncoding: 'literal' | 'data';
|
|
40
|
+
};
|
|
41
|
+
/** What the deploy can honestly promise about the generator's template branch, derived from
|
|
42
|
+
* the selection-time dependency report (never a new RPC): chain-complete iff every registry
|
|
43
|
+
* dep has proven ON-CHAIN bytes (OnChain refs read their data contract directly, so they
|
|
44
|
+
* count as on-chain; `abx verify` is the from-chain confirmation either way). */
|
|
45
|
+
export declare function expectedChainComplete(deps: ParsedDependencyRef[], checks: DepCheck[]): {
|
|
46
|
+
expected: boolean | null;
|
|
47
|
+
detail: string;
|
|
48
|
+
};
|
|
49
|
+
/** The generator this project's on-chain lane actually uses: the collection `animation_url`
|
|
50
|
+
* field's `renderer` value when set (that address is what the metadata renderer staticcalls),
|
|
51
|
+
* else the chain's canonical generator. Null when neither exists. */
|
|
52
|
+
export declare function generatorFor(state: ProjectState, override?: string): Address | null;
|
|
53
|
+
/** Whether verify should run the on-chain-URI report: the tokenURIRenderer toggle is on, or
|
|
54
|
+
* the collection `animation` field points at a field renderer (the generator). */
|
|
55
|
+
export declare function hasOnChainUriLane(state: ProjectState): boolean;
|
|
56
|
+
export interface TokenUriProbe {
|
|
57
|
+
tokenId: string;
|
|
58
|
+
/** tokenURI(id) is a `data:application/json;base64,` URI (resolves on-chain, any RPC). */
|
|
59
|
+
onChainJson: boolean;
|
|
60
|
+
uriPrefix: string;
|
|
61
|
+
animation: {
|
|
62
|
+
form: 'data-html';
|
|
63
|
+
bytes: number;
|
|
64
|
+
marker: string | null;
|
|
65
|
+
} | {
|
|
66
|
+
form: 'url';
|
|
67
|
+
url: string;
|
|
68
|
+
} | {
|
|
69
|
+
form: 'data-other';
|
|
70
|
+
prefix: string;
|
|
71
|
+
} | {
|
|
72
|
+
form: 'missing';
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export interface OnChainUriReport {
|
|
76
|
+
generator: Address;
|
|
77
|
+
status: GeneratorStatus;
|
|
78
|
+
/** Null when no token is minted yet (tokenURI would revert). */
|
|
79
|
+
probe: TokenUriProbe | null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The verify-time on-chain-URI report: `generator.onChainStatus(token)` (branch ·
|
|
83
|
+
* chain-completeness · unresolved refs · URL budget) plus a `tokenURI` probe of the lowest
|
|
84
|
+
* minted token — decoded, with the `animation_url` form checked against the branch.
|
|
85
|
+
*/
|
|
86
|
+
export declare function onChainUriReport(client: GeneratorReadClient, state: ProjectState, generatorOverride?: string): Promise<OnChainUriReport>;
|
|
87
|
+
/**
|
|
88
|
+
* After a param write: if the project's `params.keys` CSV exists but doesn't list the
|
|
89
|
+
* configured key, the on-chain generator's tokenData will silently omit it (breaking byte
|
|
90
|
+
* parity with the resolver's). Returns the fixed CSV to suggest, or null when no nudge is
|
|
91
|
+
* warranted (no CSV = not on the on-chain lane; already listed = fine).
|
|
92
|
+
*/
|
|
93
|
+
export declare function paramsKeysNudge(existingCsv: string | null, configuredKey: string): string | null;
|
|
94
|
+
/** Read the project's `params.keys` contract param from chain (literal or data-backed);
|
|
95
|
+
* null when unset. Best-effort — callers must treat a throw as "no nudge". */
|
|
96
|
+
export declare function readParamsKeys(client: GeneratorReadClient, contract: Address): Promise<string | null>;
|
|
97
|
+
//# sourceMappingURL=onchain-uri.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onchain-uri.d.ts","sourceRoot":"","sources":["../src/onchain-uri.ts"],"names":[],"mappings":"AAaA,OAAO,EAUL,KAAK,OAAO,EACZ,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,GAAG,EACR,KAAK,mBAAmB,EACxB,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,WAAW,CAAC;AAExC;;wDAEwD;AACxD,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,+FAA+F;AAC/F,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ1F;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;gGACgG;AAChG,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAAC,IAAI,EAAE,GAAG,CAAC;IAAC,QAAQ,EAAE,SAAS,GAAG,MAAM,CAAA;CAAC,CAmBrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG;IAAC,KAAK,EAAE,GAAG,EAAE,CAAC;IAAC,kBAAkB,EAAE,SAAS,GAAG,MAAM,CAAA;CAAC,CAuBzD;AAID;;;kFAGkF;AAClF,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,mBAAmB,EAAE,EAC3B,MAAM,EAAE,QAAQ,EAAE,GACjB;IAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAoB5C;AAID;;sEAEsE;AACtE,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAUnF;AAED;mFACmF;AACnF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAM9D;AAYD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EACL;QAAC,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAC,GACzD;QAAC,IAAI,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,GAC1B;QAAC,IAAI,EAAE,YAAY,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,GACpC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAC,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;IACxB,gEAAgE;IAChE,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7B;AAmBD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,YAAY,EACnB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAgC3B;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKhG;AAED;+EAC+E;AAC/E,wBAAsB,cAAc,CAAC,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgB3G"}
|