Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env node
2/**
3 * test-temple-keep.mjs - Test keeping a piece via Temple mobile wallet
4 *
5 * This script:
6 * 1. Connects to Temple via QR code (Beacon P2P)
7 * 2. Prepares the keep parameters
8 * 3. Sends the operation to Temple for signing
9 *
10 * Usage:
11 * node test-temple-keep.mjs <piece-code>
12 * node test-temple-keep.mjs puf
13 */
14
15import { pairWallet, sendContractCall } from "./beacon-node.mjs";
16import { connect } from "../system/backend/database.mjs";
17import { TezosToolkit } from "@taquito/taquito";
18
19// Contract and network config - Ghostnet v3
20const CONTRACT_ADDRESS = "KT1StXrQNvRd9dNPpHdCGEstcGiBV6neq79K";
21const NETWORK = "ghostnet";
22const RPC = "https://ghostnet.ecadinfra.com";
23
24// Colors
25const RESET = "\x1b[0m";
26const BOLD = "\x1b[1m";
27const DIM = "\x1b[2m";
28const CYAN = "\x1b[36m";
29const GREEN = "\x1b[32m";
30const YELLOW = "\x1b[33m";
31const RED = "\x1b[31m";
32
33// String to hex bytes (for Michelson)
34function stringToBytes(str) {
35 return Buffer.from(str, "utf8").toString("hex");
36}
37
38async function main() {
39 const pieceCode = process.argv[2];
40
41 if (!pieceCode) {
42 console.log(`${RED}Usage: node test-temple-keep.mjs <piece-code>${RESET}`);
43 console.log(`${DIM}Example: node test-temple-keep.mjs puf${RESET}\n`);
44 process.exit(1);
45 }
46
47 const cleanCode = pieceCode.replace(/^\$/, '');
48
49 console.log(`\n${BOLD}${CYAN}╔════════════════════════════════════════════════════════════════╗${RESET}`);
50 console.log(`${BOLD}${CYAN}║ 🏺 Keep via Temple Wallet: $${cleanCode}${RESET.padEnd(30)}${BOLD}${CYAN}║${RESET}`);
51 console.log(`${BOLD}${CYAN}╚════════════════════════════════════════════════════════════════╝${RESET}\n`);
52
53 // Step 1: Pair with Temple
54 console.log(`${CYAN}Step 1: Connect to Temple Wallet${RESET}\n`);
55 const pairResult = await pairWallet("Aesthetic Computer");
56
57 if (!pairResult?.permissionResponse?.address) {
58 console.log(`${RED}❌ Failed to connect wallet${RESET}\n`);
59 process.exit(1);
60 }
61
62 const { client, permissionResponse } = pairResult;
63 const walletAddress = permissionResponse.address;
64
65 console.log(`\n${GREEN}✓ Wallet connected: ${walletAddress}${RESET}\n`);
66
67 // Step 2: Get piece metadata from MongoDB (optional - for display)
68 console.log(`${CYAN}Step 2: Preparing keep parameters${RESET}\n`);
69
70 // For testing, we'll create minimal metadata
71 // In production, this would come from the KidLisp piece in MongoDB
72 const testMetadata = {
73 artifactUri: stringToBytes(`ipfs://test-artifact-${cleanCode}`),
74 attributes: stringToBytes("[]"),
75 content_hash: stringToBytes(`test-hash-${cleanCode}-${Date.now()}`),
76 content_type: stringToBytes("text/kidlisp"),
77 creators: stringToBytes(`["${walletAddress}"]`),
78 decimals: stringToBytes("0"),
79 description: stringToBytes(`Test keep for $${cleanCode}`),
80 displayUri: stringToBytes(`https://aesthetic.computer/$${cleanCode}`),
81 formats: stringToBytes("[]"),
82 isBooleanAmount: stringToBytes("true"),
83 metadata_uri: stringToBytes(""),
84 name: stringToBytes(`$${cleanCode}`),
85 owner: walletAddress,
86 rights: stringToBytes(""),
87 shouldPreferSymbol: stringToBytes("false"),
88 symbol: stringToBytes(`$${cleanCode}`),
89 tags: stringToBytes(`["KidLisp"]`),
90 thumbnailUri: stringToBytes(`https://aesthetic.computer/$${cleanCode}/thumbnail`)
91 };
92
93 console.log(`${DIM}Piece: $${cleanCode}${RESET}`);
94 console.log(`${DIM}Owner: ${walletAddress}${RESET}`);
95 console.log(`${DIM}Contract: ${CONTRACT_ADDRESS}${RESET}\n`);
96
97 // Step 3: Build Michelson parameters for the keep entrypoint
98 // The keep entrypoint expects a complex record type
99 const keepParams = {
100 prim: "Pair",
101 args: [
102 { bytes: testMetadata.artifactUri }, // artifactUri
103 { prim: "Pair", args: [
104 { bytes: testMetadata.attributes }, // attributes
105 { prim: "Pair", args: [
106 { bytes: testMetadata.content_hash }, // content_hash
107 { prim: "Pair", args: [
108 { bytes: testMetadata.content_type }, // content_type
109 { prim: "Pair", args: [
110 { bytes: testMetadata.creators }, // creators
111 { prim: "Pair", args: [
112 { bytes: testMetadata.decimals }, // decimals
113 { prim: "Pair", args: [
114 { bytes: testMetadata.description }, // description
115 { prim: "Pair", args: [
116 { bytes: testMetadata.displayUri }, // displayUri
117 { prim: "Pair", args: [
118 { bytes: testMetadata.formats }, // formats
119 { prim: "Pair", args: [
120 { bytes: testMetadata.isBooleanAmount }, // isBooleanAmount
121 { prim: "Pair", args: [
122 { bytes: testMetadata.metadata_uri }, // metadata_uri
123 { prim: "Pair", args: [
124 { bytes: testMetadata.name }, // name
125 { prim: "Pair", args: [
126 { string: testMetadata.owner }, // owner (address)
127 { prim: "Pair", args: [
128 { bytes: testMetadata.rights }, // rights
129 { prim: "Pair", args: [
130 { bytes: testMetadata.shouldPreferSymbol }, // shouldPreferSymbol
131 { prim: "Pair", args: [
132 { bytes: testMetadata.symbol }, // symbol
133 { prim: "Pair", args: [
134 { bytes: testMetadata.tags }, // tags
135 { bytes: testMetadata.thumbnailUri } // thumbnailUri
136 ]}
137 ]}
138 ]}
139 ]}
140 ]}
141 ]}
142 ]}
143 ]}
144 ]}
145 ]}
146 ]}
147 ]}
148 ]}
149 ]}
150 ]}
151 ]}
152 ]
153 };
154
155 // Step 4: Send the operation to Temple
156 console.log(`${CYAN}Step 3: Send operation to Temple for signing${RESET}\n`);
157 console.log(`${YELLOW}📱 Check your Temple wallet to approve the transaction...${RESET}\n`);
158 console.log(`${DIM}Keep fee: 5 XTZ${RESET}\n`);
159
160 try {
161 const response = await sendContractCall(
162 client,
163 CONTRACT_ADDRESS,
164 "keep",
165 keepParams,
166 "5000000" // Keep fee: 5 XTZ in mutez
167 );
168
169 console.log(`\n${GREEN}🏺 Keep operation submitted!${RESET}\n`);
170 if (response.transactionHash) {
171 console.log(`${CYAN}View on TzKT:${RESET} https://ghostnet.tzkt.io/${response.transactionHash}`);
172 }
173 } catch (err) {
174 console.log(`\n${RED}❌ Keep failed: ${err.message}${RESET}\n`);
175 process.exit(1);
176 }
177}
178
179main().catch(err => {
180 console.error(`${RED}Error: ${err.message}${RESET}`);
181 process.exit(1);
182});