@net-protocol/cli 0.1.3 → 0.1.4
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 +1 -1
- package/dist/cli/index.mjs +291 -5
- package/dist/cli/index.mjs.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
package/dist/cli/index.mjs
CHANGED
|
@@ -5,12 +5,14 @@ import { createRequire } from 'module';
|
|
|
5
5
|
import chalk4 from 'chalk';
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import { readFileSync } from 'fs';
|
|
8
|
-
import { StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, STORAGE_CONTRACT, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
|
|
9
|
-
import { stringToHex, createWalletClient, http, hexToString, parseEther, encodeFunctionData, defineChain } from 'viem';
|
|
8
|
+
import { StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, STORAGE_CONTRACT as STORAGE_CONTRACT$1, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
|
|
9
|
+
import { stringToHex, createWalletClient, http, hexToString, parseEther, encodeFunctionData, publicActions, defineChain } from 'viem';
|
|
10
10
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
11
11
|
import { getNetContract, getChainName, getPublicClient, getChainRpcUrls, NetClient } from '@net-protocol/core';
|
|
12
12
|
import { createRelayX402Client, createRelaySession, checkBackendWalletBalance, fundBackendWallet, batchTransactions, submitTransactionsViaRelay, waitForConfirmations, retryFailedTransactions as retryFailedTransactions$1 } from '@net-protocol/relay';
|
|
13
13
|
import { isNetrSupportedChain, NetrClient } from '@net-protocol/netr';
|
|
14
|
+
import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getXUsernameStorageArgs } from '@net-protocol/profiles';
|
|
15
|
+
import { base } from 'viem/chains';
|
|
14
16
|
|
|
15
17
|
function getRequiredChainId(optionValue) {
|
|
16
18
|
const chainId = optionValue || (process.env.NET_CHAIN_ID ? parseInt(process.env.NET_CHAIN_ID, 10) : void 0);
|
|
@@ -297,7 +299,7 @@ async function filterExistingTransactions(params) {
|
|
|
297
299
|
async function filterXmlStorageTransactions(params) {
|
|
298
300
|
const { storageClient, transactions, operatorAddress } = params;
|
|
299
301
|
const metadataTx = transactions.find(
|
|
300
|
-
(tx) => tx.to.toLowerCase() === STORAGE_CONTRACT.address.toLowerCase()
|
|
302
|
+
(tx) => tx.to.toLowerCase() === STORAGE_CONTRACT$1.address.toLowerCase()
|
|
301
303
|
);
|
|
302
304
|
const chunkTxs = transactions.filter(
|
|
303
305
|
(tx) => tx.to.toLowerCase() === CHUNKED_STORAGE_CONTRACT.address.toLowerCase()
|
|
@@ -1468,8 +1470,8 @@ function registerStorageCommand(program2) {
|
|
|
1468
1470
|
console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
|
|
1469
1471
|
console.log(chalk4.blue(`\u{1F517} Using relay API: ${options.apiUrl}`));
|
|
1470
1472
|
const result = await uploadFileWithRelay(uploadRelayOptions);
|
|
1471
|
-
const { privateKeyToAccount:
|
|
1472
|
-
const userAccount =
|
|
1473
|
+
const { privateKeyToAccount: privateKeyToAccount8 } = await import('viem/accounts');
|
|
1474
|
+
const userAccount = privateKeyToAccount8(commonOptions.privateKey);
|
|
1473
1475
|
const storageUrl = generateStorageUrl(
|
|
1474
1476
|
userAccount.address,
|
|
1475
1477
|
commonOptions.chainId,
|
|
@@ -2216,6 +2218,289 @@ function registerTokenCommand(program2) {
|
|
|
2216
2218
|
tokenCommand.addCommand(deployCommand);
|
|
2217
2219
|
tokenCommand.addCommand(infoCommand);
|
|
2218
2220
|
}
|
|
2221
|
+
async function executeProfileGet(options) {
|
|
2222
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
2223
|
+
chainId: options.chainId,
|
|
2224
|
+
rpcUrl: options.rpcUrl
|
|
2225
|
+
});
|
|
2226
|
+
const client = new StorageClient({
|
|
2227
|
+
chainId: readOnlyOptions.chainId,
|
|
2228
|
+
overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
|
|
2229
|
+
});
|
|
2230
|
+
try {
|
|
2231
|
+
let profilePicture;
|
|
2232
|
+
try {
|
|
2233
|
+
const pictureResult = await client.readStorageData({
|
|
2234
|
+
key: PROFILE_PICTURE_STORAGE_KEY,
|
|
2235
|
+
operator: options.address
|
|
2236
|
+
});
|
|
2237
|
+
if (pictureResult.data) {
|
|
2238
|
+
profilePicture = pictureResult.data;
|
|
2239
|
+
}
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2242
|
+
if (errorMessage !== "StoredDataNotFound") {
|
|
2243
|
+
throw error;
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
let xUsername;
|
|
2247
|
+
try {
|
|
2248
|
+
const metadataResult = await client.readStorageData({
|
|
2249
|
+
key: PROFILE_METADATA_STORAGE_KEY,
|
|
2250
|
+
operator: options.address
|
|
2251
|
+
});
|
|
2252
|
+
if (metadataResult.data) {
|
|
2253
|
+
const metadata = parseProfileMetadata(metadataResult.data);
|
|
2254
|
+
xUsername = metadata?.x_username;
|
|
2255
|
+
}
|
|
2256
|
+
} catch (error) {
|
|
2257
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2258
|
+
if (errorMessage !== "StoredDataNotFound") {
|
|
2259
|
+
throw error;
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
const hasProfile = profilePicture || xUsername;
|
|
2263
|
+
if (options.json) {
|
|
2264
|
+
const output = {
|
|
2265
|
+
address: options.address,
|
|
2266
|
+
chainId: readOnlyOptions.chainId,
|
|
2267
|
+
profilePicture: profilePicture || null,
|
|
2268
|
+
xUsername: xUsername || null,
|
|
2269
|
+
hasProfile
|
|
2270
|
+
};
|
|
2271
|
+
console.log(JSON.stringify(output, null, 2));
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
console.log(chalk4.white.bold("\nProfile:\n"));
|
|
2275
|
+
console.log(` ${chalk4.cyan("Address:")} ${options.address}`);
|
|
2276
|
+
console.log(` ${chalk4.cyan("Chain ID:")} ${readOnlyOptions.chainId}`);
|
|
2277
|
+
console.log(
|
|
2278
|
+
` ${chalk4.cyan("Profile Picture:")} ${profilePicture || chalk4.gray("(not set)")}`
|
|
2279
|
+
);
|
|
2280
|
+
console.log(
|
|
2281
|
+
` ${chalk4.cyan("X Username:")} ${xUsername ? `@${xUsername}` : chalk4.gray("(not set)")}`
|
|
2282
|
+
);
|
|
2283
|
+
if (!hasProfile) {
|
|
2284
|
+
console.log(chalk4.yellow("\n No profile data found for this address."));
|
|
2285
|
+
}
|
|
2286
|
+
console.log();
|
|
2287
|
+
} catch (error) {
|
|
2288
|
+
exitWithError(
|
|
2289
|
+
`Failed to read profile: ${error instanceof Error ? error.message : String(error)}`
|
|
2290
|
+
);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
async function executeProfileSetPicture(options) {
|
|
2294
|
+
if (!isValidUrl(options.url)) {
|
|
2295
|
+
exitWithError(
|
|
2296
|
+
`Invalid URL: "${options.url}". Please provide a valid URL (e.g., https://example.com/image.jpg)`
|
|
2297
|
+
);
|
|
2298
|
+
}
|
|
2299
|
+
const storageArgs = getProfilePictureStorageArgs(options.url);
|
|
2300
|
+
if (options.encodeOnly) {
|
|
2301
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
2302
|
+
chainId: options.chainId,
|
|
2303
|
+
rpcUrl: options.rpcUrl
|
|
2304
|
+
});
|
|
2305
|
+
const encoded = encodeTransaction(
|
|
2306
|
+
{
|
|
2307
|
+
to: STORAGE_CONTRACT.address,
|
|
2308
|
+
abi: STORAGE_CONTRACT.abi,
|
|
2309
|
+
functionName: "put",
|
|
2310
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
2311
|
+
},
|
|
2312
|
+
readOnlyOptions.chainId
|
|
2313
|
+
);
|
|
2314
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const commonOptions = parseCommonOptions({
|
|
2318
|
+
privateKey: options.privateKey,
|
|
2319
|
+
chainId: options.chainId,
|
|
2320
|
+
rpcUrl: options.rpcUrl
|
|
2321
|
+
});
|
|
2322
|
+
try {
|
|
2323
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
2324
|
+
const rpcUrls = getChainRpcUrls({
|
|
2325
|
+
chainId: commonOptions.chainId,
|
|
2326
|
+
rpcUrl: commonOptions.rpcUrl
|
|
2327
|
+
});
|
|
2328
|
+
const client = createWalletClient({
|
|
2329
|
+
account,
|
|
2330
|
+
chain: base,
|
|
2331
|
+
// TODO: Support other chains
|
|
2332
|
+
transport: http(rpcUrls[0])
|
|
2333
|
+
}).extend(publicActions);
|
|
2334
|
+
console.log(chalk4.blue(`\u{1F4F7} Setting profile picture...`));
|
|
2335
|
+
console.log(chalk4.gray(` URL: ${options.url}`));
|
|
2336
|
+
console.log(chalk4.gray(` Address: ${account.address}`));
|
|
2337
|
+
const hash = await client.writeContract({
|
|
2338
|
+
address: STORAGE_CONTRACT.address,
|
|
2339
|
+
abi: STORAGE_CONTRACT.abi,
|
|
2340
|
+
functionName: "put",
|
|
2341
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
2342
|
+
});
|
|
2343
|
+
console.log(chalk4.blue(`\u23F3 Waiting for confirmation...`));
|
|
2344
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
2345
|
+
if (receipt.status === "success") {
|
|
2346
|
+
console.log(
|
|
2347
|
+
chalk4.green(
|
|
2348
|
+
`
|
|
2349
|
+
\u2713 Profile picture updated successfully!
|
|
2350
|
+
Transaction: ${hash}
|
|
2351
|
+
URL: ${options.url}`
|
|
2352
|
+
)
|
|
2353
|
+
);
|
|
2354
|
+
} else {
|
|
2355
|
+
exitWithError(`Transaction failed: ${hash}`);
|
|
2356
|
+
}
|
|
2357
|
+
} catch (error) {
|
|
2358
|
+
exitWithError(
|
|
2359
|
+
`Failed to set profile picture: ${error instanceof Error ? error.message : String(error)}`
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
async function executeProfileSetUsername(options) {
|
|
2364
|
+
if (!isValidXUsername(options.username)) {
|
|
2365
|
+
exitWithError(
|
|
2366
|
+
`Invalid X username: "${options.username}". Usernames must be 1-15 characters, alphanumeric and underscores only.`
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
2369
|
+
const storageArgs = getXUsernameStorageArgs(options.username);
|
|
2370
|
+
const displayUsername = options.username.startsWith("@") ? options.username : `@${options.username}`;
|
|
2371
|
+
if (options.encodeOnly) {
|
|
2372
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
2373
|
+
chainId: options.chainId,
|
|
2374
|
+
rpcUrl: options.rpcUrl
|
|
2375
|
+
});
|
|
2376
|
+
const encoded = encodeTransaction(
|
|
2377
|
+
{
|
|
2378
|
+
to: STORAGE_CONTRACT.address,
|
|
2379
|
+
abi: STORAGE_CONTRACT.abi,
|
|
2380
|
+
functionName: "put",
|
|
2381
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
2382
|
+
},
|
|
2383
|
+
readOnlyOptions.chainId
|
|
2384
|
+
);
|
|
2385
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
const commonOptions = parseCommonOptions({
|
|
2389
|
+
privateKey: options.privateKey,
|
|
2390
|
+
chainId: options.chainId,
|
|
2391
|
+
rpcUrl: options.rpcUrl
|
|
2392
|
+
});
|
|
2393
|
+
try {
|
|
2394
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
2395
|
+
const rpcUrls = getChainRpcUrls({
|
|
2396
|
+
chainId: commonOptions.chainId,
|
|
2397
|
+
rpcUrl: commonOptions.rpcUrl
|
|
2398
|
+
});
|
|
2399
|
+
const client = createWalletClient({
|
|
2400
|
+
account,
|
|
2401
|
+
chain: base,
|
|
2402
|
+
// TODO: Support other chains
|
|
2403
|
+
transport: http(rpcUrls[0])
|
|
2404
|
+
}).extend(publicActions);
|
|
2405
|
+
console.log(chalk4.blue(`\u{1F426} Setting X username...`));
|
|
2406
|
+
console.log(chalk4.gray(` Username: ${displayUsername}`));
|
|
2407
|
+
console.log(chalk4.gray(` Address: ${account.address}`));
|
|
2408
|
+
const hash = await client.writeContract({
|
|
2409
|
+
address: STORAGE_CONTRACT.address,
|
|
2410
|
+
abi: STORAGE_CONTRACT.abi,
|
|
2411
|
+
functionName: "put",
|
|
2412
|
+
args: [storageArgs.bytesKey, storageArgs.topic, storageArgs.bytesValue]
|
|
2413
|
+
});
|
|
2414
|
+
console.log(chalk4.blue(`\u23F3 Waiting for confirmation...`));
|
|
2415
|
+
const receipt = await client.waitForTransactionReceipt({ hash });
|
|
2416
|
+
if (receipt.status === "success") {
|
|
2417
|
+
console.log(
|
|
2418
|
+
chalk4.green(
|
|
2419
|
+
`
|
|
2420
|
+
\u2713 X username updated successfully!
|
|
2421
|
+
Transaction: ${hash}
|
|
2422
|
+
Username: ${displayUsername}`
|
|
2423
|
+
)
|
|
2424
|
+
);
|
|
2425
|
+
} else {
|
|
2426
|
+
exitWithError(`Transaction failed: ${hash}`);
|
|
2427
|
+
}
|
|
2428
|
+
} catch (error) {
|
|
2429
|
+
exitWithError(
|
|
2430
|
+
`Failed to set X username: ${error instanceof Error ? error.message : String(error)}`
|
|
2431
|
+
);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
// src/commands/profile/index.ts
|
|
2436
|
+
function registerProfileCommand(program2) {
|
|
2437
|
+
const profileCommand = program2.command("profile").description("User profile operations");
|
|
2438
|
+
const getCommand = new Command("get").description("Get profile data for an address").requiredOption("--address <address>", "Wallet address to get profile for").option(
|
|
2439
|
+
"--chain-id <id>",
|
|
2440
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
2441
|
+
(value) => parseInt(value, 10)
|
|
2442
|
+
).option(
|
|
2443
|
+
"--rpc-url <url>",
|
|
2444
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
2445
|
+
).option("--json", "Output in JSON format").action(async (options) => {
|
|
2446
|
+
await executeProfileGet({
|
|
2447
|
+
address: options.address,
|
|
2448
|
+
chainId: options.chainId,
|
|
2449
|
+
rpcUrl: options.rpcUrl,
|
|
2450
|
+
json: options.json
|
|
2451
|
+
});
|
|
2452
|
+
});
|
|
2453
|
+
const setPictureCommand = new Command("set-picture").description("Set your profile picture URL").requiredOption("--url <url>", "Image URL for profile picture").option(
|
|
2454
|
+
"--private-key <key>",
|
|
2455
|
+
"Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
|
|
2456
|
+
).option(
|
|
2457
|
+
"--chain-id <id>",
|
|
2458
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
2459
|
+
(value) => parseInt(value, 10)
|
|
2460
|
+
).option(
|
|
2461
|
+
"--rpc-url <url>",
|
|
2462
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
2463
|
+
).option(
|
|
2464
|
+
"--encode-only",
|
|
2465
|
+
"Output transaction data as JSON instead of executing"
|
|
2466
|
+
).action(async (options) => {
|
|
2467
|
+
await executeProfileSetPicture({
|
|
2468
|
+
url: options.url,
|
|
2469
|
+
privateKey: options.privateKey,
|
|
2470
|
+
chainId: options.chainId,
|
|
2471
|
+
rpcUrl: options.rpcUrl,
|
|
2472
|
+
encodeOnly: options.encodeOnly
|
|
2473
|
+
});
|
|
2474
|
+
});
|
|
2475
|
+
const setUsernameCommand = new Command("set-x-username").description("Set your X (Twitter) username for your profile").requiredOption(
|
|
2476
|
+
"--username <username>",
|
|
2477
|
+
"Your X (Twitter) username (with or without @)"
|
|
2478
|
+
).option(
|
|
2479
|
+
"--private-key <key>",
|
|
2480
|
+
"Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
|
|
2481
|
+
).option(
|
|
2482
|
+
"--chain-id <id>",
|
|
2483
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
2484
|
+
(value) => parseInt(value, 10)
|
|
2485
|
+
).option(
|
|
2486
|
+
"--rpc-url <url>",
|
|
2487
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
2488
|
+
).option(
|
|
2489
|
+
"--encode-only",
|
|
2490
|
+
"Output transaction data as JSON instead of executing"
|
|
2491
|
+
).action(async (options) => {
|
|
2492
|
+
await executeProfileSetUsername({
|
|
2493
|
+
username: options.username,
|
|
2494
|
+
privateKey: options.privateKey,
|
|
2495
|
+
chainId: options.chainId,
|
|
2496
|
+
rpcUrl: options.rpcUrl,
|
|
2497
|
+
encodeOnly: options.encodeOnly
|
|
2498
|
+
});
|
|
2499
|
+
});
|
|
2500
|
+
profileCommand.addCommand(getCommand);
|
|
2501
|
+
profileCommand.addCommand(setPictureCommand);
|
|
2502
|
+
profileCommand.addCommand(setUsernameCommand);
|
|
2503
|
+
}
|
|
2219
2504
|
|
|
2220
2505
|
// src/cli/index.ts
|
|
2221
2506
|
var require2 = createRequire(import.meta.url);
|
|
@@ -2227,6 +2512,7 @@ registerMessageCommand(program);
|
|
|
2227
2512
|
registerChainsCommand(program);
|
|
2228
2513
|
registerInfoCommand(program);
|
|
2229
2514
|
registerTokenCommand(program);
|
|
2515
|
+
registerProfileCommand(program);
|
|
2230
2516
|
program.parse();
|
|
2231
2517
|
//# sourceMappingURL=index.mjs.map
|
|
2232
2518
|
//# sourceMappingURL=index.mjs.map
|