@sentio/cli 3.6.3-rc.1 → 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 +108 -24
- package/package.json +1 -1
- package/src/commands/upload.ts +61 -12
- package/src/network.ts +75 -19
package/lib/index.js
CHANGED
|
@@ -139214,6 +139214,13 @@ 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
139225
|
`function getProcessor(string processorId) view returns (
|
|
139219
139226
|
tuple(
|
|
@@ -139235,6 +139242,7 @@ var CONTROLLER_ABI = [
|
|
|
139235
139242
|
"function getAllocations(string processorId) view returns (tuple(uint256 indexerId, uint256 allocationTime, bool indexerReady, uint256 replicaIndex)[])"
|
|
139236
139243
|
];
|
|
139237
139244
|
var DATABASES_ABI = ["function getProcessorDatabases(string processorId) view returns (string[])"];
|
|
139245
|
+
var PERMISSIONS_ABI = ["function isOperator(address account, address operator) view returns (bool)"];
|
|
139238
139246
|
var ERC20_ABI = [
|
|
139239
139247
|
"function balanceOf(address account) view returns (uint256)",
|
|
139240
139248
|
"function approve(address spender, uint256 amount) returns (bool)"
|
|
@@ -139245,7 +139253,8 @@ var ADDRESS_BOOK_KEYS = {
|
|
|
139245
139253
|
controller: "controller",
|
|
139246
139254
|
token: "sentio_token",
|
|
139247
139255
|
billing: "billing",
|
|
139248
|
-
databases: "databases"
|
|
139256
|
+
databases: "databases",
|
|
139257
|
+
permissions: "permissions"
|
|
139249
139258
|
};
|
|
139250
139259
|
var cachedAddresses;
|
|
139251
139260
|
async function resolveNetworkAddresses(config) {
|
|
@@ -139266,19 +139275,29 @@ async function resolveNetworkAddresses(config) {
|
|
|
139266
139275
|
}
|
|
139267
139276
|
}
|
|
139268
139277
|
};
|
|
139269
|
-
const [processorRegistry, controller, token, billing, databases] = await Promise.all([
|
|
139278
|
+
const [processorRegistry, controller, token, billing, databases, permissions] = await Promise.all([
|
|
139270
139279
|
resolveAddress2(ADDRESS_BOOK_KEYS.processorRegistry),
|
|
139271
139280
|
resolveAddress2(ADDRESS_BOOK_KEYS.controller),
|
|
139272
139281
|
resolveAddress2(ADDRESS_BOOK_KEYS.token),
|
|
139273
139282
|
resolveAddress2(ADDRESS_BOOK_KEYS.billing),
|
|
139274
|
-
resolveAddress2(ADDRESS_BOOK_KEYS.databases)
|
|
139283
|
+
resolveAddress2(ADDRESS_BOOK_KEYS.databases),
|
|
139284
|
+
resolveAddress2(ADDRESS_BOOK_KEYS.permissions)
|
|
139275
139285
|
]);
|
|
139276
139286
|
console.log(source_default.gray(`ProcessorRegistry: ${processorRegistry}`));
|
|
139277
139287
|
console.log(source_default.gray(`Controller: ${controller}`));
|
|
139278
139288
|
console.log(source_default.gray(`ST Token: ${token}`));
|
|
139279
139289
|
console.log(source_default.gray(`Billing: ${billing}`));
|
|
139280
139290
|
console.log(source_default.gray(`Databases: ${databases}`));
|
|
139281
|
-
|
|
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
|
+
};
|
|
139282
139301
|
return cachedAddresses;
|
|
139283
139302
|
}
|
|
139284
139303
|
function getWalletFromPrivateKey(privateKey) {
|
|
@@ -139314,6 +139333,11 @@ async function checkBillingBalance(config, addresses, walletAddress) {
|
|
|
139314
139333
|
return 0n;
|
|
139315
139334
|
}
|
|
139316
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
|
+
}
|
|
139317
139341
|
async function uploadToIPFS(fileBuffer, ipfsUrl) {
|
|
139318
139342
|
const formData = new FormData();
|
|
139319
139343
|
const blob = new Blob([fileBuffer], { type: "application/octet-stream" });
|
|
@@ -139398,7 +139422,7 @@ async function waitForProcessorDatabaseCleanup(addresses, processorId, provider,
|
|
|
139398
139422
|
}
|
|
139399
139423
|
throw new Error(`Timeout: processor databases not cleaned up within ${timeoutMs / 1e3}s`);
|
|
139400
139424
|
}
|
|
139401
|
-
async function createProcessorOnChain(config, addresses, wallet, processorId, ipfsCid, requiredChainIds, sdkVersion) {
|
|
139425
|
+
async function createProcessorOnChain(config, addresses, wallet, processorId, ipfsCid, requiredChainIds, sdkVersion, ownerOverride) {
|
|
139402
139426
|
const provider = new ethers_exports.JsonRpcProvider(config.rpcUrl);
|
|
139403
139427
|
const signer = wallet.connect(provider);
|
|
139404
139428
|
const registry = new ethers_exports.Contract(addresses.processorRegistry, PROCESSOR_REGISTRY_ABI, signer);
|
|
@@ -139417,11 +139441,22 @@ async function createProcessorOnChain(config, addresses, wallet, processorId, ip
|
|
|
139417
139441
|
console.log(source_default.gray(` IPFS CID: ${ipfsCid}`));
|
|
139418
139442
|
console.log(source_default.gray(` Chains: ${requiredChainIds.join(", ")}`));
|
|
139419
139443
|
console.log(source_default.gray(` SDK Version: ${sdkVersion}`));
|
|
139420
|
-
|
|
139421
|
-
|
|
139422
|
-
|
|
139423
|
-
|
|
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
|
|
139424
139458
|
);
|
|
139459
|
+
const tx = await submitAndWait(config.explorerUrl, "createProcessor", txPromise);
|
|
139425
139460
|
console.log(source_default.green(`Processor created. Tx: ${config.explorerUrl}/tx/${tx.hash}`));
|
|
139426
139461
|
return tx.hash;
|
|
139427
139462
|
}
|
|
@@ -139448,24 +139483,31 @@ async function stopProcessorOnChain(config, addresses, wallet, processorId) {
|
|
|
139448
139483
|
console.log(source_default.green(`Processor stopped. Tx: ${config.explorerUrl}/tx/${tx.hash}`));
|
|
139449
139484
|
return tx.hash;
|
|
139450
139485
|
}
|
|
139451
|
-
async function confirmNoPlatformUpload(walletAddress, stBalance, billingBalance, addresses, networkConfig) {
|
|
139486
|
+
async function confirmNoPlatformUpload(walletAddress, stBalance, billingBalance, addresses, networkConfig, ownerOverride) {
|
|
139452
139487
|
const formattedST = ethers_exports.formatEther(stBalance);
|
|
139453
139488
|
const formattedBilling = ethers_exports.formatEther(billingBalance);
|
|
139454
139489
|
console.log();
|
|
139455
139490
|
console.log(source_default.blue("=== Sentio Network Direct Upload (No Platform) ==="));
|
|
139456
|
-
|
|
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
|
+
}
|
|
139457
139497
|
console.log(source_default.white(` ST Balance: ${formattedST} ST`));
|
|
139458
139498
|
console.log(source_default.white(` Billing Balance: ${formattedBilling} ST`));
|
|
139459
139499
|
if (billingBalance === 0n) {
|
|
139500
|
+
const who = ownerOverride ? `the owner (${ownerOverride})` : "you";
|
|
139501
|
+
const keyHint = ownerOverride ? "<owner's private key>" : "$PRIVATE_KEY";
|
|
139460
139502
|
console.log();
|
|
139461
139503
|
console.log(
|
|
139462
139504
|
source_default.yellow(
|
|
139463
|
-
` \u26A0 Your Billing balance is 0. Indexing fees are charged from the Billing contract.
|
|
139464
|
-
|
|
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.
|
|
139465
139507
|
Example (replace 100 with your desired ST amount):
|
|
139466
139508
|
export AMOUNT=$(cast to-wei 100 ether)
|
|
139467
|
-
cast send ${addresses.token} "approve(address,uint256)" ${addresses.billing} $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key $
|
|
139468
|
-
cast send ${addresses.billing} "deposit(uint256)" $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key $
|
|
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}`
|
|
139469
139511
|
)
|
|
139470
139512
|
);
|
|
139471
139513
|
}
|
|
@@ -139763,7 +139805,10 @@ function myParseInt(value, dummyPrevious) {
|
|
|
139763
139805
|
return parsedValue;
|
|
139764
139806
|
}
|
|
139765
139807
|
function createUploadCommand() {
|
|
139766
|
-
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(
|
|
139767
139812
|
"--continue-from <version>",
|
|
139768
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.",
|
|
139769
139814
|
myParseInt
|
|
@@ -139799,13 +139844,41 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139799
139844
|
const privateKey = requirePrivateKey();
|
|
139800
139845
|
const wallet = getWalletFromPrivateKey(privateKey);
|
|
139801
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;
|
|
139802
139856
|
console.log(source_default.blue("Resolving contract addresses from AddressBook..."));
|
|
139803
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
|
+
}
|
|
139804
139870
|
const [stBalance, billingBalance] = await Promise.all([
|
|
139805
|
-
checkSTBalance(networkConfig, addresses,
|
|
139806
|
-
checkBillingBalance(networkConfig, addresses,
|
|
139871
|
+
checkSTBalance(networkConfig, addresses, effectiveOwner),
|
|
139872
|
+
checkBillingBalance(networkConfig, addresses, effectiveOwner)
|
|
139807
139873
|
]);
|
|
139808
|
-
const confirmed = await confirmNoPlatformUpload(
|
|
139874
|
+
const confirmed = await confirmNoPlatformUpload(
|
|
139875
|
+
walletAddress,
|
|
139876
|
+
stBalance,
|
|
139877
|
+
billingBalance,
|
|
139878
|
+
addresses,
|
|
139879
|
+
networkConfig,
|
|
139880
|
+
ownerOverride
|
|
139881
|
+
);
|
|
139809
139882
|
if (!confirmed) {
|
|
139810
139883
|
console.log("Upload cancelled.");
|
|
139811
139884
|
process.exit(0);
|
|
@@ -139855,9 +139928,10 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139855
139928
|
const existingProcessor = await getProcessorOnChain(networkConfig, addresses, processorId);
|
|
139856
139929
|
if (existingProcessor) {
|
|
139857
139930
|
const existingOwner = existingProcessor.owner.toLowerCase();
|
|
139858
|
-
const
|
|
139859
|
-
if (existingOwner ===
|
|
139860
|
-
|
|
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}).`));
|
|
139861
139935
|
const shouldReplace = await confirm(`Replace existing processor "${processorId}"?`);
|
|
139862
139936
|
if (!shouldReplace) {
|
|
139863
139937
|
console.log("Upload cancelled.");
|
|
@@ -139866,9 +139940,10 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139866
139940
|
await stopProcessorOnChain(networkConfig, addresses, wallet, processorId);
|
|
139867
139941
|
await deleteProcessorOnChain(networkConfig, addresses, wallet, processorId);
|
|
139868
139942
|
} else {
|
|
139943
|
+
const expectedLabel = ownerOverride ? `expected owner ${ownerOverride}` : `your wallet ${wallet.address}`;
|
|
139869
139944
|
console.log(
|
|
139870
139945
|
source_default.yellow(
|
|
139871
|
-
`Processor "${processorId}" already exists and is owned by ${existingProcessor.owner} (not
|
|
139946
|
+
`Processor "${processorId}" already exists and is owned by ${existingProcessor.owner} (not ${expectedLabel}).`
|
|
139872
139947
|
)
|
|
139873
139948
|
);
|
|
139874
139949
|
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
@@ -139884,7 +139959,16 @@ async function runNoPlatformUpload(processorConfig, options) {
|
|
|
139884
139959
|
console.log(source_default.blue(`Using processor ID: ${processorId}`));
|
|
139885
139960
|
}
|
|
139886
139961
|
}
|
|
139887
|
-
await createProcessorOnChain(
|
|
139962
|
+
await createProcessorOnChain(
|
|
139963
|
+
networkConfig,
|
|
139964
|
+
addresses,
|
|
139965
|
+
wallet,
|
|
139966
|
+
processorId,
|
|
139967
|
+
cid,
|
|
139968
|
+
requiredChainIds,
|
|
139969
|
+
sdkVersion,
|
|
139970
|
+
ownerOverride
|
|
139971
|
+
);
|
|
139888
139972
|
await startProcessorOnChain(networkConfig, addresses, wallet, processorId);
|
|
139889
139973
|
console.log();
|
|
139890
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,6 +48,13 @@ 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
59
|
`function getProcessor(string processorId) view returns (
|
|
53
60
|
tuple(
|
|
@@ -74,6 +81,9 @@ const CONTROLLER_ABI = [
|
|
|
74
81
|
// Databases: getProcessorDatabases
|
|
75
82
|
const DATABASES_ABI = ['function getProcessorDatabases(string processorId) view returns (string[])']
|
|
76
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)']
|
|
86
|
+
|
|
77
87
|
// ERC20: balanceOf + approve
|
|
78
88
|
const ERC20_ABI = [
|
|
79
89
|
'function balanceOf(address account) view returns (uint256)',
|
|
@@ -91,7 +101,8 @@ const ADDRESS_BOOK_KEYS = {
|
|
|
91
101
|
controller: 'controller',
|
|
92
102
|
token: 'sentio_token',
|
|
93
103
|
billing: 'billing',
|
|
94
|
-
databases: 'databases'
|
|
104
|
+
databases: 'databases',
|
|
105
|
+
permissions: 'permissions'
|
|
95
106
|
} as const
|
|
96
107
|
|
|
97
108
|
interface ResolvedAddresses {
|
|
@@ -101,6 +112,7 @@ interface ResolvedAddresses {
|
|
|
101
112
|
token: string
|
|
102
113
|
billing: string
|
|
103
114
|
databases: string
|
|
115
|
+
permissions: string
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
let cachedAddresses: ResolvedAddresses | undefined
|
|
@@ -130,12 +142,13 @@ export async function resolveNetworkAddresses(config: SentioNetworkConfig): Prom
|
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
144
|
|
|
133
|
-
const [processorRegistry, controller, token, billing, databases] = await Promise.all([
|
|
145
|
+
const [processorRegistry, controller, token, billing, databases, permissions] = await Promise.all([
|
|
134
146
|
resolveAddress(ADDRESS_BOOK_KEYS.processorRegistry),
|
|
135
147
|
resolveAddress(ADDRESS_BOOK_KEYS.controller),
|
|
136
148
|
resolveAddress(ADDRESS_BOOK_KEYS.token),
|
|
137
149
|
resolveAddress(ADDRESS_BOOK_KEYS.billing),
|
|
138
|
-
resolveAddress(ADDRESS_BOOK_KEYS.databases)
|
|
150
|
+
resolveAddress(ADDRESS_BOOK_KEYS.databases),
|
|
151
|
+
resolveAddress(ADDRESS_BOOK_KEYS.permissions)
|
|
139
152
|
])
|
|
140
153
|
|
|
141
154
|
console.log(chalk.gray(`ProcessorRegistry: ${processorRegistry}`))
|
|
@@ -143,8 +156,17 @@ export async function resolveNetworkAddresses(config: SentioNetworkConfig): Prom
|
|
|
143
156
|
console.log(chalk.gray(`ST Token: ${token}`))
|
|
144
157
|
console.log(chalk.gray(`Billing: ${billing}`))
|
|
145
158
|
console.log(chalk.gray(`Databases: ${databases}`))
|
|
146
|
-
|
|
147
|
-
|
|
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
|
+
}
|
|
148
170
|
return cachedAddresses
|
|
149
171
|
}
|
|
150
172
|
|
|
@@ -196,6 +218,17 @@ export async function checkBillingBalance(
|
|
|
196
218
|
}
|
|
197
219
|
}
|
|
198
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
|
+
|
|
199
232
|
// --- IPFS Upload ---
|
|
200
233
|
|
|
201
234
|
export async function uploadToIPFS(fileBuffer: Buffer, ipfsUrl: string): Promise<string> {
|
|
@@ -349,7 +382,8 @@ export async function createProcessorOnChain(
|
|
|
349
382
|
processorId: string,
|
|
350
383
|
ipfsCid: string,
|
|
351
384
|
requiredChainIds: string[],
|
|
352
|
-
sdkVersion: string
|
|
385
|
+
sdkVersion: string,
|
|
386
|
+
ownerOverride?: string
|
|
353
387
|
): Promise<string> {
|
|
354
388
|
const provider = new ethers.JsonRpcProvider(config.rpcUrl)
|
|
355
389
|
const signer = wallet.connect(provider)
|
|
@@ -372,12 +406,26 @@ export async function createProcessorOnChain(
|
|
|
372
406
|
console.log(chalk.gray(` IPFS CID: ${ipfsCid}`))
|
|
373
407
|
console.log(chalk.gray(` Chains: ${requiredChainIds.join(', ')}`))
|
|
374
408
|
console.log(chalk.gray(` SDK Version: ${sdkVersion}`))
|
|
409
|
+
if (ownerOverride) {
|
|
410
|
+
console.log(chalk.gray(` Owner: ${ownerOverride} (operator: ${wallet.address})`))
|
|
411
|
+
}
|
|
375
412
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
'createProcessor'
|
|
379
|
-
|
|
380
|
-
|
|
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)
|
|
381
429
|
console.log(chalk.green(`Processor created. Tx: ${config.explorerUrl}/tx/${tx.hash}`))
|
|
382
430
|
return tx.hash
|
|
383
431
|
}
|
|
@@ -430,26 +478,34 @@ export async function confirmNoPlatformUpload(
|
|
|
430
478
|
stBalance: bigint,
|
|
431
479
|
billingBalance: bigint,
|
|
432
480
|
addresses: ResolvedAddresses,
|
|
433
|
-
networkConfig: SentioNetworkConfig
|
|
481
|
+
networkConfig: SentioNetworkConfig,
|
|
482
|
+
ownerOverride?: string
|
|
434
483
|
): Promise<boolean> {
|
|
435
484
|
const formattedST = ethers.formatEther(stBalance)
|
|
436
485
|
const formattedBilling = ethers.formatEther(billingBalance)
|
|
437
486
|
console.log()
|
|
438
487
|
console.log(chalk.blue('=== Sentio Network Direct Upload (No Platform) ==='))
|
|
439
|
-
|
|
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
|
+
}
|
|
440
494
|
console.log(chalk.white(` ST Balance: ${formattedST} ST`))
|
|
441
495
|
console.log(chalk.white(` Billing Balance: ${formattedBilling} ST`))
|
|
442
496
|
|
|
443
497
|
if (billingBalance === 0n) {
|
|
498
|
+
const who = ownerOverride ? `the owner (${ownerOverride})` : 'you'
|
|
499
|
+
const keyHint = ownerOverride ? "<owner's private key>" : '$PRIVATE_KEY'
|
|
444
500
|
console.log()
|
|
445
501
|
console.log(
|
|
446
502
|
chalk.yellow(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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` +
|
|
450
506
|
` export AMOUNT=$(cast to-wei 100 ether)\n` +
|
|
451
|
-
` cast send ${addresses.token} "approve(address,uint256)" ${addresses.billing} $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key $
|
|
452
|
-
` cast send ${addresses.billing} "deposit(uint256)" $AMOUNT --rpc-url ${networkConfig.rpcUrl} --private-key $
|
|
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}`
|
|
453
509
|
)
|
|
454
510
|
)
|
|
455
511
|
}
|