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.
Files changed (86) hide show
  1. package/README.md +66 -0
  2. package/contracts/PublicKeyRegistry.sol +87 -0
  3. package/dist/src/access.d.ts +176 -0
  4. package/dist/src/access.d.ts.map +1 -0
  5. package/dist/src/access.js +283 -0
  6. package/dist/src/access.js.map +1 -0
  7. package/dist/src/batch.d.ts +107 -0
  8. package/dist/src/batch.d.ts.map +1 -0
  9. package/dist/src/batch.js +188 -0
  10. package/dist/src/batch.js.map +1 -0
  11. package/dist/src/cli.d.ts +40 -0
  12. package/dist/src/cli.d.ts.map +1 -0
  13. package/dist/src/cli.js +361 -0
  14. package/dist/src/cli.js.map +1 -0
  15. package/dist/src/constraints.d.ts +126 -0
  16. package/dist/src/constraints.d.ts.map +1 -0
  17. package/dist/src/constraints.js +192 -0
  18. package/dist/src/constraints.js.map +1 -0
  19. package/dist/src/crypto.d.ts +118 -0
  20. package/dist/src/crypto.d.ts.map +1 -0
  21. package/dist/src/crypto.js +192 -0
  22. package/dist/src/crypto.js.map +1 -0
  23. package/dist/src/factory-client.d.ts +106 -0
  24. package/dist/src/factory-client.d.ts.map +1 -0
  25. package/dist/src/factory-client.js +202 -0
  26. package/dist/src/factory-client.js.map +1 -0
  27. package/dist/src/index-cache.d.ts +156 -0
  28. package/dist/src/index-cache.d.ts.map +1 -0
  29. package/dist/src/index-cache.js +265 -0
  30. package/dist/src/index-cache.js.map +1 -0
  31. package/dist/src/index.d.ts +60 -0
  32. package/dist/src/index.d.ts.map +1 -0
  33. package/dist/src/index.js +60 -0
  34. package/dist/src/index.js.map +1 -0
  35. package/dist/src/migrations.d.ts +114 -0
  36. package/dist/src/migrations.d.ts.map +1 -0
  37. package/dist/src/migrations.js +173 -0
  38. package/dist/src/migrations.js.map +1 -0
  39. package/dist/src/model.d.ts +198 -0
  40. package/dist/src/model.d.ts.map +1 -0
  41. package/dist/src/model.js +379 -0
  42. package/dist/src/model.js.map +1 -0
  43. package/dist/src/query.d.ts +155 -0
  44. package/dist/src/query.d.ts.map +1 -0
  45. package/dist/src/query.js +386 -0
  46. package/dist/src/query.js.map +1 -0
  47. package/dist/src/registry.d.ts +45 -0
  48. package/dist/src/registry.d.ts.map +1 -0
  49. package/dist/src/registry.js +80 -0
  50. package/dist/src/registry.js.map +1 -0
  51. package/dist/src/schema-manager.d.ts +109 -0
  52. package/dist/src/schema-manager.d.ts.map +1 -0
  53. package/dist/src/schema-manager.js +259 -0
  54. package/dist/src/schema-manager.js.map +1 -0
  55. package/dist/src/table-client.d.ts +156 -0
  56. package/dist/src/table-client.d.ts.map +1 -0
  57. package/dist/src/table-client.js +292 -0
  58. package/dist/src/table-client.js.map +1 -0
  59. package/dist/src/typed-table.d.ts +159 -0
  60. package/dist/src/typed-table.d.ts.map +1 -0
  61. package/dist/src/typed-table.js +246 -0
  62. package/dist/src/typed-table.js.map +1 -0
  63. package/dist/src/types.d.ts +48 -0
  64. package/dist/src/types.d.ts.map +1 -0
  65. package/dist/src/types.js +222 -0
  66. package/dist/src/types.js.map +1 -0
  67. package/keyManager.js +337 -0
  68. package/package.json +38 -0
  69. package/src/access.ts +421 -0
  70. package/src/batch.ts +259 -0
  71. package/src/cli.ts +349 -0
  72. package/src/constraints.ts +283 -0
  73. package/src/crypto.ts +239 -0
  74. package/src/factory-client.ts +237 -0
  75. package/src/index-cache.ts +351 -0
  76. package/src/index.ts +171 -0
  77. package/src/migrations.ts +215 -0
  78. package/src/model.ts +538 -0
  79. package/src/query.ts +508 -0
  80. package/src/registry.ts +100 -0
  81. package/src/schema-manager.ts +301 -0
  82. package/src/table-client.ts +393 -0
  83. package/src/typed-table.ts +340 -0
  84. package/src/types.ts +284 -0
  85. package/tsconfig.json +22 -0
  86. package/walletUtils.js +204 -0
