Monorepo for Aesthetic.Computer
aesthetic.computer
1/**
2 * Keeps v6 Launch Prep - Source Invariants
3 *
4 * Offline checks to keep launch-prep assumptions stable:
5 * - v6 metadata policy is reflected in live mint/update code paths
6 * - deploy tooling supports v6 profile with production defaults
7 */
8
9import fs from 'node:fs';
10import path from 'node:path';
11
12describe('🚀 Keeps v6 Launch Prep - Source Checks', () => {
13 const keepMintPath = path.join(process.cwd(), 'system', 'netlify', 'functions', 'keep-mint.mjs');
14 const keepUpdatePath = path.join(process.cwd(), 'system', 'netlify', 'functions', 'keep-update.mjs');
15 const keepUpdateConfirmPath = path.join(process.cwd(), 'system', 'netlify', 'functions', 'keep-update-confirm.mjs');
16 const kidlispKeepPath = path.join(process.cwd(), 'system', 'netlify', 'functions', 'kidlisp-keep.mjs');
17 const keepsCliPath = path.join(process.cwd(), 'tezos', 'keeps.mjs');
18 const compilePath = path.join(process.cwd(), 'tezos', 'compile.fish');
19 const netlifyTomlPath = path.join(process.cwd(), 'system', 'netlify.toml');
20 const v6ContractPath = path.join(process.cwd(), 'tezos', 'kidlisp_keeps_fa2_v6.py');
21 const v6CompiledTzPath = path.join(process.cwd(), 'tezos', 'KeepsFA2v6', 'step_002_cont_0_contract.tz');
22
23 let keepMintSource;
24 let keepUpdateSource;
25 let keepUpdateConfirmSource;
26 let kidlispKeepSource;
27 let keepsCliSource;
28 let compileSource;
29 let netlifyTomlSource;
30 let v6ContractSource;
31 let v6CompiledTzSource;
32
33 beforeAll(() => {
34 keepMintSource = fs.readFileSync(keepMintPath, 'utf8');
35 keepUpdateSource = fs.readFileSync(keepUpdatePath, 'utf8');
36 keepUpdateConfirmSource = fs.readFileSync(keepUpdateConfirmPath, 'utf8');
37 kidlispKeepSource = fs.readFileSync(kidlispKeepPath, 'utf8');
38 keepsCliSource = fs.readFileSync(keepsCliPath, 'utf8');
39 compileSource = fs.readFileSync(compilePath, 'utf8');
40 netlifyTomlSource = fs.readFileSync(netlifyTomlPath, 'utf8');
41 v6ContractSource = fs.readFileSync(v6ContractPath, 'utf8');
42 v6CompiledTzSource = fs.readFileSync(v6CompiledTzPath, 'utf8');
43 });
44
45 const entrypointBlock = (signature) => v6ContractSource.split(signature)[1]?.split('@sp.entrypoint')[0] || '';
46 const netlifyFunctionBlock = (name) => netlifyTomlSource.split(`[functions.${name}]`)[1]?.split('[functions.')[0] || '';
47
48 it('enforces KidLisp-only tags in mint/update pipelines', () => {
49 expect(keepMintSource).toContain('const tags = ["KidLisp"]');
50 expect(keepUpdateSource).toContain('const tags = ["KidLisp"]');
51 });
52
53 it('uses single Characters attribute in mint/update metadata payloads', () => {
54 expect(keepMintSource).toContain('{ name: "Characters", value:');
55 expect(keepUpdateSource).toContain('{ name: "Characters", value:');
56
57 expect(keepMintSource).not.toContain('{ name: "Handle", value:');
58 expect(keepMintSource).not.toContain('{ name: "User", value:');
59 expect(keepMintSource).not.toContain('{ name: "Packed on", value:');
60 expect(keepMintSource).not.toContain('Analyzer Version');
61 expect(keepMintSource).not.toContain('Author Handle');
62 expect(keepMintSource).not.toContain('Author Code');
63 expect(keepUpdateSource).not.toContain('Analyzer Version');
64 });
65
66 it('has deploy profile support for v6 production metadata and fee defaults', () => {
67 expect(keepsCliSource).toContain('const CONTRACT_PROFILES =');
68 expect(keepsCliSource).toContain("v6:");
69 expect(keepsCliSource).toContain("artifactKey: 'v6'");
70 expect(keepsCliSource).toContain("KeepsFA2v6/step_002_cont_0_contract.tz");
71 expect(keepsCliSource).toContain("name: 'KidLisp'");
72 expect(keepsCliSource).toContain("version: '6.0.0'");
73 expect(keepsCliSource).toContain("description: 'https://keep.kidlisp.com'");
74 expect(keepsCliSource).toContain('keepFeeMutez: 2_500_000');
75 expect(keepsCliSource).toContain("await deployContract(getNetwork(1), { contractProfile })");
76 });
77
78 it('includes staging deprecation housekeeping workflow in keeps CLI', () => {
79 expect(keepsCliSource).toContain("async function deprecateStagingContracts");
80 expect(keepsCliSource).toContain("case 'deprecate-staging'");
81 expect(keepsCliSource).toContain('--replacement=<KT1...>');
82 expect(keepsCliSource).toContain('--addresses=<KT1,KT1,...>');
83 });
84
85 it('supports dedicated v6 compile target in compile.fish', () => {
86 expect(compileSource).toContain('./compile.fish v6');
87 expect(compileSource).toContain('case v6');
88 expect(compileSource).toContain('set source_file kidlisp_keeps_fa2_v6.py');
89 expect(compileSource).toContain('set output_dir KeepsFA2v6');
90 });
91
92 it('exposes expected v6 custom contract entrypoints in source and artifact', () => {
93 const expectedDefs = [
94 'def keep(self, params):',
95 'def edit_metadata(self, params):',
96 'def lock_metadata(self, token_id):',
97 'def set_contract_metadata(self, params):',
98 'def lock_contract_metadata(self):',
99 'def set_keep_fee(self, new_fee):',
100 'def withdraw_fees(self, destination):',
101 'def burn_keep(self, token_id):',
102 'def pause(self):',
103 'def unpause(self):',
104 'def set_default_royalty(self, bps):',
105 'def admin_transfer(self, params):'
106 ];
107
108 expectedDefs.forEach((signature) => expect(v6ContractSource).toContain(signature));
109
110 const expectedEntrypoints = [
111 '%keep',
112 '%edit_metadata',
113 '%lock_metadata',
114 '%set_contract_metadata',
115 '%lock_contract_metadata',
116 '%set_keep_fee',
117 '%withdraw_fees',
118 '%burn_keep',
119 '%pause',
120 '%unpause',
121 '%set_default_royalty',
122 '%admin_transfer'
123 ];
124
125 expectedEntrypoints.forEach((entrypoint) => expect(v6CompiledTzSource).toContain(entrypoint));
126
127 const expectedInheritedEntrypoints = [
128 '%transfer',
129 '%balance_of',
130 '%update_operators',
131 '%set_administrator'
132 ];
133
134 expectedInheritedEntrypoints.forEach((entrypoint) => expect(v6CompiledTzSource).toContain(entrypoint));
135 });
136
137 it('keeps strict guards on keep mint entrypoint', () => {
138 const keepBlock = entrypointBlock('def keep(self, params):');
139 expect(keepBlock).toContain('assert not self.data.paused, "MINTING_PAUSED"');
140 expect(keepBlock).toContain('if not is_admin:');
141 expect(keepBlock).toContain('assert sp.amount >= self.data.keep_fee, "INSUFFICIENT_FEE"');
142 expect(keepBlock).toContain('assert params.owner == sp.sender, "MUST_MINT_TO_SELF"');
143 expect(keepBlock).toContain('assert not self.data.content_hashes.contains(params.content_hash), "DUPLICATE_CONTENT_HASH"');
144 });
145
146 it('keeps FA2 mint/burn compatibility while disabling bypass paths in v6', () => {
147 const mintBlock = entrypointBlock('def mint(self, batch):');
148 const burnBlock = entrypointBlock('def burn(self, batch):');
149 expect(mintBlock).toContain('assert False, "MINT_DISABLED_USE_KEEP"');
150 expect(burnBlock).toContain('assert False, "BURN_DISABLED_USE_BURN_KEEP"');
151 expect(v6CompiledTzSource).toContain('%mint');
152 expect(v6CompiledTzSource).toContain('%burn');
153 expect(v6CompiledTzSource).toContain('MINT_DISABLED_USE_KEEP');
154 expect(v6CompiledTzSource).toContain('BURN_DISABLED_USE_BURN_KEEP');
155 });
156
157 it('keeps lock_metadata scoped to admin or token owner', () => {
158 const lockBlock = entrypointBlock('def lock_metadata(self, token_id):');
159 expect(lockBlock).toContain('is_admin = self.is_administrator_()');
160 expect(lockBlock).toContain('is_owner = self.data.ledger.get(token_id');
161 expect(lockBlock).toContain('assert is_admin or is_owner, "NOT_AUTHORIZED"');
162 });
163
164 it('keeps royalties immutable in v6 contract edit_metadata', () => {
165 expect(v6ContractSource).toContain('original_royalties = existing_info.get("royalties"');
166 expect(v6ContractSource).toContain('token_info["royalties"] = original_royalties');
167 });
168
169 it('restricts v6 edit_metadata to token owner only', () => {
170 const editBlock = v6ContractSource.split('def edit_metadata(self, params):')[1]?.split('@sp.entrypoint')[0] || '';
171 expect(editBlock).toContain('is_owner = self.data.ledger.get(params.token_id');
172 expect(editBlock).toContain('assert is_owner, "NOT_TOKEN_OWNER"');
173 expect(editBlock).not.toContain('is_admin = self.is_administrator_()');
174 expect(editBlock).not.toContain('is_creator = self.data.token_creators.get(params.token_id');
175 expect(editBlock).not.toContain('NOT_AUTHORIZED');
176 });
177
178 it('restricts v6 burn_keep to token owner (not admin)', () => {
179 const burnBlock = entrypointBlock('def burn_keep(self, token_id):');
180 expect(burnBlock).toContain('assert current_owner == sp.sender, "NOT_TOKEN_OWNER"');
181 expect(burnBlock).not.toContain('FA2_NOT_ADMIN');
182 });
183
184 it('keeps admin-only controls on governance endpoints', () => {
185 const adminOnlySigs = [
186 'def set_contract_metadata(self, params):',
187 'def lock_contract_metadata(self):',
188 'def set_keep_fee(self, new_fee):',
189 'def withdraw_fees(self, destination):',
190 'def pause(self):',
191 'def unpause(self):',
192 'def set_default_royalty(self, bps):',
193 'def admin_transfer(self, params):'
194 ];
195
196 adminOnlySigs.forEach((signature) => {
197 const block = entrypointBlock(signature);
198 expect(block).toContain('assert self.is_administrator_(), "FA2_NOT_ADMIN"');
199 });
200 });
201
202 it('keeps default royalty bounded to 25% max', () => {
203 const royaltyBlock = entrypointBlock('def set_default_royalty(self, bps):');
204 expect(royaltyBlock).toContain('assert bps <= 2500, "MAX_ROYALTY_25_PERCENT"');
205 });
206
207 it('uses contract default_royalty_bps in mint pipeline and preserves royalties on update', () => {
208 expect(keepMintSource).toContain('storage.default_royalty_bps');
209 expect(keepMintSource).toContain('const royalties = buildRoyalties(');
210 expect(keepUpdateSource).toContain('parseRoyaltiesFromHex');
211 expect(keepUpdateSource).toContain('preservedRoyalties || buildRoyalties(');
212 });
213
214 it('keeps regenerate path resilient when thumbnail bake exceeds prep budget', () => {
215 expect(keepMintSource).toContain('const forceFreshMedia = Boolean(regenerate);');
216 expect(keepMintSource).toContain('KEEP_MINT_EFFECTIVE_PREP_MS');
217 expect(keepMintSource).toContain('KEEP_MINT_FORCE_FRESH_THUMBNAIL_AWAIT_MS');
218 expect(keepMintSource).toContain('Low time budget, reusing previous thumbnail');
219 expect(keepMintSource).toContain('Bake timeout, reusing previous thumbnail');
220 });
221
222 it('resolves keeps contract address from Mongo secrets (not Netlify env var)', () => {
223 expect(keepMintSource).toContain('getKeepsContractAddress');
224 expect(keepUpdateSource).toContain('getKeepsContractAddress');
225 expect(keepUpdateConfirmSource).toContain('getKeepsContractAddress');
226 expect(kidlispKeepSource).toContain('getKeepsContractAddress');
227
228 expect(keepMintSource).not.toContain('TEZOS_KEEPS_CONTRACT');
229 expect(keepUpdateSource).not.toContain('TEZOS_KEEPS_CONTRACT');
230 expect(keepUpdateConfirmSource).not.toContain('TEZOS_KEEPS_CONTRACT');
231 expect(kidlispKeepSource).not.toContain('TEZOS_KEEPS_CONTRACT');
232 });
233
234 it('keeps Netlify keep functions decoupled from TEZOS_KEEPS_CONTRACT env var', () => {
235 expect(netlifyTomlSource).toContain('[functions.keeps-config]');
236
237 const keepMintBlock = netlifyFunctionBlock('keep-mint');
238 const keepUpdateBlock = netlifyFunctionBlock('keep-update');
239 const keepUpdateConfirmBlock = netlifyFunctionBlock('keep-update-confirm');
240 const kidlispKeepBlock = netlifyFunctionBlock('kidlisp-keep');
241
242 expect(keepMintBlock).not.toContain('TEZOS_KEEPS_CONTRACT');
243 expect(keepUpdateBlock).not.toContain('TEZOS_KEEPS_CONTRACT');
244 expect(keepUpdateConfirmBlock).not.toContain('TEZOS_KEEPS_CONTRACT');
245 expect(kidlispKeepBlock).not.toContain('TEZOS_KEEPS_CONTRACT');
246 });
247});