@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.js CHANGED
@@ -1,3 +1,162 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/client/common/auth/session.ts
12
+ var session_exports = {};
13
+ __export(session_exports, {
14
+ SessionError: () => SessionError,
15
+ getComputeApiSession: () => getComputeApiSession,
16
+ isSessionValid: () => isSessionValid,
17
+ loginToComputeApi: () => loginToComputeApi,
18
+ logoutFromComputeApi: () => logoutFromComputeApi
19
+ });
20
+ function stripHexPrefix2(hex) {
21
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
22
+ }
23
+ async function parseErrorResponse(response) {
24
+ try {
25
+ const data = await response.json();
26
+ return data.error || response.statusText;
27
+ } catch {
28
+ return response.statusText;
29
+ }
30
+ }
31
+ async function loginToComputeApi(config, request) {
32
+ let response;
33
+ try {
34
+ response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
35
+ method: "POST",
36
+ credentials: "include",
37
+ // Include cookies for session management
38
+ headers: {
39
+ "Content-Type": "application/json"
40
+ },
41
+ body: JSON.stringify({
42
+ message: request.message,
43
+ signature: stripHexPrefix2(request.signature)
44
+ })
45
+ });
46
+ } catch (error) {
47
+ throw new SessionError(
48
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
49
+ "NETWORK_ERROR"
50
+ );
51
+ }
52
+ if (!response.ok) {
53
+ const errorMessage = await parseErrorResponse(response);
54
+ const status = response.status;
55
+ if (status === 400) {
56
+ if (errorMessage.toLowerCase().includes("siwe")) {
57
+ throw new SessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
58
+ }
59
+ throw new SessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
60
+ }
61
+ if (status === 401) {
62
+ throw new SessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
63
+ }
64
+ throw new SessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
65
+ }
66
+ const data = await response.json();
67
+ return {
68
+ success: data.success,
69
+ address: data.address
70
+ };
71
+ }
72
+ async function getComputeApiSession(config) {
73
+ let response;
74
+ try {
75
+ response = await fetch(`${config.baseUrl}/auth/session`, {
76
+ method: "GET",
77
+ credentials: "include",
78
+ // Include cookies for session management
79
+ headers: {
80
+ "Content-Type": "application/json"
81
+ }
82
+ });
83
+ } catch {
84
+ return {
85
+ authenticated: false
86
+ };
87
+ }
88
+ if (response.status === 401) {
89
+ return {
90
+ authenticated: false
91
+ };
92
+ }
93
+ if (!response.ok) {
94
+ const errorMessage = await parseErrorResponse(response);
95
+ throw new SessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
96
+ }
97
+ const data = await response.json();
98
+ return {
99
+ authenticated: data.authenticated,
100
+ address: data.address,
101
+ chainId: data.chain_id
102
+ };
103
+ }
104
+ async function logoutFromComputeApi(config) {
105
+ let response;
106
+ try {
107
+ response = await fetch(`${config.baseUrl}/auth/logout`, {
108
+ method: "POST",
109
+ credentials: "include",
110
+ // Include cookies for session management
111
+ headers: {
112
+ "Content-Type": "application/json"
113
+ }
114
+ });
115
+ } catch (error) {
116
+ throw new SessionError(
117
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
118
+ "NETWORK_ERROR"
119
+ );
120
+ }
121
+ if (response.status === 401) {
122
+ return;
123
+ }
124
+ if (!response.ok) {
125
+ const errorMessage = await parseErrorResponse(response);
126
+ throw new SessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
127
+ }
128
+ }
129
+ async function isSessionValid(config) {
130
+ const session = await getComputeApiSession(config);
131
+ return session.authenticated;
132
+ }
133
+ var SessionError;
134
+ var init_session = __esm({
135
+ "src/client/common/auth/session.ts"() {
136
+ "use strict";
137
+ SessionError = class extends Error {
138
+ constructor(message, code, statusCode) {
139
+ super(message);
140
+ this.code = code;
141
+ this.statusCode = statusCode;
142
+ this.name = "SessionError";
143
+ }
144
+ };
145
+ }
146
+ });
147
+
148
+ // src/client/common/types/index.ts
149
+ var noopLogger = {
150
+ debug: () => {
151
+ },
152
+ info: () => {
153
+ },
154
+ warn: () => {
155
+ },
156
+ error: () => {
157
+ }
158
+ };
159
+
1
160
  // src/client/common/config/environment.ts
2
161
  var SEPOLIA_CHAIN_ID = 11155111;
3
162
  var MAINNET_CHAIN_ID = 1;
@@ -12,6 +171,14 @@ var ChainAddresses = {
12
171
  PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
13
172
  }
14
173
  };
174
+ var BILLING_ENVIRONMENTS = {
175
+ dev: {
176
+ billingApiServerURL: "https://billingapi-dev.eigencloud.xyz"
177
+ },
178
+ prod: {
179
+ billingApiServerURL: "https://billingapi.eigencloud.xyz"
180
+ }
181
+ };
15
182
  var ENVIRONMENTS = {
16
183
  "sepolia-dev": {
17
184
  name: "sepolia",
@@ -70,6 +237,13 @@ function getEnvironmentConfig(environment, chainID) {
70
237
  chainID: BigInt(resolvedChainID)
71
238
  };
72
239
  }
240
+ function getBillingEnvironmentConfig(build) {
241
+ const config = BILLING_ENVIRONMENTS[build];
242
+ if (!config) {
243
+ throw new Error(`Unknown billing environment: ${build}`);
244
+ }
245
+ return config;
246
+ }
73
247
  function getBuildType() {
74
248
  const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
75
249
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
@@ -103,8 +277,13 @@ import { privateKeyToAccount } from "viem/accounts";
103
277
 
104
278
  // src/client/common/constants.ts
105
279
  import { sepolia, mainnet } from "viem/chains";
280
+ var SUPPORTED_CHAINS = [mainnet, sepolia];
106
281
 
107
282
  // src/client/common/utils/helpers.ts
283
+ function getChainFromID(chainID, fallback = sepolia2) {
284
+ const id = Number(chainID);
285
+ return extractChain({ chains: SUPPORTED_CHAINS, id }) || fallback;
286
+ }
108
287
  function addHexPrefix(value) {
109
288
  return value.startsWith("0x") ? value : `0x${value}`;
110
289
  }
@@ -338,8 +517,40 @@ async function calculatePermissionSignature(options) {
338
517
  });
339
518
  return { signature, digest };
340
519
  }
520
+ var generateBillingSigData = (product, expiry) => {
521
+ return {
522
+ domain: {
523
+ name: "EigenCloud Billing API",
524
+ version: "1"
525
+ },
526
+ types: {
527
+ BillingAuth: [
528
+ { name: "product", type: "string" },
529
+ { name: "expiry", type: "uint256" }
530
+ ]
531
+ },
532
+ primaryType: "BillingAuth",
533
+ message: {
534
+ product,
535
+ expiry
536
+ }
537
+ };
538
+ };
539
+ async function calculateBillingAuthSignature(options) {
540
+ const { walletClient, product, expiry } = options;
541
+ const account = walletClient.account;
542
+ if (!account) {
543
+ throw new Error("WalletClient must have an account attached");
544
+ }
545
+ const signature = await walletClient.signTypedData({
546
+ account,
547
+ ...generateBillingSigData(product, expiry)
548
+ });
549
+ return { signature, expiry };
550
+ }
341
551
 
342
552
  // src/client/common/utils/userapi.ts
553
+ init_session();
343
554
  function isJsonObject(value) {
344
555
  return typeof value === "object" && value !== null && !Array.isArray(value);
345
556
  }
@@ -356,15 +567,16 @@ var CanViewAppLogsPermission = "0x2fd3f2fe";
356
567
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
357
568
  var CanUpdateAppProfilePermission = "0x036fef61";
358
569
  function getDefaultClientId() {
359
- const version = true ? "0.2.1-dev" : "0.0.0";
570
+ const version = true ? "0.3.0-dev" : "0.0.0";
360
571
  return `ecloud-sdk/v${version}`;
361
572
  }
