@oleary-labs/signet-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.d.ts +38 -0
- package/dist/admin.d.ts.map +1 -0
- package/dist/admin.js +112 -0
- package/dist/admin.js.map +1 -0
- package/dist/authkey-session.d.ts +64 -0
- package/dist/authkey-session.d.ts.map +1 -0
- package/dist/authkey-session.js +164 -0
- package/dist/authkey-session.js.map +1 -0
- package/dist/bootstrap.d.ts +30 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +60 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bundler.d.ts +85 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +160 -0
- package/dist/bundler.js.map +1 -0
- package/dist/delegate.d.ts +57 -0
- package/dist/delegate.d.ts.map +1 -0
- package/dist/delegate.js +111 -0
- package/dist/delegate.js.map +1 -0
- package/dist/frostVerify.d.ts +23 -0
- package/dist/frostVerify.d.ts.map +1 -0
- package/dist/frostVerify.js +69 -0
- package/dist/frostVerify.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/jwks.d.ts +28 -0
- package/dist/jwks.d.ts.map +1 -0
- package/dist/jwks.js +81 -0
- package/dist/jwks.js.map +1 -0
- package/dist/jwt.d.ts +27 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +50 -0
- package/dist/jwt.js.map +1 -0
- package/dist/keygen.d.ts +26 -0
- package/dist/keygen.d.ts.map +1 -0
- package/dist/keygen.js +60 -0
- package/dist/keygen.js.map +1 -0
- package/dist/oauth.d.ts +34 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +119 -0
- package/dist/oauth.js.map +1 -0
- package/dist/request.d.ts +42 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +115 -0
- package/dist/request.js.map +1 -0
- package/dist/scopedSign.d.ts +82 -0
- package/dist/scopedSign.d.ts.map +1 -0
- package/dist/scopedSign.js +130 -0
- package/dist/scopedSign.js.map +1 -0
- package/dist/server-prover.d.ts +29 -0
- package/dist/server-prover.d.ts.map +1 -0
- package/dist/server-prover.js +54 -0
- package/dist/server-prover.js.map +1 -0
- package/dist/session.d.ts +14 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +29 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/userop.d.ts +104 -0
- package/dist/userop.d.ts.map +1 -0
- package/dist/userop.js +212 -0
- package/dist/userop.js.map +1 -0
- package/dist/x402.d.ts +127 -0
- package/dist/x402.d.ts.map +1 -0
- package/dist/x402.js +167 -0
- package/dist/x402.js.map +1 -0
- package/package.json +64 -0
- package/src/admin.ts +178 -0
- package/src/authkey-session.ts +241 -0
- package/src/bootstrap.ts +106 -0
- package/src/bundler.ts +256 -0
- package/src/delegate.ts +163 -0
- package/src/frostVerify.ts +79 -0
- package/src/generate-inputs.ts +158 -0
- package/src/index.ts +43 -0
- package/src/jwks.ts +92 -0
- package/src/jwt.ts +74 -0
- package/src/keygen.ts +89 -0
- package/src/oauth.ts +157 -0
- package/src/partial-sha.ts +99 -0
- package/src/proof.ts +99 -0
- package/src/request.ts +174 -0
- package/src/scopedSign.ts +184 -0
- package/src/server-prover.ts +76 -0
- package/src/session.ts +33 -0
- package/src/types.ts +63 -0
- package/src/userop.ts +368 -0
- package/src/witness.ts +132 -0
- package/src/x402.ts +275 -0
package/dist/admin.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin API authentication for Signet group management.
|
|
3
|
+
*
|
|
4
|
+
* Admin auth is stateless: sign SHA256(group_id : nonce : timestamp_BE)
|
|
5
|
+
* with a trusted auth key. For Schnorr auth keys (prefix 0x01), the
|
|
6
|
+
* signature is produced via FROST threshold signing through the bootstrap
|
|
7
|
+
* group.
|
|
8
|
+
*/
|
|
9
|
+
import type { SessionKeypair, IdTokenClaims } from "./types";
|
|
10
|
+
export interface AdminAuthConfig {
|
|
11
|
+
nodeProxyUrl: string;
|
|
12
|
+
bootstrapGroup: string;
|
|
13
|
+
bootstrapNodes: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface AdminAuth {
|
|
16
|
+
group_id: string;
|
|
17
|
+
auth_key_pub: string;
|
|
18
|
+
signature: string;
|
|
19
|
+
nonce: string;
|
|
20
|
+
timestamp: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build an admin auth payload by threshold-signing the admin hash
|
|
24
|
+
* via the bootstrap group.
|
|
25
|
+
*
|
|
26
|
+
* The admin hash is: SHA256(group_id + ":" + nonce + ":" + timestamp_8BE)
|
|
27
|
+
* The signature is a 65-byte FROST Schnorr signature.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildAdminAuth(config: AdminAuthConfig, groupId: string, authKeyPub: string, sessionKeypair: SessionKeypair, claims: IdTokenClaims): Promise<AdminAuth>;
|
|
30
|
+
/**
|
|
31
|
+
* Call an admin endpoint with auth.
|
|
32
|
+
*
|
|
33
|
+
* If the signing request fails with a 401 "session not found" error and
|
|
34
|
+
* a reauthenticate callback is provided, re-establishes the node session
|
|
35
|
+
* and retries once.
|
|
36
|
+
*/
|
|
37
|
+
export declare function adminRequest<T>(config: AdminAuthConfig, nodeUrl: string, path: string, groupId: string, authKeyPub: string, sessionKeypair: SessionKeypair, claims: IdTokenClaims, extraBody?: Record<string, unknown>, reauthenticate?: () => Promise<void>): Promise<T>;
|
|
38
|
+
//# sourceMappingURL=admin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAI7D,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,CAAC,CAwCpB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,aAAa,EACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GACnC,OAAO,CAAC,CAAC,CAAC,CAWZ"}
|
package/dist/admin.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin API authentication for Signet group management.
|
|
3
|
+
*
|
|
4
|
+
* Admin auth is stateless: sign SHA256(group_id : nonce : timestamp_BE)
|
|
5
|
+
* with a trusted auth key. For Schnorr auth keys (prefix 0x01), the
|
|
6
|
+
* signature is produced via FROST threshold signing through the bootstrap
|
|
7
|
+
* group.
|
|
8
|
+
*/
|
|
9
|
+
import { signSignRequest } from "./request";
|
|
10
|
+
import { bytesToHex } from "./session";
|
|
11
|
+
/**
|
|
12
|
+
* Build an admin auth payload by threshold-signing the admin hash
|
|
13
|
+
* via the bootstrap group.
|
|
14
|
+
*
|
|
15
|
+
* The admin hash is: SHA256(group_id + ":" + nonce + ":" + timestamp_8BE)
|
|
16
|
+
* The signature is a 65-byte FROST Schnorr signature.
|
|
17
|
+
*/
|
|
18
|
+
export async function buildAdminAuth(config, groupId, authKeyPub, sessionKeypair, claims) {
|
|
19
|
+
const normalizedGroupId = groupId.toLowerCase();
|
|
20
|
+
const nonce = generateNonce();
|
|
21
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
22
|
+
// Build the admin hash: SHA256(group_id + ":" + nonce + ":" + timestamp_8BE)
|
|
23
|
+
const adminHash = await computeAdminHash(normalizedGroupId, nonce, timestamp);
|
|
24
|
+
// Threshold-sign the hash via bootstrap group
|
|
25
|
+
const signReq = await signSignRequest(sessionKeypair, claims, config.bootstrapGroup, adminHash);
|
|
26
|
+
const signRes = await fetch(config.nodeProxyUrl, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
"x-node-url": config.bootstrapNodes[0],
|
|
31
|
+
"x-node-path": "/v1/sign",
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify(signReq),
|
|
34
|
+
});
|
|
35
|
+
if (!signRes.ok) {
|
|
36
|
+
const body = await signRes.text();
|
|
37
|
+
throw new Error(`Admin signing failed: ${signRes.status} — ${body}`);
|
|
38
|
+
}
|
|
39
|
+
const { ethereum_signature } = await signRes.json();
|
|
40
|
+
return {
|
|
41
|
+
group_id: normalizedGroupId,
|
|
42
|
+
auth_key_pub: authKeyPub,
|
|
43
|
+
signature: ethereum_signature,
|
|
44
|
+
nonce,
|
|
45
|
+
timestamp,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Call an admin endpoint with auth.
|
|
50
|
+
*
|
|
51
|
+
* If the signing request fails with a 401 "session not found" error and
|
|
52
|
+
* a reauthenticate callback is provided, re-establishes the node session
|
|
53
|
+
* and retries once.
|
|
54
|
+
*/
|
|
55
|
+
export async function adminRequest(config, nodeUrl, path, groupId, authKeyPub, sessionKeypair, claims, extraBody, reauthenticate) {
|
|
56
|
+
try {
|
|
57
|
+
return await adminRequestInner(config, nodeUrl, path, groupId, authKeyPub, sessionKeypair, claims, extraBody);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
61
|
+
if (reauthenticate && /session not found/i.test(msg)) {
|
|
62
|
+
await reauthenticate();
|
|
63
|
+
return await adminRequestInner(config, nodeUrl, path, groupId, authKeyPub, sessionKeypair, claims, extraBody);
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function adminRequestInner(config, nodeUrl, path, groupId, authKeyPub, sessionKeypair, claims, extraBody) {
|
|
69
|
+
const auth = await buildAdminAuth(config, groupId, authKeyPub, sessionKeypair, claims);
|
|
70
|
+
const res = await fetch(config.nodeProxyUrl, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: {
|
|
73
|
+
"Content-Type": "application/json",
|
|
74
|
+
"x-node-url": nodeUrl,
|
|
75
|
+
"x-node-path": path,
|
|
76
|
+
},
|
|
77
|
+
body: JSON.stringify({ ...auth, ...extraBody }),
|
|
78
|
+
});
|
|
79
|
+
if (!res.ok) {
|
|
80
|
+
const body = await res.text();
|
|
81
|
+
throw new Error(`Admin ${path} failed: ${res.status} — ${body}`);
|
|
82
|
+
}
|
|
83
|
+
return res.json();
|
|
84
|
+
}
|
|
85
|
+
async function computeAdminHash(groupId, nonce, timestamp) {
|
|
86
|
+
const enc = new TextEncoder();
|
|
87
|
+
const parts = [];
|
|
88
|
+
parts.push(enc.encode(groupId));
|
|
89
|
+
parts.push(enc.encode(":"));
|
|
90
|
+
parts.push(enc.encode(nonce));
|
|
91
|
+
parts.push(enc.encode(":"));
|
|
92
|
+
// timestamp as 8-byte big-endian
|
|
93
|
+
const tsBuf = new ArrayBuffer(8);
|
|
94
|
+
const view = new DataView(tsBuf);
|
|
95
|
+
view.setBigUint64(0, BigInt(timestamp));
|
|
96
|
+
parts.push(new Uint8Array(tsBuf));
|
|
97
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
98
|
+
const buf = new Uint8Array(total);
|
|
99
|
+
let offset = 0;
|
|
100
|
+
for (const p of parts) {
|
|
101
|
+
buf.set(p, offset);
|
|
102
|
+
offset += p.length;
|
|
103
|
+
}
|
|
104
|
+
const digest = await crypto.subtle.digest("SHA-256", buf);
|
|
105
|
+
return new Uint8Array(digest);
|
|
106
|
+
}
|
|
107
|
+
function generateNonce() {
|
|
108
|
+
const bytes = new Uint8Array(16);
|
|
109
|
+
crypto.getRandomValues(bytes);
|
|
110
|
+
return bytesToHex(bytes);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth key certificate session flow.
|
|
3
|
+
*
|
|
4
|
+
* Creates a node session using an ECDSA auth key certificate instead of
|
|
5
|
+
* an OAuth/ZK proof. This enables keygen and signing without user login —
|
|
6
|
+
* useful for backend services, AI agents, or any programmatic access.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. Sign a certificate binding an identity + session pub to the auth key
|
|
10
|
+
* 2. POST /v1/auth with the certificate to establish a session
|
|
11
|
+
* 3. Use the session key for subsequent keygen/sign requests (same as OAuth flow)
|
|
12
|
+
*
|
|
13
|
+
* The identity becomes the key namespace: keys are stored as "authkey:<identity>"
|
|
14
|
+
* (or "authkey:<identity>:<suffix>" with key_suffix).
|
|
15
|
+
*/
|
|
16
|
+
import type { SessionKeypair } from "./types";
|
|
17
|
+
export interface AuthKeyCertConfig {
|
|
18
|
+
/** Base URL or proxy URL for the node */
|
|
19
|
+
nodeUrl: string;
|
|
20
|
+
/** If set, requests go through this proxy (for CORS) */
|
|
21
|
+
proxyEndpoint?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AuthKeyCertResult {
|
|
24
|
+
identity: string;
|
|
25
|
+
expiresAt: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Authenticate with a node using an auth key certificate.
|
|
29
|
+
*
|
|
30
|
+
* @param config - Node URL / proxy config
|
|
31
|
+
* @param groupId - Group contract address
|
|
32
|
+
* @param authPrivateKey - 32-byte ECDSA private key (matches an on-chain auth key)
|
|
33
|
+
* @param identity - Logical identity string (e.g. "my-backend", "agent-1")
|
|
34
|
+
* @param sessionKeypair - Ephemeral session keypair for subsequent requests
|
|
35
|
+
* @param expiry - Certificate expiry (unix seconds). Default: 1 hour from now.
|
|
36
|
+
*/
|
|
37
|
+
export declare function authenticateWithAuthKey(config: AuthKeyCertConfig, groupId: string, authPrivateKey: Uint8Array, identity: string, sessionKeypair: SessionKeypair, expiry?: number): Promise<AuthKeyCertResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Authenticate with a node using a Schnorr auth key certificate.
|
|
40
|
+
*
|
|
41
|
+
* The certificate hash is threshold-signed via the bootstrap group (FROST),
|
|
42
|
+
* producing a 65-byte Schnorr signature. This works for auth keys registered
|
|
43
|
+
* on-chain with the 0x01 (Schnorr) prefix.
|
|
44
|
+
*
|
|
45
|
+
* @param config - Bootstrap group config for threshold signing
|
|
46
|
+
* @param targetNodeUrl - The target group node to authenticate with
|
|
47
|
+
* @param proxyEndpoint - CORS proxy URL
|
|
48
|
+
* @param targetGroupId - The target group contract address
|
|
49
|
+
* @param authKeyPub - Auth key with 0x01 prefix (e.g. "0x0103d767f7...")
|
|
50
|
+
* @param identity - Logical identity string (e.g. "key-tester")
|
|
51
|
+
* @param sessionKeypair - Session keypair for the target group node
|
|
52
|
+
* @param bootstrapSessionKeypair - Session keypair for the bootstrap group (already authed)
|
|
53
|
+
* @param claims - OAuth claims for signing requests to the bootstrap group
|
|
54
|
+
* @param expiry - Certificate expiry (unix seconds). Default: 1 hour from now.
|
|
55
|
+
*/
|
|
56
|
+
export declare function authenticateWithSchnorrAuthKey(config: {
|
|
57
|
+
bootstrapGroup: string;
|
|
58
|
+
bootstrapNodes: string[];
|
|
59
|
+
nodeProxyUrl: string;
|
|
60
|
+
}, targetNodeUrl: string, proxyEndpoint: string, targetGroupId: string, authKeyPub: string, identity: string, sessionKeypair: SessionKeypair, bootstrapSessionKeypair: SessionKeypair, claims: {
|
|
61
|
+
iss: string;
|
|
62
|
+
sub: string;
|
|
63
|
+
}, expiry?: number): Promise<AuthKeyCertResult>;
|
|
64
|
+
//# sourceMappingURL=authkey-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authkey-session.d.ts","sourceRoot":"","sources":["../src/authkey-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,UAAU,EAC1B,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,cAAc,EAC9B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC,CAwD5B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,8BAA8B,CAClD,MAAM,EAAE;IACN,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB,EACD,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,cAAc,EAC9B,uBAAuB,EAAE,cAAc,EACvC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACpC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC,CAqE5B"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth key certificate session flow.
|
|
3
|
+
*
|
|
4
|
+
* Creates a node session using an ECDSA auth key certificate instead of
|
|
5
|
+
* an OAuth/ZK proof. This enables keygen and signing without user login —
|
|
6
|
+
* useful for backend services, AI agents, or any programmatic access.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. Sign a certificate binding an identity + session pub to the auth key
|
|
10
|
+
* 2. POST /v1/auth with the certificate to establish a session
|
|
11
|
+
* 3. Use the session key for subsequent keygen/sign requests (same as OAuth flow)
|
|
12
|
+
*
|
|
13
|
+
* The identity becomes the key namespace: keys are stored as "authkey:<identity>"
|
|
14
|
+
* (or "authkey:<identity>:<suffix>" with key_suffix).
|
|
15
|
+
*/
|
|
16
|
+
import { bytesToHex } from "./session";
|
|
17
|
+
/**
|
|
18
|
+
* Authenticate with a node using an auth key certificate.
|
|
19
|
+
*
|
|
20
|
+
* @param config - Node URL / proxy config
|
|
21
|
+
* @param groupId - Group contract address
|
|
22
|
+
* @param authPrivateKey - 32-byte ECDSA private key (matches an on-chain auth key)
|
|
23
|
+
* @param identity - Logical identity string (e.g. "my-backend", "agent-1")
|
|
24
|
+
* @param sessionKeypair - Ephemeral session keypair for subsequent requests
|
|
25
|
+
* @param expiry - Certificate expiry (unix seconds). Default: 1 hour from now.
|
|
26
|
+
*/
|
|
27
|
+
export async function authenticateWithAuthKey(config, groupId, authPrivateKey, identity, sessionKeypair, expiry) {
|
|
28
|
+
const { getPublicKey, signAsync } = await import("@noble/secp256k1");
|
|
29
|
+
const normalizedGroupId = groupId.toLowerCase();
|
|
30
|
+
const certExpiry = expiry ?? Math.floor(Date.now() / 1000) + 3600;
|
|
31
|
+
// Derive the auth key public key (with ECDSA prefix 0x00)
|
|
32
|
+
const authPubBytes = getPublicKey(authPrivateKey, true);
|
|
33
|
+
const authKeyPub = `0x00${bytesToHex(authPubBytes)}`;
|
|
34
|
+
// Sign certificate: SHA256(identity + ":" + group_id + ":" + session_pub_hex + ":" + expiry_8BE)
|
|
35
|
+
const certHash = await computeCertHash(identity, normalizedGroupId, sessionKeypair.publicKeyHex, certExpiry);
|
|
36
|
+
const certSig = await signAsync(certHash, authPrivateKey, {
|
|
37
|
+
lowS: true,
|
|
38
|
+
prehash: false,
|
|
39
|
+
});
|
|
40
|
+
const certSigHex = bytesToHex(new Uint8Array(certSig));
|
|
41
|
+
// POST /v1/auth with certificate
|
|
42
|
+
const url = config.proxyEndpoint ?? `${config.nodeUrl}/v1/auth`;
|
|
43
|
+
const headers = { "Content-Type": "application/json" };
|
|
44
|
+
if (config.proxyEndpoint) {
|
|
45
|
+
headers["x-node-url"] = config.nodeUrl;
|
|
46
|
+
headers["x-node-path"] = "/v1/auth";
|
|
47
|
+
}
|
|
48
|
+
const res = await fetch(url, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers,
|
|
51
|
+
body: JSON.stringify({
|
|
52
|
+
group_id: normalizedGroupId,
|
|
53
|
+
session_pub: sessionKeypair.publicKeyHex,
|
|
54
|
+
certificate: {
|
|
55
|
+
identity,
|
|
56
|
+
expiry: certExpiry,
|
|
57
|
+
auth_key_pub: authKeyPub,
|
|
58
|
+
signature: certSigHex,
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
const body = await res.text();
|
|
64
|
+
throw new Error(`Auth key auth failed: ${res.status} — ${body}`);
|
|
65
|
+
}
|
|
66
|
+
const data = await res.json();
|
|
67
|
+
return {
|
|
68
|
+
identity: data.identity ?? identity,
|
|
69
|
+
expiresAt: data.expires_at ?? certExpiry,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Authenticate with a node using a Schnorr auth key certificate.
|
|
74
|
+
*
|
|
75
|
+
* The certificate hash is threshold-signed via the bootstrap group (FROST),
|
|
76
|
+
* producing a 65-byte Schnorr signature. This works for auth keys registered
|
|
77
|
+
* on-chain with the 0x01 (Schnorr) prefix.
|
|
78
|
+
*
|
|
79
|
+
* @param config - Bootstrap group config for threshold signing
|
|
80
|
+
* @param targetNodeUrl - The target group node to authenticate with
|
|
81
|
+
* @param proxyEndpoint - CORS proxy URL
|
|
82
|
+
* @param targetGroupId - The target group contract address
|
|
83
|
+
* @param authKeyPub - Auth key with 0x01 prefix (e.g. "0x0103d767f7...")
|
|
84
|
+
* @param identity - Logical identity string (e.g. "key-tester")
|
|
85
|
+
* @param sessionKeypair - Session keypair for the target group node
|
|
86
|
+
* @param bootstrapSessionKeypair - Session keypair for the bootstrap group (already authed)
|
|
87
|
+
* @param claims - OAuth claims for signing requests to the bootstrap group
|
|
88
|
+
* @param expiry - Certificate expiry (unix seconds). Default: 1 hour from now.
|
|
89
|
+
*/
|
|
90
|
+
export async function authenticateWithSchnorrAuthKey(config, targetNodeUrl, proxyEndpoint, targetGroupId, authKeyPub, identity, sessionKeypair, bootstrapSessionKeypair, claims, expiry) {
|
|
91
|
+
const { signSignRequest } = await import("./request");
|
|
92
|
+
const normalizedGroupId = targetGroupId.toLowerCase();
|
|
93
|
+
const certExpiry = expiry ?? Math.floor(Date.now() / 1000) + 3600;
|
|
94
|
+
// Compute certificate hash
|
|
95
|
+
const certHash = await computeCertHash(identity, normalizedGroupId, sessionKeypair.publicKeyHex, certExpiry);
|
|
96
|
+
// Threshold-sign the cert hash via bootstrap group
|
|
97
|
+
const signReq = await signSignRequest(bootstrapSessionKeypair, claims, config.bootstrapGroup, certHash);
|
|
98
|
+
const signRes = await fetch(config.nodeProxyUrl, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": "application/json",
|
|
102
|
+
"x-node-url": config.bootstrapNodes[0],
|
|
103
|
+
"x-node-path": "/v1/sign",
|
|
104
|
+
},
|
|
105
|
+
body: JSON.stringify(signReq),
|
|
106
|
+
});
|
|
107
|
+
if (!signRes.ok) {
|
|
108
|
+
const body = await signRes.text();
|
|
109
|
+
throw new Error(`Schnorr cert signing failed: ${signRes.status} — ${body}`);
|
|
110
|
+
}
|
|
111
|
+
const { ethereum_signature } = await signRes.json();
|
|
112
|
+
// POST /v1/auth on the target group node with the Schnorr-signed certificate
|
|
113
|
+
const res = await fetch(proxyEndpoint, {
|
|
114
|
+
method: "POST",
|
|
115
|
+
headers: {
|
|
116
|
+
"Content-Type": "application/json",
|
|
117
|
+
"x-node-url": targetNodeUrl,
|
|
118
|
+
"x-node-path": "/v1/auth",
|
|
119
|
+
},
|
|
120
|
+
body: JSON.stringify({
|
|
121
|
+
group_id: normalizedGroupId,
|
|
122
|
+
session_pub: sessionKeypair.publicKeyHex,
|
|
123
|
+
certificate: {
|
|
124
|
+
identity,
|
|
125
|
+
expiry: certExpiry,
|
|
126
|
+
auth_key_pub: authKeyPub,
|
|
127
|
+
signature: ethereum_signature,
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const body = await res.text();
|
|
133
|
+
throw new Error(`Schnorr auth key auth failed: ${res.status} — ${body}`);
|
|
134
|
+
}
|
|
135
|
+
const data = await res.json();
|
|
136
|
+
return {
|
|
137
|
+
identity: data.identity ?? identity,
|
|
138
|
+
expiresAt: data.expires_at ?? certExpiry,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async function computeCertHash(identity, groupId, sessionPubHex, expiry) {
|
|
142
|
+
const enc = new TextEncoder();
|
|
143
|
+
const expiryBuf = new ArrayBuffer(8);
|
|
144
|
+
new DataView(expiryBuf).setBigUint64(0, BigInt(expiry));
|
|
145
|
+
const parts = [
|
|
146
|
+
enc.encode(identity),
|
|
147
|
+
enc.encode(":"),
|
|
148
|
+
enc.encode(groupId),
|
|
149
|
+
enc.encode(":"),
|
|
150
|
+
enc.encode(sessionPubHex),
|
|
151
|
+
enc.encode(":"),
|
|
152
|
+
new Uint8Array(expiryBuf),
|
|
153
|
+
];
|
|
154
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
155
|
+
const buf = new Uint8Array(total);
|
|
156
|
+
let offset = 0;
|
|
157
|
+
for (const p of parts) {
|
|
158
|
+
buf.set(p, offset);
|
|
159
|
+
offset += p.length;
|
|
160
|
+
}
|
|
161
|
+
const digest = await crypto.subtle.digest("SHA-256", buf);
|
|
162
|
+
return new Uint8Array(digest);
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=authkey-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authkey-session.js","sourceRoot":"","sources":["../src/authkey-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap group node authentication.
|
|
3
|
+
*
|
|
4
|
+
* After generating a ZK proof of the JWT, POST it to each
|
|
5
|
+
* bootstrap node to register the session key.
|
|
6
|
+
*/
|
|
7
|
+
export interface BootstrapConfig {
|
|
8
|
+
groupId: string;
|
|
9
|
+
nodeUrls: string[];
|
|
10
|
+
/** Proxy endpoint for CORS — if set, requests go through this instead of directly to nodes */
|
|
11
|
+
proxyEndpoint?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface AuthResult {
|
|
14
|
+
identity: string;
|
|
15
|
+
expiresAt: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Authenticate with all bootstrap nodes.
|
|
19
|
+
*
|
|
20
|
+
* Posts the ZK proof + session public key to each node's /v1/auth.
|
|
21
|
+
* All nodes must accept the session for signing to work.
|
|
22
|
+
*/
|
|
23
|
+
export declare function authenticateWithBootstrap(config: BootstrapConfig, proof: Uint8Array, sessionPubHex: string, claims: {
|
|
24
|
+
iss: string;
|
|
25
|
+
sub: string;
|
|
26
|
+
exp: number;
|
|
27
|
+
aud: string;
|
|
28
|
+
azp: string;
|
|
29
|
+
}, jwksModulusBytes: Uint8Array): Promise<AuthResult>;
|
|
30
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8FAA8F;IAC9F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,UAAU,EACjB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAC3E,gBAAgB,EAAE,UAAU,GAC3B,OAAO,CAAC,UAAU,CAAC,CA2CrB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap group node authentication.
|
|
3
|
+
*
|
|
4
|
+
* After generating a ZK proof of the JWT, POST it to each
|
|
5
|
+
* bootstrap node to register the session key.
|
|
6
|
+
*/
|
|
7
|
+
import { bytesToHex } from "./session";
|
|
8
|
+
/**
|
|
9
|
+
* Authenticate with all bootstrap nodes.
|
|
10
|
+
*
|
|
11
|
+
* Posts the ZK proof + session public key to each node's /v1/auth.
|
|
12
|
+
* All nodes must accept the session for signing to work.
|
|
13
|
+
*/
|
|
14
|
+
export async function authenticateWithBootstrap(config, proof, sessionPubHex, claims, jwksModulusBytes) {
|
|
15
|
+
const request = {
|
|
16
|
+
group_id: config.groupId,
|
|
17
|
+
session_pub: sessionPubHex,
|
|
18
|
+
proof: bytesToHex(proof),
|
|
19
|
+
sub: claims.sub,
|
|
20
|
+
iss: claims.iss,
|
|
21
|
+
exp: claims.exp,
|
|
22
|
+
aud: claims.aud,
|
|
23
|
+
azp: claims.azp,
|
|
24
|
+
jwks_modulus: bytesToHex(jwksModulusBytes),
|
|
25
|
+
};
|
|
26
|
+
// Auth with all nodes in parallel
|
|
27
|
+
const results = await Promise.allSettled(config.nodeUrls.map((url) => authWithNode(url, request, config.proxyEndpoint)));
|
|
28
|
+
// Check that at least one succeeded
|
|
29
|
+
const successes = results.filter((r) => r.status === "fulfilled");
|
|
30
|
+
const failures = results.filter((r) => r.status === "rejected");
|
|
31
|
+
if (successes.length === 0) {
|
|
32
|
+
const reasons = failures.map((f) => f.reason?.message ?? String(f.reason));
|
|
33
|
+
throw new Error(`All bootstrap nodes rejected auth: ${reasons.join("; ")}`);
|
|
34
|
+
}
|
|
35
|
+
if (failures.length > 0) {
|
|
36
|
+
console.warn(`${failures.length}/${config.nodeUrls.length} bootstrap nodes failed auth:`, failures.map((f) => f.reason?.message));
|
|
37
|
+
}
|
|
38
|
+
return successes[0].value;
|
|
39
|
+
}
|
|
40
|
+
async function authWithNode(baseUrl, request, proxyEndpoint) {
|
|
41
|
+
const url = proxyEndpoint ?? `${baseUrl}/v1/auth`;
|
|
42
|
+
const headers = {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
};
|
|
45
|
+
if (proxyEndpoint) {
|
|
46
|
+
headers["x-node-url"] = baseUrl;
|
|
47
|
+
headers["x-node-path"] = "/v1/auth";
|
|
48
|
+
}
|
|
49
|
+
const res = await fetch(url, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers,
|
|
52
|
+
body: JSON.stringify(request),
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const body = await res.text();
|
|
56
|
+
throw new Error(`${baseUrl}: ${res.status} — ${body}`);
|
|
57
|
+
}
|
|
58
|
+
return res.json();
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundler RPC client for ERC-4337 UserOperations.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic — all config is passed in, no env imports.
|
|
5
|
+
* Handles serialization between the packed UserOp format (used
|
|
6
|
+
* internally and for hashing) and the unpacked v0.7 RPC format
|
|
7
|
+
* expected by signet-min-bundler.
|
|
8
|
+
*/
|
|
9
|
+
import { type Address, type Hex } from "viem";
|
|
10
|
+
import type { PackedUserOperation } from "./userop";
|
|
11
|
+
export interface BundlerSendResult {
|
|
12
|
+
userOpHash: Hex;
|
|
13
|
+
}
|
|
14
|
+
export interface UserOpReceipt {
|
|
15
|
+
userOpHash: Hex;
|
|
16
|
+
transactionHash: Hex;
|
|
17
|
+
success: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface GasEstimate {
|
|
20
|
+
preVerificationGas: Hex;
|
|
21
|
+
verificationGasLimit: Hex;
|
|
22
|
+
callGasLimit: Hex;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* ERC-7677 paymaster sponsorship result.
|
|
26
|
+
*
|
|
27
|
+
* The bundler returns paymaster gas limits as separate fields, but
|
|
28
|
+
* signet-min-bundler's wire format for eth_sendUserOperation expects
|
|
29
|
+
* them packed into the front of `paymasterData` (see packPaymasterData
|
|
30
|
+
* and the note on the getHash comment in internal/paymaster/paymaster.go).
|
|
31
|
+
*/
|
|
32
|
+
export interface PaymasterSponsorship {
|
|
33
|
+
paymaster: Address;
|
|
34
|
+
paymasterData: Hex;
|
|
35
|
+
paymasterVerificationGasLimit: Hex;
|
|
36
|
+
paymasterPostOpGasLimit: Hex;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* ERC-7677 context object passed as the 4th parameter to
|
|
40
|
+
* pm_getPaymasterStubData / pm_getPaymasterData.
|
|
41
|
+
*/
|
|
42
|
+
export interface PaymasterContext {
|
|
43
|
+
invite_code?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function sendUserOp(bundlerUrl: string, entryPointAddress: Address, userOp: PackedUserOperation): Promise<BundlerSendResult>;
|
|
46
|
+
export declare function getUserOpReceipt(bundlerUrl: string, userOpHash: Hex): Promise<UserOpReceipt | null>;
|
|
47
|
+
export declare function estimateUserOpGas(bundlerUrl: string, entryPointAddress: Address, userOp: PackedUserOperation): Promise<GasEstimate>;
|
|
48
|
+
/**
|
|
49
|
+
* Call pm_getPaymasterStubData.
|
|
50
|
+
*
|
|
51
|
+
* Returns paymaster fields with a correctly-sized but zeroed signature,
|
|
52
|
+
* suitable for gas estimation. Call this BEFORE eth_estimateUserOperationGas
|
|
53
|
+
* so the estimate accounts for the paymaster's verification overhead.
|
|
54
|
+
*/
|
|
55
|
+
export declare function getPaymasterStubData(bundlerUrl: string, entryPointAddress: Address, chainId: number, userOp: PackedUserOperation, context?: PaymasterContext): Promise<PaymasterSponsorship>;
|
|
56
|
+
/**
|
|
57
|
+
* Call pm_getPaymasterData.
|
|
58
|
+
*
|
|
59
|
+
* Returns a real paymaster signature. The bundler first checks the
|
|
60
|
+
* paymaster contract's shouldSponsor() policy via eth_call; if sponsorship
|
|
61
|
+
* is rejected, this throws.
|
|
62
|
+
*
|
|
63
|
+
* IMPORTANT: call AFTER gas estimation, because the paymaster signs over
|
|
64
|
+
* the gas fields. Changing gas after this call invalidates the signature.
|
|
65
|
+
*/
|
|
66
|
+
export declare function getPaymasterData(bundlerUrl: string, entryPointAddress: Address, chainId: number, userOp: PackedUserOperation, context?: PaymasterContext): Promise<PaymasterSponsorship>;
|
|
67
|
+
/**
|
|
68
|
+
* Pack an ERC-7677 sponsorship response into the on-chain paymasterAndData
|
|
69
|
+
* layout expected by EntryPoint v0.7:
|
|
70
|
+
*
|
|
71
|
+
* [paymaster:20][verifGasLimit:16][postOpGasLimit:16][paymasterData:rest]
|
|
72
|
+
*
|
|
73
|
+
* The paymaster's getHash function reads paymasterAndData[20:52] as the
|
|
74
|
+
* packed (verifGasLimit || postOpGasLimit) uint128 pair — so the returned
|
|
75
|
+
* layout must match exactly or the paymaster signature will not verify
|
|
76
|
+
* on-chain (AA34 signature error).
|
|
77
|
+
*
|
|
78
|
+
* NOTE on the bundler's wire format: signet-min-bundler's FromRPC
|
|
79
|
+
* concatenates the JSON `paymaster` + `paymasterData` fields directly
|
|
80
|
+
* into paymasterAndData. It does NOT re-insert gas-limit bytes. So when
|
|
81
|
+
* serializing for eth_sendUserOperation, the `paymasterData` on the wire
|
|
82
|
+
* already includes the packed gas limits.
|
|
83
|
+
*/
|
|
84
|
+
export declare function applyPaymasterSponsorship(userOp: PackedUserOperation, s: PaymasterSponsorship): PackedUserOperation;
|
|
85
|
+
//# sourceMappingURL=bundler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,GAAG,EAAU,MAAM,MAAM,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAMpD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,GAAG,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,GAAG,CAAC;IAChB,eAAe,EAAE,GAAG,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,kBAAkB,EAAE,GAAG,CAAC;IACxB,oBAAoB,EAAE,GAAG,CAAC;IAC1B,YAAY,EAAE,GAAG,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,GAAG,CAAC;IACnB,6BAA6B,EAAE,GAAG,CAAC;IACnC,uBAAuB,EAAE,GAAG,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,OAAO,EAC1B,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAED,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,GAAG,GACd,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAY/B;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,OAAO,EAC1B,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAMtB;AAMD;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,OAAO,EAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,mBAAmB,EAC3B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,OAAO,EAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,mBAAmB,EAC3B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,mBAAmB,EAC3B,CAAC,EAAE,oBAAoB,GACtB,mBAAmB,CAQrB"}
|