@layr-labs/ecloud-sdk 0.2.1-dev → 0.3.0-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
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,34 +30,216 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/client/common/auth/session.ts
34
+ var session_exports = {};
35
+ __export(session_exports, {
36
+ SessionError: () => SessionError,
37
+ getComputeApiSession: () => getComputeApiSession,
38
+ isSessionValid: () => isSessionValid,
39
+ loginToComputeApi: () => loginToComputeApi,
40
+ logoutFromComputeApi: () => logoutFromComputeApi
41
+ });
42
+ function stripHexPrefix2(hex) {
43
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
44
+ }
45
+ async function parseErrorResponse(response) {
46
+ try {
47
+ const data = await response.json();
48
+ return data.error || response.statusText;
49
+ } catch {
50
+ return response.statusText;
51
+ }
52
+ }
53
+ async function loginToComputeApi(config, request) {
54
+ let response;
55
+ try {
56
+ response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
57
+ method: "POST",
58
+ credentials: "include",
59
+ // Include cookies for session management
60
+ headers: {
61
+ "Content-Type": "application/json"
62
+ },
63
+ body: JSON.stringify({
64
+ message: request.message,
65
+ signature: stripHexPrefix2(request.signature)
66
+ })
67
+ });
68
+ } catch (error) {
69
+ throw new SessionError(
70
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
71
+ "NETWORK_ERROR"
72
+ );
73
+ }
74
+ if (!response.ok) {
75
+ const errorMessage = await parseErrorResponse(response);
76
+ const status = response.status;
77
+ if (status === 400) {
78
+ if (errorMessage.toLowerCase().includes("siwe")) {
79
+ throw new SessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
80
+ }
81
+ throw new SessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
82
+ }
83
+ if (status === 401) {
84
+ throw new SessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
85
+ }
86
+ throw new SessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
87
+ }
88
+ const data = await response.json();
89
+ return {
90
+ success: data.success,
91
+ address: data.address
92
+ };
93
+ }
94
+ async function getComputeApiSession(config) {
95
+ let response;
96
+ try {
97
+ response = await fetch(`${config.baseUrl}/auth/session`, {
98
+ method: "GET",
99
+ credentials: "include",
100
+ // Include cookies for session management
101
+ headers: {
102
+ "Content-Type": "application/json"
103
+ }
104
+ });
105
+ } catch {
106
+ return {
107
+ authenticated: false
108
+ };
109
+ }
110
+ if (response.status === 401) {
111
+ return {
112
+ authenticated: false
113
+ };
114
+ }
115
+ if (!response.ok) {
116
+ const errorMessage = await parseErrorResponse(response);
117
+ throw new SessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
118
+ }
119
+ const data = await response.json();
120
+ return {
121
+ authenticated: data.authenticated,
122
+ address: data.address,
123
+ chainId: data.chain_id
124
+ };
125
+ }
126
+ async function logoutFromComputeApi(config) {
127
+ let response;
128
+ try {
129
+ response = await fetch(`${config.baseUrl}/auth/logout`, {
130
+ method: "POST",
131
+ credentials: "include",
132
+ // Include cookies for session management
133
+ headers: {
134
+ "Content-Type": "application/json"
135
+ }
136
+ });
137
+ } catch (error) {
138
+ throw new SessionError(
139
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
140
+ "NETWORK_ERROR"
141
+ );
142
+ }
143
+ if (response.status === 401) {
144
+ return;
145
+ }
146
+ if (!response.ok) {
147
+ const errorMessage = await parseErrorResponse(response);
148
+ throw new SessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
149
+ }
150
+ }
151
+ async function isSessionValid(config) {
152
+ const session = await getComputeApiSession(config);
153
+ return session.authenticated;
154
+ }
155
+ var SessionError;
156
+ var init_session = __esm({
157
+ "src/client/common/auth/session.ts"() {
158
+ "use strict";
159
+ SessionError = class extends Error {
160
+ constructor(message, code, statusCode) {
161
+ super(message);
162
+ this.code = code;
163
+ this.statusCode = statusCode;
164
+ this.name = "SessionError";
165
+ }
166
+ };
167
+ }
168
+ });
169
+
30
170
  // src/browser.ts
31
171
  var browser_exports = {};
