@layr-labs/ecloud-sdk 0.2.1-dev → 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/dist/browser.cjs CHANGED
@@ -30,31 +30,69 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/browser.ts
31
31
  var browser_exports = {};
32
32
  __export(browser_exports, {
33
+ BillingApiClient: () => BillingApiClient,
34
+ BuildApiClient: () => BuildApiClient,
35
+ SessionError: () => SessionError,
33
36
  UserApiClient: () => UserApiClient,
37
+ addHexPrefix: () => addHexPrefix,
34
38
  assertValidImageReference: () => assertValidImageReference,
35
39
  assertValidPrivateKey: () => assertValidPrivateKey,
40
+ calculateAppID: () => calculateAppID,
41
+ checkERC7702Delegation: () => checkERC7702Delegation,
42
+ createSiweMessage: () => createSiweMessage,
43
+ deployApp: () => deployApp,
36
44
  encodeStartAppData: () => encodeStartAppData,
37
45
  encodeStopAppData: () => encodeStopAppData,
38
46
  encodeTerminateAppData: () => encodeTerminateAppData,
47
+ encryptRSAOAEPAndAES256GCM: () => encryptRSAOAEPAndAES256GCM,
39
48
  estimateBatchGas: () => estimateBatchGas,
40
49
  estimateTransactionGas: () => estimateTransactionGas,
50
+ executeBatch: () => executeBatch,
51
+ executeDeployBatch: () => executeDeployBatch,
52
+ executeDeployBatched: () => executeDeployBatched,
53
+ executeDeploySequential: () => executeDeploySequential,
54
+ executeUpgradeBatch: () => executeUpgradeBatch,
41
55
  extractAppNameFromImage: () => extractAppNameFromImage,
42
56
  formatETH: () => formatETH,
43
57
  generateNewPrivateKey: () => generateNewPrivateKey,
58
+ generateNonce: () => generateNonce,
44
59
  getActiveAppCount: () => getActiveAppCount,
45
60
  getAllAppsByDeveloper: () => getAllAppsByDeveloper,
61
+ getAppProtectedHeaders: () => getAppProtectedHeaders,
46
62
  getAppsByCreator: () => getAppsByCreator,
47
63
  getAppsByDeveloper: () => getAppsByDeveloper,
48
64
  getAvailableEnvironments: () => getAvailableEnvironments,
65
+ getBillingEnvironmentConfig: () => getBillingEnvironmentConfig,
49
66
  getBuildType: () => getBuildType,
67
+ getChainFromID: () => getChainFromID,
68
+ getComputeApiSession: () => getComputeApiSession,
50
69
  getEnvironmentConfig: () => getEnvironmentConfig,
70
+ getKMSKeysForEnvironment: () => getKMSKeysForEnvironment,
51
71
  getMaxActiveAppsPerUser: () => getMaxActiveAppsPerUser,
72
+ isDelegated: () => isDelegated,
52
73
  isEnvironmentAvailable: () => isEnvironmentAvailable,
53
74
  isMainnet: () => isMainnet,
75
+ isSessionValid: () => isSessionValid,
76
+ isSiweMessageExpired: () => isSiweMessageExpired,
77
+ isSiweMessageNotYetValid: () => isSiweMessageNotYetValid,
54
78
  isSubscriptionActive: () => isSubscriptionActive,
79
+ loginToComputeApi: () => loginToComputeApi,
80
+ logoutFromComputeApi: () => logoutFromComputeApi,
81
+ noopLogger: () => noopLogger,
82
+ parseSiweMessage: () => parseSiweMessage,
83
+ prepareDeployBatch: () => prepareDeployBatch,
84
+ prepareUpgradeBatch: () => prepareUpgradeBatch,
55
85
  sanitizeString: () => sanitizeString,
56
86
  sanitizeURL: () => sanitizeURL,
57
87
  sanitizeXURL: () => sanitizeXURL,
88
+ sendAndWaitForTransaction: () => sendAndWaitForTransaction,
89
+ stripHexPrefix: () => stripHexPrefix,
90
+ supportsEIP5792: () => supportsEIP5792,
91
+ supportsEIP7702: () => supportsEIP7702,
92
+ suspend: () => suspend,
93
+ undelegate: () => undelegate,
94
+ upgradeApp: () => upgradeApp,
95
+ useComputeSession: () => useComputeSession,
58
96
  validateAppID: () => validateAppID,
59
97
  validateAppName: () => validateAppName,
60
98
  validateCreateAppParams: () => validateCreateAppParams,
@@ -69,6 +107,18 @@ __export(browser_exports, {
69
107
  });
70
108
  module.exports = __toCommonJS(browser_exports);
71
109
 
110
+ // src/client/common/types/index.ts
111
+ var noopLogger = {
112
+ debug: () => {
113
+ },
114
+ info: () => {
115
+ },
116
+ warn: () => {
117
+ },
118
+ error: () => {
119
+ }
120
+ };
121
+
72
122
  // src/client/common/config/environment.ts
73
123
  var SEPOLIA_CHAIN_ID = 11155111;
74
124
  var MAINNET_CHAIN_ID = 1;
@@ -83,6 +133,14 @@ var ChainAddresses = {
83
133
  PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
84
134
  }
85
135
  };
136
+ var BILLING_ENVIRONMENTS = {
137
+ dev: {
138
+ billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
139
+ },
140
+ prod: {
141
+ billingApiServerURL: "https://billingapi.eigencloud.xyz"
142
+ }
143
+ };
86
144
  var ENVIRONMENTS = {
87
145
  "sepolia-dev": {
88
146
  name: "sepolia",
@@ -141,6 +199,13 @@ function getEnvironmentConfig(environment, chainID) {
141
199
  chainID: BigInt(resolvedChainID)
142
200
  };
143
201
  }
202
+ function getBillingEnvironmentConfig(build) {
203
+ const config = BILLING_ENVIRONMENTS[build];
204
+ if (!config) {
205
+ throw new Error(`Unknown billing environment: ${build}`);
206
+ }
207
+ return config;
208
+ }
144
209
  function getBuildType() {
145
210
  const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
146
211
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
@@ -174,8 +239,13 @@ var import_accounts = require("viem/accounts");
174
239
 
175
240
  // src/client/common/constants.ts
176
241
  var import_chains = require("viem/chains");
242
+ var SUPPORTED_CHAINS = [import_chains.mainnet, import_chains.sepolia];
177
243
 
178
244
  // src/client/common/utils/helpers.ts
245
+ function getChainFromID(chainID, fallback = import_chains2.sepolia) {
246
+ const id = Number(chainID);
247
+ return (0, import_viem.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
248
+ }
179
249
  function addHexPrefix(value) {
180
250
  return value.startsWith("0x") ? value : `0x${value}`;
181
251
  }
@@ -409,6 +479,160 @@ async function calculatePermissionSignature(options) {
409
479
  });
410
480
  return { signature, digest };
411
481
  }
482
+ var generateBillingSigData = (product, expiry) => {
483
+ return {
484
+ domain: {
485
+ name: "EigenCloud Billing API",
486
+ version: "1"
487
+ },
488
+ types: {
489
+ BillingAuth: [
490
+ { name: "product", type: "string" },
491
+ { name: "expiry", type: "uint256" }
492
+ ]
493
+ },
494
+ primaryType: "BillingAuth",
495
+ message: {
496
+ product,
497
+ expiry
498
+ }
499
+ };
500
+ };
501
+ async function calculateBillingAuthSignature(options) {
502
+ const { walletClient, product, expiry } = options;
503
+ const account = walletClient.account;
504
+ if (!account) {
505
+ throw new Error("WalletClient must have an account attached");
506
+ }
507
+ const signature = await walletClient.signTypedData({
508
+ account,
509
+ ...generateBillingSigData(product, expiry)
510
+ });
511
+ return { signature, expiry };
512
+ }
513
+
514
+ // src/client/common/auth/session.ts
515
+ var SessionError = class extends Error {
516
+ constructor(message, code, statusCode) {
517
+ super(message);
518
+ this.code = code;
519
+ this.statusCode = statusCode;
520
+ this.name = "SessionError";
521
+ }
522
+ };
523
+ function stripHexPrefix2(hex) {
524
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
525
+ }
526
+ async function parseErrorResponse(response) {
527
+ try {
528
+ const data = await response.json();
529
+ return data.error || response.statusText;
530
+ } catch {
531
+ return response.statusText;
532
+ }
533
+ }
534
+ async function loginToComputeApi(config, request) {
535
+ let response;
536
+ try {
537
+ response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
538
+ method: "POST",
539
+ credentials: "include",
540
+ // Include cookies for session management
541
+ headers: {
542
+ "Content-Type": "application/json"
543
+ },
544
+ body: JSON.stringify({
545
+ message: request.message,
546
+ signature: stripHexPrefix2(request.signature)
547
+ })
548
+ });
549
+ } catch (error) {
550
+ throw new SessionError(
551
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
552
+ "NETWORK_ERROR"
553
+ );
554
+ }
555
+ if (!response.ok) {
556
+ const errorMessage = await parseErrorResponse(response);
557
+ const status = response.status;
558
+ if (status === 400) {
559
+ if (errorMessage.toLowerCase().includes("siwe")) {
560
+ throw new SessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
561
+ }
562
+ throw new SessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
563
+ }
564
+ if (status === 401) {
565
+ throw new SessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
566
+ }
567
+ throw new SessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
568
+ }
569
+ const data = await response.json();
570
+ return {
571
+ success: data.success,
572
+ address: data.address
573
+ };
574
+ }
575
+ async function getComputeApiSession(config) {
576
+ let response;
577
+ try {
578
+ response = await fetch(`${config.baseUrl}/auth/session`, {
579
+ method: "GET",
580
+ credentials: "include",
581
+ // Include cookies for session management
582
+ headers: {
583
+ "Content-Type": "application/json"
584
+ }
585
+ });
586
+ } catch {
587
+ return {
588
+ authenticated: false
589
+ };
590
+ }
591
+ if (response.status === 401) {
592
+ return {
593
+ authenticated: false
594
+ };
595
+ }
596
+ if (!response.ok) {
597
+ const errorMessage = await parseErrorResponse(response);
598
+ throw new SessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
599
+ }
600
+ const data = await response.json();
601
+ return {
602
+ authenticated: data.authenticated,
603
+ address: data.address,
604
+ chainId: data.chain_id
605
+ };
606
+ }
607
+ async function logoutFromComputeApi(config) {
608
+ let response;
609
+ try {
610
+ response = await fetch(`${config.baseUrl}/auth/logout`, {
611
+ method: "POST",
612
+ credentials: "include",
613
+ // Include cookies for session management
614
+ headers: {
615
+ "Content-Type": "application/json"
616
+ }
617
+ });
618
+ } catch (error) {
619
+ throw new SessionError(
620
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
621
+ "NETWORK_ERROR"
622
+ );
623
+ }
624
+ if (response.status === 401) {
625
+ return;
626
+ }
627
+ if (!response.ok) {
628
+ const errorMessage = await parseErrorResponse(response);
629
+ throw new SessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
630
+ }
631
+ }
632
+ async function isSessionValid(config) {
633
+ const session = await getComputeApiSession(config);
634
+ return session.authenticated;
635
+ }
412
636
 
413
637
  // src/client/common/utils/userapi.ts