362
573
  var UserApiClient = class {
363
- constructor(config, walletClient, publicClient, clientId) {
574
+ constructor(config, walletClient, publicClient, options) {
364
575
  this.config = config;
365
576
  this.walletClient = walletClient;
366
577
  this.publicClient = publicClient;
367
- this.clientId = clientId || getDefaultClientId();
578
+ this.clientId = options?.clientId || getDefaultClientId();
579
+ this.useSession = options?.useSession ?? false;
368
580
  }
369
581
  /**
370
582
  * Get the address of the connected wallet
@@ -452,7 +664,7 @@ var UserApiClient = class {
452
664
  const apps = result.apps || result.Apps || [];
453
665
  return apps.map((app, i) => ({
454
666
  address: app.address || appIDs[i],
455
- status: app.status || app.Status || ""
667
+ status: app.app_status || app.App_Status || ""
456
668
  }));
457
669
  }
458
670
  /**
@@ -484,9 +696,11 @@ var UserApiClient = class {
484
696
  const headers = {
485
697
  "x-client-id": this.clientId
486
698
  };
487
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
488
- const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
489
- Object.assign(headers, authHeaders);
699
+ if (!this.useSession) {
700
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
701
+ const authHeaders = await this.generateAuthHeaders(CanUpdateAppProfilePermission, expiry);
702
+ Object.assign(headers, authHeaders);
703
+ }
490
704
  try {
491
705
  const response = await axios.post(endpoint, formData, {
492
706
  headers,
@@ -495,8 +709,10 @@ var UserApiClient = class {
495
709
  // Don't throw on any status
496
710
  maxContentLength: Infinity,
497
711
  // Allow large file uploads
498
- maxBodyLength: Infinity
712
+ maxBodyLength: Infinity,
499
713
  // Allow large file uploads
714
+ withCredentials: true
715
+ // Include cookies for session auth
500
716
  });
501
717
  const status = response.status;
502
718
  if (status !== 200 && status !== 201) {
@@ -530,7 +746,7 @@ Please check:
530
746
  const headers = {
531
747
  "x-client-id": this.clientId
532
748
  };
533
- if (permission) {
749
+ if (permission && !this.useSession) {
534
750
  const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
535
751
  const authHeaders = await this.generateAuthHeaders(permission, expiry);
536
752
  Object.assign(headers, authHeaders);
@@ -539,8 +755,10 @@ Please check:
539
755
  const response = await axios.get(url, {
540
756
  headers,
541
757
  maxRedirects: 0,
542
- validateStatus: () => true
758
+ validateStatus: () => true,
543
759
  // Don't throw on any status
760
+ withCredentials: true
761
+ // Include cookies for session auth
544
762
  });
545
763
  const status = response.status;
546
764
  const statusText = status >= 200 && status < 300 ? "OK" : "Error";
@@ -582,6 +800,65 @@ Please check:
582
800
  "X-eigenx-expiry": expiry.toString()
583
801
  };
584
802
  }
803
+ // ==========================================================================
804
+ // SIWE Session Management
805
+ // ==========================================================================
806
+ /**
807
+ * Login to the compute API using SIWE (Sign-In with Ethereum)
808
+ *
809
+ * This establishes a session with the compute API by verifying the SIWE message
810
+ * and signature. On success, a session cookie is set in the browser.
811
+ *
812
+ * @param request - Login request containing SIWE message and signature
813
+ * @returns Login result with the authenticated address
814
+ *
815
+ * @example
816
+ * ```typescript
817
+ * import { createSiweMessage } from "@layr-labs/ecloud-sdk/browser";
818
+ *
819
+ * const { message } = createSiweMessage({
820
+ * address: userAddress,
821
+ * chainId: 11155111,
822
+ * domain: window.location.host,
823
+ * uri: window.location.origin,
824
+ * });
825
+ *
826
+ * const signature = await signMessageAsync({ message });
827
+ * const result = await client.siweLogin({ message, signature });
828
+ * ```
829
+ */
830
+ async siweLogin(request) {
831
+ return loginToComputeApi({ baseUrl: this.config.userApiServerURL }, request);
832
+ }
833
+ /**
834
+ * Logout from the compute API
835
+ *
836
+ * This destroys the current session and clears the session cookie.
837
+ *
838
+ * @example
839
+ * ```typescript
840
+ * await client.siweLogout();
841
+ * ```
842
+ */
843
+ async siweLogout() {
844
+ return logoutFromComputeApi({ baseUrl: this.config.userApiServerURL });
845
+ }
846
+ /**
847
+ * Get the current SIWE session status from the compute API
848
+ *
849
+ * @returns Session information including authentication status and address
850
+ *
851
+ * @example
852
+ * ```typescript
853
+ * const session = await client.getSiweSession();
854
+ * if (session.authenticated) {
855
+ * console.log(`Logged in as ${session.address}`);
856
+ * }
857
+ * ```
858
+ */
859
+ async getSiweSession() {
860
+ return getComputeApiSession({ baseUrl: this.config.userApiServerURL });
861
+ }
585
862
  };
586
863
  function transformAppReleaseBuild(raw) {
587
864
  if (!isJsonObject(raw)) return void 0;
@@ -626,79 +903,658 @@ function transformAppRelease(raw) {
626
903
  };
627
904
  }
628
905
 
629
- // src/client/common/contract/eip7702.ts
630
- import { encodeFunctionData, encodeAbiParameters, decodeErrorResult } from "viem";
906
+ // src/client/common/utils/billingapi.ts
907
+ import axios2 from "axios";
631
908
 
632
- // src/client/common/abis/ERC7702Delegator.json
633
- var ERC7702Delegator_default = [
634
- {
635
- type: "constructor",
636
- inputs: [
637
- {
638
- name: "_delegationManager",
639
- type: "address",
640
- internalType: "contractIDelegationManager"
909
+ // src/client/common/auth/billingSession.ts
910
+ var BillingSessionError = class extends Error {
911
+ constructor(message, code, statusCode) {
912
+ super(message);
913
+ this.code = code;
914
+ this.statusCode = statusCode;
915
+ this.name = "BillingSessionError";
916
+ }
917
+ };
918
+ function stripHexPrefix3(hex) {
919
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
920
+ }
921
+ async function parseErrorResponse2(response) {
922
+ try {
923
+ const data = await response.json();
924
+ return data.error || response.statusText;
925
+ } catch {
926
+ return response.statusText;
927
+ }
928
+ }
929
+ async function loginToBillingApi(config, request) {
930
+ let response;
931
+ try {
932
+ response = await fetch(`${config.baseUrl}/auth/siwe/login`, {
933
+ method: "POST",
934
+ credentials: "include",
935
+ // Include cookies for session management
936
+ headers: {
937
+ "Content-Type": "application/json"
641
938
  },
642
- {
643
- name: "_entryPoint",
644
- type: "address",
645
- internalType: "contractIEntryPoint"
939
+ body: JSON.stringify({
940
+ message: request.message,
941
+ signature: stripHexPrefix3(request.signature)
942
+ })
943
+ });
944
+ } catch (error) {
945
+ throw new BillingSessionError(
946
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
947
+ "NETWORK_ERROR"
948
+ );
949
+ }
950
+ if (!response.ok) {
951
+ const errorMessage = await parseErrorResponse2(response);
952
+ const status = response.status;
953
+ if (status === 400) {
954
+ if (errorMessage.toLowerCase().includes("siwe")) {
955
+ throw new BillingSessionError(`Invalid SIWE message: ${errorMessage}`, "INVALID_MESSAGE", status);
646
956
  }
647
- ],
648
- stateMutability: "nonpayable"
649
- },
650
- {
651
- type: "receive",
652
- stateMutability: "payable"
653
- },
654
- {
655
- type: "function",
656
- name: "DOMAIN_VERSION",
657
- inputs: [],
658
- outputs: [
659
- {
660
- name: "",
661
- type: "string",
662
- internalType: "string"
957
+ throw new BillingSessionError(`Bad request: ${errorMessage}`, "INVALID_MESSAGE", status);
958
+ }
959
+ if (status === 401) {
960
+ throw new BillingSessionError(`Invalid signature: ${errorMessage}`, "INVALID_SIGNATURE", status);
961
+ }
962
+ throw new BillingSessionError(`Login failed: ${errorMessage}`, "UNKNOWN", status);
963
+ }
964
+ const data = await response.json();
965
+ return {
966
+ success: data.success,
967
+ address: data.address
968
+ };
969
+ }
970
+ async function getBillingApiSession(config) {
971
+ let response;
972
+ try {
973
+ response = await fetch(`${config.baseUrl}/auth/session`, {
974
+ method: "GET",
975
+ credentials: "include",
976
+ // Include cookies for session management
977
+ headers: {
978
+ "Content-Type": "application/json"
663
979
  }
664
- ],
665
- stateMutability: "view"
666
- },
667
- {
668
- type: "function",
669
- name: "NAME",
670
- inputs: [],
671
- outputs: [
672
- {
673
- name: "",
674
- type: "string",
675
- internalType: "string"
980
+ });
981
+ } catch {
982
+ return {
983
+ authenticated: false
984
+ };
985
+ }
986
+ if (response.status === 401) {
987
+ return {
988
+ authenticated: false
989
+ };
990
+ }
991
+ if (!response.ok) {
992
+ const errorMessage = await parseErrorResponse2(response);
993
+ throw new BillingSessionError(`Failed to get session: ${errorMessage}`, "UNKNOWN", response.status);
994
+ }
995
+ const data = await response.json();
996
+ return {
997
+ authenticated: data.authenticated,
998
+ address: data.address,
999
+ chainId: data.chainId,
1000
+ authenticatedAt: data.authenticatedAt
1001
+ };
1002
+ }
1003
+ async function logoutFromBillingApi(config) {
1004
+ let response;
1005
+ try {
1006
+ response = await fetch(`${config.baseUrl}/auth/logout`, {
1007
+ method: "POST",
1008
+ credentials: "include",
1009
+ // Include cookies for session management
1010
+ headers: {
1011
+ "Content-Type": "application/json"
676
1012
  }
677
- ],
678
- stateMutability: "view"
679
- },
680
- {
681
- type: "function",
682
- name: "PACKED_USER_OP_TYPEHASH",
683
- inputs: [],
684
- outputs: [
685
- {
686
- name: "",
687
- type: "bytes32",
688
- internalType: "bytes32"
1013
+ });
1014
+ } catch (error) {
1015
+ throw new BillingSessionError(
1016
+ `Network error connecting to ${config.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,
1017
+ "NETWORK_ERROR"
1018
+ );
1019
+ }
1020
+ if (response.status === 401) {
1021
+ return;
1022
+ }
1023
+ if (!response.ok) {
1024
+ const errorMessage = await parseErrorResponse2(response);
1025
+ throw new BillingSessionError(`Logout failed: ${errorMessage}`, "UNKNOWN", response.status);
1026
+ }
1027
+ }
1028
+ async function isBillingSessionValid(config) {
1029
+ const session = await getBillingApiSession(config);
1030
+ return session.authenticated;
1031
+ }
1032
+ async function loginToBothApis(computeConfig, billingConfig, request) {
1033
+ const { loginToComputeApi: loginToComputeApi2 } = await Promise.resolve().then(() => (init_session(), session_exports));
1034
+ const [compute, billing] = await Promise.all([
1035
+ loginToComputeApi2(computeConfig, request),
1036
+ loginToBillingApi(billingConfig, request)
1037
+ ]);
1038
+ return { compute, billing };
1039
+ }
1040
+ async function logoutFromBothApis(computeConfig, billingConfig) {
1041
+ const { logoutFromComputeApi: logoutFromComputeApi2 } = await Promise.resolve().then(() => (init_session(), session_exports));
1042
+ await Promise.all([
1043
+ logoutFromComputeApi2(computeConfig),
1044
+ logoutFromBillingApi(billingConfig)
1045
+ ]);
1046
+ }
1047
+
1048
+ // src/client/common/utils/billingapi.ts
1049
+ var BillingApiClient = class {
1050
+ constructor(config, walletClient, options = {}) {
1051
+ this.config = config;
1052
+ this.walletClient = walletClient;
1053
+ this.options = options;
1054
+ this.useSession = options.useSession ?? false;
1055
+ if (!this.useSession && !walletClient) {
1056
+ throw new Error("WalletClient is required when not using session authentication");
1057
+ }
1058
+ }
1059
+ /**
1060
+ * Get the address of the connected wallet
1061
+ * Returns undefined if using session auth without a wallet client
1062
+ */
1063
+ get address() {
1064
+ const account = this.walletClient?.account;
1065
+ if (!account) {
1066
+ if (!this.useSession) {
1067
+ throw new Error("WalletClient must have an account attached");
689
1068
  }
690
- ],
691
- stateMutability: "view"
692
- },
693
- {
694
- type: "function",
695
- name: "VERSION",
696
- inputs: [],
697
- outputs: [
698
- {
699
- name: "",
700
- type: "string",
701
- internalType: "string"
1069
+ return void 0;
1070
+ }
1071
+ return account.address;
1072
+ }
1073
+ /**
1074
+ * Get the base URL of the billing API
1075
+ */
1076
+ get baseUrl() {
1077
+ return this.config.billingApiServerURL;
1078
+ }
1079
+ // ==========================================================================
1080
+ // SIWE Session Methods
1081
+ // ==========================================================================
1082
+ /**
1083
+ * Login to the billing API using SIWE
1084
+ *
1085
+ * This establishes a session with the billing API by verifying the SIWE message
1086
+ * and signature. On success, a session cookie is set in the browser.
1087
+ *
1088
+ * @param request - Login request containing SIWE message and signature
1089
+ * @returns Login result with the authenticated address
1090
+ *
1091
+ * @example
1092
+ * ```typescript
1093
+ * const { message } = createSiweMessage({
1094
+ * address: userAddress,
1095
+ * chainId: 11155111,
1096
+ * domain: window.location.host,
1097
+ * uri: window.location.origin,
1098
+ * });
1099
+ *
1100
+ * const signature = await signMessageAsync({ message });
1101
+ * const result = await billingClient.siweLogin({ message, signature });
1102
+ * ```
1103
+ */
1104
+ async siweLogin(request) {
1105
+ return loginToBillingApi({ baseUrl: this.baseUrl }, request);
1106
+ }
1107
+ /**
1108
+ * Logout from the billing API
1109
+ *
1110
+ * This destroys the current session and clears the session cookie.
1111
+ */
1112
+ async siweLogout() {
1113
+ return logoutFromBillingApi({ baseUrl: this.baseUrl });
1114
+ }
1115
+ /**
1116
+ * Get the current session status from the billing API
1117
+ *
1118
+ * @returns Session information including authentication status and address
1119
+ */
1120
+ async getSession() {
1121
+ return getBillingApiSession({ baseUrl: this.baseUrl });
1122
+ }
1123
+ /**
1124
+ * Check if there is a valid session
1125
+ *
1126
+ * @returns True if session is authenticated, false otherwise
1127
+ */
1128
+ async isSessionValid() {
1129
+ const session = await this.getSession();
1130
+ return session.authenticated;
1131
+ }
1132
+ // ==========================================================================
1133
+ // Subscription Methods
1134
+ // ==========================================================================
1135
+ async createSubscription(productId = "compute", options) {
1136
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1137
+ const body = options ? {
1138
+ success_url: options.successUrl,
1139
+ cancel_url: options.cancelUrl
1140
+ } : void 0;
1141
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", productId, body);
1142
+ return resp.json();
1143
+ }
1144
+ async getSubscription(productId = "compute") {
1145
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1146
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", productId);
1147
+ return resp.json();
1148
+ }
1149
+ async cancelSubscription(productId = "compute") {
1150
+ const endpoint = `${this.config.billingApiServerURL}/products/${productId}/subscription`;
1151
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", productId);
1152
+ }
1153
+ // ==========================================================================
1154
+ // Internal Methods
1155
+ // ==========================================================================
1156
+ /**
1157
+ * Make an authenticated request to the billing API
1158
+ *
1159
+ * Uses session auth if useSession is true, otherwise uses EIP-712 signature auth.
1160
+ */
1161
+ async makeAuthenticatedRequest(url, method, productId, body) {
1162
+ if (this.useSession) {
1163
+ return this.makeSessionAuthenticatedRequest(url, method, body);
1164
+ }
1165
+ return this.makeSignatureAuthenticatedRequest(url, method, productId, body);
1166
+ }
1167
+ /**
1168
+ * Make a request using session-based authentication (cookies)
1169
+ */
1170
+ async makeSessionAuthenticatedRequest(url, method, body) {
1171
+ const headers = {};
1172
+ if (body) {
1173
+ headers["Content-Type"] = "application/json";
1174
+ }
1175
+ try {
1176
+ const response = await fetch(url, {
1177
+ method,
1178
+ credentials: "include",
1179
+ // Include cookies for session management
1180
+ headers,
1181
+ body: body ? JSON.stringify(body) : void 0
1182
+ });
1183
+ const status = response.status;
1184
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
1185
+ if (status < 200 || status >= 300) {
1186
+ let errorBody;
1187
+ try {
1188
+ errorBody = await response.text();
1189
+ } catch {
1190
+ errorBody = statusText;
1191
+ }
1192
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${errorBody}`);
1193
+ }
1194
+ const responseData = await response.json();
1195
+ return {
1196
+ json: async () => responseData,
1197
+ text: async () => JSON.stringify(responseData)
1198
+ };
1199
+ } catch (error) {
1200
+ if (error.name === "TypeError" || error.message?.includes("fetch")) {
1201
+ throw new Error(
1202
+ `Failed to connect to BillingAPI at ${url}: ${error.message}
1203
+ Please check:
1204
+ 1. Your internet connection
1205
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
1206
+ 3. Firewall/proxy settings`
1207
+ );
1208
+ }
1209
+ throw error;
1210
+ }
1211
+ }
1212
+ /**
1213
+ * Make a request using EIP-712 signature authentication
1214
+ */
1215
+ async makeSignatureAuthenticatedRequest(url, method, productId, body) {
1216
+ if (!this.walletClient) {
1217
+ throw new Error("WalletClient is required for signature authentication");
1218
+ }
1219
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
1220
+ const { signature } = await calculateBillingAuthSignature({
1221
+ walletClient: this.walletClient,
1222
+ product: productId,
1223
+ expiry
1224
+ });
1225
+ const headers = {
1226
+ Authorization: `Bearer ${signature}`,
1227
+ "X-Account": this.address,
1228
+ "X-Expiry": expiry.toString()
1229
+ };
1230
+ if (body) {
1231
+ headers["Content-Type"] = "application/json";
1232
+ }
1233
+ try {
1234
+ const response = await axios2({
1235
+ method,
1236
+ url,
1237
+ headers,
1238
+ data: body,
1239
+ timeout: 3e4,
1240
+ maxRedirects: 0,
1241
+ validateStatus: () => true
1242
+ // Don't throw on any status
1243
+ });
1244
+ const status = response.status;
1245
+ const statusText = status >= 200 && status < 300 ? "OK" : "Error";
1246
+ if (status < 200 || status >= 300) {
1247
+ const body2 = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
1248
+ throw new Error(`BillingAPI request failed: ${status} ${statusText} - ${body2}`);
1249
+ }
1250
+ return {
1251
+ json: async () => response.data,
1252
+ text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
1253
+ };
1254
+ } catch (error) {
1255
+ if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
1256
+ const cause = error.cause?.message || error.cause || error.message;
1257
+ throw new Error(
1258
+ `Failed to connect to BillingAPI at ${url}: ${cause}
1259
+ Please check:
1260
+ 1. Your internet connection
1261
+ 2. The API server is accessible: ${this.config.billingApiServerURL}
1262
+ 3. Firewall/proxy settings`
1263
+ );
1264
+ }
1265
+ throw error;
1266
+ }
1267
+ }
1268
+ };
1269
+
1270
+ // src/client/common/utils/buildapi.ts
1271
+ import axios3 from "axios";
1272
+ var MAX_RETRIES = 5;
1273
+ var INITIAL_BACKOFF_MS = 1e3;
1274
+ var MAX_BACKOFF_MS = 3e4;
1275
+ async function sleep(ms) {
1276
+ return new Promise((resolve) => setTimeout(resolve, ms));
1277
+ }
1278
+ function getRetryDelay(res, attempt) {
1279
+ const retryAfter = res.headers["retry-after"];
1280
+ if (retryAfter) {
1281
+ const seconds = parseInt(retryAfter, 10);
1282
+ if (!isNaN(seconds)) {
1283
+ return Math.min(seconds * 1e3, MAX_BACKOFF_MS);
1284
+ }
1285
+ }
1286
+ return Math.min(INITIAL_BACKOFF_MS * Math.pow(2, attempt), MAX_BACKOFF_MS);
1287
+ }
1288
+ async function requestWithRetry(config) {
1289
+ let lastResponse;
1290
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1291
+ const res = await axios3({ ...config, validateStatus: () => true });
1292
+ lastResponse = res;
1293
+ if (res.status !== 429) {
1294
+ return res;
1295
+ }
1296
+ if (attempt < MAX_RETRIES) {
1297
+ const delay = getRetryDelay(res, attempt);
1298
+ await sleep(delay);
1299
+ }
1300
+ }
1301
+ return lastResponse;
1302
+ }
1303
+ var BuildApiClient = class {
1304
+ constructor(options) {
1305
+ let url = options.baseUrl;
1306
+ while (url.endsWith("/")) {
1307
+ url = url.slice(0, -1);
1308
+ }
1309
+ this.baseUrl = url;
1310
+ this.clientId = options.clientId;
1311
+ this.walletClient = options.walletClient;
1312
+ this.useSession = options.useSession ?? false;
1313
+ this.billingSessionId = options.billingSessionId;
1314
+ }
1315
+ /**
1316
+ * Update the billing session ID.
1317
+ * Call this after logging into the billing API to enable session-based auth for builds.
1318
+ */
1319
+ setBillingSessionId(sessionId) {
1320
+ this.billingSessionId = sessionId;
1321
+ }
1322
+ /**
1323
+ * Get the address of the connected wallet
1324
+ */
1325
+ get address() {
1326
+ const account = this.walletClient?.account;
1327
+ if (!account) {
1328
+ throw new Error("WalletClient must have an account attached");
1329
+ }
1330
+ return account.address;
1331
+ }
1332
+ /**
1333
+ * Submit a new build request.
1334
+ * Supports two auth modes (session auth is tried first when billingSessionId is available):
1335
+ * 1. Session-based auth: X-Billing-Session header (forwarded billing_session cookie)
1336
+ * 2. Signature-based auth: Authorization + X-Account + X-eigenx-expiry headers (requires walletClient)
1337
+ */
1338
+ async submitBuild(payload) {
1339
+ if (this.useSession && this.billingSessionId) {
1340
+ return this.billingSessionAuthJsonRequest("/builds", "POST", payload);
1341
+ }
1342
+ return this.signatureAuthJsonRequest("/builds", "POST", payload);
1343
+ }
1344
+ async getBuild(buildId) {
1345
+ return this.publicJsonRequest(`/builds/${encodeURIComponent(buildId)}`);
1346
+ }
1347
+ async getBuildByDigest(digest) {
1348
+ return this.publicJsonRequest(`/builds/image/${encodeURIComponent(digest)}`);
1349
+ }
1350
+ async verify(identifier) {
1351
+ return this.publicJsonRequest(`/builds/verify/${encodeURIComponent(identifier)}`);
1352
+ }
1353
+ /**
1354
+ * Get build logs. Supports session auth (identity verification only, no billing check).
1355
+ */
1356
+ async getLogs(buildId) {
1357
+ return this.sessionOrSignatureTextRequest(`/builds/${encodeURIComponent(buildId)}/logs`);
1358
+ }
1359
+ async listBuilds(params) {
1360
+ const res = await requestWithRetry({
1361
+ url: `${this.baseUrl}/builds`,
1362
+ method: "GET",
1363
+ params,
1364
+ headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
1365
+ timeout: 6e4,
1366
+ validateStatus: () => true,
1367
+ withCredentials: this.useSession
1368
+ });
1369
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1370
+ return res.data;
1371
+ }
1372
+ async publicJsonRequest(path) {
1373
+ const res = await requestWithRetry({
1374
+ url: `${this.baseUrl}${path}`,
1375
+ method: "GET",
1376
+ headers: this.clientId ? { "x-client-id": this.clientId } : void 0,
1377
+ timeout: 6e4,
1378
+ validateStatus: () => true,
1379
+ withCredentials: this.useSession
1380
+ });
1381
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1382
+ return res.data;
1383
+ }
1384
+ /**
1385
+ * Make a request that ALWAYS requires signature auth (for billing verification).
1386
+ * Used for endpoints like POST /builds that need to verify subscription status.
1387
+ */
1388
+ async signatureAuthJsonRequest(path, method, body) {
1389
+ if (!this.walletClient?.account) {
1390
+ throw new Error("WalletClient with account required for authenticated requests");
1391
+ }
1392
+ const headers = {
1393
+ "Content-Type": "application/json"
1394
+ };
1395
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1396
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
1397
+ const { signature } = await calculateBillingAuthSignature({
1398
+ walletClient: this.walletClient,
1399
+ product: "compute",
1400
+ expiry
1401
+ });
1402
+ headers.Authorization = `Bearer ${signature}`;
1403
+ headers["X-eigenx-expiry"] = expiry.toString();
1404
+ headers["X-Account"] = this.address;
1405
+ const res = await requestWithRetry({
1406
+ url: `${this.baseUrl}${path}`,
1407
+ method,
1408
+ headers,
1409
+ data: body,
1410
+ timeout: 6e4,
1411
+ validateStatus: () => true,
1412
+ withCredentials: this.useSession
1413
+ });
1414
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1415
+ return res.data;
1416
+ }
1417
+ /**
1418
+ * Make a request using billing session auth (for billing verification without wallet signature).
1419
+ * Forwards the billing_session cookie value via X-Billing-Session header.
1420
+ * Used for endpoints that need to verify subscription status when using session-based auth.
1421
+ */
1422
+ async billingSessionAuthJsonRequest(path, method, body) {
1423
+ if (!this.billingSessionId) {
1424
+ throw new Error("billingSessionId required for session-based billing auth");
1425
+ }
1426
+ const headers = {
1427
+ "Content-Type": "application/json",
1428
+ "X-Billing-Session": this.billingSessionId
1429
+ };
1430
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1431
+ const res = await requestWithRetry({
1432
+ url: `${this.baseUrl}${path}`,
1433
+ method,
1434
+ headers,
1435
+ data: body,
1436
+ timeout: 6e4,
1437
+ validateStatus: () => true,
1438
+ withCredentials: this.useSession
1439
+ });
1440
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1441
+ return res.data;
1442
+ }
1443
+ /**
1444
+ * Make an authenticated request that can use session OR signature auth.
1445
+ * When useSession is true, relies on cookies for identity verification.
1446
+ * Used for endpoints that only need identity verification (not billing).
1447
+ */
1448
+ async sessionOrSignatureTextRequest(path) {
1449
+ const headers = {};
1450
+ if (this.clientId) headers["x-client-id"] = this.clientId;
1451
+ if (!this.useSession) {
1452
+ if (!this.walletClient?.account) {
1453
+ throw new Error("WalletClient with account required for authenticated requests");
1454
+ }
1455
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + 60);
1456
+ const { signature } = await calculateBillingAuthSignature({
1457
+ walletClient: this.walletClient,
1458
+ product: "compute",
1459
+ expiry
1460
+ });
1461
+ headers.Authorization = `Bearer ${signature}`;
1462
+ headers["X-eigenx-expiry"] = expiry.toString();
1463
+ headers["X-Account"] = this.address;
1464
+ }
1465
+ const res = await requestWithRetry({
1466
+ url: `${this.baseUrl}${path}`,
1467
+ method: "GET",
1468
+ headers,
1469
+ timeout: 6e4,
1470
+ responseType: "text",
1471
+ validateStatus: () => true,
1472
+ withCredentials: this.useSession
1473
+ });
1474
+ if (res.status < 200 || res.status >= 300) throw buildApiHttpError(res);
1475
+ return typeof res.data === "string" ? res.data : JSON.stringify(res.data);
1476
+ }
1477
+ };
1478
+ function buildApiHttpError(res) {
1479
+ const status = res.status;
1480
+ const body = typeof res.data === "string" ? res.data : res.data ? JSON.stringify(res.data) : "";
1481
+ const url = res.config?.url ? ` ${res.config.url}` : "";
1482
+ return new Error(`BuildAPI request failed: ${status}${url} - ${body || "Unknown error"}`);
1483
+ }
1484
+
1485
+ // src/client/common/contract/eip7702.ts
1486
+ import { encodeFunctionData, encodeAbiParameters, decodeErrorResult } from "viem";
1487
+
1488
+ // src/client/common/abis/ERC7702Delegator.json
1489
+ var ERC7702Delegator_default = [
1490
+ {
1491
+ type: "constructor",
1492
+ inputs: [
1493
+ {
1494
+ name: "_delegationManager",
1495
+ type: "address",
1496
+ internalType: "contractIDelegationManager"
1497
+ },
1498
+ {
1499
+ name: "_entryPoint",
1500
+ type: "address",
1501
+ internalType: "contractIEntryPoint"
1502
+ }
1503
+ ],
1504
+ stateMutability: "nonpayable"
1505
+ },
1506
+ {
1507
+ type: "receive",
1508
+ stateMutability: "payable"
1509
+ },
1510
+ {
1511
+ type: "function",
1512
+ name: "DOMAIN_VERSION",
1513
+ inputs: [],
1514
+ outputs: [
1515
+ {
1516
+ name: "",
1517
+ type: "string",
1518
+ internalType: "string"
1519
+ }
1520
+ ],
1521
+ stateMutability: "view"
1522
+ },
1523
+ {
1524
+ type: "function",
1525
+ name: "NAME",
1526
+ inputs: [],
1527
+ outputs: [
1528
+ {
1529
+ name: "",
1530
+ type: "string",
1531
+ internalType: "string"
1532
+ }
1533
+ ],
1534
+ stateMutability: "view"
1535
+ },
1536
+ {
1537
+ type: "function",
1538
+ name: "PACKED_USER_OP_TYPEHASH",
1539
+ inputs: [],
1540
+ outputs: [
1541
+ {
1542
+ name: "",
1543
+ type: "bytes32",
1544
+ internalType: "bytes32"
1545
+ }
1546
+ ],
1547
+ stateMutability: "view"
1548
+ },
1549
+ {
1550
+ type: "function",
1551
+ name: "VERSION",
1552
+ inputs: [],
1553
+ outputs: [
1554
+ {
1555
+ name: "",
1556
+ type: "string",
1557
+ internalType: "string"
702
1558
  }
703
1559
  ],
704
1560
  stateMutability: "view"
@@ -1706,13 +2562,99 @@ async function estimateBatchGas(options) {
1706
2562
  maxCostEth: formatETH(maxCostWei)
1707
2563
  };
1708
2564
  }
2565
+ async function checkERC7702Delegation(publicClient, account, delegatorAddress) {
2566
+ const code = await publicClient.getCode({ address: account });
2567
+ if (!code) {
2568
+ return false;
2569
+ }
2570
+ const expectedCode = `0xef0100${delegatorAddress.slice(2)}`;
2571
+ return code.toLowerCase() === expectedCode.toLowerCase();
2572
+ }
2573
+ async function executeBatch(options, logger = noopLogger) {
2574
+ const { walletClient, publicClient, environmentConfig, executions, pendingMessage, gas } = options;
2575
+ const account = walletClient.account;
2576
+ if (!account) {
2577
+ throw new Error("Wallet client must have an account");
2578
+ }
2579
+ const chain = walletClient.chain;
2580
+ if (!chain) {
2581
+ throw new Error("Wallet client must have a chain");
2582
+ }
2583
+ const executeBatchData = encodeExecuteBatchData(executions);
2584
+ const isDelegated2 = await checkERC7702Delegation(
2585
+ publicClient,
2586
+ account.address,
2587
+ environmentConfig.erc7702DelegatorAddress
2588
+ );
2589
+ let authorizationList = [];
2590
+ if (!isDelegated2) {
2591
+ const transactionNonce = await publicClient.getTransactionCount({
2592
+ address: account.address,
2593
+ blockTag: "pending"
2594
+ });
2595
+ const chainId = await publicClient.getChainId();
2596
+ const authorizationNonce = transactionNonce + 1;
2597
+ logger.debug("Using wallet client signing for EIP-7702 authorization");
2598
+ const signedAuthorization = await walletClient.signAuthorization({
2599
+ account: account.address,
2600
+ contractAddress: environmentConfig.erc7702DelegatorAddress,
2601
+ chainId,
2602
+ nonce: Number(authorizationNonce)
2603
+ });
2604
+ authorizationList = [signedAuthorization];
2605
+ }
2606
+ if (pendingMessage) {
2607
+ logger.info(pendingMessage);
2608
+ }
2609
+ const txRequest = {
2610
+ account: walletClient.account,
2611
+ chain,
2612
+ to: account.address,
2613
+ data: executeBatchData,
2614
+ value: 0n
2615
+ };
2616
+ if (authorizationList.length > 0) {
2617
+ txRequest.authorizationList = authorizationList;
2618
+ }
2619
+ if (gas?.maxFeePerGas) {
2620
+ txRequest.maxFeePerGas = gas.maxFeePerGas;
2621
+ }
2622
+ if (gas?.maxPriorityFeePerGas) {
2623
+ txRequest.maxPriorityFeePerGas = gas.maxPriorityFeePerGas;
2624
+ }
2625
+ const hash = await walletClient.sendTransaction(txRequest);
2626
+ logger.info(`Transaction sent: ${hash}`);
2627
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
2628
+ if (receipt.status === "reverted") {
2629
+ let revertReason = "Unknown reason";
2630
+ try {
2631
+ await publicClient.call({
2632
+ to: account.address,
2633
+ data: executeBatchData,
2634
+ account: account.address
2635
+ });
2636
+ } catch (callError) {
2637
+ if (callError.data) {
2638
+ try {
2639
+ const decoded = decodeErrorResult({
2640
+ abi: ERC7702Delegator_default,
2641
+ data: callError.data
2642
+ });
2643
+ revertReason = `${decoded.errorName}: ${JSON.stringify(decoded.args)}`;
2644
+ } catch {
2645
+ revertReason = callError.message || "Unknown reason";
2646
+ }
2647
+ } else {
2648
+ revertReason = callError.message || "Unknown reason";
2649
+ }
2650
+ }
2651
+ throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2652
+ }
2653
+ return hash;
2654
+ }
1709
2655
 
1710
2656
  // src/client/common/contract/caller.ts
1711
- import {
1712
- encodeFunctionData as encodeFunctionData2,
1713
- decodeErrorResult as decodeErrorResult2,
1714
- bytesToHex
1715
- } from "viem";
2657
+ import { encodeFunctionData as encodeFunctionData2, decodeErrorResult as decodeErrorResult2, bytesToHex } from "viem";
1716
2658
 
1717
2659
  // src/client/common/abis/AppController.json
1718
2660
  var AppController_default = [
@@ -2762,55 +3704,1047 @@ var AppController_default = [
2762
3704
  }
2763
3705
  ];
2764
3706
 
2765
- // src/client/common/contract/caller.ts
2766
- function formatETH(wei) {
2767
- const eth = Number(wei) / 1e18;
2768
- const costStr = eth.toFixed(6);
2769
- const trimmed = costStr.replace(/\.?0+$/, "");
2770
- if (trimmed === "0" && wei > 0n) {
2771
- return "<0.000001";
2772
- }
2773
- return trimmed;
2774
- }
2775
- async function estimateTransactionGas(options) {
2776
- const { publicClient, from, to, data, value = 0n } = options;
2777
- const fees = await publicClient.estimateFeesPerGas();
2778
- const gasLimit = await publicClient.estimateGas({
2779
- account: from,
2780
- to,
2781
- data,
2782
- value
2783
- });
2784
- const maxFeePerGas = fees.maxFeePerGas;
2785
- const maxPriorityFeePerGas = fees.maxPriorityFeePerGas;
2786
- const maxCostWei = gasLimit * maxFeePerGas;
2787
- const maxCostEth = formatETH(maxCostWei);
2788
- return {
2789
- gasLimit,
2790
- maxFeePerGas,
2791
- maxPriorityFeePerGas,
2792
- maxCostWei,
2793
- maxCostEth
2794
- };
2795
- }
2796
- async function getActiveAppCount(publicClient, environmentConfig, user) {
2797
- const count = await publicClient.readContract({
2798
- address: environmentConfig.appControllerAddress,
2799
- abi: AppController_default,
2800
- functionName: "getActiveAppCount",
2801
- args: [user]
2802
- });
2803
- return Number(count);
2804
- }
2805
- async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
2806
- const quota = await publicClient.readContract({
2807
- address: environmentConfig.appControllerAddress,
2808
- abi: AppController_default,
2809
- functionName: "getMaxActiveAppsPerUser",
2810
- args: [user]
2811
- });
2812
- return Number(quota);
2813
- }
3707
+ // src/client/common/abis/PermissionController.json
3708
+ var PermissionController_default = [
3709
+ {
3710
+ type: "function",
3711
+ name: "acceptAdmin",
3712
+ inputs: [
3713
+ {
3714
+ name: "account",
3715
+ type: "address",
3716
+ internalType: "address"
3717
+ }
3718
+ ],
3719
+ outputs: [],
3720
+ stateMutability: "nonpayable"
3721
+ },
3722
+ {
3723
+ type: "function",
3724
+ name: "addPendingAdmin",
3725
+ inputs: [
3726
+ {
3727
+ name: "account",
3728
+ type: "address",
3729
+ internalType: "address"
3730
+ },
3731
+ {
3732
+ name: "admin",
3733
+ type: "address",
3734
+ internalType: "address"
3735
+ }
3736
+ ],
3737
+ outputs: [],
3738
+ stateMutability: "nonpayable"
3739
+ },
3740
+ {
3741
+ type: "function",
3742
+ name: "canCall",
3743
+ inputs: [
3744
+ {
3745
+ name: "account",
3746
+ type: "address",
3747
+ internalType: "address"
3748
+ },
3749
+ {
3750
+ name: "caller",
3751
+ type: "address",
3752
+ internalType: "address"
3753
+ },
3754
+ {
3755
+ name: "target",
3756
+ type: "address",
3757
+ internalType: "address"
3758
+ },
3759
+ {
3760
+ name: "selector",
3761
+ type: "bytes4",
3762
+ internalType: "bytes4"
3763
+ }
3764
+ ],
3765
+ outputs: [
3766
+ {
3767
+ name: "",
3768
+ type: "bool",
3769
+ internalType: "bool"
3770
+ }
3771
+ ],
3772
+ stateMutability: "nonpayable"
3773
+ },
3774
+ {
3775
+ type: "function",
3776
+ name: "getAdmins",
3777
+ inputs: [
3778
+ {
3779
+ name: "account",
3780
+ type: "address",
3781
+ internalType: "address"
3782
+ }
3783
+ ],
3784
+ outputs: [
3785
+ {
3786
+ name: "",
3787
+ type: "address[]",
3788
+ internalType: "address[]"
3789
+ }
3790
+ ],
3791
+ stateMutability: "view"
3792
+ },
3793
+ {
3794
+ type: "function",
3795
+ name: "getAppointeePermissions",
3796
+ inputs: [
3797
+ {
3798
+ name: "account",
3799
+ type: "address",
3800
+ internalType: "address"
3801
+ },
3802
+ {
3803
+ name: "appointee",
3804
+ type: "address",
3805
+ internalType: "address"
3806
+ }
3807
+ ],
3808
+ outputs: [
3809
+ {
3810
+ name: "",
3811
+ type: "address[]",
3812
+ internalType: "address[]"
3813
+ },
3814
+ {
3815
+ name: "",
3816
+ type: "bytes4[]",
3817
+ internalType: "bytes4[]"
3818
+ }
3819
+ ],
3820
+ stateMutability: "nonpayable"
3821
+ },
3822
+ {
3823
+ type: "function",
3824
+ name: "getAppointees",
3825
+ inputs: [
3826
+ {
3827
+ name: "account",
3828
+ type: "address",
3829
+ internalType: "address"
3830
+ },
3831
+ {
3832
+ name: "target",
3833
+ type: "address",
3834
+ internalType: "address"
3835
+ },
3836
+ {
3837
+ name: "selector",
3838
+ type: "bytes4",
3839
+ internalType: "bytes4"
3840
+ }
3841
+ ],
3842
+ outputs: [
3843
+ {
3844
+ name: "",
3845
+ type: "address[]",
3846
+ internalType: "address[]"
3847
+ }
3848
+ ],
3849
+ stateMutability: "nonpayable"
3850
+ },
3851
+ {
3852
+ type: "function",
3853
+ name: "getPendingAdmins",
3854
+ inputs: [
3855
+ {
3856
+ name: "account",
3857
+ type: "address",
3858
+ internalType: "address"
3859
+ }
3860
+ ],
3861
+ outputs: [
3862
+ {
3863
+ name: "",
3864
+ type: "address[]",
3865
+ internalType: "address[]"
3866
+ }
3867
+ ],
3868
+ stateMutability: "view"
3869
+ },
3870
+ {
3871
+ type: "function",
3872
+ name: "isAdmin",
3873
+ inputs: [
3874
+ {
3875
+ name: "account",
3876
+ type: "address",
3877
+ internalType: "address"
3878
+ },
3879
+ {
3880
+ name: "caller",
3881
+ type: "address",
3882
+ internalType: "address"
3883
+ }
3884
+ ],
3885
+ outputs: [
3886
+ {
3887
+ name: "",
3888
+ type: "bool",
3889
+ internalType: "bool"
3890
+ }
3891
+ ],
3892
+ stateMutability: "view"
3893
+ },
3894
+ {
3895
+ type: "function",
3896
+ name: "isPendingAdmin",
3897
+ inputs: [
3898
+ {
3899
+ name: "account",
3900
+ type: "address",
3901
+ internalType: "address"
3902
+ },
3903
+ {
3904
+ name: "pendingAdmin",
3905
+ type: "address",
3906
+ internalType: "address"
3907
+ }
3908
+ ],
3909
+ outputs: [
3910
+ {
3911
+ name: "",
3912
+ type: "bool",
3913
+ internalType: "bool"
3914
+ }
3915
+ ],
3916
+ stateMutability: "view"
3917
+ },
3918
+ {
3919
+ type: "function",
3920
+ name: "removeAdmin",
3921
+ inputs: [
3922
+ {
3923
+ name: "account",
3924
+ type: "address",
3925
+ internalType: "address"
3926
+ },
3927
+ {
3928
+ name: "admin",
3929
+ type: "address",
3930
+ internalType: "address"
3931
+ }
3932
+ ],
3933
+ outputs: [],
3934
+ stateMutability: "nonpayable"
3935
+ },
3936
+ {
3937
+ type: "function",
3938
+ name: "removeAppointee",
3939
+ inputs: [
3940
+ {
3941
+ name: "account",
3942
+ type: "address",
3943
+ internalType: "address"
3944
+ },
3945
+ {
3946
+ name: "appointee",
3947
+ type: "address",
3948
+ internalType: "address"
3949
+ },
3950
+ {
3951
+ name: "target",
3952
+ type: "address",
3953
+ internalType: "address"
3954
+ },
3955
+ {
3956
+ name: "selector",
3957
+ type: "bytes4",
3958
+ internalType: "bytes4"
3959
+ }
3960
+ ],
3961
+ outputs: [],
3962
+ stateMutability: "nonpayable"
3963
+ },
3964
+ {
3965
+ type: "function",
3966
+ name: "removePendingAdmin",
3967
+ inputs: [
3968
+ {
3969
+ name: "account",
3970
+ type: "address",
3971
+ internalType: "address"
3972
+ },
3973
+ {
3974
+ name: "admin",
3975
+ type: "address",
3976
+ internalType: "address"
3977
+ }
3978
+ ],
3979
+ outputs: [],
3980
+ stateMutability: "nonpayable"
3981
+ },
3982
+ {
3983
+ type: "function",
3984
+ name: "setAppointee",
3985
+ inputs: [
3986
+ {
3987
+ name: "account",
3988
+ type: "address",
3989
+ internalType: "address"
3990
+ },
3991
+ {
3992
+ name: "appointee",
3993
+ type: "address",
3994
+ internalType: "address"
3995
+ },
3996
+ {
3997
+ name: "target",
3998
+ type: "address",
3999
+ internalType: "address"
4000
+ },
4001
+ {
4002
+ name: "selector",
4003
+ type: "bytes4",
4004
+ internalType: "bytes4"
4005
+ }
4006
+ ],
4007
+ outputs: [],
4008
+ stateMutability: "nonpayable"
4009
+ },
4010
+ {
4011
+ type: "function",
4012
+ name: "version",
4013
+ inputs: [],
4014
+ outputs: [
4015
+ {
4016
+ name: "",
4017
+ type: "string",
4018
+ internalType: "string"
4019
+ }
4020
+ ],
4021
+ stateMutability: "view"
4022
+ },
4023
+ {
4024
+ type: "event",
4025
+ name: "AdminRemoved",
4026
+ inputs: [
4027
+ {
4028
+ name: "account",
4029
+ type: "address",
4030
+ indexed: true,
4031
+ internalType: "address"
4032
+ },
4033
+ {
4034
+ name: "admin",
4035
+ type: "address",
4036
+ indexed: false,
4037
+ internalType: "address"
4038
+ }
4039
+ ],
4040
+ anonymous: false
4041
+ },
4042
+ {
4043
+ type: "event",
4044
+ name: "AdminSet",
4045
+ inputs: [
4046
+ {
4047
+ name: "account",
4048
+ type: "address",
4049
+ indexed: true,
4050
+ internalType: "address"
4051
+ },
4052
+ {
4053
+ name: "admin",
4054
+ type: "address",
4055
+ indexed: false,
4056
+ internalType: "address"
4057
+ }
4058
+ ],
4059
+ anonymous: false
4060
+ },
4061
+ {
4062
+ type: "event",
4063
+ name: "AppointeeRemoved",
4064
+ inputs: [
4065
+ {
4066
+ name: "account",
4067
+ type: "address",
4068
+ indexed: true,
4069
+ internalType: "address"
4070
+ },
4071
+ {
4072
+ name: "appointee",
4073
+ type: "address",
4074
+ indexed: true,
4075
+ internalType: "address"
4076
+ },
4077
+ {
4078
+ name: "target",
4079
+ type: "address",
4080
+ indexed: false,
4081
+ internalType: "address"
4082
+ },
4083
+ {
4084
+ name: "selector",
4085
+ type: "bytes4",
4086
+ indexed: false,
4087
+ internalType: "bytes4"
4088
+ }
4089
+ ],
4090
+ anonymous: false
4091
+ },
4092
+ {
4093
+ type: "event",
4094
+ name: "AppointeeSet",
4095
+ inputs: [
4096
+ {
4097
+ name: "account",
4098
+ type: "address",
4099
+ indexed: true,
4100
+ internalType: "address"
4101
+ },
4102
+ {
4103
+ name: "appointee",
4104
+ type: "address",
4105
+ indexed: true,
4106
+ internalType: "address"
4107
+ },
4108
+ {
4109
+ name: "target",
4110
+ type: "address",
4111
+ indexed: false,
4112
+ internalType: "address"
4113
+ },
4114
+ {
4115
+ name: "selector",
4116
+ type: "bytes4",
4117
+ indexed: false,
4118
+ internalType: "bytes4"
4119
+ }
4120
+ ],
4121
+ anonymous: false
4122
+ },
4123
+ {
4124
+ type: "event",
4125
+ name: "PendingAdminAdded",
4126
+ inputs: [
4127
+ {
4128
+ name: "account",
4129
+ type: "address",
4130
+ indexed: true,
4131
+ internalType: "address"
4132
+ },
4133
+ {
4134
+ name: "admin",
4135
+ type: "address",
4136
+ indexed: false,
4137
+ internalType: "address"
4138
+ }
4139
+ ],
4140
+ anonymous: false
4141
+ },
4142
+ {
4143
+ type: "event",
4144
+ name: "PendingAdminRemoved",
4145
+ inputs: [
4146
+ {
4147
+ name: "account",
4148
+ type: "address",
4149
+ indexed: true,
4150
+ internalType: "address"
4151
+ },
4152
+ {
4153
+ name: "admin",
4154
+ type: "address",
4155
+ indexed: false,
4156
+ internalType: "address"
4157
+ }
4158
+ ],
4159
+ anonymous: false
4160
+ },
4161
+ {
4162
+ type: "error",
4163
+ name: "AdminAlreadyPending",
4164
+ inputs: []
4165
+ },
4166
+ {
4167
+ type: "error",
4168
+ name: "AdminAlreadySet",
4169
+ inputs: []
4170
+ },
4171
+ {
4172
+ type: "error",
4173
+ name: "AdminNotPending",
4174
+ inputs: []
4175
+ },
4176
+ {
4177
+ type: "error",
4178
+ name: "AdminNotSet",
4179
+ inputs: []
4180
+ },
4181
+ {
4182
+ type: "error",
4183
+ name: "AppointeeAlreadySet",
4184
+ inputs: []
4185
+ },
4186
+ {
4187
+ type: "error",
4188
+ name: "AppointeeNotSet",
4189
+ inputs: []
4190
+ },
4191
+ {
4192
+ type: "error",
4193
+ name: "CannotHaveZeroAdmins",
4194
+ inputs: []
4195
+ },
4196
+ {
4197
+ type: "error",
4198
+ name: "NotAdmin",
4199
+ inputs: []
4200
+ }
4201
+ ];
4202
+
4203
+ // src/client/common/contract/caller.ts
4204
+ function formatETH(wei) {
4205
+ const eth = Number(wei) / 1e18;
4206
+ const costStr = eth.toFixed(6);
4207
+ const trimmed = costStr.replace(/\.?0+$/, "");
4208
+ if (trimmed === "0" && wei > 0n) {
4209
+ return "<0.000001";
4210
+ }
4211
+ return trimmed;
4212
+ }
4213
+ async function estimateTransactionGas(options) {
4214
+ const { publicClient, from, to, data, value = 0n } = options;
4215
+ const fees = await publicClient.estimateFeesPerGas();
4216
+ const gasLimit = await publicClient.estimateGas({
4217
+ account: from,
4218
+ to,
4219
+ data,
4220
+ value
4221
+ });
4222
+ const maxFeePerGas = fees.maxFeePerGas;
4223
+ const maxPriorityFeePerGas = fees.maxPriorityFeePerGas;
4224
+ const maxCostWei = gasLimit * maxFeePerGas;
4225
+ const maxCostEth = formatETH(maxCostWei);
4226
+ return {
4227
+ gasLimit,
4228
+ maxFeePerGas,
4229
+ maxPriorityFeePerGas,
4230
+ maxCostWei,
4231
+ maxCostEth
4232
+ };
4233
+ }
4234
+ async function calculateAppID(options) {
4235
+ const { publicClient, environmentConfig, ownerAddress, salt } = options;
4236
+ const saltHexString = bytesToHex(salt).slice(2);
4237
+ const paddedSaltHex = saltHexString.padStart(64, "0");
4238
+ const saltHex = `0x${paddedSaltHex}`;
4239
+ const appID = await publicClient.readContract({
4240
+ address: environmentConfig.appControllerAddress,
4241
+ abi: AppController_default,
4242
+ functionName: "calculateAppId",
4243
+ args: [ownerAddress, saltHex]
4244
+ });
4245
+ return appID;
4246
+ }
4247
+ async function prepareDeployBatch(options, logger = noopLogger) {
4248
+ const { walletClient, publicClient, environmentConfig, salt, release, publicLogs } = options;
4249
+ const account = walletClient.account;
4250
+ if (!account) {
4251
+ throw new Error("WalletClient must have an account attached");
4252
+ }
4253
+ logger.info("Calculating app ID...");
4254
+ const appId = await calculateAppID({
4255
+ publicClient,
4256
+ environmentConfig,
4257
+ ownerAddress: account.address,
4258
+ salt
4259
+ });
4260
+ logger.debug(`App ID calculated: ${appId}`);
4261
+ logger.debug(`This address will be used for acceptAdmin call`);
4262
+ const saltHexString = bytesToHex(salt).slice(2);
4263
+ const paddedSaltHex = saltHexString.padStart(64, "0");
4264
+ const saltHex = `0x${paddedSaltHex}`;
4265
+ const releaseForViem = {
4266
+ rmsRelease: {
4267
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4268
+ digest: `0x${bytesToHex(artifact.digest).slice(2).padStart(64, "0")}`,
4269
+ registry: artifact.registry
4270
+ })),
4271
+ upgradeByTime: release.rmsRelease.upgradeByTime
4272
+ },
4273
+ publicEnv: bytesToHex(release.publicEnv),
4274
+ encryptedEnv: bytesToHex(release.encryptedEnv)
4275
+ };
4276
+ const createData = encodeFunctionData2({
4277
+ abi: AppController_default,
4278
+ functionName: "createApp",
4279
+ args: [saltHex, releaseForViem]
4280
+ });
4281
+ const acceptAdminData = encodeFunctionData2({
4282
+ abi: PermissionController_default,
4283
+ functionName: "acceptAdmin",
4284
+ args: [appId]
4285
+ });
4286
+ const executions = [
4287
+ {
4288
+ target: environmentConfig.appControllerAddress,
4289
+ value: 0n,
4290
+ callData: createData
4291
+ },
4292
+ {
4293
+ target: environmentConfig.permissionControllerAddress,
4294
+ value: 0n,
4295
+ callData: acceptAdminData
4296
+ }
4297
+ ];
4298
+ if (publicLogs) {
4299
+ const anyoneCanViewLogsData = encodeFunctionData2({
4300
+ abi: PermissionController_default,
4301
+ functionName: "setAppointee",
4302
+ args: [
4303
+ appId,
4304
+ "0x493219d9949348178af1f58740655951a8cd110c",
4305
+ // AnyoneCanCallAddress
4306
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4307
+ // ApiPermissionsTarget
4308
+ "0x2fd3f2fe"
4309
+ // CanViewAppLogsPermission
4310
+ ]
4311
+ });
4312
+ executions.push({
4313
+ target: environmentConfig.permissionControllerAddress,
4314
+ value: 0n,
4315
+ callData: anyoneCanViewLogsData
4316
+ });
4317
+ }
4318
+ return {
4319
+ appId,
4320
+ salt,
4321
+ executions,
4322
+ walletClient,
4323
+ publicClient,
4324
+ environmentConfig
4325
+ };
4326
+ }
4327
+ async function executeDeployBatch(data, context, gas, logger = noopLogger) {
4328
+ const pendingMessage = "Deploying new app...";
4329
+ const txHash = await executeBatch(
4330
+ {
4331
+ walletClient: context.walletClient,
4332
+ publicClient: context.publicClient,
4333
+ environmentConfig: context.environmentConfig,
4334
+ executions: data.executions,
4335
+ pendingMessage,
4336
+ gas
4337
+ },
4338
+ logger
4339
+ );
4340
+ return { appId: data.appId, txHash };
4341
+ }
4342
+ async function deployApp(options, logger = noopLogger) {
4343
+ const prepared = await prepareDeployBatch(options, logger);
4344
+ const data = {
4345
+ appId: prepared.appId,
4346
+ salt: prepared.salt,
4347
+ executions: prepared.executions
4348
+ };
4349
+ const context = {
4350
+ walletClient: prepared.walletClient,
4351
+ publicClient: prepared.publicClient,
4352
+ environmentConfig: prepared.environmentConfig
4353
+ };
4354
+ return executeDeployBatch(data, context, options.gas, logger);
4355
+ }
4356
+ function supportsEIP7702(walletClient) {
4357
+ const account = walletClient.account;
4358
+ if (!account) return false;
4359
+ return account.type === "local";
4360
+ }
4361
+ async function executeDeploySequential(options, logger = noopLogger) {
4362
+ const { walletClient, publicClient, environmentConfig, data, publicLogs, onProgress } = options;
4363
+ const account = walletClient.account;
4364
+ if (!account) {
4365
+ throw new Error("WalletClient must have an account attached");
4366
+ }
4367
+ const chain = getChainFromID(environmentConfig.chainID);
4368
+ const txHashes = {
4369
+ createApp: "0x",
4370
+ acceptAdmin: "0x"
4371
+ };
4372
+ logger.info("Step 1/3: Creating app...");
4373
+ onProgress?.("createApp");
4374
+ const createAppExecution = data.executions[0];
4375
+ const createAppHash = await walletClient.sendTransaction({
4376
+ account,
4377
+ to: createAppExecution.target,
4378
+ data: createAppExecution.callData,
4379
+ value: createAppExecution.value,
4380
+ chain
4381
+ });
4382
+ logger.info(`createApp transaction sent: ${createAppHash}`);
4383
+ const createAppReceipt = await publicClient.waitForTransactionReceipt({ hash: createAppHash });
4384
+ if (createAppReceipt.status === "reverted") {
4385
+ throw new Error(`createApp transaction reverted: ${createAppHash}`);
4386
+ }
4387
+ txHashes.createApp = createAppHash;
4388
+ logger.info(`createApp confirmed in block ${createAppReceipt.blockNumber}`);
4389
+ logger.info("Step 2/3: Accepting admin role...");
4390
+ onProgress?.("acceptAdmin", createAppHash);
4391
+ const acceptAdminExecution = data.executions[1];
4392
+ const acceptAdminHash = await walletClient.sendTransaction({
4393
+ account,
4394
+ to: acceptAdminExecution.target,
4395
+ data: acceptAdminExecution.callData,
4396
+ value: acceptAdminExecution.value,
4397
+ chain
4398
+ });
4399
+ logger.info(`acceptAdmin transaction sent: ${acceptAdminHash}`);
4400
+ const acceptAdminReceipt = await publicClient.waitForTransactionReceipt({
4401
+ hash: acceptAdminHash
4402
+ });
4403
+ if (acceptAdminReceipt.status === "reverted") {
4404
+ throw new Error(`acceptAdmin transaction reverted: ${acceptAdminHash}`);
4405
+ }
4406
+ txHashes.acceptAdmin = acceptAdminHash;
4407
+ logger.info(`acceptAdmin confirmed in block ${acceptAdminReceipt.blockNumber}`);
4408
+ if (publicLogs && data.executions.length > 2) {
4409
+ logger.info("Step 3/3: Setting public logs permission...");
4410
+ onProgress?.("setPublicLogs", acceptAdminHash);
4411
+ const setAppointeeExecution = data.executions[2];
4412
+ const setAppointeeHash = await walletClient.sendTransaction({
4413
+ account,
4414
+ to: setAppointeeExecution.target,
4415
+ data: setAppointeeExecution.callData,
4416
+ value: setAppointeeExecution.value,
4417
+ chain
4418
+ });
4419
+ logger.info(`setAppointee transaction sent: ${setAppointeeHash}`);
4420
+ const setAppointeeReceipt = await publicClient.waitForTransactionReceipt({
4421
+ hash: setAppointeeHash
4422
+ });
4423
+ if (setAppointeeReceipt.status === "reverted") {
4424
+ throw new Error(`setAppointee transaction reverted: ${setAppointeeHash}`);
4425
+ }
4426
+ txHashes.setPublicLogs = setAppointeeHash;
4427
+ logger.info(`setAppointee confirmed in block ${setAppointeeReceipt.blockNumber}`);
4428
+ }
4429
+ onProgress?.("complete", txHashes.setPublicLogs || txHashes.acceptAdmin);
4430
+ logger.info(`Deployment complete! App ID: ${data.appId}`);
4431
+ return {
4432
+ appId: data.appId,
4433
+ txHashes
4434
+ };
4435
+ }
4436
+ async function supportsEIP5792(walletClient) {
4437
+ try {
4438
+ if (typeof walletClient.getCapabilities !== "function") {
4439
+ return false;
4440
+ }
4441
+ const account = walletClient.account;
4442
+ if (!account) return false;
4443
+ const capabilities = await walletClient.getCapabilities({
4444
+ account: account.address
4445
+ });
4446
+ return capabilities !== null && capabilities !== void 0 && Object.keys(capabilities).length > 0;
4447
+ } catch {
4448
+ return false;
4449
+ }
4450
+ }
4451
+ async function executeDeployBatched(options, logger = noopLogger) {
4452
+ const { walletClient, environmentConfig, data, publicLogs, onProgress } = options;
4453
+ const account = walletClient.account;
4454
+ if (!account) {
4455
+ throw new Error("WalletClient must have an account attached");
4456
+ }
4457
+ const chain = getChainFromID(environmentConfig.chainID);
4458
+ const calls = data.executions.map(
4459
+ (execution) => ({
4460
+ to: execution.target,
4461
+ data: execution.callData,
4462
+ value: execution.value
4463
+ })
4464
+ );
4465
+ const filteredCalls = publicLogs ? calls : calls.slice(0, 2);
4466
+ logger.info(`Deploying with EIP-5792 sendCalls (${filteredCalls.length} calls)...`);
4467
+ onProgress?.("createApp");
4468
+ try {
4469
+ const { id: batchId } = await walletClient.sendCalls({
4470
+ account,
4471
+ chain,
4472
+ calls: filteredCalls,
4473
+ forceAtomic: true
4474
+ });
4475
+ logger.info(`Batch submitted with ID: ${batchId}`);
4476
+ onProgress?.("acceptAdmin");
4477
+ let status;
4478
+ let attempts = 0;
4479
+ const maxAttempts = 120;
4480
+ while (attempts < maxAttempts) {
4481
+ try {
4482
+ status = await walletClient.getCallsStatus({ id: batchId });
4483
+ if (status.status === "success" || status.status === "confirmed") {
4484
+ logger.info(`Batch confirmed with ${status.receipts?.length || 0} receipts`);
4485
+ break;
4486
+ }
4487
+ if (status.status === "failed" || status.status === "reverted") {
4488
+ throw new Error(`Batch transaction failed: ${status.status}`);
4489
+ }
4490
+ } catch (statusError) {
4491
+ if (statusError.message?.includes("not supported")) {
4492
+ logger.warn("getCallsStatus not supported, waiting for chain confirmation...");
4493
+ await new Promise((resolve) => setTimeout(resolve, 15e3));
4494
+ break;
4495
+ }
4496
+ throw statusError;
4497
+ }
4498
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
4499
+ attempts++;
4500
+ }
4501
+ if (attempts >= maxAttempts) {
4502
+ throw new Error("Timeout waiting for batch confirmation");
4503
+ }
4504
+ if (publicLogs) {
4505
+ onProgress?.("setPublicLogs");
4506
+ }
4507
+ onProgress?.("complete");
4508
+ const receipts = (status?.receipts || []).map((r) => ({
4509
+ transactionHash: r.transactionHash || r.hash
4510
+ }));
4511
+ logger.info(`Deployment complete! App ID: ${data.appId}`);
4512
+ return {
4513
+ appId: data.appId,
4514
+ batchId,
4515
+ receipts
4516
+ };
4517
+ } catch (error) {
4518
+ if (error.message?.includes("not supported") || error.message?.includes("wallet_sendCalls") || error.code === -32601) {
4519
+ throw new Error("EIP5792_NOT_SUPPORTED");
4520
+ }
4521
+ throw error;
4522
+ }
4523
+ }
4524
+ async function prepareUpgradeBatch(options) {
4525
+ const {
4526
+ walletClient,
4527
+ publicClient,
4528
+ environmentConfig,
4529
+ appID,
4530
+ release,
4531
+ publicLogs,
4532
+ needsPermissionChange
4533
+ } = options;
4534
+ const releaseForViem = {
4535
+ rmsRelease: {
4536
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4537
+ digest: `0x${bytesToHex(artifact.digest).slice(2).padStart(64, "0")}`,
4538
+ registry: artifact.registry
4539
+ })),
4540
+ upgradeByTime: release.rmsRelease.upgradeByTime
4541
+ },
4542
+ publicEnv: bytesToHex(release.publicEnv),
4543
+ encryptedEnv: bytesToHex(release.encryptedEnv)
4544
+ };
4545
+ const upgradeData = encodeFunctionData2({
4546
+ abi: AppController_default,
4547
+ functionName: "upgradeApp",
4548
+ args: [appID, releaseForViem]
4549
+ });
4550
+ const executions = [
4551
+ {
4552
+ target: environmentConfig.appControllerAddress,
4553
+ value: 0n,
4554
+ callData: upgradeData
4555
+ }
4556
+ ];
4557
+ if (needsPermissionChange) {
4558
+ if (publicLogs) {
4559
+ const addLogsData = encodeFunctionData2({
4560
+ abi: PermissionController_default,
4561
+ functionName: "setAppointee",
4562
+ args: [
4563
+ appID,
4564
+ "0x493219d9949348178af1f58740655951a8cd110c",
4565
+ // AnyoneCanCallAddress
4566
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4567
+ // ApiPermissionsTarget
4568
+ "0x2fd3f2fe"
4569
+ // CanViewAppLogsPermission
4570
+ ]
4571
+ });
4572
+ executions.push({
4573
+ target: environmentConfig.permissionControllerAddress,
4574
+ value: 0n,
4575
+ callData: addLogsData
4576
+ });
4577
+ } else {
4578
+ const removeLogsData = encodeFunctionData2({
4579
+ abi: PermissionController_default,
4580
+ functionName: "removeAppointee",
4581
+ args: [
4582
+ appID,
4583
+ "0x493219d9949348178af1f58740655951a8cd110c",
4584
+ // AnyoneCanCallAddress
4585
+ "0x57ee1fb74c1087e26446abc4fb87fd8f07c43d8d",
4586
+ // ApiPermissionsTarget
4587
+ "0x2fd3f2fe"
4588
+ // CanViewAppLogsPermission
4589
+ ]
4590
+ });
4591
+ executions.push({
4592
+ target: environmentConfig.permissionControllerAddress,
4593
+ value: 0n,
4594
+ callData: removeLogsData
4595
+ });
4596
+ }
4597
+ }
4598
+ return {
4599
+ appId: appID,
4600
+ executions,
4601
+ walletClient,
4602
+ publicClient,
4603
+ environmentConfig
4604
+ };
4605
+ }
4606
+ async function executeUpgradeBatch(data, context, gas, logger = noopLogger) {
4607
+ const pendingMessage = `Upgrading app ${data.appId}...`;
4608
+ const txHash = await executeBatch(
4609
+ {
4610
+ walletClient: context.walletClient,
4611
+ publicClient: context.publicClient,
4612
+ environmentConfig: context.environmentConfig,
4613
+ executions: data.executions,
4614
+ pendingMessage,
4615
+ gas
4616
+ },
4617
+ logger
4618
+ );
4619
+ return txHash;
4620
+ }
4621
+ async function upgradeApp(options, logger = noopLogger) {
4622
+ const prepared = await prepareUpgradeBatch(options);
4623
+ const data = {
4624
+ appId: prepared.appId,
4625
+ executions: prepared.executions
4626
+ };
4627
+ const context = {
4628
+ walletClient: prepared.walletClient,
4629
+ publicClient: prepared.publicClient,
4630
+ environmentConfig: prepared.environmentConfig
4631
+ };
4632
+ return executeUpgradeBatch(data, context, options.gas, logger);
4633
+ }
4634
+ async function sendAndWaitForTransaction(options, logger = noopLogger) {
4635
+ const {
4636
+ walletClient,
4637
+ publicClient,
4638
+ environmentConfig,
4639
+ to,
4640
+ data,
4641
+ value = 0n,
4642
+ pendingMessage,
4643
+ txDescription,
4644
+ gas
4645
+ } = options;
4646
+ const account = walletClient.account;
4647
+ if (!account) {
4648
+ throw new Error("WalletClient must have an account attached");
4649
+ }
4650
+ const chain = getChainFromID(environmentConfig.chainID);
4651
+ if (pendingMessage) {
4652
+ logger.info(`
4653
+ ${pendingMessage}`);
4654
+ }
4655
+ const hash = await walletClient.sendTransaction({
4656
+ account,
4657
+ to,
4658
+ data,
4659
+ value,
4660
+ ...gas?.maxFeePerGas && { maxFeePerGas: gas.maxFeePerGas },
4661
+ ...gas?.maxPriorityFeePerGas && {
4662
+ maxPriorityFeePerGas: gas.maxPriorityFeePerGas
4663
+ },
4664
+ chain
4665
+ });
4666
+ logger.info(`Transaction sent: ${hash}`);
4667
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
4668
+ if (receipt.status === "reverted") {
4669
+ let revertReason = "Unknown reason";
4670
+ try {
4671
+ await publicClient.call({
4672
+ to,
4673
+ data,
4674
+ account: account.address
4675
+ });
4676
+ } catch (callError) {
4677
+ if (callError.data) {
4678
+ try {
4679
+ const decoded = decodeErrorResult2({
4680
+ abi: AppController_default,
4681
+ data: callError.data
4682
+ });
4683
+ const formattedError = formatAppControllerError(decoded);
4684
+ revertReason = formattedError.message;
4685
+ } catch {
4686
+ revertReason = callError.message || "Unknown reason";
4687
+ }
4688
+ } else {
4689
+ revertReason = callError.message || "Unknown reason";
4690
+ }
4691
+ }
4692
+ logger.error(`${txDescription} transaction (hash: ${hash}) reverted: ${revertReason}`);
4693
+ throw new Error(`${txDescription} transaction (hash: ${hash}) reverted: ${revertReason}`);
4694
+ }
4695
+ return hash;
4696
+ }
4697
+ function formatAppControllerError(decoded) {
4698
+ const errorName = decoded.errorName;
4699
+ switch (errorName) {
4700
+ case "MaxActiveAppsExceeded":
4701
+ return new Error(
4702
+ "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"
4703
+ );
4704
+ case "GlobalMaxActiveAppsExceeded":
4705
+ return new Error(
4706
+ "the platform has reached the maximum number of active apps. please try again later"
4707
+ );
4708
+ case "InvalidPermissions":
4709
+ return new Error("you don't have permission to perform this operation");
4710
+ case "AppAlreadyExists":
4711
+ return new Error("an app with this owner and salt already exists");
4712
+ case "AppDoesNotExist":
4713
+ return new Error("the specified app does not exist");
4714
+ case "InvalidAppStatus":
4715
+ return new Error("the app is in an invalid state for this operation");
4716
+ case "MoreThanOneArtifact":
4717
+ return new Error("only one artifact is allowed per release");
4718
+ case "InvalidSignature":
4719
+ return new Error("invalid signature provided");
4720
+ case "SignatureExpired":
4721
+ return new Error("the provided signature has expired");
4722
+ case "InvalidReleaseMetadataURI":
4723
+ return new Error("invalid release metadata URI provided");
4724
+ case "InvalidShortString":
4725
+ return new Error("invalid short string format");
4726
+ default:
4727
+ return new Error(`contract error: ${errorName}`);
4728
+ }
4729
+ }
4730
+ async function getActiveAppCount(publicClient, environmentConfig, user) {
4731
+ const count = await publicClient.readContract({
4732
+ address: environmentConfig.appControllerAddress,
4733
+ abi: AppController_default,
4734
+ functionName: "getActiveAppCount",
4735
+ args: [user]
4736
+ });
4737
+ return Number(count);
4738
+ }
4739
+ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
4740
+ const quota = await publicClient.readContract({
4741
+ address: environmentConfig.appControllerAddress,
4742
+ abi: AppController_default,
4743
+ functionName: "getMaxActiveAppsPerUser",
4744
+ args: [user]
4745
+ });
4746
+ return Number(quota);
4747
+ }
2814
4748
  async function getAppsByCreator(publicClient, environmentConfig, creator, offset, limit) {
2815
4749
  const result = await publicClient.readContract({
2816
4750
  address: environmentConfig.appControllerAddress,
@@ -2840,7 +4774,13 @@ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 10
2840
4774
  const allApps = [];
2841
4775
  const allConfigs = [];
2842
4776
  while (true) {
2843
- const { apps, appConfigs } = await getAppsByDeveloper(publicClient, env, developer, offset, pageSize);
4777
+ const { apps, appConfigs } = await getAppsByDeveloper(
4778
+ publicClient,
4779
+ env,
4780
+ developer,
4781
+ offset,
4782
+ pageSize
4783
+ );
2844
4784
  if (apps.length === 0) break;
2845
4785
  allApps.push(...apps);
2846
4786
  allConfigs.push(...appConfigs);
@@ -2852,6 +4792,74 @@ async function getAllAppsByDeveloper(publicClient, env, developer, pageSize = 10
2852
4792
  appConfigs: allConfigs
2853
4793
  };
2854
4794
  }
4795
+ async function suspend(options, logger = noopLogger) {
4796
+ const { walletClient, publicClient, environmentConfig, account, apps } = options;
4797
+ const suspendData = encodeFunctionData2({
4798
+ abi: AppController_default,
4799
+ functionName: "suspend",
4800
+ args: [account, apps]
4801
+ });
4802
+ const pendingMessage = `Suspending ${apps.length} app(s)...`;
4803
+ return sendAndWaitForTransaction(
4804
+ {
4805
+ walletClient,
4806
+ publicClient,
4807
+ environmentConfig,
4808
+ to: environmentConfig.appControllerAddress,
4809
+ data: suspendData,
4810
+ pendingMessage,
4811
+ txDescription: "Suspend"
4812
+ },
4813
+ logger
4814
+ );
4815
+ }
4816
+ async function isDelegated(options) {
4817
+ const { publicClient, environmentConfig, address } = options;
4818
+ return checkERC7702Delegation(
4819
+ publicClient,
4820
+ address,
4821
+ environmentConfig.erc7702DelegatorAddress
4822
+ );
4823
+ }
4824
+ async function undelegate(options, logger = noopLogger) {
4825
+ const { walletClient, publicClient, environmentConfig } = options;
4826
+ const account = walletClient.account;
4827
+ if (!account) {
4828
+ throw new Error("WalletClient must have an account attached");
4829
+ }
4830
+ const chain = getChainFromID(environmentConfig.chainID);
4831
+ const transactionNonce = await publicClient.getTransactionCount({
4832
+ address: account.address,
4833
+ blockTag: "pending"
4834
+ });
4835
+ const chainId = await publicClient.getChainId();
4836
+ const authorizationNonce = BigInt(transactionNonce) + 1n;
4837
+ logger.debug("Signing undelegate authorization");
4838
+ const signedAuthorization = await walletClient.signAuthorization({
4839
+ contractAddress: "0x0000000000000000000000000000000000000000",
4840
+ chainId,
4841
+ nonce: Number(authorizationNonce),
4842
+ account
4843
+ });
4844
+ const authorizationList = [signedAuthorization];
4845
+ const hash = await walletClient.sendTransaction({
4846
+ account,
4847
+ to: account.address,
4848
+ // Send to self
4849
+ data: "0x",
4850
+ // Empty data
4851
+ value: 0n,
4852
+ authorizationList,
4853
+ chain
4854
+ });
4855
+ logger.info(`Transaction sent: ${hash}`);
4856
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
4857
+ if (receipt.status === "reverted") {
4858
+ logger.error(`Undelegate transaction (hash: ${hash}) reverted`);
4859
+ throw new Error(`Undelegate transaction (hash: ${hash}) reverted`);
4860
+ }
4861
+ return hash;
4862
+ }
2855
4863
 
2856
4864
  // src/client/common/contract/encoders.ts
2857
4865
  import { parseAbi as parseAbi2, encodeFunctionData as encodeFunctionData3 } from "viem";
@@ -2881,32 +4889,406 @@ function encodeTerminateAppData(appId) {
2881
4889
  args: [appId]
2882
4890
  });
2883
4891
  }
4892
+
4893
+ // src/client/common/auth/siwe.ts
4894
+ import { SiweMessage, generateNonce as siweGenerateNonce } from "siwe";
4895
+ var generateNonce = siweGenerateNonce;
4896
+ function createSiweMessage(params) {
4897
+ const now = /* @__PURE__ */ new Date();
4898
+ const nonce = params.nonce || generateNonce();
4899
+ const issuedAt = params.issuedAt || now;
4900
+ const expirationTime = params.expirationTime || new Date(now.getTime() + 24 * 60 * 60 * 1e3);
4901
+ const siweMessage = new SiweMessage({
4902
+ domain: params.domain,
4903
+ address: params.address,
4904
+ statement: params.statement,
4905
+ uri: params.uri,
4906
+ version: "1",
4907
+ chainId: params.chainId,
4908
+ nonce,
4909
+ issuedAt: issuedAt.toISOString(),
4910
+ expirationTime: expirationTime.toISOString(),
4911
+ notBefore: params.notBefore?.toISOString(),
4912
+ requestId: params.requestId,
4913
+ resources: params.resources
4914
+ });
4915
+ return {
4916
+ message: siweMessage.prepareMessage(),
4917
+ params: {
4918
+ address: params.address,
4919
+ chainId: params.chainId,
4920
+ domain: params.domain,
4921
+ uri: params.uri,
4922
+ nonce,
4923
+ issuedAt,
4924
+ statement: params.statement,
4925
+ expirationTime,
4926
+ notBefore: params.notBefore,
4927
+ requestId: params.requestId,
4928
+ resources: params.resources
4929
+ }
4930
+ };
4931
+ }
4932
+ function parseSiweMessage(message) {
4933
+ try {
4934
+ const siweMessage = new SiweMessage(message);
4935
+ return {
4936
+ address: siweMessage.address,
4937
+ chainId: siweMessage.chainId,
4938
+ domain: siweMessage.domain,
4939
+ uri: siweMessage.uri,
4940
+ nonce: siweMessage.nonce,
4941
+ statement: siweMessage.statement,
4942
+ issuedAt: siweMessage.issuedAt ? new Date(siweMessage.issuedAt) : void 0,
4943
+ expirationTime: siweMessage.expirationTime ? new Date(siweMessage.expirationTime) : void 0,
4944
+ notBefore: siweMessage.notBefore ? new Date(siweMessage.notBefore) : void 0,
4945
+ requestId: siweMessage.requestId,
4946
+ resources: siweMessage.resources
4947
+ };
4948
+ } catch {
4949
+ return null;
4950
+ }
4951
+ }
4952
+ function isSiweMessageExpired(params) {
4953
+ if (!params.expirationTime) return false;
4954
+ return /* @__PURE__ */ new Date() > params.expirationTime;
4955
+ }
4956
+ function isSiweMessageNotYetValid(params) {
4957
+ if (!params.notBefore) return false;
4958
+ return /* @__PURE__ */ new Date() < params.notBefore;
4959
+ }
4960
+
4961
+ // src/browser.ts
4962
+ init_session();
4963
+
4964
+ // src/client/common/hooks/useComputeSession.ts
4965
+ init_session();
4966
+ import { useCallback, useEffect, useRef, useState } from "react";
4967
+ function useComputeSession(config) {
4968
+ const {
4969
+ baseUrl,
4970
+ refreshInterval = 6e4,
4971
+ // 1 minute default
4972
+ checkOnMount = true,
4973
+ onSessionExpired,
4974
+ onSessionRefreshed,
4975
+ onError
4976
+ } = config;
4977
+ const [session, setSession] = useState(null);
4978
+ const [isLoading, setIsLoading] = useState(checkOnMount);
4979
+ const [error, setError] = useState(null);
4980
+ const wasAuthenticatedRef = useRef(false);
4981
+ const isMountedRef = useRef(true);
4982
+ const refreshIntervalRef = useRef(null);
4983
+ const apiConfig = { baseUrl };
4984
+ const checkSession = useCallback(async () => {
4985
+ try {
4986
+ const sessionInfo = await getComputeApiSession(apiConfig);
4987
+ if (!isMountedRef.current) {
4988
+ return sessionInfo;
4989
+ }
4990
+ setSession(sessionInfo);
4991
+ setError(null);
4992
+ if (wasAuthenticatedRef.current && !sessionInfo.authenticated) {
4993
+ onSessionExpired?.();
4994
+ }
4995
+ wasAuthenticatedRef.current = sessionInfo.authenticated;
4996
+ if (sessionInfo.authenticated) {
4997
+ onSessionRefreshed?.(sessionInfo);
4998
+ }
4999
+ return sessionInfo;
5000
+ } catch (err) {
5001
+ if (!isMountedRef.current) {
5002
+ throw err;
5003
+ }
5004
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Failed to check session: ${String(err)}`, "UNKNOWN");
5005
+ setError(sessionError);
5006
+ onError?.(sessionError);
5007
+ const fallbackSession = { authenticated: false };
5008
+ setSession(fallbackSession);
5009
+ return fallbackSession;
5010
+ }
5011
+ }, [baseUrl, onSessionExpired, onSessionRefreshed, onError]);
5012
+ const refresh = useCallback(async () => {
5013
+ setIsLoading(true);
5014
+ try {
5015
+ return await checkSession();
5016
+ } finally {
5017
+ if (isMountedRef.current) {
5018
+ setIsLoading(false);
5019
+ }
5020
+ }
5021
+ }, [checkSession]);
5022
+ const login = useCallback(
5023
+ async (params, signMessage) => {
5024
+ setIsLoading(true);
5025
+ setError(null);
5026
+ try {
5027
+ let domain = params.domain;
5028
+ let uri = params.uri;
5029
+ if (typeof window !== "undefined") {
5030
+ domain = domain || window.location.host;
5031
+ uri = uri || window.location.origin;
5032
+ }
5033
+ if (!domain || !uri) {
5034
+ throw new SessionError(
5035
+ "domain and uri are required when not in browser environment",
5036
+ "INVALID_MESSAGE"
5037
+ );
5038
+ }
5039
+ const siweMessage = createSiweMessage({
5040
+ ...params,
5041
+ domain,
5042
+ uri,
5043
+ statement: params.statement || "Sign in to EigenCloud Compute API"
5044
+ });
5045
+ const signature = await signMessage({ message: siweMessage.message });
5046
+ await loginToComputeApi(apiConfig, {
5047
+ message: siweMessage.message,
5048
+ signature
5049
+ });
5050
+ const sessionInfo = await checkSession();
5051
+ if (!isMountedRef.current) {
5052
+ return sessionInfo;
5053
+ }
5054
+ wasAuthenticatedRef.current = sessionInfo.authenticated;
5055
+ return sessionInfo;
5056
+ } catch (err) {
5057
+ if (!isMountedRef.current) {
5058
+ throw err;
5059
+ }
5060
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Login failed: ${String(err)}`, "UNKNOWN");
5061
+ setError(sessionError);
5062
+ onError?.(sessionError);
5063
+ throw sessionError;
5064
+ } finally {
5065
+ if (isMountedRef.current) {
5066
+ setIsLoading(false);
5067
+ }
5068
+ }
5069
+ },
5070
+ [baseUrl, checkSession, onError]
5071
+ );
5072
+ const logout = useCallback(async () => {
5073
+ setIsLoading(true);
5074
+ setError(null);
5075
+ try {
5076
+ await logoutFromComputeApi(apiConfig);
5077
+ if (!isMountedRef.current) {
5078
+ return;
5079
+ }
5080
+ const newSession = { authenticated: false };
5081
+ setSession(newSession);
5082
+ wasAuthenticatedRef.current = false;
5083
+ } catch (err) {
5084
+ if (!isMountedRef.current) {
5085
+ throw err;
5086
+ }
5087
+ const sessionError = err instanceof SessionError ? err : new SessionError(`Logout failed: ${String(err)}`, "UNKNOWN");
5088
+ setError(sessionError);
5089
+ onError?.(sessionError);
5090
+ setSession({ authenticated: false });
5091
+ wasAuthenticatedRef.current = false;
5092
+ } finally {
5093
+ if (isMountedRef.current) {
5094
+ setIsLoading(false);
5095
+ }
5096
+ }
5097
+ }, [baseUrl, onError]);
5098
+ const clearError = useCallback(() => {
5099
+ setError(null);
5100
+ }, []);
5101
+ useEffect(() => {
5102
+ isMountedRef.current = true;
5103
+ if (checkOnMount) {
5104
+ checkSession().finally(() => {
5105
+ if (isMountedRef.current) {
5106
+ setIsLoading(false);
5107
+ }
5108
+ });
5109
+ }
5110
+ return () => {
5111
+ isMountedRef.current = false;
5112
+ };
5113
+ }, [checkOnMount, checkSession]);
5114
+ useEffect(() => {
5115
+ if (refreshInterval <= 0) {
5116
+ return;
5117
+ }
5118
+ if (refreshIntervalRef.current) {
5119
+ clearInterval(refreshIntervalRef.current);
5120
+ }
5121
+ refreshIntervalRef.current = setInterval(() => {
5122
+ if (wasAuthenticatedRef.current) {
5123
+ checkSession();
5124
+ }
5125
+ }, refreshInterval);
5126
+ return () => {
5127
+ if (refreshIntervalRef.current) {
5128
+ clearInterval(refreshIntervalRef.current);
5129
+ refreshIntervalRef.current = null;
5130
+ }
5131
+ };
5132
+ }, [refreshInterval, checkSession]);
5133
+ return {
5134
+ session,
5135
+ isLoading,
5136
+ error,
5137
+ isAuthenticated: session?.authenticated ?? false,
5138
+ login,
5139
+ logout,
5140
+ refresh,
5141
+ clearError
5142
+ };
5143
+ }
5144
+
5145
+ // src/client/common/encryption/kms.ts
5146
+ import { importSPKI, CompactEncrypt } from "jose";
5147
+ function getAppProtectedHeaders(appID) {
5148
+ return {
5149
+ "x-eigenx-app-id": appID
5150
+ };
5151
+ }
5152
+ async function encryptRSAOAEPAndAES256GCM(encryptionKeyPEM, plaintext, protectedHeaders) {
5153
+ const pemString = typeof encryptionKeyPEM === "string" ? encryptionKeyPEM : encryptionKeyPEM.toString("utf-8");
5154
+ const publicKey = await importSPKI(pemString, "RSA-OAEP-256", {
5155
+ extractable: true
5156
+ });
5157
+ const header = {
5158
+ alg: "RSA-OAEP-256",
5159
+ // Key encryption algorithm (SHA-256)
5160
+ enc: "A256GCM",
5161
+ // Content encryption algorithm
5162
+ ...protectedHeaders || {}
5163
+ // Add custom protected headers
5164
+ };
5165
+ const plaintextBytes = new Uint8Array(plaintext);
5166
+ const jwe = await new CompactEncrypt(plaintextBytes).setProtectedHeader(header).encrypt(publicKey);
5167
+ return jwe;
5168
+ }
5169
+
5170
+ // keys/mainnet-alpha/prod/kms-encryption-public-key.pem
5171
+ 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-----";
5172
+
5173
+ // keys/mainnet-alpha/prod/kms-signing-public-key.pem
5174
+ var kms_signing_public_key_default = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfxbhXJjH4D0DH/iW5/rK1HzWS+f9\nEyooZTrCYjCfezuOEmRuOWNaZLvwXN8SdzrvjWA7gSvOS85hLzp4grANRQ==\n-----END PUBLIC KEY-----";
5175
+
5176
+ // keys/sepolia/dev/kms-encryption-public-key.pem
5177
+ 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-----";
5178
+
5179
+ // keys/sepolia/dev/kms-signing-public-key.pem
5180
+ var kms_signing_public_key_default2 = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEb2Q88/cxdic2xi4jS2V0dtYHjLwq\n4wVFBFmaY8TTXoMXNggKEdU6PuE8EovocVKMpw3SIlaM27z9uxksNVL2xw==\n-----END PUBLIC KEY-----\n";
5181
+
5182
+ // keys/sepolia/prod/kms-encryption-public-key.pem
5183
+ 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-----";
5184
+
5185
+ // keys/sepolia/prod/kms-signing-public-key.pem
5186
+ var kms_signing_public_key_default3 = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsk6ZdmmvBqFfKHs+1cYjIemRGN7h\n1NatIEitFRyx+3q8wmTJ9LknTE1FwWBLcCNTseJDti8Rh+SaVxfGOyJuuA==\n-----END PUBLIC KEY-----";
5187
+
5188
+ // src/client/common/utils/keys.ts
5189
+ var KEYS = {
5190
+ "mainnet-alpha": {
5191
+ prod: {
5192
+ encryption: kms_encryption_public_key_default,
5193
+ signing: kms_signing_public_key_default
5194
+ }
5195
+ },
5196
+ sepolia: {
5197
+ dev: {
5198
+ encryption: kms_encryption_public_key_default2,
5199
+ signing: kms_signing_public_key_default2
5200
+ },
5201
+ prod: {
5202
+ encryption: kms_encryption_public_key_default3,
5203
+ signing: kms_signing_public_key_default3
5204
+ }
5205
+ }
5206
+ };
5207
+ function getKMSKeysForEnvironment(environment, build = "prod") {
5208
+ const envKeys = KEYS[environment];
5209
+ if (!envKeys) {
5210
+ throw new Error(`No keys found for environment: ${environment}`);
5211
+ }
5212
+ const buildKeys = envKeys[build];
5213
+ if (!buildKeys) {
5214
+ throw new Error(`No keys found for environment: ${environment}, build: ${build}`);
5215
+ }
5216
+ return {
5217
+ encryptionKey: Buffer.from(buildKeys.encryption),
5218
+ signingKey: Buffer.from(buildKeys.signing)
5219
+ };
5220
+ }
2884
5221
  export {
5222
+ BillingApiClient,
5223
+ BillingSessionError,
5224
+ BuildApiClient,
5225
+ SessionError,
2885
5226
  UserApiClient,
5227
+ addHexPrefix,
2886
5228
  assertValidImageReference,
2887
5229
  assertValidPrivateKey,
5230
+ calculateAppID,
5231
+ checkERC7702Delegation,
5232
+ createSiweMessage,
5233
+ deployApp,
2888
5234
  encodeStartAppData,
2889
5235
  encodeStopAppData,
2890
5236
  encodeTerminateAppData,
5237
+ encryptRSAOAEPAndAES256GCM,
2891
5238
  estimateBatchGas,
2892
5239
  estimateTransactionGas,
5240
+ executeBatch,
5241
+ executeDeployBatch,
5242
+ executeDeployBatched,
5243
+ executeDeploySequential,
5244
+ executeUpgradeBatch,
2893
5245
  extractAppNameFromImage,
2894
5246
  formatETH,
2895
5247
  generateNewPrivateKey,
5248
+ generateNonce,
2896
5249
  getActiveAppCount,
2897
5250
  getAllAppsByDeveloper,
5251
+ getAppProtectedHeaders,
2898
5252
  getAppsByCreator,
2899
5253
  getAppsByDeveloper,
2900
5254
  getAvailableEnvironments,
5255
+ getBillingApiSession,
5256
+ getBillingEnvironmentConfig,
2901
5257
  getBuildType,
5258
+ getChainFromID,
5259
+ getComputeApiSession,
2902
5260
  getEnvironmentConfig,
5261
+ getKMSKeysForEnvironment,
2903
5262
  getMaxActiveAppsPerUser,
5263
+ isBillingSessionValid,
5264
+ isDelegated,
2904
5265
  isEnvironmentAvailable,
2905
5266
  isMainnet,
5267
+ isSessionValid,
5268
+ isSiweMessageExpired,
5269
+ isSiweMessageNotYetValid,
2906
5270
  isSubscriptionActive,
5271
+ loginToBillingApi,
5272
+ loginToBothApis,
5273
+ loginToComputeApi,
5274
+ logoutFromBillingApi,
5275
+ logoutFromBothApis,
5276
+ logoutFromComputeApi,
5277
+ noopLogger,
5278
+ parseSiweMessage,
5279
+ prepareDeployBatch,
5280
+ prepareUpgradeBatch,
2907
5281
  sanitizeString,
2908
5282
  sanitizeURL,
2909
5283
  sanitizeXURL,
5284
+ sendAndWaitForTransaction,
5285
+ stripHexPrefix,
5286
+ supportsEIP5792,
5287
+ supportsEIP7702,
5288
+ suspend,
5289
+ undelegate,
5290
+ upgradeApp,
5291
+ useComputeSession,
2910
5292
  validateAppID,
2911
5293
  validateAppName,
2912
5294
  validateCreateAppParams,