@layr-labs/ecloud-sdk 0.2.0-dev.3 → 0.2.1-dev
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/VERSION +2 -2
- package/dist/billing.cjs +34 -37
- package/dist/billing.cjs.map +1 -1
- package/dist/billing.d.cts +6 -3
- package/dist/billing.d.ts +6 -3
- package/dist/billing.js +511 -4
- package/dist/billing.js.map +1 -1
- package/dist/browser.cjs +2990 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +42 -0
- package/dist/browser.d.ts +42 -0
- package/dist/browser.js +2922 -0
- package/dist/browser.js.map +1 -0
- package/dist/{compute-CF2HOXed.d.ts → compute-B85ikS78.d.ts} +15 -96
- package/dist/{compute-CbmjA8kJ.d.cts → compute-CC0R7HEu.d.cts} +15 -96
- package/dist/compute.cjs +655 -841
- package/dist/compute.cjs.map +1 -1
- package/dist/compute.d.cts +2 -2
- package/dist/compute.d.ts +2 -2
- package/dist/compute.js +6797 -7
- package/dist/compute.js.map +1 -1
- package/dist/eip7702-CXCYfOnk.d.ts +400 -0
- package/dist/eip7702-DeqoCP5b.d.cts +400 -0
- package/dist/{index-D2QufVB9.d.ts → index-D5oW73Dx.d.cts} +105 -15
- package/dist/{index-D2QufVB9.d.cts → index-D5oW73Dx.d.ts} +105 -15
- package/dist/index.cjs +1404 -1554
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +123 -388
- package/dist/index.d.ts +123 -388
- package/dist/index.js +7632 -124
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/chunk-OUCNETHL.js +0 -434
- package/dist/chunk-OUCNETHL.js.map +0 -1
- package/dist/chunk-QN7KAUOB.js +0 -744
- package/dist/chunk-QN7KAUOB.js.map +0 -1
- package/dist/chunk-R44OVMCY.js +0 -6719
- package/dist/chunk-R44OVMCY.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
AuthRequiredError: () => AuthRequiredError,
|
|
34
34
|
BUILD_STATUS: () => BUILD_STATUS,
|
|
35
35
|
BadRequestError: () => BadRequestError,
|
|
36
|
+
BillingApiClient: () => BillingApiClient,
|
|
36
37
|
BuildError: () => BuildError,
|
|
37
38
|
BuildFailedError: () => BuildFailedError,
|
|
38
39
|
ConflictError: () => ConflictError,
|
|
@@ -58,6 +59,7 @@ __export(index_exports, {
|
|
|
58
59
|
createECloudClient: () => createECloudClient,
|
|
59
60
|
createMetricsContext: () => createMetricsContext,
|
|
60
61
|
createTelemetryClient: () => createTelemetryClient,
|
|
62
|
+
createViemClients: () => createClients,
|
|
61
63
|
deleteLegacyPrivateKey: () => deleteLegacyPrivateKey,
|
|
62
64
|
deletePrivateKey: () => deletePrivateKey,
|
|
63
65
|
emitMetrics: () => emitMetrics,
|
|
@@ -77,9 +79,11 @@ __export(index_exports, {
|
|
|
77
79
|
getAppLatestReleaseBlockNumbers: () => getAppLatestReleaseBlockNumbers,
|
|
78
80
|
getAvailableEnvironments: () => getAvailableEnvironments,
|
|
79
81
|
getAvailableTemplates: () => getAvailableTemplates,
|
|
82
|
+
getBillingEnvironmentConfig: () => getBillingEnvironmentConfig,
|
|
80
83
|
getBlockTimestamps: () => getBlockTimestamps,
|
|
81
84
|
getBuildType: () => getBuildType,
|
|
82
85
|
getCategoryDescriptions: () => getCategoryDescriptions,
|
|
86
|
+
getChainFromID: () => getChainFromID,
|
|
83
87
|
getCurrentInstanceType: () => getCurrentInstanceType,
|
|
84
88
|
getEnvironmentConfig: () => getEnvironmentConfig,
|
|
85
89
|
getLegacyKeys: () => getLegacyKeys,
|
|
@@ -117,7 +121,7 @@ __export(index_exports, {
|
|
|
117
121
|
validateInstanceTypeSKU: () => validateInstanceTypeSKU,
|
|
118
122
|
validateLogVisibility: () => validateLogVisibility,
|
|
119
123
|
validateLogsParams: () => validateLogsParams,
|
|
120
|
-
validatePrivateKey: () =>
|
|
124
|
+
validatePrivateKey: () => validatePrivateKey,
|
|
121
125
|
validatePrivateKeyFormat: () => validatePrivateKeyFormat,
|
|
122
126
|
validateResourceUsageMonitoring: () => validateResourceUsageMonitoring,
|
|
123
127
|
validateURL: () => validateURL,
|
|
@@ -130,118 +134,7 @@ __export(index_exports, {
|
|
|
130
134
|
module.exports = __toCommonJS(index_exports);
|
|
131
135
|
|
|
132
136
|
// src/client/modules/compute/app/index.ts
|
|
133
|
-
var
|
|
134
|
-
var import_accounts5 = require("viem/accounts");
|
|
135
|
-
|
|
136
|
-
// src/client/common/config/environment.ts
|
|
137
|
-
var SEPOLIA_CHAIN_ID = 11155111;
|
|
138
|
-
var MAINNET_CHAIN_ID = 1;
|
|
139
|
-
var CommonAddresses = {
|
|
140
|
-
ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
|
|
141
|
-
};
|
|
142
|
-
var ChainAddresses = {
|
|
143
|
-
[MAINNET_CHAIN_ID]: {
|
|
144
|
-
PermissionController: "0x25E5F8B1E7aDf44518d35D5B2271f114e081f0E5"
|
|
145
|
-
},
|
|
146
|
-
[SEPOLIA_CHAIN_ID]: {
|
|
147
|
-
PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
var BILLING_ENVIRONMENTS = {
|
|
151
|
-
dev: {
|
|
152
|
-
billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
|
|
153
|
-
},
|
|
154
|
-
prod: {
|
|
155
|
-
billingApiServerURL: "https://billingapi.eigencloud.xyz"
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
var ENVIRONMENTS = {
|
|
159
|
-
"sepolia-dev": {
|
|
160
|
-
name: "sepolia",
|
|
161
|
-
build: "dev",
|
|
162
|
-
appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
|
|
163
|
-
permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
|
|
164
|
-
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
165
|
-
kmsServerURL: "http://10.128.0.57:8080",
|
|
166
|
-
userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
|
|
167
|
-
defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
|
|
168
|
-
},
|
|
169
|
-
sepolia: {
|
|
170
|
-
name: "sepolia",
|
|
171
|
-
build: "prod",
|
|
172
|
-
appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
|
|
173
|
-
permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
|
|
174
|
-
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
175
|
-
kmsServerURL: "http://10.128.15.203:8080",
|
|
176
|
-
userApiServerURL: "https://userapi-compute-sepolia-prod.eigencloud.xyz",
|
|
177
|
-
defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
|
|
178
|
-
},
|
|
179
|
-
"mainnet-alpha": {
|
|
180
|
-
name: "mainnet-alpha",
|
|
181
|
-
build: "prod",
|
|
182
|
-
appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
|
|
183
|
-
permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
|
|
184
|
-
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
185
|
-
kmsServerURL: "http://10.128.0.2:8080",
|
|
186
|
-
userApiServerURL: "https://userapi-compute.eigencloud.xyz",
|
|
187
|
-
defaultRPCURL: "https://ethereum-rpc.publicnode.com"
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
var CHAIN_ID_TO_ENVIRONMENT = {
|
|
191
|
-
[SEPOLIA_CHAIN_ID.toString()]: "sepolia",
|
|
192
|
-
[MAINNET_CHAIN_ID.toString()]: "mainnet-alpha"
|
|
193
|
-
};
|
|
194
|
-
function getEnvironmentConfig(environment, chainID) {
|
|
195
|
-
const env = ENVIRONMENTS[environment];
|
|
196
|
-
if (!env) {
|
|
197
|
-
throw new Error(`Unknown environment: ${environment}`);
|
|
198
|
-
}
|
|
199
|
-
if (!isEnvironmentAvailable(environment)) {
|
|
200
|
-
throw new Error(
|
|
201
|
-
`Environment ${environment} is not available in this build type. Available environments: ${getAvailableEnvironments().join(", ")}`
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
if (chainID) {
|
|
205
|
-
const expectedEnv = CHAIN_ID_TO_ENVIRONMENT[chainID.toString()];
|
|
206
|
-
if (expectedEnv && expectedEnv !== environment) {
|
|
207
|
-
throw new Error(`Environment ${environment} does not match chain ID ${chainID}`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
const resolvedChainID = chainID || (environment === "sepolia" || environment === "sepolia-dev" ? SEPOLIA_CHAIN_ID : MAINNET_CHAIN_ID);
|
|
211
|
-
return {
|
|
212
|
-
...env,
|
|
213
|
-
chainID: BigInt(resolvedChainID)
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
function getBillingEnvironmentConfig(build) {
|
|
217
|
-
const config = BILLING_ENVIRONMENTS[build];
|
|
218
|
-
if (!config) {
|
|
219
|
-
throw new Error(`Unknown billing environment: ${build}`);
|
|
220
|
-
}
|
|
221
|
-
return config;
|
|
222
|
-
}
|
|
223
|
-
function getBuildType() {
|
|
224
|
-
const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
|
|
225
|
-
const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
|
|
226
|
-
const buildType = buildTimeType || runtimeType;
|
|
227
|
-
if (buildType === "dev") {
|
|
228
|
-
return "dev";
|
|
229
|
-
}
|
|
230
|
-
return "prod";
|
|
231
|
-
}
|
|
232
|
-
function getAvailableEnvironments() {
|
|
233
|
-
const buildType = getBuildType();
|
|
234
|
-
if (buildType === "dev") {
|
|
235
|
-
return ["sepolia-dev"];
|
|
236
|
-
}
|
|
237
|
-
return ["sepolia", "mainnet-alpha"];
|
|
238
|
-
}
|
|
239
|
-
function isEnvironmentAvailable(environment) {
|
|
240
|
-
return getAvailableEnvironments().includes(environment);
|
|
241
|
-
}
|
|
242
|
-
function isMainnet(environmentConfig) {
|
|
243
|
-
return environmentConfig.chainID === BigInt(MAINNET_CHAIN_ID);
|
|
244
|
-
}
|
|
137
|
+
var import_viem6 = require("viem");
|
|
245
138
|
|
|
246
139
|
// src/client/common/docker/build.ts
|
|
247
140
|
var child_process = __toESM(require("child_process"), 1);
|
|
@@ -1341,9 +1234,6 @@ function extractRegistryNameNoDocker(imageRef) {
|
|
|
1341
1234
|
return name;
|
|
1342
1235
|
}
|
|
1343
1236
|
|
|
1344
|
-
// src/client/common/contract/caller.ts
|
|
1345
|
-
var import_accounts2 = require("viem/accounts");
|
|
1346
|
-
|
|
1347
1237
|
// src/client/common/contract/eip7702.ts
|
|
1348
1238
|
var import_viem = require("viem");
|
|
1349
1239
|
|
|
@@ -2456,11 +2346,12 @@ async function executeBatch(options, logger) {
|
|
|
2456
2346
|
});
|
|
2457
2347
|
const chainId = await publicClient.getChainId();
|
|
2458
2348
|
const authorizationNonce = transactionNonce + 1;
|
|
2349
|
+
logger.debug("Using wallet client signing for EIP-7702 authorization");
|
|
2459
2350
|
const signedAuthorization = await walletClient.signAuthorization({
|
|
2460
|
-
account,
|
|
2351
|
+
account: account.address,
|
|
2461
2352
|
contractAddress: environmentConfig.erc7702DelegatorAddress,
|
|
2462
|
-
chainId
|
|
2463
|
-
nonce: authorizationNonce
|
|
2353
|
+
chainId,
|
|
2354
|
+
nonce: Number(authorizationNonce)
|
|
2464
2355
|
});
|
|
2465
2356
|
authorizationList = [signedAuthorization];
|
|
2466
2357
|
}
|
|
@@ -2515,78 +2406,31 @@ async function executeBatch(options, logger) {
|
|
|
2515
2406
|
}
|
|
2516
2407
|
|
|
2517
2408
|
// src/client/common/contract/caller.ts
|
|
2518
|
-
var
|
|
2519
|
-
var import_utils = require("viem/utils");
|
|
2520
|
-
var import_accounts3 = require("viem/accounts");
|
|
2521
|
-
|
|
2522
|
-
// src/client/common/utils/logger.ts
|
|
2523
|
-
var defaultLogger = {
|
|
2524
|
-
info: (...args) => console.info(...args),
|
|
2525
|
-
warn: (...args) => console.warn(...args),
|
|
2526
|
-
error: (...args) => console.error(...args),
|
|
2527
|
-
debug: (...args) => console.debug(...args)
|
|
2528
|
-
};
|
|
2529
|
-
var getLogger = (verbose) => ({
|
|
2530
|
-
info: (...args) => console.info(...args),
|
|
2531
|
-
warn: (...args) => console.warn(...args),
|
|
2532
|
-
error: (...args) => console.error(...args),
|
|
2533
|
-
debug: (...args) => verbose && console.debug(...args)
|
|
2534
|
-
});
|
|
2535
|
-
|
|
2536
|
-
// src/client/common/utils/userapi.ts
|
|
2537
|
-
var import_axios = __toESM(require("axios"), 1);
|
|
2538
|
-
var import_form_data = __toESM(require("form-data"), 1);
|
|
2539
|
-
var import_viem4 = require("viem");
|
|
2540
|
-
|
|
2541
|
-
// src/client/common/utils/auth.ts
|
|
2542
|
-
var import_viem2 = require("viem");
|
|
2543
|
-
var APP_CONTROLLER_ABI = (0, import_viem2.parseAbi)([
|
|
2544
|
-
"function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
|
|
2545
|
-
]);
|
|
2546
|
-
async function calculatePermissionSignature(options) {
|
|
2547
|
-
const { permission, expiry, appControllerAddress, publicClient, account } = options;
|
|
2548
|
-
const digest = await publicClient.readContract({
|
|
2549
|
-
address: appControllerAddress,
|
|
2550
|
-
abi: APP_CONTROLLER_ABI,
|
|
2551
|
-
functionName: "calculateApiPermissionDigestHash",
|
|
2552
|
-
args: [permission, expiry]
|
|
2553
|
-
});
|
|
2554
|
-
const signature = await account.signMessage({
|
|
2555
|
-
message: { raw: digest }
|
|
2556
|
-
});
|
|
2557
|
-
return { signature, digest };
|
|
2558
|
-
}
|
|
2559
|
-
async function calculateBillingAuthSignature(options) {
|
|
2560
|
-
const { account, product, expiry } = options;
|
|
2561
|
-
const signature = await account.signTypedData({
|
|
2562
|
-
domain: {
|
|
2563
|
-
name: "EigenCloud Billing API",
|
|
2564
|
-
version: "1"
|
|
2565
|
-
},
|
|
2566
|
-
types: {
|
|
2567
|
-
BillingAuth: [
|
|
2568
|
-
{ name: "product", type: "string" },
|
|
2569
|
-
{ name: "expiry", type: "uint256" }
|
|
2570
|
-
]
|
|
2571
|
-
},
|
|
2572
|
-
primaryType: "BillingAuth",
|
|
2573
|
-
message: {
|
|
2574
|
-
product,
|
|
2575
|
-
expiry
|
|
2576
|
-
}
|
|
2577
|
-
});
|
|
2578
|
-
return { signature, expiry };
|
|
2579
|
-
}
|
|
2580
|
-
|
|
2581
|
-
// src/client/common/utils/userapi.ts
|
|
2582
|
-
var import_accounts = require("viem/accounts");
|
|
2409
|
+
var import_viem3 = require("viem");
|
|
2583
2410
|
|
|
2584
2411
|
// src/client/common/utils/helpers.ts
|
|
2585
|
-
var
|
|
2412
|
+
var import_viem2 = require("viem");
|
|
2586
2413
|
var import_chains2 = require("viem/chains");
|
|
2414
|
+
var import_accounts = require("viem/accounts");
|
|
2587
2415
|
function getChainFromID(chainID, fallback = import_chains2.sepolia) {
|
|
2588
2416
|
const id = Number(chainID);
|
|
2589
|
-
return (0,
|
|
2417
|
+
return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
|
|
2418
|
+
}
|
|
2419
|
+
function createClients(options) {
|
|
2420
|
+
const { privateKey, rpcUrl, chainId } = options;
|
|
2421
|
+
const privateKeyHex = addHexPrefix(privateKey);
|
|
2422
|
+
const account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
|
|
2423
|
+
const chain = getChainFromID(chainId);
|
|
2424
|
+
const publicClient = (0, import_viem2.createPublicClient)({
|
|
2425
|
+
chain,
|
|
2426
|
+
transport: (0, import_viem2.http)(rpcUrl)
|
|
2427
|
+
});
|
|
2428
|
+
const walletClient = (0, import_viem2.createWalletClient)({
|
|
2429
|
+
account,
|
|
2430
|
+
chain,
|
|
2431
|
+
transport: (0, import_viem2.http)(rpcUrl)
|
|
2432
|
+
});
|
|
2433
|
+
return { walletClient, publicClient };
|
|
2590
2434
|
}
|
|
2591
2435
|
function addHexPrefix(value) {
|
|
2592
2436
|
return value.startsWith("0x") ? value : `0x${value}`;
|
|
@@ -2595,302 +2439,6 @@ function stripHexPrefix(value) {
|
|
|
2595
2439
|
return value.startsWith("0x") ? value.slice(2) : value;
|
|
2596
2440
|
}
|
|
2597
2441
|
|
|
2598
|
-
// src/client/common/utils/userapi.ts
|
|
2599
|
-
function isJsonObject(value) {
|
|
2600
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2601
|
-
}
|
|
2602
|
-
function readString(obj, key) {
|
|
2603
|
-
const v = obj[key];
|
|
2604
|
-
return typeof v === "string" ? v : void 0;
|
|
2605
|
-
}
|
|
2606
|
-
function readNumber(obj, key) {
|
|
2607
|
-
const v = obj[key];
|
|
2608
|
-
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
2609
|
-
}
|
|
2610
|
-
var MAX_ADDRESS_COUNT = 5;
|
|
2611
|
-
var CanViewAppLogsPermission = "0x2fd3f2fe";
|
|
2612
|
-
var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
|
|
2613
|
-
var CanUpdateAppProfilePermission = "0x036fef61";
|
|
2614
|
-
function getDefaultClientId() {
|
|
2615
|
-
const version = true ? "0.2.0-dev.3" : "0.0.0";
|
|
2616
|
-
return `ecloud-sdk/v${version}`;
|
|
2617
|
-
}
|
|
2618
|
-
var UserApiClient = class {
|
|
2619
|
-
constructor(config, privateKey, rpcUrl, clientId) {
|
|
2620
|
-
this.config = config;
|
|
2621
|
-
if (privateKey) {
|
|
2622
|
-
const privateKeyHex = addHexPrefix(privateKey);
|
|
2623
|
-
this.account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
|
|
2624
|
-
}
|
|
2625
|
-
this.rpcUrl = rpcUrl;
|
|
2626
|
-
this.clientId = clientId || getDefaultClientId();
|
|
2627
|
-
}
|
|
2628
|
-
async getInfos(appIDs, addressCount = 1) {
|
|
2629
|
-
const count = Math.min(addressCount, MAX_ADDRESS_COUNT);
|
|
2630
|
-
const endpoint = `${this.config.userApiServerURL}/info`;
|
|
2631
|
-
const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
|
|
2632
|
-
const res = await this.makeAuthenticatedRequest(url, CanViewSensitiveAppInfoPermission);
|
|
2633
|
-
const result = await res.json();
|
|
2634
|
-
return result.apps.map((app, i) => {
|
|
2635
|
-
const evmAddresses = app.addresses?.data?.evmAddresses?.slice(0, count) || [];
|
|
2636
|
-
const solanaAddresses = app.addresses?.data?.solanaAddresses?.slice(0, count) || [];
|
|
2637
|
-
return {
|
|
2638
|
-
address: appIDs[i],
|
|
2639
|
-
status: app.app_status,
|
|
2640
|
-
ip: app.ip,
|
|
2641
|
-
machineType: app.machine_type,
|
|
2642
|
-
profile: app.profile,
|
|
2643
|
-
metrics: app.metrics,
|
|
2644
|
-
evmAddresses,
|
|
2645
|
-
solanaAddresses
|
|
2646
|
-
};
|
|
2647
|
-
});
|
|
2648
|
-
}
|
|
2649
|
-
/**
|
|
2650
|
-
* Get app details from UserAPI (includes releases and build/provenance info when available).
|
|
2651
|
-
*
|
|
2652
|
-
* Endpoint: GET /apps/:appAddress
|
|
2653
|
-
*/
|
|
2654
|
-
async getApp(appAddress) {
|
|
2655
|
-
const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
|
|
2656
|
-
const res = await this.makeAuthenticatedRequest(endpoint);
|
|
2657
|
-
const raw = await res.json();
|
|
2658
|
-
if (!isJsonObject(raw)) {
|
|
2659
|
-
throw new Error("Unexpected /apps/:id response: expected object");
|
|
2660
|
-
}
|
|
2661
|
-
const id = readString(raw, "id");
|
|
2662
|
-
if (!id) {
|
|
2663
|
-
throw new Error("Unexpected /apps/:id response: missing 'id'");
|
|
2664
|
-
}
|
|
2665
|
-
const releasesRaw = raw.releases;
|
|
2666
|
-
const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
|
|
2667
|
-
return {
|
|
2668
|
-
id,
|
|
2669
|
-
creator: readString(raw, "creator"),
|
|
2670
|
-
contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
|
|
2671
|
-
releases
|
|
2672
|
-
};
|
|
2673
|
-
}
|
|
2674
|
-
/**
|
|
2675
|
-
* Get available SKUs (instance types) from UserAPI
|
|
2676
|
-
*/
|
|
2677
|
-
async getSKUs() {
|
|
2678
|
-
const endpoint = `${this.config.userApiServerURL}/skus`;
|
|
2679
|
-
const response = await this.makeAuthenticatedRequest(endpoint);
|
|
2680
|
-
const result = await response.json();
|
|
2681
|
-
return {
|
|
2682
|
-
skus: result.skus || result.SKUs || []
|
|
2683
|
-
};
|
|
2684
|
-
}
|
|
2685
|
-
/**
|
|
2686
|
-
* Get logs for an app
|
|
2687
|
-
*/
|
|
2688
|
-
async getLogs(appID) {
|
|
2689
|
-
const endpoint = `${this.config.userApiServerURL}/logs/${appID}`;
|
|
2690
|
-
const response = await this.makeAuthenticatedRequest(endpoint, CanViewAppLogsPermission);
|
|
2691
|
-
return await response.text();
|
|
2692
|
-
}
|
|
2693
|
-
/**
|
|
2694
|
-
* Get statuses for apps
|
|
2695
|
-
*/
|
|
2696
|
-
async getStatuses(appIDs) {
|
|
2697
|
-
const endpoint = `${this.config.userApiServerURL}/status`;
|
|
2698
|
-
const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
|
|
2699
|
-
const response = await this.makeAuthenticatedRequest(url);
|
|
2700
|
-
const result = await response.json();
|
|
2701
|
-
const apps = result.apps || result.Apps || [];
|
|
2702
|
-
return apps.map((app, i) => ({
|
|
2703
|
-
address: app.address || appIDs[i],
|
|
2704
|
-
status: app.status || app.Status || ""
|
|
2705
|
-
}));
|
|
2706
|
-
}
|
|
2707
|
-
/**
|
|
2708
|
-
* Upload app profile information with optional image
|
|
2709
|
-
*/
|
|
2710
|
-
async uploadAppProfile(appAddress, name, website, description, xURL, imagePath) {
|
|
2711
|
-
const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}/profile`;
|
|
2712
|
-
const formData = new import_form_data.default();
|
|
2713
|
-
formData.append("name", name);
|
|
2714
|
-
if (website) {
|
|
2715
|
-
formData.append("website", website);
|
|
2716
|
-
}
|
|
2717
|
-
if (description) {
|
|
2718
|
-
formData.append("description", description);
|
|
2719
|
-
}
|
|
2720
|
-
if (xURL) {
|
|
2721
|
-
formData.append("xURL", xURL);
|
|
2722
|
-
}
|
|
2723
|
-
if (imagePath) {
|
|
2724
|
-
const fs8 = await import("fs");
|
|
2725
|
-
const path8 = await import("path");
|
|
2726
|
-
const fileName = path8.basename(imagePath);
|
|
2727
|
-
const fileBuffer = fs8.readFileSync(imagePath);
|
|
2728
|
-
formData.append("image", fileBuffer, fileName);
|
|
2729
|
-
}
|
|
2730
|
-
const headers = {
|
|
2731
|
-
"x-client-id": this.clientId,
|
|
2732
|
-
...formData.getHeaders()
|
|
2733
|
-
};
|
|
2734
|
-
if (this.account) {
|
|
2735
|
-
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
2736
|
-
const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
|
|
2737
|
-
Object.assign(headers, authHeaders);
|
|
2738
|
-
}
|
|
2739
|
-
try {
|
|
2740
|
-
const response = await import_axios.default.post(endpoint, formData, {
|
|
2741
|
-
headers,
|
|
2742
|
-
maxRedirects: 0,
|
|
2743
|
-
validateStatus: () => true,
|
|
2744
|
-
// Don't throw on any status
|
|
2745
|
-
maxContentLength: Infinity,
|
|
2746
|
-
// Allow large file uploads
|
|
2747
|
-
maxBodyLength: Infinity
|
|
2748
|
-
// Allow large file uploads
|
|
2749
|
-
});
|
|
2750
|
-
const status = response.status;
|
|
2751
|
-
if (status !== 200 && status !== 201) {
|
|
2752
|
-
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
2753
|
-
if (status === 403 && body.includes("Cloudflare") && body.includes("challenge-platform")) {
|
|
2754
|
-
throw new Error(
|
|
2755
|
-
`Cloudflare protection is blocking the request. This is likely due to bot detection.
|
|
2756
|
-
Status: ${status}`
|
|
2757
|
-
);
|
|
2758
|
-
}
|
|
2759
|
-
throw new Error(
|
|
2760
|
-
`UserAPI request failed: ${status} ${status >= 200 && status < 300 ? "OK" : "Error"} - ${body.substring(0, 500)}${body.length > 500 ? "..." : ""}`
|
|
2761
|
-
);
|
|
2762
|
-
}
|
|
2763
|
-
return response.data;
|
|
2764
|
-
} catch (error) {
|
|
2765
|
-
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
2766
|
-
const cause = error.cause?.message || error.cause || error.message;
|
|
2767
|
-
throw new Error(
|
|
2768
|
-
`Failed to connect to UserAPI at ${endpoint}: ${cause}
|
|
2769
|
-
Please check:
|
|
2770
|
-
1. Your internet connection
|
|
2771
|
-
2. The API server is accessible: ${this.config.userApiServerURL}
|
|
2772
|
-
3. Firewall/proxy settings`
|
|
2773
|
-
);
|
|
2774
|
-
}
|
|
2775
|
-
throw error;
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
async makeAuthenticatedRequest(url, permission) {
|
|
2779
|
-
const headers = {
|
|
2780
|
-
"x-client-id": this.clientId
|
|
2781
|
-
};
|
|
2782
|
-
if (permission && this.account) {
|
|
2783
|
-
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
2784
|
-
const authHeaders = await this.generateAuthHeaders(permission, expiry);
|
|
2785
|
-
Object.assign(headers, authHeaders);
|
|
2786
|
-
}
|
|
2787
|
-
try {
|
|
2788
|
-
const response = await import_axios.default.get(url, {
|
|
2789
|
-
headers,
|
|
2790
|
-
maxRedirects: 0,
|
|
2791
|
-
validateStatus: () => true
|
|
2792
|
-
// Don't throw on any status
|
|
2793
|
-
});
|
|
2794
|
-
const status = response.status;
|
|
2795
|
-
const statusText = status >= 200 && status < 300 ? "OK" : "Error";
|
|
2796
|
-
if (status < 200 || status >= 300) {
|
|
2797
|
-
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
2798
|
-
throw new Error(`UserAPI request failed: ${status} ${statusText} - ${body}`);
|
|
2799
|
-
}
|
|
2800
|
-
return {
|
|
2801
|
-
json: async () => response.data,
|
|
2802
|
-
text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
|
|
2803
|
-
};
|
|
2804
|
-
} catch (error) {
|
|
2805
|
-
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
2806
|
-
const cause = error.cause?.message || error.cause || error.message;
|
|
2807
|
-
throw new Error(
|
|
2808
|
-
`Failed to connect to UserAPI at ${url}: ${cause}
|
|
2809
|
-
Please check:
|
|
2810
|
-
1. Your internet connection
|
|
2811
|
-
2. The API server is accessible: ${this.config.userApiServerURL}
|
|
2812
|
-
3. Firewall/proxy settings`
|
|
2813
|
-
);
|
|
2814
|
-
}
|
|
2815
|
-
throw error;
|
|
2816
|
-
}
|
|
2817
|
-
}
|
|
2818
|
-
/**
|
|
2819
|
-
* Generate authentication headers for UserAPI requests
|
|
2820
|
-
*/
|
|
2821
|
-
async generateAuthHeaders(permission, expiry) {
|
|
2822
|
-
if (!this.account) {
|
|
2823
|
-
throw new Error("Private key required for authenticated requests");
|
|
2824
|
-
}
|
|
2825
|
-
if (!this.rpcUrl) {
|
|
2826
|
-
throw new Error("RPC URL required for authenticated requests");
|
|
2827
|
-
}
|
|
2828
|
-
const chain = getChainFromID(this.config.chainID);
|
|
2829
|
-
const publicClient = (0, import_viem4.createPublicClient)({
|
|
2830
|
-
chain,
|
|
2831
|
-
transport: (0, import_viem4.http)(this.rpcUrl)
|
|
2832
|
-
});
|
|
2833
|
-
const { signature } = await calculatePermissionSignature({
|
|
2834
|
-
permission,
|
|
2835
|
-
expiry,
|
|
2836
|
-
appControllerAddress: this.config.appControllerAddress,
|
|
2837
|
-
publicClient,
|
|
2838
|
-
account: this.account
|
|
2839
|
-
});
|
|
2840
|
-
return {
|
|
2841
|
-
Authorization: `Bearer ${stripHexPrefix(signature)}`,
|
|
2842
|
-
"X-eigenx-expiry": expiry.toString()
|
|
2843
|
-
};
|
|
2844
|
-
}
|
|
2845
|
-
};
|
|
2846
|
-
function transformAppReleaseBuild(raw) {
|
|
2847
|
-
if (!isJsonObject(raw)) return void 0;
|
|
2848
|
-
const depsRaw = raw.dependencies;
|
|
2849
|
-
const deps = isJsonObject(depsRaw) ? Object.fromEntries(
|
|
2850
|
-
Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
|
|
2851
|
-
const parsed = transformAppReleaseBuild(depRaw);
|
|
2852
|
-
return parsed ? [[digest, parsed]] : [];
|
|
2853
|
-
})
|
|
2854
|
-
) : void 0;
|
|
2855
|
-
return {
|
|
2856
|
-
buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
|
|
2857
|
-
billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
|
|
2858
|
-
repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
|
|
2859
|
-
gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
|
|
2860
|
-
status: readString(raw, "status"),
|
|
2861
|
-
buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
|
|
2862
|
-
imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
|
|
2863
|
-
imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
|
|
2864
|
-
imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
|
|
2865
|
-
provenanceJson: raw.provenance_json ?? raw.provenanceJson,
|
|
2866
|
-
provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
|
|
2867
|
-
createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
|
|
2868
|
-
updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
|
|
2869
|
-
errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
|
|
2870
|
-
dependencies: deps
|
|
2871
|
-
};
|
|
2872
|
-
}
|
|
2873
|
-
function transformAppRelease(raw) {
|
|
2874
|
-
if (!isJsonObject(raw)) return void 0;
|
|
2875
|
-
return {
|
|
2876
|
-
appId: readString(raw, "appId") ?? readString(raw, "app_id"),
|
|
2877
|
-
rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
|
|
2878
|
-
imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
|
|
2879
|
-
registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
|
|
2880
|
-
publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
|
|
2881
|
-
encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
|
|
2882
|
-
upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
|
|
2883
|
-
createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
|
|
2884
|
-
createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
|
|
2885
|
-
build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
|
|
2886
|
-
};
|
|
2887
|
-
}
|
|
2888
|
-
|
|
2889
|
-
// src/client/common/utils/billing.ts
|
|
2890
|
-
function isSubscriptionActive(status) {
|
|
2891
|
-
return status === "active" || status === "trialing";
|
|
2892
|
-
}
|
|
2893
|
-
|
|
2894
2442
|
// src/client/common/abis/AppController.json
|
|
2895
2443
|
var AppController_default = [
|
|
2896
2444
|
{
|
|
@@ -4446,17 +3994,10 @@ function formatETH(wei) {
|
|
|
4446
3994
|
return trimmed;
|
|
4447
3995
|
}
|
|
4448
3996
|
async function estimateTransactionGas(options) {
|
|
4449
|
-
const {
|
|
4450
|
-
const privateKeyHex = addHexPrefix(privateKey);
|
|
4451
|
-
const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
|
|
4452
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4453
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4454
|
-
chain,
|
|
4455
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4456
|
-
});
|
|
3997
|
+
const { publicClient, from, to, data, value = 0n } = options;
|
|
4457
3998
|
const fees = await publicClient.estimateFeesPerGas();
|
|
4458
3999
|
const gasLimit = await publicClient.estimateGas({
|
|
4459
|
-
account:
|
|
4000
|
+
account: from,
|
|
4460
4001
|
to,
|
|
4461
4002
|
data,
|
|
4462
4003
|
value
|
|
@@ -4473,65 +4014,54 @@ async function estimateTransactionGas(options) {
|
|
|
4473
4014
|
maxCostEth
|
|
4474
4015
|
};
|
|
4475
4016
|
}
|
|
4476
|
-
async function calculateAppID(
|
|
4477
|
-
const
|
|
4478
|
-
const
|
|
4479
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4480
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4481
|
-
chain,
|
|
4482
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4483
|
-
});
|
|
4484
|
-
const saltHexString = Buffer.from(salt).toString("hex");
|
|
4017
|
+
async function calculateAppID(options) {
|
|
4018
|
+
const { publicClient, environmentConfig, ownerAddress, salt } = options;
|
|
4019
|
+
const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
|
|
4485
4020
|
const paddedSaltHex = saltHexString.padStart(64, "0");
|
|
4486
4021
|
const saltHex = `0x${paddedSaltHex}`;
|
|
4487
|
-
const accountAddress = typeof account.address === "string" ? account.address : account.address.toString();
|
|
4488
4022
|
const appID = await publicClient.readContract({
|
|
4489
4023
|
address: environmentConfig.appControllerAddress,
|
|
4490
4024
|
abi: AppController_default,
|
|
4491
4025
|
functionName: "calculateAppId",
|
|
4492
|
-
args: [
|
|
4026
|
+
args: [ownerAddress, saltHex]
|
|
4493
4027
|
});
|
|
4494
4028
|
return appID;
|
|
4495
4029
|
}
|
|
4496
4030
|
async function prepareDeployBatch(options, logger) {
|
|
4497
|
-
const {
|
|
4498
|
-
const
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
chain,
|
|
4503
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4504
|
-
});
|
|
4505
|
-
const walletClient = (0, import_viem5.createWalletClient)({
|
|
4506
|
-
account,
|
|
4507
|
-
chain,
|
|
4508
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4509
|
-
});
|
|
4031
|
+
const { walletClient, publicClient, environmentConfig, salt, release, publicLogs } = options;
|
|
4032
|
+
const account = walletClient.account;
|
|
4033
|
+
if (!account) {
|
|
4034
|
+
throw new Error("WalletClient must have an account attached");
|
|
4035
|
+
}
|
|
4510
4036
|
logger.info("Calculating app ID...");
|
|
4511
|
-
const appId = await calculateAppID(
|
|
4512
|
-
|
|
4037
|
+
const appId = await calculateAppID({
|
|
4038
|
+
publicClient,
|
|
4039
|
+
environmentConfig,
|
|
4040
|
+
ownerAddress: account.address,
|
|
4041
|
+
salt
|
|
4042
|
+
});
|
|
4513
4043
|
logger.debug(`App ID calculated: ${appId}`);
|
|
4514
4044
|
logger.debug(`This address will be used for acceptAdmin call`);
|
|
4515
|
-
const saltHexString =
|
|
4045
|
+
const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
|
|
4516
4046
|
const paddedSaltHex = saltHexString.padStart(64, "0");
|
|
4517
4047
|
const saltHex = `0x${paddedSaltHex}`;
|
|
4518
4048
|
const releaseForViem = {
|
|
4519
4049
|
rmsRelease: {
|
|
4520
4050
|
artifacts: release.rmsRelease.artifacts.map((artifact) => ({
|
|
4521
|
-
digest: `0x${
|
|
4051
|
+
digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
|
|
4522
4052
|
registry: artifact.registry
|
|
4523
4053
|
})),
|
|
4524
4054
|
upgradeByTime: release.rmsRelease.upgradeByTime
|
|
4525
4055
|
},
|
|
4526
|
-
publicEnv:
|
|
4527
|
-
encryptedEnv:
|
|
4056
|
+
publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
|
|
4057
|
+
encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
|
|
4528
4058
|
};
|
|
4529
|
-
const createData = (0,
|
|
4059
|
+
const createData = (0, import_viem3.encodeFunctionData)({
|
|
4530
4060
|
abi: AppController_default,
|
|
4531
4061
|
functionName: "createApp",
|
|
4532
4062
|
args: [saltHex, releaseForViem]
|
|
4533
4063
|
});
|
|
4534
|
-
const acceptAdminData = (0,
|
|
4064
|
+
const acceptAdminData = (0, import_viem3.encodeFunctionData)({
|
|
4535
4065
|
abi: PermissionController_default,
|
|
4536
4066
|
functionName: "acceptAdmin",
|
|
4537
4067
|
args: [appId]
|
|
@@ -4549,7 +4079,7 @@ async function prepareDeployBatch(options, logger) {
|
|
|
4549
4079
|
}
|
|
4550
4080
|
];
|
|
4551
4081
|
if (publicLogs) {
|
|
4552
|
-
const anyoneCanViewLogsData = (0,
|
|
4082
|
+
const anyoneCanViewLogsData = (0, import_viem3.encodeFunctionData)({
|
|
4553
4083
|
abi: PermissionController_default,
|
|
4554
4084
|
functionName: "setAppointee",
|
|
4555
4085
|
args: [
|
|
@@ -4593,17 +4123,7 @@ async function executeDeployBatch(data, context, gas, logger) {
|
|
|
4593
4123
|
return { appId: data.appId, txHash };
|
|
4594
4124
|
}
|
|
4595
4125
|
async function deployApp(options, logger) {
|
|
4596
|
-
const prepared = await prepareDeployBatch(
|
|
4597
|
-
{
|
|
4598
|
-
privateKey: options.privateKey,
|
|
4599
|
-
rpcUrl: options.rpcUrl,
|
|
4600
|
-
environmentConfig: options.environmentConfig,
|
|
4601
|
-
salt: options.salt,
|
|
4602
|
-
release: options.release,
|
|
4603
|
-
publicLogs: options.publicLogs
|
|
4604
|
-
},
|
|
4605
|
-
logger
|
|
4606
|
-
);
|
|
4126
|
+
const prepared = await prepareDeployBatch(options, logger);
|
|
4607
4127
|
const data = {
|
|
4608
4128
|
appId: prepared.appId,
|
|
4609
4129
|
salt: prepared.salt,
|
|
@@ -4617,42 +4137,22 @@ async function deployApp(options, logger) {
|
|
|
4617
4137
|
return executeDeployBatch(data, context, options.gas, logger);
|
|
4618
4138
|
}
|
|
4619
4139
|
async function prepareUpgradeBatch(options) {
|
|
4620
|
-
const {
|
|
4621
|
-
privateKey,
|
|
4622
|
-
rpcUrl,
|
|
4623
|
-
environmentConfig,
|
|
4624
|
-
appId,
|
|
4625
|
-
release,
|
|
4626
|
-
publicLogs,
|
|
4627
|
-
needsPermissionChange
|
|
4628
|
-
} = options;
|
|
4629
|
-
const privateKeyHex = addHexPrefix(privateKey);
|
|
4630
|
-
const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
|
|
4631
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4632
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4633
|
-
chain,
|
|
4634
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4635
|
-
});
|
|
4636
|
-
const walletClient = (0, import_viem5.createWalletClient)({
|
|
4637
|
-
account,
|
|
4638
|
-
chain,
|
|
4639
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4640
|
-
});
|
|
4140
|
+
const { walletClient, publicClient, environmentConfig, appID, release, publicLogs, needsPermissionChange } = options;
|
|
4641
4141
|
const releaseForViem = {
|
|
4642
4142
|
rmsRelease: {
|
|
4643
4143
|
artifacts: release.rmsRelease.artifacts.map((artifact) => ({
|
|
4644
|
-
digest: `0x${
|
|
4144
|
+
digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
|
|
4645
4145
|
registry: artifact.registry
|
|
4646
4146
|
})),
|
|
4647
4147
|
upgradeByTime: release.rmsRelease.upgradeByTime
|
|
4648
4148
|
},
|
|
4649
|
-
publicEnv:
|
|
4650
|
-
encryptedEnv:
|
|
4149
|
+
publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
|
|
4150
|
+
encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
|
|
4651
4151
|
};
|
|
4652
|
-
const upgradeData = (0,
|
|
4152
|
+
const upgradeData = (0, import_viem3.encodeFunctionData)({
|
|
4653
4153
|
abi: AppController_default,
|
|
4654
4154
|
functionName: "upgradeApp",
|
|
4655
|
-
args: [
|
|
4155
|
+
args: [appID, releaseForViem]
|
|
4656
4156
|
});
|
|
4657
4157
|
const executions = [
|
|
4658
4158
|
{
|
|
@@ -4663,11 +4163,11 @@ async function prepareUpgradeBatch(options) {
|
|
|
4663
4163
|
];
|
|
4664
4164
|
if (needsPermissionChange) {
|
|
4665
4165
|
if (publicLogs) {
|
|
4666
|
-
const addLogsData = (0,
|
|
4166
|
+
const addLogsData = (0, import_viem3.encodeFunctionData)({
|
|
4667
4167
|
abi: PermissionController_default,
|
|
4668
4168
|
functionName: "setAppointee",
|
|
4669
4169
|
args: [
|
|
4670
|
-
|
|
4170
|
+
appID,
|
|
4671
4171
|
"0x493219d9949348178af1f58740655951a8cd110c",
|
|
4672
4172
|
// AnyoneCanCallAddress
|
|
4673
4173
|
"0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
|
|
@@ -4682,11 +4182,11 @@ async function prepareUpgradeBatch(options) {
|
|
|
4682
4182
|
callData: addLogsData
|
|
4683
4183
|
});
|
|
4684
4184
|
} else {
|
|
4685
|
-
const removeLogsData = (0,
|
|
4185
|
+
const removeLogsData = (0, import_viem3.encodeFunctionData)({
|
|
4686
4186
|
abi: PermissionController_default,
|
|
4687
4187
|
functionName: "removeAppointee",
|
|
4688
4188
|
args: [
|
|
4689
|
-
|
|
4189
|
+
appID,
|
|
4690
4190
|
"0x493219d9949348178af1f58740655951a8cd110c",
|
|
4691
4191
|
// AnyoneCanCallAddress
|
|
4692
4192
|
"0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
|
|
@@ -4703,7 +4203,7 @@ async function prepareUpgradeBatch(options) {
|
|
|
4703
4203
|
}
|
|
4704
4204
|
}
|
|
4705
4205
|
return {
|
|
4706
|
-
appId,
|
|
4206
|
+
appId: appID,
|
|
4707
4207
|
executions,
|
|
4708
4208
|
walletClient,
|
|
4709
4209
|
publicClient,
|
|
@@ -4726,15 +4226,7 @@ async function executeUpgradeBatch(data, context, gas, logger) {
|
|
|
4726
4226
|
return txHash;
|
|
4727
4227
|
}
|
|
4728
4228
|
async function upgradeApp(options, logger) {
|
|
4729
|
-
const prepared = await prepareUpgradeBatch(
|
|
4730
|
-
privateKey: options.privateKey,
|
|
4731
|
-
rpcUrl: options.rpcUrl,
|
|
4732
|
-
environmentConfig: options.environmentConfig,
|
|
4733
|
-
appId: options.appId,
|
|
4734
|
-
release: options.release,
|
|
4735
|
-
publicLogs: options.publicLogs,
|
|
4736
|
-
needsPermissionChange: options.needsPermissionChange
|
|
4737
|
-
});
|
|
4229
|
+
const prepared = await prepareUpgradeBatch(options);
|
|
4738
4230
|
const data = {
|
|
4739
4231
|
appId: prepared.appId,
|
|
4740
4232
|
executions: prepared.executions
|
|
@@ -4747,29 +4239,12 @@ async function upgradeApp(options, logger) {
|
|
|
4747
4239
|
return executeUpgradeBatch(data, context, options.gas, logger);
|
|
4748
4240
|
}
|
|
4749
4241
|
async function sendAndWaitForTransaction(options, logger) {
|
|
4750
|
-
const {
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
data,
|
|
4756
|
-
value = 0n,
|
|
4757
|
-
pendingMessage,
|
|
4758
|
-
txDescription,
|
|
4759
|
-
gas
|
|
4760
|
-
} = options;
|
|
4761
|
-
const privateKeyHex = addHexPrefix(privateKey);
|
|
4762
|
-
const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
|
|
4242
|
+
const { walletClient, publicClient, environmentConfig, to, data, value = 0n, pendingMessage, txDescription, gas } = options;
|
|
4243
|
+
const account = walletClient.account;
|
|
4244
|
+
if (!account) {
|
|
4245
|
+
throw new Error("WalletClient must have an account attached");
|
|
4246
|
+
}
|
|
4763
4247
|
const chain = getChainFromID(environmentConfig.chainID);
|
|
4764
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4765
|
-
chain,
|
|
4766
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4767
|
-
});
|
|
4768
|
-
const walletClient = (0, import_viem5.createWalletClient)({
|
|
4769
|
-
account,
|
|
4770
|
-
chain,
|
|
4771
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4772
|
-
});
|
|
4773
4248
|
if (pendingMessage) {
|
|
4774
4249
|
logger.info(`
|
|
4775
4250
|
${pendingMessage}`);
|
|
@@ -4780,7 +4255,10 @@ ${pendingMessage}`);
|
|
|
4780
4255
|
data,
|
|
4781
4256
|
value,
|
|
4782
4257
|
...gas?.maxFeePerGas && { maxFeePerGas: gas.maxFeePerGas },
|
|
4783
|
-
...gas?.maxPriorityFeePerGas && {
|
|
4258
|
+
...gas?.maxPriorityFeePerGas && {
|
|
4259
|
+
maxPriorityFeePerGas: gas.maxPriorityFeePerGas
|
|
4260
|
+
},
|
|
4261
|
+
chain
|
|
4784
4262
|
});
|
|
4785
4263
|
logger.info(`Transaction sent: ${hash}`);
|
|
4786
4264
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -4795,7 +4273,7 @@ ${pendingMessage}`);
|
|
|
4795
4273
|
} catch (callError) {
|
|
4796
4274
|
if (callError.data) {
|
|
4797
4275
|
try {
|
|
4798
|
-
const decoded = (0,
|
|
4276
|
+
const decoded = (0, import_viem3.decodeErrorResult)({
|
|
4799
4277
|
abi: AppController_default,
|
|
4800
4278
|
data: callError.data
|
|
4801
4279
|
});
|
|
@@ -4846,12 +4324,7 @@ function formatAppControllerError(decoded) {
|
|
|
4846
4324
|
return new Error(`contract error: ${errorName}`);
|
|
4847
4325
|
}
|
|
4848
4326
|
}
|
|
4849
|
-
async function getActiveAppCount(
|
|
4850
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4851
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4852
|
-
chain,
|
|
4853
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4854
|
-
});
|
|
4327
|
+
async function getActiveAppCount(publicClient, environmentConfig, user) {
|
|
4855
4328
|
const count = await publicClient.readContract({
|
|
4856
4329
|
address: environmentConfig.appControllerAddress,
|
|
4857
4330
|
abi: AppController_default,
|
|
@@ -4860,12 +4333,7 @@ async function getActiveAppCount(rpcUrl, environmentConfig, user) {
|
|
|
4860
4333
|
});
|
|
4861
4334
|
return Number(count);
|
|
4862
4335
|
}
|
|
4863
|
-
async function getMaxActiveAppsPerUser(
|
|
4864
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4865
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4866
|
-
chain,
|
|
4867
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4868
|
-
});
|
|
4336
|
+
async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
|
|
4869
4337
|
const quota = await publicClient.readContract({
|
|
4870
4338
|
address: environmentConfig.appControllerAddress,
|
|
4871
4339
|
abi: AppController_default,
|
|
@@ -4874,12 +4342,7 @@ async function getMaxActiveAppsPerUser(rpcUrl, environmentConfig, user) {
|
|
|
4874
4342
|
});
|
|
4875
4343
|
return Number(quota);
|
|
4876
4344
|
}
|
|
4877
|
-
async function getAppsByDeveloper(
|
|
4878
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4879
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4880
|
-
chain,
|
|
4881
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4882
|
-
});
|
|
4345
|
+
async function getAppsByDeveloper(publicClient, environmentConfig, developer, offset, limit) {
|
|
4883
4346
|
const result = await publicClient.readContract({
|
|
4884
4347
|
address: environmentConfig.appControllerAddress,
|
|
4885
4348
|
abi: AppController_default,
|
|
@@ -4891,12 +4354,12 @@ async function getAppsByDeveloper(rpcUrl, environmentConfig, developer, offset,
|
|
|
4891
4354
|
appConfigs: result[1]
|
|
4892
4355
|
};
|
|
4893
4356
|
}
|
|
4894
|
-
async function getAllAppsByDeveloper(
|
|
4357
|
+
async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 100n) {
|
|
4895
4358
|
let offset = 0n;
|
|
4896
4359
|
const allApps = [];
|
|
4897
4360
|
const allConfigs = [];
|
|
4898
4361
|
while (true) {
|
|
4899
|
-
const { apps, appConfigs } = await getAppsByDeveloper(
|
|
4362
|
+
const { apps, appConfigs } = await getAppsByDeveloper(publicClient, env, developer, offset, pageSize);
|
|
4900
4363
|
if (apps.length === 0) break;
|
|
4901
4364
|
allApps.push(...apps);
|
|
4902
4365
|
allConfigs.push(...appConfigs);
|
|
@@ -4908,12 +4371,7 @@ async function getAllAppsByDeveloper(rpcUrl, env, developer, pageSize = 100n) {
|
|
|
4908
4371
|
appConfigs: allConfigs
|
|
4909
4372
|
};
|
|
4910
4373
|
}
|
|
4911
|
-
async function getAppLatestReleaseBlockNumbers(
|
|
4912
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4913
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4914
|
-
chain,
|
|
4915
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4916
|
-
});
|
|
4374
|
+
async function getAppLatestReleaseBlockNumbers(publicClient, environmentConfig, appIDs) {
|
|
4917
4375
|
const results = await Promise.all(
|
|
4918
4376
|
appIDs.map(
|
|
4919
4377
|
(appID) => publicClient.readContract({
|
|
@@ -4933,12 +4391,7 @@ async function getAppLatestReleaseBlockNumbers(rpcUrl, environmentConfig, appIDs
|
|
|
4933
4391
|
}
|
|
4934
4392
|
return blockNumbers;
|
|
4935
4393
|
}
|
|
4936
|
-
async function getBlockTimestamps(
|
|
4937
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4938
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4939
|
-
chain,
|
|
4940
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4941
|
-
});
|
|
4394
|
+
async function getBlockTimestamps(publicClient, blockNumbers) {
|
|
4942
4395
|
const uniqueBlockNumbers = [...new Set(blockNumbers)].filter((n) => n > 0);
|
|
4943
4396
|
const timestamps = /* @__PURE__ */ new Map();
|
|
4944
4397
|
const blocks = await Promise.all(
|
|
@@ -4955,67 +4408,34 @@ async function getBlockTimestamps(rpcUrl, environmentConfig, blockNumbers) {
|
|
|
4955
4408
|
return timestamps;
|
|
4956
4409
|
}
|
|
4957
4410
|
async function isDelegated(options) {
|
|
4958
|
-
const {
|
|
4959
|
-
const privateKeyHex = addHexPrefix(privateKey);
|
|
4960
|
-
const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
|
|
4961
|
-
const chain = getChainFromID(environmentConfig.chainID);
|
|
4962
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4963
|
-
chain,
|
|
4964
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4965
|
-
});
|
|
4411
|
+
const { publicClient, environmentConfig, address } = options;
|
|
4966
4412
|
return checkERC7702Delegation(
|
|
4967
4413
|
publicClient,
|
|
4968
|
-
|
|
4414
|
+
address,
|
|
4969
4415
|
environmentConfig.erc7702DelegatorAddress
|
|
4970
4416
|
);
|
|
4971
4417
|
}
|
|
4972
4418
|
async function undelegate(options, logger) {
|
|
4973
|
-
const {
|
|
4974
|
-
const
|
|
4975
|
-
|
|
4419
|
+
const { walletClient, publicClient, environmentConfig } = options;
|
|
4420
|
+
const account = walletClient.account;
|
|
4421
|
+
if (!account) {
|
|
4422
|
+
throw new Error("WalletClient must have an account attached");
|
|
4423
|
+
}
|
|
4976
4424
|
const chain = getChainFromID(environmentConfig.chainID);
|
|
4977
|
-
const publicClient = (0, import_viem5.createPublicClient)({
|
|
4978
|
-
chain,
|
|
4979
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4980
|
-
});
|
|
4981
|
-
const walletClient = (0, import_viem5.createWalletClient)({
|
|
4982
|
-
account,
|
|
4983
|
-
chain,
|
|
4984
|
-
transport: (0, import_viem5.http)(rpcUrl)
|
|
4985
|
-
});
|
|
4986
4425
|
const transactionNonce = await publicClient.getTransactionCount({
|
|
4987
4426
|
address: account.address,
|
|
4988
4427
|
blockTag: "pending"
|
|
4989
4428
|
});
|
|
4990
4429
|
const chainId = await publicClient.getChainId();
|
|
4991
4430
|
const authorizationNonce = BigInt(transactionNonce) + 1n;
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
nonce: authorizationNonce
|
|
4997
|
-
|
|
4998
|
-
const sighash = (0, import_utils.hashAuthorization)({
|
|
4999
|
-
chainId: authorization.chainId,
|
|
5000
|
-
contractAddress: authorization.address,
|
|
5001
|
-
nonce: Number(authorization.nonce)
|
|
5002
|
-
});
|
|
5003
|
-
const sig = await (0, import_accounts3.sign)({
|
|
5004
|
-
hash: sighash,
|
|
5005
|
-
privateKey: privateKeyHex
|
|
4431
|
+
logger.debug("Signing undelegate authorization");
|
|
4432
|
+
const signedAuthorization = await walletClient.signAuthorization({
|
|
4433
|
+
contractAddress: "0x0000000000000000000000000000000000000000",
|
|
4434
|
+
chainId,
|
|
4435
|
+
nonce: Number(authorizationNonce),
|
|
4436
|
+
account
|
|
5006
4437
|
});
|
|
5007
|
-
const
|
|
5008
|
-
const yParity = v === 27 ? 0 : 1;
|
|
5009
|
-
const authorizationList = [
|
|
5010
|
-
{
|
|
5011
|
-
chainId: authorization.chainId,
|
|
5012
|
-
address: authorization.address,
|
|
5013
|
-
nonce: Number(authorization.nonce),
|
|
5014
|
-
r: sig.r,
|
|
5015
|
-
s: sig.s,
|
|
5016
|
-
yParity
|
|
5017
|
-
}
|
|
5018
|
-
];
|
|
4438
|
+
const authorizationList = [signedAuthorization];
|
|
5019
4439
|
const hash = await walletClient.sendTransaction({
|
|
5020
4440
|
account,
|
|
5021
4441
|
to: account.address,
|
|
@@ -5023,7 +4443,8 @@ async function undelegate(options, logger) {
|
|
|
5023
4443
|
data: "0x",
|
|
5024
4444
|
// Empty data
|
|
5025
4445
|
value: 0n,
|
|
5026
|
-
authorizationList
|
|
4446
|
+
authorizationList,
|
|
4447
|
+
chain
|
|
5027
4448
|
});
|
|
5028
4449
|
logger.info(`Transaction sent: ${hash}`);
|
|
5029
4450
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -5034,360 +4455,705 @@ async function undelegate(options, logger) {
|
|
|
5034
4455
|
return hash;
|
|
5035
4456
|
}
|
|
5036
4457
|
|
|
5037
|
-
// src/client/common/
|
|
5038
|
-
var
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
const
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
if (hasChanged || initialStatus !== APP_STATUS_RUNNING) {
|
|
5057
|
-
if (!initialIP || initialIP === "No IP assigned") {
|
|
5058
|
-
logger.info(`App is now running with IP: ${ip}`);
|
|
5059
|
-
} else {
|
|
5060
|
-
logger.info("App is now running");
|
|
5061
|
-
}
|
|
5062
|
-
return true;
|
|
5063
|
-
}
|
|
5064
|
-
}
|
|
5065
|
-
if (status === APP_STATUS_FAILED) {
|
|
5066
|
-
throw new Error(`App entered ${status} state`);
|
|
5067
|
-
}
|
|
5068
|
-
return false;
|
|
5069
|
-
};
|
|
5070
|
-
while (true) {
|
|
5071
|
-
try {
|
|
5072
|
-
const info = await userApiClient.getInfos([appId], 1);
|
|
5073
|
-
if (info.length === 0) {
|
|
5074
|
-
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5075
|
-
continue;
|
|
5076
|
-
}
|
|
5077
|
-
const appInfo = info[0];
|
|
5078
|
-
const currentStatus = appInfo.status;
|
|
5079
|
-
const currentIP = appInfo.ip || "";
|
|
5080
|
-
if (stopCondition(currentStatus, currentIP)) {
|
|
5081
|
-
return currentIP || void 0;
|
|
5082
|
-
}
|
|
5083
|
-
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5084
|
-
} catch (error) {
|
|
5085
|
-
logger.warn(`Failed to fetch app info: ${error.message}`);
|
|
5086
|
-
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5087
|
-
}
|
|
4458
|
+
// src/client/common/utils/userapi.ts
|
|
4459
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
4460
|
+
|
|
4461
|
+
// src/client/common/utils/auth.ts
|
|
4462
|
+
var import_viem4 = require("viem");
|
|
4463
|
+
var APP_CONTROLLER_ABI = (0, import_viem4.parseAbi)([
|
|
4464
|
+
"function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
|
|
4465
|
+
]);
|
|
4466
|
+
async function calculatePermissionSignature(options) {
|
|
4467
|
+
const { permission, expiry, appControllerAddress, publicClient, walletClient } = options;
|
|
4468
|
+
const digest = await publicClient.readContract({
|
|
4469
|
+
address: appControllerAddress,
|
|
4470
|
+
abi: APP_CONTROLLER_ABI,
|
|
4471
|
+
functionName: "calculateApiPermissionDigestHash",
|
|
4472
|
+
args: [permission, expiry]
|
|
4473
|
+
});
|
|
4474
|
+
const account = walletClient.account;
|
|
4475
|
+
if (!account) {
|
|
4476
|
+
throw new Error("WalletClient must have an account attached");
|
|
5088
4477
|
}
|
|
4478
|
+
const signature = await walletClient.signMessage({
|
|
4479
|
+
account,
|
|
4480
|
+
message: { raw: digest }
|
|
4481
|
+
});
|
|
4482
|
+
return { signature, digest };
|
|
5089
4483
|
}
|
|
5090
|
-
var
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
}
|
|
5107
|
-
}
|
|
5108
|
-
if (status !== initialStatus) {
|
|
5109
|
-
hasChanged = true;
|
|
5110
|
-
}
|
|
5111
|
-
if (status === APP_STATUS_STOPPED && ip && hasChanged) {
|
|
5112
|
-
logger.info("App upgrade complete.");
|
|
5113
|
-
logger.info(`Status: ${status}`);
|
|
5114
|
-
logger.info(`To start the app, run: ecloud compute app start ${appId}`);
|
|
5115
|
-
return true;
|
|
5116
|
-
}
|
|
5117
|
-
if (status === APP_STATUS_RUNNING && ip && hasChanged) {
|
|
5118
|
-
if (!initialIP || initialIP === "No IP assigned") {
|
|
5119
|
-
logger.info(`App is now running with IP: ${ip}`);
|
|
5120
|
-
} else {
|
|
5121
|
-
logger.info("App is now running");
|
|
5122
|
-
}
|
|
5123
|
-
return true;
|
|
5124
|
-
}
|
|
5125
|
-
if (status === APP_STATUS_FAILED) {
|
|
5126
|
-
throw new Error(`App entered ${status} state`);
|
|
4484
|
+
var generateBillingSigData = (product, expiry) => {
|
|
4485
|
+
return {
|
|
4486
|
+
domain: {
|
|
4487
|
+
name: "EigenCloud Billing API",
|
|
4488
|
+
version: "1"
|
|
4489
|
+
},
|
|
4490
|
+
types: {
|
|
4491
|
+
BillingAuth: [
|
|
4492
|
+
{ name: "product", type: "string" },
|
|
4493
|
+
{ name: "expiry", type: "uint256" }
|
|
4494
|
+
]
|
|
4495
|
+
},
|
|
4496
|
+
primaryType: "BillingAuth",
|
|
4497
|
+
message: {
|
|
4498
|
+
product,
|
|
4499
|
+
expiry
|
|
5127
4500
|
}
|
|
5128
|
-
return false;
|
|
5129
4501
|
};
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
}
|
|
5137
|
-
const appInfo = info[0];
|
|
5138
|
-
const currentStatus = appInfo.status;
|
|
5139
|
-
const currentIP = appInfo.ip || "";
|
|
5140
|
-
if (stopCondition(currentStatus, currentIP)) {
|
|
5141
|
-
return;
|
|
5142
|
-
}
|
|
5143
|
-
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5144
|
-
} catch (error) {
|
|
5145
|
-
logger.warn(`Failed to fetch app info: ${error.message}`);
|
|
5146
|
-
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5147
|
-
}
|
|
4502
|
+
};
|
|
4503
|
+
async function calculateBillingAuthSignature(options) {
|
|
4504
|
+
const { walletClient, product, expiry } = options;
|
|
4505
|
+
const account = walletClient.account;
|
|
4506
|
+
if (!account) {
|
|
4507
|
+
throw new Error("WalletClient must have an account attached");
|
|
5148
4508
|
}
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
4509
|
+
const signature = await walletClient.signTypedData({
|
|
4510
|
+
account,
|
|
4511
|
+
...generateBillingSigData(product, expiry)
|
|
4512
|
+
});
|
|
4513
|
+
return { signature, expiry };
|
|
5152
4514
|
}
|
|
5153
4515
|
|
|
5154
|
-
// src/client/common/utils/
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
var import_viem6 = require("viem");
|
|
5158
|
-
function validateAppName(name) {
|
|
5159
|
-
if (!name) {
|
|
5160
|
-
throw new Error("App name cannot be empty");
|
|
5161
|
-
}
|
|
5162
|
-
if (name.includes(" ")) {
|
|
5163
|
-
throw new Error("App name cannot contain spaces");
|
|
5164
|
-
}
|
|
5165
|
-
if (name.length > 50) {
|
|
5166
|
-
throw new Error("App name cannot be longer than 50 characters");
|
|
5167
|
-
}
|
|
5168
|
-
}
|
|
5169
|
-
function validateImageReference(value) {
|
|
5170
|
-
if (!value) {
|
|
5171
|
-
return "Image reference cannot be empty";
|
|
5172
|
-
}
|
|
5173
|
-
if (!value.includes("/")) {
|
|
5174
|
-
return "Image reference must contain at least one /";
|
|
5175
|
-
}
|
|
5176
|
-
return true;
|
|
5177
|
-
}
|
|
5178
|
-
function assertValidImageReference(value) {
|
|
5179
|
-
const result = validateImageReference(value);
|
|
5180
|
-
if (result !== true) {
|
|
5181
|
-
throw new Error(result);
|
|
5182
|
-
}
|
|
4516
|
+
// src/client/common/utils/userapi.ts
|
|
4517
|
+
function isJsonObject(value) {
|
|
4518
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5183
4519
|
}
|
|
5184
|
-
function
|
|
5185
|
-
const
|
|
5186
|
-
|
|
5187
|
-
if (imageName.includes(":")) {
|
|
5188
|
-
imageName = imageName.split(":")[0];
|
|
5189
|
-
}
|
|
5190
|
-
return imageName;
|
|
4520
|
+
function readString(obj, key) {
|
|
4521
|
+
const v = obj[key];
|
|
4522
|
+
return typeof v === "string" ? v : void 0;
|
|
5191
4523
|
}
|
|
5192
|
-
function
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
}
|
|
5196
|
-
if (!import_fs.default.existsSync(value)) {
|
|
5197
|
-
return "File does not exist";
|
|
5198
|
-
}
|
|
5199
|
-
return true;
|
|
4524
|
+
function readNumber(obj, key) {
|
|
4525
|
+
const v = obj[key];
|
|
4526
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
5200
4527
|
}
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
4528
|
+
var MAX_ADDRESS_COUNT = 5;
|
|
4529
|
+
var CanViewAppLogsPermission = "0x2fd3f2fe";
|
|
4530
|
+
var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
|
|
4531
|
+
var CanUpdateAppProfilePermission = "0x036fef61";
|
|
4532
|
+
function getDefaultClientId() {
|
|
4533
|
+
const version = true ? "0.2.1-dev" : "0.0.0";
|
|
4534
|
+
return `ecloud-sdk/v${version}`;
|
|
5206
4535
|
}
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
4536
|
+
var UserApiClient = class {
|
|
4537
|
+
constructor(config, walletClient, publicClient, clientId) {
|
|
4538
|
+
this.config = config;
|
|
4539
|
+
this.walletClient = walletClient;
|
|
4540
|
+
this.publicClient = publicClient;
|
|
4541
|
+
this.clientId = clientId || getDefaultClientId();
|
|
5210
4542
|
}
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
4543
|
+
/**
|
|
4544
|
+
* Get the address of the connected wallet
|
|
4545
|
+
*/
|
|
4546
|
+
get address() {
|
|
4547
|
+
const account = this.walletClient.account;
|
|
4548
|
+
if (!account) {
|
|
4549
|
+
throw new Error("WalletClient must have an account attached");
|
|
5214
4550
|
}
|
|
4551
|
+
return account.address;
|
|
5215
4552
|
}
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
}
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
return
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
4553
|
+
async getInfos(appIDs, addressCount = 1) {
|
|
4554
|
+
const count = Math.min(addressCount, MAX_ADDRESS_COUNT);
|
|
4555
|
+
const endpoint = `${this.config.userApiServerURL}/info`;
|
|
4556
|
+
const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
|
|
4557
|
+
const res = await this.makeAuthenticatedRequest(url, CanViewSensitiveAppInfoPermission);
|
|
4558
|
+
const result = await res.json();
|
|
4559
|
+
return result.apps.map((app, i) => {
|
|
4560
|
+
const evmAddresses = app.addresses?.data?.evmAddresses?.slice(0, count) || [];
|
|
4561
|
+
const solanaAddresses = app.addresses?.data?.solanaAddresses?.slice(0, count) || [];
|
|
4562
|
+
return {
|
|
4563
|
+
address: appIDs[i],
|
|
4564
|
+
status: app.app_status,
|
|
4565
|
+
ip: app.ip,
|
|
4566
|
+
machineType: app.machine_type,
|
|
4567
|
+
profile: app.profile,
|
|
4568
|
+
metrics: app.metrics,
|
|
4569
|
+
evmAddresses,
|
|
4570
|
+
solanaAddresses
|
|
4571
|
+
};
|
|
4572
|
+
});
|
|
5229
4573
|
}
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
4574
|
+
/**
|
|
4575
|
+
* Get app details from UserAPI (includes releases and build/provenance info when available).
|
|
4576
|
+
*
|
|
4577
|
+
* Endpoint: GET /apps/:appAddress
|
|
4578
|
+
*/
|
|
4579
|
+
async getApp(appAddress) {
|
|
4580
|
+
const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
|
|
4581
|
+
const res = await this.makeAuthenticatedRequest(endpoint);
|
|
4582
|
+
const raw = await res.json();
|
|
4583
|
+
if (!isJsonObject(raw)) {
|
|
4584
|
+
throw new Error("Unexpected /apps/:id response: expected object");
|
|
4585
|
+
}
|
|
4586
|
+
const id = readString(raw, "id");
|
|
4587
|
+
if (!id) {
|
|
4588
|
+
throw new Error("Unexpected /apps/:id response: missing 'id'");
|
|
4589
|
+
}
|
|
4590
|
+
const releasesRaw = raw.releases;
|
|
4591
|
+
const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
|
|
4592
|
+
return {
|
|
4593
|
+
id,
|
|
4594
|
+
creator: readString(raw, "creator"),
|
|
4595
|
+
contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
|
|
4596
|
+
releases
|
|
4597
|
+
};
|
|
5234
4598
|
}
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
4599
|
+
/**
|
|
4600
|
+
* Get available SKUs (instance types) from UserAPI
|
|
4601
|
+
*/
|
|
4602
|
+
async getSKUs() {
|
|
4603
|
+
const endpoint = `${this.config.userApiServerURL}/skus`;
|
|
4604
|
+
const response = await this.makeAuthenticatedRequest(endpoint);
|
|
4605
|
+
const result = await response.json();
|
|
4606
|
+
return {
|
|
4607
|
+
skus: result.skus || result.SKUs || []
|
|
4608
|
+
};
|
|
5239
4609
|
}
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
}
|
|
5245
|
-
|
|
5246
|
-
return
|
|
4610
|
+
/**
|
|
4611
|
+
* Get logs for an app
|
|
4612
|
+
*/
|
|
4613
|
+
async getLogs(appID) {
|
|
4614
|
+
const endpoint = `${this.config.userApiServerURL}/logs/${appID}`;
|
|
4615
|
+
const response = await this.makeAuthenticatedRequest(endpoint, CanViewAppLogsPermission);
|
|
4616
|
+
return await response.text();
|
|
5247
4617
|
}
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
4618
|
+
/**
|
|
4619
|
+
* Get statuses for apps
|
|
4620
|
+
*/
|
|
4621
|
+
async getStatuses(appIDs) {
|
|
4622
|
+
const endpoint = `${this.config.userApiServerURL}/status`;
|
|
4623
|
+
const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
|
|
4624
|
+
const response = await this.makeAuthenticatedRequest(url);
|
|
4625
|
+
const result = await response.json();
|
|
4626
|
+
const apps = result.apps || result.Apps || [];
|
|
4627
|
+
return apps.map((app, i) => ({
|
|
4628
|
+
address: app.address || appIDs[i],
|
|
4629
|
+
status: app.status || app.Status || ""
|
|
4630
|
+
}));
|
|
5255
4631
|
}
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
4632
|
+
/**
|
|
4633
|
+
* Upload app profile information with optional image
|
|
4634
|
+
*
|
|
4635
|
+
* @param appAddress - The app's contract address
|
|
4636
|
+
* @param name - Display name for the app
|
|
4637
|
+
* @param options - Optional fields including website, description, xURL, and image
|
|
4638
|
+
* @param options.image - Image file as Blob or File (browser: from input element, Node.js: new Blob([buffer]))
|
|
4639
|
+
* @param options.imageName - Filename for the image (required if image is provided)
|
|
4640
|
+
*/
|
|
4641
|
+
async uploadAppProfile(appAddress, name, options) {
|
|
4642
|
+
const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}/profile`;
|
|
4643
|
+
const formData = new FormData();
|
|
4644
|
+
formData.append("name", name);
|
|
4645
|
+
if (options?.website) {
|
|
4646
|
+
formData.append("website", options.website);
|
|
5261
4647
|
}
|
|
5262
|
-
if (
|
|
5263
|
-
|
|
4648
|
+
if (options?.description) {
|
|
4649
|
+
formData.append("description", options.description);
|
|
4650
|
+
}
|
|
4651
|
+
if (options?.xURL) {
|
|
4652
|
+
formData.append("xURL", options.xURL);
|
|
4653
|
+
}
|
|
4654
|
+
if (options?.image) {
|
|
4655
|
+
const fileName = options.image instanceof File ? options.image.name : options.imageName || "image";
|
|
4656
|
+
formData.append("image", options.image, fileName);
|
|
4657
|
+
}
|
|
4658
|
+
const headers = {
|
|
4659
|
+
"x-client-id": this.clientId
|
|
4660
|
+
};
|
|
4661
|
+
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
4662
|
+
const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
|
|
4663
|
+
Object.assign(headers, authHeaders);
|
|
4664
|
+
try {
|
|
4665
|
+
const response = await import_axios.default.post(endpoint, formData, {
|
|
4666
|
+
headers,
|
|
4667
|
+
maxRedirects: 0,
|
|
4668
|
+
validateStatus: () => true,
|
|
4669
|
+
// Don't throw on any status
|
|
4670
|
+
maxContentLength: Infinity,
|
|
4671
|
+
// Allow large file uploads
|
|
4672
|
+
maxBodyLength: Infinity
|
|
4673
|
+
// Allow large file uploads
|
|
4674
|
+
});
|
|
4675
|
+
const status = response.status;
|
|
4676
|
+
if (status !== 200 && status !== 201) {
|
|
4677
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
4678
|
+
if (status === 403 && body.includes("Cloudflare") && body.includes("challenge-platform")) {
|
|
4679
|
+
throw new Error(
|
|
4680
|
+
`Cloudflare protection is blocking the request. This is likely due to bot detection.
|
|
4681
|
+
Status: ${status}`
|
|
4682
|
+
);
|
|
4683
|
+
}
|
|
4684
|
+
throw new Error(
|
|
4685
|
+
`UserAPI request failed: ${status} ${status >= 200 && status < 300 ? "OK" : "Error"} - ${body.substring(0, 500)}${body.length > 500 ? "..." : ""}`
|
|
4686
|
+
);
|
|
4687
|
+
}
|
|
4688
|
+
return response.data;
|
|
4689
|
+
} catch (error) {
|
|
4690
|
+
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
4691
|
+
const cause = error.cause?.message || error.cause || error.message;
|
|
4692
|
+
throw new Error(
|
|
4693
|
+
`Failed to connect to UserAPI at ${endpoint}: ${cause}
|
|
4694
|
+
Please check:
|
|
4695
|
+
1. Your internet connection
|
|
4696
|
+
2. The API server is accessible: ${this.config.userApiServerURL}
|
|
4697
|
+
3. Firewall/proxy settings`
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
throw error;
|
|
5264
4701
|
}
|
|
5265
|
-
} catch {
|
|
5266
|
-
return "Invalid X URL format";
|
|
5267
4702
|
}
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
4703
|
+
async makeAuthenticatedRequest(url, permission) {
|
|
4704
|
+
const headers = {
|
|
4705
|
+
"x-client-id": this.clientId
|
|
4706
|
+
};
|
|
4707
|
+
if (permission) {
|
|
4708
|
+
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
4709
|
+
const authHeaders = await this.generateAuthHeaders(permission, expiry);
|
|
4710
|
+
Object.assign(headers, authHeaders);
|
|
4711
|
+
}
|
|
4712
|
+
try {
|
|
4713
|
+
const response = await import_axios.default.get(url, {
|
|
4714
|
+
headers,
|
|
4715
|
+
maxRedirects: 0,
|
|
4716
|
+
validateStatus: () => true
|
|
4717
|
+
// Don't throw on any status
|
|
4718
|
+
});
|
|
4719
|
+
const status = response.status;
|
|
4720
|
+
const statusText = status >= 200 && status < 300 ? "OK" : "Error";
|
|
4721
|
+
if (status < 200 || status >= 300) {
|
|
4722
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
4723
|
+
throw new Error(`UserAPI request failed: ${status} ${statusText} - ${body}`);
|
|
4724
|
+
}
|
|
4725
|
+
return {
|
|
4726
|
+
json: async () => response.data,
|
|
4727
|
+
text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
|
|
4728
|
+
};
|
|
4729
|
+
} catch (error) {
|
|
4730
|
+
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
4731
|
+
const cause = error.cause?.message || error.cause || error.message;
|
|
4732
|
+
throw new Error(
|
|
4733
|
+
`Failed to connect to UserAPI at ${url}: ${cause}
|
|
4734
|
+
Please check:
|
|
4735
|
+
1. Your internet connection
|
|
4736
|
+
2. The API server is accessible: ${this.config.userApiServerURL}
|
|
4737
|
+
3. Firewall/proxy settings`
|
|
4738
|
+
);
|
|
4739
|
+
}
|
|
4740
|
+
throw error;
|
|
4741
|
+
}
|
|
5274
4742
|
}
|
|
5275
|
-
|
|
5276
|
-
|
|
4743
|
+
/**
|
|
4744
|
+
* Generate authentication headers for UserAPI requests
|
|
4745
|
+
*/
|
|
4746
|
+
async generateAuthHeaders(permission, expiry) {
|
|
4747
|
+
const { signature } = await calculatePermissionSignature({
|
|
4748
|
+
permission,
|
|
4749
|
+
expiry,
|
|
4750
|
+
appControllerAddress: this.config.appControllerAddress,
|
|
4751
|
+
publicClient: this.publicClient,
|
|
4752
|
+
walletClient: this.walletClient
|
|
4753
|
+
});
|
|
4754
|
+
return {
|
|
4755
|
+
Authorization: `Bearer ${stripHexPrefix(signature)}`,
|
|
4756
|
+
"X-eigenx-expiry": expiry.toString()
|
|
4757
|
+
};
|
|
5277
4758
|
}
|
|
5278
|
-
|
|
4759
|
+
};
|
|
4760
|
+
function transformAppReleaseBuild(raw) {
|
|
4761
|
+
if (!isJsonObject(raw)) return void 0;
|
|
4762
|
+
const depsRaw = raw.dependencies;
|
|
4763
|
+
const deps = isJsonObject(depsRaw) ? Object.fromEntries(
|
|
4764
|
+
Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
|
|
4765
|
+
const parsed = transformAppReleaseBuild(depRaw);
|
|
4766
|
+
return parsed ? [[digest, parsed]] : [];
|
|
4767
|
+
})
|
|
4768
|
+
) : void 0;
|
|
4769
|
+
return {
|
|
4770
|
+
buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
|
|
4771
|
+
billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
|
|
4772
|
+
repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
|
|
4773
|
+
gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
|
|
4774
|
+
status: readString(raw, "status"),
|
|
4775
|
+
buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
|
|
4776
|
+
imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
|
|
4777
|
+
imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
|
|
4778
|
+
imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
|
|
4779
|
+
provenanceJson: raw.provenance_json ?? raw.provenanceJson,
|
|
4780
|
+
provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
|
|
4781
|
+
createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
|
|
4782
|
+
updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
|
|
4783
|
+
errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
|
|
4784
|
+
dependencies: deps
|
|
4785
|
+
};
|
|
5279
4786
|
}
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
4787
|
+
function transformAppRelease(raw) {
|
|
4788
|
+
if (!isJsonObject(raw)) return void 0;
|
|
4789
|
+
return {
|
|
4790
|
+
appId: readString(raw, "appId") ?? readString(raw, "app_id"),
|
|
4791
|
+
rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
|
|
4792
|
+
imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
|
|
4793
|
+
registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
|
|
4794
|
+
publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
|
|
4795
|
+
encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
|
|
4796
|
+
upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
|
|
4797
|
+
createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
|
|
4798
|
+
createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
|
|
4799
|
+
build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
|
|
4800
|
+
};
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
// src/client/common/contract/watcher.ts
|
|
4804
|
+
var WATCH_POLL_INTERVAL_SECONDS = 5;
|
|
4805
|
+
var APP_STATUS_RUNNING = "Running";
|
|
4806
|
+
var APP_STATUS_FAILED = "Failed";
|
|
4807
|
+
async function watchUntilRunning(options, logger) {
|
|
4808
|
+
const { walletClient, publicClient, environmentConfig, appId } = options;
|
|
4809
|
+
const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
|
|
4810
|
+
let initialStatus;
|
|
4811
|
+
let initialIP;
|
|
4812
|
+
let hasChanged = false;
|
|
4813
|
+
const stopCondition = (status, ip) => {
|
|
4814
|
+
if (!initialStatus) {
|
|
4815
|
+
initialStatus = status;
|
|
4816
|
+
initialIP = ip;
|
|
4817
|
+
}
|
|
4818
|
+
if (status !== initialStatus) {
|
|
4819
|
+
hasChanged = true;
|
|
4820
|
+
}
|
|
4821
|
+
if (status === APP_STATUS_RUNNING && ip) {
|
|
4822
|
+
if (hasChanged || initialStatus !== APP_STATUS_RUNNING) {
|
|
4823
|
+
if (!initialIP || initialIP === "No IP assigned") {
|
|
4824
|
+
logger.info(`App is now running with IP: ${ip}`);
|
|
4825
|
+
} else {
|
|
4826
|
+
logger.info("App is now running");
|
|
4827
|
+
}
|
|
4828
|
+
return true;
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
if (status === APP_STATUS_FAILED) {
|
|
4832
|
+
throw new Error(`App entered ${status} state`);
|
|
4833
|
+
}
|
|
4834
|
+
return false;
|
|
4835
|
+
};
|
|
4836
|
+
while (true) {
|
|
4837
|
+
try {
|
|
4838
|
+
const info = await userApiClient.getInfos([appId], 1);
|
|
4839
|
+
if (info.length === 0) {
|
|
4840
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
4841
|
+
continue;
|
|
4842
|
+
}
|
|
4843
|
+
const appInfo = info[0];
|
|
4844
|
+
const currentStatus = appInfo.status;
|
|
4845
|
+
const currentIP = appInfo.ip || "";
|
|
4846
|
+
if (stopCondition(currentStatus, currentIP)) {
|
|
4847
|
+
return currentIP || void 0;
|
|
4848
|
+
}
|
|
4849
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
4850
|
+
} catch (error) {
|
|
4851
|
+
logger.warn(`Failed to fetch app info: ${error.message}`);
|
|
4852
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
4853
|
+
}
|
|
5286
4854
|
}
|
|
5287
|
-
|
|
5288
|
-
|
|
4855
|
+
}
|
|
4856
|
+
var APP_STATUS_STOPPED = "Stopped";
|
|
4857
|
+
async function watchUntilUpgradeComplete(options, logger) {
|
|
4858
|
+
const { walletClient, publicClient, environmentConfig, appId } = options;
|
|
4859
|
+
const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
|
|
4860
|
+
let initialStatus;
|
|
4861
|
+
let initialIP;
|
|
4862
|
+
let hasChanged = false;
|
|
4863
|
+
const stopCondition = (status, ip) => {
|
|
4864
|
+
if (!initialStatus) {
|
|
4865
|
+
initialStatus = status;
|
|
4866
|
+
initialIP = ip;
|
|
4867
|
+
if (status === APP_STATUS_STOPPED && ip) {
|
|
4868
|
+
logger.info("App upgrade complete.");
|
|
4869
|
+
logger.info(`Status: ${status}`);
|
|
4870
|
+
logger.info(`To start the app, run: ecloud compute app start ${appId}`);
|
|
4871
|
+
return true;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
if (status !== initialStatus) {
|
|
4875
|
+
hasChanged = true;
|
|
4876
|
+
}
|
|
4877
|
+
if (status === APP_STATUS_STOPPED && ip && hasChanged) {
|
|
4878
|
+
logger.info("App upgrade complete.");
|
|
4879
|
+
logger.info(`Status: ${status}`);
|
|
4880
|
+
logger.info(`To start the app, run: ecloud compute app start ${appId}`);
|
|
4881
|
+
return true;
|
|
4882
|
+
}
|
|
4883
|
+
if (status === APP_STATUS_RUNNING && ip && hasChanged) {
|
|
4884
|
+
if (!initialIP || initialIP === "No IP assigned") {
|
|
4885
|
+
logger.info(`App is now running with IP: ${ip}`);
|
|
4886
|
+
} else {
|
|
4887
|
+
logger.info("App is now running");
|
|
4888
|
+
}
|
|
4889
|
+
return true;
|
|
4890
|
+
}
|
|
4891
|
+
if (status === APP_STATUS_FAILED) {
|
|
4892
|
+
throw new Error(`App entered ${status} state`);
|
|
4893
|
+
}
|
|
4894
|
+
return false;
|
|
4895
|
+
};
|
|
4896
|
+
while (true) {
|
|
4897
|
+
try {
|
|
4898
|
+
const info = await userApiClient.getInfos([appId], 1);
|
|
4899
|
+
if (info.length === 0) {
|
|
4900
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
4901
|
+
continue;
|
|
4902
|
+
}
|
|
4903
|
+
const appInfo = info[0];
|
|
4904
|
+
const currentStatus = appInfo.status;
|
|
4905
|
+
const currentIP = appInfo.ip || "";
|
|
4906
|
+
if (stopCondition(currentStatus, currentIP)) {
|
|
4907
|
+
return;
|
|
4908
|
+
}
|
|
4909
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
4910
|
+
} catch (error) {
|
|
4911
|
+
logger.warn(`Failed to fetch app info: ${error.message}`);
|
|
4912
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
4913
|
+
}
|
|
5289
4914
|
}
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
4915
|
+
}
|
|
4916
|
+
function sleep(ms) {
|
|
4917
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
4918
|
+
}
|
|
4919
|
+
|
|
4920
|
+
// src/client/common/utils/validation.ts
|
|
4921
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
4922
|
+
var import_path = __toESM(require("path"), 1);
|
|
4923
|
+
var import_viem5 = require("viem");
|
|
4924
|
+
function validateAppName(name) {
|
|
4925
|
+
if (!name) {
|
|
4926
|
+
throw new Error("App name cannot be empty");
|
|
5293
4927
|
}
|
|
5294
|
-
if (
|
|
5295
|
-
|
|
5296
|
-
return `Image file size (${sizeMB} MB) exceeds maximum allowed size of 4 MB`;
|
|
4928
|
+
if (name.includes(" ")) {
|
|
4929
|
+
throw new Error("App name cannot contain spaces");
|
|
5297
4930
|
}
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
return "Image must be JPG or PNG format";
|
|
4931
|
+
if (name.length > 50) {
|
|
4932
|
+
throw new Error("App name cannot be longer than 50 characters");
|
|
5301
4933
|
}
|
|
5302
|
-
return void 0;
|
|
5303
4934
|
}
|
|
5304
|
-
function
|
|
5305
|
-
if (!
|
|
5306
|
-
|
|
4935
|
+
function validateImageReference(value) {
|
|
4936
|
+
if (!value) {
|
|
4937
|
+
return "Image reference cannot be empty";
|
|
5307
4938
|
}
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
return normalized;
|
|
4939
|
+
if (!value.includes("/")) {
|
|
4940
|
+
return "Image reference must contain at least one /";
|
|
5311
4941
|
}
|
|
5312
|
-
|
|
4942
|
+
return true;
|
|
5313
4943
|
}
|
|
5314
|
-
function
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
case "private":
|
|
5319
|
-
return { logRedirect: "always", publicLogs: false };
|
|
5320
|
-
case "off":
|
|
5321
|
-
return { logRedirect: "", publicLogs: false };
|
|
5322
|
-
default:
|
|
5323
|
-
throw new Error(
|
|
5324
|
-
`Invalid log-visibility value: ${logVisibility} (must be public, private, or off)`
|
|
5325
|
-
);
|
|
4944
|
+
function assertValidImageReference(value) {
|
|
4945
|
+
const result = validateImageReference(value);
|
|
4946
|
+
if (result !== true) {
|
|
4947
|
+
throw new Error(result);
|
|
5326
4948
|
}
|
|
5327
4949
|
}
|
|
5328
|
-
function
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
case "enable":
|
|
5334
|
-
return "always";
|
|
5335
|
-
case "disable":
|
|
5336
|
-
return "never";
|
|
5337
|
-
default:
|
|
5338
|
-
throw new Error(
|
|
5339
|
-
`Invalid resource-usage-monitoring value: ${resourceUsageMonitoring} (must be enable or disable)`
|
|
5340
|
-
);
|
|
4950
|
+
function extractAppNameFromImage(imageRef) {
|
|
4951
|
+
const parts = imageRef.split("/");
|
|
4952
|
+
let imageName = parts.length > 1 ? parts[parts.length - 1] : imageRef;
|
|
4953
|
+
if (imageName.includes(":")) {
|
|
4954
|
+
imageName = imageName.split(":")[0];
|
|
5341
4955
|
}
|
|
4956
|
+
return imageName;
|
|
5342
4957
|
}
|
|
5343
|
-
function
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
function sanitizeString(s) {
|
|
5347
|
-
return s.trim().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5348
|
-
}
|
|
5349
|
-
function sanitizeURL(rawURL) {
|
|
5350
|
-
rawURL = rawURL.trim();
|
|
5351
|
-
if (!hasScheme(rawURL)) {
|
|
5352
|
-
rawURL = "https://" + rawURL;
|
|
4958
|
+
function validateFilePath(value) {
|
|
4959
|
+
if (!value) {
|
|
4960
|
+
return "File path cannot be empty";
|
|
5353
4961
|
}
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
throw new Error(err);
|
|
4962
|
+
if (!import_fs.default.existsSync(value)) {
|
|
4963
|
+
return "File does not exist";
|
|
5357
4964
|
}
|
|
5358
|
-
return
|
|
4965
|
+
return true;
|
|
5359
4966
|
}
|
|
5360
|
-
function
|
|
5361
|
-
|
|
5362
|
-
if (
|
|
5363
|
-
|
|
5364
|
-
rawURL = `https://x.com/${username}`;
|
|
5365
|
-
} else if (!hasScheme(rawURL)) {
|
|
5366
|
-
rawURL = "https://" + rawURL;
|
|
5367
|
-
}
|
|
5368
|
-
rawURL = rawURL.replace(/twitter\.com/g, "x.com");
|
|
5369
|
-
rawURL = rawURL.replace(/www\.x\.com/g, "x.com");
|
|
5370
|
-
const err = validateXURL(rawURL);
|
|
5371
|
-
if (err) {
|
|
5372
|
-
throw new Error(err);
|
|
4967
|
+
function assertValidFilePath(value) {
|
|
4968
|
+
const result = validateFilePath(value);
|
|
4969
|
+
if (result !== true) {
|
|
4970
|
+
throw new Error(result);
|
|
5373
4971
|
}
|
|
5374
|
-
return rawURL;
|
|
5375
4972
|
}
|
|
5376
|
-
function
|
|
5377
|
-
if (!
|
|
5378
|
-
throw new Error("
|
|
5379
|
-
}
|
|
5380
|
-
if (params.imageRef) {
|
|
5381
|
-
assertValidImageReference(params.imageRef);
|
|
5382
|
-
}
|
|
5383
|
-
if (params.dockerfilePath) {
|
|
5384
|
-
assertValidFilePath(params.dockerfilePath);
|
|
4973
|
+
function validateInstanceTypeSKU(sku, availableTypes) {
|
|
4974
|
+
if (!sku) {
|
|
4975
|
+
throw new Error("Instance type SKU cannot be empty");
|
|
5385
4976
|
}
|
|
5386
|
-
|
|
5387
|
-
|
|
4977
|
+
for (const it of availableTypes) {
|
|
4978
|
+
if (it.sku === sku) {
|
|
4979
|
+
return sku;
|
|
4980
|
+
}
|
|
5388
4981
|
}
|
|
5389
|
-
|
|
5390
|
-
|
|
4982
|
+
const validSKUs = availableTypes.map((it) => it.sku).join(", ");
|
|
4983
|
+
throw new Error(`Invalid instance-type value: ${sku} (must be one of: ${validSKUs})`);
|
|
4984
|
+
}
|
|
4985
|
+
function validatePrivateKeyFormat(key) {
|
|
4986
|
+
const keyWithoutPrefix = stripHexPrefix(key);
|
|
4987
|
+
if (!/^[0-9a-fA-F]{64}$/.test(keyWithoutPrefix)) {
|
|
4988
|
+
return false;
|
|
4989
|
+
}
|
|
4990
|
+
return true;
|
|
4991
|
+
}
|
|
4992
|
+
function assertValidPrivateKey(key) {
|
|
4993
|
+
if (!key) {
|
|
4994
|
+
throw new Error("Private key is required");
|
|
4995
|
+
}
|
|
4996
|
+
if (!validatePrivateKeyFormat(key)) {
|
|
4997
|
+
throw new Error(
|
|
4998
|
+
"Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)"
|
|
4999
|
+
);
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
function validateURL(rawURL) {
|
|
5003
|
+
if (!rawURL.trim()) {
|
|
5004
|
+
return "URL cannot be empty";
|
|
5005
|
+
}
|
|
5006
|
+
try {
|
|
5007
|
+
const url = new URL(rawURL);
|
|
5008
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
5009
|
+
return "URL scheme must be http or https";
|
|
5010
|
+
}
|
|
5011
|
+
} catch {
|
|
5012
|
+
return "Invalid URL format";
|
|
5013
|
+
}
|
|
5014
|
+
return void 0;
|
|
5015
|
+
}
|
|
5016
|
+
var VALID_X_HOSTS = ["twitter.com", "www.twitter.com", "x.com", "www.x.com"];
|
|
5017
|
+
function validateXURL(rawURL) {
|
|
5018
|
+
const urlErr = validateURL(rawURL);
|
|
5019
|
+
if (urlErr) {
|
|
5020
|
+
return urlErr;
|
|
5021
|
+
}
|
|
5022
|
+
try {
|
|
5023
|
+
const url = new URL(rawURL);
|
|
5024
|
+
const host = url.hostname.toLowerCase();
|
|
5025
|
+
if (!VALID_X_HOSTS.includes(host)) {
|
|
5026
|
+
return "URL must be a valid X/Twitter URL (x.com or twitter.com)";
|
|
5027
|
+
}
|
|
5028
|
+
if (!url.pathname || url.pathname === "/") {
|
|
5029
|
+
return "X URL must include a username or profile path";
|
|
5030
|
+
}
|
|
5031
|
+
} catch {
|
|
5032
|
+
return "Invalid X URL format";
|
|
5033
|
+
}
|
|
5034
|
+
return void 0;
|
|
5035
|
+
}
|
|
5036
|
+
var MAX_DESCRIPTION_LENGTH = 1e3;
|
|
5037
|
+
function validateDescription(description) {
|
|
5038
|
+
if (!description.trim()) {
|
|
5039
|
+
return "Description cannot be empty";
|
|
5040
|
+
}
|
|
5041
|
+
if (description.length > MAX_DESCRIPTION_LENGTH) {
|
|
5042
|
+
return `Description cannot exceed ${MAX_DESCRIPTION_LENGTH} characters`;
|
|
5043
|
+
}
|
|
5044
|
+
return void 0;
|
|
5045
|
+
}
|
|
5046
|
+
var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
|
|
5047
|
+
var VALID_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png"];
|
|
5048
|
+
function validateImagePath(filePath) {
|
|
5049
|
+
const cleanedPath = filePath.trim().replace(/^["']|["']$/g, "");
|
|
5050
|
+
if (!cleanedPath) {
|
|
5051
|
+
return "Image path cannot be empty";
|
|
5052
|
+
}
|
|
5053
|
+
if (!import_fs.default.existsSync(cleanedPath)) {
|
|
5054
|
+
return `Image file not found: ${cleanedPath}`;
|
|
5055
|
+
}
|
|
5056
|
+
const stats = import_fs.default.statSync(cleanedPath);
|
|
5057
|
+
if (stats.isDirectory()) {
|
|
5058
|
+
return "Path is a directory, not a file";
|
|
5059
|
+
}
|
|
5060
|
+
if (stats.size > MAX_IMAGE_SIZE) {
|
|
5061
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
5062
|
+
return `Image file size (${sizeMB} MB) exceeds maximum allowed size of 4 MB`;
|
|
5063
|
+
}
|
|
5064
|
+
const ext = import_path.default.extname(cleanedPath).toLowerCase();
|
|
5065
|
+
if (!VALID_IMAGE_EXTENSIONS.includes(ext)) {
|
|
5066
|
+
return "Image must be JPG or PNG format";
|
|
5067
|
+
}
|
|
5068
|
+
return void 0;
|
|
5069
|
+
}
|
|
5070
|
+
function validateAppID(appID) {
|
|
5071
|
+
if (!appID) {
|
|
5072
|
+
throw new Error("App ID is required");
|
|
5073
|
+
}
|
|
5074
|
+
const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
|
|
5075
|
+
if ((0, import_viem5.isAddress)(normalized)) {
|
|
5076
|
+
return normalized;
|
|
5077
|
+
}
|
|
5078
|
+
throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
|
|
5079
|
+
}
|
|
5080
|
+
function validateLogVisibility(logVisibility) {
|
|
5081
|
+
switch (logVisibility) {
|
|
5082
|
+
case "public":
|
|
5083
|
+
return { logRedirect: "always", publicLogs: true };
|
|
5084
|
+
case "private":
|
|
5085
|
+
return { logRedirect: "always", publicLogs: false };
|
|
5086
|
+
case "off":
|
|
5087
|
+
return { logRedirect: "", publicLogs: false };
|
|
5088
|
+
default:
|
|
5089
|
+
throw new Error(
|
|
5090
|
+
`Invalid log-visibility value: ${logVisibility} (must be public, private, or off)`
|
|
5091
|
+
);
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
function validateResourceUsageMonitoring(resourceUsageMonitoring) {
|
|
5095
|
+
if (!resourceUsageMonitoring) {
|
|
5096
|
+
return "always";
|
|
5097
|
+
}
|
|
5098
|
+
switch (resourceUsageMonitoring) {
|
|
5099
|
+
case "enable":
|
|
5100
|
+
return "always";
|
|
5101
|
+
case "disable":
|
|
5102
|
+
return "never";
|
|
5103
|
+
default:
|
|
5104
|
+
throw new Error(
|
|
5105
|
+
`Invalid resource-usage-monitoring value: ${resourceUsageMonitoring} (must be enable or disable)`
|
|
5106
|
+
);
|
|
5107
|
+
}
|
|
5108
|
+
}
|
|
5109
|
+
function hasScheme(rawURL) {
|
|
5110
|
+
return rawURL.startsWith("http://") || rawURL.startsWith("https://");
|
|
5111
|
+
}
|
|
5112
|
+
function sanitizeString(s) {
|
|
5113
|
+
return s.trim().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5114
|
+
}
|
|
5115
|
+
function sanitizeURL(rawURL) {
|
|
5116
|
+
rawURL = rawURL.trim();
|
|
5117
|
+
if (!hasScheme(rawURL)) {
|
|
5118
|
+
rawURL = "https://" + rawURL;
|
|
5119
|
+
}
|
|
5120
|
+
const err = validateURL(rawURL);
|
|
5121
|
+
if (err) {
|
|
5122
|
+
throw new Error(err);
|
|
5123
|
+
}
|
|
5124
|
+
return rawURL;
|
|
5125
|
+
}
|
|
5126
|
+
function sanitizeXURL(rawURL) {
|
|
5127
|
+
rawURL = rawURL.trim();
|
|
5128
|
+
if (!rawURL.includes("://") && !rawURL.includes(".")) {
|
|
5129
|
+
const username = rawURL.startsWith("@") ? rawURL.slice(1) : rawURL;
|
|
5130
|
+
rawURL = `https://x.com/${username}`;
|
|
5131
|
+
} else if (!hasScheme(rawURL)) {
|
|
5132
|
+
rawURL = "https://" + rawURL;
|
|
5133
|
+
}
|
|
5134
|
+
rawURL = rawURL.replace(/twitter\.com/g, "x.com");
|
|
5135
|
+
rawURL = rawURL.replace(/www\.x\.com/g, "x.com");
|
|
5136
|
+
const err = validateXURL(rawURL);
|
|
5137
|
+
if (err) {
|
|
5138
|
+
throw new Error(err);
|
|
5139
|
+
}
|
|
5140
|
+
return rawURL;
|
|
5141
|
+
}
|
|
5142
|
+
function validateDeployParams(params) {
|
|
5143
|
+
if (!params.dockerfilePath && !params.imageRef) {
|
|
5144
|
+
throw new Error("Either dockerfilePath or imageRef is required for deployment");
|
|
5145
|
+
}
|
|
5146
|
+
if (params.imageRef) {
|
|
5147
|
+
assertValidImageReference(params.imageRef);
|
|
5148
|
+
}
|
|
5149
|
+
if (params.dockerfilePath) {
|
|
5150
|
+
assertValidFilePath(params.dockerfilePath);
|
|
5151
|
+
}
|
|
5152
|
+
if (!params.appName) {
|
|
5153
|
+
throw new Error("App name is required");
|
|
5154
|
+
}
|
|
5155
|
+
validateAppName(params.appName);
|
|
5156
|
+
if (!params.instanceType) {
|
|
5391
5157
|
throw new Error("Instance type is required");
|
|
5392
5158
|
}
|
|
5393
5159
|
if (!params.logVisibility) {
|
|
@@ -5447,69 +5213,245 @@ function validateLogsParams(params) {
|
|
|
5447
5213
|
validateAppID(params.appID);
|
|
5448
5214
|
}
|
|
5449
5215
|
|
|
5450
|
-
// src/client/common/
|
|
5451
|
-
var
|
|
5452
|
-
var
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5216
|
+
// src/client/common/config/environment.ts
|
|
5217
|
+
var SEPOLIA_CHAIN_ID = 11155111;
|
|
5218
|
+
var MAINNET_CHAIN_ID = 1;
|
|
5219
|
+
var CommonAddresses = {
|
|
5220
|
+
ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
|
|
5221
|
+
};
|
|
5222
|
+
var ChainAddresses = {
|
|
5223
|
+
[MAINNET_CHAIN_ID]: {
|
|
5224
|
+
PermissionController: "0x25E5F8B1E7aDf44518d35D5B2271f114e081f0E5"
|
|
5225
|
+
},
|
|
5226
|
+
[SEPOLIA_CHAIN_ID]: {
|
|
5227
|
+
PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
|
|
5461
5228
|
}
|
|
5462
|
-
|
|
5229
|
+
};
|
|
5230
|
+
var BILLING_ENVIRONMENTS = {
|
|
5231
|
+
dev: {
|
|
5232
|
+
billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
|
|
5233
|
+
},
|
|
5234
|
+
prod: {
|
|
5235
|
+
billingApiServerURL: "https://billingapi.eigencloud.xyz"
|
|
5236
|
+
}
|
|
5237
|
+
};
|
|
5238
|
+
var ENVIRONMENTS = {
|
|
5239
|
+
"sepolia-dev": {
|
|
5240
|
+
name: "sepolia",
|
|
5241
|
+
build: "dev",
|
|
5242
|
+
appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
|
|
5243
|
+
permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
|
|
5244
|
+
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
5245
|
+
kmsServerURL: "http://10.128.0.57:8080",
|
|
5246
|
+
userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
|
|
5247
|
+
defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
|
|
5248
|
+
},
|
|
5249
|
+
sepolia: {
|
|
5250
|
+
name: "sepolia",
|
|
5251
|
+
build: "prod",
|
|
5252
|
+
appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
|
|
5253
|
+
permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
|
|
5254
|
+
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
5255
|
+
kmsServerURL: "http://10.128.15.203:8080",
|
|
5256
|
+
userApiServerURL: "https://userapi-compute-sepolia-prod.eigencloud.xyz",
|
|
5257
|
+
defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
|
|
5258
|
+
},
|
|
5259
|
+
"mainnet-alpha": {
|
|
5260
|
+
name: "mainnet-alpha",
|
|
5261
|
+
build: "prod",
|
|
5262
|
+
appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
|
|
5263
|
+
permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
|
|
5264
|
+
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
5265
|
+
kmsServerURL: "http://10.128.0.2:8080",
|
|
5266
|
+
userApiServerURL: "https://userapi-compute.eigencloud.xyz",
|
|
5267
|
+
defaultRPCURL: "https://ethereum-rpc.publicnode.com"
|
|
5268
|
+
}
|
|
5269
|
+
};
|
|
5270
|
+
var CHAIN_ID_TO_ENVIRONMENT = {
|
|
5271
|
+
[SEPOLIA_CHAIN_ID.toString()]: "sepolia",
|
|
5272
|
+
[MAINNET_CHAIN_ID.toString()]: "mainnet-alpha"
|
|
5273
|
+
};
|
|
5274
|
+
function getEnvironmentConfig(environment, chainID) {
|
|
5275
|
+
const env = ENVIRONMENTS[environment];
|
|
5276
|
+
if (!env) {
|
|
5277
|
+
throw new Error(`Unknown environment: ${environment}`);
|
|
5278
|
+
}
|
|
5279
|
+
if (!isEnvironmentAvailable(environment)) {
|
|
5463
5280
|
throw new Error(
|
|
5464
|
-
`
|
|
5281
|
+
`Environment ${environment} is not available in this build type. Available environments: ${getAvailableEnvironments().join(", ")}`
|
|
5465
5282
|
);
|
|
5466
5283
|
}
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
try {
|
|
5472
|
-
const chainID = await publicClient.getChainId();
|
|
5473
|
-
if (BigInt(chainID) !== environmentConfig.chainID) {
|
|
5474
|
-
throw new Error(`Chain ID mismatch: expected ${environmentConfig.chainID}, got ${chainID}`);
|
|
5284
|
+
if (chainID) {
|
|
5285
|
+
const expectedEnv = CHAIN_ID_TO_ENVIRONMENT[chainID.toString()];
|
|
5286
|
+
if (expectedEnv && expectedEnv !== environment) {
|
|
5287
|
+
throw new Error(`Environment ${environment} does not match chain ID ${chainID}`);
|
|
5475
5288
|
}
|
|
5476
|
-
} catch (err) {
|
|
5477
|
-
throw new Error(`Cannot connect to ${environmentConfig.name} RPC at ${rpcUrl}: ${err.message}`);
|
|
5478
5289
|
}
|
|
5479
|
-
const
|
|
5480
|
-
const account = (0, import_accounts4.privateKeyToAccount)(privateKeyHex);
|
|
5481
|
-
const selfAddress = account.address;
|
|
5290
|
+
const resolvedChainID = chainID || (environment === "sepolia" || environment === "sepolia-dev" ? SEPOLIA_CHAIN_ID : MAINNET_CHAIN_ID);
|
|
5482
5291
|
return {
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
environmentConfig,
|
|
5486
|
-
account,
|
|
5487
|
-
selfAddress
|
|
5292
|
+
...env,
|
|
5293
|
+
chainID: BigInt(resolvedChainID)
|
|
5488
5294
|
};
|
|
5489
5295
|
}
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5296
|
+
function getBillingEnvironmentConfig(build) {
|
|
5297
|
+
const config = BILLING_ENVIRONMENTS[build];
|
|
5298
|
+
if (!config) {
|
|
5299
|
+
throw new Error(`Unknown billing environment: ${build}`);
|
|
5494
5300
|
}
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5301
|
+
return config;
|
|
5302
|
+
}
|
|
5303
|
+
function getBuildType() {
|
|
5304
|
+
const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
|
|
5305
|
+
const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
|
|
5306
|
+
const buildType = buildTimeType || runtimeType;
|
|
5307
|
+
if (buildType === "dev") {
|
|
5308
|
+
return "dev";
|
|
5498
5309
|
}
|
|
5499
|
-
|
|
5500
|
-
`private key required. Please provide it via:
|
|
5501
|
-
\u2022 Option: privateKey in deploy options
|
|
5502
|
-
\u2022 Environment: export PRIVATE_KEY=YOUR_KEY
|
|
5503
|
-
\u2022 Keyring: (not yet implemented)`
|
|
5504
|
-
);
|
|
5310
|
+
return "prod";
|
|
5505
5311
|
}
|
|
5506
|
-
function
|
|
5507
|
-
const
|
|
5508
|
-
if (
|
|
5509
|
-
|
|
5312
|
+
function getAvailableEnvironments() {
|
|
5313
|
+
const buildType = getBuildType();
|
|
5314
|
+
if (buildType === "dev") {
|
|
5315
|
+
return ["sepolia-dev"];
|
|
5510
5316
|
}
|
|
5317
|
+
return ["sepolia", "mainnet-alpha"];
|
|
5318
|
+
}
|
|
5319
|
+
function isEnvironmentAvailable(environment) {
|
|
5320
|
+
return getAvailableEnvironments().includes(environment);
|
|
5321
|
+
}
|
|
5322
|
+
function isMainnet(environmentConfig) {
|
|
5323
|
+
return environmentConfig.chainID === BigInt(MAINNET_CHAIN_ID);
|
|
5324
|
+
}
|
|
5325
|
+
|
|
5326
|
+
// src/client/common/utils/preflight.ts
|
|
5327
|
+
async function doPreflightChecks(options, logger) {
|
|
5328
|
+
const { walletClient, publicClient } = options;
|
|
5329
|
+
logger.debug("Determining environment...");
|
|
5330
|
+
const environmentConfig = getEnvironmentConfig(options.environment || "sepolia");
|
|
5331
|
+
const account = walletClient.account;
|
|
5332
|
+
if (!account) {
|
|
5333
|
+
throw new Error("WalletClient must have an account attached");
|
|
5334
|
+
}
|
|
5335
|
+
logger.debug("Validating chain ID...");
|
|
5336
|
+
try {
|
|
5337
|
+
const chainID = await publicClient.getChainId();
|
|
5338
|
+
if (BigInt(chainID) !== environmentConfig.chainID) {
|
|
5339
|
+
throw new Error(`Chain ID mismatch: expected ${environmentConfig.chainID}, got ${chainID}`);
|
|
5340
|
+
}
|
|
5341
|
+
} catch (err) {
|
|
5342
|
+
throw new Error(
|
|
5343
|
+
`Cannot connect to ${environmentConfig.name} RPC at ${publicClient.transport.url}: ${err.message}`
|
|
5344
|
+
);
|
|
5345
|
+
}
|
|
5346
|
+
return {
|
|
5347
|
+
walletClient,
|
|
5348
|
+
publicClient,
|
|
5349
|
+
environmentConfig,
|
|
5350
|
+
selfAddress: account.address
|
|
5351
|
+
};
|
|
5352
|
+
}
|
|
5353
|
+
|
|
5354
|
+
// src/client/common/utils/logger.ts
|
|
5355
|
+
var defaultLogger = {
|
|
5356
|
+
info: (...args) => console.info(...args),
|
|
5357
|
+
warn: (...args) => console.warn(...args),
|
|
5358
|
+
error: (...args) => console.error(...args),
|
|
5359
|
+
debug: (...args) => console.debug(...args)
|
|
5360
|
+
};
|
|
5361
|
+
var getLogger = (verbose) => ({
|
|
5362
|
+
info: (...args) => console.info(...args),
|
|
5363
|
+
warn: (...args) => console.warn(...args),
|
|
5364
|
+
error: (...args) => console.error(...args),
|
|
5365
|
+
debug: (...args) => verbose && console.debug(...args)
|
|
5366
|
+
});
|
|
5367
|
+
|
|
5368
|
+
// src/client/common/utils/billing.ts
|
|
5369
|
+
function isSubscriptionActive(status) {
|
|
5370
|
+
return status === "active" || status === "trialing";
|
|
5511
5371
|
}
|
|
5512
5372
|
|
|
5373
|
+
// src/client/common/utils/billingapi.ts
|
|
5374
|
+
var import_axios2 = __toESM(require("axios"), 1);
|
|
5375
|
+
var BillingApiClient = class {
|
|
5376
|
+
constructor(config, walletClient) {
|
|
5377
|
+
this.config = config;
|
|
5378
|
+
this.walletClient = walletClient;
|
|
5379
|
+
}
|
|
5380
|
+
/**
|
|
5381
|
+
* Get the address of the connected wallet
|
|
5382
|
+
*/
|
|
5383
|
+
get address() {
|
|
5384
|
+
const account = this.walletClient.account;
|
|
5385
|
+
if (!account) {
|
|
5386
|
+
throw new Error("WalletClient must have an account attached");
|
|
5387
|
+
}
|
|
5388
|
+
return account.address;
|
|
5389
|
+
}
|
|
5390
|
+
async createSubscription(productId = "compute") {
|
|
5391
|
+
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
5392
|
+
const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId);
|
|
5393
|
+
return resp.json();
|
|
5394
|
+
}
|
|
5395
|
+
async getSubscription(productId = "compute") {
|
|
5396
|
+
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
5397
|
+
const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
|
|
5398
|
+
return resp.json();
|
|
5399
|
+
}
|
|
5400
|
+
async cancelSubscription(productId = "compute") {
|
|
5401
|
+
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
5402
|
+
await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
|
|
5403
|
+
}
|
|
5404
|
+
/**
|
|
5405
|
+
* Make an authenticated request to the billing API
|
|
5406
|
+
*/
|
|
5407
|
+
async makeAuthenticatedRequest(url, method, productId) {
|
|
5408
|
+
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
5409
|
+
const { signature } = await calculateBillingAuthSignature({
|
|
5410
|
+
walletClient: this.walletClient,
|
|
5411
|
+
product: productId,
|
|
5412
|
+
expiry
|
|
5413
|
+
});
|
|
5414
|
+
const headers = {
|
|
5415
|
+
Authorization: `Bearer ${signature}`,
|
|
5416
|
+
"X-Account": this.address,
|
|
5417
|
+
"X-Expiry": expiry.toString()
|
|
5418
|
+
};
|
|
5419
|
+
try {
|
|
5420
|
+
const response = await (0, import_axios2.default)({
|
|
5421
|
+
method,
|
|
5422
|
+
url,
|
|
5423
|
+
headers,
|
|
5424
|
+
timeout: 3e4,
|
|
5425
|
+
maxRedirects: 0,
|
|
5426
|
+
validateStatus: () => true
|
|
5427
|
+
// Don't throw on any status
|
|
5428
|
+
});
|
|
5429
|
+
const status = response.status;
|
|
5430
|
+
const statusText = status >= 200 && status < 300 ? "OK" : "Error";
|
|
5431
|
+
if (status < 200 || status >= 300) {
|
|
5432
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
5433
|
+
throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body}`);
|
|
5434
|
+
}
|
|
5435
|
+
return {
|
|
5436
|
+
json: async () => response.data,
|
|
5437
|
+
text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
|
|
5438
|
+
};
|
|
5439
|
+
} catch (error) {
|
|
5440
|
+
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
5441
|
+
const cause = error.cause?.message || error.cause || error.message;
|
|
5442
|
+
throw new Error(
|
|
5443
|
+
`Failed to connect to BillingAPI at ${url}: ${cause}
|
|
5444
|
+
Please check:
|
|
5445
|
+
1. Your internet connection
|
|
5446
|
+
2. The API server is accessible: ${this.config.billingApiServerURL}
|
|
5447
|
+
3. Firewall/proxy settings`
|
|
5448
|
+
);
|
|
5449
|
+
}
|
|
5450
|
+
throw error;
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
};
|
|
5454
|
+
|
|
5513
5455
|
// src/client/common/telemetry/noop.ts
|
|
5514
5456
|
var NoopClient = class {
|
|
5515
5457
|
/**
|
|
@@ -5716,7 +5658,9 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5716
5658
|
}
|
|
5717
5659
|
},
|
|
5718
5660
|
async () => {
|
|
5719
|
-
if (!options.
|
|
5661
|
+
if (!options.walletClient.account) {
|
|
5662
|
+
throw new Error("WalletClient must have an account attached");
|
|
5663
|
+
}
|
|
5720
5664
|
if (!options.imageRef) throw new Error("imageRef is required for deployment");
|
|
5721
5665
|
if (!options.imageDigest) throw new Error("imageDigest is required for deployment");
|
|
5722
5666
|
assertValidImageReference(options.imageRef);
|
|
@@ -5732,8 +5676,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5732
5676
|
logger.debug("Performing preflight checks...");
|
|
5733
5677
|
const preflightCtx = await doPreflightChecks(
|
|
5734
5678
|
{
|
|
5735
|
-
|
|
5736
|
-
|
|
5679
|
+
walletClient: options.walletClient,
|
|
5680
|
+
publicClient: options.publicClient,
|
|
5737
5681
|
environment: options.environment
|
|
5738
5682
|
},
|
|
5739
5683
|
logger
|
|
@@ -5743,12 +5687,12 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5743
5687
|
const salt = generateRandomSalt();
|
|
5744
5688
|
logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
|
|
5745
5689
|
logger.debug("Calculating app ID...");
|
|
5746
|
-
const appIDToBeDeployed = await calculateAppID(
|
|
5747
|
-
preflightCtx.
|
|
5748
|
-
|
|
5749
|
-
preflightCtx.
|
|
5690
|
+
const appIDToBeDeployed = await calculateAppID({
|
|
5691
|
+
publicClient: preflightCtx.publicClient,
|
|
5692
|
+
environmentConfig: preflightCtx.environmentConfig,
|
|
5693
|
+
ownerAddress: preflightCtx.selfAddress,
|
|
5750
5694
|
salt
|
|
5751
|
-
);
|
|
5695
|
+
});
|
|
5752
5696
|
logger.info(``);
|
|
5753
5697
|
logger.info(`App ID: ${appIDToBeDeployed}`);
|
|
5754
5698
|
logger.info(``);
|
|
@@ -5766,12 +5710,13 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5766
5710
|
logger.debug("Preparing deploy batch...");
|
|
5767
5711
|
const batch = await prepareDeployBatch(
|
|
5768
5712
|
{
|
|
5769
|
-
|
|
5770
|
-
|
|
5713
|
+
walletClient: preflightCtx.walletClient,
|
|
5714
|
+
publicClient: preflightCtx.publicClient,
|
|
5771
5715
|
environmentConfig: preflightCtx.environmentConfig,
|
|
5772
5716
|
salt,
|
|
5773
5717
|
release,
|
|
5774
|
-
publicLogs
|
|
5718
|
+
publicLogs,
|
|
5719
|
+
imageRef: options.imageRef
|
|
5775
5720
|
},
|
|
5776
5721
|
logger
|
|
5777
5722
|
);
|
|
@@ -5798,8 +5743,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5798
5743
|
);
|
|
5799
5744
|
}
|
|
5800
5745
|
function validateDeployOptions(options) {
|
|
5801
|
-
if (!options.
|
|
5802
|
-
throw new Error("
|
|
5746
|
+
if (!options.walletClient.account) {
|
|
5747
|
+
throw new Error("WalletClient must have an account attached");
|
|
5803
5748
|
}
|
|
5804
5749
|
if (!options.dockerfilePath && !options.imageRef) {
|
|
5805
5750
|
throw new Error("Either dockerfilePath or imageRef is required for deployment");
|
|
@@ -5838,8 +5783,8 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5838
5783
|
logger.debug("Performing preflight checks...");
|
|
5839
5784
|
const preflightCtx = await doPreflightChecks(
|
|
5840
5785
|
{
|
|
5841
|
-
|
|
5842
|
-
|
|
5786
|
+
walletClient: options.walletClient,
|
|
5787
|
+
publicClient: options.publicClient,
|
|
5843
5788
|
environment: options.environment
|
|
5844
5789
|
},
|
|
5845
5790
|
logger
|
|
@@ -5856,12 +5801,12 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5856
5801
|
const salt = generateRandomSalt();
|
|
5857
5802
|
logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
|
|
5858
5803
|
logger.debug("Calculating app ID...");
|
|
5859
|
-
const appIDToBeDeployed = await calculateAppID(
|
|
5860
|
-
preflightCtx.
|
|
5861
|
-
|
|
5862
|
-
preflightCtx.
|
|
5804
|
+
const appIDToBeDeployed = await calculateAppID({
|
|
5805
|
+
publicClient: preflightCtx.publicClient,
|
|
5806
|
+
environmentConfig: preflightCtx.environmentConfig,
|
|
5807
|
+
ownerAddress: preflightCtx.selfAddress,
|
|
5863
5808
|
salt
|
|
5864
|
-
);
|
|
5809
|
+
});
|
|
5865
5810
|
logger.info(``);
|
|
5866
5811
|
logger.info(`App ID: ${appIDToBeDeployed}`);
|
|
5867
5812
|
logger.info(``);
|
|
@@ -5882,8 +5827,8 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5882
5827
|
logger.info("Deploying on-chain...");
|
|
5883
5828
|
const deployResult = await deployApp(
|
|
5884
5829
|
{
|
|
5885
|
-
|
|
5886
|
-
|
|
5830
|
+
walletClient: preflightCtx.walletClient,
|
|
5831
|
+
publicClient: preflightCtx.publicClient,
|
|
5887
5832
|
environmentConfig: preflightCtx.environmentConfig,
|
|
5888
5833
|
salt,
|
|
5889
5834
|
release,
|
|
@@ -5896,8 +5841,8 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5896
5841
|
logger.info("Waiting for app to start...");
|
|
5897
5842
|
const ipAddress = await watchUntilRunning(
|
|
5898
5843
|
{
|
|
5899
|
-
|
|
5900
|
-
|
|
5844
|
+
walletClient: preflightCtx.walletClient,
|
|
5845
|
+
publicClient: preflightCtx.publicClient,
|
|
5901
5846
|
environmentConfig: preflightCtx.environmentConfig,
|
|
5902
5847
|
appId: deployResult.appId
|
|
5903
5848
|
},
|
|
@@ -5914,12 +5859,10 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5914
5859
|
);
|
|
5915
5860
|
}
|
|
5916
5861
|
async function checkQuotaAvailable(preflightCtx) {
|
|
5917
|
-
const
|
|
5918
|
-
const environmentConfig = preflightCtx.environmentConfig;
|
|
5919
|
-
const userAddress = preflightCtx.selfAddress;
|
|
5862
|
+
const { publicClient, environmentConfig, selfAddress: userAddress } = preflightCtx;
|
|
5920
5863
|
let maxQuota;
|
|
5921
5864
|
try {
|
|
5922
|
-
maxQuota = await getMaxActiveAppsPerUser(
|
|
5865
|
+
maxQuota = await getMaxActiveAppsPerUser(publicClient, environmentConfig, userAddress);
|
|
5923
5866
|
} catch (err) {
|
|
5924
5867
|
throw new Error(`failed to get quota limit: ${err.message}`);
|
|
5925
5868
|
}
|
|
@@ -5930,7 +5873,7 @@ async function checkQuotaAvailable(preflightCtx) {
|
|
|
5930
5873
|
}
|
|
5931
5874
|
let activeCount;
|
|
5932
5875
|
try {
|
|
5933
|
-
activeCount = await getActiveAppCount(
|
|
5876
|
+
activeCount = await getActiveAppCount(publicClient, environmentConfig, userAddress);
|
|
5934
5877
|
} catch (err) {
|
|
5935
5878
|
throw new Error(`failed to get active app count: ${err.message}`);
|
|
5936
5879
|
}
|
|
@@ -5961,8 +5904,8 @@ async function prepareDeploy(options, logger = defaultLogger) {
|
|
|
5961
5904
|
logger.debug("Performing preflight checks...");
|
|
5962
5905
|
const preflightCtx = await doPreflightChecks(
|
|
5963
5906
|
{
|
|
5964
|
-
|
|
5965
|
-
|
|
5907
|
+
walletClient: options.walletClient,
|
|
5908
|
+
publicClient: options.publicClient,
|
|
5966
5909
|
environment: options.environment
|
|
5967
5910
|
},
|
|
5968
5911
|
logger
|
|
@@ -5979,12 +5922,12 @@ async function prepareDeploy(options, logger = defaultLogger) {
|
|
|
5979
5922
|
const salt = generateRandomSalt();
|
|
5980
5923
|
logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
|
|
5981
5924
|
logger.debug("Calculating app ID...");
|
|
5982
|
-
const appIDToBeDeployed = await calculateAppID(
|
|
5983
|
-
preflightCtx.
|
|
5984
|
-
|
|
5985
|
-
preflightCtx.
|
|
5925
|
+
const appIDToBeDeployed = await calculateAppID({
|
|
5926
|
+
publicClient: preflightCtx.publicClient,
|
|
5927
|
+
environmentConfig: preflightCtx.environmentConfig,
|
|
5928
|
+
ownerAddress: preflightCtx.selfAddress,
|
|
5986
5929
|
salt
|
|
5987
|
-
);
|
|
5930
|
+
});
|
|
5988
5931
|
logger.info(``);
|
|
5989
5932
|
logger.info(`App ID: ${appIDToBeDeployed}`);
|
|
5990
5933
|
logger.info(``);
|
|
@@ -6005,12 +5948,13 @@ async function prepareDeploy(options, logger = defaultLogger) {
|
|
|
6005
5948
|
logger.debug("Preparing deploy batch...");
|
|
6006
5949
|
const batch = await prepareDeployBatch(
|
|
6007
5950
|
{
|
|
6008
|
-
|
|
6009
|
-
|
|
5951
|
+
walletClient: preflightCtx.walletClient,
|
|
5952
|
+
publicClient: preflightCtx.publicClient,
|
|
6010
5953
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6011
5954
|
salt,
|
|
6012
5955
|
release,
|
|
6013
|
-
publicLogs
|
|
5956
|
+
publicLogs,
|
|
5957
|
+
imageRef: finalImageRef
|
|
6014
5958
|
},
|
|
6015
5959
|
logger
|
|
6016
5960
|
);
|
|
@@ -6055,25 +5999,23 @@ async function executeDeploy(options) {
|
|
|
6055
5999
|
}
|
|
6056
6000
|
);
|
|
6057
6001
|
}
|
|
6058
|
-
async function watchDeployment(appId,
|
|
6002
|
+
async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
|
|
6059
6003
|
return withSDKTelemetry(
|
|
6060
6004
|
{
|
|
6061
6005
|
functionName: "watchDeployment",
|
|
6062
6006
|
skipTelemetry,
|
|
6063
6007
|
properties: {
|
|
6064
|
-
environment
|
|
6008
|
+
environment: environmentConfig.name
|
|
6065
6009
|
}
|
|
6066
6010
|
},
|
|
6067
6011
|
async () => {
|
|
6068
|
-
const environmentConfig = getEnvironmentConfig(environment);
|
|
6069
6012
|
logger.info("Waiting for app to start...");
|
|
6070
6013
|
return watchUntilRunning(
|
|
6071
6014
|
{
|
|
6072
|
-
|
|
6073
|
-
|
|
6015
|
+
walletClient,
|
|
6016
|
+
publicClient,
|
|
6074
6017
|
environmentConfig,
|
|
6075
|
-
appId
|
|
6076
|
-
clientId
|
|
6018
|
+
appId
|
|
6077
6019
|
},
|
|
6078
6020
|
logger
|
|
6079
6021
|
);
|
|
@@ -6082,18 +6024,12 @@ async function watchDeployment(appId, privateKey, rpcUrl, environment, logger =
|
|
|
6082
6024
|
}
|
|
6083
6025
|
|
|
6084
6026
|
// src/client/common/utils/permissions.ts
|
|
6085
|
-
var import_viem8 = require("viem");
|
|
6086
6027
|
var AnyoneCanCallAddress = "0x493219d9949348178af1f58740655951a8cd110c";
|
|
6087
6028
|
var ApiPermissionsTarget = "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d";
|
|
6088
6029
|
var CanViewAppLogsPermission2 = "0x2fd3f2fe";
|
|
6089
6030
|
async function checkAppLogPermission(preflightCtx, appAddress, logger) {
|
|
6090
|
-
const chain = getChainFromID(preflightCtx.environmentConfig.chainID);
|
|
6091
|
-
const publicClient = (0, import_viem8.createPublicClient)({
|
|
6092
|
-
chain,
|
|
6093
|
-
transport: (0, import_viem8.http)(preflightCtx.rpcUrl)
|
|
6094
|
-
});
|
|
6095
6031
|
try {
|
|
6096
|
-
const canCall = await publicClient.readContract({
|
|
6032
|
+
const canCall = await preflightCtx.publicClient.readContract({
|
|
6097
6033
|
address: preflightCtx.environmentConfig.permissionControllerAddress,
|
|
6098
6034
|
abi: PermissionController_default,
|
|
6099
6035
|
functionName: "canCall",
|
|
@@ -6120,8 +6056,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
|
|
|
6120
6056
|
logger.debug("Performing preflight checks...");
|
|
6121
6057
|
const preflightCtx = await doPreflightChecks(
|
|
6122
6058
|
{
|
|
6123
|
-
|
|
6124
|
-
|
|
6059
|
+
walletClient: options.walletClient,
|
|
6060
|
+
publicClient: options.publicClient,
|
|
6125
6061
|
environment: options.environment
|
|
6126
6062
|
},
|
|
6127
6063
|
logger
|
|
@@ -6153,13 +6089,14 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
|
|
|
6153
6089
|
const needsPermissionChange = currentlyPublic !== publicLogs;
|
|
6154
6090
|
logger.debug("Preparing upgrade batch...");
|
|
6155
6091
|
const batch = await prepareUpgradeBatch({
|
|
6156
|
-
|
|
6157
|
-
|
|
6092
|
+
walletClient: preflightCtx.walletClient,
|
|
6093
|
+
publicClient: preflightCtx.publicClient,
|
|
6158
6094
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6159
|
-
|
|
6095
|
+
appID,
|
|
6160
6096
|
release,
|
|
6161
6097
|
publicLogs,
|
|
6162
|
-
needsPermissionChange
|
|
6098
|
+
needsPermissionChange,
|
|
6099
|
+
imageRef: options.imageRef
|
|
6163
6100
|
});
|
|
6164
6101
|
logger.debug("Estimating gas...");
|
|
6165
6102
|
const gasEstimate = await estimateBatchGas({
|
|
@@ -6183,8 +6120,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
|
|
|
6183
6120
|
);
|
|
6184
6121
|
}
|
|
6185
6122
|
function validateUpgradeOptions(options) {
|
|
6186
|
-
if (!options.
|
|
6187
|
-
throw new Error("
|
|
6123
|
+
if (!options.walletClient?.account) {
|
|
6124
|
+
throw new Error("walletClient with account is required for upgrade");
|
|
6188
6125
|
}
|
|
6189
6126
|
if (!options.appId) {
|
|
6190
6127
|
throw new Error("appId is required for upgrade");
|
|
@@ -6221,8 +6158,8 @@ async function upgrade(options, logger = defaultLogger) {
|
|
|
6221
6158
|
logger.debug("Performing preflight checks...");
|
|
6222
6159
|
const preflightCtx = await doPreflightChecks(
|
|
6223
6160
|
{
|
|
6224
|
-
|
|
6225
|
-
|
|
6161
|
+
walletClient: options.walletClient,
|
|
6162
|
+
publicClient: options.publicClient,
|
|
6226
6163
|
environment: options.environment
|
|
6227
6164
|
},
|
|
6228
6165
|
logger
|
|
@@ -6256,10 +6193,10 @@ async function upgrade(options, logger = defaultLogger) {
|
|
|
6256
6193
|
logger.info("Upgrading on-chain...");
|
|
6257
6194
|
const txHash = await upgradeApp(
|
|
6258
6195
|
{
|
|
6259
|
-
|
|
6260
|
-
|
|
6196
|
+
walletClient: preflightCtx.walletClient,
|
|
6197
|
+
publicClient: preflightCtx.publicClient,
|
|
6261
6198
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6262
|
-
|
|
6199
|
+
appID,
|
|
6263
6200
|
release,
|
|
6264
6201
|
publicLogs,
|
|
6265
6202
|
needsPermissionChange,
|
|
@@ -6271,8 +6208,8 @@ async function upgrade(options, logger = defaultLogger) {
|
|
|
6271
6208
|
logger.info("Waiting for upgrade to complete...");
|
|
6272
6209
|
await watchUntilUpgradeComplete(
|
|
6273
6210
|
{
|
|
6274
|
-
|
|
6275
|
-
|
|
6211
|
+
walletClient: preflightCtx.walletClient,
|
|
6212
|
+
publicClient: preflightCtx.publicClient,
|
|
6276
6213
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6277
6214
|
appId: appID
|
|
6278
6215
|
},
|
|
@@ -6299,8 +6236,8 @@ async function prepareUpgrade(options, logger = defaultLogger) {
|
|
|
6299
6236
|
logger.debug("Performing preflight checks...");
|
|
6300
6237
|
const preflightCtx = await doPreflightChecks(
|
|
6301
6238
|
{
|
|
6302
|
-
|
|
6303
|
-
|
|
6239
|
+
walletClient: options.walletClient,
|
|
6240
|
+
publicClient: options.publicClient,
|
|
6304
6241
|
environment: options.environment
|
|
6305
6242
|
},
|
|
6306
6243
|
logger
|
|
@@ -6333,13 +6270,14 @@ async function prepareUpgrade(options, logger = defaultLogger) {
|
|
|
6333
6270
|
const needsPermissionChange = currentlyPublic !== publicLogs;
|
|
6334
6271
|
logger.debug("Preparing upgrade batch...");
|
|
6335
6272
|
const batch = await prepareUpgradeBatch({
|
|
6336
|
-
|
|
6337
|
-
|
|
6273
|
+
walletClient: preflightCtx.walletClient,
|
|
6274
|
+
publicClient: preflightCtx.publicClient,
|
|
6338
6275
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6339
|
-
|
|
6276
|
+
appID,
|
|
6340
6277
|
release,
|
|
6341
6278
|
publicLogs,
|
|
6342
|
-
needsPermissionChange
|
|
6279
|
+
needsPermissionChange,
|
|
6280
|
+
imageRef: finalImageRef
|
|
6343
6281
|
});
|
|
6344
6282
|
logger.debug("Estimating gas...");
|
|
6345
6283
|
const gasEstimate = await estimateBatchGas({
|
|
@@ -6380,25 +6318,23 @@ async function executeUpgrade(options) {
|
|
|
6380
6318
|
}
|
|
6381
6319
|
);
|
|
6382
6320
|
}
|
|
6383
|
-
async function watchUpgrade(appId,
|
|
6321
|
+
async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
|
|
6384
6322
|
return withSDKTelemetry(
|
|
6385
6323
|
{
|
|
6386
6324
|
functionName: "watchUpgrade",
|
|
6387
6325
|
skipTelemetry,
|
|
6388
6326
|
properties: {
|
|
6389
|
-
environment
|
|
6327
|
+
environment: environmentConfig.name
|
|
6390
6328
|
}
|
|
6391
6329
|
},
|
|
6392
6330
|
async () => {
|
|
6393
|
-
const environmentConfig = getEnvironmentConfig(environment);
|
|
6394
6331
|
logger.info("Waiting for upgrade to complete...");
|
|
6395
6332
|
await watchUntilUpgradeComplete(
|
|
6396
6333
|
{
|
|
6397
|
-
|
|
6398
|
-
|
|
6334
|
+
walletClient,
|
|
6335
|
+
publicClient,
|
|
6399
6336
|
environmentConfig,
|
|
6400
|
-
appId
|
|
6401
|
-
clientId
|
|
6337
|
+
appId
|
|
6402
6338
|
},
|
|
6403
6339
|
logger
|
|
6404
6340
|
);
|
|
@@ -6962,31 +6898,24 @@ async function watchLogs(appID, userApiClient, initialLogs) {
|
|
|
6962
6898
|
}
|
|
6963
6899
|
console.log("\nStopped watching");
|
|
6964
6900
|
}
|
|
6965
|
-
async function logs(options, logger = defaultLogger, skipTelemetry = false) {
|
|
6966
|
-
const skipTelemetryFlag = skipTelemetry || options.skipTelemetry || false;
|
|
6901
|
+
async function logs(options, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry = false) {
|
|
6967
6902
|
return withSDKTelemetry(
|
|
6968
6903
|
{
|
|
6969
6904
|
functionName: "logs",
|
|
6970
|
-
skipTelemetry
|
|
6971
|
-
properties: { environment:
|
|
6905
|
+
skipTelemetry,
|
|
6906
|
+
properties: { environment: environmentConfig.name }
|
|
6972
6907
|
},
|
|
6973
6908
|
async () => {
|
|
6974
6909
|
console.log();
|
|
6975
6910
|
if (!options.appID) {
|
|
6976
6911
|
throw new Error("appID is required for viewing logs");
|
|
6977
6912
|
}
|
|
6978
|
-
const environment = options.environment || "sepolia";
|
|
6979
|
-
const environmentConfig = getEnvironmentConfig(environment);
|
|
6980
|
-
const rpcUrl = options.rpcUrl || environmentConfig.defaultRPCURL;
|
|
6981
|
-
if (!rpcUrl) {
|
|
6982
|
-
throw new Error("RPC URL is required for authenticated requests");
|
|
6983
|
-
}
|
|
6984
6913
|
const appID = validateAppID(options.appID);
|
|
6985
6914
|
const formattedApp = formatAppDisplay(environmentConfig.name, appID, "");
|
|
6986
6915
|
const userApiClient = new UserApiClient(
|
|
6987
6916
|
environmentConfig,
|
|
6988
|
-
|
|
6989
|
-
|
|
6917
|
+
walletClient,
|
|
6918
|
+
publicClient,
|
|
6990
6919
|
options.clientId
|
|
6991
6920
|
);
|
|
6992
6921
|
let logsText;
|
|
@@ -7063,35 +6992,39 @@ async function logs(options, logger = defaultLogger, skipTelemetry = false) {
|
|
|
7063
6992
|
}
|
|
7064
6993
|
|
|
7065
6994
|
// src/client/modules/compute/app/index.ts
|
|
7066
|
-
var CONTROLLER_ABI = (0,
|
|
6995
|
+
var CONTROLLER_ABI = (0, import_viem6.parseAbi)([
|
|
7067
6996
|
"function startApp(address appId)",
|
|
7068
6997
|
"function stopApp(address appId)",
|
|
7069
6998
|
"function terminateApp(address appId)"
|
|
7070
6999
|
]);
|
|
7071
7000
|
function encodeStartAppData(appId) {
|
|
7072
|
-
return (0,
|
|
7001
|
+
return (0, import_viem6.encodeFunctionData)({
|
|
7073
7002
|
abi: CONTROLLER_ABI,
|
|
7074
7003
|
functionName: "startApp",
|
|
7075
7004
|
args: [appId]
|
|
7076
7005
|
});
|
|
7077
7006
|
}
|
|
7078
7007
|
function encodeStopAppData(appId) {
|
|
7079
|
-
return (0,
|
|
7008
|
+
return (0, import_viem6.encodeFunctionData)({
|
|
7080
7009
|
abi: CONTROLLER_ABI,
|
|
7081
7010
|
functionName: "stopApp",
|
|
7082
7011
|
args: [appId]
|
|
7083
7012
|
});
|
|
7084
7013
|
}
|
|
7085
7014
|
function encodeTerminateAppData(appId) {
|
|
7086
|
-
return (0,
|
|
7015
|
+
return (0, import_viem6.encodeFunctionData)({
|
|
7087
7016
|
abi: CONTROLLER_ABI,
|
|
7088
7017
|
functionName: "terminateApp",
|
|
7089
7018
|
args: [appId]
|
|
7090
7019
|
});
|
|
7091
7020
|
}
|
|
7092
7021
|
function createAppModule(ctx) {
|
|
7093
|
-
const
|
|
7022
|
+
const { walletClient, publicClient } = ctx;
|
|
7094
7023
|
const skipTelemetry = ctx.skipTelemetry || false;
|
|
7024
|
+
if (!walletClient.account) {
|
|
7025
|
+
throw new Error("WalletClient must have an account attached");
|
|
7026
|
+
}
|
|
7027
|
+
const account = walletClient.account;
|
|
7095
7028
|
const environment = getEnvironmentConfig(ctx.environment);
|
|
7096
7029
|
const logger = getLogger(ctx.verbose);
|
|
7097
7030
|
return {
|
|
@@ -7102,8 +7035,8 @@ function createAppModule(ctx) {
|
|
|
7102
7035
|
async deploy(opts) {
|
|
7103
7036
|
const result = await deploy(
|
|
7104
7037
|
{
|
|
7105
|
-
|
|
7106
|
-
|
|
7038
|
+
walletClient,
|
|
7039
|
+
publicClient,
|
|
7107
7040
|
environment: ctx.environment,
|
|
7108
7041
|
appName: opts.name,
|
|
7109
7042
|
instanceType: opts.instanceType,
|
|
@@ -7127,8 +7060,8 @@ function createAppModule(ctx) {
|
|
|
7127
7060
|
const result = await upgrade(
|
|
7128
7061
|
{
|
|
7129
7062
|
appId,
|
|
7130
|
-
|
|
7131
|
-
|
|
7063
|
+
walletClient,
|
|
7064
|
+
publicClient,
|
|
7132
7065
|
environment: ctx.environment,
|
|
7133
7066
|
instanceType: opts.instanceType,
|
|
7134
7067
|
dockerfilePath: opts.dockerfile,
|
|
@@ -7149,8 +7082,8 @@ function createAppModule(ctx) {
|
|
|
7149
7082
|
async prepareDeploy(opts) {
|
|
7150
7083
|
return prepareDeploy(
|
|
7151
7084
|
{
|
|
7152
|
-
|
|
7153
|
-
|
|
7085
|
+
walletClient,
|
|
7086
|
+
publicClient,
|
|
7154
7087
|
environment: ctx.environment,
|
|
7155
7088
|
appName: opts.name,
|
|
7156
7089
|
instanceType: opts.instanceType,
|
|
@@ -7167,8 +7100,8 @@ function createAppModule(ctx) {
|
|
|
7167
7100
|
async prepareDeployFromVerifiableBuild(opts) {
|
|
7168
7101
|
return prepareDeployFromVerifiableBuild(
|
|
7169
7102
|
{
|
|
7170
|
-
|
|
7171
|
-
|
|
7103
|
+
walletClient,
|
|
7104
|
+
publicClient,
|
|
7172
7105
|
environment: ctx.environment,
|
|
7173
7106
|
appName: opts.name,
|
|
7174
7107
|
instanceType: opts.instanceType,
|
|
@@ -7183,17 +7116,6 @@ function createAppModule(ctx) {
|
|
|
7183
7116
|
);
|
|
7184
7117
|
},
|
|
7185
7118
|
async executeDeploy(prepared, gas) {
|
|
7186
|
-
const account = (0, import_accounts5.privateKeyToAccount)(privateKey);
|
|
7187
|
-
const chain = getChainFromID(environment.chainID);
|
|
7188
|
-
const publicClient = (0, import_viem9.createPublicClient)({
|
|
7189
|
-
chain,
|
|
7190
|
-
transport: (0, import_viem9.http)(ctx.rpcUrl)
|
|
7191
|
-
});
|
|
7192
|
-
const walletClient = (0, import_viem9.createWalletClient)({
|
|
7193
|
-
account,
|
|
7194
|
-
chain,
|
|
7195
|
-
transport: (0, import_viem9.http)(ctx.rpcUrl)
|
|
7196
|
-
});
|
|
7197
7119
|
const result = await executeDeploy({
|
|
7198
7120
|
prepared,
|
|
7199
7121
|
context: {
|
|
@@ -7215,11 +7137,10 @@ function createAppModule(ctx) {
|
|
|
7215
7137
|
async watchDeployment(appId) {
|
|
7216
7138
|
return watchDeployment(
|
|
7217
7139
|
appId,
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7140
|
+
walletClient,
|
|
7141
|
+
publicClient,
|
|
7142
|
+
environment,
|
|
7221
7143
|
logger,
|
|
7222
|
-
ctx.clientId,
|
|
7223
7144
|
skipTelemetry
|
|
7224
7145
|
);
|
|
7225
7146
|
},
|
|
@@ -7228,8 +7149,8 @@ function createAppModule(ctx) {
|
|
|
7228
7149
|
return prepareUpgrade(
|
|
7229
7150
|
{
|
|
7230
7151
|
appId,
|
|
7231
|
-
|
|
7232
|
-
|
|
7152
|
+
walletClient,
|
|
7153
|
+
publicClient,
|
|
7233
7154
|
environment: ctx.environment,
|
|
7234
7155
|
instanceType: opts.instanceType,
|
|
7235
7156
|
dockerfilePath: opts.dockerfile,
|
|
@@ -7246,8 +7167,8 @@ function createAppModule(ctx) {
|
|
|
7246
7167
|
return prepareUpgradeFromVerifiableBuild(
|
|
7247
7168
|
{
|
|
7248
7169
|
appId,
|
|
7249
|
-
|
|
7250
|
-
|
|
7170
|
+
walletClient,
|
|
7171
|
+
publicClient,
|
|
7251
7172
|
environment: ctx.environment,
|
|
7252
7173
|
instanceType: opts.instanceType,
|
|
7253
7174
|
envFilePath: opts.envFile,
|
|
@@ -7261,17 +7182,6 @@ function createAppModule(ctx) {
|
|
|
7261
7182
|
);
|
|
7262
7183
|
},
|
|
7263
7184
|
async executeUpgrade(prepared, gas) {
|
|
7264
|
-
const account = (0, import_accounts5.privateKeyToAccount)(privateKey);
|
|
7265
|
-
const chain = getChainFromID(environment.chainID);
|
|
7266
|
-
const publicClient = (0, import_viem9.createPublicClient)({
|
|
7267
|
-
chain,
|
|
7268
|
-
transport: (0, import_viem9.http)(ctx.rpcUrl)
|
|
7269
|
-
});
|
|
7270
|
-
const walletClient = (0, import_viem9.createWalletClient)({
|
|
7271
|
-
account,
|
|
7272
|
-
chain,
|
|
7273
|
-
transport: (0, import_viem9.http)(ctx.rpcUrl)
|
|
7274
|
-
});
|
|
7275
7185
|
const result = await executeUpgrade({
|
|
7276
7186
|
prepared,
|
|
7277
7187
|
context: {
|
|
@@ -7290,15 +7200,7 @@ function createAppModule(ctx) {
|
|
|
7290
7200
|
};
|
|
7291
7201
|
},
|
|
7292
7202
|
async watchUpgrade(appId) {
|
|
7293
|
-
return watchUpgrade(
|
|
7294
|
-
appId,
|
|
7295
|
-
privateKey,
|
|
7296
|
-
ctx.rpcUrl,
|
|
7297
|
-
ctx.environment,
|
|
7298
|
-
logger,
|
|
7299
|
-
ctx.clientId,
|
|
7300
|
-
skipTelemetry
|
|
7301
|
-
);
|
|
7203
|
+
return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
|
|
7302
7204
|
},
|
|
7303
7205
|
// Profile management
|
|
7304
7206
|
async setProfile(appId, profile) {
|
|
@@ -7311,33 +7213,32 @@ function createAppModule(ctx) {
|
|
|
7311
7213
|
async () => {
|
|
7312
7214
|
const userApiClient = new UserApiClient(
|
|
7313
7215
|
environment,
|
|
7314
|
-
|
|
7315
|
-
|
|
7216
|
+
walletClient,
|
|
7217
|
+
publicClient,
|
|
7316
7218
|
ctx.clientId
|
|
7317
7219
|
);
|
|
7318
|
-
return userApiClient.uploadAppProfile(
|
|
7319
|
-
|
|
7320
|
-
profile.
|
|
7321
|
-
profile.
|
|
7322
|
-
profile.
|
|
7323
|
-
profile.
|
|
7324
|
-
|
|
7325
|
-
);
|
|
7220
|
+
return userApiClient.uploadAppProfile(appId, profile.name, {
|
|
7221
|
+
website: profile.website,
|
|
7222
|
+
description: profile.description,
|
|
7223
|
+
xURL: profile.xURL,
|
|
7224
|
+
image: profile.image,
|
|
7225
|
+
imageName: profile.imageName
|
|
7226
|
+
});
|
|
7326
7227
|
}
|
|
7327
7228
|
);
|
|
7328
7229
|
},
|
|
7329
7230
|
async logs(opts) {
|
|
7330
7231
|
return logs(
|
|
7331
7232
|
{
|
|
7332
|
-
privateKey,
|
|
7333
7233
|
appID: opts.appID,
|
|
7334
7234
|
watch: opts.watch,
|
|
7335
|
-
environment: ctx.environment,
|
|
7336
7235
|
clientId: ctx.clientId
|
|
7337
7236
|
},
|
|
7237
|
+
walletClient,
|
|
7238
|
+
publicClient,
|
|
7239
|
+
environment,
|
|
7338
7240
|
logger,
|
|
7339
7241
|
skipTelemetry
|
|
7340
|
-
// Skip if called from CLI
|
|
7341
7242
|
);
|
|
7342
7243
|
},
|
|
7343
7244
|
async start(appId, opts) {
|
|
@@ -7350,15 +7251,15 @@ function createAppModule(ctx) {
|
|
|
7350
7251
|
},
|
|
7351
7252
|
async () => {
|
|
7352
7253
|
const pendingMessage = `Starting app ${appId}...`;
|
|
7353
|
-
const data = (0,
|
|
7254
|
+
const data = (0, import_viem6.encodeFunctionData)({
|
|
7354
7255
|
abi: CONTROLLER_ABI,
|
|
7355
7256
|
functionName: "startApp",
|
|
7356
7257
|
args: [appId]
|
|
7357
7258
|
});
|
|
7358
7259
|
const tx = await sendAndWaitForTransaction(
|
|
7359
7260
|
{
|
|
7360
|
-
|
|
7361
|
-
|
|
7261
|
+
walletClient,
|
|
7262
|
+
publicClient,
|
|
7362
7263
|
environmentConfig: environment,
|
|
7363
7264
|
to: environment.appControllerAddress,
|
|
7364
7265
|
data,
|
|
@@ -7382,15 +7283,15 @@ function createAppModule(ctx) {
|
|
|
7382
7283
|
},
|
|
7383
7284
|
async () => {
|
|
7384
7285
|
const pendingMessage = `Stopping app ${appId}...`;
|
|
7385
|
-
const data = (0,
|
|
7286
|
+
const data = (0, import_viem6.encodeFunctionData)({
|
|
7386
7287
|
abi: CONTROLLER_ABI,
|
|
7387
7288
|
functionName: "stopApp",
|
|
7388
7289
|
args: [appId]
|
|
7389
7290
|
});
|
|
7390
7291
|
const tx = await sendAndWaitForTransaction(
|
|
7391
7292
|
{
|
|
7392
|
-
|
|
7393
|
-
|
|
7293
|
+
walletClient,
|
|
7294
|
+
publicClient,
|
|
7394
7295
|
environmentConfig: environment,
|
|
7395
7296
|
to: environment.appControllerAddress,
|
|
7396
7297
|
data,
|
|
@@ -7414,15 +7315,15 @@ function createAppModule(ctx) {
|
|
|
7414
7315
|
},
|
|
7415
7316
|
async () => {
|
|
7416
7317
|
const pendingMessage = `Terminating app ${appId}...`;
|
|
7417
|
-
const data = (0,
|
|
7318
|
+
const data = (0, import_viem6.encodeFunctionData)({
|
|
7418
7319
|
abi: CONTROLLER_ABI,
|
|
7419
7320
|
functionName: "terminateApp",
|
|
7420
7321
|
args: [appId]
|
|
7421
7322
|
});
|
|
7422
7323
|
const tx = await sendAndWaitForTransaction(
|
|
7423
7324
|
{
|
|
7424
|
-
|
|
7425
|
-
|
|
7325
|
+
walletClient,
|
|
7326
|
+
publicClient,
|
|
7426
7327
|
environmentConfig: environment,
|
|
7427
7328
|
to: environment.appControllerAddress,
|
|
7428
7329
|
data,
|
|
@@ -7438,9 +7339,9 @@ function createAppModule(ctx) {
|
|
|
7438
7339
|
},
|
|
7439
7340
|
async isDelegated() {
|
|
7440
7341
|
return isDelegated({
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7342
|
+
publicClient,
|
|
7343
|
+
environmentConfig: environment,
|
|
7344
|
+
address: account.address
|
|
7444
7345
|
});
|
|
7445
7346
|
},
|
|
7446
7347
|
async undelegate() {
|
|
@@ -7454,346 +7355,36 @@ function createAppModule(ctx) {
|
|
|
7454
7355
|
async () => {
|
|
7455
7356
|
const tx = await undelegate(
|
|
7456
7357
|
{
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
environmentConfig: environment
|
|
7460
|
-
},
|
|
7461
|
-
logger
|
|
7462
|
-
);
|
|
7463
|
-
return { tx };
|
|
7464
|
-
}
|
|
7465
|
-
);
|
|
7466
|
-
}
|
|
7467
|
-
};
|
|
7468
|
-
}
|
|
7469
|
-
|
|
7470
|
-
// src/client/modules/compute/index.ts
|
|
7471
|
-
function createComputeModule(config) {
|
|
7472
|
-
return {
|
|
7473
|
-
app: createAppModule(config)
|
|
7474
|
-
};
|
|
7475
|
-
}
|
|
7476
|
-
|
|
7477
|
-
// src/client/common/utils/billingapi.ts
|
|
7478
|
-
var import_axios2 = __toESM(require("axios"), 1);
|
|
7479
|
-
var import_accounts6 = require("viem/accounts");
|
|
7480
|
-
var BillingApiClient = class {
|
|
7481
|
-
constructor(config, privateKey) {
|
|
7482
|
-
this.account = (0, import_accounts6.privateKeyToAccount)(privateKey);
|
|
7483
|
-
this.config = config;
|
|
7484
|
-
}
|
|
7485
|
-
async createSubscription(productId = "compute") {
|
|
7486
|
-
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
7487
|
-
const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId);
|
|
7488
|
-
return resp.json();
|
|
7489
|
-
}
|
|
7490
|
-
async getSubscription(productId = "compute") {
|
|
7491
|
-
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
7492
|
-
const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
|
|
7493
|
-
return resp.json();
|
|
7494
|
-
}
|
|
7495
|
-
async cancelSubscription(productId = "compute") {
|
|
7496
|
-
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
7497
|
-
await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
|
|
7498
|
-
}
|
|
7499
|
-
/**
|
|
7500
|
-
* Make an authenticated request to the billing API
|
|
7501
|
-
*/
|
|
7502
|
-
async makeAuthenticatedRequest(url, method, productId) {
|
|
7503
|
-
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
7504
|
-
const { signature } = await calculateBillingAuthSignature({
|
|
7505
|
-
account: this.account,
|
|
7506
|
-
product: productId,
|
|
7507
|
-
expiry
|
|
7508
|
-
});
|
|
7509
|
-
const headers = {
|
|
7510
|
-
Authorization: `Bearer ${signature}`,
|
|
7511
|
-
"X-Account": this.account.address,
|
|
7512
|
-
"X-Expiry": expiry.toString()
|
|
7513
|
-
};
|
|
7514
|
-
try {
|
|
7515
|
-
const response = await (0, import_axios2.default)({
|
|
7516
|
-
method,
|
|
7517
|
-
url,
|
|
7518
|
-
headers,
|
|
7519
|
-
timeout: 3e4,
|
|
7520
|
-
maxRedirects: 0,
|
|
7521
|
-
validateStatus: () => true
|
|
7522
|
-
// Don't throw on any status
|
|
7523
|
-
});
|
|
7524
|
-
const status = response.status;
|
|
7525
|
-
const statusText = status >= 200 && status < 300 ? "OK" : "Error";
|
|
7526
|
-
if (status < 200 || status >= 300) {
|
|
7527
|
-
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
7528
|
-
throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body}`);
|
|
7529
|
-
}
|
|
7530
|
-
return {
|
|
7531
|
-
json: async () => response.data,
|
|
7532
|
-
text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
|
|
7533
|
-
};
|
|
7534
|
-
} catch (error) {
|
|
7535
|
-
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
7536
|
-
const cause = error.cause?.message || error.cause || error.message;
|
|
7537
|
-
throw new Error(
|
|
7538
|
-
`Failed to connect to BillingAPI at ${url}: ${cause}
|
|
7539
|
-
Please check:
|
|
7540
|
-
1. Your internet connection
|
|
7541
|
-
2. The API server is accessible: ${this.config.billingApiServerURL}
|
|
7542
|
-
3. Firewall/proxy settings`
|
|
7543
|
-
);
|
|
7544
|
-
}
|
|
7545
|
-
throw error;
|
|
7546
|
-
}
|
|
7547
|
-
}
|
|
7548
|
-
};
|
|
7549
|
-
|
|
7550
|
-
// src/client/common/auth/keyring.ts
|
|
7551
|
-
var import_keyring = require("@napi-rs/keyring");
|
|
7552
|
-
var import_accounts7 = require("viem/accounts");
|
|
7553
|
-
var SERVICE_NAME = "ecloud";
|
|
7554
|
-
var ACCOUNT_NAME = "key";
|
|
7555
|
-
var EIGENX_SERVICE_NAME = "eigenx-cli";
|
|
7556
|
-
var EIGENX_DEV_SERVICE_NAME = "eigenx-cli-dev";
|
|
7557
|
-
var EIGENX_ACCOUNT_PREFIX = "eigenx-";
|
|
7558
|
-
var GO_KEYRING_BASE64_PREFIX = "go-keyring-base64:";
|
|
7559
|
-
var GO_KEYRING_ENCODED_PREFIX = "go-keyring-encoded:";
|
|
7560
|
-
async function storePrivateKey(privateKey) {
|
|
7561
|
-
const normalizedKey = normalizePrivateKey(privateKey);
|
|
7562
|
-
const isValid = validatePrivateKey2(normalizedKey);
|
|
7563
|
-
if (!isValid) {
|
|
7564
|
-
throw new Error("Invalid private key format");
|
|
7565
|
-
}
|
|
7566
|
-
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
7567
|
-
try {
|
|
7568
|
-
await entry.setPassword(normalizedKey);
|
|
7569
|
-
} catch (err) {
|
|
7570
|
-
throw new Error(
|
|
7571
|
-
`Failed to store key in OS keyring: ${err?.message ?? err}. Ensure keyring service is available.`
|
|
7572
|
-
);
|
|
7573
|
-
}
|
|
7574
|
-
}
|
|
7575
|
-
async function getPrivateKey() {
|
|
7576
|
-
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
7577
|
-
try {
|
|
7578
|
-
const key = await entry.getPassword();
|
|
7579
|
-
if (key && validatePrivateKey2(key)) {
|
|
7580
|
-
return key;
|
|
7581
|
-
}
|
|
7582
|
-
} catch {
|
|
7583
|
-
}
|
|
7584
|
-
return null;
|
|
7585
|
-
}
|
|
7586
|
-
async function deletePrivateKey() {
|
|
7587
|
-
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
7588
|
-
try {
|
|
7589
|
-
await entry.deletePassword();
|
|
7590
|
-
return true;
|
|
7591
|
-
} catch {
|
|
7592
|
-
console.warn("No key found in keyring");
|
|
7593
|
-
return false;
|
|
7594
|
-
}
|
|
7595
|
-
}
|
|
7596
|
-
async function listStoredKeys() {
|
|
7597
|
-
const keys = [];
|
|
7598
|
-
const creds = (0, import_keyring.findCredentials)(SERVICE_NAME);
|
|
7599
|
-
for (const cred of creds) {
|
|
7600
|
-
if (cred.account === ACCOUNT_NAME) {
|
|
7601
|
-
try {
|
|
7602
|
-
const address = getAddressFromPrivateKey(cred.password);
|
|
7603
|
-
keys.push({ address });
|
|
7604
|
-
} catch (err) {
|
|
7605
|
-
console.warn(`Warning: Invalid key found, skipping: ${err}`);
|
|
7606
|
-
}
|
|
7607
|
-
}
|
|
7608
|
-
}
|
|
7609
|
-
return keys;
|
|
7610
|
-
}
|
|
7611
|
-
async function keyExists() {
|
|
7612
|
-
const key = await getPrivateKey();
|
|
7613
|
-
return key !== null;
|
|
7614
|
-
}
|
|
7615
|
-
async function getLegacyKeys() {
|
|
7616
|
-
const keys = [];
|
|
7617
|
-
try {
|
|
7618
|
-
const eigenxCreds = (0, import_keyring.findCredentials)(EIGENX_SERVICE_NAME);
|
|
7619
|
-
for (const cred of eigenxCreds) {
|
|
7620
|
-
const accountName = cred.account;
|
|
7621
|
-
if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
|
|
7622
|
-
continue;
|
|
7623
|
-
}
|
|
7624
|
-
const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
|
|
7625
|
-
try {
|
|
7626
|
-
const decodedKey = decodeGoKeyringValue(cred.password);
|
|
7627
|
-
const address = getAddressFromPrivateKey(decodedKey);
|
|
7628
|
-
keys.push({ environment, address, source: "eigenx" });
|
|
7629
|
-
} catch (err) {
|
|
7630
|
-
console.warn(
|
|
7631
|
-
`Warning: Invalid key found for ${environment} (eigenx-cli), skipping: ${err}`
|
|
7632
|
-
);
|
|
7633
|
-
}
|
|
7634
|
-
}
|
|
7635
|
-
} catch {
|
|
7636
|
-
}
|
|
7637
|
-
try {
|
|
7638
|
-
const eigenxDevCreds = (0, import_keyring.findCredentials)(EIGENX_DEV_SERVICE_NAME);
|
|
7639
|
-
for (const cred of eigenxDevCreds) {
|
|
7640
|
-
const accountName = cred.account;
|
|
7641
|
-
if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
|
|
7642
|
-
continue;
|
|
7643
|
-
}
|
|
7644
|
-
const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
|
|
7645
|
-
try {
|
|
7646
|
-
const decodedKey = decodeGoKeyringValue(cred.password);
|
|
7647
|
-
const address = getAddressFromPrivateKey(decodedKey);
|
|
7648
|
-
keys.push({ environment, address, source: "eigenx-dev" });
|
|
7649
|
-
} catch (err) {
|
|
7650
|
-
console.warn(
|
|
7651
|
-
`Warning: Invalid key found for ${environment} (eigenx-dev), skipping: ${err}`
|
|
7652
|
-
);
|
|
7653
|
-
}
|
|
7654
|
-
}
|
|
7655
|
-
} catch {
|
|
7656
|
-
}
|
|
7657
|
-
return keys;
|
|
7658
|
-
}
|
|
7659
|
-
async function getLegacyPrivateKey(environment, source) {
|
|
7660
|
-
const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
|
|
7661
|
-
const accountName = EIGENX_ACCOUNT_PREFIX + environment;
|
|
7662
|
-
const entry = new import_keyring.AsyncEntry(serviceName, accountName);
|
|
7663
|
-
try {
|
|
7664
|
-
const rawKey = await entry.getPassword();
|
|
7665
|
-
if (rawKey) {
|
|
7666
|
-
const decodedKey = decodeGoKeyringValue(rawKey);
|
|
7667
|
-
if (validatePrivateKey2(decodedKey)) {
|
|
7668
|
-
return decodedKey;
|
|
7669
|
-
}
|
|
7670
|
-
}
|
|
7671
|
-
} catch {
|
|
7672
|
-
}
|
|
7673
|
-
return null;
|
|
7674
|
-
}
|
|
7675
|
-
async function deleteLegacyPrivateKey(environment, source) {
|
|
7676
|
-
const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
|
|
7677
|
-
const accountName = EIGENX_ACCOUNT_PREFIX + environment;
|
|
7678
|
-
const entry = new import_keyring.AsyncEntry(serviceName, accountName);
|
|
7679
|
-
try {
|
|
7680
|
-
await entry.deletePassword();
|
|
7681
|
-
return true;
|
|
7682
|
-
} catch {
|
|
7683
|
-
console.warn(`No key found for ${environment} in ${source}`);
|
|
7684
|
-
return false;
|
|
7685
|
-
}
|
|
7686
|
-
}
|
|
7687
|
-
function validatePrivateKey2(privateKey) {
|
|
7688
|
-
try {
|
|
7689
|
-
getAddressFromPrivateKey(privateKey);
|
|
7690
|
-
return true;
|
|
7691
|
-
} catch {
|
|
7692
|
-
return false;
|
|
7693
|
-
}
|
|
7694
|
-
}
|
|
7695
|
-
function getAddressFromPrivateKey(privateKey) {
|
|
7696
|
-
const normalized = normalizePrivateKey(privateKey);
|
|
7697
|
-
return (0, import_accounts7.privateKeyToAddress)(normalized);
|
|
7698
|
-
}
|
|
7699
|
-
function decodeGoKeyringValue(rawValue) {
|
|
7700
|
-
if (rawValue.startsWith(GO_KEYRING_BASE64_PREFIX)) {
|
|
7701
|
-
const encoded = rawValue.substring(GO_KEYRING_BASE64_PREFIX.length);
|
|
7702
|
-
try {
|
|
7703
|
-
const decoded = Buffer.from(encoded, "base64").toString("utf8");
|
|
7704
|
-
return decoded;
|
|
7705
|
-
} catch (err) {
|
|
7706
|
-
console.warn(`Warning: Failed to decode go-keyring base64 value: ${err}`);
|
|
7707
|
-
return rawValue;
|
|
7708
|
-
}
|
|
7709
|
-
}
|
|
7710
|
-
if (rawValue.startsWith(GO_KEYRING_ENCODED_PREFIX)) {
|
|
7711
|
-
const encoded = rawValue.substring(GO_KEYRING_ENCODED_PREFIX.length);
|
|
7712
|
-
try {
|
|
7713
|
-
const decoded = Buffer.from(encoded, "hex").toString("utf8");
|
|
7714
|
-
return decoded;
|
|
7715
|
-
} catch (err) {
|
|
7716
|
-
console.warn(`Warning: Failed to decode go-keyring hex value: ${err}`);
|
|
7717
|
-
return rawValue;
|
|
7718
|
-
}
|
|
7719
|
-
}
|
|
7720
|
-
return rawValue;
|
|
7721
|
-
}
|
|
7722
|
-
function normalizePrivateKey(privateKey) {
|
|
7723
|
-
if (!privateKey.startsWith("0x")) {
|
|
7724
|
-
return `0x${privateKey}`;
|
|
7725
|
-
}
|
|
7726
|
-
return privateKey;
|
|
7727
|
-
}
|
|
7728
|
-
|
|
7729
|
-
// src/client/common/auth/resolver.ts
|
|
7730
|
-
async function getPrivateKeyWithSource(options) {
|
|
7731
|
-
if (options.privateKey) {
|
|
7732
|
-
if (!validatePrivateKey2(options.privateKey)) {
|
|
7733
|
-
throw new Error(
|
|
7734
|
-
"Invalid private key format provided via command flag. Please check and try again."
|
|
7735
|
-
);
|
|
7736
|
-
}
|
|
7737
|
-
return {
|
|
7738
|
-
key: options.privateKey,
|
|
7739
|
-
source: "command flag"
|
|
7740
|
-
};
|
|
7741
|
-
}
|
|
7742
|
-
const envKey = process.env.ECLOUD_PRIVATE_KEY;
|
|
7743
|
-
if (envKey) {
|
|
7744
|
-
if (!validatePrivateKey2(envKey)) {
|
|
7745
|
-
throw new Error(
|
|
7746
|
-
"Invalid private key format provided via environment variable. Please check and try again."
|
|
7747
|
-
);
|
|
7748
|
-
}
|
|
7749
|
-
return {
|
|
7750
|
-
key: envKey,
|
|
7751
|
-
source: "environment variable (ECLOUD_PRIVATE_KEY)"
|
|
7752
|
-
};
|
|
7753
|
-
}
|
|
7754
|
-
const keyringKey = await getPrivateKey();
|
|
7755
|
-
if (keyringKey) {
|
|
7756
|
-
return {
|
|
7757
|
-
key: keyringKey,
|
|
7758
|
-
source: "stored credentials"
|
|
7759
|
-
};
|
|
7760
|
-
}
|
|
7761
|
-
return null;
|
|
7762
|
-
}
|
|
7763
|
-
async function requirePrivateKey(options) {
|
|
7764
|
-
const result = await getPrivateKeyWithSource({
|
|
7765
|
-
privateKey: options.privateKey
|
|
7766
|
-
});
|
|
7767
|
-
if (!result) {
|
|
7768
|
-
throw new Error(
|
|
7769
|
-
`Private key required. Please provide it via:
|
|
7770
|
-
\u2022 Keyring: ecloud auth login
|
|
7771
|
-
\u2022 Flag: --private-key YOUR_KEY
|
|
7772
|
-
\u2022 Environment: export ECLOUD_PRIVATE_KEY=YOUR_KEY`
|
|
7773
|
-
);
|
|
7774
|
-
}
|
|
7775
|
-
return result;
|
|
7358
|
+
walletClient,
|
|
7359
|
+
publicClient,
|
|
7360
|
+
environmentConfig: environment
|
|
7361
|
+
},
|
|
7362
|
+
logger
|
|
7363
|
+
);
|
|
7364
|
+
return { tx };
|
|
7365
|
+
}
|
|
7366
|
+
);
|
|
7367
|
+
}
|
|
7368
|
+
};
|
|
7776
7369
|
}
|
|
7777
7370
|
|
|
7778
|
-
// src/client/
|
|
7779
|
-
|
|
7780
|
-
function generateNewPrivateKey() {
|
|
7781
|
-
const privateKey = (0, import_accounts8.generatePrivateKey)();
|
|
7782
|
-
const address = (0, import_accounts8.privateKeyToAddress)(privateKey);
|
|
7371
|
+
// src/client/modules/compute/index.ts
|
|
7372
|
+
function createComputeModule(config) {
|
|
7783
7373
|
return {
|
|
7784
|
-
|
|
7785
|
-
address
|
|
7374
|
+
app: createAppModule(config)
|
|
7786
7375
|
};
|
|
7787
7376
|
}
|
|
7788
7377
|
|
|
7789
7378
|
// src/client/modules/billing/index.ts
|
|
7790
7379
|
function createBillingModule(config) {
|
|
7791
|
-
const { verbose = false, skipTelemetry = false } = config;
|
|
7792
|
-
|
|
7793
|
-
|
|
7380
|
+
const { verbose = false, skipTelemetry = false, walletClient } = config;
|
|
7381
|
+
if (!walletClient.account) {
|
|
7382
|
+
throw new Error("WalletClient must have an account attached");
|
|
7383
|
+
}
|
|
7384
|
+
const address = walletClient.account.address;
|
|
7794
7385
|
const logger = getLogger(verbose);
|
|
7795
7386
|
const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
|
|
7796
|
-
const billingApi = new BillingApiClient(billingEnvConfig,
|
|
7387
|
+
const billingApi = new BillingApiClient(billingEnvConfig, walletClient);
|
|
7797
7388
|
return {
|
|
7798
7389
|
address,
|
|
7799
7390
|
async subscribe(opts) {
|
|
@@ -7883,14 +7474,21 @@ function createBillingModule(config) {
|
|
|
7883
7474
|
|
|
7884
7475
|
// src/client/common/utils/buildapi.ts
|
|
7885
7476
|
var import_axios3 = __toESM(require("axios"), 1);
|
|
7886
|
-
var import_accounts9 = require("viem/accounts");
|
|
7887
7477
|
var BuildApiClient = class {
|
|
7888
7478
|
constructor(options) {
|
|
7889
7479
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
7890
7480
|
this.clientId = options.clientId;
|
|
7891
|
-
|
|
7892
|
-
|
|
7481
|
+
this.walletClient = options.walletClient;
|
|
7482
|
+
}
|
|
7483
|
+
/**
|
|
7484
|
+
* Get the address of the connected wallet
|
|
7485
|
+
*/
|
|
7486
|
+
get address() {
|
|
7487
|
+
const account = this.walletClient?.account;
|
|
7488
|
+
if (!account) {
|
|
7489
|
+
throw new Error("WalletClient must have an account attached");
|
|
7893
7490
|
}
|
|
7491
|
+
return account.address;
|
|
7894
7492
|
}
|
|
7895
7493
|
async submitBuild(payload) {
|
|
7896
7494
|
return this.authenticatedJsonRequest("/builds", "POST", payload);
|
|
@@ -7931,20 +7529,22 @@ var BuildApiClient = class {
|
|
|
7931
7529
|
return res.data;
|
|
7932
7530
|
}
|
|
7933
7531
|
async authenticatedJsonRequest(path8, method, body) {
|
|
7934
|
-
if (!this.account)
|
|
7532
|
+
if (!this.walletClient?.account) {
|
|
7533
|
+
throw new Error("WalletClient with account required for authenticated requests");
|
|
7534
|
+
}
|
|
7935
7535
|
const headers = {
|
|
7936
7536
|
"Content-Type": "application/json"
|
|
7937
7537
|
};
|
|
7938
7538
|
if (this.clientId) headers["x-client-id"] = this.clientId;
|
|
7939
7539
|
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
|
|
7940
7540
|
const { signature } = await calculateBillingAuthSignature({
|
|
7941
|
-
|
|
7541
|
+
walletClient: this.walletClient,
|
|
7942
7542
|
product: "compute",
|
|
7943
7543
|
expiry
|
|
7944
7544
|
});
|
|
7945
7545
|
headers.Authorization = `Bearer ${signature}`;
|
|
7946
7546
|
headers["X-eigenx-expiry"] = expiry.toString();
|
|
7947
|
-
headers["X-Account"] = this.
|
|
7547
|
+
headers["X-Account"] = this.address;
|
|
7948
7548
|
const res = await (0, import_axios3.default)({
|
|
7949
7549
|
url: `${this.baseUrl}${path8}`,
|
|
7950
7550
|
method,
|
|
@@ -7957,18 +7557,20 @@ var BuildApiClient = class {
|
|
|
7957
7557
|
return res.data;
|
|
7958
7558
|
}
|
|
7959
7559
|
async authenticatedTextRequest(path8) {
|
|
7960
|
-
if (!this.account)
|
|
7560
|
+
if (!this.walletClient?.account) {
|
|
7561
|
+
throw new Error("WalletClient with account required for authenticated requests");
|
|
7562
|
+
}
|
|
7961
7563
|
const headers = {};
|
|
7962
7564
|
if (this.clientId) headers["x-client-id"] = this.clientId;
|
|
7963
7565
|
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
|
|
7964
7566
|
const { signature } = await calculateBillingAuthSignature({
|
|
7965
|
-
|
|
7567
|
+
walletClient: this.walletClient,
|
|
7966
7568
|
product: "compute",
|
|
7967
7569
|
expiry
|
|
7968
7570
|
});
|
|
7969
7571
|
headers.Authorization = `Bearer ${signature}`;
|
|
7970
7572
|
headers["X-eigenx-expiry"] = expiry.toString();
|
|
7971
|
-
headers["X-Account"] = this.
|
|
7573
|
+
headers["X-Account"] = this.address;
|
|
7972
7574
|
const res = await (0, import_axios3.default)({
|
|
7973
7575
|
url: `${this.baseUrl}${path8}`,
|
|
7974
7576
|
method: "GET",
|
|
@@ -8050,13 +7652,13 @@ var BadRequestError = class extends BuildError {
|
|
|
8050
7652
|
var DEFAULT_POLL_INTERVAL = 2e3;
|
|
8051
7653
|
var DEFAULT_TIMEOUT = 30 * 60 * 1e3;
|
|
8052
7654
|
function createBuildModule(config) {
|
|
8053
|
-
const { verbose = false, skipTelemetry = false } = config;
|
|
7655
|
+
const { verbose = false, skipTelemetry = false, walletClient } = config;
|
|
8054
7656
|
const logger = getLogger(verbose);
|
|
8055
7657
|
const environment = config.environment || "sepolia";
|
|
8056
7658
|
const environmentConfig = getEnvironmentConfig(environment);
|
|
8057
7659
|
const api = new BuildApiClient({
|
|
8058
7660
|
baseUrl: environmentConfig.userApiServerURL,
|
|
8059
|
-
|
|
7661
|
+
walletClient,
|
|
8060
7662
|
clientId: config.clientId
|
|
8061
7663
|
});
|
|
8062
7664
|
return {
|
|
@@ -8068,7 +7670,7 @@ function createBuildModule(config) {
|
|
|
8068
7670
|
properties: { environment, repoUrl: request.repoUrl }
|
|
8069
7671
|
},
|
|
8070
7672
|
async () => {
|
|
8071
|
-
if (!
|
|
7673
|
+
if (!walletClient) throw new AuthRequiredError("walletClient required for submit()");
|
|
8072
7674
|
const data = await api.submitBuild({
|
|
8073
7675
|
repo_url: request.repoUrl,
|
|
8074
7676
|
git_ref: request.gitRef,
|
|
@@ -8127,7 +7729,7 @@ function createBuildModule(config) {
|
|
|
8127
7729
|
return withSDKTelemetry(
|
|
8128
7730
|
{ functionName: "build.getLogs", skipTelemetry, properties: { environment, buildId } },
|
|
8129
7731
|
async () => {
|
|
8130
|
-
if (!
|
|
7732
|
+
if (!walletClient) throw new AuthRequiredError("walletClient required for getLogs()");
|
|
8131
7733
|
return api.getLogs(buildId);
|
|
8132
7734
|
}
|
|
8133
7735
|
);
|
|
@@ -8235,13 +7837,252 @@ function transformVerifyResult(raw) {
|
|
|
8235
7837
|
};
|
|
8236
7838
|
}
|
|
8237
7839
|
|
|
7840
|
+
// src/client/common/auth/keyring.ts
|
|
7841
|
+
var import_keyring = require("@napi-rs/keyring");
|
|
7842
|
+
var import_accounts2 = require("viem/accounts");
|
|
7843
|
+
var SERVICE_NAME = "ecloud";
|
|
7844
|
+
var ACCOUNT_NAME = "key";
|
|
7845
|
+
var EIGENX_SERVICE_NAME = "eigenx-cli";
|
|
7846
|
+
var EIGENX_DEV_SERVICE_NAME = "eigenx-cli-dev";
|
|
7847
|
+
var EIGENX_ACCOUNT_PREFIX = "eigenx-";
|
|
7848
|
+
var GO_KEYRING_BASE64_PREFIX = "go-keyring-base64:";
|
|
7849
|
+
var GO_KEYRING_ENCODED_PREFIX = "go-keyring-encoded:";
|
|
7850
|
+
async function storePrivateKey(privateKey) {
|
|
7851
|
+
const normalizedKey = normalizePrivateKey(privateKey);
|
|
7852
|
+
const isValid = validatePrivateKey(normalizedKey);
|
|
7853
|
+
if (!isValid) {
|
|
7854
|
+
throw new Error("Invalid private key format");
|
|
7855
|
+
}
|
|
7856
|
+
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
7857
|
+
try {
|
|
7858
|
+
await entry.setPassword(normalizedKey);
|
|
7859
|
+
} catch (err) {
|
|
7860
|
+
throw new Error(
|
|
7861
|
+
`Failed to store key in OS keyring: ${err?.message ?? err}. Ensure keyring service is available.`
|
|
7862
|
+
);
|
|
7863
|
+
}
|
|
7864
|
+
}
|
|
7865
|
+
async function getPrivateKey() {
|
|
7866
|
+
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
7867
|
+
try {
|
|
7868
|
+
const key = await entry.getPassword();
|
|
7869
|
+
if (key && validatePrivateKey(key)) {
|
|
7870
|
+
return key;
|
|
7871
|
+
}
|
|
7872
|
+
} catch {
|
|
7873
|
+
}
|
|
7874
|
+
return null;
|
|
7875
|
+
}
|
|
7876
|
+
async function deletePrivateKey() {
|
|
7877
|
+
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
7878
|
+
try {
|
|
7879
|
+
await entry.deletePassword();
|
|
7880
|
+
return true;
|
|
7881
|
+
} catch {
|
|
7882
|
+
console.warn("No key found in keyring");
|
|
7883
|
+
return false;
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
async function listStoredKeys() {
|
|
7887
|
+
const keys = [];
|
|
7888
|
+
const creds = (0, import_keyring.findCredentials)(SERVICE_NAME);
|
|
7889
|
+
for (const cred of creds) {
|
|
7890
|
+
if (cred.account === ACCOUNT_NAME) {
|
|
7891
|
+
try {
|
|
7892
|
+
const address = getAddressFromPrivateKey(cred.password);
|
|
7893
|
+
keys.push({ address });
|
|
7894
|
+
} catch (err) {
|
|
7895
|
+
console.warn(`Warning: Invalid key found, skipping: ${err}`);
|
|
7896
|
+
}
|
|
7897
|
+
}
|
|
7898
|
+
}
|
|
7899
|
+
return keys;
|
|
7900
|
+
}
|
|
7901
|
+
async function keyExists() {
|
|
7902
|
+
const key = await getPrivateKey();
|
|
7903
|
+
return key !== null;
|
|
7904
|
+
}
|
|
7905
|
+
async function getLegacyKeys() {
|
|
7906
|
+
const keys = [];
|
|
7907
|
+
try {
|
|
7908
|
+
const eigenxCreds = (0, import_keyring.findCredentials)(EIGENX_SERVICE_NAME);
|
|
7909
|
+
for (const cred of eigenxCreds) {
|
|
7910
|
+
const accountName = cred.account;
|
|
7911
|
+
if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
|
|
7912
|
+
continue;
|
|
7913
|
+
}
|
|
7914
|
+
const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
|
|
7915
|
+
try {
|
|
7916
|
+
const decodedKey = decodeGoKeyringValue(cred.password);
|
|
7917
|
+
const address = getAddressFromPrivateKey(decodedKey);
|
|
7918
|
+
keys.push({ environment, address, source: "eigenx" });
|
|
7919
|
+
} catch (err) {
|
|
7920
|
+
console.warn(
|
|
7921
|
+
`Warning: Invalid key found for ${environment} (eigenx-cli), skipping: ${err}`
|
|
7922
|
+
);
|
|
7923
|
+
}
|
|
7924
|
+
}
|
|
7925
|
+
} catch {
|
|
7926
|
+
}
|
|
7927
|
+
try {
|
|
7928
|
+
const eigenxDevCreds = (0, import_keyring.findCredentials)(EIGENX_DEV_SERVICE_NAME);
|
|
7929
|
+
for (const cred of eigenxDevCreds) {
|
|
7930
|
+
const accountName = cred.account;
|
|
7931
|
+
if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
|
|
7932
|
+
continue;
|
|
7933
|
+
}
|
|
7934
|
+
const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
|
|
7935
|
+
try {
|
|
7936
|
+
const decodedKey = decodeGoKeyringValue(cred.password);
|
|
7937
|
+
const address = getAddressFromPrivateKey(decodedKey);
|
|
7938
|
+
keys.push({ environment, address, source: "eigenx-dev" });
|
|
7939
|
+
} catch (err) {
|
|
7940
|
+
console.warn(
|
|
7941
|
+
`Warning: Invalid key found for ${environment} (eigenx-dev), skipping: ${err}`
|
|
7942
|
+
);
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
7945
|
+
} catch {
|
|
7946
|
+
}
|
|
7947
|
+
return keys;
|
|
7948
|
+
}
|
|
7949
|
+
async function getLegacyPrivateKey(environment, source) {
|
|
7950
|
+
const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
|
|
7951
|
+
const accountName = EIGENX_ACCOUNT_PREFIX + environment;
|
|
7952
|
+
const entry = new import_keyring.AsyncEntry(serviceName, accountName);
|
|
7953
|
+
try {
|
|
7954
|
+
const rawKey = await entry.getPassword();
|
|
7955
|
+
if (rawKey) {
|
|
7956
|
+
const decodedKey = decodeGoKeyringValue(rawKey);
|
|
7957
|
+
if (validatePrivateKey(decodedKey)) {
|
|
7958
|
+
return decodedKey;
|
|
7959
|
+
}
|
|
7960
|
+
}
|
|
7961
|
+
} catch {
|
|
7962
|
+
}
|
|
7963
|
+
return null;
|
|
7964
|
+
}
|
|
7965
|
+
async function deleteLegacyPrivateKey(environment, source) {
|
|
7966
|
+
const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
|
|
7967
|
+
const accountName = EIGENX_ACCOUNT_PREFIX + environment;
|
|
7968
|
+
const entry = new import_keyring.AsyncEntry(serviceName, accountName);
|
|
7969
|
+
try {
|
|
7970
|
+
await entry.deletePassword();
|
|
7971
|
+
return true;
|
|
7972
|
+
} catch {
|
|
7973
|
+
console.warn(`No key found for ${environment} in ${source}`);
|
|
7974
|
+
return false;
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
function validatePrivateKey(privateKey) {
|
|
7978
|
+
try {
|
|
7979
|
+
getAddressFromPrivateKey(privateKey);
|
|
7980
|
+
return true;
|
|
7981
|
+
} catch {
|
|
7982
|
+
return false;
|
|
7983
|
+
}
|
|
7984
|
+
}
|
|
7985
|
+
function getAddressFromPrivateKey(privateKey) {
|
|
7986
|
+
const normalized = normalizePrivateKey(privateKey);
|
|
7987
|
+
return (0, import_accounts2.privateKeyToAddress)(normalized);
|
|
7988
|
+
}
|
|
7989
|
+
function decodeGoKeyringValue(rawValue) {
|
|
7990
|
+
if (rawValue.startsWith(GO_KEYRING_BASE64_PREFIX)) {
|
|
7991
|
+
const encoded = rawValue.substring(GO_KEYRING_BASE64_PREFIX.length);
|
|
7992
|
+
try {
|
|
7993
|
+
const decoded = Buffer.from(encoded, "base64").toString("utf8");
|
|
7994
|
+
return decoded;
|
|
7995
|
+
} catch (err) {
|
|
7996
|
+
console.warn(`Warning: Failed to decode go-keyring base64 value: ${err}`);
|
|
7997
|
+
return rawValue;
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
if (rawValue.startsWith(GO_KEYRING_ENCODED_PREFIX)) {
|
|
8001
|
+
const encoded = rawValue.substring(GO_KEYRING_ENCODED_PREFIX.length);
|
|
8002
|
+
try {
|
|
8003
|
+
const decoded = Buffer.from(encoded, "hex").toString("utf8");
|
|
8004
|
+
return decoded;
|
|
8005
|
+
} catch (err) {
|
|
8006
|
+
console.warn(`Warning: Failed to decode go-keyring hex value: ${err}`);
|
|
8007
|
+
return rawValue;
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
return rawValue;
|
|
8011
|
+
}
|
|
8012
|
+
function normalizePrivateKey(privateKey) {
|
|
8013
|
+
if (!privateKey.startsWith("0x")) {
|
|
8014
|
+
return `0x${privateKey}`;
|
|
8015
|
+
}
|
|
8016
|
+
return privateKey;
|
|
8017
|
+
}
|
|
8018
|
+
|
|
8019
|
+
// src/client/common/auth/resolver.ts
|
|
8020
|
+
async function getPrivateKeyWithSource(options) {
|
|
8021
|
+
if (options.privateKey) {
|
|
8022
|
+
if (!validatePrivateKey(options.privateKey)) {
|
|
8023
|
+
throw new Error(
|
|
8024
|
+
"Invalid private key format provided via command flag. Please check and try again."
|
|
8025
|
+
);
|
|
8026
|
+
}
|
|
8027
|
+
return {
|
|
8028
|
+
key: options.privateKey,
|
|
8029
|
+
source: "command flag"
|
|
8030
|
+
};
|
|
8031
|
+
}
|
|
8032
|
+
const envKey = process.env.ECLOUD_PRIVATE_KEY;
|
|
8033
|
+
if (envKey) {
|
|
8034
|
+
if (!validatePrivateKey(envKey)) {
|
|
8035
|
+
throw new Error(
|
|
8036
|
+
"Invalid private key format provided via environment variable. Please check and try again."
|
|
8037
|
+
);
|
|
8038
|
+
}
|
|
8039
|
+
return {
|
|
8040
|
+
key: envKey,
|
|
8041
|
+
source: "environment variable (ECLOUD_PRIVATE_KEY)"
|
|
8042
|
+
};
|
|
8043
|
+
}
|
|
8044
|
+
const keyringKey = await getPrivateKey();
|
|
8045
|
+
if (keyringKey) {
|
|
8046
|
+
return {
|
|
8047
|
+
key: keyringKey,
|
|
8048
|
+
source: "stored credentials"
|
|
8049
|
+
};
|
|
8050
|
+
}
|
|
8051
|
+
return null;
|
|
8052
|
+
}
|
|
8053
|
+
async function requirePrivateKey(options) {
|
|
8054
|
+
const result = await getPrivateKeyWithSource({
|
|
8055
|
+
privateKey: options.privateKey
|
|
8056
|
+
});
|
|
8057
|
+
if (!result) {
|
|
8058
|
+
throw new Error(
|
|
8059
|
+
`Private key required. Please provide it via:
|
|
8060
|
+
\u2022 Keyring: ecloud auth login
|
|
8061
|
+
\u2022 Flag: --private-key YOUR_KEY
|
|
8062
|
+
\u2022 Environment: export ECLOUD_PRIVATE_KEY=YOUR_KEY`
|
|
8063
|
+
);
|
|
8064
|
+
}
|
|
8065
|
+
return result;
|
|
8066
|
+
}
|
|
8067
|
+
|
|
8068
|
+
// src/client/common/auth/generate.ts
|
|
8069
|
+
var import_accounts3 = require("viem/accounts");
|
|
8070
|
+
function generateNewPrivateKey() {
|
|
8071
|
+
const privateKey = (0, import_accounts3.generatePrivateKey)();
|
|
8072
|
+
const address = (0, import_accounts3.privateKeyToAddress)(privateKey);
|
|
8073
|
+
return {
|
|
8074
|
+
privateKey,
|
|
8075
|
+
address
|
|
8076
|
+
};
|
|
8077
|
+
}
|
|
8078
|
+
|
|
8238
8079
|
// src/client/common/utils/instance.ts
|
|
8239
8080
|
async function getCurrentInstanceType(preflightCtx, appID, logger, clientId) {
|
|
8240
8081
|
try {
|
|
8241
8082
|
const userApiClient = new UserApiClient(
|
|
8242
8083
|
preflightCtx.environmentConfig,
|
|
8243
|
-
preflightCtx.
|
|
8244
|
-
preflightCtx.
|
|
8084
|
+
preflightCtx.walletClient,
|
|
8085
|
+
preflightCtx.publicClient,
|
|
8245
8086
|
clientId
|
|
8246
8087
|
);
|
|
8247
8088
|
const infos = await userApiClient.getInfos([appID], 1);
|
|
@@ -8274,16 +8115,21 @@ function createECloudClient(cfg) {
|
|
|
8274
8115
|
`RPC URL is required. Provide via options.rpcUrl, RPC_URL env var, or ensure environment has default RPC URL`
|
|
8275
8116
|
);
|
|
8276
8117
|
}
|
|
8118
|
+
const { walletClient, publicClient } = createClients({
|
|
8119
|
+
privateKey: cfg.privateKey,
|
|
8120
|
+
rpcUrl,
|
|
8121
|
+
chainId: environmentConfig.chainID
|
|
8122
|
+
});
|
|
8277
8123
|
return {
|
|
8278
8124
|
compute: createComputeModule({
|
|
8279
|
-
rpcUrl,
|
|
8280
8125
|
verbose: cfg.verbose,
|
|
8281
|
-
|
|
8126
|
+
walletClient,
|
|
8127
|
+
publicClient,
|
|
8282
8128
|
environment: cfg.environment
|
|
8283
8129
|
}),
|
|
8284
8130
|
billing: createBillingModule({
|
|
8285
8131
|
verbose: cfg.verbose,
|
|
8286
|
-
|
|
8132
|
+
walletClient
|
|
8287
8133
|
})
|
|
8288
8134
|
};
|
|
8289
8135
|
}
|
|
@@ -8292,6 +8138,7 @@ function createECloudClient(cfg) {
|
|
|
8292
8138
|
AuthRequiredError,
|
|
8293
8139
|
BUILD_STATUS,
|
|
8294
8140
|
BadRequestError,
|
|
8141
|
+
BillingApiClient,
|
|
8295
8142
|
BuildError,
|
|
8296
8143
|
BuildFailedError,
|
|
8297
8144
|
ConflictError,
|
|
@@ -8317,6 +8164,7 @@ function createECloudClient(cfg) {
|
|
|
8317
8164
|
createECloudClient,
|
|
8318
8165
|
createMetricsContext,
|
|
8319
8166
|
createTelemetryClient,
|
|
8167
|
+
createViemClients,
|
|
8320
8168
|
deleteLegacyPrivateKey,
|
|
8321
8169
|
deletePrivateKey,
|
|
8322
8170
|
emitMetrics,
|
|
@@ -8336,9 +8184,11 @@ function createECloudClient(cfg) {
|
|
|
8336
8184
|
getAppLatestReleaseBlockNumbers,
|
|
8337
8185
|
getAvailableEnvironments,
|
|
8338
8186
|
getAvailableTemplates,
|
|
8187
|
+
getBillingEnvironmentConfig,
|
|
8339
8188
|
getBlockTimestamps,
|
|
8340
8189
|
getBuildType,
|
|
8341
8190
|
getCategoryDescriptions,
|
|
8191
|
+
getChainFromID,
|
|
8342
8192
|
getCurrentInstanceType,
|
|
8343
8193
|
getEnvironmentConfig,
|
|
8344
8194
|
getLegacyKeys,
|