414
638
  function isJsonObject(value) {
@@ -427,15 +651,16 @@ var CanViewAppLogsPermission = "0x2fd3f2fe";
427
651
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
428
652
  var CanUpdateAppProfilePermission = "0x036fef61";
429
653
  function getDefaultClientId() {
430
- const version = true ? "0.2.1-dev" : "0.0.0";
654
+ const version = true ? "0.2.2-dev" : "0.0.0";
431
655
  return `ecloud-sdk/v${version}`;
432
656
  }
433
657
  var UserApiClient = class {
434
- constructor(config, walletClient, publicClient, clientId) {
658
+ constructor(config, walletClient, publicClient, options) {
435
659
  this.config = config;
436
660
  this.walletClient = walletClient;
437
661
  this.publicClient = publicClient;
438
- this.clientId = clientId || getDefaultClientId();
662
+ this.clientId = options?.clientId || getDefaultClientId();
663
+ this.useSession = options?.useSession ?? false;
439
664
  }
440
665
  /**
441
666
  * Get the address of the connected wallet
@@ -523,7 +748,7 @@ var UserApiClient = class {
523
748
  const apps = result.apps || result.Apps || [];
524
749
  return apps.map((app, i) => ({
525
750
  address: app.address || appIDs[i],
526
- status: app.status || app.Status || ""
751
+ status: app.app_status || app.App_Status || ""
527
752
  }));
528
753
  }
529
754
  /**
@@ -555,9 +780,11 @@ var UserApiClient = class {
555
780
  const headers = {
556
781
  "x-client-id": this.clientId
557
782
  };
558
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
559
- const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
560
- Object.assign(headers, authHeaders);
783
+ if (!this.useSession) {
784
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
785
+ const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
786
+ Object.assign(headers, authHeaders);
787
+ }
561
788
  try {
562
789
  const response = await import_axios.default.post(endpoint, formData, {
563
790
  headers,
@@ -566,8 +793,10 @@ var UserApiClient = class {
566
793
  // Don't throw on any status
567
794
  maxContentLength: Infinity,
568
795
  // Allow large file uploads
569
- maxBodyLength: Infinity
796
+ maxBodyLength: Infinity,
570
797
  // Allow large file uploads
798
+ withCredentials: true
799
+ // Include cookies for session auth
571
800
  });
572
801
  const status = response.status;
573
802
  if (status !== 200 && status !== 201) {
@@ -601,7 +830,7 @@ Please check:
601
830
  const headers = {
602
831
  "x-client-id": this.clientId
603
832
  };
604
- if (permission) {
833
+ if (permission && !this.useSession) {
605
834
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
606
835
  const authHeaders = await this.generateAuthHeaders(permission, expiry);
607
836
  Object.assign(headers, authHeaders);
@@ -610,8 +839,10 @@ Please check:
610
839
  const response = await import_axios.default.get(url, {
611
840
  headers,
612
841
  maxRedirects: 0,
613
- validateStatus: () => true
842
+ validateStatus: () => true,
614
843
  // Don't throw on any status
844
+ withCredentials: true
845
+ // Include cookies for session auth
615
846
  });
616
847
  const status = response.status;
617
848
  const statusText = status >= 200 && status < 300 ? "OK" : "Error";
@@ -653,6 +884,65 @@ Please check:
653
884
  "X-eigenx-expiry": expiry.toString()
654
885
  };
655
886
  }
887
+ // ==========================================================================
888
+ // SIWE Session Management
889
+ // ==========================================================================
890
+ /**
891
+ * Login to the compute API using SIWE (Sign-In with Ethereum)
892
+ *
893
+ * This establishes a session with the compute API by verifying the SIWE message
894
+ * and signature. On success, a session cookie is set in the browser.
895
+ *
896
+ * @param request - Login request containing SIWE message and signature
897
+ * @returns Login result with the authenticated address
898
+ *
899
+ * @example
900
+ * ```typescript
901
+ * import { createSiweMessage } from "@layr-labs/ecloud-sdk/browser";
902
+ *
903
+ * const { message } = createSiweMessage({
904
+ * address: userAddress,
905
+ * chainId: 11155111,
906
+ * domain: window.location.host,
907
+ * uri: window.location.origin,
908
+ * });
909
+ *
910
+ * const signature = await signMessageAsync({ message });
911
+ * const result = await client.siweLogin({ message, signature });
912
+ * ```
913
+ */
914
+ async siweLogin(request) {
915
+ return loginToComputeApi({ baseUrl: this.config.userApiServerURL }, request);
916
+ }
917
+ /**
918
+ * Logout from the compute API
919
+ *
920
+ * This destroys the current session and clears the session cookie.
921
+ *
922
+ * @example
923
+ * ```typescript
924
+ * await client.siweLogout();
925
+ * ```
926
+ */
927
+ async siweLogout() {
928
+ return logoutFromComputeApi({ baseUrl: this.config.userApiServerURL });
929
+ }
930
+ /**
931
+ * Get the current SIWE session status from the compute API
932
+ *
933
+ * @returns Session information including authentication status and address
934
+ *
935
+ * @example
936
+ * ```typescript
937
+ * const session = await client.getSiweSession();
938
+ * if (session.authenticated) {
939
+ * console.log(`Logged in as ${session.address}`);
940
+ * }
941
+ * ```
942
+ */
943
+ async getSiweSession() {
944
+ return getComputeApiSession({ baseUrl: this.config.userApiServerURL });
945
+ }
656
946
  };
657
947
  function transformAppReleaseBuild(raw) {
658
948
  if (!isJsonObject(raw)) return void 0;
@@ -697,6 +987,241 @@ function transformAppRelease(raw) {
697
987
  };
698
988
  }
699
989
 
990
+ // src/client/common/utils/billingapi.ts
991
+ var import_axios2 = __toESM(require("axios"), 1);
992
+ var BillingApiClient = class {
993
+ constructor(config, walletClient) {
994
+ this.config = config;
995
+ this.walletClient = walletClient;
996
+ }
997
+ /**
998
+ * Get the address of the connected wallet
999
+ */
1000
+ get address() {
1001
+ const account = this.walletClient.account;
1002
+ if (!account) {
1003
+ throw new Error("WalletClient must have an account attached");
1004
+ }
1005
+ return account.address;
1006
+ }
1007
+ async createSubscription(productId = "compute", options) {
1008
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1009
+ const body = options ? {
1010
+ success_url: options.successUrl,
1011
+ cancel_url: options.cancelUrl
1012
+ } : void 0;
1013
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId, body);
1014
+ return resp.json();
1015
+ }
1016
+ async getSubscription(productId = "compute") {
1017
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1018
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
1019
+ return resp.json();
1020
+ }
1021
+ async cancelSubscription(productId = "compute") {
1022
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1023
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
1024
+ }
1025
+ /**
1026
+ * Make an authenticated request to the billing API
1027
+ */
1028
+ async makeAuthenticatedRequest(url, method, productId, body) {
1029
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
1030
+ const { signature } = await calculateBillingAuthSignature({
1031
+ walletClient: this.walletClient,
1032
+ product: productId,
1033
+ expiry
1034
+ });
1035
+ const headers = {
1036
+ Authorization: `Bearer ${signature}`,
1037
+ "X-Account": this.address,
1038
+ "X-Expiry": expiry.toString()
1039
+ };
1040
+ if (body) {
1041
+ headers["Content-Type"] = "application/json";
1042
+ }
1043
+ try {
1044
+ const response = await (0, import_axios2.default)({
1045
+ method,
1046
+ url,
1047
+ headers,
1048
+ data: body,
1049
+ timeout: 3e4,
1050
+ maxRedirects: 0,
1051
+ validateStatus: () => true
1052
+ // Don't throw on any status
1053
+ });
1054
+ const status = response.status;
1055
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
1056
+ if (status < 200 || status >= 300) {
1057
+ const body2 = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
1058
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body2}`);
1059
+ }
1060
+ return {
1061
+ json: async () => response.data,
1062
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
1063
+ };
1064
+ } catch (error) {
1065
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
1066
+ const cause = error.cause?.message || error.cause || error.message;
1067
+ throw new Error(
1068
+ `Failed to connect to BillingAPI at ${url}: ${cause}
1069
+ Please check:
1070
+ 1. Your internet connection
1071
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
1072
+ 3. Firewall/proxy settings`
1073
+ );
1074
+ }
1075
+ throw error;
1076
+ }
1077
+ }
1078
+ };
1079
+
1080
+ // src/client/common/utils/buildapi.ts
1081
+ var import_axios3 = __toESM(require("axios"), 1);
1082
+ var MAX_RETRIES = 5;
1083
+ var INITIAL_BACKOFF_MS = 1e3;
1084
+ var MAX_BACKOFF_MS = 3e4;
1085
+ async function sleep(ms) {
1086
+ return new Promise((resolve) => setTimeout(resolve, ms));
1087
+ }
1088
+ function getRetryDelay(res, attempt) {
1089
+ const retryAfter = res.headers["retry-after"];
1090
+ if (retryAfter) {
1091
+ const seconds = parseInt(retryAfter, 10);
1092
+ if (!isNaN(seconds)) {
1093
+ return Math.min(seconds * 1e3, MAX_BACKOFF_MS);
1094
+ }
1095
+ }
1096
+ return Math.min(INITIAL_BACKOFF_MS * Math.pow(2, attempt), MAX_BACKOFF_MS);
1097
+ }
1098
+ async function requestWithRetry(config) {
1099
+ let lastResponse;
1100
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1101
+ const res = await (0, import_axios3.default)({ ...config, validateStatus: () => true });
1102
+ lastResponse = res;
1103
+ if (res.status !== 429) {
1104
+ return res;
1105
+ }
1106
+ if (attempt < MAX_RETRIES) {
1107
+ const delay = getRetryDelay(res, attempt);
1108
+ await sleep(delay);
1109
+ }
1110
+ }
1111
+ return lastResponse;
1112
+ }
1113
+ var BuildApiClient = class {
1114
+ constructor(options) {
1115
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
1116
+ this.clientId = options.clientId;
1117
+ this.walletClient = options.walletClient;
1118
+ }
1119
+ /**
1120
+ * Get the address of the connected wallet
1121
+ */
1122
+ get address() {
1123
+ const account = this.walletClient?.account;
1124
+ if (!account) {
1125
+ throw new Error("WalletClient must have an account attached");
1126
+ }
1127
+ return account.address;
1128
+ }
1129
+ async submitBuild(payload) {
1130
+ return this.authenticatedJsonRequest("/builds", "POST", payload);
1131
+ }
1132
+ async getBuild(buildId) {
1133
+ return this.publicJsonRequest(`/builds/${encodeURIComponent(buildId)}`);
1134
+ }
1135
+ async getBuildByDigest(digest) {
1136
+ return this.publicJsonRequest(`/builds/image/${encodeURIComponent(digest)}`);
1137
+ }
1138
+ async verify(identifier) {
1139
+ return this.publicJsonRequest(`/builds/verify/${encodeURIComponent(identifier)}`);
1140
+ }
1141
+ async getLogs(buildId) {
1142
+ return this.authenticatedTextRequest(`/builds/${encodeURIComponent(buildId)}/logs`);
1143
+ }
1144
+ async listBuilds(params) {
1145
+ const res = await requestWithRetry({
1146
+ url: `${this.baseUrl}/builds`,
1147
+ method: "GET",
1148
+ params,
1149
+ headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
1150
+ timeout: 6e4
1151
+ });
1152
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1153
+ return res.data;
1154
+ }
1155
+ async publicJsonRequest(path) {
1156
+ const res = await requestWithRetry({
1157
+ url: `${this.baseUrl}${path}`,
1158
+ method: "GET",
1159
+ headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
1160
+ timeout: 6e4
1161
+ });
1162
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1163
+ return res.data;
1164
+ }
1165
+ async authenticatedJsonRequest(path, method, body) {
1166
+ if (!this.walletClient?.account) {
1167
+ throw new Error("WalletClient with account required for authenticated requests");
1168
+ }
1169
+ const headers = {
1170
+ "Content-Type": "application/json"
1171
+ };
1172
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1173
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
1174
+ const { signature } = await calculateBillingAuthSignature({
1175
+ walletClient: this.walletClient,
1176
+ product: "compute",
1177
+ expiry
1178
+ });
1179
+ headers.Authorization = `Bearer ${signature}`;
1180
+ headers["X-eigenx-expiry"] = expiry.toString();
1181
+ headers["X-Account"] = this.address;
1182
+ const res = await requestWithRetry({
1183
+ url: `${this.baseUrl}${path}`,
1184
+ method,
1185
+ headers,
1186
+ data: body,
1187
+ timeout: 6e4
1188
+ });
1189
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1190
+ return res.data;
1191
+ }
1192
+ async authenticatedTextRequest(path) {
1193
+ if (!this.walletClient?.account) {
1194
+ throw new Error("WalletClient with account required for authenticated requests");
1195
+ }
1196
+ const headers = {};
1197
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1198
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
1199
+ const { signature } = await calculateBillingAuthSignature({
1200
+ walletClient: this.walletClient,
1201
+ product: "compute",
1202
+ expiry
1203
+ });
1204
+ headers.Authorization = `Bearer ${signature}`;
1205
+ headers["X-eigenx-expiry"] = expiry.toString();
1206
+ headers["X-Account"] = this.address;
1207
+ const res = await requestWithRetry({
1208
+ url: `${this.baseUrl}${path}`,
1209
+ method: "GET",
1210
+ headers,
1211
+ timeout: 6e4,
1212
+ responseType: "text"
1213
+ });
1214
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1215
+ return typeof res.data === "string" ? res.data : JSON.stringify(res.data);
1216
+ }
1217
+ };
1218
+ function buildApiHttpError(res) {
1219
+ const status = res.status;
1220
+ const body = typeof res.data === "string" ? res.data : res.data ? JSON.stringify(res.data) : "";
1221
+ const url = res.config?.url ? ` ${res.config.url}` : "";
1222
+ return new Error(`BuildAPI request failed: ${status}${url} - ${body || "Unknown error"}`);
1223
+ }
1224
+
700
1225
  // src/client/common/contract/eip7702.ts
701
1226
  var import_viem4 = require("viem");
702
1227
 
@@ -1777,6 +2302,96 @@ async function estimateBatchGas(options) {
1777
2302
  maxCostEth: formatETH(maxCostWei)
1778
2303
  };
1779
2304
  }
2305
+ async function checkERC7702Delegation(publicClient, account, delegatorAddress) {
2306
+ const code = await publicClient.getCode({ address: account });
2307
+ if (!code) {
2308
+ return false;
2309
+ }
2310
+ const expectedCode = `0xef0100${delegatorAddress.slice(2)}`;
2311
+ return code.toLowerCase() === expectedCode.toLowerCase();
2312
+ }
2313
+ async function executeBatch(options, logger = noopLogger) {
2314
+ const { walletClient, publicClient, environmentConfig, executions, pendingMessage, gas } = options;
2315
+ const account = walletClient.account;
2316
+ if (!account) {
2317
+ throw new Error("Wallet client must have an account");
2318
+ }
2319
+ const chain = walletClient.chain;
2320
+ if (!chain) {
2321
+ throw new Error("Wallet client must have a chain");
2322
+ }
2323
+ const executeBatchData = encodeExecuteBatchData(executions);
2324
+ const isDelegated2 = await checkERC7702Delegation(
2325
+ publicClient,
2326
+ account.address,
2327
+ environmentConfig.erc7702DelegatorAddress
2328
+ );
2329
+ let authorizationList = [];
2330
+ if (!isDelegated2) {
2331
+ const transactionNonce = await publicClient.getTransactionCount({
2332
+ address: account.address,
2333
+ blockTag: "pending"
2334
+ });
2335
+ const chainId = await publicClient.getChainId();
2336
+ const authorizationNonce = transactionNonce + 1;
2337
+ logger.debug("Using wallet client signing for EIP-7702 authorization");
2338
+ const signedAuthorization = await walletClient.signAuthorization({
2339
+ account: account.address,
2340
+ contractAddress: environmentConfig.erc7702DelegatorAddress,
2341
+ chainId,
2342
+ nonce: Number(authorizationNonce)
2343
+ });
2344
+ authorizationList = [signedAuthorization];
2345
+ }
2346
+ if (pendingMessage) {
2347
+ logger.info(pendingMessage);
2348
+ }
2349
+ const txRequest = {
2350
+ account: walletClient.account,
2351
+ chain,
2352
+ to: account.address,
2353
+ data: executeBatchData,
2354
+ value: 0n
2355
+ };
2356
+ if (authorizationList.length > 0) {
2357
+ txRequest.authorizationList = authorizationList;
2358
+ }
2359
+ if (gas?.maxFeePerGas) {
2360
+ txRequest.maxFeePerGas = gas.maxFeePerGas;
2361
+ }
2362
+ if (gas?.maxPriorityFeePerGas) {
2363
+ txRequest.maxPriorityFeePerGas = gas.maxPriorityFeePerGas;
2364
+ }
2365
+ const hash = await walletClient.sendTransaction(txRequest);
2366
+ logger.info(`Transaction sent: ${hash}`);
2367
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
2368
+ if (receipt.status === "reverted") {
2369
+ let revertReason = "Unknown reason";
2370
+ try {
2371
+ await publicClient.call({
2372
+ to: account.address,
2373
+ data: executeBatchData,
2374
+ account: account.address
2375
+ });
2376
+ } catch (callError) {
2377
+ if (callError.data) {
2378
+ try {
2379
+ const decoded = (0, import_viem4.decodeErrorResult)({
2380
+ abi: ERC7702Delegator_default,
2381
+ data: callError.data
2382
+ });
2383
+ revertReason = `${decoded.errorName}: ${JSON.stringify(decoded.args)}`;
2384
+ } catch {
2385
+ revertReason = callError.message || "Unknown reason";
2386
+ }
2387
+ } else {
2388
+ revertReason = callError.message || "Unknown reason";
2389
+ }
2390
+ }
2391
+ throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2392
+ }
2393
+ return hash;
2394
+ }
1780
2395
 
1781
2396
  // src/client/common/contract/caller.ts
1782
2397
  var import_viem5 = require("viem");
@@ -2829,57 +3444,1049 @@ var AppController_default = [
2829
3444
  }
2830
3445
  ];
2831
3446
 
2832
- // src/client/common/contract/caller.ts
2833
- function formatETH(wei) {
2834
- const eth = Number(wei) / 1e18;
2835
- const costStr = eth.toFixed(6);
2836
- const trimmed = costStr.replace(/\.?0+$/, "");
2837
- if (trimmed === "0" && wei > 0n) {
2838
- return "<0.000001";
2839
- }
2840
- return trimmed;
2841
- }
2842
- async function estimateTransactionGas(options) {
2843
- const { publicClient, from, to, data, value = 0n } = options;
2844
- const fees = await publicClient.estimateFeesPerGas();
2845
- const gasLimit = await publicClient.estimateGas({
2846
- account: from,
2847
- to,
2848
- data,
2849
- value
2850
- });
2851
- const maxFeePerGas = fees.maxFeePerGas;
2852
- const maxPriorityFeePerGas = fees.maxPriorityFeePerGas;
2853
- const maxCostWei = gasLimit * maxFeePerGas;
2854
- const maxCostEth = formatETH(maxCostWei);
2855
- return {
2856
- gasLimit,
2857
- maxFeePerGas,
2858
- maxPriorityFeePerGas,
2859
- maxCostWei,
2860
- maxCostEth
2861
- };
2862
- }
2863
- async function getActiveAppCount(publicClient, environmentConfig, user) {
2864
- const count = await publicClient.readContract({
2865
- address: environmentConfig.appControllerAddress,
2866
- abi: AppController_default,
2867
- functionName: "getActiveAppCount",
2868
- args: [user]
2869
- });
2870
- return Number(count);
2871
- }
2872
- async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
2873
- const quota = await publicClient.readContract({
2874
- address: environmentConfig.appControllerAddress,
2875
- abi: AppController_default,
2876
- functionName: "getMaxActiveAppsPerUser",
2877
- args: [user]
2878
- });
2879
- return Number(quota);
2880
- }
2881
- async function getAppsByCreator(publicClient, environmentConfig, creator, offset, limit) {
2882
- const result = await publicClient.readContract({
3447
+ // src/client/common/abis/PermissionController.json
3448
+ var PermissionController_default = [
3449
+ {
3450
+ type: "function",
3451
+ name: "acceptAdmin",
3452
+ inputs: [
3453
+ {
3454
+ name: "account",
3455
+ type: "address",
3456
+ internalType: "address"
3457
+ }
3458
+ ],
3459
+ outputs: [],
3460
+ stateMutability: "nonpayable"
3461
+ },
3462
+ {
3463
+ type: "function",
3464
+ name: "addPendingAdmin",
3465
+ inputs: [
3466
+ {
3467
+ name: "account",
3468
+ type: "address",
3469
+ internalType: "address"
3470
+ },
3471
+ {
3472
+ name: "admin",
3473
+ type: "address",
3474
+ internalType: "address"
3475
+ }
3476
+ ],
3477
+ outputs: [],
3478
+ stateMutability: "nonpayable"
3479
+ },
3480
+ {
3481
+ type: "function",
3482
+ name: "canCall",
3483
+ inputs: [
3484
+ {
3485
+ name: "account",
3486
+ type: "address",
3487
+ internalType: "address"
3488
+ },
3489
+ {
3490
+ name: "caller",
3491
+ type: "address",
3492
+ internalType: "address"
3493
+ },
3494
+ {
3495
+ name: "target",
3496
+ type: "address",
3497
+ internalType: "address"
3498
+ },
3499
+ {
3500
+ name: "selector",
3501
+ type: "bytes4",
3502
+ internalType: "bytes4"
3503
+ }
3504
+ ],
3505
+ outputs: [
3506
+ {
3507
+ name: "",
3508
+ type: "bool",
3509
+ internalType: "bool"
3510
+ }
3511
+ ],
3512
+ stateMutability: "nonpayable"
3513
+ },
3514
+ {
3515
+ type: "function",
3516
+ name: "getAdmins",
3517
+ inputs: [
3518
+ {
3519
+ name: "account",
3520
+ type: "address",
3521
+ internalType: "address"
3522
+ }
3523
+ ],
3524
+ outputs: [
3525
+ {
3526
+ name: "",
3527
+ type: "address[]",
3528
+ internalType: "address[]"
3529
+ }
3530
+ ],
3531
+ stateMutability: "view"
3532
+ },
3533
+ {
3534
+ type: "function",
3535
+ name: "getAppointeePermissions",
3536
+ inputs: [
3537
+ {
3538
+ name: "account",
3539
+ type: "address",
3540
+ internalType: "address"
3541
+ },
3542
+ {
3543
+ name: "appointee",
3544
+ type: "address",
3545
+ internalType: "address"
3546
+ }
3547
+ ],
3548
+ outputs: [
3549
+ {
3550
+ name: "",
3551
+ type: "address[]",
3552
+ internalType: "address[]"
3553
+ },
3554
+ {
3555
+ name: "",
3556
+ type: "bytes4[]",
3557
+ internalType: "bytes4[]"
3558
+ }
3559
+ ],
3560
+ stateMutability: "nonpayable"
3561
+ },
3562
+ {
3563
+ type: "function",
3564
+ name: "getAppointees",
3565
+ inputs: [
3566
+ {
3567
+ name: "account",
3568
+ type: "address",
3569
+ internalType: "address"
3570
+ },
3571
+ {
3572
+ name: "target",
3573
+ type: "address",
3574
+ internalType: "address"
3575
+ },
3576
+ {
3577
+ name: "selector",
3578
+ type: "bytes4",
3579
+ internalType: "bytes4"
3580
+ }
3581
+ ],
3582
+ outputs: [
3583
+ {
3584
+ name: "",
3585
+ type: "address[]",
3586
+ internalType: "address[]"
3587
+ }
3588
+ ],
3589
+ stateMutability: "nonpayable"
3590
+ },
3591
+ {
3592
+ type: "function",
3593
+ name: "getPendingAdmins",
3594
+ inputs: [
3595
+ {
3596
+ name: "account",
3597
+ type: "address",
3598
+ internalType: "address"
3599
+ }
3600
+ ],
3601
+ outputs: [
3602
+ {
3603
+ name: "",
3604
+ type: "address[]",
3605
+ internalType: "address[]"
3606
+ }
3607
+ ],
3608
+ stateMutability: "view"
3609
+ },
3610
+ {
3611
+ type: "function",
3612
+ name: "isAdmin",
3613
+ inputs: [
3614
+ {
3615
+ name: "account",
3616
+ type: "address",
3617
+ internalType: "address"
3618
+ },
3619
+ {
3620
+ name: "caller",
3621
+ type: "address",
3622
+ internalType: "address"
3623
+ }
3624
+ ],
3625
+ outputs: [
3626
+ {
3627
+ name: "",
3628
+ type: "bool",
3629
+ internalType: "bool"
3630
+ }
3631
+ ],
3632
+ stateMutability: "view"
3633
+ },
3634
+ {
3635
+ type: "function",
3636
+ name: "isPendingAdmin",
3637
+ inputs: [
3638
+ {
3639
+ name: "account",
3640
+ type: "address",
3641
+ internalType: "address"
3642
+ },
3643
+ {
3644
+ name: "pendingAdmin",
3645
+ type: "address",
3646
+ internalType: "address"
3647
+ }
3648
+ ],
3649
+ outputs: [
3650
+ {
3651
+ name: "",
3652
+ type: "bool",
3653
+ internalType: "bool"
3654
+ }
3655
+ ],
3656
+ stateMutability: "view"
3657
+ },
3658
+ {
3659
+ type: "function",
3660
+ name: "removeAdmin",
3661
+ inputs: [
3662
+ {
3663
+ name: "account",
3664
+ type: "address",
3665
+ internalType: "address"
3666
+ },
3667
+ {
3668
+ name: "admin",
3669
+ type: "address",
3670
+ internalType: "address"
3671
+ }
3672
+ ],
3673
+ outputs: [],
3674
+ stateMutability: "nonpayable"
3675
+ },
3676
+ {
3677
+ type: "function",
3678
+ name: "removeAppointee",
3679
+ inputs: [
3680
+ {
3681
+ name: "account",
3682
+ type: "address",
3683
+ internalType: "address"
3684
+ },
3685
+ {
3686
+ name: "appointee",
3687
+ type: "address",
3688
+ internalType: "address"
3689
+ },
3690
+ {
3691
+ name: "target",
3692
+ type: "address",
3693
+ internalType: "address"
3694
+ },
3695
+ {
3696
+ name: "selector",
3697
+ type: "bytes4",
3698
+ internalType: "bytes4"
3699
+ }
3700
+ ],
3701
+ outputs: [],
3702
+ stateMutability: "nonpayable"
3703
+ },
3704
+ {
3705
+ type: "function",
3706
+ name: "removePendingAdmin",
3707
+ inputs: [
3708
+ {
3709
+ name: "account",
3710
+ type: "address",
3711
+ internalType: "address"
3712
+ },
3713
+ {
3714
+ name: "admin",
3715
+ type: "address",
3716
+ internalType: "address"
3717
+ }
3718
+ ],
3719
+ outputs: [],
3720
+ stateMutability: "nonpayable"
3721
+ },
3722
+ {
3723
+ type: "function",
3724
+ name: "setAppointee",
3725
+ inputs: [
3726
+ {
3727
+ name: "account",
3728
+ type: "address",
3729
+ internalType: "address"
3730
+ },
3731
+ {
3732
+ name: "appointee",
3733
+ type: "address",
3734
+ internalType: "address"
3735
+ },
3736
+ {
3737
+ name: "target",
3738
+ type: "address",
3739
+ internalType: "address"
3740
+ },
3741
+ {
3742
+ name: "selector",
3743
+ type: "bytes4",
3744
+ internalType: "bytes4"
3745
+ }
3746
+ ],
3747
+ outputs: [],
3748
+ stateMutability: "nonpayable"
3749
+ },
3750
+ {
3751
+ type: "function",
3752
+ name: "version",
3753
+ inputs: [],
3754
+ outputs: [
3755
+ {
3756
+ name: "",
3757
+ type: "string",
3758
+ internalType: "string"
3759
+ }
3760
+ ],
3761
+ stateMutability: "view"
3762
+ },
3763
+ {
3764
+ type: "event",
3765
+ name: "AdminRemoved",
3766
+ inputs: [
3767
+ {
3768
+ name: "account",
3769
+ type: "address",
3770
+ indexed: true,
3771
+ internalType: "address"
3772
+ },
3773
+ {
3774
+ name: "admin",
3775
+ type: "address",
3776
+ indexed: false,
3777
+ internalType: "address"
3778
+ }
3779
+ ],
3780
+ anonymous: false
3781
+ },
3782
+ {
3783
+ type: "event",
3784
+ name: "AdminSet",
3785
+ inputs: [
3786
+ {
3787
+ name: "account",
3788
+ type: "address",
3789
+ indexed: true,
3790
+ internalType: "address"
3791
+ },
3792
+ {
3793
+ name: "admin",
3794
+ type: "address",
3795
+ indexed: false,
3796
+ internalType: "address"
3797
+ }
3798
+ ],
3799
+ anonymous: false
3800
+ },
3801
+ {
3802
+ type: "event",
3803
+ name: "AppointeeRemoved",
3804
+ inputs: [
3805
+ {
3806
+ name: "account",
3807
+ type: "address",
3808
+ indexed: true,
3809
+ internalType: "address"
3810
+ },
3811
+ {
3812
+ name: "appointee",
3813
+ type: "address",
3814
+ indexed: true,
3815
+ internalType: "address"
3816
+ },
3817
+ {
3818
+ name: "target",
3819
+ type: "address",
3820
+ indexed: false,
3821
+ internalType: "address"
3822
+ },
3823
+ {
3824
+ name: "selector",
3825
+ type: "bytes4",
3826
+ indexed: false,
3827
+ internalType: "bytes4"
3828
+ }
3829
+ ],
3830
+ anonymous: false
3831
+ },
3832
+ {
3833
+ type: "event",
3834
+ name: "AppointeeSet",
3835
+ inputs: [
3836
+ {
3837
+ name: "account",
3838
+ type: "address",
3839
+ indexed: true,
3840
+ internalType: "address"
3841
+ },
3842
+ {
3843
+ name: "appointee",
3844
+ type: "address",
3845
+ indexed: true,
3846
+ internalType: "address"
3847
+ },
3848
+ {
3849
+ name: "target",
3850
+ type: "address",
3851
+ indexed: false,
3852
+ internalType: "address"
3853
+ },
3854
+ {
3855
+ name: "selector",
3856
+ type: "bytes4",
3857
+ indexed: false,
3858
+ internalType: "bytes4"
3859
+ }
3860
+ ],
3861
+ anonymous: false
3862
+ },
3863
+ {
3864
+ type: "event",
3865
+ name: "PendingAdminAdded",
3866
+ inputs: [
3867
+ {
3868
+ name: "account",
3869
+ type: "address",
3870
+ indexed: true,
3871
+ internalType: "address"
3872
+ },
3873
+ {
3874
+ name: "admin",
3875
+ type: "address",
3876
+ indexed: false,
3877
+ internalType: "address"
3878
+ }
3879
+ ],
3880
+ anonymous: false
3881
+ },
3882
+ {
3883
+ type: "event",
3884
+ name: "PendingAdminRemoved",
3885
+ inputs: [
3886
+ {
3887
+ name: "account",
3888
+ type: "address",
3889
+ indexed: true,
3890
+ internalType: "address"
3891
+ },
3892
+ {
3893
+ name: "admin",
3894
+ type: "address",
3895
+ indexed: false,
3896
+ internalType: "address"
3897
+ }
3898
+ ],
3899
+ anonymous: false
3900
+ },
3901
+ {
3902
+ type: "error",
3903
+ name: "AdminAlreadyPending",
3904
+ inputs: []
3905
+ },
3906
+ {
3907
+ type: "error",
3908
+ name: "AdminAlreadySet",
3909
+ inputs: []
3910
+ },
3911
+ {
3912
+ type: "error",
3913
+ name: "AdminNotPending",
3914
+ inputs: []
3915
+ },
3916
+ {
3917
+ type: "error",
3918
+ name: "AdminNotSet",
3919
+ inputs: []
3920
+ },
3921
+ {
3922
+ type: "error",
3923
+ name: "AppointeeAlreadySet",
3924
+ inputs: []
3925
+ },
3926
+ {
3927
+ type: "error",
3928
+ name: "AppointeeNotSet",
3929
+ inputs: []
3930
+ },
3931
+ {
3932
+ type: "error",
3933
+ name: "CannotHaveZeroAdmins",
3934
+ inputs: []
3935
+ },
3936
+ {
3937
+ type: "error",
3938
+ name: "NotAdmin",
3939
+ inputs: []
3940
+ }
3941
+ ];
3942
+
3943
+ // src/client/common/contract/caller.ts
3944
+ function formatETH(wei) {
3945
+ const eth = Number(wei) / 1e18;
3946
+ const costStr = eth.toFixed(6);
3947
+ const trimmed = costStr.replace(/\.?0+$/, "");
3948
+ if (trimmed === "0" && wei > 0n) {
3949
+ return "<0.000001";
3950
+ }
3951
+ return trimmed;
3952
+ }
3953
+ async function estimateTransactionGas(options) {
3954
+ const { publicClient, from, to, data, value = 0n } = options;
3955
+ const fees = await publicClient.estimateFeesPerGas();
3956
+ const gasLimit = await publicClient.estimateGas({
3957
+ account: from,
3958
+ to,
3959
+ data,
3960
+ value
3961
+ });
3962
+ const maxFeePerGas = fees.maxFeePerGas;
3963
+ const maxPriorityFeePerGas = fees.maxPriorityFeePerGas;
3964
+ const maxCostWei = gasLimit * maxFeePerGas;
3965
+ const maxCostEth = formatETH(maxCostWei);
3966
+ return {
3967
+ gasLimit,
3968
+ maxFeePerGas,
3969
+ maxPriorityFeePerGas,
3970
+ maxCostWei,
3971
+ maxCostEth
3972
+ };
3973
+ }
3974
+ async function calculateAppID(options) {
3975
+ const { publicClient, environmentConfig, ownerAddress, salt } = options;
3976
+ const saltHexString = (0, import_viem5.bytesToHex)(salt).slice(2);
3977
+ const paddedSaltHex = saltHexString.padStart(64, "0");
3978
+ const saltHex = `0x${paddedSaltHex}`;
3979
+ const appID = await publicClient.readContract({
3980
+ address: environmentConfig.appControllerAddress,
3981
+ abi: AppController_default,
3982
+ functionName: "calculateAppId",
3983
+ args: [ownerAddress, saltHex]
3984
+ });
3985
+ return appID;
3986
+ }
3987
+ async function prepareDeployBatch(options, logger = noopLogger) {
3988
+ const { walletClient, publicClient, environmentConfig, salt, release, publicLogs } = options;
3989
+ const account = walletClient.account;
3990
+ if (!account) {
3991
+ throw new Error("WalletClient must have an account attached");
3992
+ }
3993
+ logger.info("Calculating app ID...");
3994
+ const appId = await calculateAppID({
3995
+ publicClient,
3996
+ environmentConfig,
3997
+ ownerAddress: account.address,
3998
+ salt
3999
+ });
4000
+ logger.debug(`App ID calculated: ${appId}`);
4001
+ logger.debug(`This address will be used for acceptAdmin call`);
4002
+ const saltHexString = (0, import_viem5.bytesToHex)(salt).slice(2);
4003
+ const paddedSaltHex = saltHexString.padStart(64, "0");
4004
+ const saltHex = `0x${paddedSaltHex}`;
4005
+ const releaseForViem = {
4006
+ rmsRelease: {
4007
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4008
+ digest: `0x${(0, import_viem5.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4009
+ registry: artifact.registry
4010
+ })),
4011
+ upgradeByTime: release.rmsRelease.upgradeByTime
4012
+ },
4013
+ publicEnv: (0, import_viem5.bytesToHex)(release.publicEnv),
4014
+ encryptedEnv: (0, import_viem5.bytesToHex)(release.encryptedEnv)
4015
+ };
4016
+ const createData = (0, import_viem5.encodeFunctionData)({
4017
+ abi: AppController_default,
4018
+ functionName: "createApp",
4019
+ args: [saltHex, releaseForViem]
4020
+ });
4021
+ const acceptAdminData = (0, import_viem5.encodeFunctionData)({
4022
+ abi: PermissionController_default,
4023
+ functionName: "acceptAdmin",
4024
+ args: [appId]
4025
+ });
4026
+ const executions = [
4027
+ {
4028
+ target: environmentConfig.appControllerAddress,
4029
+ value: 0n,
4030
+ callData: createData
4031
+ },
4032
+ {
4033
+ target: environmentConfig.permissionControllerAddress,
4034
+ value: 0n,
4035
+ callData: acceptAdminData
4036
+ }
4037
+ ];
4038
+ if (publicLogs) {
4039
+ const anyoneCanViewLogsData = (0, import_viem5.encodeFunctionData)({
4040
+ abi: PermissionController_default,
4041
+ functionName: "setAppointee",
4042
+ args: [
4043
+ appId,
4044
+ "0x493219d9949348178af1f58740655951a8cd110c",
4045
+ // AnyoneCanCallAddress
4046
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4047
+ // ApiPermissionsTarget
4048
+ "0x2fd3f2fe"
4049
+ // CanViewAppLogsPermission
4050
+ ]
4051
+ });
4052
+ executions.push({
4053
+ target: environmentConfig.permissionControllerAddress,
4054
+ value: 0n,
4055
+ callData: anyoneCanViewLogsData
4056
+ });
4057
+ }
4058
+ return {
4059
+ appId,
4060
+ salt,
4061
+ executions,
4062
+ walletClient,
4063
+ publicClient,
4064
+ environmentConfig
4065
+ };
4066
+ }
4067
+ async function executeDeployBatch(data, context, gas, logger = noopLogger) {
4068
+ const pendingMessage = "Deploying new app...";
4069
+ const txHash = await executeBatch(
4070
+ {
4071
+ walletClient: context.walletClient,
4072
+ publicClient: context.publicClient,
4073
+ environmentConfig: context.environmentConfig,
4074
+ executions: data.executions,
4075
+ pendingMessage,
4076
+ gas
4077
+ },
4078
+ logger
4079
+ );
4080
+ return { appId: data.appId, txHash };
4081
+ }
4082
+ async function deployApp(options, logger = noopLogger) {
4083
+ const prepared = await prepareDeployBatch(options, logger);
4084
+ const data = {
4085
+ appId: prepared.appId,
4086
+ salt: prepared.salt,
4087
+ executions: prepared.executions
4088
+ };
4089
+ const context = {
4090
+ walletClient: prepared.walletClient,
4091
+ publicClient: prepared.publicClient,
4092
+ environmentConfig: prepared.environmentConfig
4093
+ };
4094
+ return executeDeployBatch(data, context, options.gas, logger);
4095
+ }
4096
+ function supportsEIP7702(walletClient) {
4097
+ const account = walletClient.account;
4098
+ if (!account) return false;
4099
+ return account.type === "local";
4100
+ }
4101
+ async function executeDeploySequential(options, logger = noopLogger) {
4102
+ const { walletClient, publicClient, environmentConfig, data, publicLogs, onProgress } = options;
4103
+ const account = walletClient.account;
4104
+ if (!account) {
4105
+ throw new Error("WalletClient must have an account attached");
4106
+ }
4107
+ const chain = getChainFromID(environmentConfig.chainID);
4108
+ const txHashes = {
4109
+ createApp: "0x",
4110
+ acceptAdmin: "0x"
4111
+ };
4112
+ logger.info("Step 1/3: Creating app...");
4113
+ onProgress?.("createApp");
4114
+ const createAppExecution = data.executions[0];
4115
+ const createAppHash = await walletClient.sendTransaction({
4116
+ account,
4117
+ to: createAppExecution.target,
4118
+ data: createAppExecution.callData,
4119
+ value: createAppExecution.value,
4120
+ chain
4121
+ });
4122
+ logger.info(`createApp transaction sent: ${createAppHash}`);
4123
+ const createAppReceipt = await publicClient.waitForTransactionReceipt({ hash: createAppHash });
4124
+ if (createAppReceipt.status === "reverted") {
4125
+ throw new Error(`createApp transaction reverted: ${createAppHash}`);
4126
+ }
4127
+ txHashes.createApp = createAppHash;
4128
+ logger.info(`createApp confirmed in block ${createAppReceipt.blockNumber}`);
4129
+ logger.info("Step 2/3: Accepting admin role...");
4130
+ onProgress?.("acceptAdmin", createAppHash);
4131
+ const acceptAdminExecution = data.executions[1];
4132
+ const acceptAdminHash = await walletClient.sendTransaction({
4133
+ account,
4134
+ to: acceptAdminExecution.target,
4135
+ data: acceptAdminExecution.callData,
4136
+ value: acceptAdminExecution.value,
4137
+ chain
4138
+ });
4139
+ logger.info(`acceptAdmin transaction sent: ${acceptAdminHash}`);
4140
+ const acceptAdminReceipt = await publicClient.waitForTransactionReceipt({
4141
+ hash: acceptAdminHash
4142
+ });
4143
+ if (acceptAdminReceipt.status === "reverted") {
4144
+ throw new Error(`acceptAdmin transaction reverted: ${acceptAdminHash}`);
4145
+ }
4146
+ txHashes.acceptAdmin = acceptAdminHash;
4147
+ logger.info(`acceptAdmin confirmed in block ${acceptAdminReceipt.blockNumber}`);
4148
+ if (publicLogs && data.executions.length > 2) {
4149
+ logger.info("Step 3/3: Setting public logs permission...");
4150
+ onProgress?.("setPublicLogs", acceptAdminHash);
4151
+ const setAppointeeExecution = data.executions[2];
4152
+ const setAppointeeHash = await walletClient.sendTransaction({
4153
+ account,
4154
+ to: setAppointeeExecution.target,
4155
+ data: setAppointeeExecution.callData,
4156
+ value: setAppointeeExecution.value,
4157
+ chain
4158
+ });
4159
+ logger.info(`setAppointee transaction sent: ${setAppointeeHash}`);
4160
+ const setAppointeeReceipt = await publicClient.waitForTransactionReceipt({
4161
+ hash: setAppointeeHash
4162
+ });
4163
+ if (setAppointeeReceipt.status === "reverted") {
4164
+ throw new Error(`setAppointee transaction reverted: ${setAppointeeHash}`);
4165
+ }
4166
+ txHashes.setPublicLogs = setAppointeeHash;
4167
+ logger.info(`setAppointee confirmed in block ${setAppointeeReceipt.blockNumber}`);
4168
+ }
4169
+ onProgress?.("complete", txHashes.setPublicLogs || txHashes.acceptAdmin);
4170
+ logger.info(`Deployment complete! App ID: ${data.appId}`);
4171
+ return {
4172
+ appId: data.appId,
4173
+ txHashes
4174
+ };
4175
+ }
4176
+ async function supportsEIP5792(walletClient) {
4177
+ try {
4178
+ if (typeof walletClient.getCapabilities !== "function") {
4179
+ return false;
4180
+ }
4181
+ const account = walletClient.account;
4182
+ if (!account) return false;
4183
+ const capabilities = await walletClient.getCapabilities({
4184
+ account: account.address
4185
+ });
4186
+ return capabilities !== null && capabilities !== void 0 && Object.keys(capabilities).length > 0;
4187
+ } catch {
4188
+ return false;
4189
+ }
4190
+ }
4191
+ async function executeDeployBatched(options, logger = noopLogger) {
4192
+ const { walletClient, environmentConfig, data, publicLogs, onProgress } = options;
4193
+ const account = walletClient.account;
4194
+ if (!account) {
4195
+ throw new Error("WalletClient must have an account attached");
4196
+ }
4197
+ const chain = getChainFromID(environmentConfig.chainID);
4198
+ const calls = data.executions.map(
4199
+ (execution) => ({
4200
+ to: execution.target,
4201
+ data: execution.callData,
4202
+ value: execution.value
4203
+ })
4204
+ );
4205
+ const filteredCalls = publicLogs ? calls : calls.slice(0, 2);
4206
+ logger.info(`Deploying with EIP-5792 sendCalls (${filteredCalls.length} calls)...`);
4207
+ onProgress?.("createApp");
4208
+ try {
4209
+ const { id: batchId } = await walletClient.sendCalls({
4210
+ account,
4211
+ chain,
4212
+ calls: filteredCalls,
4213
+ forceAtomic: true
4214
+ });
4215
+ logger.info(`Batch submitted with ID: ${batchId}`);
4216
+ onProgress?.("acceptAdmin");
4217
+ let status;
4218
+ let attempts = 0;
4219
+ const maxAttempts = 120;
4220
+ while (attempts < maxAttempts) {
4221
+ try {
4222
+ status = await walletClient.getCallsStatus({ id: batchId });
4223
+ if (status.status === "success" || status.status === "confirmed") {
4224
+ logger.info(`Batch confirmed with ${status.receipts?.length || 0} receipts`);
4225
+ break;
4226
+ }
4227
+ if (status.status === "failed" || status.status === "reverted") {
4228
+ throw new Error(`Batch transaction failed: ${status.status}`);
4229
+ }
4230
+ } catch (statusError) {
4231
+ if (statusError.message?.includes("not supported")) {
4232
+ logger.warn("getCallsStatus not supported, waiting for chain confirmation...");
4233
+ await new Promise((resolve) => setTimeout(resolve, 15e3));
4234
+ break;
4235
+ }
4236
+ throw statusError;
4237
+ }
4238
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
4239
+ attempts++;
4240
+ }
4241
+ if (attempts >= maxAttempts) {
4242
+ throw new Error("Timeout waiting for batch confirmation");
4243
+ }
4244
+ if (publicLogs) {
4245
+ onProgress?.("setPublicLogs");
4246
+ }
4247
+ onProgress?.("complete");
4248
+ const receipts = (status?.receipts || []).map((r) => ({
4249
+ transactionHash: r.transactionHash || r.hash
4250
+ }));
4251
+ logger.info(`Deployment complete! App ID: ${data.appId}`);
4252
+ return {
4253
+ appId: data.appId,
4254
+ batchId,
4255
+ receipts
4256
+ };
4257
+ } catch (error) {
4258
+ if (error.message?.includes("not supported") || error.message?.includes("wallet_sendCalls") || error.code === -32601) {
4259
+ throw new Error("EIP5792_NOT_SUPPORTED");
4260
+ }
4261
+ throw error;
4262
+ }
4263
+ }
4264
+ async function prepareUpgradeBatch(options) {
4265
+ const {
4266
+ walletClient,
4267
+ publicClient,
4268
+ environmentConfig,
4269
+ appID,
4270
+ release,
4271
+ publicLogs,
4272
+ needsPermissionChange
4273
+ } = options;
4274
+ const releaseForViem = {
4275
+ rmsRelease: {
4276
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4277
+ digest: `0x${(0, import_viem5.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4278
+ registry: artifact.registry
4279
+ })),
4280
+ upgradeByTime: release.rmsRelease.upgradeByTime
4281
+ },
4282
+ publicEnv: (0, import_viem5.bytesToHex)(release.publicEnv),
4283
+ encryptedEnv: (0, import_viem5.bytesToHex)(release.encryptedEnv)
4284
+ };
4285
+ const upgradeData = (0, import_viem5.encodeFunctionData)({
4286
+ abi: AppController_default,
4287
+ functionName: "upgradeApp",
4288
+ args: [appID, releaseForViem]
4289
+ });
4290
+ const executions = [
4291
+ {
4292
+ target: environmentConfig.appControllerAddress,
4293
+ value: 0n,
4294
+ callData: upgradeData
4295
+ }
4296
+ ];
4297
+ if (needsPermissionChange) {
4298
+ if (publicLogs) {
4299
+ const addLogsData = (0, import_viem5.encodeFunctionData)({
4300
+ abi: PermissionController_default,
4301
+ functionName: "setAppointee",
4302
+ args: [
4303
+ appID,
4304
+ "0x493219d9949348178af1f58740655951a8cd110c",
4305
+ // AnyoneCanCallAddress
4306
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4307
+ // ApiPermissionsTarget
4308
+ "0x2fd3f2fe"
4309
+ // CanViewAppLogsPermission
4310
+ ]
4311
+ });
4312
+ executions.push({
4313
+ target: environmentConfig.permissionControllerAddress,
4314
+ value: 0n,
4315
+ callData: addLogsData
4316
+ });
4317
+ } else {
4318
+ const removeLogsData = (0, import_viem5.encodeFunctionData)({
4319
+ abi: PermissionController_default,
4320
+ functionName: "removeAppointee",
4321
+ args: [
4322
+ appID,
4323
+ "0x493219d9949348178af1f58740655951a8cd110c",
4324
+ // AnyoneCanCallAddress
4325
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4326
+ // ApiPermissionsTarget
4327
+ "0x2fd3f2fe"
4328
+ // CanViewAppLogsPermission
4329
+ ]
4330
+ });
4331
+ executions.push({
4332
+ target: environmentConfig.permissionControllerAddress,
4333
+ value: 0n,
4334
+ callData: removeLogsData
4335
+ });
4336
+ }
4337
+ }
4338
+ return {
4339
+ appId: appID,
4340
+ executions,
4341
+ walletClient,
4342
+ publicClient,
4343
+ environmentConfig
4344
+ };
4345
+ }
4346
+ async function executeUpgradeBatch(data, context, gas, logger = noopLogger) {
4347
+ const pendingMessage = `Upgrading app ${data.appId}...`;
4348
+ const txHash = await executeBatch(
4349
+ {
4350
+ walletClient: context.walletClient,
4351
+ publicClient: context.publicClient,
4352
+ environmentConfig: context.environmentConfig,
4353
+ executions: data.executions,
4354
+ pendingMessage,
4355
+ gas
4356
+ },
4357
+ logger
4358
+ );
4359
+ return txHash;
4360
+ }
4361
+ async function upgradeApp(options, logger = noopLogger) {
4362
+ const prepared = await prepareUpgradeBatch(options);
4363
+ const data = {
4364
+ appId: prepared.appId,
4365
+ executions: prepared.executions
4366
+ };
4367
+ const context = {
4368
+ walletClient: prepared.walletClient,
4369
+ publicClient: prepared.publicClient,
4370
+ environmentConfig: prepared.environmentConfig
4371
+ };
4372
+ return executeUpgradeBatch(data, context, options.gas, logger);
4373
+ }
4374
+ async function sendAndWaitForTransaction(options, logger = noopLogger) {
4375
+ const {
4376
+ walletClient,
4377
+ publicClient,
4378
+ environmentConfig,
4379
+ to,
4380
+ data,
4381
+ value = 0n,
4382
+ pendingMessage,
4383
+ txDescription,
4384
+ gas
4385
+ } = options;
4386
+ const account = walletClient.account;
4387
+ if (!account) {
4388
+ throw new Error("WalletClient must have an account attached");
4389
+ }
4390
+ const chain = getChainFromID(environmentConfig.chainID);
4391
+ if (pendingMessage) {
4392
+ logger.info(`
4393
+ ${pendingMessage}`);
4394
+ }
4395
+ const hash = await walletClient.sendTransaction({
4396
+ account,
4397
+ to,
4398
+ data,
4399
+ value,
4400
+ ...gas?.maxFeePerGas && { maxFeePerGas: gas.maxFeePerGas },
4401
+ ...gas?.maxPriorityFeePerGas && {
4402
+ maxPriorityFeePerGas: gas.maxPriorityFeePerGas
4403
+ },
4404
+ chain
4405
+ });
4406
+ logger.info(`Transaction sent: ${hash}`);
4407
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
4408
+ if (receipt.status === "reverted") {
4409
+ let revertReason = "Unknown reason";
4410
+ try {
4411
+ await publicClient.call({
4412
+ to,
4413
+ data,
4414
+ account: account.address
4415
+ });
4416
+ } catch (callError) {
4417
+ if (callError.data) {
4418
+ try {
4419
+ const decoded = (0, import_viem5.decodeErrorResult)({
4420
+ abi: AppController_default,
4421
+ data: callError.data
4422
+ });
4423
+ const formattedError = formatAppControllerError(decoded);
4424
+ revertReason = formattedError.message;
4425
+ } catch {
4426
+ revertReason = callError.message || "Unknown reason";
4427
+ }
4428
+ } else {
4429
+ revertReason = callError.message || "Unknown reason";
4430
+ }
4431
+ }
4432
+ logger.error(`${txDescription} transaction (hash: ${hash}) reverted: ${revertReason}`);
4433
+ throw new Error(`${txDescription} transaction (hash: ${hash}) reverted: ${revertReason}`);
4434
+ }
4435
+ return hash;
4436
+ }
4437
+ function formatAppControllerError(decoded) {
4438
+ const errorName = decoded.errorName;
4439
+ switch (errorName) {
4440
+ case "MaxActiveAppsExceeded":
4441
+ return new Error(
4442
+ "you have reached your app deployment limit. To request access or increase your limit, please visit https://onboarding.eigencloud.xyz/ or reach out to the Eigen team"
4443
+ );
4444
+ case "GlobalMaxActiveAppsExceeded":
4445
+ return new Error(
4446
+ "the platform has reached the maximum number of active apps. please try again later"
4447
+ );
4448
+ case "InvalidPermissions":
4449
+ return new Error("you don't have permission to perform this operation");
4450
+ case "AppAlreadyExists":
4451
+ return new Error("an app with this owner and salt already exists");
4452
+ case "AppDoesNotExist":
4453
+ return new Error("the specified app does not exist");
4454
+ case "InvalidAppStatus":
4455
+ return new Error("the app is in an invalid state for this operation");
4456
+ case "MoreThanOneArtifact":
4457
+ return new Error("only one artifact is allowed per release");
4458
+ case "InvalidSignature":
4459
+ return new Error("invalid signature provided");
4460
+ case "SignatureExpired":
4461
+ return new Error("the provided signature has expired");
4462
+ case "InvalidReleaseMetadataURI":
4463
+ return new Error("invalid release metadata URI provided");
4464
+ case "InvalidShortString":
4465
+ return new Error("invalid short string format");
4466
+ default:
4467
+ return new Error(`contract error: ${errorName}`);
4468
+ }
4469
+ }
4470
+ async function getActiveAppCount(publicClient, environmentConfig, user) {
4471
+ const count = await publicClient.readContract({
4472
+ address: environmentConfig.appControllerAddress,
4473
+ abi: AppController_default,
4474
+ functionName: "getActiveAppCount",
4475
+ args: [user]
4476
+ });
4477
+ return Number(count);
4478
+ }
4479
+ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
4480
+ const quota = await publicClient.readContract({
4481
+ address: environmentConfig.appControllerAddress,
4482
+ abi: AppController_default,
4483
+ functionName: "getMaxActiveAppsPerUser",
4484
+ args: [user]
4485
+ });
4486
+ return Number(quota);
4487
+ }
4488
+ async function getAppsByCreator(publicClient, environmentConfig, creator, offset, limit) {
4489
+ const result = await publicClient.readContract({
2883
4490
  address: environmentConfig.appControllerAddress,
2884
4491
  abi: AppController_default,
2885
4492
  functionName: "getAppsByCreator",
@@ -2907,7 +4514,13 @@ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 10
2907
4514
  const allApps = [];
2908
4515
  const allConfigs = [];
2909
4516
  while (true) {
2910
- const { apps, appConfigs } = await getAppsByDeveloper(publicClient, env, developer, offset, pageSize);
4517
+ const { apps, appConfigs } = await getAppsByDeveloper(
4518
+ publicClient,
4519
+ env,
4520
+ developer,
4521
+ offset,
4522
+ pageSize
4523
+ );
2911
4524
  if (apps.length === 0) break;
2912
4525
  allApps.push(...apps);
2913
4526
  allConfigs.push(...appConfigs);
@@ -2919,6 +4532,74 @@ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 10
2919
4532
  appConfigs: allConfigs
2920
4533
  };
2921
4534
  }
4535
+ async function suspend(options, logger = noopLogger) {
4536
+ const { walletClient, publicClient, environmentConfig, account, apps } = options;
4537
+ const suspendData = (0, import_viem5.encodeFunctionData)({
4538
+ abi: AppController_default,
4539
+ functionName: "suspend",
4540
+ args: [account, apps]
4541
+ });
4542
+ const pendingMessage = `Suspending ${apps.length} app(s)...`;
4543
+ return sendAndWaitForTransaction(
4544
+ {
4545
+ walletClient,
4546
+ publicClient,
4547
+ environmentConfig,
4548
+ to: environmentConfig.appControllerAddress,
4549
+ data: suspendData,
4550
+ pendingMessage,
4551
+ txDescription: "Suspend"
4552
+ },
4553
+ logger
4554
+ );
4555
+ }
4556
+ async function isDelegated(options) {
4557
+ const { publicClient, environmentConfig, address } = options;
4558
+ return checkERC7702Delegation(
4559
+ publicClient,
4560
+ address,
4561
+ environmentConfig.erc7702DelegatorAddress
4562
+ );
4563
+ }
4564
+ async function undelegate(options, logger = noopLogger) {
4565
+ const { walletClient, publicClient, environmentConfig } = options;
4566
+ const account = walletClient.account;
4567
+ if (!account) {
4568
+ throw new Error("WalletClient must have an account attached");
4569
+ }
4570
+ const chain = getChainFromID(environmentConfig.chainID);
4571
+ const transactionNonce = await publicClient.getTransactionCount({
4572
+ address: account.address,
4573
+ blockTag: "pending"
4574
+ });
4575
+ const chainId = await publicClient.getChainId();
4576
+ const authorizationNonce = BigInt(transactionNonce) + 1n;
4577
+ logger.debug("Signing undelegate authorization");
4578
+ const signedAuthorization = await walletClient.signAuthorization({
4579
+ contractAddress: "0x0000000000000000000000000000000000000000",
4580
+ chainId,
4581
+ nonce: Number(authorizationNonce),
4582
+ account
4583
+ });
4584
+ const authorizationList = [signedAuthorization];
4585
+ const hash = await walletClient.sendTransaction({
4586
+ account,
4587
+ to: account.address,
4588
+ // Send to self
4589
+ data: "0x",
4590
+ // Empty data
4591
+ value: 0n,
4592
+ authorizationList,
4593
+ chain
4594
+ });
4595
+ logger.info(`Transaction sent: ${hash}`);
4596
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
4597
+ if (receipt.status === "reverted") {
4598
+ logger.error(`Undelegate transaction (hash: ${hash}) reverted`);
4599
+ throw new Error(`Undelegate transaction (hash: ${hash}) reverted`);
4600
+ }
4601
+ return hash;
4602
+ }
2922
4603
 
2923
4604
  // src/client/common/contract/encoders.ts
2924
4605
  var import_viem6 = require("viem");
@@ -2948,33 +4629,396 @@ function encodeTerminateAppData(appId) {
2948
4629
  args: [appId]
2949
4630
  });
2950
4631
  }
4632
+
4633
+ // src/client/common/auth/siwe.ts
4634
+ var import_siwe = require("siwe");
4635
+ var generateNonce = import_siwe.generateNonce;
4636
+ function createSiweMessage(params) {
4637
+ const now = /* @__PURE__ */ new Date();
4638
+ const nonce = params.nonce || generateNonce();
4639
+ const issuedAt = params.issuedAt || now;
4640
+ const expirationTime = params.expirationTime || new Date(now.getTime() + 24 * 60 * 60 * 1e3);
4641
+ const siweMessage = new import_siwe.SiweMessage({
4642
+ domain: params.domain,
4643
+ address: params.address,
4644
+ statement: params.statement,
4645
+ uri: params.uri,
4646
+ version: "1",
4647
+ chainId: params.chainId,
4648
+ nonce,
4649
+ issuedAt: issuedAt.toISOString(),
4650
+ expirationTime: expirationTime.toISOString(),
4651
+ notBefore: params.notBefore?.toISOString(),
4652
+ requestId: params.requestId,
4653
+ resources: params.resources
4654
+ });
4655
+ return {
4656
+ message: siweMessage.prepareMessage(),
4657
+ params: {
4658
+ address: params.address,
4659
+ chainId: params.chainId,
4660
+ domain: params.domain,
4661
+ uri: params.uri,
4662
+ nonce,
4663
+ issuedAt,
4664
+ statement: params.statement,
4665
+ expirationTime,
4666
+ notBefore: params.notBefore,
4667
+ requestId: params.requestId,
4668
+ resources: params.resources
4669
+ }
4670
+ };
4671
+ }
4672
+ function parseSiweMessage(message) {
4673
+ try {
4674
+ const siweMessage = new import_siwe.SiweMessage(message);
4675
+ return {
4676
+ address: siweMessage.address,
4677
+ chainId: siweMessage.chainId,
4678
+ domain: siweMessage.domain,
4679
+ uri: siweMessage.uri,
4680
+ nonce: siweMessage.nonce,
4681
+ statement: siweMessage.statement,
4682
+ issuedAt: siweMessage.issuedAt ? new Date(siweMessage.issuedAt) : void 0,
4683
+ expirationTime: siweMessage.expirationTime ? new Date(siweMessage.expirationTime) : void 0,
4684
+ notBefore: siweMessage.notBefore ? new Date(siweMessage.notBefore) : void 0,
4685
+ requestId: siweMessage.requestId,
4686
+ resources: siweMessage.resources
4687
+ };
4688
+ } catch {
4689
+ return null;
4690
+ }
4691
+ }
4692
+ function isSiweMessageExpired(params) {
4693
+ if (!params.expirationTime) return false;
4694
+ return /* @__PURE__ */ new Date() > params.expirationTime;
4695
+ }
4696
+ function isSiweMessageNotYetValid(params) {
4697
+ if (!params.notBefore) return false;
4698
+ return /* @__PURE__ */ new Date() < params.notBefore;
4699
+ }
4700
+
4701
+ // src/client/common/hooks/useComputeSession.ts
4702
+ var import_react = require("react");
4703
+ function useComputeSession(config) {
4704
+ const {
4705
+ baseUrl,
4706
+ refreshInterval = 6e4,
4707
+ // 1 minute default
4708
+ checkOnMount = true,
4709
+ onSessionExpired,
4710
+ onSessionRefreshed,
4711
+ onError
4712
+ } = config;
4713
+ const [session, setSession] = (0, import_react.useState)(null);
4714
+ const [isLoading, setIsLoading] = (0, import_react.useState)(checkOnMount);
4715
+ const [error, setError] = (0, import_react.useState)(null);
4716
+ const wasAuthenticatedRef = (0, import_react.useRef)(false);
4717
+ const isMountedRef = (0, import_react.useRef)(true);
4718
+ const refreshIntervalRef = (0, import_react.useRef)(null);
4719
+ const apiConfig = { baseUrl };
4720
+ const checkSession = (0, import_react.useCallback)(async () => {
4721
+ try {
4722
+ const sessionInfo = await getComputeApiSession(apiConfig);
4723
+ if (!isMountedRef.current) {
4724
+ return sessionInfo;
4725
+ }
4726
+ setSession(sessionInfo);
4727
+ setError(null);
4728
+ if (wasAuthenticatedRef.current && !sessionInfo.authenticated) {
4729
+ onSessionExpired?.();
4730
+ }
4731
+ wasAuthenticatedRef.current = sessionInfo.authenticated;
4732
+ if (sessionInfo.authenticated) {
4733
+ onSessionRefreshed?.(sessionInfo);
4734
+ }
4735
+ return sessionInfo;
4736
+ } catch (err) {
4737
+ if (!isMountedRef.current) {
4738
+ throw err;
4739
+ }
4740
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Failed to check session: ${String(err)}`, "UNKNOWN");
4741
+ setError(sessionError);
4742
+ onError?.(sessionError);
4743
+ const fallbackSession = { authenticated: false };
4744
+ setSession(fallbackSession);
4745
+ return fallbackSession;
4746
+ }
4747
+ }, [baseUrl, onSessionExpired, onSessionRefreshed, onError]);
4748
+ const refresh = (0, import_react.useCallback)(async () => {
4749
+ setIsLoading(true);
4750
+ try {
4751
+ return await checkSession();
4752
+ } finally {
4753
+ if (isMountedRef.current) {
4754
+ setIsLoading(false);
4755
+ }
4756
+ }
4757
+ }, [checkSession]);
4758
+ const login = (0, import_react.useCallback)(
4759
+ async (params, signMessage) => {
4760
+ setIsLoading(true);
4761
+ setError(null);
4762
+ try {
4763
+ let domain = params.domain;
4764
+ let uri = params.uri;
4765
+ if (typeof window !== "undefined") {
4766
+ domain = domain || window.location.host;
4767
+ uri = uri || window.location.origin;
4768
+ }
4769
+ if (!domain || !uri) {
4770
+ throw new SessionError(
4771
+ "domain and uri are required when not in browser environment",
4772
+ "INVALID_MESSAGE"
4773
+ );
4774
+ }
4775
+ const siweMessage = createSiweMessage({
4776
+ ...params,
4777
+ domain,
4778
+ uri,
4779
+ statement: params.statement || "Sign in to EigenCloud Compute API"
4780
+ });
4781
+ const signature = await signMessage({ message: siweMessage.message });
4782
+ await loginToComputeApi(apiConfig, {
4783
+ message: siweMessage.message,
4784
+ signature
4785
+ });
4786
+ const sessionInfo = await checkSession();
4787
+ if (!isMountedRef.current) {
4788
+ return sessionInfo;
4789
+ }
4790
+ wasAuthenticatedRef.current = sessionInfo.authenticated;
4791
+ return sessionInfo;
4792
+ } catch (err) {
4793
+ if (!isMountedRef.current) {
4794
+ throw err;
4795
+ }
4796
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Login failed: ${String(err)}`, "UNKNOWN");
4797
+ setError(sessionError);
4798
+ onError?.(sessionError);
4799
+ throw sessionError;
4800
+ } finally {
4801
+ if (isMountedRef.current) {
4802
+ setIsLoading(false);
4803
+ }
4804
+ }
4805
+ },
4806
+ [baseUrl, checkSession, onError]
4807
+ );
4808
+ const logout = (0, import_react.useCallback)(async () => {
4809
+ setIsLoading(true);
4810
+ setError(null);
4811
+ try {
4812
+ await logoutFromComputeApi(apiConfig);
4813
+ if (!isMountedRef.current) {
4814
+ return;
4815
+ }
4816
+ const newSession = { authenticated: false };
4817
+ setSession(newSession);
4818
+ wasAuthenticatedRef.current = false;
4819
+ } catch (err) {
4820
+ if (!isMountedRef.current) {
4821
+ throw err;
4822
+ }
4823
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Logout failed: ${String(err)}`, "UNKNOWN");
4824
+ setError(sessionError);
4825
+ onError?.(sessionError);
4826
+ setSession({ authenticated: false });
4827
+ wasAuthenticatedRef.current = false;
4828
+ } finally {
4829
+ if (isMountedRef.current) {
4830
+ setIsLoading(false);
4831
+ }
4832
+ }
4833
+ }, [baseUrl, onError]);
4834
+ const clearError = (0, import_react.useCallback)(() => {
4835
+ setError(null);
4836
+ }, []);
4837
+ (0, import_react.useEffect)(() => {
4838
+ isMountedRef.current = true;
4839
+ if (checkOnMount) {
4840
+ checkSession().finally(() => {
4841
+ if (isMountedRef.current) {
4842
+ setIsLoading(false);
4843
+ }
4844
+ });
4845
+ }
4846
+ return () => {
4847
+ isMountedRef.current = false;
4848
+ };
4849
+ }, [checkOnMount, checkSession]);
4850
+ (0, import_react.useEffect)(() => {
4851
+ if (refreshInterval <= 0) {
4852
+ return;
4853
+ }
4854
+ if (refreshIntervalRef.current) {
4855
+ clearInterval(refreshIntervalRef.current);
4856
+ }
4857
+ refreshIntervalRef.current = setInterval(() => {
4858
+ if (wasAuthenticatedRef.current) {
4859
+ checkSession();
4860
+ }
4861
+ }, refreshInterval);
4862
+ return () => {
4863
+ if (refreshIntervalRef.current) {
4864
+ clearInterval(refreshIntervalRef.current);
4865
+ refreshIntervalRef.current = null;
4866
+ }
4867
+ };
4868
+ }, [refreshInterval, checkSession]);
4869
+ return {
4870
+ session,
4871
+ isLoading,
4872
+ error,
4873
+ isAuthenticated: session?.authenticated ?? false,
4874
+ login,
4875
+ logout,
4876
+ refresh,
4877
+ clearError
4878
+ };
4879
+ }
4880
+
4881
+ // src/client/common/encryption/kms.ts
4882
+ var import_jose = require("jose");
4883
+ function getAppProtectedHeaders(appID) {
4884
+ return {
4885
+ "x-eigenx-app-id": appID
4886
+ };
4887
+ }
4888
+ async function encryptRSAOAEPAndAES256GCM(encryptionKeyPEM, plaintext, protectedHeaders) {
4889
+ const pemString = typeof encryptionKeyPEM === "string" ? encryptionKeyPEM : encryptionKeyPEM.toString("utf-8");
4890
+ const publicKey = await (0, import_jose.importSPKI)(pemString, "RSA-OAEP-256", {
4891
+ extractable: true
4892
+ });
4893
+ const header = {
4894
+ alg: "RSA-OAEP-256",
4895
+ // Key encryption algorithm (SHA-256)
4896
+ enc: "A256GCM",
4897
+ // Content encryption algorithm
4898
+ ...protectedHeaders || {}
4899
+ // Add custom protected headers
4900
+ };
4901
+ const plaintextBytes = new Uint8Array(plaintext);
4902
+ const jwe = await new import_jose.CompactEncrypt(plaintextBytes).setProtectedHeader(header).encrypt(publicKey);
4903
+ return jwe;
4904
+ }
4905
+
4906
+ // keys/mainnet-alpha/prod/kms-encryption-public-key.pem
4907
+ var kms_encryption_public_key_default = "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0kHU86k17ofCIGcJKDcf\nAFurFhSLeWmOL0bwWLCeVnTPG0MMHtJOq+woE0XXSWw6lzm+jzavBBTwKde1dgal\nAp91vULAZFMUpiUdd2dNUVtvU89qW0Pgf1Eu5FDj7BkY/SnyECbWJM4ga0BmpiGy\nnQwLNN9mMGhjVoVLn2zwEGZ7JzS9Nz11EZKO/k/9DcO6LaoIFmKuvVf3jl6lvZg8\naeA0LoZXjkycHlRUt/kfKwZnhakUaYHP1ksV7ZNmolS5GYDTSKGB2KPPNR1s4/Xu\nu8zeEFC8HuGRU8XuuBeaAunitnGhbNVREUNJGff6HZOGB6CIFNXjbQETeZ3p5uro\n0v+hd1QqQYBv7+DEaMCmGnJNGAyIMr2mn4vr7wGsIj0HonlSHmQ8rmdUhL2ocNTc\nLhKgZiZmBuDpSbFW/r53R2G7CHcqaqGeUBnT54QCH4zsYKw0/4dOtwFxQpTyBf9/\n+k+KaWEJYKkx9d9OzKGyAvzrTDVOFoajddiJ6LPvRlMdOUQr3hl4IAC0/nh9lhHq\nD0R+i5WAU96TkdAe7B7iTGH2D22k0KUPR6Q9W3aF353SLxQAMPNrgG4QQufAdRJn\nAF+8ntun5TkTqjTWRSwAsUJZ1z4wb96DympWJbDi0OciJRZ3Fz3j9+amC43yCHGg\naaEMjdt35ewbztUSc04F10MCAwEAAQ==\n-----END PUBLIC KEY-----";
4908
+
4909
+ // keys/mainnet-alpha/prod/kms-signing-public-key.pem
4910
+ var kms_signing_public_key_default = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfxbhXJjH4D0DH/iW5/rK1HzWS+f9\nEyooZTrCYjCfezuOEmRuOWNaZLvwXN8SdzrvjWA7gSvOS85hLzp4grANRQ==\n-----END PUBLIC KEY-----";
4911
+
4912
+ // keys/sepolia/dev/kms-encryption-public-key.pem
4913
+ var kms_encryption_public_key_default2 = "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr/vqttU6aXX35HtsXavU\n5teysunDzZB3HyaFM4qcuRnqj+70KxqLOwZsERN5SwZ/56Jm8T2ds1CcXsQCMUMw\n+MPlsF6KMGfzghLtYHONwvKLnn+U9y886aAay6W8a0A7O7YCZehNYD3kQnCXjOIc\nMj6v8AEvMw+w/lNabjRXnwSBMKVIGp/cSL0hGwt8fGoC3TsxQN9opzvU1Z4rAw9K\na119l6dlPnqezDva378TCaXDjqKe/jSZOI1CcYpaSK2SJ+95Wbvte5j3lXbg1oT2\n0rXeJUHEJ68QxMtJplfw0Sg+Ek4CUJ2c/kbdg0u7sIIO5wcB4WHL/Lfbw2XPmcBI\nt0r0EC575D3iHF/aI01Ms2IRA0GDeHnNcr5FJLWJljTjNLEt4tFITrXwBe1Ealm3\nNCxamApl5bBSwQ72Gb5fiQFwB8Fl2/XG3wfGTFInFEvWE4c/H8dtu1wHTsyEFZcG\nB47IkD5GBSZq90Hd9xuZva55dxGpqUVrEJO88SqHGP9Oa+HLTYdEe5AR5Hitw4Mu\ndk1cCH+X5OqY9dfpdoCNbKAM0N2SJvNAnDTU2JKGYheXrnDslXR6atBmU5gDkH+W\nQVryDYl9xbwWIACMQsAQjrrtKw5xqJ4V89+06FN/wyEVF7KWAcJ4AhKiVnCvLqzb\nBbISc+gOkRsefhCDJVPEKDkCAwEAAQ==\n-----END PUBLIC KEY-----";
4914
+
4915
+ // keys/sepolia/dev/kms-signing-public-key.pem
4916
+ var kms_signing_public_key_default2 = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb2Q88/cxdic2xi4jS2V0dtYHjLwq\n4wVFBFmaY8TTXoMXNggKEdU6PuE8EovocVKMpw3SIlaM27z9uxksNVL2xw==\n-----END PUBLIC KEY-----\n";
4917
+
4918
+ // keys/sepolia/prod/kms-encryption-public-key.pem
4919
+ var kms_encryption_public_key_default3 = "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApDvk8pAivkgtiC5li5MP\nxMTJDduTeorBl18ynrooTxp2BwwgPwXfXbJaCA0qRubvc0aO2uh2VDrPM27CqMLH\no2S9YLtpLii4A1Nl7SE/MdWKWdG6v94xNGpc2YyPP7yWtHfqOkgDWp8sokl3Uq/9\nMS0pjUaI7RyS5boCTy8Qw90BxGMpucjOmqm+luw4EdPWZCrgriUR2bbGRRgAmrT1\nK4ou4IgPp799r120hwHbCWxnOvLdQdpiv2507b900xS/3yZahhnHCAn66146LU/f\nBrRpQKSM0qSpktXrrc9MH/ru2VLR5cGLp89ZcZMQA9cRGglWM5XWVY3Ti2TPJ6Kd\nAn1d7qNkGJaSdVa3x3HkOf6c6HeTyqis5/L/6L+PFhUsTRbmKg1FtwD+3xxdyf7h\nabFxryE9rv+WatHL6r6z5ztV0znJ/Fpfs5A45FWA6pfb28fA59RGpi/DQ8RxgdCH\nnZRNvdz8dTgRaXSPgkfGXBcCFqb/QhFmad7XbWDthGzfhbPOxNPtiaGRQ1Dr/Pgq\nn0ugdLbRQLmDOAFgaQcnr0U4y1TUlWJnvoZMETkVN7gmITtXA4F324ALT7Rd+Lgk\nHikW5vG+NjAEwXfPsK0YzT+VbHd7o1lbru9UxiDlN03XVEkz/oRQi47CvSTo3FSr\n5dB4lz8kov3UUcNJfQFZolMCAwEAAQ==\n-----END PUBLIC KEY-----";
4920
+
4921
+ // keys/sepolia/prod/kms-signing-public-key.pem
4922
+ var kms_signing_public_key_default3 = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsk6ZdmmvBqFfKHs+1cYjIemRGN7h\n1NatIEitFRyx+3q8wmTJ9LknTE1FwWBLcCNTseJDti8Rh+SaVxfGOyJuuA==\n-----END PUBLIC KEY-----";
4923
+
4924
+ // src/client/common/utils/keys.ts
4925
+ var KEYS = {
4926
+ "mainnet-alpha": {
4927
+ prod: {
4928
+ encryption: kms_encryption_public_key_default,
4929
+ signing: kms_signing_public_key_default
4930
+ }
4931
+ },
4932
+ sepolia: {
4933
+ dev: {
4934
+ encryption: kms_encryption_public_key_default2,
4935
+ signing: kms_signing_public_key_default2
4936
+ },
4937
+ prod: {
4938
+ encryption: kms_encryption_public_key_default3,
4939
+ signing: kms_signing_public_key_default3
4940
+ }
4941
+ }
4942
+ };
4943
+ function getKMSKeysForEnvironment(environment, build = "prod") {
4944
+ const envKeys = KEYS[environment];
4945
+ if (!envKeys) {
4946
+ throw new Error(`No keys found for environment: ${environment}`);
4947
+ }
4948
+ const buildKeys = envKeys[build];
4949
+ if (!buildKeys) {
4950
+ throw new Error(`No keys found for environment: ${environment}, build: ${build}`);
4951
+ }
4952
+ return {
4953
+ encryptionKey: Buffer.from(buildKeys.encryption),
4954
+ signingKey: Buffer.from(buildKeys.signing)
4955
+ };
4956
+ }
2951
4957
  // Annotate the CommonJS export names for ESM import in node:
