@net-protocol/cli 0.1.13 → 0.1.15
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 +38 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.mjs +2353 -125
- package/dist/cli/index.mjs.map +1 -1
- package/dist/feed/index.d.ts +58 -0
- package/dist/feed/index.mjs +1357 -0
- package/dist/feed/index.mjs.map +1 -0
- package/dist/profile/index.d.ts +8 -0
- package/dist/profile/index.mjs +970 -0
- package/dist/profile/index.mjs.map +1 -0
- package/package.json +7 -1
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk3 from 'chalk';
|
|
3
|
+
import { StorageClient, chunkDataForStorage, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
|
|
4
|
+
import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, PROFILE_CANVAS_STORAGE_KEY, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getProfileMetadataStorageArgs, isValidBio, isValidTokenAddress } from '@net-protocol/profiles';
|
|
5
|
+
import { createWalletClient, http, publicActions, encodeFunctionData } from 'viem';
|
|
6
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
7
|
+
import { base } from 'viem/chains';
|
|
8
|
+
import { getChainRpcUrls, toBytes32 } from '@net-protocol/core';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
|
|
12
|
+
// src/commands/profile/index.ts
|
|
13
|
+
function getRequiredChainId(optionValue) {
|
|
14
|
+
const chainId = optionValue || (process.env.NET_CHAIN_ID ? parseInt(process.env.NET_CHAIN_ID, 10) : void 0);
|
|
15
|
+
if (!chainId) {
|
|
16
|
+
console.error(
|
|
17
|
+
chalk3.red(
|
|
18
|
+
"Error: Chain ID is required. Provide via --chain-id flag or NET_CHAIN_ID environment variable"
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
return chainId;
|
|
24
|
+
}
|
|
25
|
+
function getRpcUrl(optionValue) {
|
|
26
|
+
return optionValue || process.env.NET_RPC_URL;
|
|
27
|
+
}
|
|
28
|
+
function parseCommonOptions(options, supportsEncodeOnly = false) {
|
|
29
|
+
const privateKey = options.privateKey || process.env.NET_PRIVATE_KEY || process.env.PRIVATE_KEY;
|
|
30
|
+
if (!privateKey) {
|
|
31
|
+
const encodeOnlyHint = supportsEncodeOnly ? ", or use --encode-only to output transaction data without submitting" : "";
|
|
32
|
+
console.error(
|
|
33
|
+
chalk3.red(
|
|
34
|
+
`Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable${encodeOnlyHint}`
|
|
35
|
+
)
|
|
36
|
+
);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
if (!privateKey.startsWith("0x") || privateKey.length !== 66) {
|
|
40
|
+
console.error(
|
|
41
|
+
chalk3.red(
|
|
42
|
+
"Error: Invalid private key format (must be 0x-prefixed, 66 characters)"
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (options.privateKey) {
|
|
48
|
+
console.warn(
|
|
49
|
+
chalk3.yellow(
|
|
50
|
+
"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead."
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
privateKey,
|
|
56
|
+
chainId: getRequiredChainId(options.chainId),
|
|
57
|
+
rpcUrl: getRpcUrl(options.rpcUrl)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function parseReadOnlyOptions(options) {
|
|
61
|
+
return {
|
|
62
|
+
chainId: getRequiredChainId(options.chainId),
|
|
63
|
+
rpcUrl: getRpcUrl(options.rpcUrl)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function exitWithError(message) {
|
|
67
|
+
console.error(chalk3.red(`Error: ${message}`));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/commands/profile/get.ts
|
|
72
|
+
async function executeProfileGet(options) {
|
|
73
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
74
|
+
chainId: options.chainId,
|
|
75
|
+
rpcUrl: options.rpcUrl
|
|
76
|
+
});
|
|
77
|
+
const client = new StorageClient({
|
|
78
|
+
chainId: readOnlyOptions.chainId,
|
|
79
|
+
overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
let profilePicture;
|
|
83
|
+
try {
|
|
84
|
+
const pictureResult = await client.readStorageData({
|
|
85
|
+
key: PROFILE_PICTURE_STORAGE_KEY,
|
|
86
|
+
operator: options.address
|
|
87
|
+
});
|
|
88
|
+
if (pictureResult.data) {
|
|
89
|
+
profilePicture = pictureResult.data;
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
93
|
+
if (errorMessage !== "StoredDataNotFound") {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
let xUsername;
|
|
98
|
+
let bio;
|
|
99
|
+
let tokenAddress;
|
|
100
|
+
try {
|
|
101
|
+
const metadataResult = await client.readStorageData({
|
|
102
|
+
key: PROFILE_METADATA_STORAGE_KEY,
|
|
103
|
+
operator: options.address
|
|
104
|
+
});
|
|
105
|
+
if (metadataResult.data) {
|
|
106
|
+
const metadata = parseProfileMetadata(metadataResult.data);
|
|
107
|
+
xUsername = metadata?.x_username;
|
|
108
|
+
bio = metadata?.bio;
|
|
109
|
+
tokenAddress = metadata?.token_address;
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
113
|
+
if (errorMessage !== "StoredDataNotFound") {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
let canvasSize;
|
|
118
|
+
let canvasIsDataUri = false;
|
|
119
|
+
try {
|
|
120
|
+
const canvasResult = await client.readChunkedStorage({
|
|
121
|
+
key: PROFILE_CANVAS_STORAGE_KEY,
|
|
122
|
+
operator: options.address
|
|
123
|
+
});
|
|
124
|
+
if (canvasResult.data) {
|
|
125
|
+
canvasSize = canvasResult.data.length;
|
|
126
|
+
canvasIsDataUri = canvasResult.data.startsWith("data:");
|
|
127
|
+
}
|
|
128
|
+
} catch (error) {
|
|
129
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
130
|
+
if (errorMessage !== "ChunkedStorage metadata not found" && !errorMessage.includes("not found")) {
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const hasProfile = profilePicture || xUsername || bio || tokenAddress || canvasSize;
|
|
135
|
+
if (options.json) {
|
|
136
|
+
const output = {
|
|
137
|
+
address: options.address,
|
|
138
|
+
chainId: readOnlyOptions.chainId,
|
|
139
|
+
profilePicture: profilePicture || null,
|
|
140
|
+
xUsername: xUsername || null,
|
|
141
|
+
bio: bio || null,
|
|
142
|
+
tokenAddress: tokenAddress || null,
|
|
143
|
+
canvas: canvasSize ? { size: canvasSize, isDataUri: canvasIsDataUri } : null,
|
|
144
|
+
hasProfile
|
|
145
|
+
};
|
|
146
|
+
console.log(JSON.stringify(output, null, 2));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
console.log(chalk3.white.bold("\nProfile:\n"));
|
|
150
|
+
console.log(` ${chalk3.cyan("Address:")} ${options.address}`);
|
|
151
|
+
console.log(` ${chalk3.cyan("Chain ID:")} ${readOnlyOptions.chainId}`);
|
|
152
|
+
console.log(
|
|
153
|
+
` ${chalk3.cyan("Profile Picture:")} ${profilePicture || chalk3.gray("(not set)")}`
|
|
154
|
+
);
|
|
155
|
+
console.log(
|
|
156
|
+
` ${chalk3.cyan("X Username:")} ${xUsername ? `@${xUsername}` : chalk3.gray("(not set)")}`
|
|
157
|
+
);
|
|
158
|
+
console.log(
|
|
159
|
+
` ${chalk3.cyan("Bio:")} ${bio || chalk3.gray("(not set)")}`
|
|
160
|
+
);
|
|
161
|
+
console.log(
|
|
162
|
+
` ${chalk3.cyan("Token Address:")} ${tokenAddress || chalk3.gray("(not set)")}`
|
|
163
|
+
);
|
|
164
|
+
console.log(
|
|
165
|
+
` ${chalk3.cyan("Canvas:")} ${canvasSize ? `${canvasSize} bytes${canvasIsDataUri ? " (data URI)" : ""}` : chalk3.gray("(not set)")}`
|
|
166
|
+
);
|
|
167
|
+
if (!hasProfile) {
|
|
168
|
+
console.log(chalk3.yellow("\n No profile data found for this address."));
|
|
169
|
+
}
|
|
170
|
+
console.log();
|
|
171
|
+
} catch (error) {
|
|
172
|
+
exitWithError(
|
|
173
|
+
`Failed to read profile: ${error instanceof Error ? error.message : String(error)}`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function encodeTransaction(config, chainId) {
|
|
178
|
+
const calldata = encodeFunctionData({
|
|
179
|
+
abi: config.abi,
|
|
180
|
+
functionName: config.functionName,
|
|
181
|
+
args: config.args
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
to: config.to,
|
|
185
|
+
data: calldata,
|
|
186
|
+
chainId,
|
|
187
|
+
value: config.value?.toString() ?? "0"
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/commands/profile/set-picture.ts
|
|
192
|
+
async function executeProfileSetPicture(options) {
|
|
193
|
+
if (!isValidUrl(options.url)) {
|
|
194
|
+
exitWithError(
|
|
195
|
+
`Invalid URL: "${options.url}". Please provide a valid URL (e.g., https://example.com/image.jpg)`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const storageArgs = getProfilePictureStorageArgs(options.url);
|
|
199
|
+
if (options.encodeOnly) {
|
|
200
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
201
|
+
chainId: options.chainId,
|
|
202
|
+
rpcUrl: options.rpcUrl
|
|
203
|
+
});
|
|
204
|
+
const encoded = encodeTransaction(
|
|
205
|
+
{
|
|
206
|
+
to: STORAGE_CONTRACT.address,
|
|
207
|
+
abi: STORAGE_CONTRACT.abi,
|
|
208
|
+
functionName: "put",
|
|
209
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
210
|
+
},
|
|
211
|
+
readOnlyOptions.chainId
|
|
212
|
+
);
|
|
213
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const commonOptions = parseCommonOptions(
|
|
217
|
+
{
|
|
218
|
+
privateKey: options.privateKey,
|
|
219
|
+
chainId: options.chainId,
|
|
220
|
+
rpcUrl: options.rpcUrl
|
|
221
|
+
},
|
|
222
|
+
true
|
|
223
|
+
// supports --encode-only
|
|
224
|
+
);
|
|
225
|
+
try {
|
|
226
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
227
|
+
const rpcUrls = getChainRpcUrls({
|
|
228
|
+
chainId: commonOptions.chainId,
|
|
229
|
+
rpcUrl: commonOptions.rpcUrl
|
|
230
|
+
});
|
|
231
|
+
const client = createWalletClient({
|
|
232
|
+
account,
|
|
233
|
+
chain: base,
|
|
234
|
+
// TODO: Support other chains
|
|
235
|
+
transport: http(rpcUrls[0])
|
|
236
|
+
}).extend(publicActions);
|
|
237
|
+
console.log(chalk3.blue(`\u{1F4F7} Setting profile picture...`));
|
|
238
|
+
console.log(chalk3.gray(` URL: ${options.url}`));
|
|
239
|
+
console.log(chalk3.gray(` Address: ${account.address}`));
|
|
240
|
+
const hash = await client.writeContract({
|
|
241
|
+
address: STORAGE_CONTRACT.address,
|
|
242
|
+
abi: STORAGE_CONTRACT.abi,
|
|
243
|
+
functionName: "put",
|
|
244
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
245
|
+
});
|
|
246
|
+
console.log(chalk3.blue(`\u23F3 Waiting for confirmation...`));
|
|
247
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
248
|
+
if (receipt.status === "success") {
|
|
249
|
+
console.log(
|
|
250
|
+
chalk3.green(
|
|
251
|
+
`
|
|
252
|
+
\u2713 Profile picture updated successfully!
|
|
253
|
+
Transaction: ${hash}
|
|
254
|
+
URL: ${options.url}`
|
|
255
|
+
)
|
|
256
|
+
);
|
|
257
|
+
} else {
|
|
258
|
+
exitWithError(`Transaction failed: ${hash}`);
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
exitWithError(
|
|
262
|
+
`Failed to set profile picture: ${error instanceof Error ? error.message : String(error)}`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async function readExistingMetadata(address, client) {
|
|
267
|
+
try {
|
|
268
|
+
const metadataResult = await client.readStorageData({
|
|
269
|
+
key: PROFILE_METADATA_STORAGE_KEY,
|
|
270
|
+
operator: address
|
|
271
|
+
});
|
|
272
|
+
if (metadataResult.data) {
|
|
273
|
+
const metadata = parseProfileMetadata(metadataResult.data);
|
|
274
|
+
return {
|
|
275
|
+
x_username: metadata?.x_username,
|
|
276
|
+
bio: metadata?.bio,
|
|
277
|
+
display_name: metadata?.display_name,
|
|
278
|
+
token_address: metadata?.token_address
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
} catch (error) {
|
|
282
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
283
|
+
if (errorMessage !== "StoredDataNotFound") {
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return {};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/commands/profile/set-username.ts
|
|
291
|
+
async function executeProfileSetUsername(options) {
|
|
292
|
+
if (!isValidXUsername(options.username)) {
|
|
293
|
+
exitWithError(
|
|
294
|
+
`Invalid X username: "${options.username}". Usernames must be 1-15 characters, alphanumeric and underscores only.`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const usernameForStorage = options.username.startsWith("@") ? options.username.slice(1) : options.username;
|
|
298
|
+
const displayUsername = `@${usernameForStorage}`;
|
|
299
|
+
if (options.encodeOnly) {
|
|
300
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
301
|
+
chainId: options.chainId,
|
|
302
|
+
rpcUrl: options.rpcUrl
|
|
303
|
+
});
|
|
304
|
+
const storageArgs = getProfileMetadataStorageArgs({
|
|
305
|
+
x_username: usernameForStorage
|
|
306
|
+
});
|
|
307
|
+
const encoded = encodeTransaction(
|
|
308
|
+
{
|
|
309
|
+
to: STORAGE_CONTRACT.address,
|
|
310
|
+
abi: STORAGE_CONTRACT.abi,
|
|
311
|
+
functionName: "put",
|
|
312
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
313
|
+
},
|
|
314
|
+
readOnlyOptions.chainId
|
|
315
|
+
);
|
|
316
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const commonOptions = parseCommonOptions(
|
|
320
|
+
{
|
|
321
|
+
privateKey: options.privateKey,
|
|
322
|
+
chainId: options.chainId,
|
|
323
|
+
rpcUrl: options.rpcUrl
|
|
324
|
+
},
|
|
325
|
+
true
|
|
326
|
+
// supports --encode-only
|
|
327
|
+
);
|
|
328
|
+
try {
|
|
329
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
330
|
+
const rpcUrls = getChainRpcUrls({
|
|
331
|
+
chainId: commonOptions.chainId,
|
|
332
|
+
rpcUrl: commonOptions.rpcUrl
|
|
333
|
+
});
|
|
334
|
+
const client = createWalletClient({
|
|
335
|
+
account,
|
|
336
|
+
chain: base,
|
|
337
|
+
// TODO: Support other chains
|
|
338
|
+
transport: http(rpcUrls[0])
|
|
339
|
+
}).extend(publicActions);
|
|
340
|
+
console.log(chalk3.blue(`Setting X username...`));
|
|
341
|
+
console.log(chalk3.gray(` Username: ${displayUsername}`));
|
|
342
|
+
console.log(chalk3.gray(` Address: ${account.address}`));
|
|
343
|
+
const storageClient = new StorageClient({
|
|
344
|
+
chainId: commonOptions.chainId,
|
|
345
|
+
overrides: commonOptions.rpcUrl ? { rpcUrls: [commonOptions.rpcUrl] } : void 0
|
|
346
|
+
});
|
|
347
|
+
const existing = await readExistingMetadata(
|
|
348
|
+
account.address,
|
|
349
|
+
storageClient
|
|
350
|
+
);
|
|
351
|
+
const storageArgs = getProfileMetadataStorageArgs({
|
|
352
|
+
x_username: usernameForStorage,
|
|
353
|
+
bio: existing.bio,
|
|
354
|
+
display_name: existing.display_name,
|
|
355
|
+
token_address: existing.token_address
|
|
356
|
+
});
|
|
357
|
+
const hash = await client.writeContract({
|
|
358
|
+
address: STORAGE_CONTRACT.address,
|
|
359
|
+
abi: STORAGE_CONTRACT.abi,
|
|
360
|
+
functionName: "put",
|
|
361
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
362
|
+
});
|
|
363
|
+
console.log(chalk3.blue(`Waiting for confirmation...`));
|
|
364
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
365
|
+
if (receipt.status === "success") {
|
|
366
|
+
console.log(
|
|
367
|
+
chalk3.green(
|
|
368
|
+
`
|
|
369
|
+
X username updated successfully!
|
|
370
|
+
Transaction: ${hash}
|
|
371
|
+
Username: ${displayUsername}`
|
|
372
|
+
)
|
|
373
|
+
);
|
|
374
|
+
} else {
|
|
375
|
+
exitWithError(`Transaction failed: ${hash}`);
|
|
376
|
+
}
|
|
377
|
+
} catch (error) {
|
|
378
|
+
exitWithError(
|
|
379
|
+
`Failed to set X username: ${error instanceof Error ? error.message : String(error)}`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
async function executeProfileSetBio(options) {
|
|
384
|
+
if (!isValidBio(options.bio)) {
|
|
385
|
+
exitWithError(
|
|
386
|
+
`Invalid bio: "${options.bio}". Bio must be 1-280 characters and cannot contain control characters.`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
if (options.encodeOnly) {
|
|
390
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
391
|
+
chainId: options.chainId,
|
|
392
|
+
rpcUrl: options.rpcUrl
|
|
393
|
+
});
|
|
394
|
+
const storageArgs = getProfileMetadataStorageArgs({ bio: options.bio });
|
|
395
|
+
const encoded = encodeTransaction(
|
|
396
|
+
{
|
|
397
|
+
to: STORAGE_CONTRACT.address,
|
|
398
|
+
abi: STORAGE_CONTRACT.abi,
|
|
399
|
+
functionName: "put",
|
|
400
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
401
|
+
},
|
|
402
|
+
readOnlyOptions.chainId
|
|
403
|
+
);
|
|
404
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const commonOptions = parseCommonOptions(
|
|
408
|
+
{
|
|
409
|
+
privateKey: options.privateKey,
|
|
410
|
+
chainId: options.chainId,
|
|
411
|
+
rpcUrl: options.rpcUrl
|
|
412
|
+
},
|
|
413
|
+
true
|
|
414
|
+
// supports --encode-only
|
|
415
|
+
);
|
|
416
|
+
try {
|
|
417
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
418
|
+
const rpcUrls = getChainRpcUrls({
|
|
419
|
+
chainId: commonOptions.chainId,
|
|
420
|
+
rpcUrl: commonOptions.rpcUrl
|
|
421
|
+
});
|
|
422
|
+
const client = createWalletClient({
|
|
423
|
+
account,
|
|
424
|
+
chain: base,
|
|
425
|
+
// TODO: Support other chains
|
|
426
|
+
transport: http(rpcUrls[0])
|
|
427
|
+
}).extend(publicActions);
|
|
428
|
+
console.log(chalk3.blue(`Setting profile bio...`));
|
|
429
|
+
console.log(chalk3.gray(` Bio: ${options.bio}`));
|
|
430
|
+
console.log(chalk3.gray(` Address: ${account.address}`));
|
|
431
|
+
const storageClient = new StorageClient({
|
|
432
|
+
chainId: commonOptions.chainId,
|
|
433
|
+
overrides: commonOptions.rpcUrl ? { rpcUrls: [commonOptions.rpcUrl] } : void 0
|
|
434
|
+
});
|
|
435
|
+
const existing = await readExistingMetadata(
|
|
436
|
+
account.address,
|
|
437
|
+
storageClient
|
|
438
|
+
);
|
|
439
|
+
const storageArgs = getProfileMetadataStorageArgs({
|
|
440
|
+
bio: options.bio,
|
|
441
|
+
x_username: existing.x_username,
|
|
442
|
+
display_name: existing.display_name,
|
|
443
|
+
token_address: existing.token_address
|
|
444
|
+
});
|
|
445
|
+
const hash = await client.writeContract({
|
|
446
|
+
address: STORAGE_CONTRACT.address,
|
|
447
|
+
abi: STORAGE_CONTRACT.abi,
|
|
448
|
+
functionName: "put",
|
|
449
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
450
|
+
});
|
|
451
|
+
console.log(chalk3.blue(`Waiting for confirmation...`));
|
|
452
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
453
|
+
if (receipt.status === "success") {
|
|
454
|
+
console.log(
|
|
455
|
+
chalk3.green(
|
|
456
|
+
`
|
|
457
|
+
Bio updated successfully!
|
|
458
|
+
Transaction: ${hash}
|
|
459
|
+
Bio: ${options.bio}`
|
|
460
|
+
)
|
|
461
|
+
);
|
|
462
|
+
} else {
|
|
463
|
+
exitWithError(`Transaction failed: ${hash}`);
|
|
464
|
+
}
|
|
465
|
+
} catch (error) {
|
|
466
|
+
exitWithError(
|
|
467
|
+
`Failed to set bio: ${error instanceof Error ? error.message : String(error)}`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function executeProfileSetTokenAddress(options) {
|
|
472
|
+
if (!isValidTokenAddress(options.tokenAddress)) {
|
|
473
|
+
exitWithError(
|
|
474
|
+
`Invalid token address: "${options.tokenAddress}". Must be a valid EVM address (0x-prefixed, 40 hex characters).`
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
const normalizedAddress = options.tokenAddress.toLowerCase();
|
|
478
|
+
if (options.encodeOnly) {
|
|
479
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
480
|
+
chainId: options.chainId,
|
|
481
|
+
rpcUrl: options.rpcUrl
|
|
482
|
+
});
|
|
483
|
+
const storageArgs = getProfileMetadataStorageArgs({
|
|
484
|
+
token_address: normalizedAddress
|
|
485
|
+
});
|
|
486
|
+
const encoded = encodeTransaction(
|
|
487
|
+
{
|
|
488
|
+
to: STORAGE_CONTRACT.address,
|
|
489
|
+
abi: STORAGE_CONTRACT.abi,
|
|
490
|
+
functionName: "put",
|
|
491
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
492
|
+
},
|
|
493
|
+
readOnlyOptions.chainId
|
|
494
|
+
);
|
|
495
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const commonOptions = parseCommonOptions(
|
|
499
|
+
{
|
|
500
|
+
privateKey: options.privateKey,
|
|
501
|
+
chainId: options.chainId,
|
|
502
|
+
rpcUrl: options.rpcUrl
|
|
503
|
+
},
|
|
504
|
+
true
|
|
505
|
+
// supports --encode-only
|
|
506
|
+
);
|
|
507
|
+
try {
|
|
508
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
509
|
+
const rpcUrls = getChainRpcUrls({
|
|
510
|
+
chainId: commonOptions.chainId,
|
|
511
|
+
rpcUrl: commonOptions.rpcUrl
|
|
512
|
+
});
|
|
513
|
+
const client = createWalletClient({
|
|
514
|
+
account,
|
|
515
|
+
chain: base,
|
|
516
|
+
// TODO: Support other chains
|
|
517
|
+
transport: http(rpcUrls[0])
|
|
518
|
+
}).extend(publicActions);
|
|
519
|
+
console.log(chalk3.blue(`Setting profile token address...`));
|
|
520
|
+
console.log(chalk3.gray(` Token Address: ${normalizedAddress}`));
|
|
521
|
+
console.log(chalk3.gray(` Address: ${account.address}`));
|
|
522
|
+
const storageClient = new StorageClient({
|
|
523
|
+
chainId: commonOptions.chainId,
|
|
524
|
+
overrides: commonOptions.rpcUrl ? { rpcUrls: [commonOptions.rpcUrl] } : void 0
|
|
525
|
+
});
|
|
526
|
+
const existing = await readExistingMetadata(
|
|
527
|
+
account.address,
|
|
528
|
+
storageClient
|
|
529
|
+
);
|
|
530
|
+
const storageArgs = getProfileMetadataStorageArgs({
|
|
531
|
+
token_address: normalizedAddress,
|
|
532
|
+
x_username: existing.x_username,
|
|
533
|
+
bio: existing.bio,
|
|
534
|
+
display_name: existing.display_name
|
|
535
|
+
});
|
|
536
|
+
const hash = await client.writeContract({
|
|
537
|
+
address: STORAGE_CONTRACT.address,
|
|
538
|
+
abi: STORAGE_CONTRACT.abi,
|
|
539
|
+
functionName: "put",
|
|
540
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
541
|
+
});
|
|
542
|
+
console.log(chalk3.blue(`Waiting for confirmation...`));
|
|
543
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
544
|
+
if (receipt.status === "success") {
|
|
545
|
+
console.log(
|
|
546
|
+
chalk3.green(
|
|
547
|
+
`
|
|
548
|
+
Token address updated successfully!
|
|
549
|
+
Transaction: ${hash}
|
|
550
|
+
Token Address: ${normalizedAddress}`
|
|
551
|
+
)
|
|
552
|
+
);
|
|
553
|
+
} else {
|
|
554
|
+
exitWithError(`Transaction failed: ${hash}`);
|
|
555
|
+
}
|
|
556
|
+
} catch (error) {
|
|
557
|
+
exitWithError(
|
|
558
|
+
`Failed to set token address: ${error instanceof Error ? error.message : String(error)}`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
var MAX_CANVAS_SIZE = 60 * 1024;
|
|
563
|
+
var CANVAS_FILENAME = "profile-compressed.html";
|
|
564
|
+
function isBinaryContent(buffer) {
|
|
565
|
+
const sampleSize = Math.min(buffer.length, 8192);
|
|
566
|
+
for (let i = 0; i < sampleSize; i++) {
|
|
567
|
+
const byte = buffer[i];
|
|
568
|
+
if (byte === 0) return true;
|
|
569
|
+
if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) return true;
|
|
570
|
+
}
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
function getMimeType(filePath) {
|
|
574
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
575
|
+
const mimeTypes = {
|
|
576
|
+
".png": "image/png",
|
|
577
|
+
".jpg": "image/jpeg",
|
|
578
|
+
".jpeg": "image/jpeg",
|
|
579
|
+
".gif": "image/gif",
|
|
580
|
+
".webp": "image/webp",
|
|
581
|
+
".svg": "image/svg+xml",
|
|
582
|
+
".pdf": "application/pdf",
|
|
583
|
+
".html": "text/html",
|
|
584
|
+
".htm": "text/html",
|
|
585
|
+
".css": "text/css",
|
|
586
|
+
".js": "application/javascript",
|
|
587
|
+
".json": "application/json",
|
|
588
|
+
".txt": "text/plain"
|
|
589
|
+
};
|
|
590
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
591
|
+
}
|
|
592
|
+
function bufferToDataUri(buffer, mimeType) {
|
|
593
|
+
const base64 = buffer.toString("base64");
|
|
594
|
+
return `data:${mimeType};base64,${base64}`;
|
|
595
|
+
}
|
|
596
|
+
async function executeProfileSetCanvas(options) {
|
|
597
|
+
if (!options.file && !options.content) {
|
|
598
|
+
exitWithError(
|
|
599
|
+
"Must provide either --file or --content to set canvas content."
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
if (options.file && options.content) {
|
|
603
|
+
exitWithError("Cannot provide both --file and --content. Choose one.");
|
|
604
|
+
}
|
|
605
|
+
let canvasContent;
|
|
606
|
+
if (options.file) {
|
|
607
|
+
const filePath = path.resolve(options.file);
|
|
608
|
+
if (!fs.existsSync(filePath)) {
|
|
609
|
+
exitWithError(`File not found: ${filePath}`);
|
|
610
|
+
}
|
|
611
|
+
const buffer = fs.readFileSync(filePath);
|
|
612
|
+
if (buffer.length > MAX_CANVAS_SIZE) {
|
|
613
|
+
exitWithError(
|
|
614
|
+
`File too large: ${buffer.length} bytes exceeds maximum of ${MAX_CANVAS_SIZE} bytes (60KB).`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
if (isBinaryContent(buffer)) {
|
|
618
|
+
const mimeType = getMimeType(filePath);
|
|
619
|
+
canvasContent = bufferToDataUri(buffer, mimeType);
|
|
620
|
+
} else {
|
|
621
|
+
canvasContent = buffer.toString("utf-8");
|
|
622
|
+
}
|
|
623
|
+
} else {
|
|
624
|
+
canvasContent = options.content;
|
|
625
|
+
const contentSize = Buffer.byteLength(canvasContent, "utf-8");
|
|
626
|
+
if (contentSize > MAX_CANVAS_SIZE) {
|
|
627
|
+
exitWithError(
|
|
628
|
+
`Content too large: ${contentSize} bytes exceeds maximum of ${MAX_CANVAS_SIZE} bytes (60KB).`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
const bytesKey = toBytes32(PROFILE_CANVAS_STORAGE_KEY);
|
|
633
|
+
const chunks = chunkDataForStorage(canvasContent);
|
|
634
|
+
if (options.encodeOnly) {
|
|
635
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
636
|
+
chainId: options.chainId,
|
|
637
|
+
rpcUrl: options.rpcUrl
|
|
638
|
+
});
|
|
639
|
+
const encoded = encodeTransaction(
|
|
640
|
+
{
|
|
641
|
+
to: CHUNKED_STORAGE_CONTRACT.address,
|
|
642
|
+
abi: CHUNKED_STORAGE_CONTRACT.abi,
|
|
643
|
+
functionName: "put",
|
|
644
|
+
args: [bytesKey, CANVAS_FILENAME, chunks]
|
|
645
|
+
},
|
|
646
|
+
readOnlyOptions.chainId
|
|
647
|
+
);
|
|
648
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const commonOptions = parseCommonOptions(
|
|
652
|
+
{
|
|
653
|
+
privateKey: options.privateKey,
|
|
654
|
+
chainId: options.chainId,
|
|
655
|
+
rpcUrl: options.rpcUrl
|
|
656
|
+
},
|
|
657
|
+
true
|
|
658
|
+
// supports --encode-only
|
|
659
|
+
);
|
|
660
|
+
try {
|
|
661
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
662
|
+
const rpcUrls = getChainRpcUrls({
|
|
663
|
+
chainId: commonOptions.chainId,
|
|
664
|
+
rpcUrl: commonOptions.rpcUrl
|
|
665
|
+
});
|
|
666
|
+
const client = createWalletClient({
|
|
667
|
+
account,
|
|
668
|
+
chain: base,
|
|
669
|
+
// TODO: Support other chains
|
|
670
|
+
transport: http(rpcUrls[0])
|
|
671
|
+
}).extend(publicActions);
|
|
672
|
+
console.log(chalk3.blue(`Setting profile canvas...`));
|
|
673
|
+
console.log(
|
|
674
|
+
chalk3.gray(` Content size: ${Buffer.byteLength(canvasContent)} bytes`)
|
|
675
|
+
);
|
|
676
|
+
console.log(chalk3.gray(` Chunks: ${chunks.length}`));
|
|
677
|
+
console.log(chalk3.gray(` Address: ${account.address}`));
|
|
678
|
+
const hash = await client.writeContract({
|
|
679
|
+
address: CHUNKED_STORAGE_CONTRACT.address,
|
|
680
|
+
abi: CHUNKED_STORAGE_CONTRACT.abi,
|
|
681
|
+
functionName: "put",
|
|
682
|
+
args: [bytesKey, CANVAS_FILENAME, chunks]
|
|
683
|
+
});
|
|
684
|
+
console.log(chalk3.blue(`Waiting for confirmation...`));
|
|
685
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
686
|
+
if (receipt.status === "success") {
|
|
687
|
+
console.log(
|
|
688
|
+
chalk3.green(
|
|
689
|
+
`
|
|
690
|
+
Canvas updated successfully!
|
|
691
|
+
Transaction: ${hash}
|
|
692
|
+
Content size: ${Buffer.byteLength(canvasContent)} bytes
|
|
693
|
+
Chunks: ${chunks.length}`
|
|
694
|
+
)
|
|
695
|
+
);
|
|
696
|
+
} else {
|
|
697
|
+
exitWithError(`Transaction failed: ${hash}`);
|
|
698
|
+
}
|
|
699
|
+
} catch (error) {
|
|
700
|
+
exitWithError(
|
|
701
|
+
`Failed to set canvas: ${error instanceof Error ? error.message : String(error)}`
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
function isDataUri(content) {
|
|
706
|
+
return content.startsWith("data:");
|
|
707
|
+
}
|
|
708
|
+
function parseDataUri(dataUri) {
|
|
709
|
+
const match = dataUri.match(/^data:([^;]+);base64,(.+)$/);
|
|
710
|
+
if (!match) {
|
|
711
|
+
throw new Error("Invalid data URI format");
|
|
712
|
+
}
|
|
713
|
+
const mimeType = match[1];
|
|
714
|
+
const base64Data = match[2];
|
|
715
|
+
const buffer = Buffer.from(base64Data, "base64");
|
|
716
|
+
return { buffer, mimeType };
|
|
717
|
+
}
|
|
718
|
+
function getExtensionFromMimeType(mimeType) {
|
|
719
|
+
const extensions = {
|
|
720
|
+
"image/png": ".png",
|
|
721
|
+
"image/jpeg": ".jpg",
|
|
722
|
+
"image/gif": ".gif",
|
|
723
|
+
"image/webp": ".webp",
|
|
724
|
+
"image/svg+xml": ".svg",
|
|
725
|
+
"application/pdf": ".pdf",
|
|
726
|
+
"text/html": ".html",
|
|
727
|
+
"text/css": ".css",
|
|
728
|
+
"application/javascript": ".js",
|
|
729
|
+
"application/json": ".json",
|
|
730
|
+
"text/plain": ".txt",
|
|
731
|
+
"application/octet-stream": ".bin"
|
|
732
|
+
};
|
|
733
|
+
return extensions[mimeType] || ".bin";
|
|
734
|
+
}
|
|
735
|
+
async function executeProfileGetCanvas(options) {
|
|
736
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
737
|
+
chainId: options.chainId,
|
|
738
|
+
rpcUrl: options.rpcUrl
|
|
739
|
+
});
|
|
740
|
+
const client = new StorageClient({
|
|
741
|
+
chainId: readOnlyOptions.chainId,
|
|
742
|
+
overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
|
|
743
|
+
});
|
|
744
|
+
try {
|
|
745
|
+
let canvasContent;
|
|
746
|
+
let canvasText;
|
|
747
|
+
try {
|
|
748
|
+
const result = await client.readChunkedStorage({
|
|
749
|
+
key: PROFILE_CANVAS_STORAGE_KEY,
|
|
750
|
+
operator: options.address
|
|
751
|
+
});
|
|
752
|
+
if (result.data) {
|
|
753
|
+
canvasContent = result.data;
|
|
754
|
+
canvasText = result.text;
|
|
755
|
+
}
|
|
756
|
+
} catch (error) {
|
|
757
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
758
|
+
if (errorMessage !== "ChunkedStorage metadata not found" && !errorMessage.includes("not found")) {
|
|
759
|
+
throw error;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
if (options.json) {
|
|
763
|
+
const output = {
|
|
764
|
+
address: options.address,
|
|
765
|
+
chainId: readOnlyOptions.chainId,
|
|
766
|
+
canvas: canvasContent || null,
|
|
767
|
+
filename: canvasText || null,
|
|
768
|
+
hasCanvas: !!canvasContent,
|
|
769
|
+
isDataUri: canvasContent ? isDataUri(canvasContent) : false,
|
|
770
|
+
contentLength: canvasContent ? canvasContent.length : 0
|
|
771
|
+
};
|
|
772
|
+
console.log(JSON.stringify(output, null, 2));
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (!canvasContent) {
|
|
776
|
+
exitWithError(`No canvas found for address: ${options.address}`);
|
|
777
|
+
}
|
|
778
|
+
if (options.output) {
|
|
779
|
+
const outputPath = path.resolve(options.output);
|
|
780
|
+
if (isDataUri(canvasContent)) {
|
|
781
|
+
const { buffer, mimeType } = parseDataUri(canvasContent);
|
|
782
|
+
let finalPath = outputPath;
|
|
783
|
+
if (!path.extname(outputPath)) {
|
|
784
|
+
finalPath = outputPath + getExtensionFromMimeType(mimeType);
|
|
785
|
+
}
|
|
786
|
+
fs.writeFileSync(finalPath, buffer);
|
|
787
|
+
console.log(
|
|
788
|
+
chalk3.green(`Canvas written to: ${finalPath} (${buffer.length} bytes)`)
|
|
789
|
+
);
|
|
790
|
+
} else {
|
|
791
|
+
fs.writeFileSync(outputPath, canvasContent, "utf-8");
|
|
792
|
+
console.log(
|
|
793
|
+
chalk3.green(
|
|
794
|
+
`Canvas written to: ${outputPath} (${canvasContent.length} bytes)`
|
|
795
|
+
)
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
console.log(canvasContent);
|
|
801
|
+
} catch (error) {
|
|
802
|
+
exitWithError(
|
|
803
|
+
`Failed to read canvas: ${error instanceof Error ? error.message : String(error)}`
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/commands/profile/index.ts
|
|
809
|
+
function registerProfileCommand(program) {
|
|
810
|
+
const profileCommand = program.command("profile").description("User profile operations");
|
|
811
|
+
const getCommand = new Command("get").description("Get profile data for an address").requiredOption("--address <address>", "Wallet address to get profile for").option(
|
|
812
|
+
"--chain-id <id>",
|
|
813
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
814
|
+
(value) => parseInt(value, 10)
|
|
815
|
+
).option(
|
|
816
|
+
"--rpc-url <url>",
|
|
817
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
818
|
+
).option("--json", "Output in JSON format").action(async (options) => {
|
|
819
|
+
await executeProfileGet({
|
|
820
|
+
address: options.address,
|
|
821
|
+
chainId: options.chainId,
|
|
822
|
+
rpcUrl: options.rpcUrl,
|
|
823
|
+
json: options.json
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
const setPictureCommand = new Command("set-picture").description("Set your profile picture URL").requiredOption("--url <url>", "Image URL for profile picture").option(
|
|
827
|
+
"--private-key <key>",
|
|
828
|
+
"Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
|
|
829
|
+
).option(
|
|
830
|
+
"--chain-id <id>",
|
|
831
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
832
|
+
(value) => parseInt(value, 10)
|
|
833
|
+
).option(
|
|
834
|
+
"--rpc-url <url>",
|
|
835
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
836
|
+
).option(
|
|
837
|
+
"--encode-only",
|
|
838
|
+
"Output transaction data as JSON instead of executing"
|
|
839
|
+
).action(async (options) => {
|
|
840
|
+
await executeProfileSetPicture({
|
|
841
|
+
url: options.url,
|
|
842
|
+
privateKey: options.privateKey,
|
|
843
|
+
chainId: options.chainId,
|
|
844
|
+
rpcUrl: options.rpcUrl,
|
|
845
|
+
encodeOnly: options.encodeOnly
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
const setUsernameCommand = new Command("set-x-username").description("Set your X (Twitter) username for your profile").requiredOption(
|
|
849
|
+
"--username <username>",
|
|
850
|
+
"Your X (Twitter) username (with or without @)"
|
|
851
|
+
).option(
|
|
852
|
+
"--private-key <key>",
|
|
853
|
+
"Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
|
|
854
|
+
).option(
|
|
855
|
+
"--chain-id <id>",
|
|
856
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
857
|
+
(value) => parseInt(value, 10)
|
|
858
|
+
).option(
|
|
859
|
+
"--rpc-url <url>",
|
|
860
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
861
|
+
).option(
|
|
862
|
+
"--encode-only",
|
|
863
|
+
"Output transaction data as JSON instead of executing"
|
|
864
|
+
).action(async (options) => {
|
|
865
|
+
await executeProfileSetUsername({
|
|
866
|
+
username: options.username,
|
|
867
|
+
privateKey: options.privateKey,
|
|
868
|
+
chainId: options.chainId,
|
|
869
|
+
rpcUrl: options.rpcUrl,
|
|
870
|
+
encodeOnly: options.encodeOnly
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
const setBioCommand = new Command("set-bio").description("Set your profile bio").requiredOption("--bio <bio>", "Your profile bio (max 280 characters)").option(
|
|
874
|
+
"--private-key <key>",
|
|
875
|
+
"Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
|
|
876
|
+
).option(
|
|
877
|
+
"--chain-id <id>",
|
|
878
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
879
|
+
(value) => parseInt(value, 10)
|
|
880
|
+
).option(
|
|
881
|
+
"--rpc-url <url>",
|
|
882
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
883
|
+
).option(
|
|
884
|
+
"--encode-only",
|
|
885
|
+
"Output transaction data as JSON instead of executing"
|
|
886
|
+
).action(async (options) => {
|
|
887
|
+
await executeProfileSetBio({
|
|
888
|
+
bio: options.bio,
|
|
889
|
+
privateKey: options.privateKey,
|
|
890
|
+
chainId: options.chainId,
|
|
891
|
+
rpcUrl: options.rpcUrl,
|
|
892
|
+
encodeOnly: options.encodeOnly
|
|
893
|
+
});
|
|
894
|
+
});
|
|
895
|
+
const setTokenAddressCommand = new Command("set-token-address").description("Set your profile token address (ERC-20 token that represents you)").requiredOption(
|
|
896
|
+
"--token-address <address>",
|
|
897
|
+
"ERC-20 token contract address (0x-prefixed)"
|
|
898
|
+
).option(
|
|
899
|
+
"--private-key <key>",
|
|
900
|
+
"Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
|
|
901
|
+
).option(
|
|
902
|
+
"--chain-id <id>",
|
|
903
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
904
|
+
(value) => parseInt(value, 10)
|
|
905
|
+
).option(
|
|
906
|
+
"--rpc-url <url>",
|
|
907
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
908
|
+
).option(
|
|
909
|
+
"--encode-only",
|
|
910
|
+
"Output transaction data as JSON instead of executing"
|
|
911
|
+
).action(async (options) => {
|
|
912
|
+
await executeProfileSetTokenAddress({
|
|
913
|
+
tokenAddress: options.tokenAddress,
|
|
914
|
+
privateKey: options.privateKey,
|
|
915
|
+
chainId: options.chainId,
|
|
916
|
+
rpcUrl: options.rpcUrl,
|
|
917
|
+
encodeOnly: options.encodeOnly
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
const setCanvasCommand = new Command("set-canvas").description("Set your profile canvas (HTML content)").option("--file <path>", "Path to file containing canvas content").option("--content <html>", "HTML content for canvas (inline)").option(
|
|
921
|
+
"--private-key <key>",
|
|
922
|
+
"Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
|
|
923
|
+
).option(
|
|
924
|
+
"--chain-id <id>",
|
|
925
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
926
|
+
(value) => parseInt(value, 10)
|
|
927
|
+
).option(
|
|
928
|
+
"--rpc-url <url>",
|
|
929
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
930
|
+
).option(
|
|
931
|
+
"--encode-only",
|
|
932
|
+
"Output transaction data as JSON instead of executing"
|
|
933
|
+
).action(async (options) => {
|
|
934
|
+
await executeProfileSetCanvas({
|
|
935
|
+
file: options.file,
|
|
936
|
+
content: options.content,
|
|
937
|
+
privateKey: options.privateKey,
|
|
938
|
+
chainId: options.chainId,
|
|
939
|
+
rpcUrl: options.rpcUrl,
|
|
940
|
+
encodeOnly: options.encodeOnly
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
const getCanvasCommand = new Command("get-canvas").description("Get profile canvas for an address").requiredOption("--address <address>", "Wallet address to get canvas for").option("--output <path>", "Write canvas content to file instead of stdout").option(
|
|
944
|
+
"--chain-id <id>",
|
|
945
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
946
|
+
(value) => parseInt(value, 10)
|
|
947
|
+
).option(
|
|
948
|
+
"--rpc-url <url>",
|
|
949
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
950
|
+
).option("--json", "Output in JSON format").action(async (options) => {
|
|
951
|
+
await executeProfileGetCanvas({
|
|
952
|
+
address: options.address,
|
|
953
|
+
output: options.output,
|
|
954
|
+
chainId: options.chainId,
|
|
955
|
+
rpcUrl: options.rpcUrl,
|
|
956
|
+
json: options.json
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
profileCommand.addCommand(getCommand);
|
|
960
|
+
profileCommand.addCommand(setPictureCommand);
|
|
961
|
+
profileCommand.addCommand(setUsernameCommand);
|
|
962
|
+
profileCommand.addCommand(setBioCommand);
|
|
963
|
+
profileCommand.addCommand(setTokenAddressCommand);
|
|
964
|
+
profileCommand.addCommand(setCanvasCommand);
|
|
965
|
+
profileCommand.addCommand(getCanvasCommand);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
export { registerProfileCommand };
|
|
969
|
+
//# sourceMappingURL=index.mjs.map
|
|
970
|
+
//# sourceMappingURL=index.mjs.map
|