Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 247 lines 12 kB view raw
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});