@sentio/cli 3.6.2 → 3.7.0-rc.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/lib/index.js +169 -51
- package/package.json +1 -1
- package/src/commands/upload.ts +61 -12
- package/src/network.ts +164 -50
package/lib/index.js
CHANGED
|
@@ -139214,8 +139214,14 @@ var PROCESSOR_REGISTRY_ABI = [
|
|
|
139214
139214
|
tuple(string chainId, bool enableRpc, bool enableTrace)[] requireChains,
|
|
139215
139215
|
string sdkVersion
|
|
139216
139216
|
) returns (string)`,
|
|
139217
|
+
`function createProcessor(
|
|
139218
|
+
address owner,
|
|
139219
|
+
string id,
|
|
139220
|
+
tuple(uint8 sourceType, string ipfsCid) source,
|
|
139221
|
+
tuple(string chainId, bool enableRpc, bool enableTrace)[] requireChains,
|
|
139222
|
+
string sdkVersion
|
|
139223
|
+
) returns (string)`,
|
|
139217
139224
|
"event ProcessorCreated(string indexed processorId)",
|
|
139218
|
-
"function getAllocations(string processorId) view returns (tuple(uint256 indexerId, uint256 timestamp, bool indexerReady)[])",
|
|
139219
139225
|
`function getProcessor(string processorId) view returns (
|
|
139220
139226
|
tuple(
|
|
139221
139227
|
string id,
|
|
@@ -139225,12 +139231,18 @@ var PROCESSOR_REGISTRY_ABI = [
|
|
|
139225
139231
|
string sdkVersion,
|
|
139226
139232
|
tuple(string chainId, bool enableRpc, bool enableTrace)[] requireChains,
|
|
139227
139233
|
tuple(uint8 sourceType, string ipfsCid) source,
|
|
139228
|
-
tuple(uint256 indexerId, uint256
|
|
139234
|
+
tuple(uint256 indexerId, uint256 allocationTime, bool indexerReady, uint256 replicaIndex)[] allocations
|
|
139229
139235
|
)
|
|
139230
139236
|
)`,
|
|
139231
139237
|
"function deleteProcessor(string processorId)"
|
|
139232
139238
|
];
|
|
139233
|
-
var CONTROLLER_ABI = [
|
|
139239
|
+
var CONTROLLER_ABI = [
|
|
139240
|
+
"function startProcessor(string processorId)",
|
|
139241
|
+
"function stopProcessor(string processorId)",
|
|
139242
|
+
"function getAllocations(string processorId) view returns (tuple(uint256 indexerId, uint256 allocationTime, bool indexerReady, uint256 replicaIndex)[])"
|
|
139243
|
+
];
|
|
139244
|
+
var DATABASES_ABI = ["function getProcessorDatabases(string processorId) view returns (string[])"];
|
|
139245
|
+
var PERMISSIONS_ABI = ["function isOperator(address account, address operator) view returns (bool)"];
|
|
139234
139246
|
var ERC20_ABI = [
|
|
139235
139247
|
"function balanceOf(address account) view returns (uint256)",
|
|
139236
139248
|
"function approve(address spender, uint256 amount) returns (bool)"
|
|
@@ -139240,7 +139252,9 @@ var ADDRESS_BOOK_KEYS = {
|
|
|
139240
139252
|
processorRegistry: "processor_registry",
|
|
139241
139253
|
controller: "controller",
|
|
139242
139254
|
token: "sentio_token",
|
|
139243
|
-
billing: "billing"
|
|
139255
|
+
billing: "billing",
|
|
139256
|
+
databases: "databases",
|
|
139257
|
+
permissions: "permissions"
|
|
139244
139258
|
};
|
|
139245
139259
|
var cachedAddresses;
|
|
139246
139260
|
async function resolveNetworkAddresses(config) {
|
|
@@ -139261,17 +139275,29 @@ async function resolveNetworkAddresses(config) {
|
|
|
139261
139275
|
}
|
|
139262
139276
|
}
|
|
139263
139277
|
};
|
|
139264
|
-
const [processorRegistry, controller, token, billing] = await Promise.all([
|
|
139278
|
+
const [processorRegistry, controller, token, billing, databases, permissions] = await Promise.all([
|
|
139265
139279
|
resolveAddress2(ADDRESS_BOOK_KEYS.processorRegistry),
|
|
139266
139280
|
resolveAddress2(ADDRESS_BOOK_KEYS.controller),
|
|
139267
139281
|
resolveAddress2(ADDRESS_BOOK_KEYS.token),
|
|
139268
|
-
resolveAddress2(ADDRESS_BOOK_KEYS.billing)
|
|
139282
|
+
resolveAddress2(ADDRESS_BOOK_KEYS.billing),
|
|
139283
|
+
resolveAddress2(ADDRESS_BOOK_KEYS.databases),
|
|
139284
|
+
resolveAddress2(ADDRESS_BOOK_KEYS.permissions)
|
|
139269
139285
|
]);
|
|
139270
139286
|
console.log(source_default.gray(`ProcessorRegistry: ${processorRegistry}`));
|
|
139271
139287
|
console.log(source_default.gray(`Controller: ${controller}`));
|
|
139272
139288
|
console.log(source_default.gray(`ST Token: ${token}`));
|
|
139273
139289
|
console.log(source_default.gray(`Billing: ${billing}`));
|
|
139274
|
-
|
|
139290
|
+
console.log(source_default.gray(`Databases: ${databases}`));
|
|
139291
|
+
console.log(source_default.gray(`Permissions: ${permissions}`));
|
|
139292
|
+
cachedAddresses = {
|
|
139293
|
+
addressBook: addressBookAddr,
|
|
139294
|
+
processorRegistry,
|
|
139295
|
+
controller,
|
|
139296
|
+
token,
|
|
139297
|
+
billing,
|
|
139298
|
+
databases,
|
|
139299
|
+
permissions
|
|
139300
|
+
};
|
|
139275
139301
|
return cachedAddresses;
|
|
139276
139302
|
}
|
|
139277
139303
|
function getWalletFromPrivateKey(privateKey) {
|
|
@@ -139307,6 +139333,11 @@ async function checkBillingBalance(config, addresses, walletAddress) {
|
|
|
139307
139333
|
return 0n;
|
|
139308
139334
|
}
|
|
139309
139335
|
}
|
|
139336
|
+
async function isOperatorOnChain(config, addresses, owner, operator) {
|
|
139337
|
+
const provider = new ethers_exports.JsonRpcProvider(config.rpcUrl);
|
|
139338
|
+
const permissions = new ethers_exports.Contract(addresses.permissions, PERMISSIONS_ABI, provider);
|
|
139339
|
+
return await permissions.isOperator(owner, operator);
|
|
139340
|
+
}
|
|
139310
139341
|
async function uploadToIPFS(fileBuffer, ipfsUrl) {
|
|
139311
139342
|
const formData = new FormData();
|
|
139312
139343
|
const blob = new Blob([fileBuffer], { type: "application/octet-stream" });
|
|
@@ -139322,6 +139353,26 @@ async function uploadToIPFS(fileBuffer, ipfsUrl) {
|
|
|
139322
139353
|
const result = await response.json();
|
|
139323
139354
|
return result.Hash;
|
|
139324
139355
|
}
|
|
139356
|
+
async function submitAndWait(explorerUrl, label, txPromise) {
|
|
139357
|
+
let tx;
|
|
139358
|
+
try {
|
|
139359
|
+
tx = await txPromise;
|
|
139360
|
+
} catch (err) {
|
|
139361
|
+
throw new Error(`${label} failed to submit: ${err.shortMessage ?? err.message}`);
|
|
139362
|
+
}
|
|
139363
|
+
console.log(source_default.gray(` Tx hash: ${explorerUrl}/tx/${tx.hash}`));
|
|
139364
|
+
console.log(source_default.blue("Waiting for confirmation..."));
|
|
139365
|
+
let receipt;
|
|
139366
|
+
try {
|
|
139367
|
+
receipt = await tx.wait();
|
|
139368
|
+
} catch (err) {
|
|
139369
|
+
throw new Error(`${label} failed: ${err.shortMessage ?? err.message}. Tx: ${explorerUrl}/tx/${tx.hash}`);
|
|
139370
|
+
}
|
|
139371
|
+
if (!receipt || receipt.status === 0) {
|
|
139372
|
+
throw new Error(`${label} transaction reverted. Tx: ${explorerUrl}/tx/${tx.hash}`);
|
|
139373
|
+
}
|
|
139374
|
+
return tx;
|
|
139375
|
+
}
|
|
139325
139376
|
async function getProcessorOnChain(config, addresses, processorId) {
|
|
139326
139377
|
const provider = new ethers_exports.JsonRpcProvider(config.rpcUrl);
|
|
139327
139378
|
const registry = new ethers_exports.Contract(addresses.processorRegistry, PROCESSOR_REGISTRY_ABI, provider);
|
|
@@ -139347,17 +139398,31 @@ async function deleteProcessorOnChain(config, addresses, wallet, processorId) {
|
|
|
139347
139398
|
const signer = wallet.connect(provider);
|
|
139348
139399
|
const registry = new ethers_exports.Contract(addresses.processorRegistry, PROCESSOR_REGISTRY_ABI, signer);
|
|
139349
139400
|
console.log(source_default.blue("Deleting existing processor on-chain..."));
|
|
139350
|
-
const tx = await registry.deleteProcessor(processorId);
|
|
139351
|
-
console.log(source_default.gray(` Tx hash: ${tx.hash}`));
|
|
139352
|
-
console.log(source_default.blue("Waiting for confirmation..."));
|
|
139353
|
-
const receipt = await tx.wait();
|
|
139354
|
-
if (receipt.status === 0) {
|
|
139355
|
-
throw new Error(`deleteProcessor transaction failed. Tx: ${config.explorerUrl}/tx/${tx.hash}`);
|
|
139356
|
-
}
|
|
139401
|
+
const tx = await submitAndWait(config.explorerUrl, "deleteProcessor", registry.deleteProcessor(processorId));
|
|
139357
139402
|
console.log(source_default.green(`Processor deleted. Tx: ${config.explorerUrl}/tx/${tx.hash}`));
|
|
139403
|
+
await waitForProcessorDatabaseCleanup(addresses, processorId, provider);
|
|
139358
139404
|
return tx.hash;
|
|
139359
139405
|
}
|
|
139360
|
-
async function
|
|
139406
|
+
async function waitForProcessorDatabaseCleanup(addresses, processorId, provider, timeoutMs = 12e4, pollIntervalMs = 5e3) {
|
|
139407
|
+
const databases = new ethers_exports.Contract(addresses.databases, DATABASES_ABI, provider);
|
|
139408
|
+
const dbIds = await databases.getProcessorDatabases(processorId);
|
|
139409
|
+
if (dbIds.length === 0) {
|
|
139410
|
+
return;
|
|
139411
|
+
}
|
|
139412
|
+
console.log(source_default.blue(`Waiting for ${dbIds.length} replica database(s) to be cleaned up...`));
|
|
139413
|
+
const deadline = Date.now() + timeoutMs;
|
|
139414
|
+
while (Date.now() < deadline) {
|
|
139415
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
139416
|
+
const remaining = await databases.getProcessorDatabases(processorId);
|
|
139417
|
+
if (remaining.length === 0) {
|
|
139418
|
+
console.log(source_default.green("All replica databases cleaned up."));
|
|
139419
|
+
return;
|
|
139420
|
+
}
|
|
139421
|
+
console.log(source_default.gray(` ${remaining.length} replica database(s) still pending cleanup...`));
|
|
139422
|
+
}
|
|
139423
|
+
throw new Error(`Timeout: processor databases not cleaned up within ${timeoutMs / 1e3}s`);
|
|
139424
|
+
}
|
|
139425
|
+
async function createProcessorOnChain(config, addresses, wallet, processorId, ipfsCid, requiredChainIds, sdkVersion, ownerOverride) {
|
|
139361
139426
|
const provider = new ethers_exports.JsonRpcProvider(config.rpcUrl);
|
|
139362
139427
|
const signer = wallet.connect(provider);
|
|
139363
139428
|
const registry = new ethers_exports.Contract(addresses.processorRegistry, PROCESSOR_REGISTRY_ABI, signer);
|
|
@@ -139376,13 +139441,22 @@ async function createProcessorOnChain(config, addresses, wallet, processorId, ip
|
|
|
139376
139441
|
console.log(source_default.gray(` IPFS CID: ${ipfsCid}`));
|
|
139377
139442
|
console.log(source_default.gray(` Chains: ${requiredChainIds.join(", ")}`));
|
|
139378
139443
|
console.log(source_default.gray(` SDK Version: ${sdkVersion}`));
|
|
139379
|
-
|
|
139380
|
-
|
|
139381
|
-
|
|
139382
|
-
const
|
|
139383
|
-
|
|
139384
|
-
|
|
139385
|
-
|
|
139444
|
+
if (ownerOverride) {
|
|
139445
|
+
console.log(source_default.gray(` Owner: ${ownerOverride} (operator: ${wallet.address})`));
|
|
139446
|
+
}
|
|
139447
|
+
const txPromise = ownerOverride ? registry["createProcessor(address,string,(uint8,string),(string,bool,bool)[],string)"](
|
|
139448
|
+
ownerOverride,
|
|
139449
|
+
processorId,
|
|
139450
|
+
source,
|
|
139451
|
+
requireChains,
|
|
139452
|
+
sdkVersion
|
|
139453
|
+
) : registry["createProcessor(string,(uint8,string),(string,bool,bool)[],string)"](
|
|
139454
|
+
processorId,
|
|
139455
|
+
source,
|
|
139456
|
+
requireChains,
|
|
139457
|
+
sdkVersion
|
|
139458
|
+
);
|
|
139459
|
+
const tx = await submitAndWait(config.explorerUrl, "createProcessor", txPromise);
|
|
139386
139460
|
console.log(source_default.green(`Processor created. Tx: ${config.explorerUrl}/tx/${tx.hash}`));
|
|
139387
139461
|
return tx.hash;
|
|
139388
139462
|
}
|
|
@@ -139391,13 +139465,7 @@ async function startProcessorOnChain(config, addresses, wallet, processorId) {
|
|
|
139391
139465
|
const signer = wallet.connect(provider);
|
|
139392
139466
|
const controller = new ethers_exports.Contract(addresses.controller, CONTROLLER_ABI, signer);
|
|
139393
139467
|
console.log(source_default.blue("Starting processor on-chain..."));
|
|
139394
|
-
const tx = await controller.startProcessor(processorId);
|
|
139395
|
-
console.log(source_default.gray(` Tx hash: ${tx.hash}`));
|
|
139396
|
-
console.log(source_default.blue("Waiting for confirmation..."));
|
|
139397
|
-
const receipt = await tx.wait();
|
|
139398
|
-
if (receipt.status === 0) {
|
|
139399
|
-
throw new Error(`startProcessor transaction failed. Tx: ${config.explorerUrl}/tx/${tx.hash}`);
|
|
139400
|
-
}
|
|
139468
|
+
const tx = await submitAndWait(config.explorerUrl, "startProcessor", controller.startProcessor(processorId));
|
|
139401
139469
|
console.log(source_default.green(`Processor started. Tx: ${config.explorerUrl}/tx/${tx.hash}`));
|
|
139402
139470
|
return tx.hash;
|
|
139403
139471
|
}
|
|
@@ -139405,33 +139473,41 @@ async function stopProcessorOnChain(config, addresses, wallet, processorId) {
|
|
|
139405
139473
|
const provider = new ethers_exports.JsonRpcProvider(config.rpcUrl);
|
|
139406
139474
|
const signer = wallet.connect(provider);
|
|
139407
139475
|
const controller = new ethers_exports.Contract(addresses.controller, CONTROLLER_ABI, signer);
|
|
139408
|
-
|
|
139409
|
-
|
|
139410
|
-
|
|
139411
|
-
|
|
139412
|
-
const receipt = await tx.wait();
|
|
139413
|
-
if (receipt.status === 0) {
|
|
139414
|
-
throw new Error(`stopProcessor transaction failed. Tx: ${config.explorerUrl}/tx/${tx.hash}`);
|
|
139476
|
+
const allocations = await controller.getAllocations(processorId);
|
|
139477
|
+
if (allocations.length === 0) {
|
|
139478
|
+
console.log(source_default.gray("Processor has no allocations, skipping stopProcessor."));
|
|
139479
|
+
return "";
|
|
139415
139480
|
}
|
|
139481
|
+
console.log(source_default.blue("Stopping processor on-chain..."));
|
|
139482
|
+
const tx = await submitAndWait(config.explorerUrl, "stopProcessor", controller.stopProcessor(processorId));
|
|
139416
139483
|
console.log(source_default.green(`Processor stopped. Tx: ${config.explorerUrl}/tx/${tx.hash}`));
|
|
139417
139484
|
return tx.hash;
|
|
139418
139485
|
}
|
|
139419
|
-
async function confirmNoPlatformUpload(walletAddress, stBalance, billingBalance, addresses, networkConfig) {
|
|
139486
|
+
async function confirmNoPlatformUpload(walletAddress, stBalance, billingBalance, addresses, networkConfig, ownerOverride) {
|
|
139420
139487
|
const formattedST = ethers_exports.formatEther(stBalance);
|
|
139421
139488
|
const formattedBilling = ethers_exports.formatEther(billingBalance);
|
|
139422
139489
|
console.log();
|
|
139423
139490
|
console.log(source_default.blue("=== Sentio Network Direct Upload (No Platform) ==="));
|
|
139424
|
-
|
|
139491
|
+
if (ownerOverride) {
|
|
139492
|
+
console.log(source_default.white(` Owner: ${ownerOverride}`));
|
|
139493
|
+
console.log(source_default.white(` Operator: ${walletAddress}`));
|
|
139494
|
+
} else {
|
|
139495
|
+
console.log(source_default.white(` Address: ${walletAddress}`));
|
|
139496
|
+
}
|
|
139425
139497
|
console.log(source_default.white(` ST Balance: ${formattedST} ST`));
|
|
139426
139498
|
console.log(source_default.white(` Billing Balance: ${formattedBilling} ST`));
|
|
139427
139499
|
if (billingBalance === 0n) {
|
|
139500
|
+
const who = ownerOverride ? `the owner (${ownerOverride})` : "you";
|
|
139501
|
+
const keyHint = ownerOverride ? "<owner's private key>" : "$PRIVATE_KEY";
|
|
139428
139502
|
console.log();
|
|
139429
139503
|
console.log(
|
|
139430
139504
|
source_default.yellow(
|
|
139431
|
-
` \u26A0 Your Billing balance is 0. Indexing fees are charged from the Billing contract.
|
|
139432
|
-
|
|
139433
|
-
|
|
139434
|
-
|
|
139505
|
+
` \u26A0 ${ownerOverride ? "Owner's" : "Your"} Billing balance is 0. Indexing fees are charged from the Billing contract.
|
|
139506
|
+
${who} must deposit ST tokens into the Billing contract before the processor can run.
|
|
139507
|
+
Example (replace 100 with your desired ST amount):
|
|
139508
|
+
export AMOUNT=$(cast to-wei 100 ether)
|
|
139509
|
+
cast send ${addresses.token} "approve(address,uint256)" ${addresses.billing} $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key ${keyHint}
|
|
139510
|
+
cast send ${addresses.billing} "deposit(uint256)" $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key ${keyHint}`
|
|
139435
139511
|
)
|
|
139436
139512
|
);
|
|
139437
139513
|
}
|
|
@@ -139729,7 +139805,10 @@ function myParseInt(value, dummyPrevious) {
|
|
|
139729
139805
|
return parsedValue;
|
|
139730
139806
|
}
|
|
139731
139807
|
function createUploadCommand() {
|
|
139732
|
-
return new Command("upload").description("Upload processor to Sentio").option(
|
|
139808
|
+
return new Command("upload").description("Upload processor to Sentio").option(
|
|
139809
|
+
"--owner <owner>",
|
|
139810
|
+
"(Optional) Project owner. Platform mode: project owner username. --no-platform mode: on-chain owner address (0x...) \u2014 $PRIVATE_KEY then signs as an operator on the owner's behalf, requires Permissions.addOperator beforehand."
|
|
139811
|
+
).option("--name <name>", "(Optional) Override Project name").option(
|
|
139733
139812
|
"--continue-from <version>",
|
|
139734
139813
|
"(Optional) Continue processing data from the specific processor version which will keeping the old data from previous version and will STOP that version IMMEDIATELY.",
|
|
139735
139814
|
myParseInt
|
|
@@ -139765,13 +139844,41 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139765
139844
|
const privateKey = requirePrivateKey();
|
|
139766
139845
|
const wallet = getWalletFromPrivateKey(privateKey);
|
|
139767
139846
|
const walletAddress = wallet.address;
|
|
139847
|
+
let ownerOverride;
|
|
139848
|
+
if (options.owner) {
|
|
139849
|
+
if (!ethers_exports.isAddress(options.owner)) {
|
|
139850
|
+
console.error(source_default.red(`Invalid --owner: "${options.owner}" is not a valid 0x-prefixed Ethereum address.`));
|
|
139851
|
+
process.exit(1);
|
|
139852
|
+
}
|
|
139853
|
+
ownerOverride = ethers_exports.getAddress(options.owner);
|
|
139854
|
+
}
|
|
139855
|
+
const effectiveOwner = ownerOverride ?? walletAddress;
|
|
139768
139856
|
console.log(source_default.blue("Resolving contract addresses from AddressBook..."));
|
|
139769
139857
|
const addresses = await resolveNetworkAddresses(networkConfig);
|
|
139858
|
+
if (ownerOverride) {
|
|
139859
|
+
const isOp = await isOperatorOnChain(networkConfig, addresses, ownerOverride, walletAddress);
|
|
139860
|
+
if (!isOp) {
|
|
139861
|
+
console.error(
|
|
139862
|
+
source_default.red(
|
|
139863
|
+
`Operator ${walletAddress} is not authorized for owner ${ownerOverride}.
|
|
139864
|
+
The owner must call Permissions.addOperator(${walletAddress}) first.`
|
|
139865
|
+
)
|
|
139866
|
+
);
|
|
139867
|
+
process.exit(1);
|
|
139868
|
+
}
|
|
139869
|
+
}
|
|
139770
139870
|
const [stBalance, billingBalance] = await Promise.all([
|
|
139771
|
-
checkSTBalance(networkConfig, addresses,
|
|
139772
|
-
checkBillingBalance(networkConfig, addresses,
|
|
139871
|
+
checkSTBalance(networkConfig, addresses, effectiveOwner),
|
|
139872
|
+
checkBillingBalance(networkConfig, addresses, effectiveOwner)
|
|
139773
139873
|
]);
|
|
139774
|
-
const confirmed = await confirmNoPlatformUpload(
|
|
139874
|
+
const confirmed = await confirmNoPlatformUpload(
|
|
139875
|
+
walletAddress,
|
|
139876
|
+
stBalance,
|
|
139877
|
+
billingBalance,
|
|
139878
|
+
addresses,
|
|
139879
|
+
networkConfig,
|
|
139880
|
+
ownerOverride
|
|
139881
|
+
);
|
|
139775
139882
|
if (!confirmed) {
|
|
139776
139883
|
console.log("Upload cancelled.");
|
|
139777
139884
|
process.exit(0);
|
|
@@ -139821,9 +139928,10 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139821
139928
|
const existingProcessor = await getProcessorOnChain(networkConfig, addresses, processorId);
|
|
139822
139929
|
if (existingProcessor) {
|
|
139823
139930
|
const existingOwner = existingProcessor.owner.toLowerCase();
|
|
139824
|
-
const
|
|
139825
|
-
if (existingOwner ===
|
|
139826
|
-
|
|
139931
|
+
const expectedOwner = effectiveOwner.toLowerCase();
|
|
139932
|
+
if (existingOwner === expectedOwner) {
|
|
139933
|
+
const ownedBy = ownerOverride ? `owned by ${ownerOverride}` : "owned by you";
|
|
139934
|
+
console.log(source_default.yellow(`Processor "${processorId}" already exists (${ownedBy}).`));
|
|
139827
139935
|
const shouldReplace = await confirm(`Replace existing processor "${processorId}"?`);
|
|
139828
139936
|
if (!shouldReplace) {
|
|
139829
139937
|
console.log("Upload cancelled.");
|
|
@@ -139832,9 +139940,10 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139832
139940
|
await stopProcessorOnChain(networkConfig, addresses, wallet, processorId);
|
|
139833
139941
|
await deleteProcessorOnChain(networkConfig, addresses, wallet, processorId);
|
|
139834
139942
|
} else {
|
|
139943
|
+
const expectedLabel = ownerOverride ? `expected owner ${ownerOverride}` : `your wallet ${wallet.address}`;
|
|
139835
139944
|
console.log(
|
|
139836
139945
|
source_default.yellow(
|
|
139837
|
-
`Processor "${processorId}" already exists and is owned by ${existingProcessor.owner} (not
|
|
139946
|
+
`Processor "${processorId}" already exists and is owned by ${existingProcessor.owner} (not ${expectedLabel}).`
|
|
139838
139947
|
)
|
|
139839
139948
|
);
|
|
139840
139949
|
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
@@ -139850,7 +139959,16 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139850
139959
|
console.log(source_default.blue(`Using processor ID: ${processorId}`));
|
|
139851
139960
|
}
|
|
139852
139961
|
}
|
|
139853
|
-
await createProcessorOnChain(
|
|
139962
|
+
await createProcessorOnChain(
|
|
139963
|
+
networkConfig,
|
|
139964
|
+
addresses,
|
|
139965
|
+
wallet,
|
|
139966
|
+
processorId,
|
|
139967
|
+
cid,
|
|
139968
|
+
requiredChainIds,
|
|
139969
|
+
sdkVersion,
|
|
139970
|
+
ownerOverride
|
|
139971
|
+
);
|
|
139854
139972
|
await startProcessorOnChain(networkConfig, addresses, wallet, processorId);
|
|
139855
139973
|
console.log();
|
|
139856
139974
|
console.log(source_default.green("=== Upload Complete ==="));
|
package/package.json
CHANGED
package/src/commands/upload.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
requirePrivateKey,
|
|
21
21
|
checkSTBalance,
|
|
22
22
|
checkBillingBalance,
|
|
23
|
+
isOperatorOnChain,
|
|
23
24
|
uploadToIPFS,
|
|
24
25
|
createProcessorOnChain,
|
|
25
26
|
startProcessorOnChain,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
getProcessorOnChain,
|
|
29
30
|
deleteProcessorOnChain
|
|
30
31
|
} from '../network.js'
|
|
32
|
+
import { ethers } from 'ethers'
|
|
31
33
|
import { Auth, DefaultBatchUploader, FileType, IPFSBatchUploader, WalrusBatchUploader } from '../uploader.js'
|
|
32
34
|
export { type Auth } from '../uploader.js'
|
|
33
35
|
|
|
@@ -43,7 +45,10 @@ function myParseInt(value: string, dummyPrevious: number): number {
|
|
|
43
45
|
export function createUploadCommand() {
|
|
44
46
|
return new Command('upload')
|
|
45
47
|
.description('Upload processor to Sentio')
|
|
46
|
-
.option(
|
|
48
|
+
.option(
|
|
49
|
+
'--owner <owner>',
|
|
50
|
+
"(Optional) Project owner. Platform mode: project owner username. --no-platform mode: on-chain owner address (0x...) — $PRIVATE_KEY then signs as an operator on the owner's behalf, requires Permissions.addOperator beforehand."
|
|
51
|
+
)
|
|
47
52
|
.option('--name <name>', '(Optional) Override Project name')
|
|
48
53
|
.option(
|
|
49
54
|
'--continue-from <version>',
|
|
@@ -102,21 +107,53 @@ async function runNoPlatformUpload(
|
|
|
102
107
|
const network = options.sentioNetwork || 'testnet'
|
|
103
108
|
const networkConfig = getSentioNetworkConfig(network)
|
|
104
109
|
|
|
105
|
-
// Step 1: Require PRIVATE_KEY
|
|
110
|
+
// Step 1: Require PRIVATE_KEY (operator key when --owner is set, otherwise the owner's own key)
|
|
106
111
|
const privateKey = requirePrivateKey()
|
|
107
112
|
const wallet = getWalletFromPrivateKey(privateKey)
|
|
108
113
|
const walletAddress = wallet.address
|
|
109
114
|
|
|
115
|
+
// Operator mode: --owner is the on-chain owner address; PRIVATE_KEY signs as operator on its behalf.
|
|
116
|
+
let ownerOverride: string | undefined
|
|
117
|
+
if (options.owner) {
|
|
118
|
+
if (!ethers.isAddress(options.owner)) {
|
|
119
|
+
console.error(chalk.red(`Invalid --owner: "${options.owner}" is not a valid 0x-prefixed Ethereum address.`))
|
|
120
|
+
process.exit(1)
|
|
121
|
+
}
|
|
122
|
+
ownerOverride = ethers.getAddress(options.owner)
|
|
123
|
+
}
|
|
124
|
+
const effectiveOwner = ownerOverride ?? walletAddress
|
|
125
|
+
|
|
110
126
|
// Step 2: Resolve contract addresses via AddressBook
|
|
111
127
|
console.log(chalk.blue('Resolving contract addresses from AddressBook...'))
|
|
112
128
|
const addresses = await resolveNetworkAddresses(networkConfig)
|
|
113
129
|
|
|
114
|
-
// Step
|
|
130
|
+
// Step 2.5: Pre-flight check that operator is registered for this owner.
|
|
131
|
+
if (ownerOverride) {
|
|
132
|
+
const isOp = await isOperatorOnChain(networkConfig, addresses, ownerOverride, walletAddress)
|
|
133
|
+
if (!isOp) {
|
|
134
|
+
console.error(
|
|
135
|
+
chalk.red(
|
|
136
|
+
`Operator ${walletAddress} is not authorized for owner ${ownerOverride}.\n` +
|
|
137
|
+
`The owner must call Permissions.addOperator(${walletAddress}) first.`
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
process.exit(1)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Step 3: Check ST balance and Billing balance (always against the owner — operator just submits txs)
|
|
115
145
|
const [stBalance, billingBalance] = await Promise.all([
|
|
116
|
-
checkSTBalance(networkConfig, addresses,
|
|
117
|
-
checkBillingBalance(networkConfig, addresses,
|
|
146
|
+
checkSTBalance(networkConfig, addresses, effectiveOwner),
|
|
147
|
+
checkBillingBalance(networkConfig, addresses, effectiveOwner)
|
|
118
148
|
])
|
|
119
|
-
const confirmed = await confirmNoPlatformUpload(
|
|
149
|
+
const confirmed = await confirmNoPlatformUpload(
|
|
150
|
+
walletAddress,
|
|
151
|
+
stBalance,
|
|
152
|
+
billingBalance,
|
|
153
|
+
addresses,
|
|
154
|
+
networkConfig,
|
|
155
|
+
ownerOverride
|
|
156
|
+
)
|
|
120
157
|
if (!confirmed) {
|
|
121
158
|
console.log('Upload cancelled.')
|
|
122
159
|
process.exit(0)
|
|
@@ -179,11 +216,13 @@ async function runNoPlatformUpload(
|
|
|
179
216
|
|
|
180
217
|
if (existingProcessor) {
|
|
181
218
|
const existingOwner = existingProcessor.owner.toLowerCase()
|
|
182
|
-
const
|
|
219
|
+
const expectedOwner = effectiveOwner.toLowerCase()
|
|
183
220
|
|
|
184
|
-
if (existingOwner ===
|
|
185
|
-
// Same owner — prompt to replace
|
|
186
|
-
|
|
221
|
+
if (existingOwner === expectedOwner) {
|
|
222
|
+
// Same owner — prompt to replace. In operator mode the operator is allowed to
|
|
223
|
+
// stop+delete via the contract's isProcessorAdmin check.
|
|
224
|
+
const ownedBy = ownerOverride ? `owned by ${ownerOverride}` : 'owned by you'
|
|
225
|
+
console.log(chalk.yellow(`Processor "${processorId}" already exists (${ownedBy}).`))
|
|
187
226
|
const shouldReplace = await confirm(`Replace existing processor "${processorId}"?`)
|
|
188
227
|
if (!shouldReplace) {
|
|
189
228
|
console.log('Upload cancelled.')
|
|
@@ -194,9 +233,10 @@ async function runNoPlatformUpload(
|
|
|
194
233
|
await deleteProcessorOnChain(networkConfig, addresses, wallet, processorId)
|
|
195
234
|
} else {
|
|
196
235
|
// Different owner — prompt to rename
|
|
236
|
+
const expectedLabel = ownerOverride ? `expected owner ${ownerOverride}` : `your wallet ${wallet.address}`
|
|
197
237
|
console.log(
|
|
198
238
|
chalk.yellow(
|
|
199
|
-
`Processor "${processorId}" already exists and is owned by ${existingProcessor.owner} (not
|
|
239
|
+
`Processor "${processorId}" already exists and is owned by ${existingProcessor.owner} (not ${expectedLabel}).`
|
|
200
240
|
)
|
|
201
241
|
)
|
|
202
242
|
const randomSuffix = Math.random().toString(36).substring(2, 8)
|
|
@@ -215,7 +255,16 @@ async function runNoPlatformUpload(
|
|
|
215
255
|
}
|
|
216
256
|
|
|
217
257
|
// Step 8: Create processor on-chain
|
|
218
|
-
await createProcessorOnChain(
|
|
258
|
+
await createProcessorOnChain(
|
|
259
|
+
networkConfig,
|
|
260
|
+
addresses,
|
|
261
|
+
wallet,
|
|
262
|
+
processorId,
|
|
263
|
+
cid,
|
|
264
|
+
requiredChainIds,
|
|
265
|
+
sdkVersion,
|
|
266
|
+
ownerOverride
|
|
267
|
+
)
|
|
219
268
|
|
|
220
269
|
// Step 9: Start processor on-chain
|
|
221
270
|
await startProcessorOnChain(networkConfig, addresses, wallet, processorId)
|
package/src/network.ts
CHANGED
|
@@ -40,7 +40,7 @@ const ADDRESS_BOOK_ABI = [
|
|
|
40
40
|
'function getAddress(bytes32 id) view returns (address)'
|
|
41
41
|
]
|
|
42
42
|
|
|
43
|
-
// ProcessorRegistry: createProcessor, getProcessor, deleteProcessor
|
|
43
|
+
// ProcessorRegistry: createProcessor (self + operator overloads), getProcessor, deleteProcessor
|
|
44
44
|
const PROCESSOR_REGISTRY_ABI = [
|
|
45
45
|
`function createProcessor(
|
|
46
46
|
string id,
|
|
@@ -48,8 +48,14 @@ const PROCESSOR_REGISTRY_ABI = [
|
|
|
48
48
|
tuple(string chainId, bool enableRpc, bool enableTrace)[] requireChains,
|
|
49
49
|
string sdkVersion
|
|
50
50
|
) returns (string)`,
|
|
51
|
+
`function createProcessor(
|
|
52
|
+
address owner,
|
|
53
|
+
string id,
|
|
54
|
+
tuple(uint8 sourceType, string ipfsCid) source,
|
|
55
|
+
tuple(string chainId, bool enableRpc, bool enableTrace)[] requireChains,
|
|
56
|
+
string sdkVersion
|
|
57
|
+
) returns (string)`,
|
|
51
58
|
'event ProcessorCreated(string indexed processorId)',
|
|
52
|
-
'function getAllocations(string processorId) view returns (tuple(uint256 indexerId, uint256 timestamp, bool indexerReady)[])',
|
|
53
59
|
`function getProcessor(string processorId) view returns (
|
|
54
60
|
tuple(
|
|
55
61
|
string id,
|
|
@@ -59,14 +65,24 @@ const PROCESSOR_REGISTRY_ABI = [
|
|
|
59
65
|
string sdkVersion,
|
|
60
66
|
tuple(string chainId, bool enableRpc, bool enableTrace)[] requireChains,
|
|
61
67
|
tuple(uint8 sourceType, string ipfsCid) source,
|
|
62
|
-
tuple(uint256 indexerId, uint256
|
|
68
|
+
tuple(uint256 indexerId, uint256 allocationTime, bool indexerReady, uint256 replicaIndex)[] allocations
|
|
63
69
|
)
|
|
64
70
|
)`,
|
|
65
71
|
'function deleteProcessor(string processorId)'
|
|
66
72
|
]
|
|
67
73
|
|
|
68
|
-
// Controller: startProcessor / stopProcessor
|
|
69
|
-
const CONTROLLER_ABI = [
|
|
74
|
+
// Controller: startProcessor / stopProcessor / getAllocations
|
|
75
|
+
const CONTROLLER_ABI = [
|
|
76
|
+
'function startProcessor(string processorId)',
|
|
77
|
+
'function stopProcessor(string processorId)',
|
|
78
|
+
'function getAllocations(string processorId) view returns (tuple(uint256 indexerId, uint256 allocationTime, bool indexerReady, uint256 replicaIndex)[])'
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
// Databases: getProcessorDatabases
|
|
82
|
+
const DATABASES_ABI = ['function getProcessorDatabases(string processorId) view returns (string[])']
|
|
83
|
+
|
|
84
|
+
// Permissions: isOperator (used to pre-check operator delegation before submitting tx)
|
|
85
|
+
const PERMISSIONS_ABI = ['function isOperator(address account, address operator) view returns (bool)']
|
|
70
86
|
|
|
71
87
|
// ERC20: balanceOf + approve
|
|
72
88
|
const ERC20_ABI = [
|
|
@@ -84,7 +100,9 @@ const ADDRESS_BOOK_KEYS = {
|
|
|
84
100
|
processorRegistry: 'processor_registry',
|
|
85
101
|
controller: 'controller',
|
|
86
102
|
token: 'sentio_token',
|
|
87
|
-
billing: 'billing'
|
|
103
|
+
billing: 'billing',
|
|
104
|
+
databases: 'databases',
|
|
105
|
+
permissions: 'permissions'
|
|
88
106
|
} as const
|
|
89
107
|
|
|
90
108
|
interface ResolvedAddresses {
|
|
@@ -93,6 +111,8 @@ interface ResolvedAddresses {
|
|
|
93
111
|
controller: string
|
|
94
112
|
token: string
|
|
95
113
|
billing: string
|
|
114
|
+
databases: string
|
|
115
|
+
permissions: string
|
|
96
116
|
}
|
|
97
117
|
|
|
98
118
|
let cachedAddresses: ResolvedAddresses | undefined
|
|
@@ -122,19 +142,31 @@ export async function resolveNetworkAddresses(config: SentioNetworkConfig): Prom
|
|
|
122
142
|
}
|
|
123
143
|
}
|
|
124
144
|
|
|
125
|
-
const [processorRegistry, controller, token, billing] = await Promise.all([
|
|
145
|
+
const [processorRegistry, controller, token, billing, databases, permissions] = await Promise.all([
|
|
126
146
|
resolveAddress(ADDRESS_BOOK_KEYS.processorRegistry),
|
|
127
147
|
resolveAddress(ADDRESS_BOOK_KEYS.controller),
|
|
128
148
|
resolveAddress(ADDRESS_BOOK_KEYS.token),
|
|
129
|
-
resolveAddress(ADDRESS_BOOK_KEYS.billing)
|
|
149
|
+
resolveAddress(ADDRESS_BOOK_KEYS.billing),
|
|
150
|
+
resolveAddress(ADDRESS_BOOK_KEYS.databases),
|
|
151
|
+
resolveAddress(ADDRESS_BOOK_KEYS.permissions)
|
|
130
152
|
])
|
|
131
153
|
|
|
132
154
|
console.log(chalk.gray(`ProcessorRegistry: ${processorRegistry}`))
|
|
133
155
|
console.log(chalk.gray(`Controller: ${controller}`))
|
|
134
156
|
console.log(chalk.gray(`ST Token: ${token}`))
|
|
135
157
|
console.log(chalk.gray(`Billing: ${billing}`))
|
|
136
|
-
|
|
137
|
-
|
|
158
|
+
console.log(chalk.gray(`Databases: ${databases}`))
|
|
159
|
+
console.log(chalk.gray(`Permissions: ${permissions}`))
|
|
160
|
+
|
|
161
|
+
cachedAddresses = {
|
|
162
|
+
addressBook: addressBookAddr,
|
|
163
|
+
processorRegistry,
|
|
164
|
+
controller,
|
|
165
|
+
token,
|
|
166
|
+
billing,
|
|
167
|
+
databases,
|
|
168
|
+
permissions
|
|
169
|
+
}
|
|
138
170
|
return cachedAddresses
|
|
139
171
|
}
|
|
140
172
|
|
|
@@ -186,6 +218,17 @@ export async function checkBillingBalance(
|
|
|
186
218
|
}
|
|
187
219
|
}
|
|
188
220
|
|
|
221
|
+
export async function isOperatorOnChain(
|
|
222
|
+
config: SentioNetworkConfig,
|
|
223
|
+
addresses: ResolvedAddresses,
|
|
224
|
+
owner: string,
|
|
225
|
+
operator: string
|
|
226
|
+
): Promise<boolean> {
|
|
227
|
+
const provider = new ethers.JsonRpcProvider(config.rpcUrl)
|
|
228
|
+
const permissions = new ethers.Contract(addresses.permissions, PERMISSIONS_ABI, provider)
|
|
229
|
+
return await permissions.isOperator(owner, operator)
|
|
230
|
+
}
|
|
231
|
+
|
|
189
232
|
// --- IPFS Upload ---
|
|
190
233
|
|
|
191
234
|
export async function uploadToIPFS(fileBuffer: Buffer, ipfsUrl: string): Promise<string> {
|
|
@@ -207,6 +250,37 @@ export async function uploadToIPFS(fileBuffer: Buffer, ipfsUrl: string): Promise
|
|
|
207
250
|
return result.Hash
|
|
208
251
|
}
|
|
209
252
|
|
|
253
|
+
// --- Tx Helpers ---
|
|
254
|
+
|
|
255
|
+
async function submitAndWait(
|
|
256
|
+
explorerUrl: string,
|
|
257
|
+
label: string,
|
|
258
|
+
txPromise: Promise<ethers.TransactionResponse>
|
|
259
|
+
): Promise<ethers.TransactionResponse> {
|
|
260
|
+
let tx: ethers.TransactionResponse
|
|
261
|
+
try {
|
|
262
|
+
tx = await txPromise
|
|
263
|
+
} catch (err: any) {
|
|
264
|
+
throw new Error(`${label} failed to submit: ${err.shortMessage ?? err.message}`)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log(chalk.gray(` Tx hash: ${explorerUrl}/tx/${tx.hash}`))
|
|
268
|
+
console.log(chalk.blue('Waiting for confirmation...'))
|
|
269
|
+
|
|
270
|
+
let receipt: ethers.TransactionReceipt | null
|
|
271
|
+
try {
|
|
272
|
+
receipt = await tx.wait()
|
|
273
|
+
} catch (err: any) {
|
|
274
|
+
throw new Error(`${label} failed: ${err.shortMessage ?? err.message}. Tx: ${explorerUrl}/tx/${tx.hash}`)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!receipt || receipt.status === 0) {
|
|
278
|
+
throw new Error(`${label} transaction reverted. Tx: ${explorerUrl}/tx/${tx.hash}`)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return tx
|
|
282
|
+
}
|
|
283
|
+
|
|
210
284
|
// --- Contract Interactions ---
|
|
211
285
|
|
|
212
286
|
export interface OnChainProcessor {
|
|
@@ -260,17 +334,45 @@ export async function deleteProcessorOnChain(
|
|
|
260
334
|
const registry = new ethers.Contract(addresses.processorRegistry, PROCESSOR_REGISTRY_ABI, signer)
|
|
261
335
|
|
|
262
336
|
console.log(chalk.blue('Deleting existing processor on-chain...'))
|
|
263
|
-
const tx = await registry.deleteProcessor(processorId)
|
|
264
|
-
console.log(chalk.
|
|
265
|
-
|
|
337
|
+
const tx = await submitAndWait(config.explorerUrl, 'deleteProcessor', registry.deleteProcessor(processorId))
|
|
338
|
+
console.log(chalk.green(`Processor deleted. Tx: ${config.explorerUrl}/tx/${tx.hash}`))
|
|
339
|
+
await waitForProcessorDatabaseCleanup(addresses, processorId, provider)
|
|
340
|
+
|
|
341
|
+
return tx.hash
|
|
342
|
+
}
|
|
266
343
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
344
|
+
async function waitForProcessorDatabaseCleanup(
|
|
345
|
+
addresses: ResolvedAddresses,
|
|
346
|
+
processorId: string,
|
|
347
|
+
provider: ethers.JsonRpcProvider,
|
|
348
|
+
timeoutMs = 120_000,
|
|
349
|
+
pollIntervalMs = 5_000
|
|
350
|
+
): Promise<void> {
|
|
351
|
+
const databases = new ethers.Contract(addresses.databases, DATABASES_ABI, provider)
|
|
352
|
+
|
|
353
|
+
// getProcessorDatabases has no active check — returns all db IDs still in storage.
|
|
354
|
+
// Only when this returns empty is it safe to recreate databases with the same IDs.
|
|
355
|
+
const dbIds: string[] = await databases.getProcessorDatabases(processorId)
|
|
356
|
+
if (dbIds.length === 0) {
|
|
357
|
+
return
|
|
270
358
|
}
|
|
271
359
|
|
|
272
|
-
console.log(chalk.
|
|
273
|
-
|
|
360
|
+
console.log(chalk.blue(`Waiting for ${dbIds.length} replica database(s) to be cleaned up...`))
|
|
361
|
+
|
|
362
|
+
const deadline = Date.now() + timeoutMs
|
|
363
|
+
while (Date.now() < deadline) {
|
|
364
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs))
|
|
365
|
+
|
|
366
|
+
const remaining: string[] = await databases.getProcessorDatabases(processorId)
|
|
367
|
+
if (remaining.length === 0) {
|
|
368
|
+
console.log(chalk.green('All replica databases cleaned up.'))
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
console.log(chalk.gray(` ${remaining.length} replica database(s) still pending cleanup...`))
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
throw new Error(`Timeout: processor databases not cleaned up within ${timeoutMs / 1000}s`)
|
|
274
376
|
}
|
|
275
377
|
|
|
276
378
|
export async function createProcessorOnChain(
|
|
@@ -280,7 +382,8 @@ export async function createProcessorOnChain(
|
|
|
280
382
|
processorId: string,
|
|
281
383
|
ipfsCid: string,
|
|
282
384
|
requiredChainIds: string[],
|
|
283
|
-
sdkVersion: string
|
|
385
|
+
sdkVersion: string,
|
|
386
|
+
ownerOverride?: string
|
|
284
387
|
): Promise<string> {
|
|
285
388
|
const provider = new ethers.JsonRpcProvider(config.rpcUrl)
|
|
286
389
|
const signer = wallet.connect(provider)
|
|
@@ -303,16 +406,26 @@ export async function createProcessorOnChain(
|
|
|
303
406
|
console.log(chalk.gray(` IPFS CID: ${ipfsCid}`))
|
|
304
407
|
console.log(chalk.gray(` Chains: ${requiredChainIds.join(', ')}`))
|
|
305
408
|
console.log(chalk.gray(` SDK Version: ${sdkVersion}`))
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
console.log(chalk.gray(` Tx hash: ${tx.hash}`))
|
|
309
|
-
console.log(chalk.blue('Waiting for confirmation...'))
|
|
310
|
-
|
|
311
|
-
const receipt = await tx.wait()
|
|
312
|
-
if (receipt.status === 0) {
|
|
313
|
-
throw new Error(`createProcessor transaction failed. Tx: ${config.explorerUrl}/tx/${tx.hash}`)
|
|
409
|
+
if (ownerOverride) {
|
|
410
|
+
console.log(chalk.gray(` Owner: ${ownerOverride} (operator: ${wallet.address})`))
|
|
314
411
|
}
|
|
315
412
|
|
|
413
|
+
// ethers v6 disambiguates overloads by full signature when both are present.
|
|
414
|
+
const txPromise: Promise<ethers.TransactionResponse> = ownerOverride
|
|
415
|
+
? registry['createProcessor(address,string,(uint8,string),(string,bool,bool)[],string)'](
|
|
416
|
+
ownerOverride,
|
|
417
|
+
processorId,
|
|
418
|
+
source,
|
|
419
|
+
requireChains,
|
|
420
|
+
sdkVersion
|
|
421
|
+
)
|
|
422
|
+
: registry['createProcessor(string,(uint8,string),(string,bool,bool)[],string)'](
|
|
423
|
+
processorId,
|
|
424
|
+
source,
|
|
425
|
+
requireChains,
|
|
426
|
+
sdkVersion
|
|
427
|
+
)
|
|
428
|
+
const tx = await submitAndWait(config.explorerUrl, 'createProcessor', txPromise)
|
|
316
429
|
console.log(chalk.green(`Processor created. Tx: ${config.explorerUrl}/tx/${tx.hash}`))
|
|
317
430
|
return tx.hash
|
|
318
431
|
}
|
|
@@ -329,15 +442,7 @@ export async function startProcessorOnChain(
|
|
|
329
442
|
const controller = new ethers.Contract(addresses.controller, CONTROLLER_ABI, signer)
|
|
330
443
|
|
|
331
444
|
console.log(chalk.blue('Starting processor on-chain...'))
|
|
332
|
-
const tx = await controller.startProcessor(processorId)
|
|
333
|
-
console.log(chalk.gray(` Tx hash: ${tx.hash}`))
|
|
334
|
-
console.log(chalk.blue('Waiting for confirmation...'))
|
|
335
|
-
|
|
336
|
-
const receipt = await tx.wait()
|
|
337
|
-
if (receipt.status === 0) {
|
|
338
|
-
throw new Error(`startProcessor transaction failed. Tx: ${config.explorerUrl}/tx/${tx.hash}`)
|
|
339
|
-
}
|
|
340
|
-
|
|
445
|
+
const tx = await submitAndWait(config.explorerUrl, 'startProcessor', controller.startProcessor(processorId))
|
|
341
446
|
console.log(chalk.green(`Processor started. Tx: ${config.explorerUrl}/tx/${tx.hash}`))
|
|
342
447
|
return tx.hash
|
|
343
448
|
}
|
|
@@ -353,16 +458,15 @@ export async function stopProcessorOnChain(
|
|
|
353
458
|
|
|
354
459
|
const controller = new ethers.Contract(addresses.controller, CONTROLLER_ABI, signer)
|
|
355
460
|
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const receipt = await tx.wait()
|
|
362
|
-
if (receipt.status === 0) {
|
|
363
|
-
throw new Error(`stopProcessor transaction failed. Tx: ${config.explorerUrl}/tx/${tx.hash}`)
|
|
461
|
+
// stopProcessor reverts with ProcessorNotAllocated if the processor has no active allocations
|
|
462
|
+
const allocations: unknown[] = await controller.getAllocations(processorId)
|
|
463
|
+
if (allocations.length === 0) {
|
|
464
|
+
console.log(chalk.gray('Processor has no allocations, skipping stopProcessor.'))
|
|
465
|
+
return ''
|
|
364
466
|
}
|
|
365
467
|
|
|
468
|
+
console.log(chalk.blue('Stopping processor on-chain...'))
|
|
469
|
+
const tx = await submitAndWait(config.explorerUrl, 'stopProcessor', controller.stopProcessor(processorId))
|
|
366
470
|
console.log(chalk.green(`Processor stopped. Tx: ${config.explorerUrl}/tx/${tx.hash}`))
|
|
367
471
|
return tx.hash
|
|
368
472
|
}
|
|
@@ -374,24 +478,34 @@ export async function confirmNoPlatformUpload(
|
|
|
374
478
|
stBalance: bigint,
|
|
375
479
|
billingBalance: bigint,
|
|
376
480
|
addresses: ResolvedAddresses,
|
|
377
|
-
networkConfig: SentioNetworkConfig
|
|
481
|
+
networkConfig: SentioNetworkConfig,
|
|
482
|
+
ownerOverride?: string
|
|
378
483
|
): Promise<boolean> {
|
|
379
484
|
const formattedST = ethers.formatEther(stBalance)
|
|
380
485
|
const formattedBilling = ethers.formatEther(billingBalance)
|
|
381
486
|
console.log()
|
|
382
487
|
console.log(chalk.blue('=== Sentio Network Direct Upload (No Platform) ==='))
|
|
383
|
-
|
|
488
|
+
if (ownerOverride) {
|
|
489
|
+
console.log(chalk.white(` Owner: ${ownerOverride}`))
|
|
490
|
+
console.log(chalk.white(` Operator: ${walletAddress}`))
|
|
491
|
+
} else {
|
|
492
|
+
console.log(chalk.white(` Address: ${walletAddress}`))
|
|
493
|
+
}
|
|
384
494
|
console.log(chalk.white(` ST Balance: ${formattedST} ST`))
|
|
385
495
|
console.log(chalk.white(` Billing Balance: ${formattedBilling} ST`))
|
|
386
496
|
|
|
387
497
|
if (billingBalance === 0n) {
|
|
498
|
+
const who = ownerOverride ? `the owner (${ownerOverride})` : 'you'
|
|
499
|
+
const keyHint = ownerOverride ? "<owner's private key>" : '$PRIVATE_KEY'
|
|
388
500
|
console.log()
|
|
389
501
|
console.log(
|
|
390
502
|
chalk.yellow(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
`
|
|
394
|
-
`
|
|
503
|
+
` ⚠ ${ownerOverride ? "Owner's" : 'Your'} Billing balance is 0. Indexing fees are charged from the Billing contract.\n` +
|
|
504
|
+
` ${who} must deposit ST tokens into the Billing contract before the processor can run.\n` +
|
|
505
|
+
` Example (replace 100 with your desired ST amount):\n` +
|
|
506
|
+
` export AMOUNT=$(cast to-wei 100 ether)\n` +
|
|
507
|
+
` cast send ${addresses.token} "approve(address,uint256)" ${addresses.billing} $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key ${keyHint}\n` +
|
|
508
|
+
` cast send ${addresses.billing} "deposit(uint256)" $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key ${keyHint}`
|
|
395
509
|
)
|
|
396
510
|
)
|
|
397
511
|
}
|