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/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
|
+
}
|