2952
4958
  0 && (module.exports = {
4959
+ BillingApiClient,
4960
+ BuildApiClient,
4961
+ SessionError,
2953
4962
  UserApiClient,
4963
+ addHexPrefix,
2954
4964
  assertValidImageReference,
2955
4965
  assertValidPrivateKey,
4966
+ calculateAppID,
4967
+ checkERC7702Delegation,
4968
+ createSiweMessage,
4969
+ deployApp,
2956
4970
  encodeStartAppData,
2957
4971
  encodeStopAppData,
2958
4972
  encodeTerminateAppData,
4973
+ encryptRSAOAEPAndAES256GCM,
2959
4974
  estimateBatchGas,
2960
4975
  estimateTransactionGas,
4976
+ executeBatch,
4977
+ executeDeployBatch,
4978
+ executeDeployBatched,
4979
+ executeDeploySequential,
4980
+ executeUpgradeBatch,
2961
4981
  extractAppNameFromImage,
2962
4982
  formatETH,
2963
4983
  generateNewPrivateKey,
4984
+ generateNonce,
2964
4985
  getActiveAppCount,
2965
4986
  getAllAppsByDeveloper,
4987
+ getAppProtectedHeaders,
2966
4988
  getAppsByCreator,
2967
4989
  getAppsByDeveloper,
2968
4990
  getAvailableEnvironments,
4991
+ getBillingEnvironmentConfig,
2969
4992
  getBuildType,
4993
+ getChainFromID,
4994
+ getComputeApiSession,
2970
4995
  getEnvironmentConfig,
4996
+ getKMSKeysForEnvironment,
2971
4997
  getMaxActiveAppsPerUser,
4998
+ isDelegated,
2972
4999
  isEnvironmentAvailable,
2973
5000
  isMainnet,
5001
+ isSessionValid,
5002
+ isSiweMessageExpired,
5003
+ isSiweMessageNotYetValid,
2974
5004
  isSubscriptionActive,
5005
+ loginToComputeApi,
5006
+ logoutFromComputeApi,
5007
+ noopLogger,
5008
+ parseSiweMessage,
5009
+ prepareDeployBatch,
5010
+ prepareUpgradeBatch,
2975
5011
  sanitizeString,
2976
5012
  sanitizeURL,
2977
5013
  sanitizeXURL,
5014
+ sendAndWaitForTransaction,
5015
+ stripHexPrefix,
5016
+ supportsEIP5792,
5017
+ supportsEIP7702,
5018
+ suspend,
5019
+ undelegate,
5020
+ upgradeApp,
5021
+ useComputeSession,
2978
5022
  validateAppID,
2979
5023
  validateAppName,
2980
5024
  validateCreateAppParams,