web3ql-client 1.2.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/README.md +66 -0
- package/contracts/PublicKeyRegistry.sol +87 -0
- package/dist/src/access.d.ts +176 -0
- package/dist/src/access.d.ts.map +1 -0
- package/dist/src/access.js +283 -0
- package/dist/src/access.js.map +1 -0
- package/dist/src/batch.d.ts +107 -0
- package/dist/src/batch.d.ts.map +1 -0
- package/dist/src/batch.js +188 -0
- package/dist/src/batch.js.map +1 -0
- package/dist/src/cli.d.ts +40 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +361 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/constraints.d.ts +126 -0
- package/dist/src/constraints.d.ts.map +1 -0
- package/dist/src/constraints.js +192 -0
- package/dist/src/constraints.js.map +1 -0
- package/dist/src/crypto.d.ts +118 -0
- package/dist/src/crypto.d.ts.map +1 -0
- package/dist/src/crypto.js +192 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/factory-client.d.ts +106 -0
- package/dist/src/factory-client.d.ts.map +1 -0
- package/dist/src/factory-client.js +202 -0
- package/dist/src/factory-client.js.map +1 -0
- package/dist/src/index-cache.d.ts +156 -0
- package/dist/src/index-cache.d.ts.map +1 -0
- package/dist/src/index-cache.js +265 -0
- package/dist/src/index-cache.js.map +1 -0
- package/dist/src/index.d.ts +60 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +60 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/migrations.d.ts +114 -0
- package/dist/src/migrations.d.ts.map +1 -0
- package/dist/src/migrations.js +173 -0
- package/dist/src/migrations.js.map +1 -0
- package/dist/src/model.d.ts +198 -0
- package/dist/src/model.d.ts.map +1 -0
- package/dist/src/model.js +379 -0
- package/dist/src/model.js.map +1 -0
- package/dist/src/query.d.ts +155 -0
- package/dist/src/query.d.ts.map +1 -0
- package/dist/src/query.js +386 -0
- package/dist/src/query.js.map +1 -0
- package/dist/src/registry.d.ts +45 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +80 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/schema-manager.d.ts +109 -0
- package/dist/src/schema-manager.d.ts.map +1 -0
- package/dist/src/schema-manager.js +259 -0
- package/dist/src/schema-manager.js.map +1 -0
- package/dist/src/table-client.d.ts +156 -0
- package/dist/src/table-client.d.ts.map +1 -0
- package/dist/src/table-client.js +292 -0
- package/dist/src/table-client.js.map +1 -0
- package/dist/src/typed-table.d.ts +159 -0
- package/dist/src/typed-table.d.ts.map +1 -0
- package/dist/src/typed-table.js +246 -0
- package/dist/src/typed-table.js.map +1 -0
- package/dist/src/types.d.ts +48 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +222 -0
- package/dist/src/types.js.map +1 -0
- package/keyManager.js +337 -0
- package/package.json +38 -0
- package/src/access.ts +421 -0
- package/src/batch.ts +259 -0
- package/src/cli.ts +349 -0
- package/src/constraints.ts +283 -0
- package/src/crypto.ts +239 -0
- package/src/factory-client.ts +237 -0
- package/src/index-cache.ts +351 -0
- package/src/index.ts +171 -0
- package/src/migrations.ts +215 -0
- package/src/model.ts +538 -0
- package/src/query.ts +508 -0
- package/src/registry.ts +100 -0
- package/src/schema-manager.ts +301 -0
- package/src/table-client.ts +393 -0
- package/src/typed-table.ts +340 -0
- package/src/types.ts +284 -0
- package/tsconfig.json +22 -0
- package/walletUtils.js +204 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file constraints.ts
|
|
3
|
+
* @notice Web3QL v1.2 — integrity constraint engine.
|
|
4
|
+
*
|
|
5
|
+
* Constraints live in the SDK layer (off-chain) with the following guarantees:
|
|
6
|
+
*
|
|
7
|
+
* • PRIMARY KEY uniqueness — contract enforces at write (requires exists() pre-check)
|
|
8
|
+
* • UNIQUE — SDK maintains an in-memory seen-values set per column
|
|
9
|
+
* • DEFAULT / NOT NULL — enforced by types.ts validateAndEncode()
|
|
10
|
+
* • CHECK — per-column validation function
|
|
11
|
+
* • FOREIGN KEY — SDK reads target table at write time
|
|
12
|
+
* • AUTO_INCREMENT — SDK-maintained counter (persisted in a meta record)
|
|
13
|
+
* • ON DELETE CASCADE — SDK fetches referencing records and deletes them
|
|
14
|
+
*
|
|
15
|
+
* None of these require contract changes for CHECK/UNIQUE (client-side enforcement).
|
|
16
|
+
* PK uniqueness IS enforced at the contract level via `recordExists`.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* ─────────────────────────────────────────────────────────────
|
|
20
|
+
* const constraints = new ConstraintEngine([
|
|
21
|
+
* { type: 'unique', column: 'email' },
|
|
22
|
+
* { type: 'check', column: 'age', check: (v) => Number(v) >= 0 },
|
|
23
|
+
* { type: 'fk', column: 'userId', references: { table: userTable, column: 'id' } },
|
|
24
|
+
* ]);
|
|
25
|
+
*
|
|
26
|
+
* // Before writing:
|
|
27
|
+
* await constraints.validate(row, existingRows);
|
|
28
|
+
*
|
|
29
|
+
* // Get next AUTO_INCREMENT id:
|
|
30
|
+
* const nextId = await constraints.nextId(tableAddress, client);
|
|
31
|
+
* ─────────────────────────────────────────────────────────────
|
|
32
|
+
*/
|
|
33
|
+
// ─────────────────────────────────────────────────────────────
|
|
34
|
+
// Constraint violation error
|
|
35
|
+
// ─────────────────────────────────────────────────────────────
|
|
36
|
+
export class ConstraintViolation extends Error {
|
|
37
|
+
constraintType;
|
|
38
|
+
column;
|
|
39
|
+
constructor(constraintType, column, message) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.constraintType = constraintType;
|
|
42
|
+
this.column = column;
|
|
43
|
+
this.name = 'ConstraintViolation';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ─────────────────────────────────────────────────────────────
|
|
47
|
+
// ConstraintEngine
|
|
48
|
+
// ─────────────────────────────────────────────────────────────
|
|
49
|
+
export class ConstraintEngine {
|
|
50
|
+
constraints;
|
|
51
|
+
constructor(constraints = []) {
|
|
52
|
+
this.constraints = constraints;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate a new/updated row against all constraints.
|
|
56
|
+
*
|
|
57
|
+
* @param row The row being written (fully encoded, post validateAndEncode).
|
|
58
|
+
* @param existingRows Already-decoded rows from the same table (for UNIQUE checks).
|
|
59
|
+
* Pass an empty array if fetching is not possible.
|
|
60
|
+
*/
|
|
61
|
+
async validate(row, existingRows = []) {
|
|
62
|
+
for (const c of this.constraints) {
|
|
63
|
+
switch (c.type) {
|
|
64
|
+
case 'notNull':
|
|
65
|
+
this._checkNotNull(c, row);
|
|
66
|
+
break;
|
|
67
|
+
case 'unique':
|
|
68
|
+
this._checkUnique(c, row, existingRows);
|
|
69
|
+
break;
|
|
70
|
+
case 'check':
|
|
71
|
+
await this._checkCheck(c, row);
|
|
72
|
+
break;
|
|
73
|
+
case 'fk':
|
|
74
|
+
await this._checkForeignKey(c, row);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
_checkNotNull(c, row) {
|
|
80
|
+
const v = row[c.column];
|
|
81
|
+
if (v === null || v === undefined || v === '__NULL__') {
|
|
82
|
+
throw new ConstraintViolation('notNull', c.column, `NOT NULL violation: column "${c.column}" cannot be null`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
_checkUnique(c, row, existingRows) {
|
|
86
|
+
const newVal = row[c.column];
|
|
87
|
+
const conflict = existingRows.find((existing) => existing[c.column] === newVal && newVal !== null && newVal !== undefined);
|
|
88
|
+
if (conflict) {
|
|
89
|
+
throw new ConstraintViolation('unique', c.column, `UNIQUE violation: column "${c.column}" already has value "${String(newVal)}"`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async _checkCheck(c, row) {
|
|
93
|
+
const value = row[c.column];
|
|
94
|
+
const valid = await c.check(value, row);
|
|
95
|
+
if (!valid) {
|
|
96
|
+
throw new ConstraintViolation('check', c.column, `CHECK violation: column "${c.column}"${c.name ? ` (${c.name})` : ''} rejected value "${String(value)}"`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async _checkForeignKey(c, row) {
|
|
100
|
+
const refValue = row[c.column];
|
|
101
|
+
if (refValue === null || refValue === undefined || refValue === '__NULL__')
|
|
102
|
+
return; // NULL FK is allowed
|
|
103
|
+
const { table, tableName } = c.references;
|
|
104
|
+
const refKey = table.deriveKey(tableName, BigInt(String(refValue)));
|
|
105
|
+
const exists = await table.exists(refKey);
|
|
106
|
+
if (!exists) {
|
|
107
|
+
throw new ConstraintViolation('fk', c.column, `FOREIGN KEY violation: column "${c.column}" references non-existent record "${String(refValue)}"`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* ON DELETE CASCADE — delete all records in this table that reference
|
|
112
|
+
* the deleted row in the foreign-key column.
|
|
113
|
+
*
|
|
114
|
+
* @param deletedValue The primary-key value of the deleted parent record.
|
|
115
|
+
* @param fkColumn The column in this table that holds the FK.
|
|
116
|
+
* @param ownedRecords All decoded records in this table (to find referencing rows).
|
|
117
|
+
* @param deleteRecord Callback to actually delete a record by its bytes32 key.
|
|
118
|
+
*/
|
|
119
|
+
async onDeleteCascade(deletedValue, fkColumn, ownedRecords, deleteRecord) {
|
|
120
|
+
const toDelete = ownedRecords.filter((r) => String(r.data[fkColumn]) === String(deletedValue));
|
|
121
|
+
for (const r of toDelete) {
|
|
122
|
+
await deleteRecord(r.key);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// ─────────────────────────────────────────────────────────────
|
|
127
|
+
// AUTO_INCREMENT counter
|
|
128
|
+
// ─────────────────────────────────────────────────────────────
|
|
129
|
+
/**
|
|
130
|
+
* Client-side AUTO_INCREMENT counter backed by a special meta record on-chain.
|
|
131
|
+
*
|
|
132
|
+
* The counter is stored as a JSON record encrypted for the table owner under
|
|
133
|
+
* the key `keccak256("__auto_increment__" + tableName)`.
|
|
134
|
+
*
|
|
135
|
+
* ⚠ This is NOT atomic across multiple concurrent writers.
|
|
136
|
+
* For single-writer tables (personal data), it is safe.
|
|
137
|
+
* For multi-writer tables, use a relay-maintained counter (v1.2).
|
|
138
|
+
*/
|
|
139
|
+
export class AutoIncrementCounter {
|
|
140
|
+
tableName;
|
|
141
|
+
client;
|
|
142
|
+
metaKey;
|
|
143
|
+
constructor(tableName, client) {
|
|
144
|
+
this.tableName = tableName;
|
|
145
|
+
this.client = client;
|
|
146
|
+
this.metaKey = client.deriveKey(`__auto_increment__${tableName}`, 0n);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Read the current counter value. Returns 0 if not yet initialised.
|
|
150
|
+
*/
|
|
151
|
+
async current() {
|
|
152
|
+
try {
|
|
153
|
+
const exists = await this.client.exists(this.metaKey);
|
|
154
|
+
if (!exists)
|
|
155
|
+
return 0n;
|
|
156
|
+
const json = await this.client.readPlaintext(this.metaKey);
|
|
157
|
+
const { counter } = JSON.parse(json);
|
|
158
|
+
return BigInt(counter);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return 0n;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Atomically increment and return the next ID.
|
|
166
|
+
* Reads current → increments → writes new value → returns incremented.
|
|
167
|
+
*/
|
|
168
|
+
async next() {
|
|
169
|
+
const cur = await this.current();
|
|
170
|
+
const nextId = cur + 1n;
|
|
171
|
+
const payload = JSON.stringify({ counter: nextId.toString() });
|
|
172
|
+
if (cur === 0n) {
|
|
173
|
+
await this.client.writeRaw(this.metaKey, payload);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
await this.client.updateRaw(this.metaKey, payload);
|
|
177
|
+
}
|
|
178
|
+
return nextId;
|
|
179
|
+
}
|
|
180
|
+
/** Reset the counter (use with caution — may cause PK collisions). */
|
|
181
|
+
async reset(to = 0n) {
|
|
182
|
+
const payload = JSON.stringify({ counter: to.toString() });
|
|
183
|
+
const cur = await this.current();
|
|
184
|
+
if (cur === 0n) {
|
|
185
|
+
await this.client.writeRaw(this.metaKey, payload);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
await this.client.updateRaw(this.metaKey, payload);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=constraints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constraints.js","sourceRoot":"","sources":["../../src/constraints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file crypto.ts
|
|
3
|
+
* @notice Off-chain encryption layer for Web3QL.
|
|
4
|
+
*
|
|
5
|
+
* Security model:
|
|
6
|
+
* ─────────────────────────────────────────────────────────────
|
|
7
|
+
* • One random 32-byte symmetric key is generated per record.
|
|
8
|
+
* • Record data is encrypted with that key using NaCl secretbox
|
|
9
|
+
* (XSalsa20-Poly1305, 256-bit key, 192-bit nonce, 128-bit MAC).
|
|
10
|
+
* • The symmetric key is encrypted separately for each authorised
|
|
11
|
+
* user using NaCl box (X25519-XSalsa20-Poly1305, ECDH key agreement).
|
|
12
|
+
* • Only the encrypted blobs ever touch the chain — the symmetric key
|
|
13
|
+
* and plaintext never leave the client device.
|
|
14
|
+
*
|
|
15
|
+
* Key derivation from Ethereum wallet:
|
|
16
|
+
* ─────────────────────────────────────────────────────────────
|
|
17
|
+
* X25519 private key = SHA-256(Ethereum private key bytes)
|
|
18
|
+
* X25519 public key = scalar multiplication (computed by nacl)
|
|
19
|
+
*
|
|
20
|
+
* This means no separate key management — the wallet IS the
|
|
21
|
+
* encryption identity. Losing the wallet private key = losing
|
|
22
|
+
* access to encrypted records (same as losing any crypto key).
|
|
23
|
+
*
|
|
24
|
+
* Wire format (self-describing, no external framing needed):
|
|
25
|
+
* ─────────────────────────────────────────────────────────────
|
|
26
|
+
* secretbox blob: [ 24-byte nonce | encrypted payload ]
|
|
27
|
+
* box blob: [ 24-byte nonce | encrypted payload ]
|
|
28
|
+
*/
|
|
29
|
+
import type { Signer } from 'ethers';
|
|
30
|
+
export interface EncryptionKeypair {
|
|
31
|
+
/** X25519 public key — safe to publish on-chain */
|
|
32
|
+
publicKey: Uint8Array;
|
|
33
|
+
/** X25519 private key — never leaves the client */
|
|
34
|
+
privateKey: Uint8Array;
|
|
35
|
+
}
|
|
36
|
+
/** Message signed by the wallet to derive the X25519 keypair.
|
|
37
|
+
* Must be identical to the constant in cloud/lib/browser-crypto.ts so
|
|
38
|
+
* browser and SDK produce the same keypair for the same wallet.
|
|
39
|
+
*/
|
|
40
|
+
export declare const KEY_DERIVATION_MESSAGE = "Web3QL encryption key derivation v1";
|
|
41
|
+
/**
|
|
42
|
+
* Derive an X25519 keypair from an ethers Signer by signing a fixed
|
|
43
|
+
* derivation message. Because Ethereum personal_sign (RFC 6979) is
|
|
44
|
+
* deterministic, the same wallet always produces the same keypair.
|
|
45
|
+
*
|
|
46
|
+
* ✅ Use this when both SDK and browser clients need to share the same
|
|
47
|
+
* encryption identity — the resulting pubkey matches what the browser
|
|
48
|
+
* derives via `deriveKeypairFromSignature` in browser-crypto.ts.
|
|
49
|
+
*
|
|
50
|
+
* @param signer Any ethers v6 Signer (e.g. `new ethers.Wallet(privKey)`).
|
|
51
|
+
*/
|
|
52
|
+
export declare function deriveKeypairFromWallet(signer: Signer): Promise<EncryptionKeypair>;
|
|
53
|
+
/**
|
|
54
|
+
* Derive an X25519 encryption keypair from an Ethereum private key.
|
|
55
|
+
*
|
|
56
|
+
* @deprecated Prefer `deriveKeypairFromWallet(signer)` — it produces a
|
|
57
|
+
* keypair that is compatible with the Web3QL browser Explorer and any
|
|
58
|
+
* other client that uses wallet-signature derivation. Raw-private-key
|
|
59
|
+
* derivation generates a DIFFERENT keypair, so records written with this
|
|
60
|
+
* function cannot be decrypted in the browser (and vice-versa).
|
|
61
|
+
*
|
|
62
|
+
* @param ethPrivateKey Hex string, with or without "0x" prefix.
|
|
63
|
+
*/
|
|
64
|
+
export declare function deriveKeypair(ethPrivateKey: string): EncryptionKeypair;
|
|
65
|
+
/**
|
|
66
|
+
* Derive ONLY the public key (useful when you only have a signed
|
|
67
|
+
* message and want to register — not when you have the private key).
|
|
68
|
+
* Exposed for completeness; normally call deriveKeypair() directly.
|
|
69
|
+
*/
|
|
70
|
+
export declare function publicKeyFromPrivate(ethPrivateKey: string): Uint8Array;
|
|
71
|
+
/**
|
|
72
|
+
* Generate a random 32-byte symmetric key for a new record.
|
|
73
|
+
* Call this once per write — never reuse across records.
|
|
74
|
+
*/
|
|
75
|
+
export declare function generateSymmetricKey(): Uint8Array;
|
|
76
|
+
/**
|
|
77
|
+
* Encrypt plaintext with a symmetric key.
|
|
78
|
+
* Wire format: [ 24-byte nonce | MAC+ciphertext ]
|
|
79
|
+
*/
|
|
80
|
+
export declare function encryptData(plaintext: Uint8Array, symmetricKey: Uint8Array): Uint8Array;
|
|
81
|
+
/**
|
|
82
|
+
* Decrypt a secretbox blob (nonce||ciphertext) with a symmetric key.
|
|
83
|
+
* Throws if the MAC check fails (data tampered or wrong key).
|
|
84
|
+
*/
|
|
85
|
+
export declare function decryptData(blob: Uint8Array, symmetricKey: Uint8Array): Uint8Array;
|
|
86
|
+
/**
|
|
87
|
+
* Encrypt a symmetric key for a specific recipient.
|
|
88
|
+
*
|
|
89
|
+
* The caller (sharer) needs their own X25519 private key and the
|
|
90
|
+
* recipient's X25519 public key (registered on-chain in PublicKeyRegistry).
|
|
91
|
+
*
|
|
92
|
+
* Wire format: [ 24-byte nonce | MAC+ciphertext ]
|
|
93
|
+
*/
|
|
94
|
+
export declare function encryptKeyForRecipient(symmetricKey: Uint8Array, recipientPublicKey: Uint8Array, senderPrivateKey: Uint8Array): Uint8Array;
|
|
95
|
+
/**
|
|
96
|
+
* Decrypt a symmetric key that was encrypted for us.
|
|
97
|
+
* Throws if authentication fails.
|
|
98
|
+
*/
|
|
99
|
+
export declare function decryptKeyFromSender(blob: Uint8Array, senderPublicKey: Uint8Array, recipientPrivKey: Uint8Array): Uint8Array;
|
|
100
|
+
/**
|
|
101
|
+
* Encrypt the symmetric key to yourself — used on the initial write
|
|
102
|
+
* so the owner can always recover their own key.
|
|
103
|
+
*/
|
|
104
|
+
export declare function encryptKeyForSelf(symmetricKey: Uint8Array, keypair: EncryptionKeypair): Uint8Array;
|
|
105
|
+
/**
|
|
106
|
+
* Decrypt a symmetric key that was encrypted to yourself.
|
|
107
|
+
*/
|
|
108
|
+
export declare function decryptKeyForSelf(blob: Uint8Array, keypair: EncryptionKeypair): Uint8Array;
|
|
109
|
+
/**
|
|
110
|
+
* Encode a 32-byte X25519 public key as a 0x-prefixed hex string
|
|
111
|
+
* suitable for passing to PublicKeyRegistry.register().
|
|
112
|
+
*/
|
|
113
|
+
export declare function publicKeyToHex(pubKey: Uint8Array): string;
|
|
114
|
+
/**
|
|
115
|
+
* Decode a 0x-prefixed hex bytes32 from the registry back to Uint8Array.
|
|
116
|
+
*/
|
|
117
|
+
export declare function hexToPublicKey(hex: string): Uint8Array;
|
|
118
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMrC,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,SAAS,EAAI,UAAU,CAAC;IACxB,mDAAmD;IACnD,UAAU,EAAG,UAAU,CAAC;CACzB;AAMD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,wCAAwC,CAAC;AAE5E;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,iBAAiB,CAOtE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,CAEtE;AAMD;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,UAAU,CAEjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAM,UAAU,EACzB,YAAY,EAAG,UAAU,GACxB,UAAU,CAKZ;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAW,UAAU,EACzB,YAAY,EAAG,UAAU,GACxB,UAAU,CAMZ;AAMD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAS,UAAU,EAC/B,kBAAkB,EAAG,UAAU,EAC/B,gBAAgB,EAAK,UAAU,GAC9B,UAAU,CAKZ;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAgB,UAAU,EAC9B,eAAe,EAAK,UAAU,EAC9B,gBAAgB,EAAI,UAAU,GAC7B,UAAU,CAMZ;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAG,UAAU,EACzB,OAAO,EAAQ,iBAAiB,GAC/B,UAAU,CAEZ;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAM,UAAU,EACpB,OAAO,EAAG,iBAAiB,GAC1B,UAAU,CAEZ;AAMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAKtD"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file crypto.ts
|
|
3
|
+
* @notice Off-chain encryption layer for Web3QL.
|
|
4
|
+
*
|
|
5
|
+
* Security model:
|
|
6
|
+
* ─────────────────────────────────────────────────────────────
|
|
7
|
+
* • One random 32-byte symmetric key is generated per record.
|
|
8
|
+
* • Record data is encrypted with that key using NaCl secretbox
|
|
9
|
+
* (XSalsa20-Poly1305, 256-bit key, 192-bit nonce, 128-bit MAC).
|
|
10
|
+
* • The symmetric key is encrypted separately for each authorised
|
|
11
|
+
* user using NaCl box (X25519-XSalsa20-Poly1305, ECDH key agreement).
|
|
12
|
+
* • Only the encrypted blobs ever touch the chain — the symmetric key
|
|
13
|
+
* and plaintext never leave the client device.
|
|
14
|
+
*
|
|
15
|
+
* Key derivation from Ethereum wallet:
|
|
16
|
+
* ─────────────────────────────────────────────────────────────
|
|
17
|
+
* X25519 private key = SHA-256(Ethereum private key bytes)
|
|
18
|
+
* X25519 public key = scalar multiplication (computed by nacl)
|
|
19
|
+
*
|
|
20
|
+
* This means no separate key management — the wallet IS the
|
|
21
|
+
* encryption identity. Losing the wallet private key = losing
|
|
22
|
+
* access to encrypted records (same as losing any crypto key).
|
|
23
|
+
*
|
|
24
|
+
* Wire format (self-describing, no external framing needed):
|
|
25
|
+
* ─────────────────────────────────────────────────────────────
|
|
26
|
+
* secretbox blob: [ 24-byte nonce | encrypted payload ]
|
|
27
|
+
* box blob: [ 24-byte nonce | encrypted payload ]
|
|
28
|
+
*/
|
|
29
|
+
import nacl from 'tweetnacl';
|
|
30
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
31
|
+
import { hexToBytes } from '@noble/hashes/utils';
|
|
32
|
+
// ─────────────────────────────────────────────────────────────
|
|
33
|
+
// Keypair derivation
|
|
34
|
+
// ─────────────────────────────────────────────────────────────
|
|
35
|
+
/** Message signed by the wallet to derive the X25519 keypair.
|
|
36
|
+
* Must be identical to the constant in cloud/lib/browser-crypto.ts so
|
|
37
|
+
* browser and SDK produce the same keypair for the same wallet.
|
|
38
|
+
*/
|
|
39
|
+
export const KEY_DERIVATION_MESSAGE = 'Web3QL encryption key derivation v1';
|
|
40
|
+
/**
|
|
41
|
+
* Derive an X25519 keypair from an ethers Signer by signing a fixed
|
|
42
|
+
* derivation message. Because Ethereum personal_sign (RFC 6979) is
|
|
43
|
+
* deterministic, the same wallet always produces the same keypair.
|
|
44
|
+
*
|
|
45
|
+
* ✅ Use this when both SDK and browser clients need to share the same
|
|
46
|
+
* encryption identity — the resulting pubkey matches what the browser
|
|
47
|
+
* derives via `deriveKeypairFromSignature` in browser-crypto.ts.
|
|
48
|
+
*
|
|
49
|
+
* @param signer Any ethers v6 Signer (e.g. `new ethers.Wallet(privKey)`).
|
|
50
|
+
*/
|
|
51
|
+
export async function deriveKeypairFromWallet(signer) {
|
|
52
|
+
const sig = await signer.signMessage(KEY_DERIVATION_MESSAGE);
|
|
53
|
+
const sigHex = sig.startsWith('0x') ? sig.slice(2) : sig;
|
|
54
|
+
const seed = sha256(hexToBytes(sigHex));
|
|
55
|
+
const kp = nacl.box.keyPair.fromSecretKey(seed);
|
|
56
|
+
return { publicKey: kp.publicKey, privateKey: kp.secretKey };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Derive an X25519 encryption keypair from an Ethereum private key.
|
|
60
|
+
*
|
|
61
|
+
* @deprecated Prefer `deriveKeypairFromWallet(signer)` — it produces a
|
|
62
|
+
* keypair that is compatible with the Web3QL browser Explorer and any
|
|
63
|
+
* other client that uses wallet-signature derivation. Raw-private-key
|
|
64
|
+
* derivation generates a DIFFERENT keypair, so records written with this
|
|
65
|
+
* function cannot be decrypted in the browser (and vice-versa).
|
|
66
|
+
*
|
|
67
|
+
* @param ethPrivateKey Hex string, with or without "0x" prefix.
|
|
68
|
+
*/
|
|
69
|
+
export function deriveKeypair(ethPrivateKey) {
|
|
70
|
+
const stripped = ethPrivateKey.startsWith('0x')
|
|
71
|
+
? ethPrivateKey.slice(2)
|
|
72
|
+
: ethPrivateKey;
|
|
73
|
+
const seed = sha256(hexToBytes(stripped));
|
|
74
|
+
const kp = nacl.box.keyPair.fromSecretKey(seed);
|
|
75
|
+
return { publicKey: kp.publicKey, privateKey: kp.secretKey };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Derive ONLY the public key (useful when you only have a signed
|
|
79
|
+
* message and want to register — not when you have the private key).
|
|
80
|
+
* Exposed for completeness; normally call deriveKeypair() directly.
|
|
81
|
+
*/
|
|
82
|
+
export function publicKeyFromPrivate(ethPrivateKey) {
|
|
83
|
+
return deriveKeypair(ethPrivateKey).publicKey;
|
|
84
|
+
}
|
|
85
|
+
// ─────────────────────────────────────────────────────────────
|
|
86
|
+
// Symmetric encryption (record data)
|
|
87
|
+
// ─────────────────────────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* Generate a random 32-byte symmetric key for a new record.
|
|
90
|
+
* Call this once per write — never reuse across records.
|
|
91
|
+
*/
|
|
92
|
+
export function generateSymmetricKey() {
|
|
93
|
+
return nacl.randomBytes(nacl.secretbox.keyLength); // 32 bytes
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Encrypt plaintext with a symmetric key.
|
|
97
|
+
* Wire format: [ 24-byte nonce | MAC+ciphertext ]
|
|
98
|
+
*/
|
|
99
|
+
export function encryptData(plaintext, symmetricKey) {
|
|
100
|
+
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
|
|
101
|
+
const encrypted = nacl.secretbox(plaintext, nonce, symmetricKey);
|
|
102
|
+
if (!encrypted)
|
|
103
|
+
throw new Error('encryptData: nacl.secretbox returned null');
|
|
104
|
+
return concat(nonce, encrypted);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Decrypt a secretbox blob (nonce||ciphertext) with a symmetric key.
|
|
108
|
+
* Throws if the MAC check fails (data tampered or wrong key).
|
|
109
|
+
*/
|
|
110
|
+
export function decryptData(blob, symmetricKey) {
|
|
111
|
+
const nonce = blob.subarray(0, nacl.secretbox.nonceLength);
|
|
112
|
+
const ciphertext = blob.subarray(nacl.secretbox.nonceLength);
|
|
113
|
+
const plaintext = nacl.secretbox.open(ciphertext, nonce, symmetricKey);
|
|
114
|
+
if (!plaintext)
|
|
115
|
+
throw new Error('decryptData: authentication failed — wrong key or tampered data');
|
|
116
|
+
return plaintext;
|
|
117
|
+
}
|
|
118
|
+
// ─────────────────────────────────────────────────────────────
|
|
119
|
+
// Asymmetric key wrapping (per-recipient symmetric key copies)
|
|
120
|
+
// ─────────────────────────────────────────────────────────────
|
|
121
|
+
/**
|
|
122
|
+
* Encrypt a symmetric key for a specific recipient.
|
|
123
|
+
*
|
|
124
|
+
* The caller (sharer) needs their own X25519 private key and the
|
|
125
|
+
* recipient's X25519 public key (registered on-chain in PublicKeyRegistry).
|
|
126
|
+
*
|
|
127
|
+
* Wire format: [ 24-byte nonce | MAC+ciphertext ]
|
|
128
|
+
*/
|
|
129
|
+
export function encryptKeyForRecipient(symmetricKey, recipientPublicKey, senderPrivateKey) {
|
|
130
|
+
const nonce = nacl.randomBytes(nacl.box.nonceLength);
|
|
131
|
+
const encrypted = nacl.box(symmetricKey, nonce, recipientPublicKey, senderPrivateKey);
|
|
132
|
+
if (!encrypted)
|
|
133
|
+
throw new Error('encryptKeyForRecipient: nacl.box returned null');
|
|
134
|
+
return concat(nonce, encrypted);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Decrypt a symmetric key that was encrypted for us.
|
|
138
|
+
* Throws if authentication fails.
|
|
139
|
+
*/
|
|
140
|
+
export function decryptKeyFromSender(blob, senderPublicKey, recipientPrivKey) {
|
|
141
|
+
const nonce = blob.subarray(0, nacl.box.nonceLength);
|
|
142
|
+
const ciphertext = blob.subarray(nacl.box.nonceLength);
|
|
143
|
+
const key = nacl.box.open(ciphertext, nonce, senderPublicKey, recipientPrivKey);
|
|
144
|
+
if (!key)
|
|
145
|
+
throw new Error('decryptKeyFromSender: authentication failed — wrong key or tampered data');
|
|
146
|
+
return key;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Encrypt the symmetric key to yourself — used on the initial write
|
|
150
|
+
* so the owner can always recover their own key.
|
|
151
|
+
*/
|
|
152
|
+
export function encryptKeyForSelf(symmetricKey, keypair) {
|
|
153
|
+
return encryptKeyForRecipient(symmetricKey, keypair.publicKey, keypair.privateKey);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Decrypt a symmetric key that was encrypted to yourself.
|
|
157
|
+
*/
|
|
158
|
+
export function decryptKeyForSelf(blob, keypair) {
|
|
159
|
+
return decryptKeyFromSender(blob, keypair.publicKey, keypair.privateKey);
|
|
160
|
+
}
|
|
161
|
+
// ─────────────────────────────────────────────────────────────
|
|
162
|
+
// Encode/decode public key for on-chain storage (bytes32)
|
|
163
|
+
// ─────────────────────────────────────────────────────────────
|
|
164
|
+
/**
|
|
165
|
+
* Encode a 32-byte X25519 public key as a 0x-prefixed hex string
|
|
166
|
+
* suitable for passing to PublicKeyRegistry.register().
|
|
167
|
+
*/
|
|
168
|
+
export function publicKeyToHex(pubKey) {
|
|
169
|
+
if (pubKey.length !== 32)
|
|
170
|
+
throw new Error('publicKeyToHex: expected 32 bytes');
|
|
171
|
+
return '0x' + Buffer.from(pubKey).toString('hex');
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Decode a 0x-prefixed hex bytes32 from the registry back to Uint8Array.
|
|
175
|
+
*/
|
|
176
|
+
export function hexToPublicKey(hex) {
|
|
177
|
+
const stripped = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
178
|
+
const bytes = hexToBytes(stripped);
|
|
179
|
+
if (bytes.length !== 32)
|
|
180
|
+
throw new Error('hexToPublicKey: expected 32 hex bytes');
|
|
181
|
+
return bytes;
|
|
182
|
+
}
|
|
183
|
+
// ─────────────────────────────────────────────────────────────
|
|
184
|
+
// Utility
|
|
185
|
+
// ─────────────────────────────────────────────────────────────
|
|
186
|
+
function concat(a, b) {
|
|
187
|
+
const out = new Uint8Array(a.length + b.length);
|
|
188
|
+
out.set(a, 0);
|
|
189
|
+
out.set(b, a.length);
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file factory-client.ts
|
|
3
|
+
* @notice High-level client for the Web3QL Factory + Database contracts.
|
|
4
|
+
*
|
|
5
|
+
* Entry point for every Web3QL app:
|
|
6
|
+
* ─────────────────────────────────────────────────────────────
|
|
7
|
+
* const web3ql = new Web3QLClient(factoryAddress, signer, keypair, registryAddress);
|
|
8
|
+
*
|
|
9
|
+
* // Register your public key once (one-time, ~40k gas)
|
|
10
|
+
* await web3ql.register();
|
|
11
|
+
*
|
|
12
|
+
* // Create a personal database (or get existing)
|
|
13
|
+
* const db = await web3ql.getOrCreateDatabase();
|
|
14
|
+
*
|
|
15
|
+
* // Create a table
|
|
16
|
+
* const tableAddr = await db.createTable('users', schemaBytes);
|
|
17
|
+
*
|
|
18
|
+
* // Get an encrypted client for that table
|
|
19
|
+
* const users = db.table(tableAddr);
|
|
20
|
+
*
|
|
21
|
+
* // Write (auto-encrypts)
|
|
22
|
+
* await users.writeRaw(users.deriveKey('users', 1n), '{"name":"Alice"}');
|
|
23
|
+
*/
|
|
24
|
+
import { ethers } from 'ethers';
|
|
25
|
+
import { EncryptedTableClient } from './table-client.js';
|
|
26
|
+
import { PublicKeyRegistryClient } from './registry.js';
|
|
27
|
+
import type { EncryptionKeypair } from './crypto.js';
|
|
28
|
+
export declare class DatabaseClient {
|
|
29
|
+
readonly address: string;
|
|
30
|
+
private contract;
|
|
31
|
+
private signer;
|
|
32
|
+
private keypair;
|
|
33
|
+
constructor(address: string, signer: ethers.Signer, keypair: EncryptionKeypair);
|
|
34
|
+
/**
|
|
35
|
+
* Create a new encrypted table inside this database.
|
|
36
|
+
* @param name Human-readable table name (matches your schema).
|
|
37
|
+
* @param schemaBytes ABI-encoded schema from @web3ql/compiler's compileSchema().
|
|
38
|
+
* @returns Address of the deployed table proxy.
|
|
39
|
+
*/
|
|
40
|
+
createTable(name: string, schemaBytes: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Get the address of an existing table by name.
|
|
43
|
+
* Returns ethers.ZeroAddress if not found.
|
|
44
|
+
*/
|
|
45
|
+
getTable(name: string): Promise<string>;
|
|
46
|
+
/**
|
|
47
|
+
* List all table names in this database.
|
|
48
|
+
*/
|
|
49
|
+
listTables(): Promise<string[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Drop (remove from registry) a table by name.
|
|
52
|
+
* Records inside the table are NOT purged by this call — they remain
|
|
53
|
+
* as unreachable ciphertext. Use SchemaManager.dropTable() first to
|
|
54
|
+
* bulk-delete records if you want storage refunds.
|
|
55
|
+
*/
|
|
56
|
+
dropTable(name: string): Promise<ethers.TransactionReceipt>;
|
|
57
|
+
/**
|
|
58
|
+
* Get an EncryptedTableClient for a specific table address.
|
|
59
|
+
* Use this after createTable() or getTable() to read/write records.
|
|
60
|
+
*/
|
|
61
|
+
table(tableAddress: string): EncryptedTableClient;
|
|
62
|
+
}
|
|
63
|
+
export declare class Web3QLClient {
|
|
64
|
+
private factory;
|
|
65
|
+
private signer;
|
|
66
|
+
private keypair;
|
|
67
|
+
readonly registry: PublicKeyRegistryClient;
|
|
68
|
+
constructor(factoryAddress: string, signer: ethers.Signer, keypair: EncryptionKeypair, registryAddress: string);
|
|
69
|
+
/**
|
|
70
|
+
* Register the caller's encryption public key on-chain.
|
|
71
|
+
* Must be called once per address before anyone can share records
|
|
72
|
+
* with that address. Safe to call again — will overwrite.
|
|
73
|
+
*/
|
|
74
|
+
register(): Promise<ethers.TransactionReceipt>;
|
|
75
|
+
/**
|
|
76
|
+
* Check if an address has registered its public key.
|
|
77
|
+
*/
|
|
78
|
+
isRegistered(address: string): Promise<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Create a new personal database for the calling wallet.
|
|
81
|
+
* Most users only ever need one database — use getOrCreateDatabase().
|
|
82
|
+
* @param name Human-readable label stored immutably on-chain.
|
|
83
|
+
*/
|
|
84
|
+
createDatabase(name?: string): Promise<DatabaseClient>;
|
|
85
|
+
/**
|
|
86
|
+
* Get all database proxies owned by a user.
|
|
87
|
+
*/
|
|
88
|
+
getDatabases(owner?: string): Promise<string[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Return the first existing database for the caller, or create one
|
|
91
|
+
* if none exists. Idempotent — safe to call on every app startup.
|
|
92
|
+
* @param name Passed to createDatabase() only when a new one is created.
|
|
93
|
+
*/
|
|
94
|
+
getOrCreateDatabase(name?: string): Promise<DatabaseClient>;
|
|
95
|
+
/**
|
|
96
|
+
* Wrap an already-known database address (e.g. loaded from config).
|
|
97
|
+
*/
|
|
98
|
+
database(address: string): DatabaseClient;
|
|
99
|
+
/**
|
|
100
|
+
* Remove a database from the factory’s registry.
|
|
101
|
+
* The proxy contract is NOT destroyed — it remains on-chain.
|
|
102
|
+
* After calling this it will no longer appear in getDatabases().
|
|
103
|
+
*/
|
|
104
|
+
removeDatabase(dbAddress: string): Promise<ethers.TransactionReceipt>;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=factory-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory-client.d.ts","sourceRoot":"","sources":["../../src/factory-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAoC,QAAQ,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAsB,mBAAmB,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAmB,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAoB,aAAa,CAAC;AA4BnE,qBAAa,cAAc;IACzB,QAAQ,CAAC,OAAO,EAAG,MAAM,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAsB;gBAGnC,OAAO,EAAI,MAAM,EACjB,MAAM,EAAK,MAAM,CAAC,MAAM,EACxB,OAAO,EAAI,iBAAiB;IAQ9B;;;;;OAKG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBrE;;;OAGG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK7C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAKrC;;;;;OAKG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC;IAMjE;;;OAGG;IACH,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,oBAAoB;CAGlD;AAMD,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;gBAGzC,cAAc,EAAK,MAAM,EACzB,MAAM,EAAa,MAAM,CAAC,MAAM,EAChC,OAAO,EAAY,iBAAiB,EACpC,eAAe,EAAI,MAAM;IAQ3B;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC;IAIpD;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD;;;;OAIG;IACG,cAAc,CAAC,IAAI,GAAE,MAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAqBhE;;OAEG;IACG,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAMrD;;;;OAIG;IACG,mBAAmB,CAAC,IAAI,GAAE,MAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAQrE;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc;IAIzC;;;;OAIG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC;CAK5E"}
|