@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/VERSION +2 -2
- package/dist/billing.cjs +286 -9
- package/dist/billing.cjs.map +1 -1
- package/dist/billing.d.cts +1 -1
- package/dist/billing.d.ts +1 -1
- package/dist/billing.js +288 -9
- package/dist/billing.js.map +1 -1
- package/dist/browser.cjs +2551 -127
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +243 -5
- package/dist/browser.d.ts +243 -5
- package/dist/browser.js +2514 -132
- package/dist/browser.js.map +1 -1
- package/dist/{compute-B85ikS78.d.ts → compute-CdZxISln.d.ts} +1 -1
- package/dist/{compute-CC0R7HEu.d.cts → compute-Dstl0CA0.d.cts} +1 -1
- package/dist/compute.cjs +247 -22
- package/dist/compute.cjs.map +1 -1
- package/dist/compute.d.cts +2 -2
- package/dist/compute.d.ts +2 -2
- package/dist/compute.js +250 -27
- package/dist/compute.js.map +1 -1
- package/dist/helpers-4w0Iojmm.d.ts +983 -0
- package/dist/helpers-Dk0zwgms.d.cts +983 -0
- package/dist/{index-D5oW73Dx.d.cts → index-C0w92tCs.d.cts} +313 -2
- package/dist/{index-D5oW73Dx.d.ts → index-C0w92tCs.d.ts} +313 -2
- package/dist/index.cjs +751 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -88
- package/dist/index.d.ts +8 -88
- package/dist/index.js +743 -65
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
- package/dist/eip7702-CXCYfOnk.d.ts +0 -400
- package/dist/eip7702-DeqoCP5b.d.cts +0 -400
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.
|
|
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,
|
|
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.
|
|
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
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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/
|
|
630
|
-
import
|
|
906
|
+
// src/client/common/utils/billingapi.ts
|
|
907
|
+
import axios2 from "axios";
|
|
631
908
|
|
|
632
|
-
// src/client/common/
|
|
633
|
-
var
|
|
634
|
-
{
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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/
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
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(
|
|
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,
|