32
172
  __export(browser_exports, {
173
+ BillingApiClient: () => BillingApiClient,
174
+ BillingSessionError: () => BillingSessionError,
175
+ BuildApiClient: () => BuildApiClient,
176
+ SessionError: () => SessionError,
33
177
  UserApiClient: () => UserApiClient,
178
+ addHexPrefix: () => addHexPrefix,
34
179
  assertValidImageReference: () => assertValidImageReference,
35
180
  assertValidPrivateKey: () => assertValidPrivateKey,
181
+ calculateAppID: () => calculateAppID,
182
+ checkERC7702Delegation: () => checkERC7702Delegation,
183
+ createSiweMessage: () => createSiweMessage,
184
+ deployApp: () => deployApp,
36
185
  encodeStartAppData: () => encodeStartAppData,
37
186
  encodeStopAppData: () => encodeStopAppData,
38
187
  encodeTerminateAppData: () => encodeTerminateAppData,
188
+ encryptRSAOAEPAndAES256GCM: () => encryptRSAOAEPAndAES256GCM,
39
189
  estimateBatchGas: () => estimateBatchGas,
40
190
  estimateTransactionGas: () => estimateTransactionGas,
191
+ executeBatch: () => executeBatch,
192
+ executeDeployBatch: () => executeDeployBatch,
193
+ executeDeployBatched: () => executeDeployBatched,
194
+ executeDeploySequential: () => executeDeploySequential,
195
+ executeUpgradeBatch: () => executeUpgradeBatch,
41
196
  extractAppNameFromImage: () => extractAppNameFromImage,
42
197
  formatETH: () => formatETH,
43
198
  generateNewPrivateKey: () => generateNewPrivateKey,
199
+ generateNonce: () => generateNonce,
44
200
  getActiveAppCount: () => getActiveAppCount,
45
201
  getAllAppsByDeveloper: () => getAllAppsByDeveloper,
202
+ getAppProtectedHeaders: () => getAppProtectedHeaders,
46
203
  getAppsByCreator: () => getAppsByCreator,
47
204
  getAppsByDeveloper: () => getAppsByDeveloper,
48
205
  getAvailableEnvironments: () => getAvailableEnvironments,
206
+ getBillingApiSession: () => getBillingApiSession,
207
+ getBillingEnvironmentConfig: () => getBillingEnvironmentConfig,
49
208
  getBuildType: () => getBuildType,
209
+ getChainFromID: () => getChainFromID,
210
+ getComputeApiSession: () => getComputeApiSession,
50
211
  getEnvironmentConfig: () => getEnvironmentConfig,
212
+ getKMSKeysForEnvironment: () => getKMSKeysForEnvironment,
51
213
  getMaxActiveAppsPerUser: () => getMaxActiveAppsPerUser,
214
+ isBillingSessionValid: () => isBillingSessionValid,
215
+ isDelegated: () => isDelegated,
52
216
  isEnvironmentAvailable: () => isEnvironmentAvailable,
53
217
  isMainnet: () => isMainnet,
218
+ isSessionValid: () => isSessionValid,
219
+ isSiweMessageExpired: () => isSiweMessageExpired,
220
+ isSiweMessageNotYetValid: () => isSiweMessageNotYetValid,
54
221
  isSubscriptionActive: () => isSubscriptionActive,
222
+ loginToBillingApi: () => loginToBillingApi,
223
+ loginToBothApis: () => loginToBothApis,
224
+ loginToComputeApi: () => loginToComputeApi,
225
+ logoutFromBillingApi: () => logoutFromBillingApi,
226
+ logoutFromBothApis: () => logoutFromBothApis,
227
+ logoutFromComputeApi: () => logoutFromComputeApi,
228
+ noopLogger: () => noopLogger,
229
+ parseSiweMessage: () => parseSiweMessage,
230
+ prepareDeployBatch: () => prepareDeployBatch,
231
+ prepareUpgradeBatch: () => prepareUpgradeBatch,
55
232
  sanitizeString: () => sanitizeString,
56
233
  sanitizeURL: () => sanitizeURL,
57
234
  sanitizeXURL: () => sanitizeXURL,
235
+ sendAndWaitForTransaction: () => sendAndWaitForTransaction,
236
+ stripHexPrefix: () => stripHexPrefix,
237
+ supportsEIP5792: () => supportsEIP5792,
238
+ supportsEIP7702: () => supportsEIP7702,
239
+ suspend: () => suspend,
240
+ undelegate: () => undelegate,
241
+ upgradeApp: () => upgradeApp,
242
+ useComputeSession: () => useComputeSession,
58
243
  validateAppID: () => validateAppID,
59
244
  validateAppName: () => validateAppName,
60
245
  validateCreateAppParams: () => validateCreateAppParams,
@@ -69,6 +254,18 @@ __export(browser_exports, {
69
254
  });
70
255
  module.exports = __toCommonJS(browser_exports);
71
256
 
257
+ // src/client/common/types/index.ts
258
+ var noopLogger = {
259
+ debug: () => {
260
+ },
261
+ info: () => {
262
+ },
263
+ warn: () => {
264
+ },
265
+ error: () => {
266
+ }
267
+ };
268
+
72
269
  // src/client/common/config/environment.ts
73
270
  var SEPOLIA_CHAIN_ID = 11155111;
74
271
  var MAINNET_CHAIN_ID = 1;
@@ -83,6 +280,14 @@ var ChainAddresses = {
83
280
  PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
84
281
  }
85
282
  };
283
+ var BILLING_ENVIRONMENTS = {
284
+ dev: {
285
+ billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
286
+ },
287
+ prod: {
288
+ billingApiServerURL: "https://billingapi.eigencloud.xyz"
289
+ }
290
+ };
86
291
  var ENVIRONMENTS = {
87
292
  "sepolia-dev": {
88
293
  name: "sepolia",
@@ -141,6 +346,13 @@ function getEnvironmentConfig(environment, chainID) {
141
346
  chainID: BigInt(resolvedChainID)
142
347
  };
143
348
  }
349
+ function getBillingEnvironmentConfig(build) {
350
+ const config = BILLING_ENVIRONMENTS[build];
351
+ if (!config) {
352
+ throw new Error(`Unknown billing environment: ${build}`);
353
+ }
354
+ return config;
355
+ }
144
356
  function getBuildType() {
145
357
  const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
146
358
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
@@ -174,8 +386,13 @@ var import_accounts = require("viem/accounts");
174
386
 
175
387
  // src/client/common/constants.ts
176
388
  var import_chains = require("viem/chains");
389
+ var SUPPORTED_CHAINS = [import_chains.mainnet, import_chains.sepolia];
177
390
 
178
391
  // src/client/common/utils/helpers.ts
392
+ function getChainFromID(chainID, fallback = import_chains2.sepolia) {
393
+ const id = Number(chainID);
394
+ return (0, import_viem.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback;
395
+ }
179
396
  function addHexPrefix(value) {
180
397
  return value.startsWith("0x") ? value : `0x${value}`;
181
398
  }
@@ -409,8 +626,40 @@ async function calculatePermissionSignature(options) {
409
626
  });
410
627
  return { signature, digest };
411
628
  }
629
+ var generateBillingSigData = (product, expiry) => {
630
+ return {
631
+ domain: {
632
+ name: "EigenCloud Billing API",
633
+ version: "1"
634
+ },
635
+ types: {
636
+ BillingAuth: [
637
+ { name: "product", type: "string" },
638
+ { name: "expiry", type: "uint256" }
639
+ ]
640
+ },
641
+ primaryType: "BillingAuth",
642
+ message: {
643
+ product,
644
+ expiry
645
+ }
646
+ };
647
+ };
648
+ async function calculateBillingAuthSignature(options) {
649
+ const { walletClient, product, expiry } = options;
650
+ const account = walletClient.account;
651
+ if (!account) {
652
+ throw new Error("WalletClient must have an account attached");
653
+ }
654
+ const signature = await walletClient.signTypedData({
655
+ account,
656
+ ...generateBillingSigData(product, expiry)
657
+ });
658
+ return { signature, expiry };
659
+ }
412
660
 
413
661
  // src/client/common/utils/userapi.ts
662
+ init_session();
414
663
  function isJsonObject(value) {
415
664
  return typeof value === "object" && value !== null && !Array.isArray(value);
416
665
  }
@@ -427,15 +676,16 @@ var CanViewAppLogsPermission = "0x2fd3f2fe";
427
676
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
428
677
  var CanUpdateAppProfilePermission = "0x036fef61";
429
678
  function getDefaultClientId() {
430
- const version = true ? "0.2.1-dev" : "0.0.0";
679
+ const version = true ? "0.3.0-dev" : "0.0.0";
431
680
  return `ecloud-sdk/v${version}`;
432
681
  }
433
682
  var UserApiClient = class {
434
- constructor(config, walletClient, publicClient, clientId) {
683
+ constructor(config, walletClient, publicClient, options) {
435
684
  this.config = config;
436
685
  this.walletClient = walletClient;
437
686
  this.publicClient = publicClient;
438
- this.clientId = clientId || getDefaultClientId();
687
+ this.clientId = options?.clientId || getDefaultClientId();
688
+ this.useSession = options?.useSession ?? false;
439
689
  }
440
690
  /**
441
691
  * Get the address of the connected wallet
@@ -523,7 +773,7 @@ var UserApiClient = class {
523
773
  const apps = result.apps || result.Apps || [];
524
774
  return apps.map((app, i) => ({
525
775
  address: app.address || appIDs[i],
526
- status: app.status || app.Status || ""
776
+ status: app.app_status || app.App_Status || ""
527
777
  }));
528
778
  }
529
779
  /**
@@ -555,9 +805,11 @@ var UserApiClient = class {
555
805
  const headers = {
556
806
  "x-client-id": this.clientId
557
807
  };
558
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
559
- const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
560
- Object.assign(headers, authHeaders);
808
+ if (!this.useSession) {
809
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
810
+ const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
811
+ Object.assign(headers, authHeaders);
812
+ }
561
813
  try {
562
814
  const response = await import_axios.default.post(endpoint, formData, {
563
815
  headers,
@@ -566,8 +818,10 @@ var UserApiClient = class {
566
818
  // Don't throw on any status
567
819
  maxContentLength: Infinity,
568
820
  // Allow large file uploads
569
- maxBodyLength: Infinity
821
+ maxBodyLength: Infinity,
570
822
  // Allow large file uploads
823
+ withCredentials: true
824
+ // Include cookies for session auth
571
825
  });
572
826
  const status = response.status;
573
827
  if (status !== 200 && status !== 201) {
@@ -601,7 +855,7 @@ Please check:
601
855
  const headers = {
602
856
  "x-client-id": this.clientId
603
857
  };
604
- if (permission) {
858
+ if (permission && !this.useSession) {
605
859
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
606
860
  const authHeaders = await this.generateAuthHeaders(permission, expiry);
607
861
  Object.assign(headers, authHeaders);
@@ -610,8 +864,10 @@ Please check:
610
864
  const response = await import_axios.default.get(url, {
611
865
  headers,
612
866
  maxRedirects: 0,
613
- validateStatus: () => true
867
+ validateStatus: () => true,
614
868
  // Don't throw on any status
869
+ withCredentials: true
870
+ // Include cookies for session auth
615
871
  });
616
872
  const status = response.status;
617
873
  const statusText = status >= 200 && status < 300 ? "OK" : "Error";
@@ -653,6 +909,65 @@ Please check:
653
909
  "X-eigenx-expiry": expiry.toString()
654
910
  };
655
911
  }
912
+ // ==========================================================================
913
+ // SIWE Session Management
914
+ // ==========================================================================
915
+ /**
916
+ * Login to the compute API using SIWE (Sign-In with Ethereum)
917
+ *
918
+ * This establishes a session with the compute API by verifying the SIWE message
919
+ * and signature. On success, a session cookie is set in the browser.
920
+ *
921
+ * @param request - Login request containing SIWE message and signature
922
+ * @returns Login result with the authenticated address
923
+ *
924
+ * @example
925
+ * ```typescript
926
+ * import { createSiweMessage } from "@layr-labs/ecloud-sdk/browser";
927
+ *
928
+ * const { message } = createSiweMessage({
929
+ * address: userAddress,
930
+ * chainId: 11155111,
931
+ * domain: window.location.host,
932
+ * uri: window.location.origin,
933
+ * });
934
+ *
935
+ * const signature = await signMessageAsync({ message });
936
+ * const result = await client.siweLogin({ message, signature });
937
+ * ```
938
+ */
939
+ async siweLogin(request) {
940
+ return loginToComputeApi({ baseUrl: this.config.userApiServerURL }, request);
941
+ }
942
+ /**
943
+ * Logout from the compute API
944
+ *
945
+ * This destroys the current session and clears the session cookie.
946
+ *
947
+ * @example
948
+ * ```typescript
949
+ * await client.siweLogout();
950
+ * ```
951
+ */
952
+ async siweLogout() {
953
+ return logoutFromComputeApi({ baseUrl: this.config.userApiServerURL });
954
+ }
955
+ /**
956
+ * Get the current SIWE session status from the compute API
957
+ *
958
+ * @returns Session information including authentication status and address
959
+ *
960
+ * @example
961
+ * ```typescript
962
+ * const session = await client.getSiweSession();
963
+ * if (session.authenticated) {
964
+ * console.log(`Logged in as ${session.address}`);
965
+ * }
966
+ * ```
967
+ */
968
+ async getSiweSession() {
969
+ return getComputeApiSession({ baseUrl: this.config.userApiServerURL });
970
+ }
656
971
  };
657
972
  function transformAppReleaseBuild(raw) {
658
973
  if (!isJsonObject(raw)) return void 0;
@@ -697,79 +1012,658 @@ function transformAppRelease(raw) {
697
1012
  };
698
1013
  }
699
1014
 
700
- // src/client/common/contract/eip7702.ts
701
- var import_viem4 = require("viem");
1015
+ // src/client/common/utils/billingapi.ts
1016
+ var import_axios2 = __toESM(require("axios"), 1);
702
1017
 
703
- // src/client/common/abis/ERC7702Delegator.json
704
- var ERC7702Delegator_default = [
705
- {
706
- type: "constructor",
707
- inputs: [
708
- {
709
- name: "_delegationManager",
710
- type: "address",
711
- internalType: "contractIDelegationManager"
1018
+ // src/client/common/auth/billingSession.ts
1019
+ var BillingSessionError = class extends Error {
1020
+ constructor(message, code, statusCode) {
1021
+ super(message);
1022
+ this.code = code;
1023
+ this.statusCode = statusCode;
1024
+ this.name = "BillingSessionError";
1025
+ }
1026
+ };
1027
+ function stripHexPrefix3(hex) {
1028
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
1029
+ }
1030
+ async function parseErrorResponse2(response) {
1031
+ try {
1032
+ const data = await response.json();
1033
+ return data.error || response.statusText;
1034
+ } catch {
1035
+ return response.statusText;
1036
+ }
1037
+ }
1038
+ async function loginToBillingApi(config, request) {
1039
+ let response;
1040
+ try {
1041
+ response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
1042
+ method: "POST",
1043
+ credentials: "include",
1044
+ // Include cookies for session management
1045
+ headers: {
1046
+ "Content-Type": "application/json"
712
1047
  },
713
- {
714
- name: "_entryPoint",
715
- type: "address",
716
- internalType: "contractIEntryPoint"
1048
+ body: JSON.stringify({
1049
+ message: request.message,
1050
+ signature: stripHexPrefix3(request.signature)
1051
+ })
1052
+ });
1053
+ } catch (error) {
1054
+ throw new BillingSessionError(
1055
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
1056
+ "NETWORK_ERROR"
1057
+ );
1058
+ }
1059
+ if (!response.ok) {
1060
+ const errorMessage = await parseErrorResponse2(response);
1061
+ const status = response.status;
1062
+ if (status === 400) {
1063
+ if (errorMessage.toLowerCase().includes("siwe")) {
1064
+ throw new BillingSessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
717
1065
  }
718
- ],
719
- stateMutability: "nonpayable"
720
- },
721
- {
722
- type: "receive",
723
- stateMutability: "payable"
724
- },
725
- {
726
- type: "function",
727
- name: "DOMAIN_VERSION",
728
- inputs: [],
729
- outputs: [
730
- {
731
- name: "",
732
- type: "string",
733
- internalType: "string"
1066
+ throw new BillingSessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
1067
+ }
1068
+ if (status === 401) {
1069
+ throw new BillingSessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
1070
+ }
1071
+ throw new BillingSessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
1072
+ }
1073
+ const data = await response.json();
1074
+ return {
1075
+ success: data.success,
1076
+ address: data.address
1077
+ };
1078
+ }
1079
+ async function getBillingApiSession(config) {
1080
+ let response;
1081
+ try {
1082
+ response = await fetch(`${config.baseUrl}/auth/session`, {
1083
+ method: "GET",
1084
+ credentials: "include",
1085
+ // Include cookies for session management
1086
+ headers: {
1087
+ "Content-Type": "application/json"
734
1088
  }
735
- ],
736
- stateMutability: "view"
737
- },
738
- {
739
- type: "function",
740
- name: "NAME",
741
- inputs: [],
742
- outputs: [
743
- {
744
- name: "",
745
- type: "string",
746
- internalType: "string"
1089
+ });
1090
+ } catch {
1091
+ return {
1092
+ authenticated: false
1093
+ };
1094
+ }
1095
+ if (response.status === 401) {
1096
+ return {
1097
+ authenticated: false
1098
+ };
1099
+ }
1100
+ if (!response.ok) {
1101
+ const errorMessage = await parseErrorResponse2(response);
1102
+ throw new BillingSessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
1103
+ }
1104
+ const data = await response.json();
1105
+ return {
1106
+ authenticated: data.authenticated,
1107
+ address: data.address,
1108
+ chainId: data.chainId,
1109
+ authenticatedAt: data.authenticatedAt
1110
+ };
1111
+ }
1112
+ async function logoutFromBillingApi(config) {
1113
+ let response;
1114
+ try {
1115
+ response = await fetch(`${config.baseUrl}/auth/logout`, {
1116
+ method: "POST",
1117
+ credentials: "include",
1118
+ // Include cookies for session management
1119
+ headers: {
1120
+ "Content-Type": "application/json"
747
1121
  }
748
- ],
749
- stateMutability: "view"
750
- },
751
- {
752
- type: "function",
753
- name: "PACKED_USER_OP_TYPEHASH",
754
- inputs: [],
755
- outputs: [
756
- {
757
- name: "",
758
- type: "bytes32",
759
- internalType: "bytes32"
1122
+ });
1123
+ } catch (error) {
1124
+ throw new BillingSessionError(
1125
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
1126
+ "NETWORK_ERROR"
1127
+ );
1128
+ }
1129
+ if (response.status === 401) {
1130
+ return;
1131
+ }
1132
+ if (!response.ok) {
1133
+ const errorMessage = await parseErrorResponse2(response);
1134
+ throw new BillingSessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
1135
+ }
1136
+ }
1137
+ async function isBillingSessionValid(config) {
1138
+ const session = await getBillingApiSession(config);
1139
+ return session.authenticated;
1140
+ }
1141
+ async function loginToBothApis(computeConfig, billingConfig, request) {
1142
+ const { loginToComputeApi: loginToComputeApi2 } = await Promise.resolve().then(() => (init_session(), session_exports));
1143
+ const [compute, billing] = await Promise.all([
1144
+ loginToComputeApi2(computeConfig, request),
1145
+ loginToBillingApi(billingConfig, request)
1146
+ ]);
1147
+ return { compute, billing };
1148
+ }
1149
+ async function logoutFromBothApis(computeConfig, billingConfig) {
1150
+ const { logoutFromComputeApi: logoutFromComputeApi2 } = await Promise.resolve().then(() => (init_session(), session_exports));
1151
+ await Promise.all([
1152
+ logoutFromComputeApi2(computeConfig),
1153
+ logoutFromBillingApi(billingConfig)
1154
+ ]);
1155
+ }
1156
+
1157
+ // src/client/common/utils/billingapi.ts
1158
+ var BillingApiClient = class {
1159
+ constructor(config, walletClient, options = {}) {
1160
+ this.config = config;
1161
+ this.walletClient = walletClient;
1162
+ this.options = options;
1163
+ this.useSession = options.useSession ?? false;
1164
+ if (!this.useSession && !walletClient) {
1165
+ throw new Error("WalletClient is required when not using session authentication");
1166
+ }
1167
+ }
1168
+ /**
1169
+ * Get the address of the connected wallet
1170
+ * Returns undefined if using session auth without a wallet client
1171
+ */
1172
+ get address() {
1173
+ const account = this.walletClient?.account;
1174
+ if (!account) {
1175
+ if (!this.useSession) {
1176
+ throw new Error("WalletClient must have an account attached");
760
1177
  }
761
- ],
762
- stateMutability: "view"
763
- },
764
- {
765
- type: "function",
766
- name: "VERSION",
767
- inputs: [],
768
- outputs: [
769
- {
770
- name: "",
771
- type: "string",
772
- internalType: "string"
1178
+ return void 0;
1179
+ }
1180
+ return account.address;
1181
+ }
1182
+ /**
1183
+ * Get the base URL of the billing API
1184
+ */
1185
+ get baseUrl() {
1186
+ return this.config.billingApiServerURL;
1187
+ }
1188
+ // ==========================================================================
1189
+ // SIWE Session Methods
1190
+ // ==========================================================================
1191
+ /**
1192
+ * Login to the billing API using SIWE
1193
+ *
1194
+ * This establishes a session with the billing API by verifying the SIWE message
1195
+ * and signature. On success, a session cookie is set in the browser.
1196
+ *
1197
+ * @param request - Login request containing SIWE message and signature
1198
+ * @returns Login result with the authenticated address
1199
+ *
1200
+ * @example
1201
+ * ```typescript
1202
+ * const { message } = createSiweMessage({
1203
+ * address: userAddress,
1204
+ * chainId: 11155111,
1205
+ * domain: window.location.host,
1206
+ * uri: window.location.origin,
1207
+ * });
1208
+ *
1209
+ * const signature = await signMessageAsync({ message });
1210
+ * const result = await billingClient.siweLogin({ message, signature });
1211
+ * ```
1212
+ */
1213
+ async siweLogin(request) {
1214
+ return loginToBillingApi({ baseUrl: this.baseUrl }, request);
1215
+ }
1216
+ /**
1217
+ * Logout from the billing API
1218
+ *
1219
+ * This destroys the current session and clears the session cookie.
1220
+ */
1221
+ async siweLogout() {
1222
+ return logoutFromBillingApi({ baseUrl: this.baseUrl });
1223
+ }
1224
+ /**
1225
+ * Get the current session status from the billing API
1226
+ *
1227
+ * @returns Session information including authentication status and address
1228
+ */
1229
+ async getSession() {
1230
+ return getBillingApiSession({ baseUrl: this.baseUrl });
1231
+ }
1232
+ /**
1233
+ * Check if there is a valid session
1234
+ *
1235
+ * @returns True if session is authenticated, false otherwise
1236
+ */
1237
+ async isSessionValid() {
1238
+ const session = await this.getSession();
1239
+ return session.authenticated;
1240
+ }
1241
+ // ==========================================================================
1242
+ // Subscription Methods
1243
+ // ==========================================================================
1244
+ async createSubscription(productId = "compute", options) {
1245
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1246
+ const body = options ? {
1247
+ success_url: options.successUrl,
1248
+ cancel_url: options.cancelUrl
1249
+ } : void 0;
1250
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId, body);
1251
+ return resp.json();
1252
+ }
1253
+ async getSubscription(productId = "compute") {
1254
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1255
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
1256
+ return resp.json();
1257
+ }
1258
+ async cancelSubscription(productId = "compute") {
1259
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1260
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
1261
+ }
1262
+ // ==========================================================================
1263
+ // Internal Methods
1264
+ // ==========================================================================
1265
+ /**
1266
+ * Make an authenticated request to the billing API
1267
+ *
1268
+ * Uses session auth if useSession is true, otherwise uses EIP-712 signature auth.
1269
+ */
1270
+ async makeAuthenticatedRequest(url, method, productId, body) {
1271
+ if (this.useSession) {
1272
+ return this.makeSessionAuthenticatedRequest(url, method, body);
1273
+ }
1274
+ return this.makeSignatureAuthenticatedRequest(url, method, productId, body);
1275
+ }
1276
+ /**
1277
+ * Make a request using session-based authentication (cookies)
1278
+ */
1279
+ async makeSessionAuthenticatedRequest(url, method, body) {
1280
+ const headers = {};
1281
+ if (body) {
1282
+ headers["Content-Type"] = "application/json";
1283
+ }
1284
+ try {
1285
+ const response = await fetch(url, {
1286
+ method,
1287
+ credentials: "include",
1288
+ // Include cookies for session management
1289
+ headers,
1290
+ body: body ? JSON.stringify(body) : void 0
1291
+ });
1292
+ const status = response.status;
1293
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
1294
+ if (status < 200 || status >= 300) {
1295
+ let errorBody;
1296
+ try {
1297
+ errorBody = await response.text();
1298
+ } catch {
1299
+ errorBody = statusText;
1300
+ }
1301
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${errorBody}`);
1302
+ }
1303
+ const responseData = await response.json();
1304
+ return {
1305
+ json: async () => responseData,
1306
+ text: async () => JSON.stringify(responseData)
1307
+ };
1308
+ } catch (error) {
1309
+ if (error.name === "TypeError" || error.message?.includes("fetch")) {
1310
+ throw new Error(
1311
+ `Failed to connect to BillingAPI at ${url}: ${error.message}
1312
+ Please check:
1313
+ 1. Your internet connection
1314
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
1315
+ 3. Firewall/proxy settings`
1316
+ );
1317
+ }
1318
+ throw error;
1319
+ }
1320
+ }
1321
+ /**
1322
+ * Make a request using EIP-712 signature authentication
1323
+ */
1324
+ async makeSignatureAuthenticatedRequest(url, method, productId, body) {
1325
+ if (!this.walletClient) {
1326
+ throw new Error("WalletClient is required for signature authentication");
1327
+ }
1328
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
1329
+ const { signature } = await calculateBillingAuthSignature({
1330
+ walletClient: this.walletClient,
1331
+ product: productId,
1332
+ expiry
1333
+ });
1334
+ const headers = {
1335
+ Authorization: `Bearer ${signature}`,
1336
+ "X-Account": this.address,
1337
+ "X-Expiry": expiry.toString()
1338
+ };
1339
+ if (body) {
1340
+ headers["Content-Type"] = "application/json";
1341
+ }
1342
+ try {
1343
+ const response = await (0, import_axios2.default)({
1344
+ method,
1345
+ url,
1346
+ headers,
1347
+ data: body,
1348
+ timeout: 3e4,
1349
+ maxRedirects: 0,
1350
+ validateStatus: () => true
1351
+ // Don't throw on any status
1352
+ });
1353
+ const status = response.status;
1354
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
1355
+ if (status < 200 || status >= 300) {
1356
+ const body2 = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
1357
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body2}`);
1358
+ }
1359
+ return {
1360
+ json: async () => response.data,
1361
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
1362
+ };
1363
+ } catch (error) {
1364
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
1365
+ const cause = error.cause?.message || error.cause || error.message;
1366
+ throw new Error(
1367
+ `Failed to connect to BillingAPI at ${url}: ${cause}
1368
+ Please check:
1369
+ 1. Your internet connection
1370
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
1371
+ 3. Firewall/proxy settings`
1372
+ );
1373
+ }
1374
+ throw error;
1375
+ }
1376
+ }
1377
+ };
1378
+
1379
+ // src/client/common/utils/buildapi.ts
1380
+ var import_axios3 = __toESM(require("axios"), 1);
1381
+ var MAX_RETRIES = 5;
1382
+ var INITIAL_BACKOFF_MS = 1e3;
1383
+ var MAX_BACKOFF_MS = 3e4;
1384
+ async function sleep(ms) {
1385
+ return new Promise((resolve) => setTimeout(resolve, ms));
1386
+ }
1387
+ function getRetryDelay(res, attempt) {
1388
+ const retryAfter = res.headers["retry-after"];
1389
+ if (retryAfter) {
1390
+ const seconds = parseInt(retryAfter, 10);
1391
+ if (!isNaN(seconds)) {
1392
+ return Math.min(seconds * 1e3, MAX_BACKOFF_MS);
1393
+ }
1394
+ }
1395
+ return Math.min(INITIAL_BACKOFF_MS * Math.pow(2, attempt), MAX_BACKOFF_MS);
1396
+ }
1397
+ async function requestWithRetry(config) {
1398
+ let lastResponse;
1399
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1400
+ const res = await (0, import_axios3.default)({ ...config, validateStatus: () => true });
1401
+ lastResponse = res;
1402
+ if (res.status !== 429) {
1403
+ return res;
1404
+ }
1405
+ if (attempt < MAX_RETRIES) {
1406
+ const delay = getRetryDelay(res, attempt);
1407
+ await sleep(delay);
1408
+ }
1409
+ }
1410
+ return lastResponse;
1411
+ }
1412
+ var BuildApiClient = class {
1413
+ constructor(options) {
1414
+ let url = options.baseUrl;
1415
+ while (url.endsWith("/")) {
1416
+ url = url.slice(0, -1);
1417
+ }
1418
+ this.baseUrl = url;
1419
+ this.clientId = options.clientId;
1420
+ this.walletClient = options.walletClient;
1421
+ this.useSession = options.useSession ?? false;
1422
+ this.billingSessionId = options.billingSessionId;
1423
+ }
1424
+ /**
1425
+ * Update the billing session ID.
1426
+ * Call this after logging into the billing API to enable session-based auth for builds.
1427
+ */
1428
+ setBillingSessionId(sessionId) {
1429
+ this.billingSessionId = sessionId;
1430
+ }
1431
+ /**
1432
+ * Get the address of the connected wallet
1433
+ */
1434
+ get address() {
1435
+ const account = this.walletClient?.account;
1436
+ if (!account) {
1437
+ throw new Error("WalletClient must have an account attached");
1438
+ }
1439
+ return account.address;
1440
+ }
1441
+ /**
1442
+ * Submit a new build request.
1443
+ * Supports two auth modes (session auth is tried first when billingSessionId is available):
1444
+ * 1. Session-based auth: X-Billing-Session header (forwarded billing_session cookie)
1445
+ * 2. Signature-based auth: Authorization + X-Account + X-eigenx-expiry headers (requires walletClient)
1446
+ */
1447
+ async submitBuild(payload) {
1448
+ if (this.useSession && this.billingSessionId) {
1449
+ return this.billingSessionAuthJsonRequest("/builds", "POST", payload);
1450
+ }
1451
+ return this.signatureAuthJsonRequest("/builds", "POST", payload);
1452
+ }
1453
+ async getBuild(buildId) {
1454
+ return this.publicJsonRequest(`/builds/${encodeURIComponent(buildId)}`);
1455
+ }
1456
+ async getBuildByDigest(digest) {
1457
+ return this.publicJsonRequest(`/builds/image/${encodeURIComponent(digest)}`);
1458
+ }
1459
+ async verify(identifier) {
1460
+ return this.publicJsonRequest(`/builds/verify/${encodeURIComponent(identifier)}`);
1461
+ }
1462
+ /**
1463
+ * Get build logs. Supports session auth (identity verification only, no billing check).
1464
+ */
1465
+ async getLogs(buildId) {
1466
+ return this.sessionOrSignatureTextRequest(`/builds/${encodeURIComponent(buildId)}/logs`);
1467
+ }
1468
+ async listBuilds(params) {
1469
+ const res = await requestWithRetry({
1470
+ url: `${this.baseUrl}/builds`,
1471
+ method: "GET",
1472
+ params,
1473
+ headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
1474
+ timeout: 6e4,
1475
+ validateStatus: () => true,
1476
+ withCredentials: this.useSession
1477
+ });
1478
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1479
+ return res.data;
1480
+ }
1481
+ async publicJsonRequest(path) {
1482
+ const res = await requestWithRetry({
1483
+ url: `${this.baseUrl}${path}`,
1484
+ method: "GET",
1485
+ headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
1486
+ timeout: 6e4,
1487
+ validateStatus: () => true,
1488
+ withCredentials: this.useSession
1489
+ });
1490
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1491
+ return res.data;
1492
+ }
1493
+ /**
1494
+ * Make a request that ALWAYS requires signature auth (for billing verification).
1495
+ * Used for endpoints like POST /builds that need to verify subscription status.
1496
+ */
1497
+ async signatureAuthJsonRequest(path, method, body) {
1498
+ if (!this.walletClient?.account) {
1499
+ throw new Error("WalletClient with account required for authenticated requests");
1500
+ }
1501
+ const headers = {
1502
+ "Content-Type": "application/json"
1503
+ };
1504
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1505
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
1506
+ const { signature } = await calculateBillingAuthSignature({
1507
+ walletClient: this.walletClient,
1508
+ product: "compute",
1509
+ expiry
1510
+ });
1511
+ headers.Authorization = `Bearer ${signature}`;
1512
+ headers["X-eigenx-expiry"] = expiry.toString();
1513
+ headers["X-Account"] = this.address;
1514
+ const res = await requestWithRetry({
1515
+ url: `${this.baseUrl}${path}`,
1516
+ method,
1517
+ headers,
1518
+ data: body,
1519
+ timeout: 6e4,
1520
+ validateStatus: () => true,
1521
+ withCredentials: this.useSession
1522
+ });
1523
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1524
+ return res.data;
1525
+ }
1526
+ /**
1527
+ * Make a request using billing session auth (for billing verification without wallet signature).
1528
+ * Forwards the billing_session cookie value via X-Billing-Session header.
1529
+ * Used for endpoints that need to verify subscription status when using session-based auth.
1530
+ */
1531
+ async billingSessionAuthJsonRequest(path, method, body) {
1532
+ if (!this.billingSessionId) {
1533
+ throw new Error("billingSessionId required for session-based billing auth");
1534
+ }
1535
+ const headers = {
1536
+ "Content-Type": "application/json",
1537
+ "X-Billing-Session": this.billingSessionId
1538
+ };
1539
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1540
+ const res = await requestWithRetry({
1541
+ url: `${this.baseUrl}${path}`,
1542
+ method,
1543
+ headers,
1544
+ data: body,
1545
+ timeout: 6e4,
1546
+ validateStatus: () => true,
1547
+ withCredentials: this.useSession
1548
+ });
1549
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1550
+ return res.data;
1551
+ }
1552
+ /**
1553
+ * Make an authenticated request that can use session OR signature auth.
1554
+ * When useSession is true, relies on cookies for identity verification.
1555
+ * Used for endpoints that only need identity verification (not billing).
1556
+ */
1557
+ async sessionOrSignatureTextRequest(path) {
1558
+ const headers = {};
1559
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1560
+ if (!this.useSession) {
1561
+ if (!this.walletClient?.account) {
1562
+ throw new Error("WalletClient with account required for authenticated requests");
1563
+ }
1564
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
1565
+ const { signature } = await calculateBillingAuthSignature({
1566
+ walletClient: this.walletClient,
1567
+ product: "compute",
1568
+ expiry
1569
+ });
1570
+ headers.Authorization = `Bearer ${signature}`;
1571
+ headers["X-eigenx-expiry"] = expiry.toString();
1572
+ headers["X-Account"] = this.address;
1573
+ }
1574
+ const res = await requestWithRetry({
1575
+ url: `${this.baseUrl}${path}`,
1576
+ method: "GET",
1577
+ headers,
1578
+ timeout: 6e4,
1579
+ responseType: "text",
1580
+ validateStatus: () => true,
1581
+ withCredentials: this.useSession
1582
+ });
1583
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1584
+ return typeof res.data === "string" ? res.data : JSON.stringify(res.data);
1585
+ }
1586
+ };
1587
+ function buildApiHttpError(res) {
1588
+ const status = res.status;
1589
+ const body = typeof res.data === "string" ? res.data : res.data ? JSON.stringify(res.data) : "";
1590
+ const url = res.config?.url ? ` ${res.config.url}` : "";
1591
+ return new Error(`BuildAPI request failed: ${status}${url} - ${body || "Unknown error"}`);
1592
+ }
1593
+
1594
+ // src/client/common/contract/eip7702.ts
1595
+ var import_viem4 = require("viem");
1596
+
1597
+ // src/client/common/abis/ERC7702Delegator.json
1598
+ var ERC7702Delegator_default = [
1599
+ {
1600
+ type: "constructor",
1601
+ inputs: [
1602
+ {
1603
+ name: "_delegationManager",
1604
+ type: "address",
1605
+ internalType: "contractIDelegationManager"
1606
+ },
1607
+ {
1608
+ name: "_entryPoint",
1609
+ type: "address",
1610
+ internalType: "contractIEntryPoint"
1611
+ }
1612
+ ],
1613
+ stateMutability: "nonpayable"
1614
+ },
1615
+ {
1616
+ type: "receive",
1617
+ stateMutability: "payable"
1618
+ },
1619
+ {
1620
+ type: "function",
1621
+ name: "DOMAIN_VERSION",
1622
+ inputs: [],
1623
+ outputs: [
1624
+ {
1625
+ name: "",
1626
+ type: "string",
1627
+ internalType: "string"
1628
+ }
1629
+ ],
1630
+ stateMutability: "view"
1631
+ },
1632
+ {
1633
+ type: "function",
1634
+ name: "NAME",
1635
+ inputs: [],
1636
+ outputs: [
1637
+ {
1638
+ name: "",
1639
+ type: "string",
1640
+ internalType: "string"
1641
+ }
1642
+ ],
1643
+ stateMutability: "view"
1644
+ },
1645
+ {
1646
+ type: "function",
1647
+ name: "PACKED_USER_OP_TYPEHASH",
1648
+ inputs: [],
1649
+ outputs: [
1650
+ {
1651
+ name: "",
1652
+ type: "bytes32",
1653
+ internalType: "bytes32"
1654
+ }
1655
+ ],
1656
+ stateMutability: "view"
1657
+ },
1658
+ {
1659
+ type: "function",
1660
+ name: "VERSION",
1661
+ inputs: [],
1662
+ outputs: [
1663
+ {
1664
+ name: "",
1665
+ type: "string",
1666
+ internalType: "string"
773
1667
  }
774
1668
  ],
775
1669
  stateMutability: "view"
@@ -1777,6 +2671,96 @@ async function estimateBatchGas(options) {
1777
2671
  maxCostEth: formatETH(maxCostWei)
1778
2672
  };
1779
2673
  }
2674
+ async function checkERC7702Delegation(publicClient, account, delegatorAddress) {
2675
+ const code = await publicClient.getCode({ address: account });
2676
+ if (!code) {
2677
+ return false;
2678
+ }
2679
+ const expectedCode = `0xef0100${delegatorAddress.slice(2)}`;
2680
+ return code.toLowerCase() === expectedCode.toLowerCase();
2681
+ }
2682
+ async function executeBatch(options, logger = noopLogger) {
2683
+ const { walletClient, publicClient, environmentConfig, executions, pendingMessage, gas } = options;
2684
+ const account = walletClient.account;
2685
+ if (!account) {
2686
+ throw new Error("Wallet client must have an account");
2687
+ }
2688
+ const chain = walletClient.chain;
2689
+ if (!chain) {
2690
+ throw new Error("Wallet client must have a chain");
2691
+ }
2692
+ const executeBatchData = encodeExecuteBatchData(executions);
2693
+ const isDelegated2 = await checkERC7702Delegation(
2694
+ publicClient,
2695
+ account.address,
2696
+ environmentConfig.erc7702DelegatorAddress
2697
+ );
2698
+ let authorizationList = [];
2699
+ if (!isDelegated2) {
2700
+ const transactionNonce = await publicClient.getTransactionCount({
2701
+ address: account.address,
2702
+ blockTag: "pending"
2703
+ });
2704
+ const chainId = await publicClient.getChainId();
2705
+ const authorizationNonce = transactionNonce + 1;
2706
+ logger.debug("Using wallet client signing for EIP-7702 authorization");
2707
+ const signedAuthorization = await walletClient.signAuthorization({
2708
+ account: account.address,
2709
+ contractAddress: environmentConfig.erc7702DelegatorAddress,
2710
+ chainId,
2711
+ nonce: Number(authorizationNonce)
2712
+ });
2713
+ authorizationList = [signedAuthorization];
2714
+ }
2715
+ if (pendingMessage) {
2716
+ logger.info(pendingMessage);
2717
+ }
2718
+ const txRequest = {
2719
+ account: walletClient.account,
2720
+ chain,
2721
+ to: account.address,
2722
+ data: executeBatchData,
2723
+ value: 0n
2724
+ };
2725
+ if (authorizationList.length > 0) {
2726
+ txRequest.authorizationList = authorizationList;
2727
+ }
2728
+ if (gas?.maxFeePerGas) {
2729
+ txRequest.maxFeePerGas = gas.maxFeePerGas;
2730
+ }
2731
+ if (gas?.maxPriorityFeePerGas) {
2732
+ txRequest.maxPriorityFeePerGas = gas.maxPriorityFeePerGas;
2733
+ }
2734
+ const hash = await walletClient.sendTransaction(txRequest);
2735
+ logger.info(`Transaction sent: ${hash}`);
2736
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
2737
+ if (receipt.status === "reverted") {
2738
+ let revertReason = "Unknown reason";
2739
+ try {
2740
+ await publicClient.call({
2741
+ to: account.address,
2742
+ data: executeBatchData,
2743
+ account: account.address
2744
+ });
2745
+ } catch (callError) {
2746
+ if (callError.data) {
2747
+ try {
2748
+ const decoded = (0, import_viem4.decodeErrorResult)({
2749
+ abi: ERC7702Delegator_default,
2750
+ data: callError.data
2751
+ });
2752
+ revertReason = `${decoded.errorName}: ${JSON.stringify(decoded.args)}`;
2753
+ } catch {
2754
+ revertReason = callError.message || "Unknown reason";
2755
+ }
2756
+ } else {
2757
+ revertReason = callError.message || "Unknown reason";
2758
+ }
2759
+ }
2760
+ throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2761
+ }
2762
+ return hash;
2763
+ }
1780
2764
 
1781
2765
  // src/client/common/contract/caller.ts
1782
2766
  var import_viem5 = require("viem");
@@ -2829,55 +3813,1047 @@ var AppController_default = [
2829
3813
  }
2830
3814
  ];
2831
3815
 
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
- }
3816
+ // src/client/common/abis/PermissionController.json
3817
+ var PermissionController_default = [
3818
+ {
3819
+ type: "function",
3820
+ name: "acceptAdmin",
3821
+ inputs: [
3822
+ {
3823
+ name: "account",
3824
+ type: "address",
3825
+ internalType: "address"
3826
+ }
3827
+ ],
3828
+ outputs: [],
3829
+ stateMutability: "nonpayable"
3830
+ },
3831
+ {
3832
+ type: "function",
3833
+ name: "addPendingAdmin",
3834
+ inputs: [
3835
+ {
3836
+ name: "account",
3837
+ type: "address",
3838
+ internalType: "address"
3839
+ },
3840
+ {
3841
+ name: "admin",
3842
+ type: "address",
3843
+ internalType: "address"
3844
+ }
3845
+ ],
3846
+ outputs: [],
3847
+ stateMutability: "nonpayable"
3848
+ },
3849
+ {
3850
+ type: "function",
3851
+ name: "canCall",
3852
+ inputs: [
3853
+ {
3854
+ name: "account",
3855
+ type: "address",
3856
+ internalType: "address"
3857
+ },
3858
+ {
3859
+ name: "caller",
3860
+ type: "address",
3861
+ internalType: "address"
3862
+ },
3863
+ {
3864
+ name: "target",
3865
+ type: "address",
3866
+ internalType: "address"
3867
+ },
3868
+ {
3869
+ name: "selector",
3870
+ type: "bytes4",
3871
+ internalType: "bytes4"
3872
+ }
3873
+ ],
3874
+ outputs: [
3875
+ {
3876
+ name: "",
3877
+ type: "bool",
3878
+ internalType: "bool"
3879
+ }
3880
+ ],
3881
+ stateMutability: "nonpayable"
3882
+ },
3883
+ {
3884
+ type: "function",
3885
+ name: "getAdmins",
3886
+ inputs: [
3887
+ {
3888
+ name: "account",
3889
+ type: "address",
3890
+ internalType: "address"
3891
+ }
3892
+ ],
3893
+ outputs: [
3894
+ {
3895
+ name: "",
3896
+ type: "address[]",
3897
+ internalType: "address[]"
3898
+ }
3899
+ ],
3900
+ stateMutability: "view"
3901
+ },
3902
+ {
3903
+ type: "function",
3904
+ name: "getAppointeePermissions",
3905
+ inputs: [
3906
+ {
3907
+ name: "account",
3908
+ type: "address",
3909
+ internalType: "address"
3910
+ },
3911
+ {
3912
+ name: "appointee",
3913
+ type: "address",
3914
+ internalType: "address"
3915
+ }
3916
+ ],
3917
+ outputs: [
3918
+ {
3919
+ name: "",
3920
+ type: "address[]",
3921
+ internalType: "address[]"
3922
+ },
3923
+ {
3924
+ name: "",
3925
+ type: "bytes4[]",
3926
+ internalType: "bytes4[]"
3927
+ }
3928
+ ],
3929
+ stateMutability: "nonpayable"
3930
+ },
3931
+ {
3932
+ type: "function",
3933
+ name: "getAppointees",
3934
+ inputs: [
3935
+ {
3936
+ name: "account",
3937
+ type: "address",
3938
+ internalType: "address"
3939
+ },
3940
+ {
3941
+ name: "target",
3942
+ type: "address",
3943
+ internalType: "address"
3944
+ },
3945
+ {
3946
+ name: "selector",
3947
+ type: "bytes4",
3948
+ internalType: "bytes4"
3949
+ }
3950
+ ],
3951
+ outputs: [
3952
+ {
3953
+ name: "",
3954
+ type: "address[]",
3955
+ internalType: "address[]"
3956
+ }
3957
+ ],
3958
+ stateMutability: "nonpayable"
3959
+ },
3960
+ {
3961
+ type: "function",
3962
+ name: "getPendingAdmins",
3963
+ inputs: [
3964
+ {
3965
+ name: "account",
3966
+ type: "address",
3967
+ internalType: "address"
3968
+ }
3969
+ ],
3970
+ outputs: [
3971
+ {
3972
+ name: "",
3973
+ type: "address[]",
3974
+ internalType: "address[]"
3975
+ }
3976
+ ],
3977
+ stateMutability: "view"
3978
+ },
3979
+ {
3980
+ type: "function",
3981
+ name: "isAdmin",
3982
+ inputs: [
3983
+ {
3984
+ name: "account",
3985
+ type: "address",
3986
+ internalType: "address"
3987
+ },
3988
+ {
3989
+ name: "caller",
3990
+ type: "address",
3991
+ internalType: "address"
3992
+ }
3993
+ ],
3994
+ outputs: [
3995
+ {
3996
+ name: "",
3997
+ type: "bool",
3998
+ internalType: "bool"
3999
+ }
4000
+ ],
4001
+ stateMutability: "view"
4002
+ },
4003
+ {
4004
+ type: "function",
4005
+ name: "isPendingAdmin",
4006
+ inputs: [
4007
+ {
4008
+ name: "account",
4009
+ type: "address",
4010
+ internalType: "address"
4011
+ },
4012
+ {
4013
+ name: "pendingAdmin",
4014
+ type: "address",
4015
+ internalType: "address"
4016
+ }
4017
+ ],
4018
+ outputs: [
4019
+ {
4020
+ name: "",
4021
+ type: "bool",
4022
+ internalType: "bool"
4023
+ }
4024
+ ],
4025
+ stateMutability: "view"
4026
+ },
4027
+ {
4028
+ type: "function",
4029
+ name: "removeAdmin",
4030
+ inputs: [
4031
+ {
4032
+ name: "account",
4033
+ type: "address",
4034
+ internalType: "address"
4035
+ },
4036
+ {
4037
+ name: "admin",
4038
+ type: "address",
4039
+ internalType: "address"
4040
+ }
4041
+ ],
4042
+ outputs: [],
4043
+ stateMutability: "nonpayable"
4044
+ },
4045
+ {
4046
+ type: "function",
4047
+ name: "removeAppointee",
4048
+ inputs: [
4049
+ {
4050
+ name: "account",
4051
+ type: "address",
4052
+ internalType: "address"
4053
+ },
4054
+ {
4055
+ name: "appointee",
4056
+ type: "address",
4057
+ internalType: "address"
4058
+ },
4059
+ {
4060
+ name: "target",
4061
+ type: "address",
4062
+ internalType: "address"
4063
+ },
4064
+ {
4065
+ name: "selector",
4066
+ type: "bytes4",
4067
+ internalType: "bytes4"
4068
+ }
4069
+ ],
4070
+ outputs: [],
4071
+ stateMutability: "nonpayable"
4072
+ },
4073
+ {
4074
+ type: "function",
4075
+ name: "removePendingAdmin",
4076
+ inputs: [
4077
+ {
4078
+ name: "account",
4079
+ type: "address",
4080
+ internalType: "address"
4081
+ },
4082
+ {
4083
+ name: "admin",
4084
+ type: "address",
4085
+ internalType: "address"
4086
+ }
4087
+ ],
4088
+ outputs: [],
4089
+ stateMutability: "nonpayable"
4090
+ },
4091
+ {
4092
+ type: "function",
4093
+ name: "setAppointee",
4094
+ inputs: [
4095
+ {
4096
+ name: "account",
4097
+ type: "address",
4098
+ internalType: "address"
4099
+ },
4100
+ {
4101
+ name: "appointee",
4102
+ type: "address",
4103
+ internalType: "address"
4104
+ },
4105
+ {
4106
+ name: "target",
4107
+ type: "address",
4108
+ internalType: "address"
4109
+ },
4110
+ {
4111
+ name: "selector",
4112
+ type: "bytes4",
4113
+ internalType: "bytes4"
4114
+ }
4115
+ ],
4116
+ outputs: [],
4117
+ stateMutability: "nonpayable"
4118
+ },
4119
+ {
4120
+ type: "function",
4121
+ name: "version",
4122
+ inputs: [],
4123
+ outputs: [
4124
+ {
4125
+ name: "",
4126
+ type: "string",
4127
+ internalType: "string"
4128
+ }
4129
+ ],
4130
+ stateMutability: "view"
4131
+ },
4132
+ {
4133
+ type: "event",
4134
+ name: "AdminRemoved",
4135
+ inputs: [
4136
+ {
4137
+ name: "account",
4138
+ type: "address",
4139
+ indexed: true,
4140
+ internalType: "address"
4141
+ },
4142
+ {
4143
+ name: "admin",
4144
+ type: "address",
4145
+ indexed: false,
4146
+ internalType: "address"
4147
+ }
4148
+ ],
4149
+ anonymous: false
4150
+ },
4151
+ {
4152
+ type: "event",
4153
+ name: "AdminSet",
4154
+ inputs: [
4155
+ {
4156
+ name: "account",
4157
+ type: "address",
4158
+ indexed: true,
4159
+ internalType: "address"
4160
+ },
4161
+ {
4162
+ name: "admin",
4163
+ type: "address",
4164
+ indexed: false,
4165
+ internalType: "address"
4166
+ }
4167
+ ],
4168
+ anonymous: false
4169
+ },
4170
+ {
4171
+ type: "event",
4172
+ name: "AppointeeRemoved",
4173
+ inputs: [
4174
+ {
4175
+ name: "account",
4176
+ type: "address",
4177
+ indexed: true,
4178
+ internalType: "address"
4179
+ },
4180
+ {
4181
+ name: "appointee",
4182
+ type: "address",
4183
+ indexed: true,
4184
+ internalType: "address"
4185
+ },
4186
+ {
4187
+ name: "target",
4188
+ type: "address",
4189
+ indexed: false,
4190
+ internalType: "address"
4191
+ },
4192
+ {
4193
+ name: "selector",
4194
+ type: "bytes4",
4195
+ indexed: false,
4196
+ internalType: "bytes4"
4197
+ }
4198
+ ],
4199
+ anonymous: false
4200
+ },
4201
+ {
4202
+ type: "event",
4203
+ name: "AppointeeSet",
4204
+ inputs: [
4205
+ {
4206
+ name: "account",
4207
+ type: "address",
4208
+ indexed: true,
4209
+ internalType: "address"
4210
+ },
4211
+ {
4212
+ name: "appointee",
4213
+ type: "address",
4214
+ indexed: true,
4215
+ internalType: "address"
4216
+ },
4217
+ {
4218
+ name: "target",
4219
+ type: "address",
4220
+ indexed: false,
4221
+ internalType: "address"
4222
+ },
4223
+ {
4224
+ name: "selector",
4225
+ type: "bytes4",
4226
+ indexed: false,
4227
+ internalType: "bytes4"
4228
+ }
4229
+ ],
4230
+ anonymous: false
4231
+ },
4232
+ {
4233
+ type: "event",
4234
+ name: "PendingAdminAdded",
4235
+ inputs: [
4236
+ {
4237
+ name: "account",
4238
+ type: "address",
4239
+ indexed: true,
4240
+ internalType: "address"
4241
+ },
4242
+ {
4243
+ name: "admin",
4244
+ type: "address",
4245
+ indexed: false,
4246
+ internalType: "address"
4247
+ }
4248
+ ],
4249
+ anonymous: false
4250
+ },
4251
+ {
4252
+ type: "event",
4253
+ name: "PendingAdminRemoved",
4254
+ inputs: [
4255
+ {
4256
+ name: "account",
4257
+ type: "address",
4258
+ indexed: true,
4259
+ internalType: "address"
4260
+ },
4261
+ {
4262
+ name: "admin",
4263
+ type: "address",
4264
+ indexed: false,
4265
+ internalType: "address"
4266
+ }
4267
+ ],
4268
+ anonymous: false
4269
+ },
4270
+ {
4271
+ type: "error",
4272
+ name: "AdminAlreadyPending",
4273
+ inputs: []
4274
+ },
4275
+ {
4276
+ type: "error",
4277
+ name: "AdminAlreadySet",
4278
+ inputs: []
4279
+ },
4280
+ {
4281
+ type: "error",
4282
+ name: "AdminNotPending",
4283
+ inputs: []
4284
+ },
4285
+ {
4286
+ type: "error",
4287
+ name: "AdminNotSet",
4288
+ inputs: []
4289
+ },
4290
+ {
4291
+ type: "error",
4292
+ name: "AppointeeAlreadySet",
4293
+ inputs: []
4294
+ },
4295
+ {
4296
+ type: "error",
4297
+ name: "AppointeeNotSet",
4298
+ inputs: []
4299
+ },
4300
+ {
4301
+ type: "error",
4302
+ name: "CannotHaveZeroAdmins",
4303
+ inputs: []
4304
+ },
4305
+ {
4306
+ type: "error",
4307
+ name: "NotAdmin",
4308
+ inputs: []
4309
+ }
4310
+ ];
4311
+
4312
+ // src/client/common/contract/caller.ts
4313
+ function formatETH(wei) {
4314
+ const eth = Number(wei) / 1e18;
4315
+ const costStr = eth.toFixed(6);
4316
+ const trimmed = costStr.replace(/\.?0+$/, "");
4317
+ if (trimmed === "0" && wei > 0n) {
4318
+ return "<0.000001";
4319
+ }
4320
+ return trimmed;
4321
+ }
4322
+ async function estimateTransactionGas(options) {
4323
+ const { publicClient, from, to, data, value = 0n } = options;
4324
+ const fees = await publicClient.estimateFeesPerGas();
4325
+ const gasLimit = await publicClient.estimateGas({
4326
+ account: from,
4327
+ to,
4328
+ data,
4329
+ value
4330
+ });
4331
+ const maxFeePerGas = fees.maxFeePerGas;
4332
+ const maxPriorityFeePerGas = fees.maxPriorityFeePerGas;
4333
+ const maxCostWei = gasLimit * maxFeePerGas;
4334
+ const maxCostEth = formatETH(maxCostWei);
4335
+ return {
4336
+ gasLimit,
4337
+ maxFeePerGas,
4338
+ maxPriorityFeePerGas,
4339
+ maxCostWei,
4340
+ maxCostEth
4341
+ };
4342
+ }
4343
+ async function calculateAppID(options) {
4344
+ const { publicClient, environmentConfig, ownerAddress, salt } = options;
4345
+ const saltHexString = (0, import_viem5.bytesToHex)(salt).slice(2);
4346
+ const paddedSaltHex = saltHexString.padStart(64, "0");
4347
+ const saltHex = `0x${paddedSaltHex}`;
4348
+ const appID = await publicClient.readContract({
4349
+ address: environmentConfig.appControllerAddress,
4350
+ abi: AppController_default,
4351
+ functionName: "calculateAppId",
4352
+ args: [ownerAddress, saltHex]
4353
+ });
4354
+ return appID;
4355
+ }
4356
+ async function prepareDeployBatch(options, logger = noopLogger) {
4357
+ const { walletClient, publicClient, environmentConfig, salt, release, publicLogs } = options;
4358
+ const account = walletClient.account;
4359
+ if (!account) {
4360
+ throw new Error("WalletClient must have an account attached");
4361
+ }
4362
+ logger.info("Calculating app ID...");
4363
+ const appId = await calculateAppID({
4364
+ publicClient,
4365
+ environmentConfig,
4366
+ ownerAddress: account.address,
4367
+ salt
4368
+ });
4369
+ logger.debug(`App ID calculated: ${appId}`);
4370
+ logger.debug(`This address will be used for acceptAdmin call`);
4371
+ const saltHexString = (0, import_viem5.bytesToHex)(salt).slice(2);
4372
+ const paddedSaltHex = saltHexString.padStart(64, "0");
4373
+ const saltHex = `0x${paddedSaltHex}`;
4374
+ const releaseForViem = {
4375
+ rmsRelease: {
4376
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4377
+ digest: `0x${(0, import_viem5.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4378
+ registry: artifact.registry
4379
+ })),
4380
+ upgradeByTime: release.rmsRelease.upgradeByTime
4381
+ },
4382
+ publicEnv: (0, import_viem5.bytesToHex)(release.publicEnv),
4383
+ encryptedEnv: (0, import_viem5.bytesToHex)(release.encryptedEnv)
4384
+ };
4385
+ const createData = (0, import_viem5.encodeFunctionData)({
4386
+ abi: AppController_default,
4387
+ functionName: "createApp",
4388
+ args: [saltHex, releaseForViem]
4389
+ });
4390
+ const acceptAdminData = (0, import_viem5.encodeFunctionData)({
4391
+ abi: PermissionController_default,
4392
+ functionName: "acceptAdmin",
4393
+ args: [appId]
4394
+ });
4395
+ const executions = [
4396
+ {
4397
+ target: environmentConfig.appControllerAddress,
4398
+ value: 0n,
4399
+ callData: createData
4400
+ },
4401
+ {
4402
+ target: environmentConfig.permissionControllerAddress,
4403
+ value: 0n,
4404
+ callData: acceptAdminData
4405
+ }
4406
+ ];
4407
+ if (publicLogs) {
4408
+ const anyoneCanViewLogsData = (0, import_viem5.encodeFunctionData)({
4409
+ abi: PermissionController_default,
4410
+ functionName: "setAppointee",
4411
+ args: [
4412
+ appId,
4413
+ "0x493219d9949348178af1f58740655951a8cd110c",
4414
+ // AnyoneCanCallAddress
4415
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4416
+ // ApiPermissionsTarget
4417
+ "0x2fd3f2fe"
4418
+ // CanViewAppLogsPermission
4419
+ ]
4420
+ });
4421
+ executions.push({
4422
+ target: environmentConfig.permissionControllerAddress,
4423
+ value: 0n,
4424
+ callData: anyoneCanViewLogsData
4425
+ });
4426
+ }
4427
+ return {
4428
+ appId,
4429
+ salt,
4430
+ executions,
4431
+ walletClient,
4432
+ publicClient,
4433
+ environmentConfig
4434
+ };
4435
+ }
4436
+ async function executeDeployBatch(data, context, gas, logger = noopLogger) {
4437
+ const pendingMessage = "Deploying new app...";
4438
+ const txHash = await executeBatch(
4439
+ {
4440
+ walletClient: context.walletClient,
4441
+ publicClient: context.publicClient,
4442
+ environmentConfig: context.environmentConfig,
4443
+ executions: data.executions,
4444
+ pendingMessage,
4445
+ gas
4446
+ },
4447
+ logger
4448
+ );
4449
+ return { appId: data.appId, txHash };
4450
+ }
4451
+ async function deployApp(options, logger = noopLogger) {
4452
+ const prepared = await prepareDeployBatch(options, logger);
4453
+ const data = {
4454
+ appId: prepared.appId,
4455
+ salt: prepared.salt,
4456
+ executions: prepared.executions
4457
+ };
4458
+ const context = {
4459
+ walletClient: prepared.walletClient,
4460
+ publicClient: prepared.publicClient,
4461
+ environmentConfig: prepared.environmentConfig
4462
+ };
4463
+ return executeDeployBatch(data, context, options.gas, logger);
4464
+ }
4465
+ function supportsEIP7702(walletClient) {
4466
+ const account = walletClient.account;
4467
+ if (!account) return false;
4468
+ return account.type === "local";
4469
+ }
4470
+ async function executeDeploySequential(options, logger = noopLogger) {
4471
+ const { walletClient, publicClient, environmentConfig, data, publicLogs, onProgress } = options;
4472
+ const account = walletClient.account;
4473
+ if (!account) {
4474
+ throw new Error("WalletClient must have an account attached");
4475
+ }
4476
+ const chain = getChainFromID(environmentConfig.chainID);
4477
+ const txHashes = {
4478
+ createApp: "0x",
4479
+ acceptAdmin: "0x"
4480
+ };
4481
+ logger.info("Step 1/3: Creating app...");
4482
+ onProgress?.("createApp");
4483
+ const createAppExecution = data.executions[0];
4484
+ const createAppHash = await walletClient.sendTransaction({
4485
+ account,
4486
+ to: createAppExecution.target,
4487
+ data: createAppExecution.callData,
4488
+ value: createAppExecution.value,
4489
+ chain
4490
+ });
4491
+ logger.info(`createApp transaction sent: ${createAppHash}`);
4492
+ const createAppReceipt = await publicClient.waitForTransactionReceipt({ hash: createAppHash });
4493
+ if (createAppReceipt.status === "reverted") {
4494
+ throw new Error(`createApp transaction reverted: ${createAppHash}`);
4495
+ }
4496
+ txHashes.createApp = createAppHash;
4497
+ logger.info(`createApp confirmed in block ${createAppReceipt.blockNumber}`);
4498
+ logger.info("Step 2/3: Accepting admin role...");
4499
+ onProgress?.("acceptAdmin", createAppHash);
4500
+ const acceptAdminExecution = data.executions[1];
4501
+ const acceptAdminHash = await walletClient.sendTransaction({
4502
+ account,
4503
+ to: acceptAdminExecution.target,
4504
+ data: acceptAdminExecution.callData,
4505
+ value: acceptAdminExecution.value,
4506
+ chain
4507
+ });
4508
+ logger.info(`acceptAdmin transaction sent: ${acceptAdminHash}`);
4509
+ const acceptAdminReceipt = await publicClient.waitForTransactionReceipt({
4510
+ hash: acceptAdminHash
4511
+ });
4512
+ if (acceptAdminReceipt.status === "reverted") {
4513
+ throw new Error(`acceptAdmin transaction reverted: ${acceptAdminHash}`);
4514
+ }
4515
+ txHashes.acceptAdmin = acceptAdminHash;
4516
+ logger.info(`acceptAdmin confirmed in block ${acceptAdminReceipt.blockNumber}`);
4517
+ if (publicLogs && data.executions.length > 2) {
4518
+ logger.info("Step 3/3: Setting public logs permission...");
4519
+ onProgress?.("setPublicLogs", acceptAdminHash);
4520
+ const setAppointeeExecution = data.executions[2];
4521
+ const setAppointeeHash = await walletClient.sendTransaction({
4522
+ account,
4523
+ to: setAppointeeExecution.target,
4524
+ data: setAppointeeExecution.callData,
4525
+ value: setAppointeeExecution.value,
4526
+ chain
4527
+ });
4528
+ logger.info(`setAppointee transaction sent: ${setAppointeeHash}`);
4529
+ const setAppointeeReceipt = await publicClient.waitForTransactionReceipt({
4530
+ hash: setAppointeeHash
4531
+ });
4532
+ if (setAppointeeReceipt.status === "reverted") {
4533
+ throw new Error(`setAppointee transaction reverted: ${setAppointeeHash}`);
4534
+ }
4535
+ txHashes.setPublicLogs = setAppointeeHash;
4536
+ logger.info(`setAppointee confirmed in block ${setAppointeeReceipt.blockNumber}`);
4537
+ }
4538
+ onProgress?.("complete", txHashes.setPublicLogs || txHashes.acceptAdmin);
4539
+ logger.info(`Deployment complete! App ID: ${data.appId}`);
4540
+ return {
4541
+ appId: data.appId,
4542
+ txHashes
4543
+ };
4544
+ }
4545
+ async function supportsEIP5792(walletClient) {
4546
+ try {
4547
+ if (typeof walletClient.getCapabilities !== "function") {
4548
+ return false;
4549
+ }
4550
+ const account = walletClient.account;
4551
+ if (!account) return false;
4552
+ const capabilities = await walletClient.getCapabilities({
4553
+ account: account.address
4554
+ });
4555
+ return capabilities !== null && capabilities !== void 0 && Object.keys(capabilities).length > 0;
4556
+ } catch {
4557
+ return false;
4558
+ }
4559
+ }
4560
+ async function executeDeployBatched(options, logger = noopLogger) {
4561
+ const { walletClient, environmentConfig, data, publicLogs, onProgress } = options;
4562
+ const account = walletClient.account;
4563
+ if (!account) {
4564
+ throw new Error("WalletClient must have an account attached");
4565
+ }
4566
+ const chain = getChainFromID(environmentConfig.chainID);
4567
+ const calls = data.executions.map(
4568
+ (execution) => ({
4569
+ to: execution.target,
4570
+ data: execution.callData,
4571
+ value: execution.value
4572
+ })
4573
+ );
4574
+ const filteredCalls = publicLogs ? calls : calls.slice(0, 2);
4575
+ logger.info(`Deploying with EIP-5792 sendCalls (${filteredCalls.length} calls)...`);
4576
+ onProgress?.("createApp");
4577
+ try {
4578
+ const { id: batchId } = await walletClient.sendCalls({
4579
+ account,
4580
+ chain,
4581
+ calls: filteredCalls,
4582
+ forceAtomic: true
4583
+ });
4584
+ logger.info(`Batch submitted with ID: ${batchId}`);
4585
+ onProgress?.("acceptAdmin");
4586
+ let status;
4587
+ let attempts = 0;
4588
+ const maxAttempts = 120;
4589
+ while (attempts < maxAttempts) {
4590
+ try {
4591
+ status = await walletClient.getCallsStatus({ id: batchId });
4592
+ if (status.status === "success" || status.status === "confirmed") {
4593
+ logger.info(`Batch confirmed with ${status.receipts?.length || 0} receipts`);
4594
+ break;
4595
+ }
4596
+ if (status.status === "failed" || status.status === "reverted") {
4597
+ throw new Error(`Batch transaction failed: ${status.status}`);
4598
+ }
4599
+ } catch (statusError) {
4600
+ if (statusError.message?.includes("not supported")) {
4601
+ logger.warn("getCallsStatus not supported, waiting for chain confirmation...");
4602
+ await new Promise((resolve) => setTimeout(resolve, 15e3));
4603
+ break;
4604
+ }
4605
+ throw statusError;
4606
+ }
4607
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
4608
+ attempts++;
4609
+ }
4610
+ if (attempts >= maxAttempts) {
4611
+ throw new Error("Timeout waiting for batch confirmation");
4612
+ }
4613
+ if (publicLogs) {
4614
+ onProgress?.("setPublicLogs");
4615
+ }
4616
+ onProgress?.("complete");
4617
+ const receipts = (status?.receipts || []).map((r) => ({
4618
+ transactionHash: r.transactionHash || r.hash
4619
+ }));
4620
+ logger.info(`Deployment complete! App ID: ${data.appId}`);
4621
+ return {
4622
+ appId: data.appId,
4623
+ batchId,
4624
+ receipts
4625
+ };
4626
+ } catch (error) {
4627
+ if (error.message?.includes("not supported") || error.message?.includes("wallet_sendCalls") || error.code === -32601) {
4628
+ throw new Error("EIP5792_NOT_SUPPORTED");
4629
+ }
4630
+ throw error;
4631
+ }
4632
+ }
4633
+ async function prepareUpgradeBatch(options) {
4634
+ const {
4635
+ walletClient,
4636
+ publicClient,
4637
+ environmentConfig,
4638
+ appID,
4639
+ release,
4640
+ publicLogs,
4641
+ needsPermissionChange
4642
+ } = options;
4643
+ const releaseForViem = {
4644
+ rmsRelease: {
4645
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4646
+ digest: `0x${(0, import_viem5.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4647
+ registry: artifact.registry
4648
+ })),
4649
+ upgradeByTime: release.rmsRelease.upgradeByTime
4650
+ },
4651
+ publicEnv: (0, import_viem5.bytesToHex)(release.publicEnv),
4652
+ encryptedEnv: (0, import_viem5.bytesToHex)(release.encryptedEnv)
4653
+ };
4654
+ const upgradeData = (0, import_viem5.encodeFunctionData)({
4655
+ abi: AppController_default,
4656
+ functionName: "upgradeApp",
4657
+ args: [appID, releaseForViem]
4658
+ });
4659
+ const executions = [
4660
+ {
4661
+ target: environmentConfig.appControllerAddress,
4662
+ value: 0n,
4663
+ callData: upgradeData
4664
+ }
4665
+ ];
4666
+ if (needsPermissionChange) {
4667
+ if (publicLogs) {
4668
+ const addLogsData = (0, import_viem5.encodeFunctionData)({
4669
+ abi: PermissionController_default,
4670
+ functionName: "setAppointee",
4671
+ args: [
4672
+ appID,
4673
+ "0x493219d9949348178af1f58740655951a8cd110c",
4674
+ // AnyoneCanCallAddress
4675
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4676
+ // ApiPermissionsTarget
4677
+ "0x2fd3f2fe"
4678
+ // CanViewAppLogsPermission
4679
+ ]
4680
+ });
4681
+ executions.push({
4682
+ target: environmentConfig.permissionControllerAddress,
4683
+ value: 0n,
4684
+ callData: addLogsData
4685
+ });
4686
+ } else {
4687
+ const removeLogsData = (0, import_viem5.encodeFunctionData)({
4688
+ abi: PermissionController_default,
4689
+ functionName: "removeAppointee",
4690
+ args: [
4691
+ appID,
4692
+ "0x493219d9949348178af1f58740655951a8cd110c",
4693
+ // AnyoneCanCallAddress
4694
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4695
+ // ApiPermissionsTarget
4696
+ "0x2fd3f2fe"
4697
+ // CanViewAppLogsPermission
4698
+ ]
4699
+ });
4700
+ executions.push({
4701
+ target: environmentConfig.permissionControllerAddress,
4702
+ value: 0n,
4703
+ callData: removeLogsData
4704
+ });
4705
+ }
4706
+ }
4707
+ return {
4708
+ appId: appID,
4709
+ executions,
4710
+ walletClient,
4711
+ publicClient,
4712
+ environmentConfig
4713
+ };
4714
+ }
4715
+ async function executeUpgradeBatch(data, context, gas, logger = noopLogger) {
4716
+ const pendingMessage = `Upgrading app ${data.appId}...`;
4717
+ const txHash = await executeBatch(
4718
+ {
4719
+ walletClient: context.walletClient,
4720
+ publicClient: context.publicClient,
4721
+ environmentConfig: context.environmentConfig,
4722
+ executions: data.executions,
4723
+ pendingMessage,
4724
+ gas
4725
+ },
4726
+ logger
4727
+ );
4728
+ return txHash;
4729
+ }
4730
+ async function upgradeApp(options, logger = noopLogger) {
4731
+ const prepared = await prepareUpgradeBatch(options);
4732
+ const data = {
4733
+ appId: prepared.appId,
4734
+ executions: prepared.executions
4735
+ };
4736
+ const context = {
4737
+ walletClient: prepared.walletClient,
4738
+ publicClient: prepared.publicClient,
4739
+ environmentConfig: prepared.environmentConfig
4740
+ };
4741
+ return executeUpgradeBatch(data, context, options.gas, logger);
4742
+ }
4743
+ async function sendAndWaitForTransaction(options, logger = noopLogger) {
4744
+ const {
4745
+ walletClient,
4746
+ publicClient,
4747
+ environmentConfig,
4748
+ to,
4749
+ data,
4750
+ value = 0n,
4751
+ pendingMessage,
4752
+ txDescription,
4753
+ gas
4754
+ } = options;
4755
+ const account = walletClient.account;
4756
+ if (!account) {
4757
+ throw new Error("WalletClient must have an account attached");
4758
+ }
4759
+ const chain = getChainFromID(environmentConfig.chainID);
4760
+ if (pendingMessage) {
4761
+ logger.info(`
4762
+ ${pendingMessage}`);
4763
+ }
4764
+ const hash = await walletClient.sendTransaction({
4765
+ account,
4766
+ to,
4767
+ data,
4768
+ value,
4769
+ ...gas?.maxFeePerGas && { maxFeePerGas: gas.maxFeePerGas },
4770
+ ...gas?.maxPriorityFeePerGas && {
4771
+ maxPriorityFeePerGas: gas.maxPriorityFeePerGas
4772
+ },
4773
+ chain
4774
+ });
4775
+ logger.info(`Transaction sent: ${hash}`);
4776
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
4777
+ if (receipt.status === "reverted") {
4778
+ let revertReason = "Unknown reason";
4779
+ try {
4780
+ await publicClient.call({
4781
+ to,
4782
+ data,
4783
+ account: account.address
4784
+ });
4785
+ } catch (callError) {
4786
+ if (callError.data) {
4787
+ try {
4788
+ const decoded = (0, import_viem5.decodeErrorResult)({
4789
+ abi: AppController_default,
4790
+ data: callError.data
4791
+ });
4792
+ const formattedError = formatAppControllerError(decoded);
4793
+ revertReason = formattedError.message;
4794
+ } catch {
4795
+ revertReason = callError.message || "Unknown reason";
4796
+ }
4797
+ } else {
4798
+ revertReason = callError.message || "Unknown reason";
4799
+ }
4800
+ }
4801
+ logger.error(`${txDescription} transaction (hash: ${hash}) reverted: ${revertReason}`);
4802
+ throw new Error(`${txDescription} transaction (hash: ${hash}) reverted: ${revertReason}`);
4803
+ }
4804
+ return hash;
4805
+ }
4806
+ function formatAppControllerError(decoded) {
4807
+ const errorName = decoded.errorName;
4808
+ switch (errorName) {
4809
+ case "MaxActiveAppsExceeded":
4810
+ return new Error(
4811
+ "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"
4812
+ );
4813
+ case "GlobalMaxActiveAppsExceeded":
4814
+ return new Error(
4815
+ "the platform has reached the maximum number of active apps. please try again later"
4816
+ );
4817
+ case "InvalidPermissions":
4818
+ return new Error("you don't have permission to perform this operation");
4819
+ case "AppAlreadyExists":
4820
+ return new Error("an app with this owner and salt already exists");
4821
+ case "AppDoesNotExist":
4822
+ return new Error("the specified app does not exist");
4823
+ case "InvalidAppStatus":
4824
+ return new Error("the app is in an invalid state for this operation");
4825
+ case "MoreThanOneArtifact":
4826
+ return new Error("only one artifact is allowed per release");
4827
+ case "InvalidSignature":
4828
+ return new Error("invalid signature provided");
4829
+ case "SignatureExpired":
4830
+ return new Error("the provided signature has expired");
4831
+ case "InvalidReleaseMetadataURI":
4832
+ return new Error("invalid release metadata URI provided");
4833
+ case "InvalidShortString":
4834
+ return new Error("invalid short string format");
4835
+ default:
4836
+ return new Error(`contract error: ${errorName}`);
4837
+ }
4838
+ }
4839
+ async function getActiveAppCount(publicClient, environmentConfig, user) {
4840
+ const count = await publicClient.readContract({
4841
+ address: environmentConfig.appControllerAddress,
4842
+ abi: AppController_default,
4843
+ functionName: "getActiveAppCount",
4844
+ args: [user]
4845
+ });
4846
+ return Number(count);
4847
+ }
4848
+ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
4849
+ const quota = await publicClient.readContract({
4850
+ address: environmentConfig.appControllerAddress,
4851
+ abi: AppController_default,
4852
+ functionName: "getMaxActiveAppsPerUser",
4853
+ args: [user]
4854
+ });
4855
+ return Number(quota);
4856
+ }
2881
4857
  async function getAppsByCreator(publicClient, environmentConfig, creator, offset, limit) {
2882
4858
  const result = await publicClient.readContract({
2883
4859
  address: environmentConfig.appControllerAddress,
@@ -2907,7 +4883,13 @@ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 10
2907
4883
  const allApps = [];
2908
4884
  const allConfigs = [];
2909
4885
  while (true) {
2910
- const { apps, appConfigs } = await getAppsByDeveloper(publicClient, env, developer, offset, pageSize);
4886
+ const { apps, appConfigs } = await getAppsByDeveloper(
4887
+ publicClient,
4888
+ env,
4889
+ developer,
4890
+ offset,
4891
+ pageSize
4892
+ );
2911
4893
  if (apps.length === 0) break;
2912
4894
  allApps.push(...apps);
2913
4895
  allConfigs.push(...appConfigs);
@@ -2919,6 +4901,74 @@ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 10
2919
4901
  appConfigs: allConfigs
2920
4902
  };
2921
4903
  }
4904
+ async function suspend(options, logger = noopLogger) {
4905
+ const { walletClient, publicClient, environmentConfig, account, apps } = options;
4906
+ const suspendData = (0, import_viem5.encodeFunctionData)({
4907
+ abi: AppController_default,
4908
+ functionName: "suspend",
4909
+ args: [account, apps]
4910
+ });
4911
+ const pendingMessage = `Suspending ${apps.length} app(s)...`;
4912
+ return sendAndWaitForTransaction(
4913
+ {
4914
+ walletClient,
4915
+ publicClient,
4916
+ environmentConfig,
4917
+ to: environmentConfig.appControllerAddress,
4918
+ data: suspendData,
4919
+ pendingMessage,
4920
+ txDescription: "Suspend"
4921
+ },
4922
+ logger
4923
+ );
4924
+ }
4925
+ async function isDelegated(options) {
4926
+ const { publicClient, environmentConfig, address } = options;
4927
+ return checkERC7702Delegation(
4928
+ publicClient,
4929
+ address,
4930
+ environmentConfig.erc7702DelegatorAddress
4931
+ );
4932
+ }
4933
+ async function undelegate(options, logger = noopLogger) {
4934
+ const { walletClient, publicClient, environmentConfig } = options;
4935
+ const account = walletClient.account;
4936
+ if (!account) {
4937
+ throw new Error("WalletClient must have an account attached");
4938
+ }
4939
+ const chain = getChainFromID(environmentConfig.chainID);
4940
+ const transactionNonce = await publicClient.getTransactionCount({
4941
+ address: account.address,
4942
+ blockTag: "pending"
4943
+ });
4944
+ const chainId = await publicClient.getChainId();
4945
+ const authorizationNonce = BigInt(transactionNonce) + 1n;
4946
+ logger.debug("Signing undelegate authorization");
4947
+ const signedAuthorization = await walletClient.signAuthorization({
4948
+ contractAddress: "0x0000000000000000000000000000000000000000",
4949
+ chainId,
4950
+ nonce: Number(authorizationNonce),
4951
+ account
4952
+ });
4953
+ const authorizationList = [signedAuthorization];
4954
+ const hash = await walletClient.sendTransaction({
4955
+ account,
4956
+ to: account.address,
4957
+ // Send to self
4958
+ data: "0x",
4959
+ // Empty data
4960
+ value: 0n,
4961
+ authorizationList,
4962
+ chain
4963
+ });
4964
+ logger.info(`Transaction sent: ${hash}`);
4965
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
4966
+ if (receipt.status === "reverted") {
4967
+ logger.error(`Undelegate transaction (hash: ${hash}) reverted`);
4968
+ throw new Error(`Undelegate transaction (hash: ${hash}) reverted`);
4969
+ }
4970
+ return hash;
4971
+ }
2922
4972
 
2923
4973
  // src/client/common/contract/encoders.ts
2924
4974
  var import_viem6 = require("viem");
@@ -2948,33 +4998,407 @@ function encodeTerminateAppData(appId) {
2948
4998
  args: [appId]
2949
4999
  });
2950
5000
  }
5001
+
5002
+ // src/client/common/auth/siwe.ts
5003
+ var import_siwe = require("siwe");
5004
+ var generateNonce = import_siwe.generateNonce;
5005
+ function createSiweMessage(params) {
5006
+ const now = /* @__PURE__ */ new Date();
5007
+ const nonce = params.nonce || generateNonce();
5008
+ const issuedAt = params.issuedAt || now;
5009
+ const expirationTime = params.expirationTime || new Date(now.getTime() + 24 * 60 * 60 * 1e3);
5010
+ const siweMessage = new import_siwe.SiweMessage({
5011
+ domain: params.domain,
5012
+ address: params.address,
5013
+ statement: params.statement,
5014
+ uri: params.uri,
5015
+ version: "1",
5016
+ chainId: params.chainId,
5017
+ nonce,
5018
+ issuedAt: issuedAt.toISOString(),
5019
+ expirationTime: expirationTime.toISOString(),
5020
+ notBefore: params.notBefore?.toISOString(),
5021
+ requestId: params.requestId,
5022
+ resources: params.resources
5023
+ });
5024
+ return {
5025
+ message: siweMessage.prepareMessage(),
5026
+ params: {
5027
+ address: params.address,
5028
+ chainId: params.chainId,
5029
+ domain: params.domain,
5030
+ uri: params.uri,
5031
+ nonce,
5032
+ issuedAt,
5033
+ statement: params.statement,
5034
+ expirationTime,
5035
+ notBefore: params.notBefore,
5036
+ requestId: params.requestId,
5037
+ resources: params.resources
5038
+ }
5039
+ };
5040
+ }
5041
+ function parseSiweMessage(message) {
5042
+ try {
5043
+ const siweMessage = new import_siwe.SiweMessage(message);
5044
+ return {
5045
+ address: siweMessage.address,
5046
+ chainId: siweMessage.chainId,
5047
+ domain: siweMessage.domain,
5048
+ uri: siweMessage.uri,
5049
+ nonce: siweMessage.nonce,
5050
+ statement: siweMessage.statement,
5051
+ issuedAt: siweMessage.issuedAt ? new Date(siweMessage.issuedAt) : void 0,
5052
+ expirationTime: siweMessage.expirationTime ? new Date(siweMessage.expirationTime) : void 0,
5053
+ notBefore: siweMessage.notBefore ? new Date(siweMessage.notBefore) : void 0,
5054
+ requestId: siweMessage.requestId,
5055
+ resources: siweMessage.resources
5056
+ };
5057
+ } catch {
5058
+ return null;
5059
+ }
5060
+ }
5061
+ function isSiweMessageExpired(params) {
5062
+ if (!params.expirationTime) return false;
5063
+ return /* @__PURE__ */ new Date() > params.expirationTime;
5064
+ }
5065
+ function isSiweMessageNotYetValid(params) {
5066
+ if (!params.notBefore) return false;
5067
+ return /* @__PURE__ */ new Date() < params.notBefore;
5068
+ }
5069
+
5070
+ // src/browser.ts
5071
+ init_session();
5072
+
5073
+ // src/client/common/hooks/useComputeSession.ts
5074
+ var import_react = require("react");
5075
+ init_session();
5076
+ function useComputeSession(config) {
5077
+ const {
5078
+ baseUrl,
5079
+ refreshInterval = 6e4,
5080
+ // 1 minute default
5081
+ checkOnMount = true,
5082
+ onSessionExpired,
5083
+ onSessionRefreshed,
5084
+ onError
5085
+ } = config;
5086
+ const [session, setSession] = (0, import_react.useState)(null);
5087
+ const [isLoading, setIsLoading] = (0, import_react.useState)(checkOnMount);
5088
+ const [error, setError] = (0, import_react.useState)(null);
5089
+ const wasAuthenticatedRef = (0, import_react.useRef)(false);
5090
+ const isMountedRef = (0, import_react.useRef)(true);
5091
+ const refreshIntervalRef = (0, import_react.useRef)(null);
5092
+ const apiConfig = { baseUrl };
5093
+ const checkSession = (0, import_react.useCallback)(async () => {
5094
+ try {
5095
+ const sessionInfo = await getComputeApiSession(apiConfig);
5096
+ if (!isMountedRef.current) {
5097
+ return sessionInfo;
5098
+ }
5099
+ setSession(sessionInfo);
5100
+ setError(null);
5101
+ if (wasAuthenticatedRef.current && !sessionInfo.authenticated) {
5102
+ onSessionExpired?.();
5103
+ }
5104
+ wasAuthenticatedRef.current = sessionInfo.authenticated;
5105
+ if (sessionInfo.authenticated) {
5106
+ onSessionRefreshed?.(sessionInfo);
5107
+ }
5108
+ return sessionInfo;
5109
+ } catch (err) {
5110
+ if (!isMountedRef.current) {
5111
+ throw err;
5112
+ }
5113
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Failed to check session: ${String(err)}`, "UNKNOWN");
5114
+ setError(sessionError);
5115
+ onError?.(sessionError);
5116
+ const fallbackSession = { authenticated: false };
5117
+ setSession(fallbackSession);
5118
+ return fallbackSession;
5119
+ }
5120
+ }, [baseUrl, onSessionExpired, onSessionRefreshed, onError]);
5121
+ const refresh = (0, import_react.useCallback)(async () => {
5122
+ setIsLoading(true);
5123
+ try {
5124
+ return await checkSession();
5125
+ } finally {
5126
+ if (isMountedRef.current) {
5127
+ setIsLoading(false);
5128
+ }
5129
+ }
5130
+ }, [checkSession]);
5131
+ const login = (0, import_react.useCallback)(
5132
+ async (params, signMessage) => {
5133
+ setIsLoading(true);
5134
+ setError(null);
5135
+ try {
5136
+ let domain = params.domain;
5137
+ let uri = params.uri;
5138
+ if (typeof window !== "undefined") {
5139
+ domain = domain || window.location.host;
5140
+ uri = uri || window.location.origin;
5141
+ }
5142
+ if (!domain || !uri) {
5143
+ throw new SessionError(
5144
+ "domain and uri are required when not in browser environment",
5145
+ "INVALID_MESSAGE"
5146
+ );
5147
+ }
5148
+ const siweMessage = createSiweMessage({
5149
+ ...params,
5150
+ domain,
5151
+ uri,
5152
+ statement: params.statement || "Sign in to EigenCloud Compute API"
5153
+ });
5154
+ const signature = await signMessage({ message: siweMessage.message });
5155
+ await loginToComputeApi(apiConfig, {
5156
+ message: siweMessage.message,
5157
+ signature
5158
+ });
5159
+ const sessionInfo = await checkSession();
5160
+ if (!isMountedRef.current) {
5161
+ return sessionInfo;
5162
+ }
5163
+ wasAuthenticatedRef.current = sessionInfo.authenticated;
5164
+ return sessionInfo;
5165
+ } catch (err) {
5166
+ if (!isMountedRef.current) {
5167
+ throw err;
5168
+ }
5169
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Login failed: ${String(err)}`, "UNKNOWN");
5170
+ setError(sessionError);
5171
+ onError?.(sessionError);
5172
+ throw sessionError;
5173
+ } finally {
5174
+ if (isMountedRef.current) {
5175
+ setIsLoading(false);
5176
+ }
5177
+ }
5178
+ },
5179
+ [baseUrl, checkSession, onError]
5180
+ );
5181
+ const logout = (0, import_react.useCallback)(async () => {
5182
+ setIsLoading(true);
5183
+ setError(null);
5184
+ try {
5185
+ await logoutFromComputeApi(apiConfig);
5186
+ if (!isMountedRef.current) {
5187
+ return;
5188
+ }
5189
+ const newSession = { authenticated: false };
5190
+ setSession(newSession);
5191
+ wasAuthenticatedRef.current = false;
5192
+ } catch (err) {
5193
+ if (!isMountedRef.current) {
5194
+ throw err;
5195
+ }
5196
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Logout failed: ${String(err)}`, "UNKNOWN");
5197
+ setError(sessionError);
5198
+ onError?.(sessionError);
5199
+ setSession({ authenticated: false });
5200
+ wasAuthenticatedRef.current = false;
5201
+ } finally {
5202
+ if (isMountedRef.current) {
5203
+ setIsLoading(false);
5204
+ }
5205
+ }
5206
+ }, [baseUrl, onError]);
5207
+ const clearError = (0, import_react.useCallback)(() => {
5208
+ setError(null);
5209
+ }, []);
5210
+ (0, import_react.useEffect)(() => {
5211
+ isMountedRef.current = true;
5212
+ if (checkOnMount) {
5213
+ checkSession().finally(() => {
5214
+ if (isMountedRef.current) {
5215
+ setIsLoading(false);
5216
+ }
5217
+ });
5218
+ }
5219
+ return () => {
5220
+ isMountedRef.current = false;
5221
+ };
5222
+ }, [checkOnMount, checkSession]);
5223
+ (0, import_react.useEffect)(() => {
5224
+ if (refreshInterval <= 0) {
5225
+ return;
5226
+ }
5227
+ if (refreshIntervalRef.current) {
5228
+ clearInterval(refreshIntervalRef.current);
5229
+ }
5230
+ refreshIntervalRef.current = setInterval(() => {
5231
+ if (wasAuthenticatedRef.current) {
5232
+ checkSession();
5233
+ }
5234
+ }, refreshInterval);
5235
+ return () => {
5236
+ if (refreshIntervalRef.current) {
5237
+ clearInterval(refreshIntervalRef.current);
5238
+ refreshIntervalRef.current = null;
5239
+ }
5240
+ };
5241
+ }, [refreshInterval, checkSession]);
5242
+ return {
5243
+ session,
5244
+ isLoading,
5245
+ error,
5246
+ isAuthenticated: session?.authenticated ?? false,
5247
+ login,
5248
+ logout,
5249
+ refresh,
5250
+ clearError
5251
+ };
5252
+ }
5253
+
5254
+ // src/client/common/encryption/kms.ts
5255
+ var import_jose = require("jose");
5256
+ function getAppProtectedHeaders(appID) {
5257
+ return {
5258
+ "x-eigenx-app-id": appID
5259
+ };
5260
+ }
5261
+ async function encryptRSAOAEPAndAES256GCM(encryptionKeyPEM, plaintext, protectedHeaders) {
5262
+ const pemString = typeof encryptionKeyPEM === "string" ? encryptionKeyPEM : encryptionKeyPEM.toString("utf-8");
5263
+ const publicKey = await (0, import_jose.importSPKI)(pemString, "RSA-OAEP-256", {
5264
+ extractable: true
5265
+ });
5266
+ const header = {
5267
+ alg: "RSA-OAEP-256",
5268
+ // Key encryption algorithm (SHA-256)
5269
+ enc: "A256GCM",
5270
+ // Content encryption algorithm
5271
+ ...protectedHeaders || {}
5272
+ // Add custom protected headers
5273
+ };
5274
+ const plaintextBytes = new Uint8Array(plaintext);
5275
+ const jwe = await new import_jose.CompactEncrypt(plaintextBytes).setProtectedHeader(header).encrypt(publicKey);
5276
+ return jwe;
5277
+ }
5278
+
5279
+ // keys/mainnet-alpha/prod/kms-encryption-public-key.pem
5280
+ 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-----";
5281
+
5282
+ // keys/mainnet-alpha/prod/kms-signing-public-key.pem
5283
+ var kms_signing_public_key_default = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfxbhXJjH4D0DH/iW5/rK1HzWS+f9\nEyooZTrCYjCfezuOEmRuOWNaZLvwXN8SdzrvjWA7gSvOS85hLzp4grANRQ==\n-----END PUBLIC KEY-----";
5284
+
5285
+ // keys/sepolia/dev/kms-encryption-public-key.pem
5286
+ 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-----";
5287
+
5288
+ // keys/sepolia/dev/kms-signing-public-key.pem
5289
+ var kms_signing_public_key_default2 = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb2Q88/cxdic2xi4jS2V0dtYHjLwq\n4wVFBFmaY8TTXoMXNggKEdU6PuE8EovocVKMpw3SIlaM27z9uxksNVL2xw==\n-----END PUBLIC KEY-----\n";
5290
+
5291
+ // keys/sepolia/prod/kms-encryption-public-key.pem
5292
+ 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-----";
5293
+
5294
+ // keys/sepolia/prod/kms-signing-public-key.pem
5295
+ var kms_signing_public_key_default3 = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsk6ZdmmvBqFfKHs+1cYjIemRGN7h\n1NatIEitFRyx+3q8wmTJ9LknTE1FwWBLcCNTseJDti8Rh+SaVxfGOyJuuA==\n-----END PUBLIC KEY-----";
5296
+
5297
+ // src/client/common/utils/keys.ts
5298
+ var KEYS = {
5299
+ "mainnet-alpha": {
5300
+ prod: {
5301
+ encryption: kms_encryption_public_key_default,
5302
+ signing: kms_signing_public_key_default
5303
+ }
5304
+ },
5305
+ sepolia: {
5306
+ dev: {
5307
+ encryption: kms_encryption_public_key_default2,
5308
+ signing: kms_signing_public_key_default2
5309
+ },
5310
+ prod: {
5311
+ encryption: kms_encryption_public_key_default3,
5312
+ signing: kms_signing_public_key_default3
5313
+ }
5314
+ }
5315
+ };
5316
+ function getKMSKeysForEnvironment(environment, build = "prod") {
5317
+ const envKeys = KEYS[environment];
5318
+ if (!envKeys) {
5319
+ throw new Error(`No keys found for environment: ${environment}`);
5320
+ }
5321
+ const buildKeys = envKeys[build];
5322
+ if (!buildKeys) {
5323
+ throw new Error(`No keys found for environment: ${environment}, build: ${build}`);
5324
+ }
5325
+ return {
5326
+ encryptionKey: Buffer.from(buildKeys.encryption),
5327
+ signingKey: Buffer.from(buildKeys.signing)
5328
+ };
5329
+ }
2951
5330
  // Annotate the CommonJS export names for ESM import in node:
2952
5331
  0 && (module.exports = {
5332
+ BillingApiClient,
5333
+ BillingSessionError,
5334
+ BuildApiClient,
5335
+ SessionError,
2953
5336
  UserApiClient,
5337
+ addHexPrefix,
2954
5338
  assertValidImageReference,
2955
5339
  assertValidPrivateKey,
5340
+ calculateAppID,
5341
+ checkERC7702Delegation,
5342
+ createSiweMessage,
5343
+ deployApp,
2956
5344
  encodeStartAppData,
2957
5345
  encodeStopAppData,
2958
5346
  encodeTerminateAppData,
5347
+ encryptRSAOAEPAndAES256GCM,
2959
5348
  estimateBatchGas,
2960
5349
  estimateTransactionGas,
5350
+ executeBatch,
5351
+ executeDeployBatch,
5352
+ executeDeployBatched,
5353
+ executeDeploySequential,
5354
+ executeUpgradeBatch,
2961
5355
  extractAppNameFromImage,
2962
5356
  formatETH,
2963
5357
  generateNewPrivateKey,
5358
+ generateNonce,
2964
5359
  getActiveAppCount,
2965
5360
  getAllAppsByDeveloper,
5361
+ getAppProtectedHeaders,
2966
5362
  getAppsByCreator,
2967
5363
  getAppsByDeveloper,
2968
5364
  getAvailableEnvironments,
5365
+ getBillingApiSession,
5366
+ getBillingEnvironmentConfig,
2969
5367
  getBuildType,
5368
+ getChainFromID,
5369
+ getComputeApiSession,
2970
5370
  getEnvironmentConfig,
5371
+ getKMSKeysForEnvironment,
2971
5372
  getMaxActiveAppsPerUser,
5373
+ isBillingSessionValid,
5374
+ isDelegated,
2972
5375
  isEnvironmentAvailable,
2973
5376
  isMainnet,
5377
+ isSessionValid,
5378
+ isSiweMessageExpired,
5379
+ isSiweMessageNotYetValid,
2974
5380
  isSubscriptionActive,
5381
+ loginToBillingApi,
5382
+ loginToBothApis,
5383
+ loginToComputeApi,
5384
+ logoutFromBillingApi,
5385
+ logoutFromBothApis,
5386
+ logoutFromComputeApi,
5387
+ noopLogger,
5388
+ parseSiweMessage,
5389
+ prepareDeployBatch,
5390
+ prepareUpgradeBatch,
2975
5391
  sanitizeString,
2976
5392
  sanitizeURL,
2977
5393
  sanitizeXURL,
5394
+ sendAndWaitForTransaction,
5395
+ stripHexPrefix,
5396
+ supportsEIP5792,
5397
+ supportsEIP7702,
5398
+ suspend,
5399
+ undelegate,
5400
+ upgradeApp,
5401
+ useComputeSession,
2978
5402
  validateAppID,
2979
5403
  validateAppName,
2980
5404
  validateCreateAppParams,