package/walletUtils.js ADDED
@@ -0,0 +1,204 @@
1
+ /**
2
+ * @file walletUtils.js
3
+ * @notice Wallet balance, funding checks, and gas estimation for Web3QL.
4
+ *
5
+ * All RPC calls target the Celo network (mainnet or Sepolia testnet).
6
+ * Set CELO_RPC_URL and CELO_NETWORK env vars to override defaults.
7
+ *
8
+ * CELO_RPC_URL — JSON-RPC endpoint (default: Alfajores forno)
9
+ * CELO_NETWORK — "mainnet" | "testnet" (default: "testnet")
10
+ */
11
+
12
+ import { ethers } from 'ethers';
13
+
14
+ // ─────────────────────────────────────────────────────────────
15
+ // Config
16
+ // ─────────────────────────────────────────────────────────────
17
+
18
+ const CELO_SEPOLIA_RPC = 'https://forno.celo-sepolia.celo-testnet.org'; // Celo Sepolia (chainId 11142220)
19
+ const CELO_MAINNET_RPC = 'https://forno.celo.org';
20
+
21
+ /**
22
+ * Minimum balance required to write a record.
23
+ * 0.01 CELO covers gas for write() + potential SSTORE costs.
24
+ */
25
+ const MIN_BALANCE_CELO = '0.01';
26
+ const MIN_BALANCE_WEI = ethers.parseEther(MIN_BALANCE_CELO);
27
+
28
+ // ─────────────────────────────────────────────────────────────
29
+ // Provider factory
30
+ // ─────────────────────────────────────────────────────────────
31
+
32
+ function getProvider() {
33
+ const rpcUrl = process.env.CELO_RPC_URL
34
+ ?? (process.env.CELO_NETWORK === 'mainnet' ? CELO_MAINNET_RPC : CELO_SEPOLIA_RPC);
35
+ return new ethers.JsonRpcProvider(rpcUrl);
36
+ }
37
+
38
+ // ─────────────────────────────────────────────────────────────
39
+ // getWalletBalance
40
+ // ─────────────────────────────────────────────────────────────
41
+
42
+ /**
43
+ * Fetch the CELO balance of an address.
44
+ *
45
+ * @param {string} address EVM address (checksummed or lowercase)
46
+ * @returns {Promise<{
47
+ * address : string,
48
+ * balanceWei : string,
49
+ * balanceCELO: string,
50
+ * sufficient : boolean,
51
+ * }>}
52
+ */
53
+ export async function getWalletBalance(address) {
54
+ if (!ethers.isAddress(address)) {
55
+ throw new Error(`walletUtils.getWalletBalance: invalid address "${address}"`);
56
+ }
57
+
58
+ const provider = getProvider();
59
+ const balanceWei = await provider.getBalance(address);
60
+
61
+ return {
62
+ address : ethers.getAddress(address), // normalize to checksum
63
+ balanceWei : balanceWei.toString(),
64
+ balanceCELO : ethers.formatEther(balanceWei),
65
+ sufficient : balanceWei >= MIN_BALANCE_WEI,
66
+ };
67
+ }
68
+
69
+ // ─────────────────────────────────────────────────────────────
70
+ // estimateWriteCost
71
+ // ─────────────────────────────────────────────────────────────
72
+
73
+ /**
74
+ * Estimate the gas cost of a single write() call for a given data size.
75
+ *
76
+ * Gas model breakdown:
77
+ * - Base transaction: 21,000
78
+ * - SSTORE for RecordMeta: ~5 slots × 20,000 = 100,000
79
+ * - SSTORE for keys array: ~20,000
80
+ * - Calldata (non-zero): per-byte cost via block base fee
81
+ * - Encrypted key blob: 125 bytes fixed overhead
82
+ *
83
+ * Note: this is a conservative upper-bound estimate, not a simulation.
84
+ * For a precise estimate on a specific record, call provider.estimateGas()
85
+ * directly using the signer.
86
+ *
87
+ * @param {number} dataBytes Length in bytes of the plaintext (pre-encryption)
88
+ * @returns {Promise<{
89
+ * estimatedGas : string,
90
+ * estimatedCELO: string,
91
+ * estimatedUSD : string,
92
+ * }>}
93
+ */
94
+ export async function estimateWriteCost(dataBytes) {
95
+ if (typeof dataBytes !== 'number' || dataBytes < 0) {
96
+ throw new Error('walletUtils.estimateWriteCost: dataBytes must be a non-negative number');
97
+ }
98
+
99
+ const provider = getProvider();
100
+ const feeData = await provider.getFeeData();
101
+ const gasPrice = feeData.gasPrice ?? feeData.maxFeePerGas;
102
+ if (!gasPrice) {
103
+ throw new Error('walletUtils.estimateWriteCost: could not retrieve gas price from provider');
104
+ }
105
+
106
+ // AES-GCM adds 28 bytes overhead (12 IV + 16 authTag); encrypted key blob is 125 bytes
107
+ const ciphertextBytes = dataBytes + 28;
108
+ const encryptedKeyBytes = 125;
109
+
110
+ // Calldata cost: ~68 gas per non-zero byte (worst-case conservative estimate)
111
+ const calldataBytes = 32n // bytes32 key arg
112
+ + 32n // ciphertext offset
113
+ + 32n // ciphertext length
114
+ + BigInt(ciphertextBytes)
115
+ + 32n // encryptedKey offset
116
+ + 32n // encryptedKey length
117
+ + BigInt(encryptedKeyBytes);
118
+ const calldataGas = calldataBytes * 68n;
119
+
120
+ const storageGas = 21_000n + 100_000n + 20_000n; // base + metadata + key slot
121
+ const estimatedGas = storageGas + calldataGas;
122
+
123
+ const estimatedWei = estimatedGas * gasPrice;
124
+ const estimatedCELO = ethers.formatEther(estimatedWei);
125
+
126
+ // Approximate CELO/USD price — replace with a price oracle in production
127
+ const celoUsdPrice = Number(process.env.CELO_USD_PRICE ?? '0.58');
128
+ const estimatedUSD = (parseFloat(estimatedCELO) * celoUsdPrice).toFixed(6);
129
+
130
+ return {
131
+ estimatedGas : estimatedGas.toString(),
132
+ estimatedCELO,
133
+ estimatedUSD : `$${estimatedUSD}`,
134
+ };
135
+ }
136
+
137
+ // ─────────────────────────────────────────────────────────────
138
+ // fundingGuide
139
+ // ─────────────────────────────────────────────────────────────
140
+
141
+ /**
142
+ * Return a structured funding guide when a wallet's balance is insufficient.
143
+ *
144
+ * @param {string} address EVM address to check
145
+ * @returns {Promise<{
146
+ * address : string,
147
+ * required : string,
148
+ * current : string,
149
+ * sufficient : boolean,
150
+ * message : string,
151
+ * instructions: string,
152
+ * faucet? : string,
153
+ * }>}
154
+ */
155
+ export async function fundingGuide(address) {
156
+ if (!ethers.isAddress(address)) {
157
+ throw new Error(`walletUtils.fundingGuide: invalid address "${address}"`);
158
+ }
159
+
160
+ const { balanceCELO, sufficient } = await getWalletBalance(address);
161
+ const isTestnet = (process.env.CELO_NETWORK ?? 'testnet') !== 'mainnet';
162
+ const normalized = ethers.getAddress(address);
163
+
164
+ const guide = {
165
+ address : normalized,
166
+ required : `${MIN_BALANCE_CELO} CELO`,
167
+ current : `${balanceCELO} CELO`,
168
+ sufficient,
169
+ message : sufficient
170
+ ? 'Wallet has sufficient balance for write operations.'
171
+ : 'Wallet balance is below the minimum required to write records on-chain.',
172
+ instructions: isTestnet
173
+ ? `1. Visit https://faucet.celo.org\n2. Paste your address: ${normalized}\n3. Request test CELO (takes ~5 seconds)`
174
+ : `Send at least ${MIN_BALANCE_CELO} CELO to ${normalized} from a funded wallet.`,
175
+ };
176
+
177
+ if (isTestnet) {
178
+ guide.faucet = 'https://faucet.celo.org';
179
+ }
180
+
181
+ return guide;
182
+ }
183
+
184
+ // ─────────────────────────────────────────────────────────────
185
+ // requireSufficientBalance (convenience guard used by connector)
186
+ // ─────────────────────────────────────────────────────────────
187
+
188
+ /**
189
+ * Throws a structured funding error if the address lacks minimum balance.
190
+ * Used as a preflight guard in write / update endpoints.
191
+ *
192
+ * @param {string} address
193
+ * @throws {{ code: 'INSUFFICIENT_BALANCE', fundingGuide: object }}
194
+ */
195
+ export async function requireSufficientBalance(address) {
196
+ const balance = await getWalletBalance(address);
197
+ if (!balance.sufficient) {
198
+ const guide = await fundingGuide(address);
199
+ const err = new Error(`Insufficient balance: ${balance.balanceCELO} CELO (need ${MIN_BALANCE_CELO})`);
200
+ err.code = 'INSUFFICIENT_BALANCE';
201
+ err.fundingGuide = guide;
202
+ throw err;
203
+ }
204
+ }