@layr-labs/ecloud-sdk 0.2.0 → 0.2.2-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 +48 -43
- 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 +519 -4
- package/dist/billing.js.map +1 -1
- package/dist/browser.cjs +5034 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +239 -0
- package/dist/browser.d.ts +239 -0
- package/dist/browser.js +4924 -0
- package/dist/browser.js.map +1 -0
- package/dist/{compute-CF2HOXed.d.ts → compute-BYhSs8en.d.ts} +15 -96
- package/dist/{compute-CbmjA8kJ.d.cts → compute-Bpjb3hYD.d.cts} +15 -96
- package/dist/compute.cjs +875 -846
- 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 +7009 -8
- package/dist/compute.js.map +1 -1
- package/dist/helpers-CEvhJz7f.d.cts +742 -0
- package/dist/helpers-CQuBwQnu.d.ts +742 -0
- package/dist/index-DeQzn_yM.d.cts +739 -0
- package/dist/index-DeQzn_yM.d.ts +739 -0
- package/dist/index.cjs +1958 -1758
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -414
- package/dist/index.d.ts +69 -414
- package/dist/index.js +7977 -134
- package/dist/index.js.map +1 -1
- package/package.json +17 -2
- package/dist/chunk-CA5Y4OVI.js +0 -744
- package/dist/chunk-CA5Y4OVI.js.map +0 -1
- package/dist/chunk-ZDXN2WKP.js +0 -434
- package/dist/chunk-ZDXN2WKP.js.map +0 -1
- package/dist/chunk-ZTLKZMSW.js +0 -6719
- package/dist/chunk-ZTLKZMSW.js.map +0 -1
- package/dist/index-D2QufVB9.d.cts +0 -342
- package/dist/index-D2QufVB9.d.ts +0 -342
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,
|
|
@@ -41,6 +42,7 @@ __export(index_exports, {
|
|
|
41
42
|
NotFoundError: () => NotFoundError,
|
|
42
43
|
PRIMARY_LANGUAGES: () => PRIMARY_LANGUAGES,
|
|
43
44
|
PostHogClient: () => PostHogClient,
|
|
45
|
+
SessionError: () => SessionError,
|
|
44
46
|
TimeoutError: () => TimeoutError,
|
|
45
47
|
UserApiClient: () => UserApiClient,
|
|
46
48
|
addHexPrefix: () => addHexPrefix,
|
|
@@ -57,7 +59,9 @@ __export(index_exports, {
|
|
|
57
59
|
createComputeModule: () => createComputeModule,
|
|
58
60
|
createECloudClient: () => createECloudClient,
|
|
59
61
|
createMetricsContext: () => createMetricsContext,
|
|
62
|
+
createSiweMessage: () => createSiweMessage,
|
|
60
63
|
createTelemetryClient: () => createTelemetryClient,
|
|
64
|
+
createViemClients: () => createClients,
|
|
61
65
|
deleteLegacyPrivateKey: () => deleteLegacyPrivateKey,
|
|
62
66
|
deletePrivateKey: () => deletePrivateKey,
|
|
63
67
|
emitMetrics: () => emitMetrics,
|
|
@@ -72,14 +76,18 @@ __export(index_exports, {
|
|
|
72
76
|
fetchTemplateCatalog: () => fetchTemplateCatalog,
|
|
73
77
|
formatETH: () => formatETH,
|
|
74
78
|
generateNewPrivateKey: () => generateNewPrivateKey,
|
|
79
|
+
generateNonce: () => generateNonce,
|
|
75
80
|
getAddressFromPrivateKey: () => getAddressFromPrivateKey,
|
|
76
81
|
getAllAppsByDeveloper: () => getAllAppsByDeveloper,
|
|
77
82
|
getAppLatestReleaseBlockNumbers: () => getAppLatestReleaseBlockNumbers,
|
|
78
83
|
getAvailableEnvironments: () => getAvailableEnvironments,
|
|
79
84
|
getAvailableTemplates: () => getAvailableTemplates,
|
|
85
|
+
getBillingEnvironmentConfig: () => getBillingEnvironmentConfig,
|
|
80
86
|
getBlockTimestamps: () => getBlockTimestamps,
|
|
81
87
|
getBuildType: () => getBuildType,
|
|
82
88
|
getCategoryDescriptions: () => getCategoryDescriptions,
|
|
89
|
+
getChainFromID: () => getChainFromID,
|
|
90
|
+
getComputeApiSession: () => getComputeApiSession,
|
|
83
91
|
getCurrentInstanceType: () => getCurrentInstanceType,
|
|
84
92
|
getEnvironmentConfig: () => getEnvironmentConfig,
|
|
85
93
|
getLegacyKeys: () => getLegacyKeys,
|
|
@@ -92,10 +100,17 @@ __export(index_exports, {
|
|
|
92
100
|
isEnvironmentAvailable: () => isEnvironmentAvailable,
|
|
93
101
|
isMainnet: () => isMainnet,
|
|
94
102
|
isNoopClient: () => isNoopClient,
|
|
103
|
+
isSessionValid: () => isSessionValid,
|
|
104
|
+
isSiweMessageExpired: () => isSiweMessageExpired,
|
|
105
|
+
isSiweMessageNotYetValid: () => isSiweMessageNotYetValid,
|
|
95
106
|
isSubscriptionActive: () => isSubscriptionActive,
|
|
96
107
|
keyExists: () => keyExists,
|
|
97
108
|
listStoredKeys: () => listStoredKeys,
|
|
109
|
+
loginToComputeApi: () => loginToComputeApi,
|
|
110
|
+
logoutFromComputeApi: () => logoutFromComputeApi,
|
|
98
111
|
logs: () => logs,
|
|
112
|
+
noopLogger: () => noopLogger,
|
|
113
|
+
parseSiweMessage: () => parseSiweMessage,
|
|
99
114
|
prepareDeploy: () => prepareDeploy,
|
|
100
115
|
prepareDeployFromVerifiableBuild: () => prepareDeployFromVerifiableBuild,
|
|
101
116
|
prepareUpgrade: () => prepareUpgrade,
|
|
@@ -117,7 +132,7 @@ __export(index_exports, {
|
|
|
117
132
|
validateInstanceTypeSKU: () => validateInstanceTypeSKU,
|
|
118
133
|
validateLogVisibility: () => validateLogVisibility,
|
|
119
134
|
validateLogsParams: () => validateLogsParams,
|
|
120
|
-
validatePrivateKey: () =>
|
|
135
|
+
validatePrivateKey: () => validatePrivateKey,
|
|
121
136
|
validatePrivateKeyFormat: () => validatePrivateKeyFormat,
|
|
122
137
|
validateResourceUsageMonitoring: () => validateResourceUsageMonitoring,
|
|
123
138
|
validateURL: () => validateURL,
|
|
@@ -130,118 +145,7 @@ __export(index_exports, {
|
|
|
130
145
|
module.exports = __toCommonJS(index_exports);
|
|
131
146
|
|
|
132
147
|
// 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 ? "prod"?.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
|
-
}
|
|
148
|
+
var import_viem6 = require("viem");
|
|
245
149
|
|
|
246
150
|
// src/client/common/docker/build.ts
|
|
247
151
|
var child_process = __toESM(require("child_process"), 1);
|
|
@@ -1341,12 +1245,21 @@ function extractRegistryNameNoDocker(imageRef) {
|
|
|
1341
1245
|
return name;
|
|
1342
1246
|
}
|
|
1343
1247
|
|
|
1344
|
-
// src/client/common/contract/caller.ts
|
|
1345
|
-
var import_accounts2 = require("viem/accounts");
|
|
1346
|
-
|
|
1347
1248
|
// src/client/common/contract/eip7702.ts
|
|
1348
1249
|
var import_viem = require("viem");
|
|
1349
1250
|
|
|
1251
|
+
// src/client/common/types/index.ts
|
|
1252
|
+
var noopLogger = {
|
|
1253
|
+
debug: () => {
|
|
1254
|
+
},
|
|
1255
|
+
info: () => {
|
|
1256
|
+
},
|
|
1257
|
+
warn: () => {
|
|
1258
|
+
},
|
|
1259
|
+
error: () => {
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1350
1263
|
// src/client/common/abis/ERC7702Delegator.json
|
|
1351
1264
|
var ERC7702Delegator_default = [
|
|
1352
1265
|
{
|
|
@@ -2432,7 +2345,7 @@ async function checkERC7702Delegation(publicClient, account, delegatorAddress) {
|
|
|
2432
2345
|
const expectedCode = `0xef0100${delegatorAddress.slice(2)}`;
|
|
2433
2346
|
return code.toLowerCase() === expectedCode.toLowerCase();
|
|
2434
2347
|
}
|
|
2435
|
-
async function executeBatch(options, logger) {
|
|
2348
|
+
async function executeBatch(options, logger = noopLogger) {
|
|
2436
2349
|
const { walletClient, publicClient, environmentConfig, executions, pendingMessage, gas } = options;
|
|
2437
2350
|
const account = walletClient.account;
|
|
2438
2351
|
if (!account) {
|
|
@@ -2456,11 +2369,12 @@ async function executeBatch(options, logger) {
|
|
|
2456
2369
|
});
|
|
2457
2370
|
const chainId = await publicClient.getChainId();
|
|
2458
2371
|
const authorizationNonce = transactionNonce + 1;
|
|
2372
|
+
logger.debug("Using wallet client signing for EIP-7702 authorization");
|
|
2459
2373
|
const signedAuthorization = await walletClient.signAuthorization({
|
|
2460
|
-
account,
|
|
2374
|
+
account: account.address,
|
|
2461
2375
|
contractAddress: environmentConfig.erc7702DelegatorAddress,
|
|
2462
|
-
chainId
|
|
2463
|
-
nonce: authorizationNonce
|
|
2376
|
+
chainId,
|
|
2377
|
+
nonce: Number(authorizationNonce)
|
|
2464
2378
|
});
|
|
2465
2379
|
authorizationList = [signedAuthorization];
|
|
2466
2380
|
}
|
|
@@ -2515,78 +2429,31 @@ async function executeBatch(options, logger) {
|
|
|
2515
2429
|
}
|
|
2516
2430
|
|
|
2517
2431
|
// 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");
|
|
2432
|
+
var import_viem3 = require("viem");
|
|
2583
2433
|
|
|
2584
2434
|
// src/client/common/utils/helpers.ts
|
|
2585
|
-
var
|
|
2435
|
+
var import_viem2 = require("viem");
|
|
2586
2436
|
var import_chains2 = require("viem/chains");
|
|
2437
|
+
var import_accounts = require("viem/accounts");
|
|
2587
2438
|
function getChainFromID(chainID, fallback = import_chains2.sepolia) {
|
|
2588
2439
|
const id = Number(chainID);
|
|
2589
|
-
return (0,
|
|
2440
|
+
return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
|
|
2441
|
+
}
|
|
2442
|
+
function createClients(options) {
|
|
2443
|
+
const { privateKey, rpcUrl, chainId } = options;
|
|
2444
|
+
const privateKeyHex = addHexPrefix(privateKey);
|
|
2445
|
+
const account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
|
|
2446
|
+
const chain = getChainFromID(chainId);
|
|
2447
|
+
const publicClient = (0, import_viem2.createPublicClient)({
|
|
2448
|
+
chain,
|
|
2449
|
+
transport: (0, import_viem2.http)(rpcUrl)
|
|
2450
|
+
});
|
|
2451
|
+
const walletClient = (0, import_viem2.createWalletClient)({
|
|
2452
|
+
account,
|
|
2453
|
+
chain,
|
|
2454
|
+
transport: (0, import_viem2.http)(rpcUrl)
|
|
2455
|
+
});
|
|
2456
|
+
return { walletClient, publicClient };
|
|
2590
2457
|
}
|
|
2591
2458
|
function addHexPrefix(value) {
|
|
2592
2459
|
return value.startsWith("0x") ? value : `0x${value}`;
|
|
@@ -2595,302 +2462,6 @@ function stripHexPrefix(value) {
|
|
|
2595
2462
|
return value.startsWith("0x") ? value.slice(2) : value;
|
|
2596
2463
|
}
|
|
2597
2464
|
|
|
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" : "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
2465
|
// src/client/common/abis/AppController.json
|
|
2895
2466
|
var AppController_default = [
|
|
2896
2467
|
{
|
|
@@ -4446,17 +4017,10 @@ function formatETH(wei) {
|
|
|
4446
4017
|
return trimmed;
|
|
4447
4018
|
}
|
|
4448
4019
|
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
|
-
});
|
|
4020
|
+
const { publicClient, from, to, data, value = 0n } = options;
|
|
4457
4021
|
const fees = await publicClient.estimateFeesPerGas();
|
|
4458
4022
|
const gasLimit = await publicClient.estimateGas({
|
|
4459
|
-
account:
|
|
4023
|
+
account: from,
|
|
4460
4024
|
to,
|
|
4461
4025
|
data,
|
|
4462
4026
|
value
|
|
@@ -4473,65 +4037,54 @@ async function estimateTransactionGas(options) {
|
|
|
4473
4037
|
maxCostEth
|
|
4474
4038
|
};
|
|
4475
4039
|
}
|
|
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");
|
|
4040
|
+
async function calculateAppID(options) {
|
|
4041
|
+
const { publicClient, environmentConfig, ownerAddress, salt } = options;
|
|
4042
|
+
const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
|
|
4485
4043
|
const paddedSaltHex = saltHexString.padStart(64, "0");
|
|
4486
4044
|
const saltHex = `0x${paddedSaltHex}`;
|
|
4487
|
-
const accountAddress = typeof account.address === "string" ? account.address : account.address.toString();
|
|
4488
4045
|
const appID = await publicClient.readContract({
|
|
4489
4046
|
address: environmentConfig.appControllerAddress,
|
|
4490
4047
|
abi: AppController_default,
|
|
4491
4048
|
functionName: "calculateAppId",
|
|
4492
|
-
args: [
|
|
4049
|
+
args: [ownerAddress, saltHex]
|
|
4493
4050
|
});
|
|
4494
4051
|
return appID;
|
|
4495
4052
|
}
|
|
4496
|
-
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
|
-
});
|
|
4053
|
+
async function prepareDeployBatch(options, logger = noopLogger) {
|
|
4054
|
+
const { walletClient, publicClient, environmentConfig, salt, release, publicLogs } = options;
|
|
4055
|
+
const account = walletClient.account;
|
|
4056
|
+
if (!account) {
|
|
4057
|
+
throw new Error("WalletClient must have an account attached");
|
|
4058
|
+
}
|
|
4510
4059
|
logger.info("Calculating app ID...");
|
|
4511
|
-
const appId = await calculateAppID(
|
|
4512
|
-
|
|
4060
|
+
const appId = await calculateAppID({
|
|
4061
|
+
publicClient,
|
|
4062
|
+
environmentConfig,
|
|
4063
|
+
ownerAddress: account.address,
|
|
4064
|
+
salt
|
|
4065
|
+
});
|
|
4513
4066
|
logger.debug(`App ID calculated: ${appId}`);
|
|
4514
4067
|
logger.debug(`This address will be used for acceptAdmin call`);
|
|
4515
|
-
const saltHexString =
|
|
4068
|
+
const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
|
|
4516
4069
|
const paddedSaltHex = saltHexString.padStart(64, "0");
|
|
4517
4070
|
const saltHex = `0x${paddedSaltHex}`;
|
|
4518
4071
|
const releaseForViem = {
|
|
4519
4072
|
rmsRelease: {
|
|
4520
4073
|
artifacts: release.rmsRelease.artifacts.map((artifact) => ({
|
|
4521
|
-
digest: `0x${
|
|
4074
|
+
digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
|
|
4522
4075
|
registry: artifact.registry
|
|
4523
4076
|
})),
|
|
4524
4077
|
upgradeByTime: release.rmsRelease.upgradeByTime
|
|
4525
4078
|
},
|
|
4526
|
-
publicEnv:
|
|
4527
|
-
encryptedEnv:
|
|
4079
|
+
publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
|
|
4080
|
+
encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
|
|
4528
4081
|
};
|
|
4529
|
-
const createData = (0,
|
|
4082
|
+
const createData = (0, import_viem3.encodeFunctionData)({
|
|
4530
4083
|
abi: AppController_default,
|
|
4531
4084
|
functionName: "createApp",
|
|
4532
4085
|
args: [saltHex, releaseForViem]
|
|
4533
4086
|
});
|
|
4534
|
-
const acceptAdminData = (0,
|
|
4087
|
+
const acceptAdminData = (0, import_viem3.encodeFunctionData)({
|
|
4535
4088
|
abi: PermissionController_default,
|
|
4536
4089
|
functionName: "acceptAdmin",
|
|
4537
4090
|
args: [appId]
|
|
@@ -4549,7 +4102,7 @@ async function prepareDeployBatch(options, logger) {
|
|
|
4549
4102
|
}
|
|
4550
4103
|
];
|
|
4551
4104
|
if (publicLogs) {
|
|
4552
|
-
const anyoneCanViewLogsData = (0,
|
|
4105
|
+
const anyoneCanViewLogsData = (0, import_viem3.encodeFunctionData)({
|
|
4553
4106
|
abi: PermissionController_default,
|
|
4554
4107
|
functionName: "setAppointee",
|
|
4555
4108
|
args: [
|
|
@@ -4577,7 +4130,7 @@ async function prepareDeployBatch(options, logger) {
|
|
|
4577
4130
|
environmentConfig
|
|
4578
4131
|
};
|
|
4579
4132
|
}
|
|
4580
|
-
async function executeDeployBatch(data, context, gas, logger) {
|
|
4133
|
+
async function executeDeployBatch(data, context, gas, logger = noopLogger) {
|
|
4581
4134
|
const pendingMessage = "Deploying new app...";
|
|
4582
4135
|
const txHash = await executeBatch(
|
|
4583
4136
|
{
|
|
@@ -4592,18 +4145,8 @@ async function executeDeployBatch(data, context, gas, logger) {
|
|
|
4592
4145
|
);
|
|
4593
4146
|
return { appId: data.appId, txHash };
|
|
4594
4147
|
}
|
|
4595
|
-
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
|
-
);
|
|
4148
|
+
async function deployApp(options, logger = noopLogger) {
|
|
4149
|
+
const prepared = await prepareDeployBatch(options, logger);
|
|
4607
4150
|
const data = {
|
|
4608
4151
|
appId: prepared.appId,
|
|
4609
4152
|
salt: prepared.salt,
|
|
@@ -4618,41 +4161,29 @@ async function deployApp(options, logger) {
|
|
|
4618
4161
|
}
|
|
4619
4162
|
async function prepareUpgradeBatch(options) {
|
|
4620
4163
|
const {
|
|
4621
|
-
|
|
4622
|
-
|
|
4164
|
+
walletClient,
|
|
4165
|
+
publicClient,
|
|
4623
4166
|
environmentConfig,
|
|
4624
|
-
|
|
4167
|
+
appID,
|
|
4625
4168
|
release,
|
|
4626
4169
|
publicLogs,
|
|
4627
4170
|
needsPermissionChange
|
|
4628
4171
|
} = 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
|
-
});
|
|
4641
4172
|
const releaseForViem = {
|
|
4642
4173
|
rmsRelease: {
|
|
4643
4174
|
artifacts: release.rmsRelease.artifacts.map((artifact) => ({
|
|
4644
|
-
digest: `0x${
|
|
4175
|
+
digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
|
|
4645
4176
|
registry: artifact.registry
|
|
4646
4177
|
})),
|
|
4647
4178
|
upgradeByTime: release.rmsRelease.upgradeByTime
|
|
4648
4179
|
},
|
|
4649
|
-
publicEnv:
|
|
4650
|
-
encryptedEnv:
|
|
4180
|
+
publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
|
|
4181
|
+
encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
|
|
4651
4182
|
};
|
|
4652
|
-
const upgradeData = (0,
|
|
4183
|
+
const upgradeData = (0, import_viem3.encodeFunctionData)({
|
|
4653
4184
|
abi: AppController_default,
|
|
4654
4185
|
functionName: "upgradeApp",
|
|
4655
|
-
args: [
|
|
4186
|
+
args: [appID, releaseForViem]
|
|
4656
4187
|
});
|
|
4657
4188
|
const executions = [
|
|
4658
4189
|
{
|
|
@@ -4663,11 +4194,11 @@ async function prepareUpgradeBatch(options) {
|
|
|
4663
4194
|
];
|
|
4664
4195
|
if (needsPermissionChange) {
|
|
4665
4196
|
if (publicLogs) {
|
|
4666
|
-
const addLogsData = (0,
|
|
4197
|
+
const addLogsData = (0, import_viem3.encodeFunctionData)({
|
|
4667
4198
|
abi: PermissionController_default,
|
|
4668
4199
|
functionName: "setAppointee",
|
|
4669
4200
|
args: [
|
|
4670
|
-
|
|
4201
|
+
appID,
|
|
4671
4202
|
"0x493219d9949348178af1f58740655951a8cd110c",
|
|
4672
4203
|
// AnyoneCanCallAddress
|
|
4673
4204
|
"0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
|
|
@@ -4682,11 +4213,11 @@ async function prepareUpgradeBatch(options) {
|
|
|
4682
4213
|
callData: addLogsData
|
|
4683
4214
|
});
|
|
4684
4215
|
} else {
|
|
4685
|
-
const removeLogsData = (0,
|
|
4216
|
+
const removeLogsData = (0, import_viem3.encodeFunctionData)({
|
|
4686
4217
|
abi: PermissionController_default,
|
|
4687
4218
|
functionName: "removeAppointee",
|
|
4688
4219
|
args: [
|
|
4689
|
-
|
|
4220
|
+
appID,
|
|
4690
4221
|
"0x493219d9949348178af1f58740655951a8cd110c",
|
|
4691
4222
|
// AnyoneCanCallAddress
|
|
4692
4223
|
"0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
|
|
@@ -4703,14 +4234,14 @@ async function prepareUpgradeBatch(options) {
|
|
|
4703
4234
|
}
|
|
4704
4235
|
}
|
|
4705
4236
|
return {
|
|
4706
|
-
appId,
|
|
4237
|
+
appId: appID,
|
|
4707
4238
|
executions,
|
|
4708
4239
|
walletClient,
|
|
4709
4240
|
publicClient,
|
|
4710
4241
|
environmentConfig
|
|
4711
4242
|
};
|
|
4712
4243
|
}
|
|
4713
|
-
async function executeUpgradeBatch(data, context, gas, logger) {
|
|
4244
|
+
async function executeUpgradeBatch(data, context, gas, logger = noopLogger) {
|
|
4714
4245
|
const pendingMessage = `Upgrading app ${data.appId}...`;
|
|
4715
4246
|
const txHash = await executeBatch(
|
|
4716
4247
|
{
|
|
@@ -4725,16 +4256,8 @@ async function executeUpgradeBatch(data, context, gas, logger) {
|
|
|
4725
4256
|
);
|
|
4726
4257
|
return txHash;
|
|
4727
4258
|
}
|
|
4728
|
-
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
|
-
});
|
|
4259
|
+
async function upgradeApp(options, logger = noopLogger) {
|
|
4260
|
+
const prepared = await prepareUpgradeBatch(options);
|
|
4738
4261
|
const data = {
|
|
4739
4262
|
appId: prepared.appId,
|
|
4740
4263
|
executions: prepared.executions
|
|
@@ -4746,10 +4269,10 @@ async function upgradeApp(options, logger) {
|
|
|
4746
4269
|
};
|
|
4747
4270
|
return executeUpgradeBatch(data, context, options.gas, logger);
|
|
4748
4271
|
}
|
|
4749
|
-
async function sendAndWaitForTransaction(options, logger) {
|
|
4272
|
+
async function sendAndWaitForTransaction(options, logger = noopLogger) {
|
|
4750
4273
|
const {
|
|
4751
|
-
|
|
4752
|
-
|
|
4274
|
+
walletClient,
|
|
4275
|
+
publicClient,
|
|
4753
4276
|
environmentConfig,
|
|
4754
4277
|
to,
|
|
4755
4278
|
data,
|
|
@@ -4758,18 +4281,11 @@ async function sendAndWaitForTransaction(options, logger) {
|
|
|
4758
4281
|
txDescription,
|
|
4759
4282
|
gas
|
|
4760
4283
|
} = options;
|
|
4761
|
-
const
|
|
4762
|
-
|
|
4284
|
+
const account = walletClient.account;
|
|
4285
|
+
if (!account) {
|
|
4286
|
+
throw new Error("WalletClient must have an account attached");
|
|
4287
|
+
}
|
|
4763
4288
|
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
4289
|
if (pendingMessage) {
|
|
4774
4290
|
logger.info(`
|
|
4775
4291
|
${pendingMessage}`);
|
|
@@ -4780,7 +4296,10 @@ ${pendingMessage}`);
|
|
|
4780
4296
|
data,
|
|
4781
4297
|
value,
|
|
4782
4298
|
...gas?.maxFeePerGas && { maxFeePerGas: gas.maxFeePerGas },
|
|
4783
|
-
...gas?.maxPriorityFeePerGas && {
|
|
4299
|
+
...gas?.maxPriorityFeePerGas && {
|
|
4300
|
+
maxPriorityFeePerGas: gas.maxPriorityFeePerGas
|
|
4301
|
+
},
|
|
4302
|
+
chain
|
|
4784
4303
|
});
|
|
4785
4304
|
logger.info(`Transaction sent: ${hash}`);
|
|
4786
4305
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -4795,7 +4314,7 @@ ${pendingMessage}`);
|
|
|
4795
4314
|
} catch (callError) {
|
|
4796
4315
|
if (callError.data) {
|
|
4797
4316
|
try {
|
|
4798
|
-
const decoded = (0,
|
|
4317
|
+
const decoded = (0, import_viem3.decodeErrorResult)({
|
|
4799
4318
|
abi: AppController_default,
|
|
4800
4319
|
data: callError.data
|
|
4801
4320
|
});
|
|
@@ -4846,12 +4365,7 @@ function formatAppControllerError(decoded) {
|
|
|
4846
4365
|
return new Error(`contract error: ${errorName}`);
|
|
4847
4366
|
}
|
|
4848
4367
|
}
|
|
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
|
-
});
|
|
4368
|
+
async function getActiveAppCount(publicClient, environmentConfig, user) {
|
|
4855
4369
|
const count = await publicClient.readContract({
|
|
4856
4370
|
address: environmentConfig.appControllerAddress,
|
|
4857
4371
|
abi: AppController_default,
|
|
@@ -4860,12 +4374,7 @@ async function getActiveAppCount(rpcUrl, environmentConfig, user) {
|
|
|
4860
4374
|
});
|
|
4861
4375
|
return Number(count);
|
|
4862
4376
|
}
|
|
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
|
-
});
|
|
4377
|
+
async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
|
|
4869
4378
|
const quota = await publicClient.readContract({
|
|
4870
4379
|
address: environmentConfig.appControllerAddress,
|
|
4871
4380
|
abi: AppController_default,
|
|
@@ -4874,12 +4383,7 @@ async function getMaxActiveAppsPerUser(rpcUrl, environmentConfig, user) {
|
|
|
4874
4383
|
});
|
|
4875
4384
|
return Number(quota);
|
|
4876
4385
|
}
|
|
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
|
-
});
|
|
4386
|
+
async function getAppsByDeveloper(publicClient, environmentConfig, developer, offset, limit) {
|
|
4883
4387
|
const result = await publicClient.readContract({
|
|
4884
4388
|
address: environmentConfig.appControllerAddress,
|
|
4885
4389
|
abi: AppController_default,
|
|
@@ -4891,12 +4395,18 @@ async function getAppsByDeveloper(rpcUrl, environmentConfig, developer, offset,
|
|
|
4891
4395
|
appConfigs: result[1]
|
|
4892
4396
|
};
|
|
4893
4397
|
}
|
|
4894
|
-
async function getAllAppsByDeveloper(
|
|
4398
|
+
async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 100n) {
|
|
4895
4399
|
let offset = 0n;
|
|
4896
4400
|
const allApps = [];
|
|
4897
4401
|
const allConfigs = [];
|
|
4898
4402
|
while (true) {
|
|
4899
|
-
const { apps, appConfigs } = await getAppsByDeveloper(
|
|
4403
|
+
const { apps, appConfigs } = await getAppsByDeveloper(
|
|
4404
|
+
publicClient,
|
|
4405
|
+
env,
|
|
4406
|
+
developer,
|
|
4407
|
+
offset,
|
|
4408
|
+
pageSize
|
|
4409
|
+
);
|
|
4900
4410
|
if (apps.length === 0) break;
|
|
4901
4411
|
allApps.push(...apps);
|
|
4902
4412
|
allConfigs.push(...appConfigs);
|
|
@@ -4908,12 +4418,7 @@ async function getAllAppsByDeveloper(rpcUrl, env, developer, pageSize = 100n) {
|
|
|
4908
4418
|
appConfigs: allConfigs
|
|
4909
4419
|
};
|
|
4910
4420
|
}
|
|
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
|
-
});
|
|
4421
|
+
async function getAppLatestReleaseBlockNumbers(publicClient, environmentConfig, appIDs) {
|
|
4917
4422
|
const results = await Promise.all(
|
|
4918
4423
|
appIDs.map(
|
|
4919
4424
|
(appID) => publicClient.readContract({
|
|
@@ -4933,12 +4438,7 @@ async function getAppLatestReleaseBlockNumbers(rpcUrl, environmentConfig, appIDs
|
|
|
4933
4438
|
}
|
|
4934
4439
|
return blockNumbers;
|
|
4935
4440
|
}
|
|
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
|
-
});
|
|
4441
|
+
async function getBlockTimestamps(publicClient, blockNumbers) {
|
|
4942
4442
|
const uniqueBlockNumbers = [...new Set(blockNumbers)].filter((n) => n > 0);
|
|
4943
4443
|
const timestamps = /* @__PURE__ */ new Map();
|
|
4944
4444
|
const blocks = await Promise.all(
|
|
@@ -4955,67 +4455,34 @@ async function getBlockTimestamps(rpcUrl, environmentConfig, blockNumbers) {
|
|
|
4955
4455
|
return timestamps;
|
|
4956
4456
|
}
|
|
4957
4457
|
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
|
-
});
|
|
4458
|
+
const { publicClient, environmentConfig, address } = options;
|
|
4966
4459
|
return checkERC7702Delegation(
|
|
4967
4460
|
publicClient,
|
|
4968
|
-
|
|
4461
|
+
address,
|
|
4969
4462
|
environmentConfig.erc7702DelegatorAddress
|
|
4970
4463
|
);
|
|
4971
4464
|
}
|
|
4972
|
-
async function undelegate(options, logger) {
|
|
4973
|
-
const {
|
|
4974
|
-
const
|
|
4975
|
-
|
|
4465
|
+
async function undelegate(options, logger = noopLogger) {
|
|
4466
|
+
const { walletClient, publicClient, environmentConfig } = options;
|
|
4467
|
+
const account = walletClient.account;
|
|
4468
|
+
if (!account) {
|
|
4469
|
+
throw new Error("WalletClient must have an account attached");
|
|
4470
|
+
}
|
|
4976
4471
|
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
4472
|
const transactionNonce = await publicClient.getTransactionCount({
|
|
4987
4473
|
address: account.address,
|
|
4988
4474
|
blockTag: "pending"
|
|
4989
4475
|
});
|
|
4990
4476
|
const chainId = await publicClient.getChainId();
|
|
4991
4477
|
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
|
|
4478
|
+
logger.debug("Signing undelegate authorization");
|
|
4479
|
+
const signedAuthorization = await walletClient.signAuthorization({
|
|
4480
|
+
contractAddress: "0x0000000000000000000000000000000000000000",
|
|
4481
|
+
chainId,
|
|
4482
|
+
nonce: Number(authorizationNonce),
|
|
4483
|
+
account
|
|
5006
4484
|
});
|
|
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
|
-
];
|
|
4485
|
+
const authorizationList = [signedAuthorization];
|
|
5019
4486
|
const hash = await walletClient.sendTransaction({
|
|
5020
4487
|
account,
|
|
5021
4488
|
to: account.address,
|
|
@@ -5023,7 +4490,8 @@ async function undelegate(options, logger) {
|
|
|
5023
4490
|
data: "0x",
|
|
5024
4491
|
// Empty data
|
|
5025
4492
|
value: 0n,
|
|
5026
|
-
authorizationList
|
|
4493
|
+
authorizationList,
|
|
4494
|
+
chain
|
|
5027
4495
|
});
|
|
5028
4496
|
logger.info(`Transaction sent: ${hash}`);
|
|
5029
4497
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -5034,340 +4502,874 @@ async function undelegate(options, logger) {
|
|
|
5034
4502
|
return hash;
|
|
5035
4503
|
}
|
|
5036
4504
|
|
|
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
|
-
}
|
|
4505
|
+
// src/client/common/utils/userapi.ts
|
|
4506
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
4507
|
+
|
|
4508
|
+
// src/client/common/utils/auth.ts
|
|
4509
|
+
var import_viem4 = require("viem");
|
|
4510
|
+
var APP_CONTROLLER_ABI = (0, import_viem4.parseAbi)([
|
|
4511
|
+
"function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
|
|
4512
|
+
]);
|
|
4513
|
+
async function calculatePermissionSignature(options) {
|
|
4514
|
+
const { permission, expiry, appControllerAddress, publicClient, walletClient } = options;
|
|
4515
|
+
const digest = await publicClient.readContract({
|
|
4516
|
+
address: appControllerAddress,
|
|
4517
|
+
abi: APP_CONTROLLER_ABI,
|
|
4518
|
+
functionName: "calculateApiPermissionDigestHash",
|
|
4519
|
+
args: [permission, expiry]
|
|
4520
|
+
});
|
|
4521
|
+
const account = walletClient.account;
|
|
4522
|
+
if (!account) {
|
|
4523
|
+
throw new Error("WalletClient must have an account attached");
|
|
5088
4524
|
}
|
|
4525
|
+
const signature = await walletClient.signMessage({
|
|
4526
|
+
account,
|
|
4527
|
+
message: { raw: digest }
|
|
4528
|
+
});
|
|
4529
|
+
return { signature, digest };
|
|
5089
4530
|
}
|
|
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`);
|
|
4531
|
+
var generateBillingSigData = (product, expiry) => {
|
|
4532
|
+
return {
|
|
4533
|
+
domain: {
|
|
4534
|
+
name: "EigenCloud Billing API",
|
|
4535
|
+
version: "1"
|
|
4536
|
+
},
|
|
4537
|
+
types: {
|
|
4538
|
+
BillingAuth: [
|
|
4539
|
+
{ name: "product", type: "string" },
|
|
4540
|
+
{ name: "expiry", type: "uint256" }
|
|
4541
|
+
]
|
|
4542
|
+
},
|
|
4543
|
+
primaryType: "BillingAuth",
|
|
4544
|
+
message: {
|
|
4545
|
+
product,
|
|
4546
|
+
expiry
|
|
5127
4547
|
}
|
|
5128
|
-
return false;
|
|
5129
4548
|
};
|
|
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
|
-
}
|
|
4549
|
+
};
|
|
4550
|
+
async function calculateBillingAuthSignature(options) {
|
|
4551
|
+
const { walletClient, product, expiry } = options;
|
|
4552
|
+
const account = walletClient.account;
|
|
4553
|
+
if (!account) {
|
|
4554
|
+
throw new Error("WalletClient must have an account attached");
|
|
5148
4555
|
}
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
4556
|
+
const signature = await walletClient.signTypedData({
|
|
4557
|
+
account,
|
|
4558
|
+
...generateBillingSigData(product, expiry)
|
|
4559
|
+
});
|
|
4560
|
+
return { signature, expiry };
|
|
5152
4561
|
}
|
|
5153
4562
|
|
|
5154
|
-
// src/client/common/
|
|
5155
|
-
var
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
}
|
|
5162
|
-
if (name.includes(" ")) {
|
|
5163
|
-
throw new Error("App name cannot contain spaces");
|
|
4563
|
+
// src/client/common/auth/session.ts
|
|
4564
|
+
var SessionError = class extends Error {
|
|
4565
|
+
constructor(message, code, statusCode) {
|
|
4566
|
+
super(message);
|
|
4567
|
+
this.code = code;
|
|
4568
|
+
this.statusCode = statusCode;
|
|
4569
|
+
this.name = "SessionError";
|
|
5164
4570
|
}
|
|
5165
|
-
|
|
5166
|
-
|
|
4571
|
+
};
|
|
4572
|
+
function stripHexPrefix2(hex) {
|
|
4573
|
+
return hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
4574
|
+
}
|
|
4575
|
+
async function parseErrorResponse(response) {
|
|
4576
|
+
try {
|
|
4577
|
+
const data = await response.json();
|
|
4578
|
+
return data.error || response.statusText;
|
|
4579
|
+
} catch {
|
|
4580
|
+
return response.statusText;
|
|
5167
4581
|
}
|
|
5168
4582
|
}
|
|
5169
|
-
function
|
|
5170
|
-
|
|
5171
|
-
|
|
4583
|
+
async function loginToComputeApi(config, request) {
|
|
4584
|
+
let response;
|
|
4585
|
+
try {
|
|
4586
|
+
response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
|
|
4587
|
+
method: "POST",
|
|
4588
|
+
credentials: "include",
|
|
4589
|
+
// Include cookies for session management
|
|
4590
|
+
headers: {
|
|
4591
|
+
"Content-Type": "application/json"
|
|
4592
|
+
},
|
|
4593
|
+
body: JSON.stringify({
|
|
4594
|
+
message: request.message,
|
|
4595
|
+
signature: stripHexPrefix2(request.signature)
|
|
4596
|
+
})
|
|
4597
|
+
});
|
|
4598
|
+
} catch (error) {
|
|
4599
|
+
throw new SessionError(
|
|
4600
|
+
`Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
|
|
4601
|
+
"NETWORK_ERROR"
|
|
4602
|
+
);
|
|
5172
4603
|
}
|
|
5173
|
-
if (!
|
|
5174
|
-
|
|
4604
|
+
if (!response.ok) {
|
|
4605
|
+
const errorMessage = await parseErrorResponse(response);
|
|
4606
|
+
const status = response.status;
|
|
4607
|
+
if (status === 400) {
|
|
4608
|
+
if (errorMessage.toLowerCase().includes("siwe")) {
|
|
4609
|
+
throw new SessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
|
|
4610
|
+
}
|
|
4611
|
+
throw new SessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
|
|
4612
|
+
}
|
|
4613
|
+
if (status === 401) {
|
|
4614
|
+
throw new SessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
|
|
4615
|
+
}
|
|
4616
|
+
throw new SessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
|
|
5175
4617
|
}
|
|
5176
|
-
|
|
4618
|
+
const data = await response.json();
|
|
4619
|
+
return {
|
|
4620
|
+
success: data.success,
|
|
4621
|
+
address: data.address
|
|
4622
|
+
};
|
|
5177
4623
|
}
|
|
5178
|
-
function
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
4624
|
+
async function getComputeApiSession(config) {
|
|
4625
|
+
let response;
|
|
4626
|
+
try {
|
|
4627
|
+
response = await fetch(`${config.baseUrl}/auth/session`, {
|
|
4628
|
+
method: "GET",
|
|
4629
|
+
credentials: "include",
|
|
4630
|
+
// Include cookies for session management
|
|
4631
|
+
headers: {
|
|
4632
|
+
"Content-Type": "application/json"
|
|
4633
|
+
}
|
|
4634
|
+
});
|
|
4635
|
+
} catch {
|
|
4636
|
+
return {
|
|
4637
|
+
authenticated: false
|
|
4638
|
+
};
|
|
5182
4639
|
}
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
if (imageName.includes(":")) {
|
|
5188
|
-
imageName = imageName.split(":")[0];
|
|
4640
|
+
if (response.status === 401) {
|
|
4641
|
+
return {
|
|
4642
|
+
authenticated: false
|
|
4643
|
+
};
|
|
5189
4644
|
}
|
|
5190
|
-
|
|
4645
|
+
if (!response.ok) {
|
|
4646
|
+
const errorMessage = await parseErrorResponse(response);
|
|
4647
|
+
throw new SessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
|
|
4648
|
+
}
|
|
4649
|
+
const data = await response.json();
|
|
4650
|
+
return {
|
|
4651
|
+
authenticated: data.authenticated,
|
|
4652
|
+
address: data.address,
|
|
4653
|
+
chainId: data.chain_id
|
|
4654
|
+
};
|
|
5191
4655
|
}
|
|
5192
|
-
function
|
|
5193
|
-
|
|
5194
|
-
|
|
4656
|
+
async function logoutFromComputeApi(config) {
|
|
4657
|
+
let response;
|
|
4658
|
+
try {
|
|
4659
|
+
response = await fetch(`${config.baseUrl}/auth/logout`, {
|
|
4660
|
+
method: "POST",
|
|
4661
|
+
credentials: "include",
|
|
4662
|
+
// Include cookies for session management
|
|
4663
|
+
headers: {
|
|
4664
|
+
"Content-Type": "application/json"
|
|
4665
|
+
}
|
|
4666
|
+
});
|
|
4667
|
+
} catch (error) {
|
|
4668
|
+
throw new SessionError(
|
|
4669
|
+
`Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
|
|
4670
|
+
"NETWORK_ERROR"
|
|
4671
|
+
);
|
|
5195
4672
|
}
|
|
5196
|
-
if (
|
|
5197
|
-
return
|
|
4673
|
+
if (response.status === 401) {
|
|
4674
|
+
return;
|
|
5198
4675
|
}
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
const result = validateFilePath(value);
|
|
5203
|
-
if (result !== true) {
|
|
5204
|
-
throw new Error(result);
|
|
4676
|
+
if (!response.ok) {
|
|
4677
|
+
const errorMessage = await parseErrorResponse(response);
|
|
4678
|
+
throw new SessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
|
|
5205
4679
|
}
|
|
5206
4680
|
}
|
|
5207
|
-
function
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
}
|
|
5211
|
-
for (const it of availableTypes) {
|
|
5212
|
-
if (it.sku === sku) {
|
|
5213
|
-
return sku;
|
|
5214
|
-
}
|
|
5215
|
-
}
|
|
5216
|
-
const validSKUs = availableTypes.map((it) => it.sku).join(", ");
|
|
5217
|
-
throw new Error(`Invalid instance-type value: ${sku} (must be one of: ${validSKUs})`);
|
|
4681
|
+
async function isSessionValid(config) {
|
|
4682
|
+
const session = await getComputeApiSession(config);
|
|
4683
|
+
return session.authenticated;
|
|
5218
4684
|
}
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
}
|
|
5224
|
-
return true;
|
|
4685
|
+
|
|
4686
|
+
// src/client/common/utils/userapi.ts
|
|
4687
|
+
function isJsonObject(value) {
|
|
4688
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5225
4689
|
}
|
|
5226
|
-
function
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
}
|
|
5230
|
-
if (!validatePrivateKeyFormat(key)) {
|
|
5231
|
-
throw new Error(
|
|
5232
|
-
"Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)"
|
|
5233
|
-
);
|
|
5234
|
-
}
|
|
4690
|
+
function readString(obj, key) {
|
|
4691
|
+
const v = obj[key];
|
|
4692
|
+
return typeof v === "string" ? v : void 0;
|
|
5235
4693
|
}
|
|
5236
|
-
function
|
|
5237
|
-
|
|
5238
|
-
|
|
4694
|
+
function readNumber(obj, key) {
|
|
4695
|
+
const v = obj[key];
|
|
4696
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
4697
|
+
}
|
|
4698
|
+
var MAX_ADDRESS_COUNT = 5;
|
|
4699
|
+
var CanViewAppLogsPermission = "0x2fd3f2fe";
|
|
4700
|
+
var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
|
|
4701
|
+
var CanUpdateAppProfilePermission = "0x036fef61";
|
|
4702
|
+
function getDefaultClientId() {
|
|
4703
|
+
const version = true ? "0.2.2-dev" : "0.0.0";
|
|
4704
|
+
return `ecloud-sdk/v${version}`;
|
|
4705
|
+
}
|
|
4706
|
+
var UserApiClient = class {
|
|
4707
|
+
constructor(config, walletClient, publicClient, options) {
|
|
4708
|
+
this.config = config;
|
|
4709
|
+
this.walletClient = walletClient;
|
|
4710
|
+
this.publicClient = publicClient;
|
|
4711
|
+
this.clientId = options?.clientId || getDefaultClientId();
|
|
4712
|
+
this.useSession = options?.useSession ?? false;
|
|
5239
4713
|
}
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
4714
|
+
/**
|
|
4715
|
+
* Get the address of the connected wallet
|
|
4716
|
+
*/
|
|
4717
|
+
get address() {
|
|
4718
|
+
const account = this.walletClient.account;
|
|
4719
|
+
if (!account) {
|
|
4720
|
+
throw new Error("WalletClient must have an account attached");
|
|
5244
4721
|
}
|
|
5245
|
-
|
|
5246
|
-
return "Invalid URL format";
|
|
4722
|
+
return account.address;
|
|
5247
4723
|
}
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
return
|
|
4724
|
+
async getInfos(appIDs, addressCount = 1) {
|
|
4725
|
+
const count = Math.min(addressCount, MAX_ADDRESS_COUNT);
|
|
4726
|
+
const endpoint = `${this.config.userApiServerURL}/info`;
|
|
4727
|
+
const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
|
|
4728
|
+
const res = await this.makeAuthenticatedRequest(url, CanViewSensitiveAppInfoPermission);
|
|
4729
|
+
const result = await res.json();
|
|
4730
|
+
return result.apps.map((app, i) => {
|
|
4731
|
+
const evmAddresses = app.addresses?.data?.evmAddresses?.slice(0, count) || [];
|
|
4732
|
+
const solanaAddresses = app.addresses?.data?.solanaAddresses?.slice(0, count) || [];
|
|
4733
|
+
return {
|
|
4734
|
+
address: appIDs[i],
|
|
4735
|
+
status: app.app_status,
|
|
4736
|
+
ip: app.ip,
|
|
4737
|
+
machineType: app.machine_type,
|
|
4738
|
+
profile: app.profile,
|
|
4739
|
+
metrics: app.metrics,
|
|
4740
|
+
evmAddresses,
|
|
4741
|
+
solanaAddresses
|
|
4742
|
+
};
|
|
4743
|
+
});
|
|
5255
4744
|
}
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
4745
|
+
/**
|
|
4746
|
+
* Get app details from UserAPI (includes releases and build/provenance info when available).
|
|
4747
|
+
*
|
|
4748
|
+
* Endpoint: GET /apps/:appAddress
|
|
4749
|
+
*/
|
|
4750
|
+
async getApp(appAddress) {
|
|
4751
|
+
const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
|
|
4752
|
+
const res = await this.makeAuthenticatedRequest(endpoint);
|
|
4753
|
+
const raw = await res.json();
|
|
4754
|
+
if (!isJsonObject(raw)) {
|
|
4755
|
+
throw new Error("Unexpected /apps/:id response: expected object");
|
|
5261
4756
|
}
|
|
5262
|
-
|
|
5263
|
-
|
|
4757
|
+
const id = readString(raw, "id");
|
|
4758
|
+
if (!id) {
|
|
4759
|
+
throw new Error("Unexpected /apps/:id response: missing 'id'");
|
|
5264
4760
|
}
|
|
5265
|
-
|
|
5266
|
-
|
|
4761
|
+
const releasesRaw = raw.releases;
|
|
4762
|
+
const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
|
|
4763
|
+
return {
|
|
4764
|
+
id,
|
|
4765
|
+
creator: readString(raw, "creator"),
|
|
4766
|
+
contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
|
|
4767
|
+
releases
|
|
4768
|
+
};
|
|
5267
4769
|
}
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
4770
|
+
/**
|
|
4771
|
+
* Get available SKUs (instance types) from UserAPI
|
|
4772
|
+
*/
|
|
4773
|
+
async getSKUs() {
|
|
4774
|
+
const endpoint = `${this.config.userApiServerURL}/skus`;
|
|
4775
|
+
const response = await this.makeAuthenticatedRequest(endpoint);
|
|
4776
|
+
const result = await response.json();
|
|
4777
|
+
return {
|
|
4778
|
+
skus: result.skus || result.SKUs || []
|
|
4779
|
+
};
|
|
5274
4780
|
}
|
|
5275
|
-
|
|
5276
|
-
|
|
4781
|
+
/**
|
|
4782
|
+
* Get logs for an app
|
|
4783
|
+
*/
|
|
4784
|
+
async getLogs(appID) {
|
|
4785
|
+
const endpoint = `${this.config.userApiServerURL}/logs/${appID}`;
|
|
4786
|
+
const response = await this.makeAuthenticatedRequest(endpoint, CanViewAppLogsPermission);
|
|
4787
|
+
return await response.text();
|
|
5277
4788
|
}
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
4789
|
+
/**
|
|
4790
|
+
* Get statuses for apps
|
|
4791
|
+
*/
|
|
4792
|
+
async getStatuses(appIDs) {
|
|
4793
|
+
const endpoint = `${this.config.userApiServerURL}/status`;
|
|
4794
|
+
const url = `${endpoint}?${new URLSearchParams({ apps: appIDs.join(",") })}`;
|
|
4795
|
+
const response = await this.makeAuthenticatedRequest(url);
|
|
4796
|
+
const result = await response.json();
|
|
4797
|
+
const apps = result.apps || result.Apps || [];
|
|
4798
|
+
return apps.map((app, i) => ({
|
|
4799
|
+
address: app.address || appIDs[i],
|
|
4800
|
+
status: app.app_status || app.App_Status || ""
|
|
4801
|
+
}));
|
|
5286
4802
|
}
|
|
5287
|
-
|
|
5288
|
-
|
|
4803
|
+
/**
|
|
4804
|
+
* Upload app profile information with optional image
|
|
4805
|
+
*
|
|
4806
|
+
* @param appAddress - The app's contract address
|
|
4807
|
+
* @param name - Display name for the app
|
|
4808
|
+
* @param options - Optional fields including website, description, xURL, and image
|
|
4809
|
+
* @param options.image - Image file as Blob or File (browser: from input element, Node.js: new Blob([buffer]))
|
|
4810
|
+
* @param options.imageName - Filename for the image (required if image is provided)
|
|
4811
|
+
*/
|
|
4812
|
+
async uploadAppProfile(appAddress, name, options) {
|
|
4813
|
+
const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}/profile`;
|
|
4814
|
+
const formData = new FormData();
|
|
4815
|
+
formData.append("name", name);
|
|
4816
|
+
if (options?.website) {
|
|
4817
|
+
formData.append("website", options.website);
|
|
4818
|
+
}
|
|
4819
|
+
if (options?.description) {
|
|
4820
|
+
formData.append("description", options.description);
|
|
4821
|
+
}
|
|
4822
|
+
if (options?.xURL) {
|
|
4823
|
+
formData.append("xURL", options.xURL);
|
|
4824
|
+
}
|
|
4825
|
+
if (options?.image) {
|
|
4826
|
+
const fileName = options.image instanceof File ? options.image.name : options.imageName || "image";
|
|
4827
|
+
formData.append("image", options.image, fileName);
|
|
4828
|
+
}
|
|
4829
|
+
const headers = {
|
|
4830
|
+
"x-client-id": this.clientId
|
|
4831
|
+
};
|
|
4832
|
+
if (!this.useSession) {
|
|
4833
|
+
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
4834
|
+
const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
|
|
4835
|
+
Object.assign(headers, authHeaders);
|
|
4836
|
+
}
|
|
4837
|
+
try {
|
|
4838
|
+
const response = await import_axios.default.post(endpoint, formData, {
|
|
4839
|
+
headers,
|
|
4840
|
+
maxRedirects: 0,
|
|
4841
|
+
validateStatus: () => true,
|
|
4842
|
+
// Don't throw on any status
|
|
4843
|
+
maxContentLength: Infinity,
|
|
4844
|
+
// Allow large file uploads
|
|
4845
|
+
maxBodyLength: Infinity,
|
|
4846
|
+
// Allow large file uploads
|
|
4847
|
+
withCredentials: true
|
|
4848
|
+
// Include cookies for session auth
|
|
4849
|
+
});
|
|
4850
|
+
const status = response.status;
|
|
4851
|
+
if (status !== 200 && status !== 201) {
|
|
4852
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
4853
|
+
if (status === 403 && body.includes("Cloudflare") && body.includes("challenge-platform")) {
|
|
4854
|
+
throw new Error(
|
|
4855
|
+
`Cloudflare protection is blocking the request. This is likely due to bot detection.
|
|
4856
|
+
Status: ${status}`
|
|
4857
|
+
);
|
|
4858
|
+
}
|
|
4859
|
+
throw new Error(
|
|
4860
|
+
`UserAPI request failed: ${status} ${status >= 200 && status < 300 ? "OK" : "Error"} - ${body.substring(0, 500)}${body.length > 500 ? "..." : ""}`
|
|
4861
|
+
);
|
|
4862
|
+
}
|
|
4863
|
+
return response.data;
|
|
4864
|
+
} catch (error) {
|
|
4865
|
+
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
4866
|
+
const cause = error.cause?.message || error.cause || error.message;
|
|
4867
|
+
throw new Error(
|
|
4868
|
+
`Failed to connect to UserAPI at ${endpoint}: ${cause}
|
|
4869
|
+
Please check:
|
|
4870
|
+
1. Your internet connection
|
|
4871
|
+
2. The API server is accessible: ${this.config.userApiServerURL}
|
|
4872
|
+
3. Firewall/proxy settings`
|
|
4873
|
+
);
|
|
4874
|
+
}
|
|
4875
|
+
throw error;
|
|
4876
|
+
}
|
|
5289
4877
|
}
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
4878
|
+
async makeAuthenticatedRequest(url, permission) {
|
|
4879
|
+
const headers = {
|
|
4880
|
+
"x-client-id": this.clientId
|
|
4881
|
+
};
|
|
4882
|
+
if (permission && !this.useSession) {
|
|
4883
|
+
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
4884
|
+
const authHeaders = await this.generateAuthHeaders(permission, expiry);
|
|
4885
|
+
Object.assign(headers, authHeaders);
|
|
4886
|
+
}
|
|
4887
|
+
try {
|
|
4888
|
+
const response = await import_axios.default.get(url, {
|
|
4889
|
+
headers,
|
|
4890
|
+
maxRedirects: 0,
|
|
4891
|
+
validateStatus: () => true,
|
|
4892
|
+
// Don't throw on any status
|
|
4893
|
+
withCredentials: true
|
|
4894
|
+
// Include cookies for session auth
|
|
4895
|
+
});
|
|
4896
|
+
const status = response.status;
|
|
4897
|
+
const statusText = status >= 200 && status < 300 ? "OK" : "Error";
|
|
4898
|
+
if (status < 200 || status >= 300) {
|
|
4899
|
+
const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
4900
|
+
throw new Error(`UserAPI request failed: ${status} ${statusText} - ${body}`);
|
|
4901
|
+
}
|
|
4902
|
+
return {
|
|
4903
|
+
json: async () => response.data,
|
|
4904
|
+
text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
|
|
4905
|
+
};
|
|
4906
|
+
} catch (error) {
|
|
4907
|
+
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
4908
|
+
const cause = error.cause?.message || error.cause || error.message;
|
|
4909
|
+
throw new Error(
|
|
4910
|
+
`Failed to connect to UserAPI at ${url}: ${cause}
|
|
4911
|
+
Please check:
|
|
4912
|
+
1. Your internet connection
|
|
4913
|
+
2. The API server is accessible: ${this.config.userApiServerURL}
|
|
4914
|
+
3. Firewall/proxy settings`
|
|
4915
|
+
);
|
|
4916
|
+
}
|
|
4917
|
+
throw error;
|
|
4918
|
+
}
|
|
5293
4919
|
}
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
4920
|
+
/**
|
|
4921
|
+
* Generate authentication headers for UserAPI requests
|
|
4922
|
+
*/
|
|
4923
|
+
async generateAuthHeaders(permission, expiry) {
|
|
4924
|
+
const { signature } = await calculatePermissionSignature({
|
|
4925
|
+
permission,
|
|
4926
|
+
expiry,
|
|
4927
|
+
appControllerAddress: this.config.appControllerAddress,
|
|
4928
|
+
publicClient: this.publicClient,
|
|
4929
|
+
walletClient: this.walletClient
|
|
4930
|
+
});
|
|
4931
|
+
return {
|
|
4932
|
+
Authorization: `Bearer ${stripHexPrefix(signature)}`,
|
|
4933
|
+
"X-eigenx-expiry": expiry.toString()
|
|
4934
|
+
};
|
|
5297
4935
|
}
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
4936
|
+
// ==========================================================================
|
|
4937
|
+
// SIWE Session Management
|
|
4938
|
+
// ==========================================================================
|
|
4939
|
+
/**
|
|
4940
|
+
* Login to the compute API using SIWE (Sign-In with Ethereum)
|
|
4941
|
+
*
|
|
4942
|
+
* This establishes a session with the compute API by verifying the SIWE message
|
|
4943
|
+
* and signature. On success, a session cookie is set in the browser.
|
|
4944
|
+
*
|
|
4945
|
+
* @param request - Login request containing SIWE message and signature
|
|
4946
|
+
* @returns Login result with the authenticated address
|
|
4947
|
+
*
|
|
4948
|
+
* @example
|
|
4949
|
+
* ```typescript
|
|
4950
|
+
* import { createSiweMessage } from "@layr-labs/ecloud-sdk/browser";
|
|
4951
|
+
*
|
|
4952
|
+
* const { message } = createSiweMessage({
|
|
4953
|
+
* address: userAddress,
|
|
4954
|
+
* chainId: 11155111,
|
|
4955
|
+
* domain: window.location.host,
|
|
4956
|
+
* uri: window.location.origin,
|
|
4957
|
+
* });
|
|
4958
|
+
*
|
|
4959
|
+
* const signature = await signMessageAsync({ message });
|
|
4960
|
+
* const result = await client.siweLogin({ message, signature });
|
|
4961
|
+
* ```
|
|
4962
|
+
*/
|
|
4963
|
+
async siweLogin(request) {
|
|
4964
|
+
return loginToComputeApi({ baseUrl: this.config.userApiServerURL }, request);
|
|
5301
4965
|
}
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
4966
|
+
/**
|
|
4967
|
+
* Logout from the compute API
|
|
4968
|
+
*
|
|
4969
|
+
* This destroys the current session and clears the session cookie.
|
|
4970
|
+
*
|
|
4971
|
+
* @example
|
|
4972
|
+
* ```typescript
|
|
4973
|
+
* await client.siweLogout();
|
|
4974
|
+
* ```
|
|
4975
|
+
*/
|
|
4976
|
+
async siweLogout() {
|
|
4977
|
+
return logoutFromComputeApi({ baseUrl: this.config.userApiServerURL });
|
|
5307
4978
|
}
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
4979
|
+
/**
|
|
4980
|
+
* Get the current SIWE session status from the compute API
|
|
4981
|
+
*
|
|
4982
|
+
* @returns Session information including authentication status and address
|
|
4983
|
+
*
|
|
4984
|
+
* @example
|
|
4985
|
+
* ```typescript
|
|
4986
|
+
* const session = await client.getSiweSession();
|
|
4987
|
+
* if (session.authenticated) {
|
|
4988
|
+
* console.log(`Logged in as ${session.address}`);
|
|
4989
|
+
* }
|
|
4990
|
+
* ```
|
|
4991
|
+
*/
|
|
4992
|
+
async getSiweSession() {
|
|
4993
|
+
return getComputeApiSession({ baseUrl: this.config.userApiServerURL });
|
|
5311
4994
|
}
|
|
5312
|
-
|
|
4995
|
+
};
|
|
4996
|
+
function transformAppReleaseBuild(raw) {
|
|
4997
|
+
if (!isJsonObject(raw)) return void 0;
|
|
4998
|
+
const depsRaw = raw.dependencies;
|
|
4999
|
+
const deps = isJsonObject(depsRaw) ? Object.fromEntries(
|
|
5000
|
+
Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
|
|
5001
|
+
const parsed = transformAppReleaseBuild(depRaw);
|
|
5002
|
+
return parsed ? [[digest, parsed]] : [];
|
|
5003
|
+
})
|
|
5004
|
+
) : void 0;
|
|
5005
|
+
return {
|
|
5006
|
+
buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
|
|
5007
|
+
billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
|
|
5008
|
+
repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
|
|
5009
|
+
gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
|
|
5010
|
+
status: readString(raw, "status"),
|
|
5011
|
+
buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
|
|
5012
|
+
imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
|
|
5013
|
+
imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
|
|
5014
|
+
imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
|
|
5015
|
+
provenanceJson: raw.provenance_json ?? raw.provenanceJson,
|
|
5016
|
+
provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
|
|
5017
|
+
createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
|
|
5018
|
+
updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
|
|
5019
|
+
errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
|
|
5020
|
+
dependencies: deps
|
|
5021
|
+
};
|
|
5313
5022
|
}
|
|
5314
|
-
function
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5023
|
+
function transformAppRelease(raw) {
|
|
5024
|
+
if (!isJsonObject(raw)) return void 0;
|
|
5025
|
+
return {
|
|
5026
|
+
appId: readString(raw, "appId") ?? readString(raw, "app_id"),
|
|
5027
|
+
rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
|
|
5028
|
+
imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
|
|
5029
|
+
registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
|
|
5030
|
+
publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
|
|
5031
|
+
encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
|
|
5032
|
+
upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
|
|
5033
|
+
createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
|
|
5034
|
+
createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
|
|
5035
|
+
build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
|
|
5036
|
+
};
|
|
5327
5037
|
}
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5038
|
+
|
|
5039
|
+
// src/client/common/contract/watcher.ts
|
|
5040
|
+
var WATCH_POLL_INTERVAL_SECONDS = 5;
|
|
5041
|
+
var APP_STATUS_RUNNING = "Running";
|
|
5042
|
+
var APP_STATUS_FAILED = "Failed";
|
|
5043
|
+
async function watchUntilRunning(options, logger) {
|
|
5044
|
+
const { walletClient, publicClient, environmentConfig, appId } = options;
|
|
5045
|
+
const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
|
|
5046
|
+
let initialStatus;
|
|
5047
|
+
let initialIP;
|
|
5048
|
+
let hasChanged = false;
|
|
5049
|
+
const stopCondition = (status, ip) => {
|
|
5050
|
+
if (!initialStatus) {
|
|
5051
|
+
initialStatus = status;
|
|
5052
|
+
initialIP = ip;
|
|
5053
|
+
}
|
|
5054
|
+
if (status !== initialStatus) {
|
|
5055
|
+
hasChanged = true;
|
|
5056
|
+
}
|
|
5057
|
+
if (status === APP_STATUS_RUNNING && ip) {
|
|
5058
|
+
if (hasChanged || initialStatus !== APP_STATUS_RUNNING) {
|
|
5059
|
+
if (!initialIP || initialIP === "No IP assigned") {
|
|
5060
|
+
logger.info(`App is now running with IP: ${ip}`);
|
|
5061
|
+
} else {
|
|
5062
|
+
logger.info("App is now running");
|
|
5063
|
+
}
|
|
5064
|
+
return true;
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
if (status === APP_STATUS_FAILED) {
|
|
5068
|
+
throw new Error(`App entered ${status} state`);
|
|
5069
|
+
}
|
|
5070
|
+
return false;
|
|
5071
|
+
};
|
|
5072
|
+
while (true) {
|
|
5073
|
+
try {
|
|
5074
|
+
const info = await userApiClient.getInfos([appId], 1);
|
|
5075
|
+
if (info.length === 0) {
|
|
5076
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5077
|
+
continue;
|
|
5078
|
+
}
|
|
5079
|
+
const appInfo = info[0];
|
|
5080
|
+
const currentStatus = appInfo.status;
|
|
5081
|
+
const currentIP = appInfo.ip || "";
|
|
5082
|
+
if (stopCondition(currentStatus, currentIP)) {
|
|
5083
|
+
return currentIP || void 0;
|
|
5084
|
+
}
|
|
5085
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5086
|
+
} catch (error) {
|
|
5087
|
+
logger.warn(`Failed to fetch app info: ${error.message}`);
|
|
5088
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5089
|
+
}
|
|
5341
5090
|
}
|
|
5342
5091
|
}
|
|
5343
|
-
|
|
5344
|
-
|
|
5092
|
+
var APP_STATUS_STOPPED = "Stopped";
|
|
5093
|
+
async function watchUntilUpgradeComplete(options, logger) {
|
|
5094
|
+
const { walletClient, publicClient, environmentConfig, appId } = options;
|
|
5095
|
+
const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
|
|
5096
|
+
let initialStatus;
|
|
5097
|
+
let initialIP;
|
|
5098
|
+
let hasChanged = false;
|
|
5099
|
+
const stopCondition = (status, ip) => {
|
|
5100
|
+
if (!initialStatus) {
|
|
5101
|
+
initialStatus = status;
|
|
5102
|
+
initialIP = ip;
|
|
5103
|
+
if (status === APP_STATUS_STOPPED && ip) {
|
|
5104
|
+
logger.info("App upgrade complete.");
|
|
5105
|
+
logger.info(`Status: ${status}`);
|
|
5106
|
+
logger.info(`To start the app, run: ecloud compute app start ${appId}`);
|
|
5107
|
+
return true;
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
5110
|
+
if (status !== initialStatus) {
|
|
5111
|
+
hasChanged = true;
|
|
5112
|
+
}
|
|
5113
|
+
if (status === APP_STATUS_STOPPED && ip && hasChanged) {
|
|
5114
|
+
logger.info("App upgrade complete.");
|
|
5115
|
+
logger.info(`Status: ${status}`);
|
|
5116
|
+
logger.info(`To start the app, run: ecloud compute app start ${appId}`);
|
|
5117
|
+
return true;
|
|
5118
|
+
}
|
|
5119
|
+
if (status === APP_STATUS_RUNNING && ip && hasChanged) {
|
|
5120
|
+
if (!initialIP || initialIP === "No IP assigned") {
|
|
5121
|
+
logger.info(`App is now running with IP: ${ip}`);
|
|
5122
|
+
} else {
|
|
5123
|
+
logger.info("App is now running");
|
|
5124
|
+
}
|
|
5125
|
+
return true;
|
|
5126
|
+
}
|
|
5127
|
+
if (status === APP_STATUS_FAILED) {
|
|
5128
|
+
throw new Error(`App entered ${status} state`);
|
|
5129
|
+
}
|
|
5130
|
+
return false;
|
|
5131
|
+
};
|
|
5132
|
+
while (true) {
|
|
5133
|
+
try {
|
|
5134
|
+
const info = await userApiClient.getInfos([appId], 1);
|
|
5135
|
+
if (info.length === 0) {
|
|
5136
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5137
|
+
continue;
|
|
5138
|
+
}
|
|
5139
|
+
const appInfo = info[0];
|
|
5140
|
+
const currentStatus = appInfo.status;
|
|
5141
|
+
const currentIP = appInfo.ip || "";
|
|
5142
|
+
if (stopCondition(currentStatus, currentIP)) {
|
|
5143
|
+
return;
|
|
5144
|
+
}
|
|
5145
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5146
|
+
} catch (error) {
|
|
5147
|
+
logger.warn(`Failed to fetch app info: ${error.message}`);
|
|
5148
|
+
await sleep(WATCH_POLL_INTERVAL_SECONDS * 1e3);
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5345
5151
|
}
|
|
5346
|
-
function
|
|
5347
|
-
return
|
|
5152
|
+
function sleep(ms) {
|
|
5153
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
5348
5154
|
}
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5155
|
+
|
|
5156
|
+
// src/client/common/utils/validation.ts
|
|
5157
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
5158
|
+
var import_path = __toESM(require("path"), 1);
|
|
5159
|
+
var import_viem5 = require("viem");
|
|
5160
|
+
function validateAppName(name) {
|
|
5161
|
+
if (!name) {
|
|
5162
|
+
throw new Error("App name cannot be empty");
|
|
5353
5163
|
}
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5164
|
+
if (name.includes(" ")) {
|
|
5165
|
+
throw new Error("App name cannot contain spaces");
|
|
5166
|
+
}
|
|
5167
|
+
if (name.length > 50) {
|
|
5168
|
+
throw new Error("App name cannot be longer than 50 characters");
|
|
5357
5169
|
}
|
|
5358
|
-
return rawURL;
|
|
5359
5170
|
}
|
|
5360
|
-
function
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
const username = rawURL.startsWith("@") ? rawURL.slice(1) : rawURL;
|
|
5364
|
-
rawURL = `https://x.com/${username}`;
|
|
5365
|
-
} else if (!hasScheme(rawURL)) {
|
|
5366
|
-
rawURL = "https://" + rawURL;
|
|
5171
|
+
function validateImageReference(value) {
|
|
5172
|
+
if (!value) {
|
|
5173
|
+
return "Image reference cannot be empty";
|
|
5367
5174
|
}
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5175
|
+
if (!value.includes("/")) {
|
|
5176
|
+
return "Image reference must contain at least one /";
|
|
5177
|
+
}
|
|
5178
|
+
return true;
|
|
5179
|
+
}
|
|
5180
|
+
function assertValidImageReference(value) {
|
|
5181
|
+
const result = validateImageReference(value);
|
|
5182
|
+
if (result !== true) {
|
|
5183
|
+
throw new Error(result);
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
function extractAppNameFromImage(imageRef) {
|
|
5187
|
+
const parts = imageRef.split("/");
|
|
5188
|
+
let imageName = parts.length > 1 ? parts[parts.length - 1] : imageRef;
|
|
5189
|
+
if (imageName.includes(":")) {
|
|
5190
|
+
imageName = imageName.split(":")[0];
|
|
5191
|
+
}
|
|
5192
|
+
return imageName;
|
|
5193
|
+
}
|
|
5194
|
+
function validateFilePath(value) {
|
|
5195
|
+
if (!value) {
|
|
5196
|
+
return "File path cannot be empty";
|
|
5197
|
+
}
|
|
5198
|
+
if (!import_fs.default.existsSync(value)) {
|
|
5199
|
+
return "File does not exist";
|
|
5200
|
+
}
|
|
5201
|
+
return true;
|
|
5202
|
+
}
|
|
5203
|
+
function assertValidFilePath(value) {
|
|
5204
|
+
const result = validateFilePath(value);
|
|
5205
|
+
if (result !== true) {
|
|
5206
|
+
throw new Error(result);
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
function validateInstanceTypeSKU(sku, availableTypes) {
|
|
5210
|
+
if (!sku) {
|
|
5211
|
+
throw new Error("Instance type SKU cannot be empty");
|
|
5212
|
+
}
|
|
5213
|
+
for (const it of availableTypes) {
|
|
5214
|
+
if (it.sku === sku) {
|
|
5215
|
+
return sku;
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
const validSKUs = availableTypes.map((it) => it.sku).join(", ");
|
|
5219
|
+
throw new Error(`Invalid instance-type value: ${sku} (must be one of: ${validSKUs})`);
|
|
5220
|
+
}
|
|
5221
|
+
function validatePrivateKeyFormat(key) {
|
|
5222
|
+
const keyWithoutPrefix = stripHexPrefix(key);
|
|
5223
|
+
if (!/^[0-9a-fA-F]{64}$/.test(keyWithoutPrefix)) {
|
|
5224
|
+
return false;
|
|
5225
|
+
}
|
|
5226
|
+
return true;
|
|
5227
|
+
}
|
|
5228
|
+
function assertValidPrivateKey(key) {
|
|
5229
|
+
if (!key) {
|
|
5230
|
+
throw new Error("Private key is required");
|
|
5231
|
+
}
|
|
5232
|
+
if (!validatePrivateKeyFormat(key)) {
|
|
5233
|
+
throw new Error(
|
|
5234
|
+
"Invalid private key format (must be 64 hex characters, optionally prefixed with 0x)"
|
|
5235
|
+
);
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
function validateURL(rawURL) {
|
|
5239
|
+
if (!rawURL.trim()) {
|
|
5240
|
+
return "URL cannot be empty";
|
|
5241
|
+
}
|
|
5242
|
+
try {
|
|
5243
|
+
const url = new URL(rawURL);
|
|
5244
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
5245
|
+
return "URL scheme must be http or https";
|
|
5246
|
+
}
|
|
5247
|
+
} catch {
|
|
5248
|
+
return "Invalid URL format";
|
|
5249
|
+
}
|
|
5250
|
+
return void 0;
|
|
5251
|
+
}
|
|
5252
|
+
var VALID_X_HOSTS = ["twitter.com", "www.twitter.com", "x.com", "www.x.com"];
|
|
5253
|
+
function validateXURL(rawURL) {
|
|
5254
|
+
const urlErr = validateURL(rawURL);
|
|
5255
|
+
if (urlErr) {
|
|
5256
|
+
return urlErr;
|
|
5257
|
+
}
|
|
5258
|
+
try {
|
|
5259
|
+
const url = new URL(rawURL);
|
|
5260
|
+
const host = url.hostname.toLowerCase();
|
|
5261
|
+
if (!VALID_X_HOSTS.includes(host)) {
|
|
5262
|
+
return "URL must be a valid X/Twitter URL (x.com or twitter.com)";
|
|
5263
|
+
}
|
|
5264
|
+
if (!url.pathname || url.pathname === "/") {
|
|
5265
|
+
return "X URL must include a username or profile path";
|
|
5266
|
+
}
|
|
5267
|
+
} catch {
|
|
5268
|
+
return "Invalid X URL format";
|
|
5269
|
+
}
|
|
5270
|
+
return void 0;
|
|
5271
|
+
}
|
|
5272
|
+
var MAX_DESCRIPTION_LENGTH = 1e3;
|
|
5273
|
+
function validateDescription(description) {
|
|
5274
|
+
if (!description.trim()) {
|
|
5275
|
+
return "Description cannot be empty";
|
|
5276
|
+
}
|
|
5277
|
+
if (description.length > MAX_DESCRIPTION_LENGTH) {
|
|
5278
|
+
return `Description cannot exceed ${MAX_DESCRIPTION_LENGTH} characters`;
|
|
5279
|
+
}
|
|
5280
|
+
return void 0;
|
|
5281
|
+
}
|
|
5282
|
+
var MAX_IMAGE_SIZE = 4 * 1024 * 1024;
|
|
5283
|
+
var VALID_IMAGE_EXTENSIONS = [".jpg", ".jpeg", ".png"];
|
|
5284
|
+
function validateImagePath(filePath) {
|
|
5285
|
+
const cleanedPath = filePath.trim().replace(/^["']|["']$/g, "");
|
|
5286
|
+
if (!cleanedPath) {
|
|
5287
|
+
return "Image path cannot be empty";
|
|
5288
|
+
}
|
|
5289
|
+
if (!import_fs.default.existsSync(cleanedPath)) {
|
|
5290
|
+
return `Image file not found: ${cleanedPath}`;
|
|
5291
|
+
}
|
|
5292
|
+
const stats = import_fs.default.statSync(cleanedPath);
|
|
5293
|
+
if (stats.isDirectory()) {
|
|
5294
|
+
return "Path is a directory, not a file";
|
|
5295
|
+
}
|
|
5296
|
+
if (stats.size > MAX_IMAGE_SIZE) {
|
|
5297
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
5298
|
+
return `Image file size (${sizeMB} MB) exceeds maximum allowed size of 4 MB`;
|
|
5299
|
+
}
|
|
5300
|
+
const ext = import_path.default.extname(cleanedPath).toLowerCase();
|
|
5301
|
+
if (!VALID_IMAGE_EXTENSIONS.includes(ext)) {
|
|
5302
|
+
return "Image must be JPG or PNG format";
|
|
5303
|
+
}
|
|
5304
|
+
return void 0;
|
|
5305
|
+
}
|
|
5306
|
+
function validateAppID(appID) {
|
|
5307
|
+
if (!appID) {
|
|
5308
|
+
throw new Error("App ID is required");
|
|
5309
|
+
}
|
|
5310
|
+
const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
|
|
5311
|
+
if ((0, import_viem5.isAddress)(normalized)) {
|
|
5312
|
+
return normalized;
|
|
5313
|
+
}
|
|
5314
|
+
throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
|
|
5315
|
+
}
|
|
5316
|
+
function validateLogVisibility(logVisibility) {
|
|
5317
|
+
switch (logVisibility) {
|
|
5318
|
+
case "public":
|
|
5319
|
+
return { logRedirect: "always", publicLogs: true };
|
|
5320
|
+
case "private":
|
|
5321
|
+
return { logRedirect: "always", publicLogs: false };
|
|
5322
|
+
case "off":
|
|
5323
|
+
return { logRedirect: "", publicLogs: false };
|
|
5324
|
+
default:
|
|
5325
|
+
throw new Error(
|
|
5326
|
+
`Invalid log-visibility value: ${logVisibility} (must be public, private, or off)`
|
|
5327
|
+
);
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
5330
|
+
function validateResourceUsageMonitoring(resourceUsageMonitoring) {
|
|
5331
|
+
if (!resourceUsageMonitoring) {
|
|
5332
|
+
return "always";
|
|
5333
|
+
}
|
|
5334
|
+
switch (resourceUsageMonitoring) {
|
|
5335
|
+
case "enable":
|
|
5336
|
+
return "always";
|
|
5337
|
+
case "disable":
|
|
5338
|
+
return "never";
|
|
5339
|
+
default:
|
|
5340
|
+
throw new Error(
|
|
5341
|
+
`Invalid resource-usage-monitoring value: ${resourceUsageMonitoring} (must be enable or disable)`
|
|
5342
|
+
);
|
|
5343
|
+
}
|
|
5344
|
+
}
|
|
5345
|
+
function hasScheme(rawURL) {
|
|
5346
|
+
return rawURL.startsWith("http://") || rawURL.startsWith("https://");
|
|
5347
|
+
}
|
|
5348
|
+
function sanitizeString(s) {
|
|
5349
|
+
return s.trim().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5350
|
+
}
|
|
5351
|
+
function sanitizeURL(rawURL) {
|
|
5352
|
+
rawURL = rawURL.trim();
|
|
5353
|
+
if (!hasScheme(rawURL)) {
|
|
5354
|
+
rawURL = "https://" + rawURL;
|
|
5355
|
+
}
|
|
5356
|
+
const err = validateURL(rawURL);
|
|
5357
|
+
if (err) {
|
|
5358
|
+
throw new Error(err);
|
|
5359
|
+
}
|
|
5360
|
+
return rawURL;
|
|
5361
|
+
}
|
|
5362
|
+
function sanitizeXURL(rawURL) {
|
|
5363
|
+
rawURL = rawURL.trim();
|
|
5364
|
+
if (!rawURL.includes("://") && !rawURL.includes(".")) {
|
|
5365
|
+
const username = rawURL.startsWith("@") ? rawURL.slice(1) : rawURL;
|
|
5366
|
+
rawURL = `https://x.com/${username}`;
|
|
5367
|
+
} else if (!hasScheme(rawURL)) {
|
|
5368
|
+
rawURL = "https://" + rawURL;
|
|
5369
|
+
}
|
|
5370
|
+
rawURL = rawURL.replace(/twitter\.com/g, "x.com");
|
|
5371
|
+
rawURL = rawURL.replace(/www\.x\.com/g, "x.com");
|
|
5372
|
+
const err = validateXURL(rawURL);
|
|
5371
5373
|
if (err) {
|
|
5372
5374
|
throw new Error(err);
|
|
5373
5375
|
}
|
|
@@ -5447,84 +5449,268 @@ function validateLogsParams(params) {
|
|
|
5447
5449
|
validateAppID(params.appID);
|
|
5448
5450
|
}
|
|
5449
5451
|
|
|
5450
|
-
// src/client/common/
|
|
5451
|
-
var
|
|
5452
|
-
var
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
if (!rpcUrl) {
|
|
5463
|
-
throw new Error(
|
|
5464
|
-
`RPC URL is required. Provide via options.rpcUrl, RPC_URL env var, or ensure environment has default RPC URL`
|
|
5465
|
-
);
|
|
5466
|
-
}
|
|
5467
|
-
logger.debug("Testing network connectivity...");
|
|
5468
|
-
const publicClient = (0, import_viem7.createPublicClient)({
|
|
5469
|
-
transport: (0, import_viem7.http)(rpcUrl)
|
|
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}`);
|
|
5475
|
-
}
|
|
5476
|
-
} catch (err) {
|
|
5477
|
-
throw new Error(`Cannot connect to ${environmentConfig.name} RPC at ${rpcUrl}: ${err.message}`);
|
|
5478
|
-
}
|
|
5479
|
-
const privateKeyHex = addHexPrefix(privateKey);
|
|
5480
|
-
const account = (0, import_accounts4.privateKeyToAccount)(privateKeyHex);
|
|
5481
|
-
const selfAddress = account.address;
|
|
5482
|
-
return {
|
|
5483
|
-
privateKey: privateKeyHex,
|
|
5484
|
-
rpcUrl,
|
|
5485
|
-
environmentConfig,
|
|
5486
|
-
account,
|
|
5487
|
-
selfAddress
|
|
5488
|
-
};
|
|
5489
|
-
}
|
|
5490
|
-
async function getPrivateKeyOrFail(privateKey) {
|
|
5491
|
-
if (privateKey) {
|
|
5492
|
-
validatePrivateKey(privateKey);
|
|
5493
|
-
return privateKey;
|
|
5494
|
-
}
|
|
5495
|
-
if (process.env.PRIVATE_KEY) {
|
|
5496
|
-
validatePrivateKey(process.env.PRIVATE_KEY);
|
|
5497
|
-
return process.env.PRIVATE_KEY;
|
|
5498
|
-
}
|
|
5499
|
-
throw new Error(
|
|
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
|
-
);
|
|
5505
|
-
}
|
|
5506
|
-
function validatePrivateKey(key) {
|
|
5507
|
-
const cleaned = stripHexPrefix(key);
|
|
5508
|
-
if (!/^[0-9a-fA-F]{64}$/.test(cleaned)) {
|
|
5509
|
-
throw new Error("Invalid private key format (must be 64 hex characters)");
|
|
5510
|
-
}
|
|
5511
|
-
}
|
|
5512
|
-
|
|
5513
|
-
// src/client/common/telemetry/noop.ts
|
|
5514
|
-
var NoopClient = class {
|
|
5515
|
-
/**
|
|
5516
|
-
* AddMetric implements the TelemetryClient interface
|
|
5517
|
-
*/
|
|
5518
|
-
async addMetric(_metric) {
|
|
5519
|
-
}
|
|
5520
|
-
/**
|
|
5521
|
-
* Close implements the TelemetryClient interface
|
|
5522
|
-
*/
|
|
5523
|
-
async close() {
|
|
5452
|
+
// src/client/common/config/environment.ts
|
|
5453
|
+
var SEPOLIA_CHAIN_ID = 11155111;
|
|
5454
|
+
var MAINNET_CHAIN_ID = 1;
|
|
5455
|
+
var CommonAddresses = {
|
|
5456
|
+
ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
|
|
5457
|
+
};
|
|
5458
|
+
var ChainAddresses = {
|
|
5459
|
+
[MAINNET_CHAIN_ID]: {
|
|
5460
|
+
PermissionController: "0x25E5F8B1E7aDf44518d35D5B2271f114e081f0E5"
|
|
5461
|
+
},
|
|
5462
|
+
[SEPOLIA_CHAIN_ID]: {
|
|
5463
|
+
PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
|
|
5524
5464
|
}
|
|
5525
5465
|
};
|
|
5526
|
-
|
|
5527
|
-
|
|
5466
|
+
var BILLING_ENVIRONMENTS = {
|
|
5467
|
+
dev: {
|
|
5468
|
+
billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
|
|
5469
|
+
},
|
|
5470
|
+
prod: {
|
|
5471
|
+
billingApiServerURL: "https://billingapi.eigencloud.xyz"
|
|
5472
|
+
}
|
|
5473
|
+
};
|
|
5474
|
+
var ENVIRONMENTS = {
|
|
5475
|
+
"sepolia-dev": {
|
|
5476
|
+
name: "sepolia",
|
|
5477
|
+
build: "dev",
|
|
5478
|
+
appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
|
|
5479
|
+
permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
|
|
5480
|
+
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
5481
|
+
kmsServerURL: "http://10.128.0.57:8080",
|
|
5482
|
+
userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
|
|
5483
|
+
defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
|
|
5484
|
+
},
|
|
5485
|
+
sepolia: {
|
|
5486
|
+
name: "sepolia",
|
|
5487
|
+
build: "prod",
|
|
5488
|
+
appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
|
|
5489
|
+
permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
|
|
5490
|
+
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
5491
|
+
kmsServerURL: "http://10.128.15.203:8080",
|
|
5492
|
+
userApiServerURL: "https://userapi-compute-sepolia-prod.eigencloud.xyz",
|
|
5493
|
+
defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com"
|
|
5494
|
+
},
|
|
5495
|
+
"mainnet-alpha": {
|
|
5496
|
+
name: "mainnet-alpha",
|
|
5497
|
+
build: "prod",
|
|
5498
|
+
appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
|
|
5499
|
+
permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
|
|
5500
|
+
erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
|
|
5501
|
+
kmsServerURL: "http://10.128.0.2:8080",
|
|
5502
|
+
userApiServerURL: "https://userapi-compute.eigencloud.xyz",
|
|
5503
|
+
defaultRPCURL: "https://ethereum-rpc.publicnode.com"
|
|
5504
|
+
}
|
|
5505
|
+
};
|
|
5506
|
+
var CHAIN_ID_TO_ENVIRONMENT = {
|
|
5507
|
+
[SEPOLIA_CHAIN_ID.toString()]: "sepolia",
|
|
5508
|
+
[MAINNET_CHAIN_ID.toString()]: "mainnet-alpha"
|
|
5509
|
+
};
|
|
5510
|
+
function getEnvironmentConfig(environment, chainID) {
|
|
5511
|
+
const env = ENVIRONMENTS[environment];
|
|
5512
|
+
if (!env) {
|
|
5513
|
+
throw new Error(`Unknown environment: ${environment}`);
|
|
5514
|
+
}
|
|
5515
|
+
if (!isEnvironmentAvailable(environment)) {
|
|
5516
|
+
throw new Error(
|
|
5517
|
+
`Environment ${environment} is not available in this build type. Available environments: ${getAvailableEnvironments().join(", ")}`
|
|
5518
|
+
);
|
|
5519
|
+
}
|
|
5520
|
+
if (chainID) {
|
|
5521
|
+
const expectedEnv = CHAIN_ID_TO_ENVIRONMENT[chainID.toString()];
|
|
5522
|
+
if (expectedEnv && expectedEnv !== environment) {
|
|
5523
|
+
throw new Error(`Environment ${environment} does not match chain ID ${chainID}`);
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
const resolvedChainID = chainID || (environment === "sepolia" || environment === "sepolia-dev" ? SEPOLIA_CHAIN_ID : MAINNET_CHAIN_ID);
|
|
5527
|
+
return {
|
|
5528
|
+
...env,
|
|
5529
|
+
chainID: BigInt(resolvedChainID)
|
|
5530
|
+
};
|
|
5531
|
+
}
|
|
5532
|
+
function getBillingEnvironmentConfig(build) {
|
|
5533
|
+
const config = BILLING_ENVIRONMENTS[build];
|
|
5534
|
+
if (!config) {
|
|
5535
|
+
throw new Error(`Unknown billing environment: ${build}`);
|
|
5536
|
+
}
|
|
5537
|
+
return config;
|
|
5538
|
+
}
|
|
5539
|
+
function getBuildType() {
|
|
5540
|
+
const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
|
|
5541
|
+
const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
|
|
5542
|
+
const buildType = buildTimeType || runtimeType;
|
|
5543
|
+
if (buildType === "dev") {
|
|
5544
|
+
return "dev";
|
|
5545
|
+
}
|
|
5546
|
+
return "prod";
|
|
5547
|
+
}
|
|
5548
|
+
function getAvailableEnvironments() {
|
|
5549
|
+
const buildType = getBuildType();
|
|
5550
|
+
if (buildType === "dev") {
|
|
5551
|
+
return ["sepolia-dev"];
|
|
5552
|
+
}
|
|
5553
|
+
return ["sepolia", "mainnet-alpha"];
|
|
5554
|
+
}
|
|
5555
|
+
function isEnvironmentAvailable(environment) {
|
|
5556
|
+
return getAvailableEnvironments().includes(environment);
|
|
5557
|
+
}
|
|
5558
|
+
function isMainnet(environmentConfig) {
|
|
5559
|
+
return environmentConfig.chainID === BigInt(MAINNET_CHAIN_ID);
|
|
5560
|
+
}
|
|
5561
|
+
|
|
5562
|
+
// src/client/common/utils/preflight.ts
|
|
5563
|
+
async function doPreflightChecks(options, logger) {
|
|
5564
|
+
const { walletClient, publicClient } = options;
|
|
5565
|
+
logger.debug("Determining environment...");
|
|
5566
|
+
const environmentConfig = getEnvironmentConfig(options.environment || "sepolia");
|
|
5567
|
+
const account = walletClient.account;
|
|
5568
|
+
if (!account) {
|
|
5569
|
+
throw new Error("WalletClient must have an account attached");
|
|
5570
|
+
}
|
|
5571
|
+
logger.debug("Validating chain ID...");
|
|
5572
|
+
try {
|
|
5573
|
+
const chainID = await publicClient.getChainId();
|
|
5574
|
+
if (BigInt(chainID) !== environmentConfig.chainID) {
|
|
5575
|
+
throw new Error(`Chain ID mismatch: expected ${environmentConfig.chainID}, got ${chainID}`);
|
|
5576
|
+
}
|
|
5577
|
+
} catch (err) {
|
|
5578
|
+
throw new Error(
|
|
5579
|
+
`Cannot connect to ${environmentConfig.name} RPC at ${publicClient.transport.url}: ${err.message}`
|
|
5580
|
+
);
|
|
5581
|
+
}
|
|
5582
|
+
return {
|
|
5583
|
+
walletClient,
|
|
5584
|
+
publicClient,
|
|
5585
|
+
environmentConfig,
|
|
5586
|
+
selfAddress: account.address
|
|
5587
|
+
};
|
|
5588
|
+
}
|
|
5589
|
+
|
|
5590
|
+
// src/client/common/utils/logger.ts
|
|
5591
|
+
var defaultLogger = {
|
|
5592
|
+
info: (...args) => console.info(...args),
|
|
5593
|
+
warn: (...args) => console.warn(...args),
|
|
5594
|
+
error: (...args) => console.error(...args),
|
|
5595
|
+
debug: (...args) => console.debug(...args)
|
|
5596
|
+
};
|
|
5597
|
+
var getLogger = (verbose) => ({
|
|
5598
|
+
info: (...args) => console.info(...args),
|
|
5599
|
+
warn: (...args) => console.warn(...args),
|
|
5600
|
+
error: (...args) => console.error(...args),
|
|
5601
|
+
debug: (...args) => verbose && console.debug(...args)
|
|
5602
|
+
});
|
|
5603
|
+
|
|
5604
|
+
// src/client/common/utils/billing.ts
|
|
5605
|
+
function isSubscriptionActive(status) {
|
|
5606
|
+
return status === "active" || status === "trialing";
|
|
5607
|
+
}
|
|
5608
|
+
|
|
5609
|
+
// src/client/common/utils/billingapi.ts
|
|
5610
|
+
var import_axios2 = __toESM(require("axios"), 1);
|
|
5611
|
+
var BillingApiClient = class {
|
|
5612
|
+
constructor(config, walletClient) {
|
|
5613
|
+
this.config = config;
|
|
5614
|
+
this.walletClient = walletClient;
|
|
5615
|
+
}
|
|
5616
|
+
/**
|
|
5617
|
+
* Get the address of the connected wallet
|
|
5618
|
+
*/
|
|
5619
|
+
get address() {
|
|
5620
|
+
const account = this.walletClient.account;
|
|
5621
|
+
if (!account) {
|
|
5622
|
+
throw new Error("WalletClient must have an account attached");
|
|
5623
|
+
}
|
|
5624
|
+
return account.address;
|
|
5625
|
+
}
|
|
5626
|
+
async createSubscription(productId = "compute", options) {
|
|
5627
|
+
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
5628
|
+
const body = options ? {
|
|
5629
|
+
success_url: options.successUrl,
|
|
5630
|
+
cancel_url: options.cancelUrl
|
|
5631
|
+
} : void 0;
|
|
5632
|
+
const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId, body);
|
|
5633
|
+
return resp.json();
|
|
5634
|
+
}
|
|
5635
|
+
async getSubscription(productId = "compute") {
|
|
5636
|
+
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
5637
|
+
const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
|
|
5638
|
+
return resp.json();
|
|
5639
|
+
}
|
|
5640
|
+
async cancelSubscription(productId = "compute") {
|
|
5641
|
+
const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
|
|
5642
|
+
await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
|
|
5643
|
+
}
|
|
5644
|
+
/**
|
|
5645
|
+
* Make an authenticated request to the billing API
|
|
5646
|
+
*/
|
|
5647
|
+
async makeAuthenticatedRequest(url, method, productId, body) {
|
|
5648
|
+
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
|
|
5649
|
+
const { signature } = await calculateBillingAuthSignature({
|
|
5650
|
+
walletClient: this.walletClient,
|
|
5651
|
+
product: productId,
|
|
5652
|
+
expiry
|
|
5653
|
+
});
|
|
5654
|
+
const headers = {
|
|
5655
|
+
Authorization: `Bearer ${signature}`,
|
|
5656
|
+
"X-Account": this.address,
|
|
5657
|
+
"X-Expiry": expiry.toString()
|
|
5658
|
+
};
|
|
5659
|
+
if (body) {
|
|
5660
|
+
headers["Content-Type"] = "application/json";
|
|
5661
|
+
}
|
|
5662
|
+
try {
|
|
5663
|
+
const response = await (0, import_axios2.default)({
|
|
5664
|
+
method,
|
|
5665
|
+
url,
|
|
5666
|
+
headers,
|
|
5667
|
+
data: body,
|
|
5668
|
+
timeout: 3e4,
|
|
5669
|
+
maxRedirects: 0,
|
|
5670
|
+
validateStatus: () => true
|
|
5671
|
+
// Don't throw on any status
|
|
5672
|
+
});
|
|
5673
|
+
const status = response.status;
|
|
5674
|
+
const statusText = status >= 200 && status < 300 ? "OK" : "Error";
|
|
5675
|
+
if (status < 200 || status >= 300) {
|
|
5676
|
+
const body2 = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
|
|
5677
|
+
throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body2}`);
|
|
5678
|
+
}
|
|
5679
|
+
return {
|
|
5680
|
+
json: async () => response.data,
|
|
5681
|
+
text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
|
|
5682
|
+
};
|
|
5683
|
+
} catch (error) {
|
|
5684
|
+
if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
|
|
5685
|
+
const cause = error.cause?.message || error.cause || error.message;
|
|
5686
|
+
throw new Error(
|
|
5687
|
+
`Failed to connect to BillingAPI at ${url}: ${cause}
|
|
5688
|
+
Please check:
|
|
5689
|
+
1. Your internet connection
|
|
5690
|
+
2. The API server is accessible: ${this.config.billingApiServerURL}
|
|
5691
|
+
3. Firewall/proxy settings`
|
|
5692
|
+
);
|
|
5693
|
+
}
|
|
5694
|
+
throw error;
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
};
|
|
5698
|
+
|
|
5699
|
+
// src/client/common/telemetry/noop.ts
|
|
5700
|
+
var NoopClient = class {
|
|
5701
|
+
/**
|
|
5702
|
+
* AddMetric implements the TelemetryClient interface
|
|
5703
|
+
*/
|
|
5704
|
+
async addMetric(_metric) {
|
|
5705
|
+
}
|
|
5706
|
+
/**
|
|
5707
|
+
* Close implements the TelemetryClient interface
|
|
5708
|
+
*/
|
|
5709
|
+
async close() {
|
|
5710
|
+
}
|
|
5711
|
+
};
|
|
5712
|
+
function isNoopClient(client) {
|
|
5713
|
+
return client instanceof NoopClient;
|
|
5528
5714
|
}
|
|
5529
5715
|
|
|
5530
5716
|
// src/client/common/telemetry/posthog.ts
|
|
@@ -5716,7 +5902,9 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5716
5902
|
}
|
|
5717
5903
|
},
|
|
5718
5904
|
async () => {
|
|
5719
|
-
if (!options.
|
|
5905
|
+
if (!options.walletClient.account) {
|
|
5906
|
+
throw new Error("WalletClient must have an account attached");
|
|
5907
|
+
}
|
|
5720
5908
|
if (!options.imageRef) throw new Error("imageRef is required for deployment");
|
|
5721
5909
|
if (!options.imageDigest) throw new Error("imageDigest is required for deployment");
|
|
5722
5910
|
assertValidImageReference(options.imageRef);
|
|
@@ -5732,8 +5920,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5732
5920
|
logger.debug("Performing preflight checks...");
|
|
5733
5921
|
const preflightCtx = await doPreflightChecks(
|
|
5734
5922
|
{
|
|
5735
|
-
|
|
5736
|
-
|
|
5923
|
+
walletClient: options.walletClient,
|
|
5924
|
+
publicClient: options.publicClient,
|
|
5737
5925
|
environment: options.environment
|
|
5738
5926
|
},
|
|
5739
5927
|
logger
|
|
@@ -5743,12 +5931,12 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5743
5931
|
const salt = generateRandomSalt();
|
|
5744
5932
|
logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
|
|
5745
5933
|
logger.debug("Calculating app ID...");
|
|
5746
|
-
const appIDToBeDeployed = await calculateAppID(
|
|
5747
|
-
preflightCtx.
|
|
5748
|
-
|
|
5749
|
-
preflightCtx.
|
|
5934
|
+
const appIDToBeDeployed = await calculateAppID({
|
|
5935
|
+
publicClient: preflightCtx.publicClient,
|
|
5936
|
+
environmentConfig: preflightCtx.environmentConfig,
|
|
5937
|
+
ownerAddress: preflightCtx.selfAddress,
|
|
5750
5938
|
salt
|
|
5751
|
-
);
|
|
5939
|
+
});
|
|
5752
5940
|
logger.info(``);
|
|
5753
5941
|
logger.info(`App ID: ${appIDToBeDeployed}`);
|
|
5754
5942
|
logger.info(``);
|
|
@@ -5766,12 +5954,13 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5766
5954
|
logger.debug("Preparing deploy batch...");
|
|
5767
5955
|
const batch = await prepareDeployBatch(
|
|
5768
5956
|
{
|
|
5769
|
-
|
|
5770
|
-
|
|
5957
|
+
walletClient: preflightCtx.walletClient,
|
|
5958
|
+
publicClient: preflightCtx.publicClient,
|
|
5771
5959
|
environmentConfig: preflightCtx.environmentConfig,
|
|
5772
5960
|
salt,
|
|
5773
5961
|
release,
|
|
5774
|
-
publicLogs
|
|
5962
|
+
publicLogs,
|
|
5963
|
+
imageRef: options.imageRef
|
|
5775
5964
|
},
|
|
5776
5965
|
logger
|
|
5777
5966
|
);
|
|
@@ -5798,8 +5987,8 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
|
|
|
5798
5987
|
);
|
|
5799
5988
|
}
|
|
5800
5989
|
function validateDeployOptions(options) {
|
|
5801
|
-
if (!options.
|
|
5802
|
-
throw new Error("
|
|
5990
|
+
if (!options.walletClient.account) {
|
|
5991
|
+
throw new Error("WalletClient must have an account attached");
|
|
5803
5992
|
}
|
|
5804
5993
|
if (!options.dockerfilePath && !options.imageRef) {
|
|
5805
5994
|
throw new Error("Either dockerfilePath or imageRef is required for deployment");
|
|
@@ -5838,8 +6027,8 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5838
6027
|
logger.debug("Performing preflight checks...");
|
|
5839
6028
|
const preflightCtx = await doPreflightChecks(
|
|
5840
6029
|
{
|
|
5841
|
-
|
|
5842
|
-
|
|
6030
|
+
walletClient: options.walletClient,
|
|
6031
|
+
publicClient: options.publicClient,
|
|
5843
6032
|
environment: options.environment
|
|
5844
6033
|
},
|
|
5845
6034
|
logger
|
|
@@ -5856,12 +6045,12 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5856
6045
|
const salt = generateRandomSalt();
|
|
5857
6046
|
logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
|
|
5858
6047
|
logger.debug("Calculating app ID...");
|
|
5859
|
-
const appIDToBeDeployed = await calculateAppID(
|
|
5860
|
-
preflightCtx.
|
|
5861
|
-
|
|
5862
|
-
preflightCtx.
|
|
6048
|
+
const appIDToBeDeployed = await calculateAppID({
|
|
6049
|
+
publicClient: preflightCtx.publicClient,
|
|
6050
|
+
environmentConfig: preflightCtx.environmentConfig,
|
|
6051
|
+
ownerAddress: preflightCtx.selfAddress,
|
|
5863
6052
|
salt
|
|
5864
|
-
);
|
|
6053
|
+
});
|
|
5865
6054
|
logger.info(``);
|
|
5866
6055
|
logger.info(`App ID: ${appIDToBeDeployed}`);
|
|
5867
6056
|
logger.info(``);
|
|
@@ -5882,8 +6071,8 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5882
6071
|
logger.info("Deploying on-chain...");
|
|
5883
6072
|
const deployResult = await deployApp(
|
|
5884
6073
|
{
|
|
5885
|
-
|
|
5886
|
-
|
|
6074
|
+
walletClient: preflightCtx.walletClient,
|
|
6075
|
+
publicClient: preflightCtx.publicClient,
|
|
5887
6076
|
environmentConfig: preflightCtx.environmentConfig,
|
|
5888
6077
|
salt,
|
|
5889
6078
|
release,
|
|
@@ -5896,8 +6085,8 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5896
6085
|
logger.info("Waiting for app to start...");
|
|
5897
6086
|
const ipAddress = await watchUntilRunning(
|
|
5898
6087
|
{
|
|
5899
|
-
|
|
5900
|
-
|
|
6088
|
+
walletClient: preflightCtx.walletClient,
|
|
6089
|
+
publicClient: preflightCtx.publicClient,
|
|
5901
6090
|
environmentConfig: preflightCtx.environmentConfig,
|
|
5902
6091
|
appId: deployResult.appId
|
|
5903
6092
|
},
|
|
@@ -5914,12 +6103,10 @@ async function deploy(options, logger = defaultLogger) {
|
|
|
5914
6103
|
);
|
|
5915
6104
|
}
|
|
5916
6105
|
async function checkQuotaAvailable(preflightCtx) {
|
|
5917
|
-
const
|
|
5918
|
-
const environmentConfig = preflightCtx.environmentConfig;
|
|
5919
|
-
const userAddress = preflightCtx.selfAddress;
|
|
6106
|
+
const { publicClient, environmentConfig, selfAddress: userAddress } = preflightCtx;
|
|
5920
6107
|
let maxQuota;
|
|
5921
6108
|
try {
|
|
5922
|
-
maxQuota = await getMaxActiveAppsPerUser(
|
|
6109
|
+
maxQuota = await getMaxActiveAppsPerUser(publicClient, environmentConfig, userAddress);
|
|
5923
6110
|
} catch (err) {
|
|
5924
6111
|
throw new Error(`failed to get quota limit: ${err.message}`);
|
|
5925
6112
|
}
|
|
@@ -5930,7 +6117,7 @@ async function checkQuotaAvailable(preflightCtx) {
|
|
|
5930
6117
|
}
|
|
5931
6118
|
let activeCount;
|
|
5932
6119
|
try {
|
|
5933
|
-
activeCount = await getActiveAppCount(
|
|
6120
|
+
activeCount = await getActiveAppCount(publicClient, environmentConfig, userAddress);
|
|
5934
6121
|
} catch (err) {
|
|
5935
6122
|
throw new Error(`failed to get active app count: ${err.message}`);
|
|
5936
6123
|
}
|
|
@@ -5961,8 +6148,8 @@ async function prepareDeploy(options, logger = defaultLogger) {
|
|
|
5961
6148
|
logger.debug("Performing preflight checks...");
|
|
5962
6149
|
const preflightCtx = await doPreflightChecks(
|
|
5963
6150
|
{
|
|
5964
|
-
|
|
5965
|
-
|
|
6151
|
+
walletClient: options.walletClient,
|
|
6152
|
+
publicClient: options.publicClient,
|
|
5966
6153
|
environment: options.environment
|
|
5967
6154
|
},
|
|
5968
6155
|
logger
|
|
@@ -5979,12 +6166,12 @@ async function prepareDeploy(options, logger = defaultLogger) {
|
|
|
5979
6166
|
const salt = generateRandomSalt();
|
|
5980
6167
|
logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
|
|
5981
6168
|
logger.debug("Calculating app ID...");
|
|
5982
|
-
const appIDToBeDeployed = await calculateAppID(
|
|
5983
|
-
preflightCtx.
|
|
5984
|
-
|
|
5985
|
-
preflightCtx.
|
|
6169
|
+
const appIDToBeDeployed = await calculateAppID({
|
|
6170
|
+
publicClient: preflightCtx.publicClient,
|
|
6171
|
+
environmentConfig: preflightCtx.environmentConfig,
|
|
6172
|
+
ownerAddress: preflightCtx.selfAddress,
|
|
5986
6173
|
salt
|
|
5987
|
-
);
|
|
6174
|
+
});
|
|
5988
6175
|
logger.info(``);
|
|
5989
6176
|
logger.info(`App ID: ${appIDToBeDeployed}`);
|
|
5990
6177
|
logger.info(``);
|
|
@@ -6005,12 +6192,13 @@ async function prepareDeploy(options, logger = defaultLogger) {
|
|
|
6005
6192
|
logger.debug("Preparing deploy batch...");
|
|
6006
6193
|
const batch = await prepareDeployBatch(
|
|
6007
6194
|
{
|
|
6008
|
-
|
|
6009
|
-
|
|
6195
|
+
walletClient: preflightCtx.walletClient,
|
|
6196
|
+
publicClient: preflightCtx.publicClient,
|
|
6010
6197
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6011
6198
|
salt,
|
|
6012
6199
|
release,
|
|
6013
|
-
publicLogs
|
|
6200
|
+
publicLogs,
|
|
6201
|
+
imageRef: finalImageRef
|
|
6014
6202
|
},
|
|
6015
6203
|
logger
|
|
6016
6204
|
);
|
|
@@ -6055,25 +6243,23 @@ async function executeDeploy(options) {
|
|
|
6055
6243
|
}
|
|
6056
6244
|
);
|
|
6057
6245
|
}
|
|
6058
|
-
async function watchDeployment(appId,
|
|
6246
|
+
async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
|
|
6059
6247
|
return withSDKTelemetry(
|
|
6060
6248
|
{
|
|
6061
6249
|
functionName: "watchDeployment",
|
|
6062
6250
|
skipTelemetry,
|
|
6063
6251
|
properties: {
|
|
6064
|
-
environment
|
|
6252
|
+
environment: environmentConfig.name
|
|
6065
6253
|
}
|
|
6066
6254
|
},
|
|
6067
6255
|
async () => {
|
|
6068
|
-
const environmentConfig = getEnvironmentConfig(environment);
|
|
6069
6256
|
logger.info("Waiting for app to start...");
|
|
6070
6257
|
return watchUntilRunning(
|
|
6071
6258
|
{
|
|
6072
|
-
|
|
6073
|
-
|
|
6259
|
+
walletClient,
|
|
6260
|
+
publicClient,
|
|
6074
6261
|
environmentConfig,
|
|
6075
|
-
appId
|
|
6076
|
-
clientId
|
|
6262
|
+
appId
|
|
6077
6263
|
},
|
|
6078
6264
|
logger
|
|
6079
6265
|
);
|
|
@@ -6082,18 +6268,12 @@ async function watchDeployment(appId, privateKey, rpcUrl, environment, logger =
|
|
|
6082
6268
|
}
|
|
6083
6269
|
|
|
6084
6270
|
// src/client/common/utils/permissions.ts
|
|
6085
|
-
var import_viem8 = require("viem");
|
|
6086
6271
|
var AnyoneCanCallAddress = "0x493219d9949348178af1f58740655951a8cd110c";
|
|
6087
6272
|
var ApiPermissionsTarget = "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d";
|
|
6088
6273
|
var CanViewAppLogsPermission2 = "0x2fd3f2fe";
|
|
6089
6274
|
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
6275
|
try {
|
|
6096
|
-
const canCall = await publicClient.readContract({
|
|
6276
|
+
const canCall = await preflightCtx.publicClient.readContract({
|
|
6097
6277
|
address: preflightCtx.environmentConfig.permissionControllerAddress,
|
|
6098
6278
|
abi: PermissionController_default,
|
|
6099
6279
|
functionName: "canCall",
|
|
@@ -6120,8 +6300,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
|
|
|
6120
6300
|
logger.debug("Performing preflight checks...");
|
|
6121
6301
|
const preflightCtx = await doPreflightChecks(
|
|
6122
6302
|
{
|
|
6123
|
-
|
|
6124
|
-
|
|
6303
|
+
walletClient: options.walletClient,
|
|
6304
|
+
publicClient: options.publicClient,
|
|
6125
6305
|
environment: options.environment
|
|
6126
6306
|
},
|
|
6127
6307
|
logger
|
|
@@ -6153,13 +6333,14 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
|
|
|
6153
6333
|
const needsPermissionChange = currentlyPublic !== publicLogs;
|
|
6154
6334
|
logger.debug("Preparing upgrade batch...");
|
|
6155
6335
|
const batch = await prepareUpgradeBatch({
|
|
6156
|
-
|
|
6157
|
-
|
|
6336
|
+
walletClient: preflightCtx.walletClient,
|
|
6337
|
+
publicClient: preflightCtx.publicClient,
|
|
6158
6338
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6159
|
-
|
|
6339
|
+
appID,
|
|
6160
6340
|
release,
|
|
6161
6341
|
publicLogs,
|
|
6162
|
-
needsPermissionChange
|
|
6342
|
+
needsPermissionChange,
|
|
6343
|
+
imageRef: options.imageRef
|
|
6163
6344
|
});
|
|
6164
6345
|
logger.debug("Estimating gas...");
|
|
6165
6346
|
const gasEstimate = await estimateBatchGas({
|
|
@@ -6183,8 +6364,8 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
|
|
|
6183
6364
|
);
|
|
6184
6365
|
}
|
|
6185
6366
|
function validateUpgradeOptions(options) {
|
|
6186
|
-
if (!options.
|
|
6187
|
-
throw new Error("
|
|
6367
|
+
if (!options.walletClient?.account) {
|
|
6368
|
+
throw new Error("walletClient with account is required for upgrade");
|
|
6188
6369
|
}
|
|
6189
6370
|
if (!options.appId) {
|
|
6190
6371
|
throw new Error("appId is required for upgrade");
|
|
@@ -6221,8 +6402,8 @@ async function upgrade(options, logger = defaultLogger) {
|
|
|
6221
6402
|
logger.debug("Performing preflight checks...");
|
|
6222
6403
|
const preflightCtx = await doPreflightChecks(
|
|
6223
6404
|
{
|
|
6224
|
-
|
|
6225
|
-
|
|
6405
|
+
walletClient: options.walletClient,
|
|
6406
|
+
publicClient: options.publicClient,
|
|
6226
6407
|
environment: options.environment
|
|
6227
6408
|
},
|
|
6228
6409
|
logger
|
|
@@ -6256,10 +6437,10 @@ async function upgrade(options, logger = defaultLogger) {
|
|
|
6256
6437
|
logger.info("Upgrading on-chain...");
|
|
6257
6438
|
const txHash = await upgradeApp(
|
|
6258
6439
|
{
|
|
6259
|
-
|
|
6260
|
-
|
|
6440
|
+
walletClient: preflightCtx.walletClient,
|
|
6441
|
+
publicClient: preflightCtx.publicClient,
|
|
6261
6442
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6262
|
-
|
|
6443
|
+
appID,
|
|
6263
6444
|
release,
|
|
6264
6445
|
publicLogs,
|
|
6265
6446
|
needsPermissionChange,
|
|
@@ -6271,8 +6452,8 @@ async function upgrade(options, logger = defaultLogger) {
|
|
|
6271
6452
|
logger.info("Waiting for upgrade to complete...");
|
|
6272
6453
|
await watchUntilUpgradeComplete(
|
|
6273
6454
|
{
|
|
6274
|
-
|
|
6275
|
-
|
|
6455
|
+
walletClient: preflightCtx.walletClient,
|
|
6456
|
+
publicClient: preflightCtx.publicClient,
|
|
6276
6457
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6277
6458
|
appId: appID
|
|
6278
6459
|
},
|
|
@@ -6299,8 +6480,8 @@ async function prepareUpgrade(options, logger = defaultLogger) {
|
|
|
6299
6480
|
logger.debug("Performing preflight checks...");
|
|
6300
6481
|
const preflightCtx = await doPreflightChecks(
|
|
6301
6482
|
{
|
|
6302
|
-
|
|
6303
|
-
|
|
6483
|
+
walletClient: options.walletClient,
|
|
6484
|
+
publicClient: options.publicClient,
|
|
6304
6485
|
environment: options.environment
|
|
6305
6486
|
},
|
|
6306
6487
|
logger
|
|
@@ -6333,13 +6514,14 @@ async function prepareUpgrade(options, logger = defaultLogger) {
|
|
|
6333
6514
|
const needsPermissionChange = currentlyPublic !== publicLogs;
|
|
6334
6515
|
logger.debug("Preparing upgrade batch...");
|
|
6335
6516
|
const batch = await prepareUpgradeBatch({
|
|
6336
|
-
|
|
6337
|
-
|
|
6517
|
+
walletClient: preflightCtx.walletClient,
|
|
6518
|
+
publicClient: preflightCtx.publicClient,
|
|
6338
6519
|
environmentConfig: preflightCtx.environmentConfig,
|
|
6339
|
-
|
|
6520
|
+
appID,
|
|
6340
6521
|
release,
|
|
6341
6522
|
publicLogs,
|
|
6342
|
-
needsPermissionChange
|
|
6523
|
+
needsPermissionChange,
|
|
6524
|
+
imageRef: finalImageRef
|
|
6343
6525
|
});
|
|
6344
6526
|
logger.debug("Estimating gas...");
|
|
6345
6527
|
const gasEstimate = await estimateBatchGas({
|
|
@@ -6380,25 +6562,23 @@ async function executeUpgrade(options) {
|
|
|
6380
6562
|
}
|
|
6381
6563
|
);
|
|
6382
6564
|
}
|
|
6383
|
-
async function watchUpgrade(appId,
|
|
6565
|
+
async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
|
|
6384
6566
|
return withSDKTelemetry(
|
|
6385
6567
|
{
|
|
6386
6568
|
functionName: "watchUpgrade",
|
|
6387
6569
|
skipTelemetry,
|
|
6388
6570
|
properties: {
|
|
6389
|
-
environment
|
|
6571
|
+
environment: environmentConfig.name
|
|
6390
6572
|
}
|
|
6391
6573
|
},
|
|
6392
6574
|
async () => {
|
|
6393
|
-
const environmentConfig = getEnvironmentConfig(environment);
|
|
6394
6575
|
logger.info("Waiting for upgrade to complete...");
|
|
6395
6576
|
await watchUntilUpgradeComplete(
|
|
6396
6577
|
{
|
|
6397
|
-
|
|
6398
|
-
|
|
6578
|
+
walletClient,
|
|
6579
|
+
publicClient,
|
|
6399
6580
|
environmentConfig,
|
|
6400
|
-
appId
|
|
6401
|
-
clientId
|
|
6581
|
+
appId
|
|
6402
6582
|
},
|
|
6403
6583
|
logger
|
|
6404
6584
|
);
|
|
@@ -6962,32 +7142,25 @@ async function watchLogs(appID, userApiClient, initialLogs) {
|
|
|
6962
7142
|
}
|
|
6963
7143
|
console.log("\nStopped watching");
|
|
6964
7144
|
}
|
|
6965
|
-
async function logs(options, logger = defaultLogger, skipTelemetry = false) {
|
|
6966
|
-
const skipTelemetryFlag = skipTelemetry || options.skipTelemetry || false;
|
|
7145
|
+
async function logs(options, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry = false) {
|
|
6967
7146
|
return withSDKTelemetry(
|
|
6968
7147
|
{
|
|
6969
7148
|
functionName: "logs",
|
|
6970
|
-
skipTelemetry
|
|
6971
|
-
properties: { environment:
|
|
7149
|
+
skipTelemetry,
|
|
7150
|
+
properties: { environment: environmentConfig.name }
|
|
6972
7151
|
},
|
|
6973
7152
|
async () => {
|
|
6974
7153
|
console.log();
|
|
6975
7154
|
if (!options.appID) {
|
|
6976
7155
|
throw new Error("appID is required for viewing logs");
|
|
6977
7156
|
}
|
|
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
7157
|
const appID = validateAppID(options.appID);
|
|
6985
7158
|
const formattedApp = formatAppDisplay(environmentConfig.name, appID, "");
|
|
6986
7159
|
const userApiClient = new UserApiClient(
|
|
6987
7160
|
environmentConfig,
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
options.clientId
|
|
7161
|
+
walletClient,
|
|
7162
|
+
publicClient,
|
|
7163
|
+
options.clientId ? { clientId: options.clientId } : void 0
|
|
6991
7164
|
);
|
|
6992
7165
|
let logsText;
|
|
6993
7166
|
let logsError = null;
|
|
@@ -7063,35 +7236,39 @@ async function logs(options, logger = defaultLogger, skipTelemetry = false) {
|
|
|
7063
7236
|
}
|
|
7064
7237
|
|
|
7065
7238
|
// src/client/modules/compute/app/index.ts
|
|
7066
|
-
var CONTROLLER_ABI = (0,
|
|
7239
|
+
var CONTROLLER_ABI = (0, import_viem6.parseAbi)([
|
|
7067
7240
|
"function startApp(address appId)",
|
|
7068
7241
|
"function stopApp(address appId)",
|
|
7069
7242
|
"function terminateApp(address appId)"
|
|
7070
7243
|
]);
|
|
7071
7244
|
function encodeStartAppData(appId) {
|
|
7072
|
-
return (0,
|
|
7245
|
+
return (0, import_viem6.encodeFunctionData)({
|
|
7073
7246
|
abi: CONTROLLER_ABI,
|
|
7074
7247
|
functionName: "startApp",
|
|
7075
7248
|
args: [appId]
|
|
7076
7249
|
});
|
|
7077
7250
|
}
|
|
7078
7251
|
function encodeStopAppData(appId) {
|
|
7079
|
-
return (0,
|
|
7252
|
+
return (0, import_viem6.encodeFunctionData)({
|
|
7080
7253
|
abi: CONTROLLER_ABI,
|
|
7081
7254
|
functionName: "stopApp",
|
|
7082
7255
|
args: [appId]
|
|
7083
7256
|
});
|
|
7084
7257
|
}
|
|
7085
7258
|
function encodeTerminateAppData(appId) {
|
|
7086
|
-
return (0,
|
|
7259
|
+
return (0, import_viem6.encodeFunctionData)({
|
|
7087
7260
|
abi: CONTROLLER_ABI,
|
|
7088
7261
|
functionName: "terminateApp",
|
|
7089
7262
|
args: [appId]
|
|
7090
7263
|
});
|
|
7091
7264
|
}
|
|
7092
7265
|
function createAppModule(ctx) {
|
|
7093
|
-
const
|
|
7266
|
+
const { walletClient, publicClient } = ctx;
|
|
7094
7267
|
const skipTelemetry = ctx.skipTelemetry || false;
|
|
7268
|
+
if (!walletClient.account) {
|
|
7269
|
+
throw new Error("WalletClient must have an account attached");
|
|
7270
|
+
}
|
|
7271
|
+
const account = walletClient.account;
|
|
7095
7272
|
const environment = getEnvironmentConfig(ctx.environment);
|
|
7096
7273
|
const logger = getLogger(ctx.verbose);
|
|
7097
7274
|
return {
|
|
@@ -7102,8 +7279,8 @@ function createAppModule(ctx) {
|
|
|
7102
7279
|
async deploy(opts) {
|
|
7103
7280
|
const result = await deploy(
|
|
7104
7281
|
{
|
|
7105
|
-
|
|
7106
|
-
|
|
7282
|
+
walletClient,
|
|
7283
|
+
publicClient,
|
|
7107
7284
|
environment: ctx.environment,
|
|
7108
7285
|
appName: opts.name,
|
|
7109
7286
|
instanceType: opts.instanceType,
|
|
@@ -7127,8 +7304,8 @@ function createAppModule(ctx) {
|
|
|
7127
7304
|
const result = await upgrade(
|
|
7128
7305
|
{
|
|
7129
7306
|
appId,
|
|
7130
|
-
|
|
7131
|
-
|
|
7307
|
+
walletClient,
|
|
7308
|
+
publicClient,
|
|
7132
7309
|
environment: ctx.environment,
|
|
7133
7310
|
instanceType: opts.instanceType,
|
|
7134
7311
|
dockerfilePath: opts.dockerfile,
|
|
@@ -7149,8 +7326,8 @@ function createAppModule(ctx) {
|
|
|
7149
7326
|
async prepareDeploy(opts) {
|
|
7150
7327
|
return prepareDeploy(
|
|
7151
7328
|
{
|
|
7152
|
-
|
|
7153
|
-
|
|
7329
|
+
walletClient,
|
|
7330
|
+
publicClient,
|
|
7154
7331
|
environment: ctx.environment,
|
|
7155
7332
|
appName: opts.name,
|
|
7156
7333
|
instanceType: opts.instanceType,
|
|
@@ -7167,9 +7344,9 @@ function createAppModule(ctx) {
|
|
|
7167
7344
|
async prepareDeployFromVerifiableBuild(opts) {
|
|
7168
7345
|
return prepareDeployFromVerifiableBuild(
|
|
7169
7346
|
{
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
environment: ctx.environment,
|
|
7347
|
+
walletClient,
|
|
7348
|
+
publicClient,
|
|
7349
|
+
environment: ctx.environment,
|
|
7173
7350
|
appName: opts.name,
|
|
7174
7351
|
instanceType: opts.instanceType,
|
|
7175
7352
|
envFilePath: opts.envFile,
|
|
@@ -7183,17 +7360,6 @@ function createAppModule(ctx) {
|
|
|
7183
7360
|
);
|
|
7184
7361
|
},
|
|
7185
7362
|
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
7363
|
const result = await executeDeploy({
|
|
7198
7364
|
prepared,
|
|
7199
7365
|
context: {
|
|
@@ -7209,591 +7375,260 @@ function createAppModule(ctx) {
|
|
|
7209
7375
|
appId: result.appId,
|
|
7210
7376
|
txHash: result.txHash,
|
|
7211
7377
|
appName: result.appName,
|
|
7212
|
-
imageRef: result.imageRef
|
|
7213
|
-
};
|
|
7214
|
-
},
|
|
7215
|
-
async watchDeployment(appId) {
|
|
7216
|
-
return watchDeployment(
|
|
7217
|
-
appId,
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
logger,
|
|
7222
|
-
|
|
7223
|
-
skipTelemetry
|
|
7224
|
-
);
|
|
7225
|
-
},
|
|
7226
|
-
// Granular upgrade control
|
|
7227
|
-
async prepareUpgrade(appId, opts) {
|
|
7228
|
-
return prepareUpgrade(
|
|
7229
|
-
{
|
|
7230
|
-
appId,
|
|
7231
|
-
privateKey,
|
|
7232
|
-
rpcUrl: ctx.rpcUrl,
|
|
7233
|
-
environment: ctx.environment,
|
|
7234
|
-
instanceType: opts.instanceType,
|
|
7235
|
-
dockerfilePath: opts.dockerfile,
|
|
7236
|
-
envFilePath: opts.envFile,
|
|
7237
|
-
imageRef: opts.imageRef,
|
|
7238
|
-
logVisibility: opts.logVisibility,
|
|
7239
|
-
resourceUsageMonitoring: opts.resourceUsageMonitoring,
|
|
7240
|
-
skipTelemetry
|
|
7241
|
-
},
|
|
7242
|
-
logger
|
|
7243
|
-
);
|
|
7244
|
-
},
|
|
7245
|
-
async prepareUpgradeFromVerifiableBuild(appId, opts) {
|
|
7246
|
-
return prepareUpgradeFromVerifiableBuild(
|
|
7247
|
-
{
|
|
7248
|
-
appId,
|
|
7249
|
-
privateKey,
|
|
7250
|
-
rpcUrl: ctx.rpcUrl,
|
|
7251
|
-
environment: ctx.environment,
|
|
7252
|
-
instanceType: opts.instanceType,
|
|
7253
|
-
envFilePath: opts.envFile,
|
|
7254
|
-
imageRef: opts.imageRef,
|
|
7255
|
-
imageDigest: opts.imageDigest,
|
|
7256
|
-
logVisibility: opts.logVisibility,
|
|
7257
|
-
resourceUsageMonitoring: opts.resourceUsageMonitoring,
|
|
7258
|
-
skipTelemetry
|
|
7259
|
-
},
|
|
7260
|
-
logger
|
|
7261
|
-
);
|
|
7262
|
-
},
|
|
7263
|
-
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
|
-
const result = await executeUpgrade({
|
|
7276
|
-
prepared,
|
|
7277
|
-
context: {
|
|
7278
|
-
walletClient,
|
|
7279
|
-
publicClient,
|
|
7280
|
-
environmentConfig: environment
|
|
7281
|
-
},
|
|
7282
|
-
gas,
|
|
7283
|
-
logger,
|
|
7284
|
-
skipTelemetry
|
|
7285
|
-
});
|
|
7286
|
-
return {
|
|
7287
|
-
appId: result.appId,
|
|
7288
|
-
txHash: result.txHash,
|
|
7289
|
-
imageRef: result.imageRef
|
|
7290
|
-
};
|
|
7291
|
-
},
|
|
7292
|
-
async watchUpgrade(appId) {
|
|
7293
|
-
return watchUpgrade(
|
|
7294
|
-
appId,
|
|
7295
|
-
privateKey,
|
|
7296
|
-
ctx.rpcUrl,
|
|
7297
|
-
ctx.environment,
|
|
7298
|
-
logger,
|
|
7299
|
-
ctx.clientId,
|
|
7300
|
-
skipTelemetry
|
|
7301
|
-
);
|
|
7302
|
-
},
|
|
7303
|
-
// Profile management
|
|
7304
|
-
async setProfile(appId, profile) {
|
|
7305
|
-
return withSDKTelemetry(
|
|
7306
|
-
{
|
|
7307
|
-
functionName: "setProfile",
|
|
7308
|
-
skipTelemetry,
|
|
7309
|
-
properties: { environment: ctx.environment }
|
|
7310
|
-
},
|
|
7311
|
-
async () => {
|
|
7312
|
-
const userApiClient = new UserApiClient(
|
|
7313
|
-
environment,
|
|
7314
|
-
privateKey,
|
|
7315
|
-
ctx.rpcUrl,
|
|
7316
|
-
ctx.clientId
|
|
7317
|
-
);
|
|
7318
|
-
return userApiClient.uploadAppProfile(
|
|
7319
|
-
appId,
|
|
7320
|
-
profile.name,
|
|
7321
|
-
profile.website,
|
|
7322
|
-
profile.description,
|
|
7323
|
-
profile.xURL,
|
|
7324
|
-
profile.imagePath
|
|
7325
|
-
);
|
|
7326
|
-
}
|
|
7327
|
-
);
|
|
7328
|
-
},
|
|
7329
|
-
async logs(opts) {
|
|
7330
|
-
return logs(
|
|
7331
|
-
{
|
|
7332
|
-
privateKey,
|
|
7333
|
-
appID: opts.appID,
|
|
7334
|
-
watch: opts.watch,
|
|
7335
|
-
environment: ctx.environment,
|
|
7336
|
-
clientId: ctx.clientId
|
|
7337
|
-
},
|
|
7338
|
-
logger,
|
|
7339
|
-
skipTelemetry
|
|
7340
|
-
// Skip if called from CLI
|
|
7341
|
-
);
|
|
7342
|
-
},
|
|
7343
|
-
async start(appId, opts) {
|
|
7344
|
-
return withSDKTelemetry(
|
|
7345
|
-
{
|
|
7346
|
-
functionName: "start",
|
|
7347
|
-
skipTelemetry,
|
|
7348
|
-
// Skip if called from CLI
|
|
7349
|
-
properties: { environment: ctx.environment }
|
|
7350
|
-
},
|
|
7351
|
-
async () => {
|
|
7352
|
-
const pendingMessage = `Starting app ${appId}...`;
|
|
7353
|
-
const data = (0, import_viem9.encodeFunctionData)({
|
|
7354
|
-
abi: CONTROLLER_ABI,
|
|
7355
|
-
functionName: "startApp",
|
|
7356
|
-
args: [appId]
|
|
7357
|
-
});
|
|
7358
|
-
const tx = await sendAndWaitForTransaction(
|
|
7359
|
-
{
|
|
7360
|
-
privateKey,
|
|
7361
|
-
rpcUrl: ctx.rpcUrl,
|
|
7362
|
-
environmentConfig: environment,
|
|
7363
|
-
to: environment.appControllerAddress,
|
|
7364
|
-
data,
|
|
7365
|
-
pendingMessage,
|
|
7366
|
-
txDescription: "StartApp",
|
|
7367
|
-
gas: opts?.gas
|
|
7368
|
-
},
|
|
7369
|
-
logger
|
|
7370
|
-
);
|
|
7371
|
-
return { tx };
|
|
7372
|
-
}
|
|
7373
|
-
);
|
|
7374
|
-
},
|
|
7375
|
-
async stop(appId, opts) {
|
|
7376
|
-
return withSDKTelemetry(
|
|
7377
|
-
{
|
|
7378
|
-
functionName: "stop",
|
|
7379
|
-
skipTelemetry,
|
|
7380
|
-
// Skip if called from CLI
|
|
7381
|
-
properties: { environment: ctx.environment }
|
|
7382
|
-
},
|
|
7383
|
-
async () => {
|
|
7384
|
-
const pendingMessage = `Stopping app ${appId}...`;
|
|
7385
|
-
const data = (0, import_viem9.encodeFunctionData)({
|
|
7386
|
-
abi: CONTROLLER_ABI,
|
|
7387
|
-
functionName: "stopApp",
|
|
7388
|
-
args: [appId]
|
|
7389
|
-
});
|
|
7390
|
-
const tx = await sendAndWaitForTransaction(
|
|
7391
|
-
{
|
|
7392
|
-
privateKey,
|
|
7393
|
-
rpcUrl: ctx.rpcUrl,
|
|
7394
|
-
environmentConfig: environment,
|
|
7395
|
-
to: environment.appControllerAddress,
|
|
7396
|
-
data,
|
|
7397
|
-
pendingMessage,
|
|
7398
|
-
txDescription: "StopApp",
|
|
7399
|
-
gas: opts?.gas
|
|
7400
|
-
},
|
|
7401
|
-
logger
|
|
7402
|
-
);
|
|
7403
|
-
return { tx };
|
|
7404
|
-
}
|
|
7405
|
-
);
|
|
7406
|
-
},
|
|
7407
|
-
async terminate(appId, opts) {
|
|
7408
|
-
return withSDKTelemetry(
|
|
7409
|
-
{
|
|
7410
|
-
functionName: "terminate",
|
|
7411
|
-
skipTelemetry,
|
|
7412
|
-
// Skip if called from CLI
|
|
7413
|
-
properties: { environment: ctx.environment }
|
|
7414
|
-
},
|
|
7415
|
-
async () => {
|
|
7416
|
-
const pendingMessage = `Terminating app ${appId}...`;
|
|
7417
|
-
const data = (0, import_viem9.encodeFunctionData)({
|
|
7418
|
-
abi: CONTROLLER_ABI,
|
|
7419
|
-
functionName: "terminateApp",
|
|
7420
|
-
args: [appId]
|
|
7421
|
-
});
|
|
7422
|
-
const tx = await sendAndWaitForTransaction(
|
|
7423
|
-
{
|
|
7424
|
-
privateKey,
|
|
7425
|
-
rpcUrl: ctx.rpcUrl,
|
|
7426
|
-
environmentConfig: environment,
|
|
7427
|
-
to: environment.appControllerAddress,
|
|
7428
|
-
data,
|
|
7429
|
-
pendingMessage,
|
|
7430
|
-
txDescription: "TerminateApp",
|
|
7431
|
-
gas: opts?.gas
|
|
7432
|
-
},
|
|
7433
|
-
logger
|
|
7434
|
-
);
|
|
7435
|
-
return { tx };
|
|
7436
|
-
}
|
|
7437
|
-
);
|
|
7438
|
-
},
|
|
7439
|
-
async isDelegated() {
|
|
7440
|
-
return isDelegated({
|
|
7441
|
-
privateKey,
|
|
7442
|
-
rpcUrl: ctx.rpcUrl,
|
|
7443
|
-
environmentConfig: environment
|
|
7444
|
-
});
|
|
7445
|
-
},
|
|
7446
|
-
async undelegate() {
|
|
7447
|
-
return withSDKTelemetry(
|
|
7448
|
-
{
|
|
7449
|
-
functionName: "undelegate",
|
|
7450
|
-
skipTelemetry,
|
|
7451
|
-
// Skip if called from CLI
|
|
7452
|
-
properties: { environment: ctx.environment }
|
|
7453
|
-
},
|
|
7454
|
-
async () => {
|
|
7455
|
-
const tx = await undelegate(
|
|
7456
|
-
{
|
|
7457
|
-
privateKey,
|
|
7458
|
-
rpcUrl: ctx.rpcUrl,
|
|
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."
|
|
7378
|
+
imageRef: result.imageRef
|
|
7379
|
+
};
|
|
7380
|
+
},
|
|
7381
|
+
async watchDeployment(appId) {
|
|
7382
|
+
return watchDeployment(
|
|
7383
|
+
appId,
|
|
7384
|
+
walletClient,
|
|
7385
|
+
publicClient,
|
|
7386
|
+
environment,
|
|
7387
|
+
logger,
|
|
7388
|
+
skipTelemetry
|
|
7735
7389
|
);
|
|
7736
|
-
}
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7390
|
+
},
|
|
7391
|
+
// Granular upgrade control
|
|
7392
|
+
async prepareUpgrade(appId, opts) {
|
|
7393
|
+
return prepareUpgrade(
|
|
7394
|
+
{
|
|
7395
|
+
appId,
|
|
7396
|
+
walletClient,
|
|
7397
|
+
publicClient,
|
|
7398
|
+
environment: ctx.environment,
|
|
7399
|
+
instanceType: opts.instanceType,
|
|
7400
|
+
dockerfilePath: opts.dockerfile,
|
|
7401
|
+
envFilePath: opts.envFile,
|
|
7402
|
+
imageRef: opts.imageRef,
|
|
7403
|
+
logVisibility: opts.logVisibility,
|
|
7404
|
+
resourceUsageMonitoring: opts.resourceUsageMonitoring,
|
|
7405
|
+
skipTelemetry
|
|
7406
|
+
},
|
|
7407
|
+
logger
|
|
7408
|
+
);
|
|
7409
|
+
},
|
|
7410
|
+
async prepareUpgradeFromVerifiableBuild(appId, opts) {
|
|
7411
|
+
return prepareUpgradeFromVerifiableBuild(
|
|
7412
|
+
{
|
|
7413
|
+
appId,
|
|
7414
|
+
walletClient,
|
|
7415
|
+
publicClient,
|
|
7416
|
+
environment: ctx.environment,
|
|
7417
|
+
instanceType: opts.instanceType,
|
|
7418
|
+
envFilePath: opts.envFile,
|
|
7419
|
+
imageRef: opts.imageRef,
|
|
7420
|
+
imageDigest: opts.imageDigest,
|
|
7421
|
+
logVisibility: opts.logVisibility,
|
|
7422
|
+
resourceUsageMonitoring: opts.resourceUsageMonitoring,
|
|
7423
|
+
skipTelemetry
|
|
7424
|
+
},
|
|
7425
|
+
logger
|
|
7426
|
+
);
|
|
7427
|
+
},
|
|
7428
|
+
async executeUpgrade(prepared, gas) {
|
|
7429
|
+
const result = await executeUpgrade({
|
|
7430
|
+
prepared,
|
|
7431
|
+
context: {
|
|
7432
|
+
walletClient,
|
|
7433
|
+
publicClient,
|
|
7434
|
+
environmentConfig: environment
|
|
7435
|
+
},
|
|
7436
|
+
gas,
|
|
7437
|
+
logger,
|
|
7438
|
+
skipTelemetry
|
|
7439
|
+
});
|
|
7440
|
+
return {
|
|
7441
|
+
appId: result.appId,
|
|
7442
|
+
txHash: result.txHash,
|
|
7443
|
+
imageRef: result.imageRef
|
|
7444
|
+
};
|
|
7445
|
+
},
|
|
7446
|
+
async watchUpgrade(appId) {
|
|
7447
|
+
return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
|
|
7448
|
+
},
|
|
7449
|
+
// Profile management
|
|
7450
|
+
async setProfile(appId, profile) {
|
|
7451
|
+
return withSDKTelemetry(
|
|
7452
|
+
{
|
|
7453
|
+
functionName: "setProfile",
|
|
7454
|
+
skipTelemetry,
|
|
7455
|
+
properties: { environment: ctx.environment }
|
|
7456
|
+
},
|
|
7457
|
+
async () => {
|
|
7458
|
+
const userApiClient = new UserApiClient(
|
|
7459
|
+
environment,
|
|
7460
|
+
walletClient,
|
|
7461
|
+
publicClient,
|
|
7462
|
+
ctx.clientId ? { clientId: ctx.clientId } : void 0
|
|
7463
|
+
);
|
|
7464
|
+
return userApiClient.uploadAppProfile(appId, profile.name, {
|
|
7465
|
+
website: profile.website,
|
|
7466
|
+
description: profile.description,
|
|
7467
|
+
xURL: profile.xURL,
|
|
7468
|
+
image: profile.image,
|
|
7469
|
+
imageName: profile.imageName
|
|
7470
|
+
});
|
|
7471
|
+
}
|
|
7472
|
+
);
|
|
7473
|
+
},
|
|
7474
|
+
async logs(opts) {
|
|
7475
|
+
return logs(
|
|
7476
|
+
{
|
|
7477
|
+
appID: opts.appID,
|
|
7478
|
+
watch: opts.watch,
|
|
7479
|
+
clientId: ctx.clientId
|
|
7480
|
+
},
|
|
7481
|
+
walletClient,
|
|
7482
|
+
publicClient,
|
|
7483
|
+
environment,
|
|
7484
|
+
logger,
|
|
7485
|
+
skipTelemetry
|
|
7486
|
+
);
|
|
7487
|
+
},
|
|
7488
|
+
async start(appId, opts) {
|
|
7489
|
+
return withSDKTelemetry(
|
|
7490
|
+
{
|
|
7491
|
+
functionName: "start",
|
|
7492
|
+
skipTelemetry,
|
|
7493
|
+
// Skip if called from CLI
|
|
7494
|
+
properties: { environment: ctx.environment }
|
|
7495
|
+
},
|
|
7496
|
+
async () => {
|
|
7497
|
+
const pendingMessage = `Starting app ${appId}...`;
|
|
7498
|
+
const data = (0, import_viem6.encodeFunctionData)({
|
|
7499
|
+
abi: CONTROLLER_ABI,
|
|
7500
|
+
functionName: "startApp",
|
|
7501
|
+
args: [appId]
|
|
7502
|
+
});
|
|
7503
|
+
const tx = await sendAndWaitForTransaction(
|
|
7504
|
+
{
|
|
7505
|
+
walletClient,
|
|
7506
|
+
publicClient,
|
|
7507
|
+
environmentConfig: environment,
|
|
7508
|
+
to: environment.appControllerAddress,
|
|
7509
|
+
data,
|
|
7510
|
+
pendingMessage,
|
|
7511
|
+
txDescription: "StartApp",
|
|
7512
|
+
gas: opts?.gas
|
|
7513
|
+
},
|
|
7514
|
+
logger
|
|
7515
|
+
);
|
|
7516
|
+
return { tx };
|
|
7517
|
+
}
|
|
7518
|
+
);
|
|
7519
|
+
},
|
|
7520
|
+
async stop(appId, opts) {
|
|
7521
|
+
return withSDKTelemetry(
|
|
7522
|
+
{
|
|
7523
|
+
functionName: "stop",
|
|
7524
|
+
skipTelemetry,
|
|
7525
|
+
// Skip if called from CLI
|
|
7526
|
+
properties: { environment: ctx.environment }
|
|
7527
|
+
},
|
|
7528
|
+
async () => {
|
|
7529
|
+
const pendingMessage = `Stopping app ${appId}...`;
|
|
7530
|
+
const data = (0, import_viem6.encodeFunctionData)({
|
|
7531
|
+
abi: CONTROLLER_ABI,
|
|
7532
|
+
functionName: "stopApp",
|
|
7533
|
+
args: [appId]
|
|
7534
|
+
});
|
|
7535
|
+
const tx = await sendAndWaitForTransaction(
|
|
7536
|
+
{
|
|
7537
|
+
walletClient,
|
|
7538
|
+
publicClient,
|
|
7539
|
+
environmentConfig: environment,
|
|
7540
|
+
to: environment.appControllerAddress,
|
|
7541
|
+
data,
|
|
7542
|
+
pendingMessage,
|
|
7543
|
+
txDescription: "StopApp",
|
|
7544
|
+
gas: opts?.gas
|
|
7545
|
+
},
|
|
7546
|
+
logger
|
|
7547
|
+
);
|
|
7548
|
+
return { tx };
|
|
7549
|
+
}
|
|
7550
|
+
);
|
|
7551
|
+
},
|
|
7552
|
+
async terminate(appId, opts) {
|
|
7553
|
+
return withSDKTelemetry(
|
|
7554
|
+
{
|
|
7555
|
+
functionName: "terminate",
|
|
7556
|
+
skipTelemetry,
|
|
7557
|
+
// Skip if called from CLI
|
|
7558
|
+
properties: { environment: ctx.environment }
|
|
7559
|
+
},
|
|
7560
|
+
async () => {
|
|
7561
|
+
const pendingMessage = `Terminating app ${appId}...`;
|
|
7562
|
+
const data = (0, import_viem6.encodeFunctionData)({
|
|
7563
|
+
abi: CONTROLLER_ABI,
|
|
7564
|
+
functionName: "terminateApp",
|
|
7565
|
+
args: [appId]
|
|
7566
|
+
});
|
|
7567
|
+
const tx = await sendAndWaitForTransaction(
|
|
7568
|
+
{
|
|
7569
|
+
walletClient,
|
|
7570
|
+
publicClient,
|
|
7571
|
+
environmentConfig: environment,
|
|
7572
|
+
to: environment.appControllerAddress,
|
|
7573
|
+
data,
|
|
7574
|
+
pendingMessage,
|
|
7575
|
+
txDescription: "TerminateApp",
|
|
7576
|
+
gas: opts?.gas
|
|
7577
|
+
},
|
|
7578
|
+
logger
|
|
7579
|
+
);
|
|
7580
|
+
return { tx };
|
|
7581
|
+
}
|
|
7582
|
+
);
|
|
7583
|
+
},
|
|
7584
|
+
async isDelegated() {
|
|
7585
|
+
return isDelegated({
|
|
7586
|
+
publicClient,
|
|
7587
|
+
environmentConfig: environment,
|
|
7588
|
+
address: account.address
|
|
7589
|
+
});
|
|
7590
|
+
},
|
|
7591
|
+
async undelegate() {
|
|
7592
|
+
return withSDKTelemetry(
|
|
7593
|
+
{
|
|
7594
|
+
functionName: "undelegate",
|
|
7595
|
+
skipTelemetry,
|
|
7596
|
+
// Skip if called from CLI
|
|
7597
|
+
properties: { environment: ctx.environment }
|
|
7598
|
+
},
|
|
7599
|
+
async () => {
|
|
7600
|
+
const tx = await undelegate(
|
|
7601
|
+
{
|
|
7602
|
+
walletClient,
|
|
7603
|
+
publicClient,
|
|
7604
|
+
environmentConfig: environment
|
|
7605
|
+
},
|
|
7606
|
+
logger
|
|
7607
|
+
);
|
|
7608
|
+
return { tx };
|
|
7609
|
+
}
|
|
7747
7610
|
);
|
|
7748
7611
|
}
|
|
7749
|
-
|
|
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;
|
|
7612
|
+
};
|
|
7776
7613
|
}
|
|
7777
7614
|
|
|
7778
|
-
// src/client/
|
|
7779
|
-
|
|
7780
|
-
function generateNewPrivateKey() {
|
|
7781
|
-
const privateKey = (0, import_accounts8.generatePrivateKey)();
|
|
7782
|
-
const address = (0, import_accounts8.privateKeyToAddress)(privateKey);
|
|
7615
|
+
// src/client/modules/compute/index.ts
|
|
7616
|
+
function createComputeModule(config) {
|
|
7783
7617
|
return {
|
|
7784
|
-
|
|
7785
|
-
address
|
|
7618
|
+
app: createAppModule(config)
|
|
7786
7619
|
};
|
|
7787
7620
|
}
|
|
7788
7621
|
|
|
7789
7622
|
// src/client/modules/billing/index.ts
|
|
7790
7623
|
function createBillingModule(config) {
|
|
7791
|
-
const { verbose = false, skipTelemetry = false } = config;
|
|
7792
|
-
|
|
7793
|
-
|
|
7624
|
+
const { verbose = false, skipTelemetry = false, walletClient } = config;
|
|
7625
|
+
if (!walletClient.account) {
|
|
7626
|
+
throw new Error("WalletClient must have an account attached");
|
|
7627
|
+
}
|
|
7628
|
+
const address = walletClient.account.address;
|
|
7794
7629
|
const logger = getLogger(verbose);
|
|
7795
7630
|
const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
|
|
7796
|
-
const billingApi = new BillingApiClient(billingEnvConfig,
|
|
7631
|
+
const billingApi = new BillingApiClient(billingEnvConfig, walletClient);
|
|
7797
7632
|
return {
|
|
7798
7633
|
address,
|
|
7799
7634
|
async subscribe(opts) {
|
|
@@ -7883,14 +7718,52 @@ function createBillingModule(config) {
|
|
|
7883
7718
|
|
|
7884
7719
|
// src/client/common/utils/buildapi.ts
|
|
7885
7720
|
var import_axios3 = __toESM(require("axios"), 1);
|
|
7886
|
-
var
|
|
7721
|
+
var MAX_RETRIES = 5;
|
|
7722
|
+
var INITIAL_BACKOFF_MS = 1e3;
|
|
7723
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
7724
|
+
async function sleep2(ms) {
|
|
7725
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
7726
|
+
}
|
|
7727
|
+
function getRetryDelay(res, attempt) {
|
|
7728
|
+
const retryAfter = res.headers["retry-after"];
|
|
7729
|
+
if (retryAfter) {
|
|
7730
|
+
const seconds = parseInt(retryAfter, 10);
|
|
7731
|
+
if (!isNaN(seconds)) {
|
|
7732
|
+
return Math.min(seconds * 1e3, MAX_BACKOFF_MS);
|
|
7733
|
+
}
|
|
7734
|
+
}
|
|
7735
|
+
return Math.min(INITIAL_BACKOFF_MS * Math.pow(2, attempt), MAX_BACKOFF_MS);
|
|
7736
|
+
}
|
|
7737
|
+
async function requestWithRetry(config) {
|
|
7738
|
+
let lastResponse;
|
|
7739
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
7740
|
+
const res = await (0, import_axios3.default)({ ...config, validateStatus: () => true });
|
|
7741
|
+
lastResponse = res;
|
|
7742
|
+
if (res.status !== 429) {
|
|
7743
|
+
return res;
|
|
7744
|
+
}
|
|
7745
|
+
if (attempt < MAX_RETRIES) {
|
|
7746
|
+
const delay = getRetryDelay(res, attempt);
|
|
7747
|
+
await sleep2(delay);
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7750
|
+
return lastResponse;
|
|
7751
|
+
}
|
|
7887
7752
|
var BuildApiClient = class {
|
|
7888
7753
|
constructor(options) {
|
|
7889
7754
|
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
7890
7755
|
this.clientId = options.clientId;
|
|
7891
|
-
|
|
7892
|
-
|
|
7756
|
+
this.walletClient = options.walletClient;
|
|
7757
|
+
}
|
|
7758
|
+
/**
|
|
7759
|
+
* Get the address of the connected wallet
|
|
7760
|
+
*/
|
|
7761
|
+
get address() {
|
|
7762
|
+
const account = this.walletClient?.account;
|
|
7763
|
+
if (!account) {
|
|
7764
|
+
throw new Error("WalletClient must have an account attached");
|
|
7893
7765
|
}
|
|
7766
|
+
return account.address;
|
|
7894
7767
|
}
|
|
7895
7768
|
async submitBuild(payload) {
|
|
7896
7769
|
return this.authenticatedJsonRequest("/builds", "POST", payload);
|
|
@@ -7908,74 +7781,74 @@ var BuildApiClient = class {
|
|
|
7908
7781
|
return this.authenticatedTextRequest(`/builds/${encodeURIComponent(buildId)}/logs`);
|
|
7909
7782
|
}
|
|
7910
7783
|
async listBuilds(params) {
|
|
7911
|
-
const res = await (
|
|
7784
|
+
const res = await requestWithRetry({
|
|
7912
7785
|
url: `${this.baseUrl}/builds`,
|
|
7913
7786
|
method: "GET",
|
|
7914
7787
|
params,
|
|
7915
7788
|
headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
|
|
7916
|
-
timeout: 6e4
|
|
7917
|
-
validateStatus: () => true
|
|
7789
|
+
timeout: 6e4
|
|
7918
7790
|
});
|
|
7919
7791
|
if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
|
|
7920
7792
|
return res.data;
|
|
7921
7793
|
}
|
|
7922
7794
|
async publicJsonRequest(path8) {
|
|
7923
|
-
const res = await (
|
|
7795
|
+
const res = await requestWithRetry({
|
|
7924
7796
|
url: `${this.baseUrl}${path8}`,
|
|
7925
7797
|
method: "GET",
|
|
7926
7798
|
headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
|
|
7927
|
-
timeout: 6e4
|
|
7928
|
-
validateStatus: () => true
|
|
7799
|
+
timeout: 6e4
|
|
7929
7800
|
});
|
|
7930
7801
|
if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
|
|
7931
7802
|
return res.data;
|
|
7932
7803
|
}
|
|
7933
7804
|
async authenticatedJsonRequest(path8, method, body) {
|
|
7934
|
-
if (!this.account)
|
|
7805
|
+
if (!this.walletClient?.account) {
|
|
7806
|
+
throw new Error("WalletClient with account required for authenticated requests");
|
|
7807
|
+
}
|
|
7935
7808
|
const headers = {
|
|
7936
7809
|
"Content-Type": "application/json"
|
|
7937
7810
|
};
|
|
7938
7811
|
if (this.clientId) headers["x-client-id"] = this.clientId;
|
|
7939
7812
|
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
|
|
7940
7813
|
const { signature } = await calculateBillingAuthSignature({
|
|
7941
|
-
|
|
7814
|
+
walletClient: this.walletClient,
|
|
7942
7815
|
product: "compute",
|
|
7943
7816
|
expiry
|
|
7944
7817
|
});
|
|
7945
7818
|
headers.Authorization = `Bearer ${signature}`;
|
|
7946
7819
|
headers["X-eigenx-expiry"] = expiry.toString();
|
|
7947
|
-
headers["X-Account"] = this.
|
|
7948
|
-
const res = await (
|
|
7820
|
+
headers["X-Account"] = this.address;
|
|
7821
|
+
const res = await requestWithRetry({
|
|
7949
7822
|
url: `${this.baseUrl}${path8}`,
|
|
7950
7823
|
method,
|
|
7951
7824
|
headers,
|
|
7952
7825
|
data: body,
|
|
7953
|
-
timeout: 6e4
|
|
7954
|
-
validateStatus: () => true
|
|
7826
|
+
timeout: 6e4
|
|
7955
7827
|
});
|
|
7956
7828
|
if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
|
|
7957
7829
|
return res.data;
|
|
7958
7830
|
}
|
|
7959
7831
|
async authenticatedTextRequest(path8) {
|
|
7960
|
-
if (!this.account)
|
|
7832
|
+
if (!this.walletClient?.account) {
|
|
7833
|
+
throw new Error("WalletClient with account required for authenticated requests");
|
|
7834
|
+
}
|
|
7961
7835
|
const headers = {};
|
|
7962
7836
|
if (this.clientId) headers["x-client-id"] = this.clientId;
|
|
7963
7837
|
const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
|
|
7964
7838
|
const { signature } = await calculateBillingAuthSignature({
|
|
7965
|
-
|
|
7839
|
+
walletClient: this.walletClient,
|
|
7966
7840
|
product: "compute",
|
|
7967
7841
|
expiry
|
|
7968
7842
|
});
|
|
7969
7843
|
headers.Authorization = `Bearer ${signature}`;
|
|
7970
7844
|
headers["X-eigenx-expiry"] = expiry.toString();
|
|
7971
|
-
headers["X-Account"] = this.
|
|
7972
|
-
const res = await (
|
|
7845
|
+
headers["X-Account"] = this.address;
|
|
7846
|
+
const res = await requestWithRetry({
|
|
7973
7847
|
url: `${this.baseUrl}${path8}`,
|
|
7974
7848
|
method: "GET",
|
|
7975
7849
|
headers,
|
|
7976
7850
|
timeout: 6e4,
|
|
7977
|
-
responseType: "text"
|
|
7978
|
-
validateStatus: () => true
|
|
7851
|
+
responseType: "text"
|
|
7979
7852
|
});
|
|
7980
7853
|
if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
|
|
7981
7854
|
return typeof res.data === "string" ? res.data : JSON.stringify(res.data);
|
|
@@ -8050,13 +7923,13 @@ var BadRequestError = class extends BuildError {
|
|
|
8050
7923
|
var DEFAULT_POLL_INTERVAL = 2e3;
|
|
8051
7924
|
var DEFAULT_TIMEOUT = 30 * 60 * 1e3;
|
|
8052
7925
|
function createBuildModule(config) {
|
|
8053
|
-
const { verbose = false, skipTelemetry = false } = config;
|
|
7926
|
+
const { verbose = false, skipTelemetry = false, walletClient } = config;
|
|
8054
7927
|
const logger = getLogger(verbose);
|
|
8055
7928
|
const environment = config.environment || "sepolia";
|
|
8056
7929
|
const environmentConfig = getEnvironmentConfig(environment);
|
|
8057
7930
|
const api = new BuildApiClient({
|
|
8058
7931
|
baseUrl: environmentConfig.userApiServerURL,
|
|
8059
|
-
|
|
7932
|
+
walletClient,
|
|
8060
7933
|
clientId: config.clientId
|
|
8061
7934
|
});
|
|
8062
7935
|
return {
|
|
@@ -8068,7 +7941,7 @@ function createBuildModule(config) {
|
|
|
8068
7941
|
properties: { environment, repoUrl: request.repoUrl }
|
|
8069
7942
|
},
|
|
8070
7943
|
async () => {
|
|
8071
|
-
if (!
|
|
7944
|
+
if (!walletClient) throw new AuthRequiredError("walletClient required for submit()");
|
|
8072
7945
|
const data = await api.submitBuild({
|
|
8073
7946
|
repo_url: request.repoUrl,
|
|
8074
7947
|
git_ref: request.gitRef,
|
|
@@ -8127,7 +8000,7 @@ function createBuildModule(config) {
|
|
|
8127
8000
|
return withSDKTelemetry(
|
|
8128
8001
|
{ functionName: "build.getLogs", skipTelemetry, properties: { environment, buildId } },
|
|
8129
8002
|
async () => {
|
|
8130
|
-
if (!
|
|
8003
|
+
if (!walletClient) throw new AuthRequiredError("walletClient required for getLogs()");
|
|
8131
8004
|
return api.getLogs(buildId);
|
|
8132
8005
|
}
|
|
8133
8006
|
);
|
|
@@ -8164,7 +8037,7 @@ function createBuildModule(config) {
|
|
|
8164
8037
|
if (build.status === BUILD_STATUS.FAILED) {
|
|
8165
8038
|
throw new BuildFailedError(build.errorMessage ?? "Build failed", buildId);
|
|
8166
8039
|
}
|
|
8167
|
-
await
|
|
8040
|
+
await sleep3(pollIntervalMs);
|
|
8168
8041
|
}
|
|
8169
8042
|
},
|
|
8170
8043
|
async *streamLogs(buildId, pollIntervalMs = DEFAULT_POLL_INTERVAL) {
|
|
@@ -8186,12 +8059,12 @@ function createBuildModule(config) {
|
|
|
8186
8059
|
lastLength = logs2.length;
|
|
8187
8060
|
}
|
|
8188
8061
|
if (build.status !== BUILD_STATUS.BUILDING) break;
|
|
8189
|
-
await
|
|
8062
|
+
await sleep3(pollIntervalMs);
|
|
8190
8063
|
}
|
|
8191
8064
|
}
|
|
8192
8065
|
};
|
|
8193
8066
|
}
|
|
8194
|
-
function
|
|
8067
|
+
function sleep3(ms) {
|
|
8195
8068
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
8196
8069
|
}
|
|
8197
8070
|
function transformBuild(raw) {
|
|
@@ -8235,14 +8108,321 @@ function transformVerifyResult(raw) {
|
|
|
8235
8108
|
};
|
|
8236
8109
|
}
|
|
8237
8110
|
|
|
8111
|
+
// src/client/common/auth/keyring.ts
|
|
8112
|
+
var import_keyring = require("@napi-rs/keyring");
|
|
8113
|
+
var import_accounts2 = require("viem/accounts");
|
|
8114
|
+
var SERVICE_NAME = "ecloud";
|
|
8115
|
+
var ACCOUNT_NAME = "key";
|
|
8116
|
+
var EIGENX_SERVICE_NAME = "eigenx-cli";
|
|
8117
|
+
var EIGENX_DEV_SERVICE_NAME = "eigenx-cli-dev";
|
|
8118
|
+
var EIGENX_ACCOUNT_PREFIX = "eigenx-";
|
|
8119
|
+
var GO_KEYRING_BASE64_PREFIX = "go-keyring-base64:";
|
|
8120
|
+
var GO_KEYRING_ENCODED_PREFIX = "go-keyring-encoded:";
|
|
8121
|
+
async function storePrivateKey(privateKey) {
|
|
8122
|
+
const normalizedKey = normalizePrivateKey(privateKey);
|
|
8123
|
+
const isValid = validatePrivateKey(normalizedKey);
|
|
8124
|
+
if (!isValid) {
|
|
8125
|
+
throw new Error("Invalid private key format");
|
|
8126
|
+
}
|
|
8127
|
+
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
8128
|
+
try {
|
|
8129
|
+
await entry.setPassword(normalizedKey);
|
|
8130
|
+
} catch (err) {
|
|
8131
|
+
throw new Error(
|
|
8132
|
+
`Failed to store key in OS keyring: ${err?.message ?? err}. Ensure keyring service is available.`
|
|
8133
|
+
);
|
|
8134
|
+
}
|
|
8135
|
+
}
|
|
8136
|
+
async function getPrivateKey() {
|
|
8137
|
+
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
8138
|
+
try {
|
|
8139
|
+
const key = await entry.getPassword();
|
|
8140
|
+
if (key && validatePrivateKey(key)) {
|
|
8141
|
+
return key;
|
|
8142
|
+
}
|
|
8143
|
+
} catch {
|
|
8144
|
+
}
|
|
8145
|
+
return null;
|
|
8146
|
+
}
|
|
8147
|
+
async function deletePrivateKey() {
|
|
8148
|
+
const entry = new import_keyring.AsyncEntry(SERVICE_NAME, ACCOUNT_NAME);
|
|
8149
|
+
try {
|
|
8150
|
+
await entry.deletePassword();
|
|
8151
|
+
return true;
|
|
8152
|
+
} catch {
|
|
8153
|
+
console.warn("No key found in keyring");
|
|
8154
|
+
return false;
|
|
8155
|
+
}
|
|
8156
|
+
}
|
|
8157
|
+
async function listStoredKeys() {
|
|
8158
|
+
const keys = [];
|
|
8159
|
+
const creds = (0, import_keyring.findCredentials)(SERVICE_NAME);
|
|
8160
|
+
for (const cred of creds) {
|
|
8161
|
+
if (cred.account === ACCOUNT_NAME) {
|
|
8162
|
+
try {
|
|
8163
|
+
const address = getAddressFromPrivateKey(cred.password);
|
|
8164
|
+
keys.push({ address });
|
|
8165
|
+
} catch (err) {
|
|
8166
|
+
console.warn(`Warning: Invalid key found, skipping: ${err}`);
|
|
8167
|
+
}
|
|
8168
|
+
}
|
|
8169
|
+
}
|
|
8170
|
+
return keys;
|
|
8171
|
+
}
|
|
8172
|
+
async function keyExists() {
|
|
8173
|
+
const key = await getPrivateKey();
|
|
8174
|
+
return key !== null;
|
|
8175
|
+
}
|
|
8176
|
+
async function getLegacyKeys() {
|
|
8177
|
+
const keys = [];
|
|
8178
|
+
try {
|
|
8179
|
+
const eigenxCreds = (0, import_keyring.findCredentials)(EIGENX_SERVICE_NAME);
|
|
8180
|
+
for (const cred of eigenxCreds) {
|
|
8181
|
+
const accountName = cred.account;
|
|
8182
|
+
if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
|
|
8183
|
+
continue;
|
|
8184
|
+
}
|
|
8185
|
+
const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
|
|
8186
|
+
try {
|
|
8187
|
+
const decodedKey = decodeGoKeyringValue(cred.password);
|
|
8188
|
+
const address = getAddressFromPrivateKey(decodedKey);
|
|
8189
|
+
keys.push({ environment, address, source: "eigenx" });
|
|
8190
|
+
} catch (err) {
|
|
8191
|
+
console.warn(
|
|
8192
|
+
`Warning: Invalid key found for ${environment} (eigenx-cli), skipping: ${err}`
|
|
8193
|
+
);
|
|
8194
|
+
}
|
|
8195
|
+
}
|
|
8196
|
+
} catch {
|
|
8197
|
+
}
|
|
8198
|
+
try {
|
|
8199
|
+
const eigenxDevCreds = (0, import_keyring.findCredentials)(EIGENX_DEV_SERVICE_NAME);
|
|
8200
|
+
for (const cred of eigenxDevCreds) {
|
|
8201
|
+
const accountName = cred.account;
|
|
8202
|
+
if (!accountName.startsWith(EIGENX_ACCOUNT_PREFIX)) {
|
|
8203
|
+
continue;
|
|
8204
|
+
}
|
|
8205
|
+
const environment = accountName.substring(EIGENX_ACCOUNT_PREFIX.length);
|
|
8206
|
+
try {
|
|
8207
|
+
const decodedKey = decodeGoKeyringValue(cred.password);
|
|
8208
|
+
const address = getAddressFromPrivateKey(decodedKey);
|
|
8209
|
+
keys.push({ environment, address, source: "eigenx-dev" });
|
|
8210
|
+
} catch (err) {
|
|
8211
|
+
console.warn(
|
|
8212
|
+
`Warning: Invalid key found for ${environment} (eigenx-dev), skipping: ${err}`
|
|
8213
|
+
);
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
} catch {
|
|
8217
|
+
}
|
|
8218
|
+
return keys;
|
|
8219
|
+
}
|
|
8220
|
+
async function getLegacyPrivateKey(environment, source) {
|
|
8221
|
+
const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
|
|
8222
|
+
const accountName = EIGENX_ACCOUNT_PREFIX + environment;
|
|
8223
|
+
const entry = new import_keyring.AsyncEntry(serviceName, accountName);
|
|
8224
|
+
try {
|
|
8225
|
+
const rawKey = await entry.getPassword();
|
|
8226
|
+
if (rawKey) {
|
|
8227
|
+
const decodedKey = decodeGoKeyringValue(rawKey);
|
|
8228
|
+
if (validatePrivateKey(decodedKey)) {
|
|
8229
|
+
return decodedKey;
|
|
8230
|
+
}
|
|
8231
|
+
}
|
|
8232
|
+
} catch {
|
|
8233
|
+
}
|
|
8234
|
+
return null;
|
|
8235
|
+
}
|
|
8236
|
+
async function deleteLegacyPrivateKey(environment, source) {
|
|
8237
|
+
const serviceName = source === "eigenx" ? EIGENX_SERVICE_NAME : EIGENX_DEV_SERVICE_NAME;
|
|
8238
|
+
const accountName = EIGENX_ACCOUNT_PREFIX + environment;
|
|
8239
|
+
const entry = new import_keyring.AsyncEntry(serviceName, accountName);
|
|
8240
|
+
try {
|
|
8241
|
+
await entry.deletePassword();
|
|
8242
|
+
return true;
|
|
8243
|
+
} catch {
|
|
8244
|
+
console.warn(`No key found for ${environment} in ${source}`);
|
|
8245
|
+
return false;
|
|
8246
|
+
}
|
|
8247
|
+
}
|
|
8248
|
+
function validatePrivateKey(privateKey) {
|
|
8249
|
+
try {
|
|
8250
|
+
getAddressFromPrivateKey(privateKey);
|
|
8251
|
+
return true;
|
|
8252
|
+
} catch {
|
|
8253
|
+
return false;
|
|
8254
|
+
}
|
|
8255
|
+
}
|
|
8256
|
+
function getAddressFromPrivateKey(privateKey) {
|
|
8257
|
+
const normalized = normalizePrivateKey(privateKey);
|
|
8258
|
+
return (0, import_accounts2.privateKeyToAddress)(normalized);
|
|
8259
|
+
}
|
|
8260
|
+
function decodeGoKeyringValue(rawValue) {
|
|
8261
|
+
if (rawValue.startsWith(GO_KEYRING_BASE64_PREFIX)) {
|
|
8262
|
+
const encoded = rawValue.substring(GO_KEYRING_BASE64_PREFIX.length);
|
|
8263
|
+
try {
|
|
8264
|
+
const decoded = Buffer.from(encoded, "base64").toString("utf8");
|
|
8265
|
+
return decoded;
|
|
8266
|
+
} catch (err) {
|
|
8267
|
+
console.warn(`Warning: Failed to decode go-keyring base64 value: ${err}`);
|
|
8268
|
+
return rawValue;
|
|
8269
|
+
}
|
|
8270
|
+
}
|
|
8271
|
+
if (rawValue.startsWith(GO_KEYRING_ENCODED_PREFIX)) {
|
|
8272
|
+
const encoded = rawValue.substring(GO_KEYRING_ENCODED_PREFIX.length);
|
|
8273
|
+
try {
|
|
8274
|
+
const decoded = Buffer.from(encoded, "hex").toString("utf8");
|
|
8275
|
+
return decoded;
|
|
8276
|
+
} catch (err) {
|
|
8277
|
+
console.warn(`Warning: Failed to decode go-keyring hex value: ${err}`);
|
|
8278
|
+
return rawValue;
|
|
8279
|
+
}
|
|
8280
|
+
}
|
|
8281
|
+
return rawValue;
|
|
8282
|
+
}
|
|
8283
|
+
function normalizePrivateKey(privateKey) {
|
|
8284
|
+
if (!privateKey.startsWith("0x")) {
|
|
8285
|
+
return `0x${privateKey}`;
|
|
8286
|
+
}
|
|
8287
|
+
return privateKey;
|
|
8288
|
+
}
|
|
8289
|
+
|
|
8290
|
+
// src/client/common/auth/resolver.ts
|
|
8291
|
+
async function getPrivateKeyWithSource(options) {
|
|
8292
|
+
if (options.privateKey) {
|
|
8293
|
+
if (!validatePrivateKey(options.privateKey)) {
|
|
8294
|
+
throw new Error(
|
|
8295
|
+
"Invalid private key format provided via command flag. Please check and try again."
|
|
8296
|
+
);
|
|
8297
|
+
}
|
|
8298
|
+
return {
|
|
8299
|
+
key: options.privateKey,
|
|
8300
|
+
source: "command flag"
|
|
8301
|
+
};
|
|
8302
|
+
}
|
|
8303
|
+
const envKey = process.env.ECLOUD_PRIVATE_KEY;
|
|
8304
|
+
if (envKey) {
|
|
8305
|
+
if (!validatePrivateKey(envKey)) {
|
|
8306
|
+
throw new Error(
|
|
8307
|
+
"Invalid private key format provided via environment variable. Please check and try again."
|
|
8308
|
+
);
|
|
8309
|
+
}
|
|
8310
|
+
return {
|
|
8311
|
+
key: envKey,
|
|
8312
|
+
source: "environment variable (ECLOUD_PRIVATE_KEY)"
|
|
8313
|
+
};
|
|
8314
|
+
}
|
|
8315
|
+
const keyringKey = await getPrivateKey();
|
|
8316
|
+
if (keyringKey) {
|
|
8317
|
+
return {
|
|
8318
|
+
key: keyringKey,
|
|
8319
|
+
source: "stored credentials"
|
|
8320
|
+
};
|
|
8321
|
+
}
|
|
8322
|
+
return null;
|
|
8323
|
+
}
|
|
8324
|
+
async function requirePrivateKey(options) {
|
|
8325
|
+
const result = await getPrivateKeyWithSource({
|
|
8326
|
+
privateKey: options.privateKey
|
|
8327
|
+
});
|
|
8328
|
+
if (!result) {
|
|
8329
|
+
throw new Error(
|
|
8330
|
+
`Private key required. Please provide it via:
|
|
8331
|
+
\u2022 Keyring: ecloud auth login
|
|
8332
|
+
\u2022 Flag: --private-key YOUR_KEY
|
|
8333
|
+
\u2022 Environment: export ECLOUD_PRIVATE_KEY=YOUR_KEY`
|
|
8334
|
+
);
|
|
8335
|
+
}
|
|
8336
|
+
return result;
|
|
8337
|
+
}
|
|
8338
|
+
|
|
8339
|
+
// src/client/common/auth/generate.ts
|
|
8340
|
+
var import_accounts3 = require("viem/accounts");
|
|
8341
|
+
function generateNewPrivateKey() {
|
|
8342
|
+
const privateKey = (0, import_accounts3.generatePrivateKey)();
|
|
8343
|
+
const address = (0, import_accounts3.privateKeyToAddress)(privateKey);
|
|
8344
|
+
return {
|
|
8345
|
+
privateKey,
|
|
8346
|
+
address
|
|
8347
|
+
};
|
|
8348
|
+
}
|
|
8349
|
+
|
|
8350
|
+
// src/client/common/auth/siwe.ts
|
|
8351
|
+
var import_siwe = require("siwe");
|
|
8352
|
+
var generateNonce = import_siwe.generateNonce;
|
|
8353
|
+
function createSiweMessage(params) {
|
|
8354
|
+
const now = /* @__PURE__ */ new Date();
|
|
8355
|
+
const nonce = params.nonce || generateNonce();
|
|
8356
|
+
const issuedAt = params.issuedAt || now;
|
|
8357
|
+
const expirationTime = params.expirationTime || new Date(now.getTime() + 24 * 60 * 60 * 1e3);
|
|
8358
|
+
const siweMessage = new import_siwe.SiweMessage({
|
|
8359
|
+
domain: params.domain,
|
|
8360
|
+
address: params.address,
|
|
8361
|
+
statement: params.statement,
|
|
8362
|
+
uri: params.uri,
|
|
8363
|
+
version: "1",
|
|
8364
|
+
chainId: params.chainId,
|
|
8365
|
+
nonce,
|
|
8366
|
+
issuedAt: issuedAt.toISOString(),
|
|
8367
|
+
expirationTime: expirationTime.toISOString(),
|
|
8368
|
+
notBefore: params.notBefore?.toISOString(),
|
|
8369
|
+
requestId: params.requestId,
|
|
8370
|
+
resources: params.resources
|
|
8371
|
+
});
|
|
8372
|
+
return {
|
|
8373
|
+
message: siweMessage.prepareMessage(),
|
|
8374
|
+
params: {
|
|
8375
|
+
address: params.address,
|
|
8376
|
+
chainId: params.chainId,
|
|
8377
|
+
domain: params.domain,
|
|
8378
|
+
uri: params.uri,
|
|
8379
|
+
nonce,
|
|
8380
|
+
issuedAt,
|
|
8381
|
+
statement: params.statement,
|
|
8382
|
+
expirationTime,
|
|
8383
|
+
notBefore: params.notBefore,
|
|
8384
|
+
requestId: params.requestId,
|
|
8385
|
+
resources: params.resources
|
|
8386
|
+
}
|
|
8387
|
+
};
|
|
8388
|
+
}
|
|
8389
|
+
function parseSiweMessage(message) {
|
|
8390
|
+
try {
|
|
8391
|
+
const siweMessage = new import_siwe.SiweMessage(message);
|
|
8392
|
+
return {
|
|
8393
|
+
address: siweMessage.address,
|
|
8394
|
+
chainId: siweMessage.chainId,
|
|
8395
|
+
domain: siweMessage.domain,
|
|
8396
|
+
uri: siweMessage.uri,
|
|
8397
|
+
nonce: siweMessage.nonce,
|
|
8398
|
+
statement: siweMessage.statement,
|
|
8399
|
+
issuedAt: siweMessage.issuedAt ? new Date(siweMessage.issuedAt) : void 0,
|
|
8400
|
+
expirationTime: siweMessage.expirationTime ? new Date(siweMessage.expirationTime) : void 0,
|
|
8401
|
+
notBefore: siweMessage.notBefore ? new Date(siweMessage.notBefore) : void 0,
|
|
8402
|
+
requestId: siweMessage.requestId,
|
|
8403
|
+
resources: siweMessage.resources
|
|
8404
|
+
};
|
|
8405
|
+
} catch {
|
|
8406
|
+
return null;
|
|
8407
|
+
}
|
|
8408
|
+
}
|
|
8409
|
+
function isSiweMessageExpired(params) {
|
|
8410
|
+
if (!params.expirationTime) return false;
|
|
8411
|
+
return /* @__PURE__ */ new Date() > params.expirationTime;
|
|
8412
|
+
}
|
|
8413
|
+
function isSiweMessageNotYetValid(params) {
|
|
8414
|
+
if (!params.notBefore) return false;
|
|
8415
|
+
return /* @__PURE__ */ new Date() < params.notBefore;
|
|
8416
|
+
}
|
|
8417
|
+
|
|
8238
8418
|
// src/client/common/utils/instance.ts
|
|
8239
8419
|
async function getCurrentInstanceType(preflightCtx, appID, logger, clientId) {
|
|
8240
8420
|
try {
|
|
8241
8421
|
const userApiClient = new UserApiClient(
|
|
8242
8422
|
preflightCtx.environmentConfig,
|
|
8243
|
-
preflightCtx.
|
|
8244
|
-
preflightCtx.
|
|
8245
|
-
clientId
|
|
8423
|
+
preflightCtx.walletClient,
|
|
8424
|
+
preflightCtx.publicClient,
|
|
8425
|
+
clientId ? { clientId } : void 0
|
|
8246
8426
|
);
|
|
8247
8427
|
const infos = await userApiClient.getInfos([appID], 1);
|
|
8248
8428
|
if (infos.length === 0) {
|
|
@@ -8274,16 +8454,21 @@ function createECloudClient(cfg) {
|
|
|
8274
8454
|
`RPC URL is required. Provide via options.rpcUrl, RPC_URL env var, or ensure environment has default RPC URL`
|
|
8275
8455
|
);
|
|
8276
8456
|
}
|
|
8457
|
+
const { walletClient, publicClient } = createClients({
|
|
8458
|
+
privateKey: cfg.privateKey,
|
|
8459
|
+
rpcUrl,
|
|
8460
|
+
chainId: environmentConfig.chainID
|
|
8461
|
+
});
|
|
8277
8462
|
return {
|
|
8278
8463
|
compute: createComputeModule({
|
|
8279
|
-
rpcUrl,
|
|
8280
8464
|
verbose: cfg.verbose,
|
|
8281
|
-
|
|
8465
|
+
walletClient,
|
|
8466
|
+
publicClient,
|
|
8282
8467
|
environment: cfg.environment
|
|
8283
8468
|
}),
|
|
8284
8469
|
billing: createBillingModule({
|
|
8285
8470
|
verbose: cfg.verbose,
|
|
8286
|
-
|
|
8471
|
+
walletClient
|
|
8287
8472
|
})
|
|
8288
8473
|
};
|
|
8289
8474
|
}
|
|
@@ -8292,6 +8477,7 @@ function createECloudClient(cfg) {
|
|
|
8292
8477
|
AuthRequiredError,
|
|
8293
8478
|
BUILD_STATUS,
|
|
8294
8479
|
BadRequestError,
|
|
8480
|
+
BillingApiClient,
|
|
8295
8481
|
BuildError,
|
|
8296
8482
|
BuildFailedError,
|
|
8297
8483
|
ConflictError,
|
|
@@ -8300,6 +8486,7 @@ function createECloudClient(cfg) {
|
|
|
8300
8486
|
NotFoundError,
|
|
8301
8487
|
PRIMARY_LANGUAGES,
|
|
8302
8488
|
PostHogClient,
|
|
8489
|
+
SessionError,
|
|
8303
8490
|
TimeoutError,
|
|
8304
8491
|
UserApiClient,
|
|
8305
8492
|
addHexPrefix,
|
|
@@ -8316,7 +8503,9 @@ function createECloudClient(cfg) {
|
|
|
8316
8503
|
createComputeModule,
|
|
8317
8504
|
createECloudClient,
|
|
8318
8505
|
createMetricsContext,
|
|
8506
|
+
createSiweMessage,
|
|
8319
8507
|
createTelemetryClient,
|
|
8508
|
+
createViemClients,
|
|
8320
8509
|
deleteLegacyPrivateKey,
|
|
8321
8510
|
deletePrivateKey,
|
|
8322
8511
|
emitMetrics,
|
|
@@ -8331,14 +8520,18 @@ function createECloudClient(cfg) {
|
|
|
8331
8520
|
fetchTemplateCatalog,
|
|
8332
8521
|
formatETH,
|
|
8333
8522
|
generateNewPrivateKey,
|
|
8523
|
+
generateNonce,
|
|
8334
8524
|
getAddressFromPrivateKey,
|
|
8335
8525
|
getAllAppsByDeveloper,
|
|
8336
8526
|
getAppLatestReleaseBlockNumbers,
|
|
8337
8527
|
getAvailableEnvironments,
|
|
8338
8528
|
getAvailableTemplates,
|
|
8529
|
+
getBillingEnvironmentConfig,
|
|
8339
8530
|
getBlockTimestamps,
|
|
8340
8531
|
getBuildType,
|
|
8341
8532
|
getCategoryDescriptions,
|
|
8533
|
+
getChainFromID,
|
|
8534
|
+
getComputeApiSession,
|
|
8342
8535
|
getCurrentInstanceType,
|
|
8343
8536
|
getEnvironmentConfig,
|
|
8344
8537
|
getLegacyKeys,
|
|
@@ -8351,10 +8544,17 @@ function createECloudClient(cfg) {
|
|
|
8351
8544
|
isEnvironmentAvailable,
|
|
8352
8545
|
isMainnet,
|
|
8353
8546
|
isNoopClient,
|
|
8547
|
+
isSessionValid,
|
|
8548
|
+
isSiweMessageExpired,
|
|
8549
|
+
isSiweMessageNotYetValid,
|
|
8354
8550
|
isSubscriptionActive,
|
|
8355
8551
|
keyExists,
|
|
8356
8552
|
listStoredKeys,
|
|
8553
|
+
loginToComputeApi,
|
|
8554
|
+
logoutFromComputeApi,
|
|
8357
8555
|
logs,
|
|
8556
|
+
noopLogger,
|
|
8557
|
+
parseSiweMessage,
|
|
8358
8558
|
prepareDeploy,
|
|
8359
8559
|
prepareDeployFromVerifiableBuild,
|
|
8360
8560
|
prepareUpgrade,
|