@net-protocol/cli 0.1.0 → 0.1.1
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 +242 -0
- package/dist/cli/index.mjs +991 -53
- package/dist/cli/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import 'dotenv/config';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import
|
|
4
|
+
import chalk4 from 'chalk';
|
|
5
|
+
import * as fs from 'fs';
|
|
5
6
|
import { readFileSync } from 'fs';
|
|
6
|
-
import { detectFileTypeFromBase64, base64ToDataUri,
|
|
7
|
-
import { stringToHex, createWalletClient, http, hexToString, defineChain } from 'viem';
|
|
7
|
+
import { StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, STORAGE_CONTRACT, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
|
|
8
|
+
import { stringToHex, createWalletClient, http, hexToString, parseEther, encodeFunctionData, defineChain } from 'viem';
|
|
8
9
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
9
|
-
import { getPublicClient, getChainRpcUrls } from '@net-protocol/core';
|
|
10
|
+
import { getNetContract, getChainName, getPublicClient, getChainRpcUrls, NetClient } from '@net-protocol/core';
|
|
10
11
|
import { createRelayX402Client, createRelaySession, checkBackendWalletBalance, fundBackendWallet, batchTransactions, submitTransactionsViaRelay, waitForConfirmations, retryFailedTransactions as retryFailedTransactions$1 } from '@net-protocol/relay';
|
|
12
|
+
import { isNetrSupportedChain, NetrClient } from '@net-protocol/netr';
|
|
11
13
|
|
|
14
|
+
function getRequiredChainId(optionValue) {
|
|
15
|
+
const chainId = optionValue || (process.env.NET_CHAIN_ID ? parseInt(process.env.NET_CHAIN_ID, 10) : void 0);
|
|
16
|
+
if (!chainId) {
|
|
17
|
+
console.error(
|
|
18
|
+
chalk4.red(
|
|
19
|
+
"Error: Chain ID is required. Provide via --chain-id flag or NET_CHAIN_ID environment variable"
|
|
20
|
+
)
|
|
21
|
+
);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
return chainId;
|
|
25
|
+
}
|
|
26
|
+
function getRpcUrl(optionValue) {
|
|
27
|
+
return optionValue || process.env.NET_RPC_URL;
|
|
28
|
+
}
|
|
12
29
|
function parseCommonOptions(options) {
|
|
13
30
|
const privateKey = options.privateKey || process.env.NET_PRIVATE_KEY || process.env.PRIVATE_KEY;
|
|
14
31
|
if (!privateKey) {
|
|
15
32
|
console.error(
|
|
16
|
-
|
|
33
|
+
chalk4.red(
|
|
17
34
|
"Error: Private key is required. Provide via --private-key flag or NET_PRIVATE_KEY/PRIVATE_KEY environment variable"
|
|
18
35
|
)
|
|
19
36
|
);
|
|
@@ -21,7 +38,7 @@ function parseCommonOptions(options) {
|
|
|
21
38
|
}
|
|
22
39
|
if (!privateKey.startsWith("0x") || privateKey.length !== 66) {
|
|
23
40
|
console.error(
|
|
24
|
-
|
|
41
|
+
chalk4.red(
|
|
25
42
|
"Error: Invalid private key format (must be 0x-prefixed, 66 characters)"
|
|
26
43
|
)
|
|
27
44
|
);
|
|
@@ -29,25 +46,21 @@ function parseCommonOptions(options) {
|
|
|
29
46
|
}
|
|
30
47
|
if (options.privateKey) {
|
|
31
48
|
console.warn(
|
|
32
|
-
|
|
33
|
-
"
|
|
49
|
+
chalk4.yellow(
|
|
50
|
+
"Warning: Private key provided via command line. Consider using NET_PRIVATE_KEY environment variable instead."
|
|
34
51
|
)
|
|
35
52
|
);
|
|
36
53
|
}
|
|
37
|
-
const chainId = options.chainId || (process.env.NET_CHAIN_ID ? parseInt(process.env.NET_CHAIN_ID, 10) : void 0);
|
|
38
|
-
if (!chainId) {
|
|
39
|
-
console.error(
|
|
40
|
-
chalk2.red(
|
|
41
|
-
"Error: Chain ID is required. Provide via --chain-id flag or NET_CHAIN_ID environment variable"
|
|
42
|
-
)
|
|
43
|
-
);
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
const rpcUrl = options.rpcUrl || process.env.NET_RPC_URL;
|
|
47
54
|
return {
|
|
48
55
|
privateKey,
|
|
49
|
-
chainId,
|
|
50
|
-
rpcUrl
|
|
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)
|
|
51
64
|
};
|
|
52
65
|
}
|
|
53
66
|
async function checkNormalStorageExists(params) {
|
|
@@ -1019,6 +1032,202 @@ async function previewFile(options) {
|
|
|
1019
1032
|
};
|
|
1020
1033
|
}
|
|
1021
1034
|
}
|
|
1035
|
+
function formatMessage(message, index) {
|
|
1036
|
+
const timestamp = new Date(Number(message.timestamp) * 1e3).toISOString();
|
|
1037
|
+
const lines = [
|
|
1038
|
+
chalk4.cyan(`[${index}]`) + ` ${chalk4.gray(timestamp)}`,
|
|
1039
|
+
` ${chalk4.white("Sender:")} ${message.sender}`,
|
|
1040
|
+
` ${chalk4.white("App:")} ${message.app}`
|
|
1041
|
+
];
|
|
1042
|
+
if (message.topic) {
|
|
1043
|
+
lines.push(` ${chalk4.white("Topic:")} ${message.topic}`);
|
|
1044
|
+
}
|
|
1045
|
+
lines.push(` ${chalk4.white("Text:")} ${message.text}`);
|
|
1046
|
+
if (message.data && message.data !== "0x") {
|
|
1047
|
+
lines.push(` ${chalk4.white("Data:")} ${message.data}`);
|
|
1048
|
+
}
|
|
1049
|
+
return lines.join("\n");
|
|
1050
|
+
}
|
|
1051
|
+
function messageToJson(message, index) {
|
|
1052
|
+
return {
|
|
1053
|
+
index,
|
|
1054
|
+
sender: message.sender,
|
|
1055
|
+
app: message.app,
|
|
1056
|
+
timestamp: Number(message.timestamp),
|
|
1057
|
+
text: message.text,
|
|
1058
|
+
topic: message.topic,
|
|
1059
|
+
data: message.data
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function printMessages(messages, startIndex, json) {
|
|
1063
|
+
if (json) {
|
|
1064
|
+
const output = messages.map((msg, i) => messageToJson(msg, startIndex + i));
|
|
1065
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1066
|
+
} else {
|
|
1067
|
+
if (messages.length === 0) {
|
|
1068
|
+
console.log(chalk4.yellow("No messages found"));
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
messages.forEach((msg, i) => {
|
|
1072
|
+
console.log(formatMessage(msg, startIndex + i));
|
|
1073
|
+
if (i < messages.length - 1) {
|
|
1074
|
+
console.log();
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function printCount(count, label, json) {
|
|
1080
|
+
if (json) {
|
|
1081
|
+
console.log(JSON.stringify({ count }, null, 2));
|
|
1082
|
+
} else {
|
|
1083
|
+
console.log(`${chalk4.white(label)} ${chalk4.cyan(count)}`);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
function exitWithError(message) {
|
|
1087
|
+
console.error(chalk4.red(`Error: ${message}`));
|
|
1088
|
+
process.exit(1);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// src/commands/storage/core/read.ts
|
|
1092
|
+
async function executeStorageRead(options) {
|
|
1093
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
1094
|
+
chainId: options.chainId,
|
|
1095
|
+
rpcUrl: options.rpcUrl
|
|
1096
|
+
});
|
|
1097
|
+
const client = new StorageClient({
|
|
1098
|
+
chainId: readOnlyOptions.chainId,
|
|
1099
|
+
overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
|
|
1100
|
+
});
|
|
1101
|
+
try {
|
|
1102
|
+
const result = await client.readStorageData({
|
|
1103
|
+
key: options.key,
|
|
1104
|
+
operator: options.operator,
|
|
1105
|
+
index: options.index
|
|
1106
|
+
});
|
|
1107
|
+
if (options.json) {
|
|
1108
|
+
const output = {
|
|
1109
|
+
key: options.key,
|
|
1110
|
+
operator: options.operator,
|
|
1111
|
+
chainId: readOnlyOptions.chainId,
|
|
1112
|
+
text: result.text,
|
|
1113
|
+
data: options.raw ? result.data : result.data,
|
|
1114
|
+
isXml: result.isXml,
|
|
1115
|
+
...options.index !== void 0 && { index: options.index }
|
|
1116
|
+
};
|
|
1117
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
console.log(chalk4.white.bold("\nStorage Value:\n"));
|
|
1121
|
+
console.log(` ${chalk4.cyan("Key:")} ${options.key}`);
|
|
1122
|
+
console.log(` ${chalk4.cyan("Operator:")} ${options.operator}`);
|
|
1123
|
+
console.log(` ${chalk4.cyan("Chain ID:")} ${readOnlyOptions.chainId}`);
|
|
1124
|
+
if (options.index !== void 0) {
|
|
1125
|
+
console.log(` ${chalk4.cyan("Index:")} ${options.index}`);
|
|
1126
|
+
}
|
|
1127
|
+
console.log(` ${chalk4.cyan("Text:")} ${result.text || "(empty)"}`);
|
|
1128
|
+
console.log(` ${chalk4.cyan("Is XML:")} ${result.isXml ? "Yes" : "No"}`);
|
|
1129
|
+
const dataPreview = result.data.length > 500 ? result.data.substring(0, 500) + "... (truncated)" : result.data;
|
|
1130
|
+
console.log(` ${chalk4.cyan("Data:")}`);
|
|
1131
|
+
if (result.data) {
|
|
1132
|
+
console.log(chalk4.gray(` ${dataPreview.split("\n").join("\n ")}`));
|
|
1133
|
+
} else {
|
|
1134
|
+
console.log(chalk4.gray(" (empty)"));
|
|
1135
|
+
}
|
|
1136
|
+
console.log();
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1139
|
+
if (errorMessage === "StoredDataNotFound") {
|
|
1140
|
+
if (options.json) {
|
|
1141
|
+
console.log(
|
|
1142
|
+
JSON.stringify(
|
|
1143
|
+
{
|
|
1144
|
+
key: options.key,
|
|
1145
|
+
operator: options.operator,
|
|
1146
|
+
chainId: readOnlyOptions.chainId,
|
|
1147
|
+
error: "Not found"
|
|
1148
|
+
},
|
|
1149
|
+
null,
|
|
1150
|
+
2
|
|
1151
|
+
)
|
|
1152
|
+
);
|
|
1153
|
+
process.exit(1);
|
|
1154
|
+
}
|
|
1155
|
+
exitWithError(
|
|
1156
|
+
`No data found for key "${options.key}" and operator "${options.operator}"`
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
exitWithError(`Failed to read storage: ${errorMessage}`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
function encodeTransaction(config, chainId) {
|
|
1163
|
+
const calldata = encodeFunctionData({
|
|
1164
|
+
abi: config.abi,
|
|
1165
|
+
functionName: config.functionName,
|
|
1166
|
+
args: config.args
|
|
1167
|
+
});
|
|
1168
|
+
return {
|
|
1169
|
+
to: config.to,
|
|
1170
|
+
data: calldata,
|
|
1171
|
+
chainId,
|
|
1172
|
+
value: config.value?.toString() ?? "0"
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// src/commands/storage/core/encode.ts
|
|
1177
|
+
var XML_STORAGE_THRESHOLD = 20 * 1024;
|
|
1178
|
+
async function encodeStorageUpload(options) {
|
|
1179
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
1180
|
+
chainId: options.chainId,
|
|
1181
|
+
rpcUrl: options.rpcUrl
|
|
1182
|
+
});
|
|
1183
|
+
let operatorAddress;
|
|
1184
|
+
if (options.privateKey) {
|
|
1185
|
+
const account = privateKeyToAccount(options.privateKey);
|
|
1186
|
+
operatorAddress = account.address;
|
|
1187
|
+
} else {
|
|
1188
|
+
operatorAddress = "0x0000000000000000000000000000000000000000";
|
|
1189
|
+
}
|
|
1190
|
+
const fileContent = fs.readFileSync(options.filePath, "utf-8");
|
|
1191
|
+
const fileSize = Buffer.byteLength(fileContent, "utf-8");
|
|
1192
|
+
const client = new StorageClient({
|
|
1193
|
+
chainId: readOnlyOptions.chainId
|
|
1194
|
+
});
|
|
1195
|
+
const useXmlStorage = fileSize > XML_STORAGE_THRESHOLD;
|
|
1196
|
+
if (useXmlStorage) {
|
|
1197
|
+
const { transactionConfigs, topLevelHash, metadata } = client.prepareXmlStorage({
|
|
1198
|
+
data: fileContent,
|
|
1199
|
+
operatorAddress,
|
|
1200
|
+
storageKey: options.storageKey,
|
|
1201
|
+
filename: options.text
|
|
1202
|
+
});
|
|
1203
|
+
const encodedTransactions = transactionConfigs.map(
|
|
1204
|
+
(config) => encodeTransaction(config, readOnlyOptions.chainId)
|
|
1205
|
+
);
|
|
1206
|
+
return {
|
|
1207
|
+
storageKey: options.storageKey,
|
|
1208
|
+
storageType: "xml",
|
|
1209
|
+
operatorAddress,
|
|
1210
|
+
transactions: encodedTransactions,
|
|
1211
|
+
topLevelHash
|
|
1212
|
+
};
|
|
1213
|
+
} else {
|
|
1214
|
+
const config = client.preparePut({
|
|
1215
|
+
key: options.storageKey,
|
|
1216
|
+
text: options.text,
|
|
1217
|
+
value: fileContent
|
|
1218
|
+
});
|
|
1219
|
+
const encodedTransaction = encodeTransaction(
|
|
1220
|
+
config,
|
|
1221
|
+
readOnlyOptions.chainId
|
|
1222
|
+
);
|
|
1223
|
+
return {
|
|
1224
|
+
storageKey: options.storageKey,
|
|
1225
|
+
storageType: "normal",
|
|
1226
|
+
operatorAddress,
|
|
1227
|
+
transactions: [encodedTransaction]
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1022
1231
|
|
|
1023
1232
|
// src/commands/storage/index.ts
|
|
1024
1233
|
function registerStorageCommand(program2) {
|
|
@@ -1033,7 +1242,32 @@ function registerStorageCommand(program2) {
|
|
|
1033
1242
|
).option(
|
|
1034
1243
|
"--rpc-url <url>",
|
|
1035
1244
|
"Custom RPC URL (can also be set via NET_RPC_URL env var)"
|
|
1245
|
+
).option(
|
|
1246
|
+
"--encode-only",
|
|
1247
|
+
"Output transaction data as JSON instead of executing"
|
|
1036
1248
|
).action(async (options) => {
|
|
1249
|
+
if (options.encodeOnly) {
|
|
1250
|
+
try {
|
|
1251
|
+
const result = await encodeStorageUpload({
|
|
1252
|
+
filePath: options.file,
|
|
1253
|
+
storageKey: options.key,
|
|
1254
|
+
text: options.text,
|
|
1255
|
+
privateKey: options.privateKey,
|
|
1256
|
+
chainId: options.chainId,
|
|
1257
|
+
rpcUrl: options.rpcUrl
|
|
1258
|
+
});
|
|
1259
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1260
|
+
process.exit(0);
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
console.error(
|
|
1263
|
+
chalk4.red(
|
|
1264
|
+
`Encode failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1265
|
+
)
|
|
1266
|
+
);
|
|
1267
|
+
process.exit(1);
|
|
1268
|
+
}
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1037
1271
|
const commonOptions = parseCommonOptions({
|
|
1038
1272
|
privateKey: options.privateKey,
|
|
1039
1273
|
chainId: options.chainId,
|
|
@@ -1048,7 +1282,7 @@ function registerStorageCommand(program2) {
|
|
|
1048
1282
|
rpcUrl: commonOptions.rpcUrl
|
|
1049
1283
|
};
|
|
1050
1284
|
try {
|
|
1051
|
-
console.log(
|
|
1285
|
+
console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
|
|
1052
1286
|
const result = await uploadFile(uploadOptions);
|
|
1053
1287
|
const storageUrl = generateStorageUrl(
|
|
1054
1288
|
result.operatorAddress,
|
|
@@ -1057,31 +1291,31 @@ function registerStorageCommand(program2) {
|
|
|
1057
1291
|
);
|
|
1058
1292
|
if (result.skipped && result.transactionsSent === 0) {
|
|
1059
1293
|
console.log(
|
|
1060
|
-
|
|
1294
|
+
chalk4.green(
|
|
1061
1295
|
`\u2713 All data already stored - skipping upload
|
|
1062
1296
|
Storage Key: ${options.key}
|
|
1063
1297
|
Skipped: ${result.transactionsSkipped} transaction(s)${storageUrl ? `
|
|
1064
|
-
Storage URL: ${
|
|
1298
|
+
Storage URL: ${chalk4.cyan(storageUrl)}` : ""}`
|
|
1065
1299
|
)
|
|
1066
1300
|
);
|
|
1067
1301
|
process.exit(0);
|
|
1068
1302
|
}
|
|
1069
1303
|
if (result.success) {
|
|
1070
1304
|
console.log(
|
|
1071
|
-
|
|
1305
|
+
chalk4.green(
|
|
1072
1306
|
`\u2713 File uploaded successfully!
|
|
1073
1307
|
Storage Key: ${options.key}
|
|
1074
1308
|
Storage Type: ${result.storageType === "xml" ? "XML" : "Normal"}
|
|
1075
1309
|
Transactions Sent: ${result.transactionsSent}
|
|
1076
1310
|
Transactions Skipped: ${result.transactionsSkipped}
|
|
1077
1311
|
Final Transaction Hash: ${result.finalHash || "N/A"}${storageUrl ? `
|
|
1078
|
-
Storage URL: ${
|
|
1312
|
+
Storage URL: ${chalk4.cyan(storageUrl)}` : ""}`
|
|
1079
1313
|
)
|
|
1080
1314
|
);
|
|
1081
1315
|
process.exit(0);
|
|
1082
1316
|
} else {
|
|
1083
1317
|
console.error(
|
|
1084
|
-
|
|
1318
|
+
chalk4.red(
|
|
1085
1319
|
`\u2717 Upload completed with errors
|
|
1086
1320
|
Transactions Sent: ${result.transactionsSent}
|
|
1087
1321
|
Transactions Skipped: ${result.transactionsSkipped}
|
|
@@ -1093,7 +1327,7 @@ function registerStorageCommand(program2) {
|
|
|
1093
1327
|
}
|
|
1094
1328
|
} catch (error) {
|
|
1095
1329
|
console.error(
|
|
1096
|
-
|
|
1330
|
+
chalk4.red(
|
|
1097
1331
|
`Upload failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1098
1332
|
)
|
|
1099
1333
|
);
|
|
@@ -1125,54 +1359,54 @@ function registerStorageCommand(program2) {
|
|
|
1125
1359
|
rpcUrl: commonOptions.rpcUrl
|
|
1126
1360
|
};
|
|
1127
1361
|
try {
|
|
1128
|
-
console.log(
|
|
1362
|
+
console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
|
|
1129
1363
|
const result = await previewFile(previewOptions);
|
|
1130
1364
|
const storageUrl = generateStorageUrl(
|
|
1131
1365
|
result.operatorAddress,
|
|
1132
1366
|
commonOptions.chainId,
|
|
1133
1367
|
options.key
|
|
1134
1368
|
);
|
|
1135
|
-
console.log(
|
|
1136
|
-
console.log(` Storage Key: ${
|
|
1369
|
+
console.log(chalk4.cyan("\n\u{1F4CA} Storage Preview:"));
|
|
1370
|
+
console.log(` Storage Key: ${chalk4.white(result.storageKey)}`);
|
|
1137
1371
|
console.log(
|
|
1138
|
-
` Storage Type: ${
|
|
1372
|
+
` Storage Type: ${chalk4.white(
|
|
1139
1373
|
result.storageType === "xml" ? "XML" : "Normal"
|
|
1140
1374
|
)}`
|
|
1141
1375
|
);
|
|
1142
|
-
console.log(` Total Chunks: ${
|
|
1376
|
+
console.log(` Total Chunks: ${chalk4.white(result.totalChunks)}`);
|
|
1143
1377
|
console.log(
|
|
1144
|
-
` Already Stored: ${
|
|
1378
|
+
` Already Stored: ${chalk4.green(result.alreadyStoredChunks)}`
|
|
1145
1379
|
);
|
|
1146
1380
|
console.log(
|
|
1147
|
-
` Need to Store: ${
|
|
1381
|
+
` Need to Store: ${chalk4.yellow(result.needToStoreChunks)}`
|
|
1148
1382
|
);
|
|
1149
1383
|
if (result.storageType === "xml" && result.metadataNeedsStorage) {
|
|
1150
|
-
console.log(` Metadata: ${
|
|
1384
|
+
console.log(` Metadata: ${chalk4.yellow("Needs Storage")}`);
|
|
1151
1385
|
} else if (result.storageType === "xml") {
|
|
1152
|
-
console.log(` Metadata: ${
|
|
1386
|
+
console.log(` Metadata: ${chalk4.green("Already Stored")}`);
|
|
1153
1387
|
}
|
|
1154
1388
|
console.log(
|
|
1155
|
-
` Total Transactions: ${
|
|
1389
|
+
` Total Transactions: ${chalk4.white(result.totalTransactions)}`
|
|
1156
1390
|
);
|
|
1157
1391
|
console.log(
|
|
1158
|
-
` Transactions to Send: ${
|
|
1392
|
+
` Transactions to Send: ${chalk4.yellow(result.transactionsToSend)}`
|
|
1159
1393
|
);
|
|
1160
1394
|
console.log(
|
|
1161
|
-
` Transactions Skipped: ${
|
|
1395
|
+
` Transactions Skipped: ${chalk4.green(result.transactionsSkipped)}`
|
|
1162
1396
|
);
|
|
1163
1397
|
console.log(
|
|
1164
|
-
` Operator Address: ${
|
|
1398
|
+
` Operator Address: ${chalk4.gray(result.operatorAddress)}`
|
|
1165
1399
|
);
|
|
1166
1400
|
if (storageUrl) {
|
|
1167
|
-
console.log(` Storage URL: ${
|
|
1401
|
+
console.log(` Storage URL: ${chalk4.cyan(storageUrl)}`);
|
|
1168
1402
|
}
|
|
1169
1403
|
if (result.needToStoreChunks === 0 && !result.metadataNeedsStorage) {
|
|
1170
1404
|
console.log(
|
|
1171
|
-
|
|
1405
|
+
chalk4.green("\n\u2713 All data is already stored - no upload needed")
|
|
1172
1406
|
);
|
|
1173
1407
|
} else {
|
|
1174
1408
|
console.log(
|
|
1175
|
-
|
|
1409
|
+
chalk4.yellow(
|
|
1176
1410
|
`
|
|
1177
1411
|
\u26A0 ${result.transactionsToSend} transaction(s) would be sent`
|
|
1178
1412
|
)
|
|
@@ -1181,7 +1415,7 @@ function registerStorageCommand(program2) {
|
|
|
1181
1415
|
process.exit(0);
|
|
1182
1416
|
} catch (error) {
|
|
1183
1417
|
console.error(
|
|
1184
|
-
|
|
1418
|
+
chalk4.red(
|
|
1185
1419
|
`Preview failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1186
1420
|
)
|
|
1187
1421
|
);
|
|
@@ -1213,7 +1447,7 @@ function registerStorageCommand(program2) {
|
|
|
1213
1447
|
const secretKey = options.secretKey || process.env.X402_SECRET_KEY;
|
|
1214
1448
|
if (!secretKey) {
|
|
1215
1449
|
console.error(
|
|
1216
|
-
|
|
1450
|
+
chalk4.red(
|
|
1217
1451
|
"Error: --secret-key is required or set X402_SECRET_KEY environment variable"
|
|
1218
1452
|
)
|
|
1219
1453
|
);
|
|
@@ -1230,11 +1464,11 @@ function registerStorageCommand(program2) {
|
|
|
1230
1464
|
secretKey
|
|
1231
1465
|
};
|
|
1232
1466
|
try {
|
|
1233
|
-
console.log(
|
|
1234
|
-
console.log(
|
|
1467
|
+
console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
|
|
1468
|
+
console.log(chalk4.blue(`\u{1F517} Using relay API: ${options.apiUrl}`));
|
|
1235
1469
|
const result = await uploadFileWithRelay(uploadRelayOptions);
|
|
1236
|
-
const { privateKeyToAccount:
|
|
1237
|
-
const userAccount =
|
|
1470
|
+
const { privateKeyToAccount: privateKeyToAccount6 } = await import('viem/accounts');
|
|
1471
|
+
const userAccount = privateKeyToAccount6(commonOptions.privateKey);
|
|
1238
1472
|
const storageUrl = generateStorageUrl(
|
|
1239
1473
|
userAccount.address,
|
|
1240
1474
|
commonOptions.chainId,
|
|
@@ -1242,7 +1476,7 @@ function registerStorageCommand(program2) {
|
|
|
1242
1476
|
);
|
|
1243
1477
|
if (result.success) {
|
|
1244
1478
|
console.log(
|
|
1245
|
-
|
|
1479
|
+
chalk4.green(
|
|
1246
1480
|
`\u2713 File uploaded successfully via relay!
|
|
1247
1481
|
Storage Key: ${options.key}
|
|
1248
1482
|
Top-Level Hash: ${result.topLevelHash}
|
|
@@ -1252,13 +1486,13 @@ function registerStorageCommand(program2) {
|
|
|
1252
1486
|
Backend Wallet: ${result.backendWalletAddress}
|
|
1253
1487
|
Chunk Transaction Hashes: ${result.chunkTransactionHashes.length > 0 ? result.chunkTransactionHashes.join(", ") : "None"}${result.metadataTransactionHash ? `
|
|
1254
1488
|
Metadata Transaction Hash: ${result.metadataTransactionHash}` : ""}${storageUrl ? `
|
|
1255
|
-
Storage URL: ${
|
|
1489
|
+
Storage URL: ${chalk4.cyan(storageUrl)}` : ""}`
|
|
1256
1490
|
)
|
|
1257
1491
|
);
|
|
1258
1492
|
process.exit(0);
|
|
1259
1493
|
} else {
|
|
1260
1494
|
console.error(
|
|
1261
|
-
|
|
1495
|
+
chalk4.red(
|
|
1262
1496
|
`\u2717 Upload completed with errors
|
|
1263
1497
|
Chunks Sent: ${result.chunksSent}
|
|
1264
1498
|
Chunks Skipped: ${result.chunksSkipped}
|
|
@@ -1270,22 +1504,726 @@ function registerStorageCommand(program2) {
|
|
|
1270
1504
|
}
|
|
1271
1505
|
} catch (error) {
|
|
1272
1506
|
console.error(
|
|
1273
|
-
|
|
1507
|
+
chalk4.red(
|
|
1274
1508
|
`Upload via relay failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1275
1509
|
)
|
|
1276
1510
|
);
|
|
1277
1511
|
process.exit(1);
|
|
1278
1512
|
}
|
|
1279
1513
|
});
|
|
1514
|
+
const readCommand = new Command("read").description("Read data from Net Storage").requiredOption("--key <key>", "Storage key to read").requiredOption("--operator <address>", "Operator address (wallet that stored the data)").option(
|
|
1515
|
+
"--chain-id <id>",
|
|
1516
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
1517
|
+
(value) => parseInt(value, 10)
|
|
1518
|
+
).option(
|
|
1519
|
+
"--rpc-url <url>",
|
|
1520
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
1521
|
+
).option(
|
|
1522
|
+
"--index <n>",
|
|
1523
|
+
"Historical version index (0 = oldest). Omit for latest.",
|
|
1524
|
+
(value) => parseInt(value, 10)
|
|
1525
|
+
).option("--json", "Output in JSON format").option("--raw", "Output raw data without truncation (use with --json)").action(async (options) => {
|
|
1526
|
+
await executeStorageRead({
|
|
1527
|
+
key: options.key,
|
|
1528
|
+
operator: options.operator,
|
|
1529
|
+
chainId: options.chainId,
|
|
1530
|
+
rpcUrl: options.rpcUrl,
|
|
1531
|
+
index: options.index,
|
|
1532
|
+
json: options.json,
|
|
1533
|
+
raw: options.raw
|
|
1534
|
+
});
|
|
1535
|
+
});
|
|
1280
1536
|
storageCommand.addCommand(uploadCommand);
|
|
1281
1537
|
storageCommand.addCommand(previewCommand);
|
|
1282
1538
|
storageCommand.addCommand(uploadRelayCommand);
|
|
1539
|
+
storageCommand.addCommand(readCommand);
|
|
1540
|
+
}
|
|
1541
|
+
function createNetClient(options) {
|
|
1542
|
+
return new NetClient({
|
|
1543
|
+
chainId: options.chainId,
|
|
1544
|
+
overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/commands/message/send.ts
|
|
1549
|
+
function prepareMessageConfig(client, options) {
|
|
1550
|
+
return client.prepareSendMessage({
|
|
1551
|
+
text: options.text,
|
|
1552
|
+
topic: options.topic ?? "",
|
|
1553
|
+
data: options.data
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
async function executeSend(options) {
|
|
1557
|
+
if (options.encodeOnly) {
|
|
1558
|
+
executeEncodeOnly(options);
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
const commonOptions = parseCommonOptions({
|
|
1562
|
+
privateKey: options.privateKey,
|
|
1563
|
+
chainId: options.chainId,
|
|
1564
|
+
rpcUrl: options.rpcUrl
|
|
1565
|
+
});
|
|
1566
|
+
const client = createNetClient(commonOptions);
|
|
1567
|
+
const txConfig = prepareMessageConfig(client, options);
|
|
1568
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
1569
|
+
const rpcUrls = getChainRpcUrls({
|
|
1570
|
+
chainId: commonOptions.chainId,
|
|
1571
|
+
rpcUrl: commonOptions.rpcUrl
|
|
1572
|
+
});
|
|
1573
|
+
const walletClient = createWalletClient({
|
|
1574
|
+
account,
|
|
1575
|
+
transport: http(rpcUrls[0])
|
|
1576
|
+
});
|
|
1577
|
+
console.log(chalk4.blue("Sending message..."));
|
|
1578
|
+
try {
|
|
1579
|
+
const hash = await walletClient.writeContract({
|
|
1580
|
+
address: txConfig.to,
|
|
1581
|
+
abi: txConfig.abi,
|
|
1582
|
+
functionName: txConfig.functionName,
|
|
1583
|
+
args: txConfig.args,
|
|
1584
|
+
chain: null
|
|
1585
|
+
});
|
|
1586
|
+
const topicLine = options.topic ? `
|
|
1587
|
+
Topic: ${options.topic}` : "";
|
|
1588
|
+
console.log(
|
|
1589
|
+
chalk4.green(
|
|
1590
|
+
`Message sent successfully!
|
|
1591
|
+
Transaction: ${hash}
|
|
1592
|
+
Text: ${options.text}${topicLine}`
|
|
1593
|
+
)
|
|
1594
|
+
);
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
exitWithError(
|
|
1597
|
+
`Failed to send message: ${error instanceof Error ? error.message : String(error)}`
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
function executeEncodeOnly(options) {
|
|
1602
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
1603
|
+
chainId: options.chainId,
|
|
1604
|
+
rpcUrl: options.rpcUrl
|
|
1605
|
+
});
|
|
1606
|
+
const client = new NetClient({ chainId: readOnlyOptions.chainId });
|
|
1607
|
+
const txConfig = prepareMessageConfig(client, options);
|
|
1608
|
+
const encoded = encodeTransaction(txConfig, readOnlyOptions.chainId);
|
|
1609
|
+
console.log(JSON.stringify(encoded, null, 2));
|
|
1610
|
+
}
|
|
1611
|
+
function buildFilter(options) {
|
|
1612
|
+
if (!options.app) {
|
|
1613
|
+
return void 0;
|
|
1614
|
+
}
|
|
1615
|
+
return {
|
|
1616
|
+
appAddress: options.app,
|
|
1617
|
+
topic: options.topic,
|
|
1618
|
+
maker: options.sender
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function warnIgnoredFilters(options) {
|
|
1622
|
+
if (options.app || !options.topic && !options.sender) {
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
const ignored = [];
|
|
1626
|
+
if (options.topic) ignored.push("--topic");
|
|
1627
|
+
if (options.sender) ignored.push("--sender");
|
|
1628
|
+
console.warn(
|
|
1629
|
+
chalk4.yellow(
|
|
1630
|
+
`Warning: ${ignored.join(" and ")} ignored because --app is required for filtering`
|
|
1631
|
+
)
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
// src/commands/message/read.ts
|
|
1636
|
+
function printEmptyResult(message, json) {
|
|
1637
|
+
if (json) {
|
|
1638
|
+
console.log(JSON.stringify([], null, 2));
|
|
1639
|
+
} else {
|
|
1640
|
+
console.log(chalk4.yellow(message));
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
async function executeRead(options) {
|
|
1644
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
1645
|
+
chainId: options.chainId,
|
|
1646
|
+
rpcUrl: options.rpcUrl
|
|
1647
|
+
});
|
|
1648
|
+
const client = createNetClient(readOnlyOptions);
|
|
1649
|
+
const filter = buildFilter(options);
|
|
1650
|
+
const json = options.json ?? false;
|
|
1651
|
+
warnIgnoredFilters(options);
|
|
1652
|
+
try {
|
|
1653
|
+
if (options.index !== void 0) {
|
|
1654
|
+
const message = await client.getMessageAtIndex({
|
|
1655
|
+
messageIndex: options.index,
|
|
1656
|
+
appAddress: filter?.appAddress,
|
|
1657
|
+
topic: filter?.topic,
|
|
1658
|
+
maker: filter?.maker
|
|
1659
|
+
});
|
|
1660
|
+
if (!message) {
|
|
1661
|
+
exitWithError(`No message found at index ${options.index}`);
|
|
1662
|
+
}
|
|
1663
|
+
printMessages([message], options.index, json);
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
const count = await client.getMessageCount({ filter });
|
|
1667
|
+
if (count === 0) {
|
|
1668
|
+
printEmptyResult("No messages found", json);
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
const hasExplicitRange = options.start !== void 0 || options.end !== void 0;
|
|
1672
|
+
let startIndex;
|
|
1673
|
+
let endIndex;
|
|
1674
|
+
if (hasExplicitRange) {
|
|
1675
|
+
startIndex = options.start ?? 0;
|
|
1676
|
+
endIndex = options.end ?? count;
|
|
1677
|
+
} else {
|
|
1678
|
+
const limit = options.limit ?? 10;
|
|
1679
|
+
startIndex = Math.max(0, count - limit);
|
|
1680
|
+
endIndex = count;
|
|
1681
|
+
}
|
|
1682
|
+
startIndex = Math.max(0, startIndex);
|
|
1683
|
+
endIndex = Math.min(endIndex, count);
|
|
1684
|
+
if (startIndex >= endIndex) {
|
|
1685
|
+
printEmptyResult("No messages in specified range", json);
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
const messages = await client.getMessagesBatch({
|
|
1689
|
+
filter,
|
|
1690
|
+
startIndex,
|
|
1691
|
+
endIndex
|
|
1692
|
+
});
|
|
1693
|
+
printMessages(messages, startIndex, json);
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
exitWithError(
|
|
1696
|
+
`Failed to read messages: ${error instanceof Error ? error.message : String(error)}`
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// src/commands/message/count.ts
|
|
1702
|
+
function buildCountLabel(filter) {
|
|
1703
|
+
if (!filter) {
|
|
1704
|
+
return "Total messages:";
|
|
1705
|
+
}
|
|
1706
|
+
const parts = [];
|
|
1707
|
+
if (filter.appAddress) parts.push(`app=${filter.appAddress}`);
|
|
1708
|
+
if (filter.topic) parts.push(`topic="${filter.topic}"`);
|
|
1709
|
+
if (filter.maker) parts.push(`sender=${filter.maker}`);
|
|
1710
|
+
return `Messages (${parts.join(", ")}):`;
|
|
1711
|
+
}
|
|
1712
|
+
async function executeCount(options) {
|
|
1713
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
1714
|
+
chainId: options.chainId,
|
|
1715
|
+
rpcUrl: options.rpcUrl
|
|
1716
|
+
});
|
|
1717
|
+
const client = createNetClient(readOnlyOptions);
|
|
1718
|
+
const filter = buildFilter(options);
|
|
1719
|
+
warnIgnoredFilters(options);
|
|
1720
|
+
try {
|
|
1721
|
+
const count = await client.getMessageCount({ filter });
|
|
1722
|
+
printCount(count, buildCountLabel(filter), options.json ?? false);
|
|
1723
|
+
} catch (error) {
|
|
1724
|
+
exitWithError(
|
|
1725
|
+
`Failed to get message count: ${error instanceof Error ? error.message : String(error)}`
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// src/commands/message/index.ts
|
|
1731
|
+
function registerMessageCommand(program2) {
|
|
1732
|
+
const messageCommand = program2.command("message").description("Message operations");
|
|
1733
|
+
const sendCommand = new Command("send").description("Send a message to Net Protocol").requiredOption("--text <text>", "Message text").option("--topic <topic>", "Message topic", "").option("--data <hex>", "Additional hex data").option(
|
|
1734
|
+
"--private-key <key>",
|
|
1735
|
+
"Private key (0x-prefixed hex). Can also be set via NET_PRIVATE_KEY env var"
|
|
1736
|
+
).option(
|
|
1737
|
+
"--chain-id <id>",
|
|
1738
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
1739
|
+
(value) => parseInt(value, 10)
|
|
1740
|
+
).option(
|
|
1741
|
+
"--rpc-url <url>",
|
|
1742
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
1743
|
+
).option(
|
|
1744
|
+
"--encode-only",
|
|
1745
|
+
"Output transaction data as JSON instead of executing"
|
|
1746
|
+
).action(async (options) => {
|
|
1747
|
+
await executeSend({
|
|
1748
|
+
text: options.text,
|
|
1749
|
+
topic: options.topic,
|
|
1750
|
+
data: options.data,
|
|
1751
|
+
privateKey: options.privateKey,
|
|
1752
|
+
chainId: options.chainId,
|
|
1753
|
+
rpcUrl: options.rpcUrl,
|
|
1754
|
+
encodeOnly: options.encodeOnly
|
|
1755
|
+
});
|
|
1756
|
+
});
|
|
1757
|
+
const readCommand = new Command("read").description("Read messages from Net Protocol").option("--app <address>", "Filter by app address").option("--topic <topic>", "Filter by topic").option("--sender <address>", "Filter by sender address").option(
|
|
1758
|
+
"--start <n>",
|
|
1759
|
+
"Start index (inclusive)",
|
|
1760
|
+
(value) => parseInt(value, 10)
|
|
1761
|
+
).option(
|
|
1762
|
+
"--end <n>",
|
|
1763
|
+
"End index (exclusive)",
|
|
1764
|
+
(value) => parseInt(value, 10)
|
|
1765
|
+
).option(
|
|
1766
|
+
"--index <n>",
|
|
1767
|
+
"Single message at index",
|
|
1768
|
+
(value) => parseInt(value, 10)
|
|
1769
|
+
).option(
|
|
1770
|
+
"--limit <n>",
|
|
1771
|
+
"Number of latest messages (default: 10)",
|
|
1772
|
+
(value) => parseInt(value, 10)
|
|
1773
|
+
).option(
|
|
1774
|
+
"--chain-id <id>",
|
|
1775
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
1776
|
+
(value) => parseInt(value, 10)
|
|
1777
|
+
).option(
|
|
1778
|
+
"--rpc-url <url>",
|
|
1779
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
1780
|
+
).option("--json", "Output in JSON format").action(async (options) => {
|
|
1781
|
+
await executeRead({
|
|
1782
|
+
app: options.app,
|
|
1783
|
+
topic: options.topic,
|
|
1784
|
+
sender: options.sender,
|
|
1785
|
+
start: options.start,
|
|
1786
|
+
end: options.end,
|
|
1787
|
+
index: options.index,
|
|
1788
|
+
limit: options.limit,
|
|
1789
|
+
chainId: options.chainId,
|
|
1790
|
+
rpcUrl: options.rpcUrl,
|
|
1791
|
+
json: options.json
|
|
1792
|
+
});
|
|
1793
|
+
});
|
|
1794
|
+
const countCommand = new Command("count").description("Get message count from Net Protocol").option("--app <address>", "Filter by app address").option("--topic <topic>", "Filter by topic").option("--sender <address>", "Filter by sender address").option(
|
|
1795
|
+
"--chain-id <id>",
|
|
1796
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
1797
|
+
(value) => parseInt(value, 10)
|
|
1798
|
+
).option(
|
|
1799
|
+
"--rpc-url <url>",
|
|
1800
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
1801
|
+
).option("--json", "Output in JSON format").action(async (options) => {
|
|
1802
|
+
await executeCount({
|
|
1803
|
+
app: options.app,
|
|
1804
|
+
topic: options.topic,
|
|
1805
|
+
sender: options.sender,
|
|
1806
|
+
chainId: options.chainId,
|
|
1807
|
+
rpcUrl: options.rpcUrl,
|
|
1808
|
+
json: options.json
|
|
1809
|
+
});
|
|
1810
|
+
});
|
|
1811
|
+
messageCommand.addCommand(sendCommand);
|
|
1812
|
+
messageCommand.addCommand(readCommand);
|
|
1813
|
+
messageCommand.addCommand(countCommand);
|
|
1814
|
+
}
|
|
1815
|
+
var SUPPORTED_CHAINS = [
|
|
1816
|
+
{ id: 8453, name: "Base", type: "mainnet" },
|
|
1817
|
+
{ id: 1, name: "Ethereum", type: "mainnet" },
|
|
1818
|
+
{ id: 666666666, name: "Degen", type: "mainnet" },
|
|
1819
|
+
{ id: 5112, name: "Ham", type: "mainnet" },
|
|
1820
|
+
{ id: 57073, name: "Ink", type: "mainnet" },
|
|
1821
|
+
{ id: 130, name: "Unichain", type: "mainnet" },
|
|
1822
|
+
{ id: 999, name: "HyperEVM", type: "mainnet" },
|
|
1823
|
+
{ id: 9745, name: "Plasma", type: "mainnet" },
|
|
1824
|
+
{ id: 143, name: "Monad", type: "mainnet" },
|
|
1825
|
+
{ id: 84532, name: "Base Sepolia", type: "testnet" },
|
|
1826
|
+
{ id: 11155111, name: "Sepolia", type: "testnet" }
|
|
1827
|
+
];
|
|
1828
|
+
function registerChainsCommand(program2) {
|
|
1829
|
+
program2.command("chains").description("List supported chains").option("--json", "Output in JSON format").action((options) => {
|
|
1830
|
+
if (options.json) {
|
|
1831
|
+
console.log(JSON.stringify(SUPPORTED_CHAINS, null, 2));
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
console.log(chalk4.white.bold("Supported Chains:\n"));
|
|
1835
|
+
console.log(chalk4.cyan("Mainnets:"));
|
|
1836
|
+
SUPPORTED_CHAINS.filter((c) => c.type === "mainnet").forEach((chain) => {
|
|
1837
|
+
console.log(` ${chalk4.white(chain.name)} ${chalk4.gray(`(${chain.id})`)}`);
|
|
1838
|
+
});
|
|
1839
|
+
console.log(chalk4.cyan("\nTestnets:"));
|
|
1840
|
+
SUPPORTED_CHAINS.filter((c) => c.type === "testnet").forEach((chain) => {
|
|
1841
|
+
console.log(` ${chalk4.white(chain.name)} ${chalk4.gray(`(${chain.id})`)}`);
|
|
1842
|
+
});
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
function registerInfoCommand(program2) {
|
|
1846
|
+
program2.command("info").description("Show contract info and stats").option(
|
|
1847
|
+
"--chain-id <id>",
|
|
1848
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
1849
|
+
(value) => parseInt(value, 10)
|
|
1850
|
+
).option(
|
|
1851
|
+
"--rpc-url <url>",
|
|
1852
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
1853
|
+
).option("--json", "Output in JSON format").action(async (options) => {
|
|
1854
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
1855
|
+
chainId: options.chainId,
|
|
1856
|
+
rpcUrl: options.rpcUrl
|
|
1857
|
+
});
|
|
1858
|
+
try {
|
|
1859
|
+
const client = createNetClient(readOnlyOptions);
|
|
1860
|
+
const contract = getNetContract(readOnlyOptions.chainId);
|
|
1861
|
+
const chainName = getChainName({ chainId: readOnlyOptions.chainId });
|
|
1862
|
+
const totalMessages = await client.getMessageCount({});
|
|
1863
|
+
const info = {
|
|
1864
|
+
chain: {
|
|
1865
|
+
id: readOnlyOptions.chainId,
|
|
1866
|
+
name: chainName || "Unknown"
|
|
1867
|
+
},
|
|
1868
|
+
contract: {
|
|
1869
|
+
address: contract.address
|
|
1870
|
+
},
|
|
1871
|
+
stats: {
|
|
1872
|
+
totalMessages
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
if (options.json) {
|
|
1876
|
+
console.log(JSON.stringify(info, null, 2));
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
console.log(chalk4.white.bold("Net Protocol Info\n"));
|
|
1880
|
+
console.log(chalk4.cyan("Chain:"));
|
|
1881
|
+
console.log(` Name: ${chalk4.white(info.chain.name)}`);
|
|
1882
|
+
console.log(` ID: ${chalk4.white(info.chain.id)}`);
|
|
1883
|
+
console.log(chalk4.cyan("\nContract:"));
|
|
1884
|
+
console.log(` Address: ${chalk4.white(info.contract.address)}`);
|
|
1885
|
+
console.log(chalk4.cyan("\nStats:"));
|
|
1886
|
+
console.log(` Total Messages: ${chalk4.white(info.stats.totalMessages)}`);
|
|
1887
|
+
} catch (error) {
|
|
1888
|
+
exitWithError(
|
|
1889
|
+
`Failed to get info: ${error instanceof Error ? error.message : String(error)}`
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
async function executeEncodeOnly2(options) {
|
|
1895
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
1896
|
+
chainId: options.chainId,
|
|
1897
|
+
rpcUrl: options.rpcUrl
|
|
1898
|
+
});
|
|
1899
|
+
if (!isNetrSupportedChain(readOnlyOptions.chainId)) {
|
|
1900
|
+
exitWithError(
|
|
1901
|
+
`Chain ${readOnlyOptions.chainId} is not supported for token deployment`
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
let deployerAddress;
|
|
1905
|
+
if (options.privateKey) {
|
|
1906
|
+
const account = privateKeyToAccount(options.privateKey);
|
|
1907
|
+
deployerAddress = account.address;
|
|
1908
|
+
} else {
|
|
1909
|
+
deployerAddress = "0x0000000000000000000000000000000000000000";
|
|
1910
|
+
}
|
|
1911
|
+
const client = new NetrClient({
|
|
1912
|
+
chainId: readOnlyOptions.chainId,
|
|
1913
|
+
overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
|
|
1914
|
+
});
|
|
1915
|
+
const saltResult = await client.generateSalt({
|
|
1916
|
+
name: options.name,
|
|
1917
|
+
symbol: options.symbol,
|
|
1918
|
+
image: options.image,
|
|
1919
|
+
animation: options.animation,
|
|
1920
|
+
deployer: deployerAddress,
|
|
1921
|
+
fid: options.fid ? BigInt(options.fid) : void 0,
|
|
1922
|
+
metadataAddress: options.metadataAddress,
|
|
1923
|
+
extraStringData: options.extraStringData
|
|
1924
|
+
});
|
|
1925
|
+
if (!saltResult) {
|
|
1926
|
+
exitWithError("Failed to generate salt for token deployment");
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
const txConfig = client.buildDeployConfig(
|
|
1930
|
+
{
|
|
1931
|
+
name: options.name,
|
|
1932
|
+
symbol: options.symbol,
|
|
1933
|
+
image: options.image,
|
|
1934
|
+
animation: options.animation,
|
|
1935
|
+
deployer: deployerAddress,
|
|
1936
|
+
fid: options.fid ? BigInt(options.fid) : void 0,
|
|
1937
|
+
mintPrice: options.mintPrice ? BigInt(options.mintPrice) : void 0,
|
|
1938
|
+
mintEndTimestamp: options.mintEndTimestamp ? BigInt(options.mintEndTimestamp) : void 0,
|
|
1939
|
+
maxMintSupply: options.maxMintSupply ? BigInt(options.maxMintSupply) : void 0,
|
|
1940
|
+
metadataAddress: options.metadataAddress,
|
|
1941
|
+
extraStringData: options.extraStringData,
|
|
1942
|
+
initialBuy: options.initialBuy ? parseEther(options.initialBuy) : void 0
|
|
1943
|
+
},
|
|
1944
|
+
saltResult.salt
|
|
1945
|
+
);
|
|
1946
|
+
const calldata = encodeFunctionData({
|
|
1947
|
+
abi: txConfig.abi,
|
|
1948
|
+
functionName: txConfig.functionName,
|
|
1949
|
+
args: txConfig.args
|
|
1950
|
+
});
|
|
1951
|
+
const result = {
|
|
1952
|
+
predictedAddress: saltResult.predictedAddress,
|
|
1953
|
+
salt: saltResult.salt,
|
|
1954
|
+
transaction: {
|
|
1955
|
+
to: txConfig.address,
|
|
1956
|
+
data: calldata,
|
|
1957
|
+
chainId: readOnlyOptions.chainId,
|
|
1958
|
+
value: txConfig.value?.toString() ?? "0"
|
|
1959
|
+
},
|
|
1960
|
+
config: {
|
|
1961
|
+
name: options.name,
|
|
1962
|
+
symbol: options.symbol,
|
|
1963
|
+
image: options.image,
|
|
1964
|
+
deployer: deployerAddress,
|
|
1965
|
+
...options.initialBuy && { initialBuy: options.initialBuy }
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1968
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1969
|
+
}
|
|
1970
|
+
async function executeTokenDeploy(options) {
|
|
1971
|
+
if (options.encodeOnly) {
|
|
1972
|
+
await executeEncodeOnly2(options);
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
const commonOptions = parseCommonOptions({
|
|
1976
|
+
privateKey: options.privateKey,
|
|
1977
|
+
chainId: options.chainId,
|
|
1978
|
+
rpcUrl: options.rpcUrl
|
|
1979
|
+
});
|
|
1980
|
+
if (!isNetrSupportedChain(commonOptions.chainId)) {
|
|
1981
|
+
exitWithError(
|
|
1982
|
+
`Chain ${commonOptions.chainId} is not supported for token deployment. Supported: Base (8453), Plasma (9745), Monad (143), HyperEVM (999)`
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
const account = privateKeyToAccount(commonOptions.privateKey);
|
|
1986
|
+
const client = new NetrClient({
|
|
1987
|
+
chainId: commonOptions.chainId,
|
|
1988
|
+
overrides: commonOptions.rpcUrl ? { rpcUrls: [commonOptions.rpcUrl] } : void 0
|
|
1989
|
+
});
|
|
1990
|
+
console.log(chalk4.blue("Generating salt and predicting token address..."));
|
|
1991
|
+
const saltResult = await client.generateSalt({
|
|
1992
|
+
name: options.name,
|
|
1993
|
+
symbol: options.symbol,
|
|
1994
|
+
image: options.image,
|
|
1995
|
+
animation: options.animation,
|
|
1996
|
+
deployer: account.address,
|
|
1997
|
+
fid: options.fid ? BigInt(options.fid) : void 0,
|
|
1998
|
+
metadataAddress: options.metadataAddress,
|
|
1999
|
+
extraStringData: options.extraStringData
|
|
2000
|
+
});
|
|
2001
|
+
if (!saltResult) {
|
|
2002
|
+
exitWithError("Failed to generate salt for token deployment");
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
console.log(
|
|
2006
|
+
chalk4.cyan(`Predicted token address: ${saltResult.predictedAddress}`)
|
|
2007
|
+
);
|
|
2008
|
+
const txConfig = client.buildDeployConfig(
|
|
2009
|
+
{
|
|
2010
|
+
name: options.name,
|
|
2011
|
+
symbol: options.symbol,
|
|
2012
|
+
image: options.image,
|
|
2013
|
+
animation: options.animation,
|
|
2014
|
+
deployer: account.address,
|
|
2015
|
+
fid: options.fid ? BigInt(options.fid) : void 0,
|
|
2016
|
+
mintPrice: options.mintPrice ? BigInt(options.mintPrice) : void 0,
|
|
2017
|
+
mintEndTimestamp: options.mintEndTimestamp ? BigInt(options.mintEndTimestamp) : void 0,
|
|
2018
|
+
maxMintSupply: options.maxMintSupply ? BigInt(options.maxMintSupply) : void 0,
|
|
2019
|
+
metadataAddress: options.metadataAddress,
|
|
2020
|
+
extraStringData: options.extraStringData,
|
|
2021
|
+
initialBuy: options.initialBuy ? parseEther(options.initialBuy) : void 0
|
|
2022
|
+
},
|
|
2023
|
+
saltResult.salt
|
|
2024
|
+
);
|
|
2025
|
+
const rpcUrls = getChainRpcUrls({
|
|
2026
|
+
chainId: commonOptions.chainId,
|
|
2027
|
+
rpcUrl: commonOptions.rpcUrl
|
|
2028
|
+
});
|
|
2029
|
+
const walletClient = createWalletClient({
|
|
2030
|
+
account,
|
|
2031
|
+
transport: http(rpcUrls[0])
|
|
2032
|
+
});
|
|
2033
|
+
if (options.initialBuy) {
|
|
2034
|
+
console.log(chalk4.blue(`Deploying token with ${options.initialBuy} ETH initial buy...`));
|
|
2035
|
+
} else {
|
|
2036
|
+
console.log(chalk4.blue("Deploying token..."));
|
|
2037
|
+
}
|
|
2038
|
+
try {
|
|
2039
|
+
const calldata = encodeFunctionData({
|
|
2040
|
+
abi: txConfig.abi,
|
|
2041
|
+
functionName: txConfig.functionName,
|
|
2042
|
+
args: txConfig.args
|
|
2043
|
+
});
|
|
2044
|
+
const hash = await walletClient.sendTransaction({
|
|
2045
|
+
to: txConfig.address,
|
|
2046
|
+
data: calldata,
|
|
2047
|
+
chain: null,
|
|
2048
|
+
value: txConfig.value
|
|
2049
|
+
});
|
|
2050
|
+
const initialBuyLine = options.initialBuy ? `
|
|
2051
|
+
Initial Buy: ${options.initialBuy} ETH` : "";
|
|
2052
|
+
console.log(
|
|
2053
|
+
chalk4.green(
|
|
2054
|
+
`Token deployed successfully!
|
|
2055
|
+
Transaction: ${hash}
|
|
2056
|
+
Token Address: ${saltResult.predictedAddress}
|
|
2057
|
+
Name: ${options.name}
|
|
2058
|
+
Symbol: ${options.symbol}${initialBuyLine}`
|
|
2059
|
+
)
|
|
2060
|
+
);
|
|
2061
|
+
} catch (error) {
|
|
2062
|
+
exitWithError(
|
|
2063
|
+
`Failed to deploy token: ${error instanceof Error ? error.message : String(error)}`
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
async function executeTokenInfo(options) {
|
|
2068
|
+
const readOnlyOptions = parseReadOnlyOptions({
|
|
2069
|
+
chainId: options.chainId,
|
|
2070
|
+
rpcUrl: options.rpcUrl
|
|
2071
|
+
});
|
|
2072
|
+
if (!isNetrSupportedChain(readOnlyOptions.chainId)) {
|
|
2073
|
+
exitWithError(
|
|
2074
|
+
`Chain ${readOnlyOptions.chainId} is not supported for Netr tokens`
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
const client = new NetrClient({
|
|
2078
|
+
chainId: readOnlyOptions.chainId,
|
|
2079
|
+
overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
|
|
2080
|
+
});
|
|
2081
|
+
const tokenAddress = options.address;
|
|
2082
|
+
try {
|
|
2083
|
+
const [token, storageData, price] = await Promise.all([
|
|
2084
|
+
client.getToken(tokenAddress),
|
|
2085
|
+
client.getStorageData(tokenAddress),
|
|
2086
|
+
client.getPrice(tokenAddress)
|
|
2087
|
+
]);
|
|
2088
|
+
if (!token) {
|
|
2089
|
+
exitWithError(`Token not found at address ${tokenAddress}`);
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
let locker = null;
|
|
2093
|
+
if (storageData?.lockerAddress) {
|
|
2094
|
+
locker = await client.getLocker(storageData.lockerAddress);
|
|
2095
|
+
}
|
|
2096
|
+
if (options.json) {
|
|
2097
|
+
const output = {
|
|
2098
|
+
address: tokenAddress,
|
|
2099
|
+
chainId: readOnlyOptions.chainId,
|
|
2100
|
+
token: {
|
|
2101
|
+
name: token.name,
|
|
2102
|
+
symbol: token.symbol,
|
|
2103
|
+
deployer: token.deployer,
|
|
2104
|
+
image: token.image,
|
|
2105
|
+
animation: token.animation || null,
|
|
2106
|
+
fid: token.fid.toString(),
|
|
2107
|
+
totalSupply: token.totalSupply.toString(),
|
|
2108
|
+
decimals: token.decimals,
|
|
2109
|
+
extraStringData: token.extraStringData || null
|
|
2110
|
+
},
|
|
2111
|
+
pool: storageData?.poolAddress || null,
|
|
2112
|
+
locker: storageData?.lockerAddress || null,
|
|
2113
|
+
price: price ? {
|
|
2114
|
+
priceInEth: price.priceInEth,
|
|
2115
|
+
priceInWeth: price.priceInWeth,
|
|
2116
|
+
tick: price.tick
|
|
2117
|
+
} : null,
|
|
2118
|
+
lockerInfo: locker ? {
|
|
2119
|
+
owner: locker.owner,
|
|
2120
|
+
duration: locker.duration.toString(),
|
|
2121
|
+
endTimestamp: locker.endTimestamp.toString(),
|
|
2122
|
+
version: locker.version
|
|
2123
|
+
} : null
|
|
2124
|
+
};
|
|
2125
|
+
console.log(JSON.stringify(output, null, 2));
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
console.log(chalk4.white.bold("\nToken Info:\n"));
|
|
2129
|
+
console.log(` ${chalk4.cyan("Address:")} ${tokenAddress}`);
|
|
2130
|
+
console.log(` ${chalk4.cyan("Chain ID:")} ${readOnlyOptions.chainId}`);
|
|
2131
|
+
console.log(` ${chalk4.cyan("Name:")} ${token.name}`);
|
|
2132
|
+
console.log(` ${chalk4.cyan("Symbol:")} ${token.symbol}`);
|
|
2133
|
+
console.log(` ${chalk4.cyan("Deployer:")} ${token.deployer}`);
|
|
2134
|
+
console.log(` ${chalk4.cyan("FID:")} ${token.fid.toString()}`);
|
|
2135
|
+
console.log(` ${chalk4.cyan("Image:")} ${token.image}`);
|
|
2136
|
+
if (token.animation) {
|
|
2137
|
+
console.log(` ${chalk4.cyan("Animation:")} ${token.animation}`);
|
|
2138
|
+
}
|
|
2139
|
+
if (token.extraStringData) {
|
|
2140
|
+
console.log(` ${chalk4.cyan("Extra Data:")} ${token.extraStringData}`);
|
|
2141
|
+
}
|
|
2142
|
+
if (storageData?.poolAddress) {
|
|
2143
|
+
console.log(` ${chalk4.cyan("Pool:")} ${storageData.poolAddress}`);
|
|
2144
|
+
}
|
|
2145
|
+
if (storageData?.lockerAddress) {
|
|
2146
|
+
console.log(` ${chalk4.cyan("Locker:")} ${storageData.lockerAddress}`);
|
|
2147
|
+
}
|
|
2148
|
+
if (price) {
|
|
2149
|
+
console.log(` ${chalk4.cyan("Price:")} ${price.priceInEth} ETH`);
|
|
2150
|
+
}
|
|
2151
|
+
if (locker) {
|
|
2152
|
+
const endDate = new Date(Number(locker.endTimestamp) * 1e3);
|
|
2153
|
+
console.log(
|
|
2154
|
+
` ${chalk4.cyan("Lock Ends:")} ${endDate.toLocaleDateString()}`
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
console.log();
|
|
2158
|
+
} catch (error) {
|
|
2159
|
+
exitWithError(
|
|
2160
|
+
`Failed to get token info: ${error instanceof Error ? error.message : String(error)}`
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// src/commands/token/index.ts
|
|
2166
|
+
function registerTokenCommand(program2) {
|
|
2167
|
+
const tokenCommand = program2.command("token").description("Token operations (Netr/Banger)");
|
|
2168
|
+
const deployCommand = new Command("deploy").description("Deploy a new Netr token").requiredOption("--name <name>", "Token name").requiredOption("--symbol <symbol>", "Token symbol").requiredOption("--image <url>", "Token image URL").option("--animation <url>", "Token animation URL").option("--fid <number>", "Farcaster ID").option(
|
|
2169
|
+
"--private-key <key>",
|
|
2170
|
+
"Private key (0x-prefixed hex). Can also be set via NET_PRIVATE_KEY env var"
|
|
2171
|
+
).option(
|
|
2172
|
+
"--chain-id <id>",
|
|
2173
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
2174
|
+
(value) => parseInt(value, 10)
|
|
2175
|
+
).option(
|
|
2176
|
+
"--rpc-url <url>",
|
|
2177
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
2178
|
+
).option(
|
|
2179
|
+
"--encode-only",
|
|
2180
|
+
"Output transaction data as JSON instead of executing"
|
|
2181
|
+
).option("--mint-price <wei>", "Mint price in wei for NFT drop").option("--mint-end-timestamp <timestamp>", "Unix timestamp when minting ends").option("--max-mint-supply <amount>", "Maximum mint supply for NFT drop").option("--metadata-address <address>", "Dynamic metadata contract address").option("--extra-string-data <data>", "Extra string data to store with token").option("--initial-buy <eth>", "ETH amount to swap for tokens on deploy (e.g., '0.001')").action(async (options) => {
|
|
2182
|
+
await executeTokenDeploy({
|
|
2183
|
+
name: options.name,
|
|
2184
|
+
symbol: options.symbol,
|
|
2185
|
+
image: options.image,
|
|
2186
|
+
animation: options.animation,
|
|
2187
|
+
fid: options.fid,
|
|
2188
|
+
privateKey: options.privateKey,
|
|
2189
|
+
chainId: options.chainId,
|
|
2190
|
+
rpcUrl: options.rpcUrl,
|
|
2191
|
+
encodeOnly: options.encodeOnly,
|
|
2192
|
+
mintPrice: options.mintPrice,
|
|
2193
|
+
mintEndTimestamp: options.mintEndTimestamp,
|
|
2194
|
+
maxMintSupply: options.maxMintSupply,
|
|
2195
|
+
metadataAddress: options.metadataAddress,
|
|
2196
|
+
extraStringData: options.extraStringData,
|
|
2197
|
+
initialBuy: options.initialBuy
|
|
2198
|
+
});
|
|
2199
|
+
});
|
|
2200
|
+
const infoCommand = new Command("info").description("Get information about a Netr token").requiredOption("--address <address>", "Token address").option(
|
|
2201
|
+
"--chain-id <id>",
|
|
2202
|
+
"Chain ID. Can also be set via NET_CHAIN_ID env var",
|
|
2203
|
+
(value) => parseInt(value, 10)
|
|
2204
|
+
).option(
|
|
2205
|
+
"--rpc-url <url>",
|
|
2206
|
+
"Custom RPC URL. Can also be set via NET_RPC_URL env var"
|
|
2207
|
+
).option("--json", "Output in JSON format").action(async (options) => {
|
|
2208
|
+
await executeTokenInfo({
|
|
2209
|
+
address: options.address,
|
|
2210
|
+
chainId: options.chainId,
|
|
2211
|
+
rpcUrl: options.rpcUrl,
|
|
2212
|
+
json: options.json
|
|
2213
|
+
});
|
|
2214
|
+
});
|
|
2215
|
+
tokenCommand.addCommand(deployCommand);
|
|
2216
|
+
tokenCommand.addCommand(infoCommand);
|
|
1283
2217
|
}
|
|
1284
2218
|
|
|
1285
2219
|
// src/cli/index.ts
|
|
1286
2220
|
var program = new Command();
|
|
1287
2221
|
program.name("netp").description("CLI tool for Net Protocol").version("0.1.0");
|
|
1288
2222
|
registerStorageCommand(program);
|
|
2223
|
+
registerMessageCommand(program);
|
|
2224
|
+
registerChainsCommand(program);
|
|
2225
|
+
registerInfoCommand(program);
|
|
2226
|
+
registerTokenCommand(program);
|
|
1289
2227
|
program.parse();
|
|
1290
2228
|
//# sourceMappingURL=index.mjs.map
|
|
1291
2229
|
//# sourceMappingURL=index.mjs.map
|