@quaivault/sdk 0.1.1 → 0.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/dist/index.cjs +614 -199
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +346 -28
- package/dist/index.d.ts +346 -28
- package/dist/index.js +616 -201
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as quais from 'quais';
|
|
2
2
|
import { Provider, Signer, Contract, Interface } from 'quais';
|
|
3
3
|
export { Provider, Signer } from 'quais';
|
|
4
|
+
import * as node_worker_threads from 'node:worker_threads';
|
|
4
5
|
import * as _supabase_postgrest_js from '@supabase/postgrest-js';
|
|
5
|
-
import {
|
|
6
|
+
import { PostgrestClient } from '@supabase/postgrest-js';
|
|
7
|
+
import { RealtimeChannel } from '@supabase/realtime-js';
|
|
6
8
|
import { z } from 'zod';
|
|
7
9
|
export { Erc1155Abi, Erc20Abi, Erc721Abi, MultiSendCallOnlyAbi, QuaiVaultAbi, QuaiVaultFactoryAbi, QuaiVaultProxyAbi, QuaiVaultProxyBytecode, SocialRecoveryModuleAbi } from './abi/index.js';
|
|
8
10
|
|
|
@@ -62,7 +64,7 @@ interface NetworkConfig {
|
|
|
62
64
|
*/
|
|
63
65
|
type Consistency = 'auto' | 'indexed' | 'chain';
|
|
64
66
|
interface ClientOptions {
|
|
65
|
-
network?: NetworkConfig |
|
|
67
|
+
network?: NetworkConfig | NetworkName;
|
|
66
68
|
provider?: Provider;
|
|
67
69
|
signer?: Signer;
|
|
68
70
|
/** Private key for a local signer. Prefer the `QUAIVAULT_PRIVATE_KEY` env var. */
|
|
@@ -129,8 +131,16 @@ interface VaultTransaction {
|
|
|
129
131
|
value: bigint;
|
|
130
132
|
data: Hex;
|
|
131
133
|
proposer: Address;
|
|
132
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Unix seconds when the proposal was recorded on chain, or 0 when unknown.
|
|
136
|
+
*
|
|
137
|
+
* Only chain reads carry a timestamp: the vault stores one in the transaction
|
|
138
|
+
* struct, but the indexer records the *block* instead. On an indexed read this is 0
|
|
139
|
+
* and {@link proposedAtBlock} carries the position. Never render 0 as a date.
|
|
140
|
+
*/
|
|
133
141
|
proposedAt: number;
|
|
142
|
+
/** Block the proposal was recorded in. Only populated on indexer reads. */
|
|
143
|
+
proposedAtBlock?: number;
|
|
134
144
|
kind: TransactionKind;
|
|
135
145
|
decoded?: DecodedCall;
|
|
136
146
|
/** One-line human-readable description. */
|
|
@@ -283,8 +293,10 @@ interface RecoveryRequest {
|
|
|
283
293
|
expiration: number;
|
|
284
294
|
status: RecoveryStatus;
|
|
285
295
|
executed: boolean;
|
|
286
|
-
/**
|
|
287
|
-
|
|
296
|
+
/**
|
|
297
|
+
* Who initiated the recovery. Only populated on indexer reads — the module's struct
|
|
298
|
+
* does not retain it.
|
|
299
|
+
*/
|
|
288
300
|
initiator?: Address;
|
|
289
301
|
source: 'indexer' | 'chain';
|
|
290
302
|
}
|
|
@@ -302,7 +314,15 @@ interface Pagination {
|
|
|
302
314
|
}
|
|
303
315
|
interface Page<T> {
|
|
304
316
|
data: T[];
|
|
317
|
+
/**
|
|
318
|
+
* Approximate size of the full result set.
|
|
319
|
+
*
|
|
320
|
+
* Taken from the query planner rather than a full scan, so it is exact on small
|
|
321
|
+
* tables and an estimate on large ones. Use it to size a progress bar, not to decide
|
|
322
|
+
* whether to keep paging — {@link hasMore} is what answers that.
|
|
323
|
+
*/
|
|
305
324
|
total: number;
|
|
325
|
+
/** Whether another page exists. Always exact. */
|
|
306
326
|
hasMore: boolean;
|
|
307
327
|
}
|
|
308
328
|
interface IndexerHealth {
|
|
@@ -315,13 +335,6 @@ interface IndexerHealth {
|
|
|
315
335
|
error?: string;
|
|
316
336
|
}
|
|
317
337
|
|
|
318
|
-
/**
|
|
319
|
-
* Retry policy for transient RPC and indexer failures.
|
|
320
|
-
*
|
|
321
|
-
* Scoped deliberately to **reads**. Retrying a write risks broadcasting the same
|
|
322
|
-
* transaction twice — a resubmit that looks like a timeout to the client may already
|
|
323
|
-
* be in the mempool — so every write path in this SDK calls through unretried.
|
|
324
|
-
*/
|
|
325
338
|
interface RetryOptions {
|
|
326
339
|
/** Total attempts including the first. Default 3. */
|
|
327
340
|
maxAttempts?: number;
|
|
@@ -424,6 +437,13 @@ declare class Connection {
|
|
|
424
437
|
vault(address: Address, write?: boolean): Contract;
|
|
425
438
|
factory(address: Address, write?: boolean): Contract;
|
|
426
439
|
socialRecovery(address: Address, write?: boolean): Contract;
|
|
440
|
+
/**
|
|
441
|
+
* Read-only handle to a token contract.
|
|
442
|
+
*
|
|
443
|
+
* No write variant: the SDK never calls a token directly. Moving tokens goes
|
|
444
|
+
* through the vault's proposal flow, which encodes the transfer as calldata.
|
|
445
|
+
*/
|
|
446
|
+
token(address: Address, standard: 'ERC20' | 'ERC721' | 'ERC1155'): Contract;
|
|
427
447
|
}
|
|
428
448
|
|
|
429
449
|
interface MineSaltOptions {
|
|
@@ -462,9 +482,31 @@ interface ResolvedMineJob {
|
|
|
462
482
|
*/
|
|
463
483
|
declare const syncStrategy: MiningStrategy;
|
|
464
484
|
/**
|
|
465
|
-
* Node-only multi-core miner
|
|
466
|
-
*
|
|
485
|
+
* Node-only multi-core miner, degrading to {@link syncStrategy} whenever workers turn
|
|
486
|
+
* out not to be usable.
|
|
487
|
+
*
|
|
488
|
+
* Two distinct failures land here. `node:worker_threads` may not exist at all
|
|
489
|
+
* (browsers, restricted runtimes). Or it exists, but the worker cannot boot — the
|
|
490
|
+
* source below is inlined as a string and resolves `quais` with a bare `require`,
|
|
491
|
+
* which no bundler traces, so a packaged consumer's worker dies on its first line.
|
|
492
|
+
*
|
|
493
|
+
* Both retreat to the sync miner rather than failing, and that retreat is always safe:
|
|
494
|
+
* mining is pure computation over random salts with no side effects and no on-chain
|
|
495
|
+
* state, so re-running it in process yields an equally valid answer. Deployment
|
|
496
|
+
* getting slower is a far better outcome than deployment becoming impossible.
|
|
467
497
|
*/
|
|
498
|
+
interface WorkerRuntime {
|
|
499
|
+
Worker: typeof node_worker_threads.Worker;
|
|
500
|
+
parallelism: number;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Build the strategy over an injectable worker runtime.
|
|
504
|
+
*
|
|
505
|
+
* The injection point exists so the retreat path can be tested: a worker that dies on
|
|
506
|
+
* its first line is the single most likely failure in a packaged consumer, and a
|
|
507
|
+
* fallback nothing exercises is a fallback nobody can trust.
|
|
508
|
+
*/
|
|
509
|
+
declare function createWorkerThreadsStrategy(load?: () => Promise<WorkerRuntime | null>): MiningStrategy;
|
|
468
510
|
declare const workerThreadsStrategy: MiningStrategy;
|
|
469
511
|
/** Prefer worker_threads on Node; the sync miner degrades gracefully everywhere else. */
|
|
470
512
|
declare function defaultStrategy(): MiningStrategy;
|
|
@@ -541,13 +583,19 @@ declare class Factory {
|
|
|
541
583
|
}
|
|
542
584
|
|
|
543
585
|
/**
|
|
544
|
-
* The indexer uses one Postgres schema per network, chosen at runtime, so the
|
|
545
|
-
*
|
|
586
|
+
* The indexer uses one Postgres schema per network, chosen at runtime, so the client
|
|
587
|
+
* cannot be pinned to postgrest-js's default `"public"` schema generic.
|
|
546
588
|
*/
|
|
547
|
-
type
|
|
589
|
+
type AnyPostgrestClient = PostgrestClient<any, any, any>;
|
|
548
590
|
/**
|
|
549
591
|
* Thin wrapper over the indexer's Supabase project.
|
|
550
592
|
*
|
|
593
|
+
* Built on `@supabase/postgrest-js` and `@supabase/realtime-js` directly rather than
|
|
594
|
+
* the `@supabase/supabase-js` umbrella. The umbrella's constructor instantiates auth,
|
|
595
|
+
* storage and functions clients unconditionally, so no bundler can shake them out —
|
|
596
|
+
* and this SDK uses none of the three. Going direct costs 57 KB → 22 KB gzip, and the
|
|
597
|
+
* only thing given up is `createClient`'s URL and header wiring, reproduced below.
|
|
598
|
+
*
|
|
551
599
|
* The anon key is a public read-only credential: the indexer's RLS grants `anon`
|
|
552
600
|
* SELECT on every table and nothing else, with all writes reserved for
|
|
553
601
|
* `service_role`. It carries no authority beyond reading already-public chain data.
|
|
@@ -555,6 +603,7 @@ type AnySupabaseClient = SupabaseClient<any, any, any, any, any>;
|
|
|
555
603
|
declare class IndexerClient {
|
|
556
604
|
readonly config: IndexerConfig;
|
|
557
605
|
private readonly client;
|
|
606
|
+
private realtime;
|
|
558
607
|
private healthCache;
|
|
559
608
|
private inflightHealth;
|
|
560
609
|
/** Health results are reused for this long to keep read paths cheap. */
|
|
@@ -562,9 +611,21 @@ declare class IndexerClient {
|
|
|
562
611
|
constructor(config: IndexerConfig, options?: {
|
|
563
612
|
healthCacheMs?: number;
|
|
564
613
|
});
|
|
565
|
-
/**
|
|
566
|
-
get
|
|
614
|
+
/** The PostgREST client, for queries the SDK does not wrap. */
|
|
615
|
+
get rest(): AnyPostgrestClient;
|
|
567
616
|
from(table: string): _supabase_postgrest_js.PostgrestQueryBuilder<any, any, any, string, unknown>;
|
|
617
|
+
/**
|
|
618
|
+
* Realtime client, constructed on first use.
|
|
619
|
+
*
|
|
620
|
+
* Lazy because it opens a WebSocket and starts a heartbeat, and the large majority
|
|
621
|
+
* of SDK usage never calls `watch()`. Nothing here should cost a socket unless
|
|
622
|
+
* somebody asked to follow a vault.
|
|
623
|
+
*/
|
|
624
|
+
private realtimeClient;
|
|
625
|
+
/** Open a Realtime channel. See `watchVault`, which is what callers should use. */
|
|
626
|
+
channel(name: string): RealtimeChannel;
|
|
627
|
+
/** Close a channel opened by {@link channel}. */
|
|
628
|
+
removeChannel(channel: RealtimeChannel): Promise<void>;
|
|
568
629
|
/**
|
|
569
630
|
* Run a query, parse each row, and surface failures as `IndexerQueryError`.
|
|
570
631
|
*
|
|
@@ -575,15 +636,36 @@ declare class IndexerClient {
|
|
|
575
636
|
* multiplied attempts if it were ever made to work. Chain reads, which do reject,
|
|
576
637
|
* are retried in `src/chain/retry.ts`.
|
|
577
638
|
*/
|
|
578
|
-
select<S extends z.ZodTypeAny>(table: string, schema: S, build: (q: ReturnType<
|
|
639
|
+
select<S extends z.ZodTypeAny>(table: string, schema: S, build: (q: ReturnType<AnyPostgrestClient["from"]>) => PromiseLike<{
|
|
579
640
|
data: unknown[] | null;
|
|
580
641
|
error: {
|
|
581
642
|
message: string;
|
|
582
643
|
code?: string;
|
|
583
644
|
} | null;
|
|
584
645
|
}>): Promise<Array<z.infer<S>>>;
|
|
646
|
+
/**
|
|
647
|
+
* Paged select: an exact `hasMore`, an approximate `total`.
|
|
648
|
+
*
|
|
649
|
+
* Both halves of that are deliberate. An `exact` count makes Postgres scan every
|
|
650
|
+
* matching row on every page request, which is invisible on a young vault and
|
|
651
|
+
* quadratic on a busy one — so the count is `estimated`, cheap and good enough for
|
|
652
|
+
* "roughly how much history is there".
|
|
653
|
+
*
|
|
654
|
+
* But `hasMore` derived from an estimate would be a lie a caller can act on: a
|
|
655
|
+
* paging loop that stops early drops real rows. So callers request one row beyond
|
|
656
|
+
* the page and this trims it, which answers "is there another page" exactly, for
|
|
657
|
+
* the cost of a single extra row.
|
|
658
|
+
*/
|
|
659
|
+
selectPage<S extends z.ZodTypeAny>(table: string, schema: S, limit: number, build: (q: ReturnType<AnyPostgrestClient['from']>) => PromiseLike<{
|
|
660
|
+
data: unknown[] | null;
|
|
661
|
+
error: {
|
|
662
|
+
message: string;
|
|
663
|
+
code?: string;
|
|
664
|
+
} | null;
|
|
665
|
+
count?: number | null;
|
|
666
|
+
}>): Promise<Page<z.infer<S>>>;
|
|
585
667
|
/** Single-row variant. Returns null for PostgREST's "no rows" code. */
|
|
586
|
-
selectOne<S extends z.ZodTypeAny>(table: string, schema: S, build: (q: ReturnType<
|
|
668
|
+
selectOne<S extends z.ZodTypeAny>(table: string, schema: S, build: (q: ReturnType<AnyPostgrestClient["from"]>) => PromiseLike<{
|
|
587
669
|
data: unknown | null;
|
|
588
670
|
error: {
|
|
589
671
|
message: string;
|
|
@@ -1132,24 +1214,56 @@ declare class IndexerQueries {
|
|
|
1132
1214
|
vaultsForGuardian(guardian: Address, options?: Pagination): Promise<WalletRow[]>;
|
|
1133
1215
|
pendingTransactions(vault: Address, options?: Pagination): Promise<TransactionRow[]>;
|
|
1134
1216
|
transaction(vault: Address, txHash: string): Promise<TransactionRow | null>;
|
|
1217
|
+
/**
|
|
1218
|
+
* Several transactions by hash, chunked for the same reasons as
|
|
1219
|
+
* {@link activeConfirmationsBatch} — hashes are long, and they go in the query string.
|
|
1220
|
+
*
|
|
1221
|
+
* Rows come back in no particular order and hashes with no row are simply absent;
|
|
1222
|
+
* the caller matches them up.
|
|
1223
|
+
*/
|
|
1224
|
+
transactionsByHash(vault: Address, txHashes: string[]): Promise<TransactionRow[]>;
|
|
1135
1225
|
transactionHistory(vault: Address, options?: Pagination & {
|
|
1136
1226
|
status?: string[];
|
|
1137
1227
|
}): Promise<Page<TransactionRow>>;
|
|
1138
1228
|
/** All confirmations for one transaction, including revoked ones. */
|
|
1139
1229
|
confirmations(vault: Address, txHash: string): Promise<ConfirmationRow[]>;
|
|
1140
1230
|
/**
|
|
1141
|
-
* Active confirmations for many transactions in
|
|
1231
|
+
* Active confirmations for many transactions, in as few round trips as is safe.
|
|
1142
1232
|
*
|
|
1143
1233
|
* "Active" here means not revoked. It does NOT mean the confirming address is
|
|
1144
1234
|
* still an owner — the indexer does not deactivate confirmations when an owner is
|
|
1145
1235
|
* removed, while the contract invalidates them via approval epochs. Callers must
|
|
1146
1236
|
* intersect with the active owner set; `Vault` does this for you.
|
|
1237
|
+
*
|
|
1238
|
+
* Chunked rather than issued as one `in(...)`, because a single filter fails two
|
|
1239
|
+
* ways at scale. PostgREST reads filters from the query string, and each 32-byte
|
|
1240
|
+
* hash costs ~70 characters there — a full {@link MAX_LIMIT} page of hashes builds a
|
|
1241
|
+
* ~14 KB request line, past the 8 KB cap most reverse proxies apply by default. The
|
|
1242
|
+
* response is bounded too: {@link CONFIRMATION_CHUNK} transactions can carry at most
|
|
1243
|
+
* `chunk × MAX_OWNERS` rows, and the chunk is sized to keep that under the
|
|
1244
|
+
* `max-rows` ceiling PostgREST deployments commonly set, so a busy vault cannot be
|
|
1245
|
+
* silently short-served.
|
|
1147
1246
|
*/
|
|
1148
1247
|
activeConfirmationsBatch(vault: Address, txHashes: string[]): Promise<Map<string, ConfirmationRow[]>>;
|
|
1149
1248
|
modules(vault: Address): Promise<string[]>;
|
|
1150
1249
|
delegatecallTargets(vault: Address): Promise<string[]>;
|
|
1151
1250
|
deposits(vault: Address, options?: Pagination): Promise<Page<DepositRow>>;
|
|
1152
1251
|
tokenTransfers(vault: Address, options?: Pagination): Promise<Page<TokenTransferRow>>;
|
|
1252
|
+
/**
|
|
1253
|
+
* Scan up to `budget` of the most recent transfer rows, paging past {@link MAX_LIMIT}.
|
|
1254
|
+
*
|
|
1255
|
+
* `tokenTransfers` clamps a single request, so a caller asking for more than the cap
|
|
1256
|
+
* silently received a short page — and then computed "did I see everything?" from
|
|
1257
|
+
* that short page's `hasMore`, which reported truncation the caller had not actually
|
|
1258
|
+
* hit. Paging here keeps the caller's budget meaningful and makes the returned
|
|
1259
|
+
* `hasMore` mean what it says: rows exist beyond the budget that was scanned.
|
|
1260
|
+
*
|
|
1261
|
+
* Pages by offset over a descending order, so a transfer landing mid-scan can shift
|
|
1262
|
+
* rows by one. That is acceptable for token *discovery* — a duplicate collapses into
|
|
1263
|
+
* the same map key and a missed row costs at most one candidate that the next call
|
|
1264
|
+
* picks up. Do not reuse this for anything that must see each row exactly once.
|
|
1265
|
+
*/
|
|
1266
|
+
tokenTransferScan(vault: Address, budget: number): Promise<Page<TokenTransferRow>>;
|
|
1153
1267
|
tokens(addresses: string[]): Promise<TokenRow[]>;
|
|
1154
1268
|
signedMessages(vault: Address): Promise<SignedMessageRow[]>;
|
|
1155
1269
|
recoveryConfig(vault: Address): Promise<{
|
|
@@ -1183,6 +1297,14 @@ declare class RecoveryModule {
|
|
|
1183
1297
|
get address(): Address;
|
|
1184
1298
|
private contract;
|
|
1185
1299
|
private get vault();
|
|
1300
|
+
/**
|
|
1301
|
+
* The vault this module acts on, through the same retrying facade every other vault
|
|
1302
|
+
* read in the SDK uses. Recovery reaches into the vault for three checks — is the
|
|
1303
|
+
* module enabled, is the caller an owner, who are the current owners — and each of
|
|
1304
|
+
* them gates a write, so none of them should be the one read that silently skips
|
|
1305
|
+
* transient-failure handling.
|
|
1306
|
+
*/
|
|
1307
|
+
private vaultContract;
|
|
1186
1308
|
/** Whether the module is currently enabled on the vault. */
|
|
1187
1309
|
isEnabled(): Promise<boolean>;
|
|
1188
1310
|
config(): Promise<RecoveryConfig>;
|
|
@@ -1265,7 +1387,13 @@ declare class RecoveryModule {
|
|
|
1265
1387
|
interface TokenBalance {
|
|
1266
1388
|
token: Address;
|
|
1267
1389
|
standard: 'ERC20' | 'ERC721' | 'ERC1155';
|
|
1390
|
+
/**
|
|
1391
|
+
* Ticker as the token reports it — deployer-controlled, so passed through
|
|
1392
|
+
* {@link sanitizeText} and capped. Falls back to `???` when the token has none or
|
|
1393
|
+
* supplied nothing printable.
|
|
1394
|
+
*/
|
|
1268
1395
|
symbol: string;
|
|
1396
|
+
/** Display name. Same provenance and treatment as {@link symbol}. */
|
|
1269
1397
|
name: string;
|
|
1270
1398
|
decimals: number;
|
|
1271
1399
|
/** ERC20: raw units. ERC721: number held. ERC1155: total across ids. */
|
|
@@ -1306,12 +1434,22 @@ interface BalanceOptions {
|
|
|
1306
1434
|
/**
|
|
1307
1435
|
* How many recent transfer rows to scan when discovering tokens. Default 500.
|
|
1308
1436
|
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1437
|
+
* Scanned across as many indexer requests as it takes, so this is a real budget
|
|
1438
|
+
* rather than a single page size. A vault with more transfers than this may not
|
|
1439
|
+
* surface its oldest holdings, so raise it for long-lived vaults. Reported back in
|
|
1440
|
+
* {@link VaultBalances.truncated}.
|
|
1311
1441
|
*/
|
|
1312
1442
|
transferScanLimit?: number;
|
|
1313
1443
|
/** Cap on ERC721 ids ownership-checked per token. Default 100. */
|
|
1314
1444
|
maxTokenIdChecks?: number;
|
|
1445
|
+
/**
|
|
1446
|
+
* Ceiling on in-flight RPC reads. Default 8.
|
|
1447
|
+
*
|
|
1448
|
+
* Applied at both fan-out levels independently, so the true ceiling is the square
|
|
1449
|
+
* of this. Raise it against a private node; lower it if a shared endpoint is
|
|
1450
|
+
* rate-limiting you.
|
|
1451
|
+
*/
|
|
1452
|
+
concurrency?: number;
|
|
1315
1453
|
}
|
|
1316
1454
|
/**
|
|
1317
1455
|
* Aggregate a vault's holdings.
|
|
@@ -1404,8 +1542,10 @@ declare const interfaces: {
|
|
|
1404
1542
|
declare const MAX_EXECUTION_DELAY: number;
|
|
1405
1543
|
declare const MAX_OWNERS = 20;
|
|
1406
1544
|
declare const MAX_MODULES = 50;
|
|
1407
|
-
/** Head of the Zodiac module linked list. */
|
|
1545
|
+
/** Head of the Zodiac module linked list. Never valid as an owner, module or guardian. */
|
|
1408
1546
|
declare const SENTINEL_MODULES = "0x0000000000000000000000000000000000000001";
|
|
1547
|
+
/** Rejected everywhere an address is committed to a role. */
|
|
1548
|
+
declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
1409
1549
|
declare const selfCall: {
|
|
1410
1550
|
addOwner: (owner: Address) => Hex;
|
|
1411
1551
|
removeOwner: (owner: Address) => Hex;
|
|
@@ -1486,6 +1626,29 @@ interface VaultContext {
|
|
|
1486
1626
|
contracts: ContractAddresses;
|
|
1487
1627
|
consistency: Consistency;
|
|
1488
1628
|
maxIndexerLagBlocks: number;
|
|
1629
|
+
/** Set by {@link Vault.pinned}. Never consulted by a write precondition. */
|
|
1630
|
+
view?: VaultView;
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Owners and threshold, captured at one instant.
|
|
1634
|
+
*
|
|
1635
|
+
* The SDK deliberately holds no read cache: the owner set decides which approvals
|
|
1636
|
+
* count toward the threshold, and serving a stale one silently would reintroduce the
|
|
1637
|
+
* exact bug `buildTransaction` intersects confirmations to avoid. An implicit TTL
|
|
1638
|
+
* would make that failure invisible and intermittent.
|
|
1639
|
+
*
|
|
1640
|
+
* So the staleness window is the caller's to open, and to see. `capturedAt` is right
|
|
1641
|
+
* there in the object; a CLI rendering one screen can pin a view for that screen and
|
|
1642
|
+
* know precisely what it pinned.
|
|
1643
|
+
*/
|
|
1644
|
+
interface VaultView {
|
|
1645
|
+
owners: Address[];
|
|
1646
|
+
threshold: number;
|
|
1647
|
+
/** Indexer head when this was taken; absent if the snapshot came from chain. */
|
|
1648
|
+
indexedAtBlock?: number;
|
|
1649
|
+
/** Unix seconds. How stale the snapshot is allowed to get is the caller's call. */
|
|
1650
|
+
capturedAt: number;
|
|
1651
|
+
source: 'indexer' | 'chain';
|
|
1489
1652
|
}
|
|
1490
1653
|
/** A handle to one deployed vault. Cheap to construct; performs no I/O until used. */
|
|
1491
1654
|
declare class Vault {
|
|
@@ -1503,13 +1666,64 @@ declare class Vault {
|
|
|
1503
1666
|
*/
|
|
1504
1667
|
private useIndexer;
|
|
1505
1668
|
private requireQueries;
|
|
1669
|
+
/**
|
|
1670
|
+
* The indexer's head, for stamping onto records read from it.
|
|
1671
|
+
*
|
|
1672
|
+
* Reads the cached health result rather than querying `indexer_state` directly.
|
|
1673
|
+
* `useIndexer()` has just called `health()` on this same code path and the result is
|
|
1674
|
+
* cached, so this is free — whereas a `state()` call is a second round trip per
|
|
1675
|
+
* hydration, paid on every indexed read purely to fill one advisory field.
|
|
1676
|
+
*
|
|
1677
|
+
* Undefined when the indexer is not answering: a head of 0 would read as "indexed at
|
|
1678
|
+
* genesis" rather than "unknown".
|
|
1679
|
+
*/
|
|
1680
|
+
private indexerHead;
|
|
1506
1681
|
private contract;
|
|
1507
1682
|
/** Owners, threshold, timelock floor, nonce and balance. Always read from chain. */
|
|
1508
1683
|
info(): Promise<VaultInfo>;
|
|
1509
|
-
/**
|
|
1684
|
+
/**
|
|
1685
|
+
* Capture owners and threshold once, for reuse across a burst of reads.
|
|
1686
|
+
*
|
|
1687
|
+
* Pair with {@link pinned}:
|
|
1688
|
+
*
|
|
1689
|
+
* ```ts
|
|
1690
|
+
* const view = await vault.view();
|
|
1691
|
+
* const pinned = vault.pinned(view);
|
|
1692
|
+
* // every read below shares one owner/threshold read
|
|
1693
|
+
* const txs = await pinned.transactions(hashes);
|
|
1694
|
+
* ```
|
|
1695
|
+
*/
|
|
1696
|
+
view(): Promise<VaultView>;
|
|
1697
|
+
/**
|
|
1698
|
+
* A handle that answers {@link owners} and {@link threshold} from `view` instead of
|
|
1699
|
+
* re-reading them.
|
|
1700
|
+
*
|
|
1701
|
+
* Reads only. Write preconditions go through `chainOwners` / `chainThreshold`, which
|
|
1702
|
+
* bypass the snapshot by construction — pinning a view can never cause this SDK to
|
|
1703
|
+
* sign against a stale owner set. Everything else about the handle, including the
|
|
1704
|
+
* connection and the recovery module, is shared with the original.
|
|
1705
|
+
*/
|
|
1706
|
+
pinned(view: VaultView): Vault;
|
|
1707
|
+
/** Active owners. Prefers a pinned view, then the indexer, then chain. */
|
|
1510
1708
|
owners(): Promise<Address[]>;
|
|
1709
|
+
/**
|
|
1710
|
+
* Owners straight from the chain, bypassing the indexer entirely.
|
|
1711
|
+
*
|
|
1712
|
+
* {@link owners} prefers the indexer, which is right for display and wrong for
|
|
1713
|
+
* anything that gates a write. A lagging indexer would let the SDK build a proposal
|
|
1714
|
+
* against an owner set that no longer exists — admitting a `removeOwner` that drops
|
|
1715
|
+
* the vault below its threshold, or rejecting one that is perfectly valid. Every
|
|
1716
|
+
* propose-time precondition reads through here instead.
|
|
1717
|
+
*/
|
|
1718
|
+
private chainOwners;
|
|
1511
1719
|
isOwner(address: Address): Promise<boolean>;
|
|
1720
|
+
/** Approvals required to execute. Prefers a pinned view, then the indexer, then chain. */
|
|
1512
1721
|
threshold(): Promise<number>;
|
|
1722
|
+
/**
|
|
1723
|
+
* Threshold straight from the chain. The write-path counterpart to {@link threshold},
|
|
1724
|
+
* for the same reason {@link chainOwners} exists.
|
|
1725
|
+
*/
|
|
1726
|
+
private chainThreshold;
|
|
1513
1727
|
balance(): Promise<bigint>;
|
|
1514
1728
|
/**
|
|
1515
1729
|
* Enabled modules, in linked-list order.
|
|
@@ -1527,6 +1741,20 @@ declare class Vault {
|
|
|
1527
1741
|
/** The vault-transaction hash for a given call, at a given nonce. */
|
|
1528
1742
|
transactionHash(to: Address, value: bigint, data: Hex, nonce?: number): Promise<Bytes32>;
|
|
1529
1743
|
transaction(txHash: Bytes32): Promise<VaultTransaction>;
|
|
1744
|
+
/**
|
|
1745
|
+
* Several transactions at once, keyed by hash.
|
|
1746
|
+
*
|
|
1747
|
+
* The plural form exists because the singular one is expensive to loop: each
|
|
1748
|
+
* `transaction()` re-reads the owner set, the threshold and the indexer head, so
|
|
1749
|
+
* fetching fifty costs fifty times that. This resolves the whole set the way the
|
|
1750
|
+
* listing methods already do — one query for the rows, one owner/threshold read
|
|
1751
|
+
* shared across them, one batched confirmations query.
|
|
1752
|
+
*
|
|
1753
|
+
* Hashes the indexer does not have fall back to chain reads, bounded by a pool.
|
|
1754
|
+
* Hashes that exist nowhere are simply absent from the map rather than throwing:
|
|
1755
|
+
* one unknown hash should not lose the caller the other forty-nine.
|
|
1756
|
+
*/
|
|
1757
|
+
transactions(txHashes: Bytes32[]): Promise<Map<Bytes32, VaultTransaction>>;
|
|
1530
1758
|
pendingTransactions(options?: Pagination): Promise<VaultTransaction[]>;
|
|
1531
1759
|
transactionHistory(options?: Pagination & {
|
|
1532
1760
|
status?: string[];
|
|
@@ -1551,6 +1779,16 @@ declare class Vault {
|
|
|
1551
1779
|
* become available. Defaults to the connected signer's address.
|
|
1552
1780
|
*/
|
|
1553
1781
|
affordances(txHash: Bytes32, caller?: Address): Promise<Affordance[]>;
|
|
1782
|
+
/**
|
|
1783
|
+
* Shared body of {@link affordances}, taking the transaction either as a value or as
|
|
1784
|
+
* an in-flight promise.
|
|
1785
|
+
*
|
|
1786
|
+
* The promise form is the point: a caller that already holds the record (like
|
|
1787
|
+
* {@link describe}) passes it and skips a redundant fetch, while `affordances()`
|
|
1788
|
+
* passes the unawaited promise so the transaction read still overlaps the two
|
|
1789
|
+
* ownership probes rather than serialising behind them.
|
|
1790
|
+
*/
|
|
1791
|
+
private resolveAffordances;
|
|
1554
1792
|
/**
|
|
1555
1793
|
* Proposal builders.
|
|
1556
1794
|
*
|
|
@@ -1828,6 +2066,23 @@ declare const QuaiVault: {
|
|
|
1828
2066
|
connect: typeof connect;
|
|
1829
2067
|
};
|
|
1830
2068
|
|
|
2069
|
+
/**
|
|
2070
|
+
* Map with a bounded number of in-flight promises, preserving input order.
|
|
2071
|
+
*
|
|
2072
|
+
* A plain `Promise.all` over a fan-out the caller does not control is a liability:
|
|
2073
|
+
* `balances()` can reach `maxTokens × maxTokenIdChecks` — 5,000 with the defaults —
|
|
2074
|
+
* concurrent RPC reads from one call, and a batched transaction read fans out over
|
|
2075
|
+
* however many hashes the caller passed. Public endpoints rate-limit long before
|
|
2076
|
+
* either, and the failures come back as transient errors on reads that would each
|
|
2077
|
+
* have succeeded on their own.
|
|
2078
|
+
*
|
|
2079
|
+
* Rejections propagate: the first failure rejects the whole call, matching
|
|
2080
|
+
* `Promise.all`. Callers that want per-item tolerance catch inside `fn`.
|
|
2081
|
+
*/
|
|
2082
|
+
declare function mapPooled<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
2083
|
+
/** Default ceiling on concurrent chain reads issued by one SDK call. */
|
|
2084
|
+
declare const DEFAULT_CONCURRENCY = 8;
|
|
2085
|
+
|
|
1831
2086
|
/**
|
|
1832
2087
|
* Raw `Transaction` struct as returned by `QuaiVault.transactions(bytes32)`.
|
|
1833
2088
|
* Field names match the Solidity struct.
|
|
@@ -1923,6 +2178,25 @@ declare class FactoryContract {
|
|
|
1923
2178
|
registerWallet(wallet: Address): Promise<ContractTransactionResponse>;
|
|
1924
2179
|
}
|
|
1925
2180
|
|
|
2181
|
+
/**
|
|
2182
|
+
* Typed, retrying facade over an ERC20/721/1155 contract.
|
|
2183
|
+
*
|
|
2184
|
+
* Tokens are only ever read, never written to — the vault's own proposal flow is what
|
|
2185
|
+
* moves them — so there is no write half here and no signer involved.
|
|
2186
|
+
*/
|
|
2187
|
+
declare class TokenContract {
|
|
2188
|
+
readonly contract: Contract;
|
|
2189
|
+
private readonly retry;
|
|
2190
|
+
constructor(contract: Contract, retry?: RetryOptions);
|
|
2191
|
+
private read;
|
|
2192
|
+
/** ERC20 units held, or — for ERC721 — the number of tokens held. */
|
|
2193
|
+
balanceOf(owner: Address): Promise<bigint>;
|
|
2194
|
+
/** ERC721 only. Reverts for a burned or never-minted id. */
|
|
2195
|
+
ownerOf(tokenId: string | bigint): Promise<string>;
|
|
2196
|
+
/** ERC1155 only. One call for many ids, so it stays a single round trip. */
|
|
2197
|
+
balanceOfBatch(owners: Address[], ids: Array<string | bigint>): Promise<bigint[]>;
|
|
2198
|
+
}
|
|
2199
|
+
|
|
1926
2200
|
/** Raw `RecoveryConfig` struct from `getRecoveryConfig(address)`. */
|
|
1927
2201
|
interface RawRecoveryConfig {
|
|
1928
2202
|
guardians: string[];
|
|
@@ -2141,6 +2415,38 @@ declare function predictVaultAddress(args: {
|
|
|
2141
2415
|
*/
|
|
2142
2416
|
declare function shardPrefixOf(address: Address): string;
|
|
2143
2417
|
|
|
2418
|
+
/**
|
|
2419
|
+
* Display sanitisation for attacker-controlled strings.
|
|
2420
|
+
*
|
|
2421
|
+
* Two values reach a consumer's screen without ever having been vetted by anyone:
|
|
2422
|
+
*
|
|
2423
|
+
* - **Token metadata.** `symbol()` and `name()` are whatever the token's deployer
|
|
2424
|
+
* chose. Getting one indexed against a vault costs one transfer of one unit.
|
|
2425
|
+
* - **Revert reasons.** `Error(string)` data comes from the contract a vault called,
|
|
2426
|
+
* which for an external call is an arbitrary address the proposer picked.
|
|
2427
|
+
*
|
|
2428
|
+
* In a browser both are inert — the DOM escapes them. In a terminal they are not. An
|
|
2429
|
+
* ANSI escape can move the cursor up and overwrite lines the SDK already printed, so
|
|
2430
|
+
* a token named `"\x1b[2A\x1b[KAll checks passed"` can forge SDK output. Bidi
|
|
2431
|
+
* overrides can reverse a rendered address while leaving the underlying bytes intact,
|
|
2432
|
+
* which is the same trick that makes malicious source look benign.
|
|
2433
|
+
*
|
|
2434
|
+
* Sanitising here rather than in each consumer means a CLI, a TUI and a log pipeline
|
|
2435
|
+
* all inherit it, and none of them has to remember. Only human-facing fields are
|
|
2436
|
+
* scrubbed — machine-readable ones (`DecodedRevert.args`) keep the raw bytes, because
|
|
2437
|
+
* a consumer comparing against an expected value needs exactly what was on chain.
|
|
2438
|
+
*/
|
|
2439
|
+
/** Default cap. Long enough for any legitimate token name, short enough to not wrap. */
|
|
2440
|
+
declare const DEFAULT_TEXT_LIMIT = 128;
|
|
2441
|
+
/**
|
|
2442
|
+
* Strip control and direction-manipulating characters, collapse surrounding
|
|
2443
|
+
* whitespace, and cap the length.
|
|
2444
|
+
*
|
|
2445
|
+
* Returns `''` for a non-string or for input that was entirely unsafe, so callers can
|
|
2446
|
+
* `sanitizeText(x) || fallback`.
|
|
2447
|
+
*/
|
|
2448
|
+
declare function sanitizeText(value: unknown, maxLength?: number): string;
|
|
2449
|
+
|
|
2144
2450
|
/**
|
|
2145
2451
|
* Address validation for Quai's dual-ledger, sharded address space.
|
|
2146
2452
|
*
|
|
@@ -2189,7 +2495,7 @@ declare function assertQuaiAddress(value: unknown, label?: string): Address;
|
|
|
2189
2495
|
declare function assertQuaiAddresses(values: readonly unknown[], label: string): Address[];
|
|
2190
2496
|
|
|
2191
2497
|
/** Machine-readable error codes. Stable across releases. */
|
|
2192
|
-
type QuaiVaultErrorCode = 'CONFIG' | 'NOT_CONNECTED' | 'NO_SIGNER' | 'NO_INDEXER' | 'INDEXER_QUERY' | 'INDEXER_STALE' | 'VALIDATION' | 'NOT_FOUND' | 'PRECONDITION' | 'REVERT' | 'SALT_MINING' | 'STALE_PROPOSAL' | 'UNSUPPORTED';
|
|
2498
|
+
type QuaiVaultErrorCode = 'ABORTED' | 'CONFIG' | 'NOT_CONNECTED' | 'NO_SIGNER' | 'NO_INDEXER' | 'INDEXER_QUERY' | 'INDEXER_STALE' | 'VALIDATION' | 'NOT_FOUND' | 'PRECONDITION' | 'REVERT' | 'SALT_MINING' | 'STALE_PROPOSAL' | 'UNSUPPORTED';
|
|
2193
2499
|
declare class QuaiVaultError extends Error {
|
|
2194
2500
|
readonly code: QuaiVaultErrorCode;
|
|
2195
2501
|
/** What the caller can do about it, in plain language. */
|
|
@@ -2205,6 +2511,18 @@ declare class QuaiVaultError extends Error {
|
|
|
2205
2511
|
/** Structured form, for logs and machine consumers. */
|
|
2206
2512
|
toJSON(): Record<string, unknown>;
|
|
2207
2513
|
}
|
|
2514
|
+
/**
|
|
2515
|
+
* A caller-supplied `AbortSignal` fired.
|
|
2516
|
+
*
|
|
2517
|
+
* Every long-running operation the SDK exposes — retries, indexer polling, salt
|
|
2518
|
+
* mining, waiting for a timelock — accepts a signal, and each used to report abort
|
|
2519
|
+
* differently: a bare `Error('Aborted')` in two places, a `PreconditionError` in a
|
|
2520
|
+
* third. Consumers switching on `err.code` to distinguish "the user pressed Ctrl-C"
|
|
2521
|
+
* from "this genuinely failed" could not do so. They can now.
|
|
2522
|
+
*/
|
|
2523
|
+
declare class AbortError extends QuaiVaultError {
|
|
2524
|
+
constructor(operation?: string);
|
|
2525
|
+
}
|
|
2208
2526
|
declare class ConfigError extends QuaiVaultError {
|
|
2209
2527
|
constructor(message: string, remediation?: string);
|
|
2210
2528
|
}
|
|
@@ -2268,4 +2586,4 @@ declare function knownErrorSelectors(): Array<{
|
|
|
2268
2586
|
signature: string;
|
|
2269
2587
|
}>;
|
|
2270
2588
|
|
|
2271
|
-
export { type Address, type AddressCheck, type AddressLedger, type Affordance, type AffordanceBlocker, type AffordanceContext, type ApprovalRecord, type BalanceOptions, type BatchCall, type Bytes32, type ClientOptions, ConfigError, Connection, type Consistency, type ContractAddresses, type CreateProgress, type CreateVaultParams, type CreateVaultResult, type DecodeContext, type DecodeResult, type DecodedBatchCall, type DecodedCall, type DecodedRevert, type DryRunResult, ENV_VARS, type ExecuteOutcome, type ExecuteResult, Factory, type FactoryContext, FactoryContract, type Hex, IndexerClient, type IndexerConfig, type IndexerHealth, IndexerQueries, IndexerQueryError, MAX_EXECUTION_DELAY, MAX_MODULES, MAX_OWNERS, type MineSaltOptions, type MinedSalt, type MiningStrategy, type NetworkConfig, type NetworkName, NoIndexerError, NoSignerError, NotFoundError, Operation, type Page, type Pagination, PreconditionError, type ProposeOptions, type ProposeResult, QuaiVault, QuaiVaultClient, QuaiVaultError, type QuaiVaultErrorCode, type RawRecovery, type RawRecoveryConfig, type RawRecoveryState, type RawTransactionState, type RawTransactionStruct, type ReceiptLike, type RecoveryAction, type RecoveryAffordance, type RecoveryConfig, RecoveryContract, RecoveryModule, type RecoveryModuleContext, type RecoveryRequest, type RecoveryStatus, type ResolvedConfig, type RetryOptions, RevertError, SENTINEL_MODULES, SaltMiningError, StaleProposalError, type Subscription, type TokenBalance, type TransactionKind, type TransactionStatus, ValidationError, Vault, type VaultAction, type VaultBalances, type VaultContext, VaultContract, type VaultInfo, type VaultTransaction, type WatchEvent, type WatchOptions, type WatchTopic, allowedActions, assertQuaiAddress, assertQuaiAddresses, classifyExecution, computeAffordances, computeBytecodeHash, computeFullSalt, connect, decodeCall, decodeMultiSendPayload, decodeRevert, decodeRevertFromError, defaultStrategy, deriveRecoveryStatus, deriveStatus, encodeInitData, encodeMultiSend, encodeMultiSendPayload, executableAfterOf, extractProposedTxHash, schemas as indexerSchemas, inspectAddress, interfaces, isNetworkName, isTerminal, isTransient, isUsableQuaiAddress, knownErrorSelectors, loadBalances, mainnet, mineSalt, minimumExpiration, networks, nowSeconds, predictVaultAddress, recoveryCall, remediationFor, resolveConfig, selfCall, shardPrefixOf, syncStrategy, testnet, token as tokenCalls, watchVault, withRetry, workerThreadsStrategy };
|
|
2589
|
+
export { AbortError, type Address, type AddressCheck, type AddressLedger, type Affordance, type AffordanceBlocker, type AffordanceContext, type ApprovalRecord, type BalanceOptions, type BatchCall, type Bytes32, type ClientOptions, ConfigError, Connection, type Consistency, type ContractAddresses, type CreateProgress, type CreateVaultParams, type CreateVaultResult, DEFAULT_CONCURRENCY, DEFAULT_TEXT_LIMIT, type DecodeContext, type DecodeResult, type DecodedBatchCall, type DecodedCall, type DecodedRevert, type DryRunResult, ENV_VARS, type ExecuteOutcome, type ExecuteResult, Factory, type FactoryContext, FactoryContract, type Hex, IndexerClient, type IndexerConfig, type IndexerHealth, IndexerQueries, IndexerQueryError, MAX_EXECUTION_DELAY, MAX_MODULES, MAX_OWNERS, type MineSaltOptions, type MinedSalt, type MiningStrategy, type NetworkConfig, type NetworkName, NoIndexerError, NoSignerError, NotFoundError, Operation, type Page, type Pagination, PreconditionError, type ProposeOptions, type ProposeResult, QuaiVault, QuaiVaultClient, QuaiVaultError, type QuaiVaultErrorCode, type RawRecovery, type RawRecoveryConfig, type RawRecoveryState, type RawTransactionState, type RawTransactionStruct, type ReceiptLike, type RecoveryAction, type RecoveryAffordance, type RecoveryConfig, RecoveryContract, RecoveryModule, type RecoveryModuleContext, type RecoveryRequest, type RecoveryStatus, type ResolvedConfig, type RetryOptions, RevertError, SENTINEL_MODULES, SaltMiningError, StaleProposalError, type Subscription, type TokenBalance, TokenContract, type TransactionKind, type TransactionStatus, ValidationError, Vault, type VaultAction, type VaultBalances, type VaultContext, VaultContract, type VaultInfo, type VaultTransaction, type VaultView, type WatchEvent, type WatchOptions, type WatchTopic, type WorkerRuntime, ZERO_ADDRESS, allowedActions, assertQuaiAddress, assertQuaiAddresses, classifyExecution, computeAffordances, computeBytecodeHash, computeFullSalt, connect, createWorkerThreadsStrategy, decodeCall, decodeMultiSendPayload, decodeRevert, decodeRevertFromError, defaultStrategy, deriveRecoveryStatus, deriveStatus, encodeInitData, encodeMultiSend, encodeMultiSendPayload, executableAfterOf, extractProposedTxHash, schemas as indexerSchemas, inspectAddress, interfaces, isNetworkName, isTerminal, isTransient, isUsableQuaiAddress, knownErrorSelectors, loadBalances, mainnet, mapPooled, mineSalt, minimumExpiration, networks, nowSeconds, predictVaultAddress, recoveryCall, remediationFor, resolveConfig, sanitizeText, selfCall, shardPrefixOf, syncStrategy, testnet, token as tokenCalls, watchVault, withRetry, workerThreadsStrategy };
|