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
package/keyManager.js
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file keyManager.js
|
|
3
|
+
* @notice Key Generation & Management for Web3QL
|
|
4
|
+
*
|
|
5
|
+
* Implements three distinct key types:
|
|
6
|
+
* A. WALLET KEY — secp256k1 keypair used to sign blockchain transactions
|
|
7
|
+
* B. ENCRYPTION KEY — 32-byte symmetric key (NaCl secretbox) for record encryption
|
|
8
|
+
* C. ACCESS KEY — per-recipient key wrapper (NaCl box / X25519-XSalsa20-Poly1305)
|
|
9
|
+
*
|
|
10
|
+
* Wire formats (matching sdk/src/crypto.ts):
|
|
11
|
+
* secretbox blob: [ 24-byte nonce | ciphertext+MAC ]
|
|
12
|
+
* box blob: [ 24-byte nonce | encrypted key+MAC ]
|
|
13
|
+
*
|
|
14
|
+
* Security guarantees:
|
|
15
|
+
* ✓ All randomness from nacl.randomBytes — NEVER Math.random()
|
|
16
|
+
* ✓ Private keys / mnemonics are never logged or stored here
|
|
17
|
+
* ✓ NaCl secretbox (XSalsa20-Poly1305) — audited, side-channel resistant
|
|
18
|
+
* ✓ NaCl box (X25519-XSalsa20-Poly1305) for key wrapping
|
|
19
|
+
* ✓ X25519 keypair derived from Ethereum private key via SHA-256 seed
|
|
20
|
+
* (same derivation as sdk/src/crypto.ts deriveKeypair)
|
|
21
|
+
*
|
|
22
|
+
* Compatibility:
|
|
23
|
+
* Records encrypted via this module ARE cross-readable by sdk/src/crypto.ts
|
|
24
|
+
* because both use the same NaCl primitives and wire format.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { ethers } from 'ethers';
|
|
28
|
+
import nacl from 'tweetnacl';
|
|
29
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
30
|
+
|
|
31
|
+
// ─────────────────────────────────────────────────────────────
|
|
32
|
+
// Constants
|
|
33
|
+
// ─────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
const CELO_SEPOLIA_RPC = 'https://forno.celo-sepolia.celo-testnet.org'; // Celo Sepolia (chainId 11142220)
|
|
36
|
+
|
|
37
|
+
// ─────────────────────────────────────────────────────────────
|
|
38
|
+
// A. WALLET KEY
|
|
39
|
+
// ─────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generate a fresh random Ethereum wallet.
|
|
43
|
+
*
|
|
44
|
+
* ⚠ PRODUCTION WARNING — generateWallet() is for initial key provisioning only.
|
|
45
|
+
* The returned privateKey / mnemonic are shown ONCE and never stored here.
|
|
46
|
+
* Caller must persist them in a secure, encrypted keystore.
|
|
47
|
+
*
|
|
48
|
+
* @returns {{ address: string, privateKey: string, mnemonic: string|null, publicKey: string }}
|
|
49
|
+
*/
|
|
50
|
+
export function generateWallet() {
|
|
51
|
+
if (process.env.NODE_ENV === 'production') {
|
|
52
|
+
console.warn(
|
|
53
|
+
'[Web3QL KeyManager] WARNING: generateWallet() called in production. ' +
|
|
54
|
+
'The returned privateKey must be stored in a secure encrypted keystore ' +
|
|
55
|
+
'and MUST NOT be logged, committed, or sent over the network.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const wallet = ethers.Wallet.createRandom();
|
|
60
|
+
return {
|
|
61
|
+
address : wallet.address,
|
|
62
|
+
privateKey: wallet.privateKey,
|
|
63
|
+
mnemonic : wallet.mnemonic?.phrase ?? null,
|
|
64
|
+
publicKey : wallet.publicKey, // compressed secp256k1, 66-char hex
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Bind an existing private key to an ethers.Wallet connected to Celo RPC.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} privateKey 64-char hex (with or without "0x" prefix)
|
|
72
|
+
* @returns {ethers.Wallet} Wallet instance connected to Celo JSON-RPC
|
|
73
|
+
* @throws {Error} Descriptive error if key is invalid
|
|
74
|
+
*/
|
|
75
|
+
export function bindWallet(privateKey) {
|
|
76
|
+
if (!privateKey || typeof privateKey !== 'string') {
|
|
77
|
+
throw new Error('KeyManager.bindWallet: privateKey must be a non-empty string');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const stripped = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
|
|
81
|
+
|
|
82
|
+
if (stripped.length !== 64) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`KeyManager.bindWallet: privateKey must be 32 bytes (64 hex chars), got ${stripped.length} chars`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (!/^[0-9a-fA-F]{64}$/.test(stripped)) {
|
|
88
|
+
throw new Error('KeyManager.bindWallet: privateKey contains non-hex characters');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const provider = new ethers.JsonRpcProvider(
|
|
92
|
+
process.env.CELO_RPC_URL ?? CELO_SEPOLIA_RPC,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
return new ethers.Wallet(`0x${stripped}`, provider);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
throw new Error(`KeyManager.bindWallet: invalid private key — ${err.message}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─────────────────────────────────────────────────────────────
|
|
103
|
+
// B. ENCRYPTION KEY (symmetric, per-record)
|
|
104
|
+
// ─────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generate a cryptographically secure 32-byte symmetric encryption key.
|
|
108
|
+
* Uses nacl.randomBytes — always CSPRNG.
|
|
109
|
+
*
|
|
110
|
+
* @returns {string} 64-char lowercase hex string
|
|
111
|
+
*/
|
|
112
|
+
export function generateEncryptionKey() {
|
|
113
|
+
return Buffer.from(nacl.randomBytes(32)).toString('hex');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─────────────────────────────────────────────────────────────
|
|
117
|
+
// C. RECORD ENCRYPTION / DECRYPTION (NaCl secretbox)
|
|
118
|
+
// ─────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Encrypt a plaintext string with NaCl secretbox (XSalsa20-Poly1305).
|
|
122
|
+
* Wire format: "0x" + hex( [ nonce(24B) | ciphertext+MAC ] )
|
|
123
|
+
* Compatible with encryptData() in sdk/src/crypto.ts.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} data UTF-8 plaintext to encrypt
|
|
126
|
+
* @param {string} encryptionKey 64-char hex symmetric key
|
|
127
|
+
* @returns {string} "0x"-prefixed hex blob
|
|
128
|
+
*/
|
|
129
|
+
export function encryptRecord(data, encryptionKey) {
|
|
130
|
+
if (typeof data !== 'string') {
|
|
131
|
+
throw new TypeError('KeyManager.encryptRecord: data must be a string');
|
|
132
|
+
}
|
|
133
|
+
const keyBuf = _parseHexKey(encryptionKey, 'encryptRecord');
|
|
134
|
+
const plaintext = new TextEncoder().encode(data);
|
|
135
|
+
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength);
|
|
136
|
+
const encrypted = nacl.secretbox(plaintext, nonce, keyBuf);
|
|
137
|
+
if (!encrypted) throw new Error('KeyManager.encryptRecord: nacl.secretbox returned null');
|
|
138
|
+
const blob = new Uint8Array(nonce.length + encrypted.length);
|
|
139
|
+
blob.set(nonce);
|
|
140
|
+
blob.set(encrypted, nonce.length);
|
|
141
|
+
return '0x' + Buffer.from(blob).toString('hex');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Decrypt a secretbox blob (from encryptRecord or the chain).
|
|
146
|
+
* Throws if MAC check fails (tampered data or wrong key).
|
|
147
|
+
* Compatible with decryptData() in sdk/src/crypto.ts.
|
|
148
|
+
*
|
|
149
|
+
* @param {string} ciphertextHex "0x"-prefixed hex blob
|
|
150
|
+
* @param {string} encryptionKey 64-char hex symmetric key
|
|
151
|
+
* @returns {string} Decrypted UTF-8 plaintext
|
|
152
|
+
*/
|
|
153
|
+
export function decryptRecord(ciphertextHex, encryptionKey) {
|
|
154
|
+
const keyBuf = _parseHexKey(encryptionKey, 'decryptRecord');
|
|
155
|
+
const clean = ciphertextHex.startsWith('0x') ? ciphertextHex.slice(2) : ciphertextHex;
|
|
156
|
+
const blob = new Uint8Array(Buffer.from(clean, 'hex'));
|
|
157
|
+
if (blob.length < nacl.secretbox.nonceLength) {
|
|
158
|
+
throw new Error('KeyManager.decryptRecord: blob too short to contain nonce');
|
|
159
|
+
}
|
|
160
|
+
const nonce = blob.subarray(0, nacl.secretbox.nonceLength);
|
|
161
|
+
const ct = blob.subarray(nacl.secretbox.nonceLength);
|
|
162
|
+
const plaintext = nacl.secretbox.open(ct, nonce, keyBuf);
|
|
163
|
+
if (!plaintext) {
|
|
164
|
+
throw new Error('KeyManager.decryptRecord: authentication failed — wrong key or tampered data');
|
|
165
|
+
}
|
|
166
|
+
return new TextDecoder().decode(plaintext);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ─────────────────────────────────────────────────────────────
|
|
170
|
+
// D. KEY WRAPPING (NaCl box — X25519-XSalsa20-Poly1305)
|
|
171
|
+
// ─────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Wrap (encrypt) an encryption key for a specific recipient.
|
|
175
|
+
* Uses NaCl box (X25519 ECDH + XSalsa20-Poly1305) — matches encryptKeyForRecipient()
|
|
176
|
+
* in sdk/src/crypto.ts and the on-chain PublicKeyRegistry scheme.
|
|
177
|
+
*
|
|
178
|
+
* @param {string} encryptionKey 64-char hex symmetric key to wrap
|
|
179
|
+
* @param {string} recipientX25519PubKey 64-char hex X25519 public key (from PublicKeyRegistry)
|
|
180
|
+
* @param {string} senderEthPrivateKey 64-char hex Ethereum private key
|
|
181
|
+
* (X25519 key is derived from it via SHA-256)
|
|
182
|
+
* @returns {string} "0x"-prefixed hex: [ nonce(24B) | encrypted+MAC(48B) ] = 72 bytes
|
|
183
|
+
*/
|
|
184
|
+
export function wrapKey(encryptionKey, recipientX25519PubKey, senderEthPrivateKey) {
|
|
185
|
+
const keyBuf = _parseHexKey(encryptionKey, 'wrapKey');
|
|
186
|
+
const recipPub = _parseX25519Key(recipientX25519PubKey, 'wrapKey');
|
|
187
|
+
const stripped = senderEthPrivateKey.startsWith('0x') ? senderEthPrivateKey.slice(2) : senderEthPrivateKey;
|
|
188
|
+
if (stripped.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(stripped)) {
|
|
189
|
+
throw new Error('KeyManager.wrapKey: senderEthPrivateKey must be 32 bytes (64 hex chars)');
|
|
190
|
+
}
|
|
191
|
+
const seed = sha256(new Uint8Array(Buffer.from(stripped, 'hex')));
|
|
192
|
+
const senderKp = nacl.box.keyPair.fromSecretKey(seed);
|
|
193
|
+
const nonce = nacl.randomBytes(nacl.box.nonceLength);
|
|
194
|
+
const encrypted = nacl.box(keyBuf, nonce, recipPub, senderKp.secretKey);
|
|
195
|
+
if (!encrypted) throw new Error('KeyManager.wrapKey: nacl.box returned null');
|
|
196
|
+
const blob = new Uint8Array(nonce.length + encrypted.length);
|
|
197
|
+
blob.set(nonce);
|
|
198
|
+
blob.set(encrypted, nonce.length);
|
|
199
|
+
return '0x' + Buffer.from(blob).toString('hex');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Unwrap (decrypt) a wrapped key.
|
|
204
|
+
* Compatible with decryptKeyFromSender() in sdk/src/crypto.ts.
|
|
205
|
+
*
|
|
206
|
+
* @param {string} wrappedKeyHex "0x"-prefixed hex from wrapKey()
|
|
207
|
+
* @param {string} senderX25519PubKey 64-char hex X25519 public key of the sender
|
|
208
|
+
* @param {string} recipientEthPrivateKey 64-char hex Ethereum private key of recipient
|
|
209
|
+
* (X25519 key is derived from it via SHA-256)
|
|
210
|
+
* @returns {string} 64-char hex symmetric key
|
|
211
|
+
*/
|
|
212
|
+
export function unwrapKey(wrappedKeyHex, senderX25519PubKey, recipientEthPrivateKey) {
|
|
213
|
+
const senderPub = _parseX25519Key(senderX25519PubKey, 'unwrapKey');
|
|
214
|
+
const stripped = recipientEthPrivateKey.startsWith('0x') ? recipientEthPrivateKey.slice(2) : recipientEthPrivateKey;
|
|
215
|
+
if (stripped.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(stripped)) {
|
|
216
|
+
throw new Error('KeyManager.unwrapKey: recipientEthPrivateKey must be 32 bytes (64 hex chars)');
|
|
217
|
+
}
|
|
218
|
+
const seed = sha256(new Uint8Array(Buffer.from(stripped, 'hex')));
|
|
219
|
+
const recipKp = nacl.box.keyPair.fromSecretKey(seed);
|
|
220
|
+
const clean = wrappedKeyHex.startsWith('0x') ? wrappedKeyHex.slice(2) : wrappedKeyHex;
|
|
221
|
+
const blob = new Uint8Array(Buffer.from(clean, 'hex'));
|
|
222
|
+
if (blob.length < nacl.box.nonceLength) {
|
|
223
|
+
throw new Error('KeyManager.unwrapKey: wrapped key blob too short');
|
|
224
|
+
}
|
|
225
|
+
const nonce = blob.subarray(0, nacl.box.nonceLength);
|
|
226
|
+
const ciphertext = blob.subarray(nacl.box.nonceLength);
|
|
227
|
+
const symKey = nacl.box.open(ciphertext, nonce, senderPub, recipKp.secretKey);
|
|
228
|
+
if (!symKey) {
|
|
229
|
+
throw new Error('KeyManager.unwrapKey: authentication failed — wrong key or tampered data');
|
|
230
|
+
}
|
|
231
|
+
return Buffer.from(symKey).toString('hex');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Rotate the key wrapper for a record.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} encryptedKey Current hex blob from wrapKey()
|
|
238
|
+
* @param {string} senderX25519PubKey X25519 public key of the original sender
|
|
239
|
+
* @param {string} recipientEthPrivateKey Recipient's Ethereum private key (derives X25519)
|
|
240
|
+
* @param {string} newRecipX25519PubKey New recipient's X25519 public key
|
|
241
|
+
* @param {string} newSenderEthPrivKey New sender's Ethereum private key (derives X25519)
|
|
242
|
+
* @returns {string} New hex blob for the new recipient
|
|
243
|
+
*/
|
|
244
|
+
export function rotateKey(encryptedKey, senderX25519PubKey, recipientEthPrivateKey, newRecipX25519PubKey, newSenderEthPrivKey) {
|
|
245
|
+
const rawKey = unwrapKey(encryptedKey, senderX25519PubKey, recipientEthPrivateKey);
|
|
246
|
+
return wrapKey(rawKey, newRecipX25519PubKey, newSenderEthPrivKey);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ─────────────────────────────────────────────────────────────
|
|
250
|
+
// Encoding helpers (for on-chain byte blobs)
|
|
251
|
+
// ─────────────────────────────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Decode a secretbox blob (from contract read()) into its components.
|
|
255
|
+
* NaCl wire format: [ nonce(24B) | ciphertext+MAC ]
|
|
256
|
+
*
|
|
257
|
+
* @param {string} hex "0x"-prefixed hex from contract read()
|
|
258
|
+
* @returns {{ nonce: string, ciphertext: string }}
|
|
259
|
+
*/
|
|
260
|
+
export function decodeCiphertextBlob(hex) {
|
|
261
|
+
const buf = Buffer.from(hex.startsWith('0x') ? hex.slice(2) : hex, 'hex');
|
|
262
|
+
if (buf.length < nacl.secretbox.nonceLength) {
|
|
263
|
+
throw new Error('KeyManager.decodeCiphertextBlob: blob too short to contain nonce');
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
nonce : buf.subarray(0, nacl.secretbox.nonceLength).toString('hex'),
|
|
267
|
+
ciphertext: buf.subarray(nacl.secretbox.nonceLength).toString('hex'),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Derive the X25519 public key corresponding to an Ethereum private key.
|
|
273
|
+
* Uses the same derivation as wrapKey/unwrapKey: SHA-256 of the private key bytes
|
|
274
|
+
* becomes the seed for nacl.box.keyPair.fromSecretKey().
|
|
275
|
+
*
|
|
276
|
+
* Callers use this to obtain the public key that should be registered in the
|
|
277
|
+
* PublicKeyRegistry and passed as `recipientX25519PubKey` to wrapKey().
|
|
278
|
+
*
|
|
279
|
+
* @param {string} ethPrivateKey 64-char hex Ethereum private key (with or without "0x")
|
|
280
|
+
* @returns {string} 64-char hex X25519 public key
|
|
281
|
+
*/
|
|
282
|
+
export function deriveX25519PublicKey(ethPrivateKey) {
|
|
283
|
+
const stripped = ethPrivateKey.startsWith('0x') ? ethPrivateKey.slice(2) : ethPrivateKey;
|
|
284
|
+
if (stripped.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(stripped)) {
|
|
285
|
+
throw new Error('KeyManager.deriveX25519PublicKey: ethPrivateKey must be 32 bytes (64 hex chars)');
|
|
286
|
+
}
|
|
287
|
+
const seed = sha256(new Uint8Array(Buffer.from(stripped, 'hex')));
|
|
288
|
+
const kp = nacl.box.keyPair.fromSecretKey(seed);
|
|
289
|
+
return Buffer.from(kp.publicKey).toString('hex');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Ensure a wrapped key hex string has a "0x" prefix for ethers/contract calls.
|
|
294
|
+
* @param {string} wrappedKeyHex hex from wrapKey()
|
|
295
|
+
* @returns {string}
|
|
296
|
+
*/
|
|
297
|
+
export function encodeWrappedKey(wrappedKeyHex) {
|
|
298
|
+
const clean = wrappedKeyHex.startsWith('0x') ? wrappedKeyHex.slice(2) : wrappedKeyHex;
|
|
299
|
+
return '0x' + clean;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Strip "0x" prefix from a wrapped key returned by getMyEncryptedKey().
|
|
304
|
+
* @param {string} hex "0x"-prefixed hex from contract
|
|
305
|
+
* @returns {string}
|
|
306
|
+
*/
|
|
307
|
+
export function decodeWrappedKey(hex) {
|
|
308
|
+
return hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ─────────────────────────────────────────────────────────────
|
|
312
|
+
// Private helpers
|
|
313
|
+
// ─────────────────────────────────────────────────────────────
|
|
314
|
+
|
|
315
|
+
function _parseHexKey(hex, fn) {
|
|
316
|
+
if (typeof hex !== 'string') {
|
|
317
|
+
throw new TypeError(`KeyManager.${fn}: key must be a string`);
|
|
318
|
+
}
|
|
319
|
+
const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
320
|
+
if (clean.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(clean)) {
|
|
321
|
+
throw new Error(`KeyManager.${fn}: key must be 32 bytes (64 hex chars), got ${clean.length}`);
|
|
322
|
+
}
|
|
323
|
+
return new Uint8Array(Buffer.from(clean, 'hex'));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function _parseX25519Key(hex, fn) {
|
|
327
|
+
if (typeof hex !== 'string') {
|
|
328
|
+
throw new TypeError(`KeyManager.${fn}: X25519 public key must be a string`);
|
|
329
|
+
}
|
|
330
|
+
const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
331
|
+
if (clean.length !== 64 || !/^[0-9a-fA-F]{64}$/.test(clean)) {
|
|
332
|
+
throw new Error(
|
|
333
|
+
`KeyManager.${fn}: X25519 public key must be 32 bytes (64 hex chars), got ${clean.length}`,
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
return new Uint8Array(Buffer.from(clean, 'hex'));
|
|
337
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web3ql-client",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Web3QL client SDK — end-to-end encrypted SQL-like storage on Celo. NaCl encryption, UUPS proxy contracts, full TypeScript. Includes CLI.",
|
|
5
|
+
"keywords": ["web3", "sql", "encrypted-storage", "celo", "nacl", "e2e-encryption", "cli"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/src/index.js",
|
|
9
|
+
"types": "./dist/src/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/src/index.js",
|
|
13
|
+
"types": "./dist/src/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"web3ql": "./dist/src/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"dev": "tsc --watch",
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"cli": "node ./dist/src/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@noble/hashes": "^1.4.0",
|
|
27
|
+
"@dotenvx/dotenvx": "^1.0.0",
|
|
28
|
+
"ethers": "^6.13.1",
|
|
29
|
+
"tweetnacl": "^1.0.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.14.10",
|
|
33
|
+
"typescript": "^5.5.3"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|