@anywalletkit/extension-kit 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +114 -2
- package/dist/index.cjs +2305 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1479 -0
- package/dist/index.d.ts +1479 -0
- package/dist/index.js +2282 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -3
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1479 @@
|
|
|
1
|
+
import { KeyringStorage, SecretStorage } from '@anywalletkit/keyring';
|
|
2
|
+
import { Quote, Swap2ExecutionPlan, CrossChainProvider, QuoteParams, IntentStatus } from '@anywalletkit/protocol-swap2-core';
|
|
3
|
+
|
|
4
|
+
/** A vault or session rule was broken. Messages are safe to show the user and never contain secrets. */
|
|
5
|
+
declare class VaultError extends Error {
|
|
6
|
+
readonly code: VaultErrorCode;
|
|
7
|
+
constructor(message: string, code: VaultErrorCode);
|
|
8
|
+
}
|
|
9
|
+
type VaultErrorCode =
|
|
10
|
+
/** The password did not open the vault (or the envelope was tampered with — AES-GCM cannot tell the two apart). */
|
|
11
|
+
'wrong-password'
|
|
12
|
+
/** `create` was called while a vault already exists — an existing wallet is never overwritten. */
|
|
13
|
+
| 'exists'
|
|
14
|
+
/** `unlock` / `getSecret` on a storage area that holds no vault. */
|
|
15
|
+
| 'missing'
|
|
16
|
+
/** The stored envelope has a version or shape this build does not read. */
|
|
17
|
+
| 'unsupported-envelope'
|
|
18
|
+
/** The session is locked: the unlocked key is gone (auto-lock, browser exit, explicit lock). */
|
|
19
|
+
| 'locked'
|
|
20
|
+
/** Bad input (empty or too-short password, bad key length, bad id). */
|
|
21
|
+
| 'invalid-input'
|
|
22
|
+
/** The stored envelope's KDF parameters are below the floor this build accepts (edited on disk). */
|
|
23
|
+
| 'weak-kdf';
|
|
24
|
+
|
|
25
|
+
/** PBKDF2-SHA256 iterations (v1 envelopes): OWASP 2023 floor for SHA-256; anything lower is refused. */
|
|
26
|
+
declare const VAULT_MIN_ITERATIONS = 600000;
|
|
27
|
+
declare const VAULT_DEFAULT_ITERATIONS = 600000;
|
|
28
|
+
/** scrypt for v2 envelopes: OWASP's recommended cost (128 MiB, ~1 s in a service worker). */
|
|
29
|
+
declare const VAULT_SCRYPT: {
|
|
30
|
+
readonly N: number;
|
|
31
|
+
readonly r: 8;
|
|
32
|
+
readonly p: 1;
|
|
33
|
+
};
|
|
34
|
+
/** The weakest scrypt an envelope may carry and still open (16 MiB). Tests use this; `create` uses {@link VAULT_SCRYPT}. */
|
|
35
|
+
declare const VAULT_MIN_SCRYPT: {
|
|
36
|
+
readonly N: number;
|
|
37
|
+
readonly r: 8;
|
|
38
|
+
readonly p: 1;
|
|
39
|
+
};
|
|
40
|
+
/** Shortest password `create` / `changePassword` accept. `unlock` takes whatever opens the vault. */
|
|
41
|
+
declare const VAULT_MIN_PASSWORD_LENGTH = 8;
|
|
42
|
+
interface SealedBox {
|
|
43
|
+
/** 96-bit AES-GCM nonce, base64. */
|
|
44
|
+
iv: string;
|
|
45
|
+
/** ciphertext ‖ 128-bit tag, base64. */
|
|
46
|
+
data: string;
|
|
47
|
+
}
|
|
48
|
+
interface VaultEnvelopeV1 {
|
|
49
|
+
v: 1;
|
|
50
|
+
kdf: {
|
|
51
|
+
name: 'PBKDF2-SHA256';
|
|
52
|
+
iterations: number;
|
|
53
|
+
salt: string;
|
|
54
|
+
};
|
|
55
|
+
/** The DEK, sealed under the password-derived key. AAD = version + kdf. */
|
|
56
|
+
dek: SealedBox;
|
|
57
|
+
/** One box per key source (keyring source id), sealed under the DEK. AAD `forgewallet-vault/secret/<id>`. */
|
|
58
|
+
secrets: Record<string, SealedBox>;
|
|
59
|
+
createdAt: number;
|
|
60
|
+
}
|
|
61
|
+
interface VaultPasskey {
|
|
62
|
+
/** WebAuthn credential id, base64url. */
|
|
63
|
+
id: string;
|
|
64
|
+
label: string;
|
|
65
|
+
createdAt: number;
|
|
66
|
+
/** The DEK sealed under HKDF(PRF output, salt = credential id). AAD = `forgewallet-vault/2/passkey/<id>`. */
|
|
67
|
+
dek: SealedBox;
|
|
68
|
+
/** The credential's public key (SPKI, base64) when the authenticator gave one — lets the worker verify assertions (passkey-per-signature). */
|
|
69
|
+
publicKey?: string;
|
|
70
|
+
/** COSE algorithm of `publicKey` (-7 = ES256). */
|
|
71
|
+
alg?: number;
|
|
72
|
+
}
|
|
73
|
+
interface VaultEnvelopeV2 {
|
|
74
|
+
v: 2;
|
|
75
|
+
kdf: {
|
|
76
|
+
name: 'scrypt';
|
|
77
|
+
N: number;
|
|
78
|
+
r: number;
|
|
79
|
+
p: number;
|
|
80
|
+
salt: string;
|
|
81
|
+
};
|
|
82
|
+
dek: SealedBox;
|
|
83
|
+
secrets: Record<string, SealedBox>;
|
|
84
|
+
passkeys: VaultPasskey[];
|
|
85
|
+
createdAt: number;
|
|
86
|
+
/** When a v1 envelope was re-sealed as v2. */
|
|
87
|
+
upgradedAt?: number;
|
|
88
|
+
}
|
|
89
|
+
type VaultEnvelope = VaultEnvelopeV1 | VaultEnvelopeV2;
|
|
90
|
+
/** Where the envelope lives. `chromeLocalVaultStorage()` for the extension, `memoryVaultStorage()` for tests. */
|
|
91
|
+
interface VaultStorage {
|
|
92
|
+
get(): Promise<VaultEnvelope | null>;
|
|
93
|
+
set(envelope: VaultEnvelope): Promise<void>;
|
|
94
|
+
clear(): Promise<void>;
|
|
95
|
+
}
|
|
96
|
+
interface VaultOptions {
|
|
97
|
+
storage: VaultStorage;
|
|
98
|
+
/** KDF for NEW envelopes and re-seals. Default scrypt (v2); `'pbkdf2'` writes v1 (tests, or a build that must stay FIPS-only). */
|
|
99
|
+
kdf?: 'scrypt' | 'pbkdf2';
|
|
100
|
+
/** PBKDF2 iterations for v1 envelopes (default and minimum {@link VAULT_DEFAULT_ITERATIONS}). */
|
|
101
|
+
iterations?: number;
|
|
102
|
+
/** scrypt cost for v2 envelopes (default {@link VAULT_SCRYPT}; not below {@link VAULT_MIN_SCRYPT}). */
|
|
103
|
+
scrypt?: {
|
|
104
|
+
N: number;
|
|
105
|
+
r: number;
|
|
106
|
+
p: number;
|
|
107
|
+
};
|
|
108
|
+
/** Progress of the scrypt derivation, 0..1 — the unlock screen's bar. */
|
|
109
|
+
onProgress?: (fraction: number) => void;
|
|
110
|
+
/** Defaults to `globalThis.crypto`; injectable for tests. */
|
|
111
|
+
crypto?: Crypto;
|
|
112
|
+
}
|
|
113
|
+
interface VaultKdfInfo {
|
|
114
|
+
version: 1 | 2;
|
|
115
|
+
name: 'PBKDF2-SHA256' | 'scrypt';
|
|
116
|
+
/** Human line for the UI, e.g. "scrypt N=2^17 r=8 p=1". */
|
|
117
|
+
summary: string;
|
|
118
|
+
}
|
|
119
|
+
declare class Vault {
|
|
120
|
+
private readonly _storage;
|
|
121
|
+
private readonly _kdf;
|
|
122
|
+
private readonly _iterations;
|
|
123
|
+
private readonly _scrypt;
|
|
124
|
+
private readonly _onProgress;
|
|
125
|
+
private readonly _crypto;
|
|
126
|
+
constructor(options: VaultOptions);
|
|
127
|
+
exists(): Promise<boolean>;
|
|
128
|
+
/** What protects the DEK right now (UI). `null` without a vault. */
|
|
129
|
+
kdfInfo(): Promise<VaultKdfInfo | null>;
|
|
130
|
+
/**
|
|
131
|
+
* Create an empty vault protected by `password`. Returns the DEK — hand it to the
|
|
132
|
+
* session, then `setSecret` the first seed. Refuses when a vault exists.
|
|
133
|
+
*/
|
|
134
|
+
create(password: string): Promise<Uint8Array>;
|
|
135
|
+
/**
|
|
136
|
+
* Derive the KEK from `password`, unseal and return the DEK. `wrong-password` on
|
|
137
|
+
* failure. A v1 (PBKDF2) envelope that opens is re-sealed as v2 (scrypt) on the
|
|
138
|
+
* way out, secrets untouched.
|
|
139
|
+
*/
|
|
140
|
+
unlock(password: string): Promise<Uint8Array>;
|
|
141
|
+
/** Re-wrap the DEK under a new password (fresh salt, current KDF). Secrets and passkeys are untouched. */
|
|
142
|
+
changePassword(oldPassword: string, newPassword: string): Promise<void>;
|
|
143
|
+
/** Public view of the registered passkeys (ids, labels; whether each can verify assertions). */
|
|
144
|
+
listPasskeys(): Promise<Array<{
|
|
145
|
+
id: string;
|
|
146
|
+
label: string;
|
|
147
|
+
createdAt: number;
|
|
148
|
+
canVerify: boolean;
|
|
149
|
+
}>>;
|
|
150
|
+
/** The SPKI public key (base64) of one passkey, for assertion verification; `null` when unknown. */
|
|
151
|
+
passkeyPublicKey(id: string): Promise<{
|
|
152
|
+
publicKey: string;
|
|
153
|
+
alg: number;
|
|
154
|
+
} | null>;
|
|
155
|
+
/**
|
|
156
|
+
* Register a passkey as a second unlock path: the DEK (from the unlocked session)
|
|
157
|
+
* is sealed under HKDF-SHA256(`prf`, salt = credential id). The password stays
|
|
158
|
+
* the recovery path; `prf` is zeroized here. A v1 envelope must be unlocked (and
|
|
159
|
+
* thereby upgraded) first.
|
|
160
|
+
*/
|
|
161
|
+
addPasskey(dek: Uint8Array, passkey: {
|
|
162
|
+
id: string;
|
|
163
|
+
prf: Uint8Array;
|
|
164
|
+
label: string;
|
|
165
|
+
publicKey?: string | undefined;
|
|
166
|
+
alg?: number | undefined;
|
|
167
|
+
}): Promise<void>;
|
|
168
|
+
/** Unseal the DEK with a passkey's PRF output. `wrong-password` when the box does not open, `missing` when the id is unknown. */
|
|
169
|
+
unlockWithPasskey(id: string, prf: Uint8Array): Promise<Uint8Array>;
|
|
170
|
+
removePasskey(id: string): Promise<void>;
|
|
171
|
+
listSecretIds(): Promise<string[]>;
|
|
172
|
+
/** Seal `secret` under the DEK as `sourceId` (overwrites an entry with the same id). */
|
|
173
|
+
setSecret(dek: Uint8Array, sourceId: string, secret: Uint8Array): Promise<void>;
|
|
174
|
+
/** Unseal one secret. `null` when the id is unknown; the caller zeroizes the result after use. */
|
|
175
|
+
getSecret(dek: Uint8Array, sourceId: string): Promise<Uint8Array | null>;
|
|
176
|
+
deleteSecret(sourceId: string): Promise<void>;
|
|
177
|
+
/** Remove the whole vault. The explicit destructive flow only (back up → wipe → create). */
|
|
178
|
+
destroy(): Promise<void>;
|
|
179
|
+
private _random;
|
|
180
|
+
/** A fresh envelope (v2 by default, v1 with `kdf: 'pbkdf2'`) wrapping `dek` under `password`. */
|
|
181
|
+
private _sealEnvelope;
|
|
182
|
+
/** The caller's DEK must open this envelope's secrets — it is the key the password protects, not any 32 bytes. */
|
|
183
|
+
private _requireDek;
|
|
184
|
+
private _derivePbkdf2;
|
|
185
|
+
private _deriveScrypt;
|
|
186
|
+
/** HKDF-SHA256(ikm = PRF output, salt = credential id bytes, info) → AES-256-GCM key. */
|
|
187
|
+
private _derivePasskeyKek;
|
|
188
|
+
private _importDek;
|
|
189
|
+
private _seal;
|
|
190
|
+
private _open;
|
|
191
|
+
private _load;
|
|
192
|
+
}
|
|
193
|
+
declare function isEnvelopeV1(x: unknown): x is VaultEnvelopeV1;
|
|
194
|
+
declare function isEnvelopeV2(x: unknown): x is VaultEnvelopeV2;
|
|
195
|
+
/** base64url (WebAuthn ids) → bytes; plain base64 is accepted too. */
|
|
196
|
+
declare function fromBase64Url(text: string): Uint8Array;
|
|
197
|
+
declare function toBase64Url(bytes: Uint8Array): string;
|
|
198
|
+
/** Envelope in memory — tests and the smoke. */
|
|
199
|
+
declare function memoryVaultStorage(initial?: VaultEnvelope | null): VaultStorage & {
|
|
200
|
+
envelope: VaultEnvelope | null;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/** Structural `chrome.storage.session` (or any `StorageArea`); `memorySessionStorage()` for tests. */
|
|
204
|
+
interface SessionStorage {
|
|
205
|
+
get(key: string): Promise<unknown>;
|
|
206
|
+
set(key: string, value: unknown): Promise<void>;
|
|
207
|
+
remove(key: string): Promise<void>;
|
|
208
|
+
}
|
|
209
|
+
/** Structural `chrome.alarms`: one named alarm, fired once at `when` (ms since epoch). */
|
|
210
|
+
interface SessionAlarms {
|
|
211
|
+
schedule(name: string, when: number): Promise<void> | void;
|
|
212
|
+
clear(name: string): Promise<void> | void;
|
|
213
|
+
/** Register the lock callback; the chrome adapter wires `chrome.alarms.onAlarm`. */
|
|
214
|
+
onFire(handler: (name: string) => void): void;
|
|
215
|
+
}
|
|
216
|
+
interface SessionOptions {
|
|
217
|
+
storage: SessionStorage;
|
|
218
|
+
alarms: SessionAlarms;
|
|
219
|
+
/** Lock this long after the last `touch` / `unlock`. Default 15 min; 0 disables the timer (lock only on browser exit or explicit lock). */
|
|
220
|
+
autoLockMs?: number;
|
|
221
|
+
/** Storage key and alarm name prefix. */
|
|
222
|
+
name?: string;
|
|
223
|
+
/** Clock, injectable for tests. */
|
|
224
|
+
now?: () => number;
|
|
225
|
+
}
|
|
226
|
+
declare const SESSION_DEFAULT_AUTOLOCK_MS: number;
|
|
227
|
+
declare class Session {
|
|
228
|
+
private readonly _storage;
|
|
229
|
+
private readonly _alarms;
|
|
230
|
+
private _autoLockMs;
|
|
231
|
+
private readonly _key;
|
|
232
|
+
private readonly _alarm;
|
|
233
|
+
private readonly _now;
|
|
234
|
+
private readonly _onLock;
|
|
235
|
+
constructor(options: SessionOptions);
|
|
236
|
+
/** Store the DEK and arm the auto-lock. The caller's copy is zeroized. */
|
|
237
|
+
unlock(dek: Uint8Array): Promise<void>;
|
|
238
|
+
/** Forget the DEK and cancel the alarm. Safe to call when already locked. */
|
|
239
|
+
lock(): Promise<void>;
|
|
240
|
+
isUnlocked(): Promise<boolean>;
|
|
241
|
+
/**
|
|
242
|
+
* The DEK for one operation (zeroize it after). `locked` when there is none or the
|
|
243
|
+
* timer ran out while the worker was asleep — the record is then removed as well, so
|
|
244
|
+
* a missed alarm cannot keep the key alive.
|
|
245
|
+
*/
|
|
246
|
+
getKey(): Promise<Uint8Array>;
|
|
247
|
+
/** User activity: push the auto-lock out again. No-op when locked. */
|
|
248
|
+
touch(): Promise<void>;
|
|
249
|
+
get autoLockMs(): number;
|
|
250
|
+
/** Change the timer (options page); re-arms from now when unlocked. 0 disables. */
|
|
251
|
+
setAutoLockMs(ms: number): Promise<void>;
|
|
252
|
+
onLock(callback: () => void): () => void;
|
|
253
|
+
private _expiry;
|
|
254
|
+
private _arm;
|
|
255
|
+
private _read;
|
|
256
|
+
}
|
|
257
|
+
/** In-memory `chrome.storage.session` stand-in. */
|
|
258
|
+
declare function memorySessionStorage(): SessionStorage & {
|
|
259
|
+
data: Map<string, unknown>;
|
|
260
|
+
};
|
|
261
|
+
/** In-memory alarms: `fire(name)` from the test, `pending` to inspect. */
|
|
262
|
+
declare function memoryAlarms(): SessionAlarms & {
|
|
263
|
+
pending: Map<string, number>;
|
|
264
|
+
fire(name: string): void;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Message shapes on the two hops between a dApp and the wallet:
|
|
269
|
+
*
|
|
270
|
+
* ```text
|
|
271
|
+
* page (injected provider, MAIN world)
|
|
272
|
+
* │ window.postMessage — PageRequest ⇄ PageResponse / PageEvent
|
|
273
|
+
* content script (isolated world) — stamps `location.origin`, owns the port
|
|
274
|
+
* │ chrome.runtime.connect(PORT_NAME) — RelayRequest ⇄ RelayResponse / RelayEvent
|
|
275
|
+
* service worker — trusts `port.sender` (set by the browser), never the page
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* The page can forge anything it posts, so a page message carries no origin and
|
|
279
|
+
* no identity: the content script adds the origin, and the service worker
|
|
280
|
+
* cross-checks it against `port.sender.origin` ({@link checkSenderOrigin}) — a
|
|
281
|
+
* mismatch is dropped, not answered. Request ids are opaque strings minted by
|
|
282
|
+
* the page side ({@link newRequestId}); the content script does not reuse them
|
|
283
|
+
* across ports.
|
|
284
|
+
*/
|
|
285
|
+
/** `chrome.runtime.connect({ name })` for content-script ports. */
|
|
286
|
+
declare const PORT_NAME = "anywalletkit/relay/1";
|
|
287
|
+
/** `window.postMessage` discriminators (both directions). */
|
|
288
|
+
declare const PAGE_TARGET: {
|
|
289
|
+
readonly toContent: "anywalletkit/content";
|
|
290
|
+
readonly toPage: "anywalletkit/page";
|
|
291
|
+
};
|
|
292
|
+
/** CAIP-2 namespace of the provider that spoke. */
|
|
293
|
+
type RelayChain = 'eip155' | 'solana' | 'ton' | 'tron' | 'bip122' | 'near';
|
|
294
|
+
interface RelayError {
|
|
295
|
+
/** EIP-1193 / EIP-1474 numbering for EVM; the same numbers are reused for the other providers. */
|
|
296
|
+
code: number;
|
|
297
|
+
message: string;
|
|
298
|
+
data?: unknown;
|
|
299
|
+
}
|
|
300
|
+
declare const RELAY_ERRORS: {
|
|
301
|
+
readonly userRejected: 4001;
|
|
302
|
+
readonly unauthorized: 4100;
|
|
303
|
+
readonly unsupportedMethod: 4200;
|
|
304
|
+
readonly disconnected: 4900;
|
|
305
|
+
readonly chainDisconnected: 4901;
|
|
306
|
+
/** EIP-3326/3085: the chain is not in the wallet (`wallet_switchEthereumChain` → suggest `wallet_addEthereumChain`). */
|
|
307
|
+
readonly chainNotAdded: 4902;
|
|
308
|
+
readonly internal: -32603;
|
|
309
|
+
readonly invalidRequest: -32600;
|
|
310
|
+
readonly invalidParams: -32602;
|
|
311
|
+
/** EIP-1474: a request of the same kind from this origin is already waiting (MetaMask's "already pending"). */
|
|
312
|
+
readonly resourceUnavailable: -32002;
|
|
313
|
+
/** EIP-1474: this origin has too many requests waiting. */
|
|
314
|
+
readonly limitExceeded: -32005;
|
|
315
|
+
};
|
|
316
|
+
interface PageRequest {
|
|
317
|
+
target: typeof PAGE_TARGET.toContent;
|
|
318
|
+
id: string;
|
|
319
|
+
chain: RelayChain;
|
|
320
|
+
method: string;
|
|
321
|
+
params?: unknown;
|
|
322
|
+
}
|
|
323
|
+
interface PageResponse {
|
|
324
|
+
target: typeof PAGE_TARGET.toPage;
|
|
325
|
+
id: string;
|
|
326
|
+
result?: unknown;
|
|
327
|
+
error?: RelayError;
|
|
328
|
+
}
|
|
329
|
+
interface PageEvent {
|
|
330
|
+
target: typeof PAGE_TARGET.toPage;
|
|
331
|
+
chain: RelayChain;
|
|
332
|
+
event: string;
|
|
333
|
+
data?: unknown;
|
|
334
|
+
}
|
|
335
|
+
interface RelayRequest {
|
|
336
|
+
kind: 'request';
|
|
337
|
+
id: string;
|
|
338
|
+
/** `location.origin` of the page, stamped by the content script. */
|
|
339
|
+
origin: string;
|
|
340
|
+
chain: RelayChain;
|
|
341
|
+
method: string;
|
|
342
|
+
params?: unknown;
|
|
343
|
+
}
|
|
344
|
+
interface RelayResponse {
|
|
345
|
+
kind: 'response';
|
|
346
|
+
id: string;
|
|
347
|
+
result?: unknown;
|
|
348
|
+
error?: RelayError;
|
|
349
|
+
}
|
|
350
|
+
interface RelayEvent {
|
|
351
|
+
kind: 'event';
|
|
352
|
+
chain: RelayChain;
|
|
353
|
+
event: string;
|
|
354
|
+
data?: unknown;
|
|
355
|
+
}
|
|
356
|
+
type RelayToWorker = RelayRequest;
|
|
357
|
+
type RelayToContent = RelayResponse | RelayEvent;
|
|
358
|
+
declare function isPageRequest(x: unknown): x is PageRequest;
|
|
359
|
+
declare function isPageResponse(x: unknown): x is PageResponse;
|
|
360
|
+
declare function isPageEvent(x: unknown): x is PageEvent;
|
|
361
|
+
declare function isRelayRequest(x: unknown): x is RelayRequest;
|
|
362
|
+
declare function isRelayToContent(x: unknown): x is RelayToContent;
|
|
363
|
+
declare function newRequestId(): string;
|
|
364
|
+
/** The content script's stamp: {@link PageRequest} + the page origin. */
|
|
365
|
+
declare function stampOrigin(request: PageRequest, origin: string): RelayRequest;
|
|
366
|
+
/** What `port.sender` looks like (structural `chrome.runtime.MessageSender`). */
|
|
367
|
+
interface PortSender {
|
|
368
|
+
origin?: string | undefined;
|
|
369
|
+
url?: string | undefined;
|
|
370
|
+
tab?: {
|
|
371
|
+
id?: number | undefined;
|
|
372
|
+
} | undefined;
|
|
373
|
+
frameId?: number | undefined;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* The service worker's check before it believes a stamped origin: it must equal the
|
|
377
|
+
* origin the browser recorded for the port. Returns the trusted origin or `null`.
|
|
378
|
+
*/
|
|
379
|
+
declare function checkSenderOrigin(sender: PortSender | undefined, stamped: string): string | null;
|
|
380
|
+
declare const relayError: (code: number, message: string, data?: unknown) => RelayError;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Adapters over the `chrome.*` APIs, typed structurally so this package needs no
|
|
384
|
+
* `@types/chrome` and tests can pass fakes. Promise forms (MV3) only.
|
|
385
|
+
*/
|
|
386
|
+
|
|
387
|
+
interface StorageAreaLike {
|
|
388
|
+
get(keys: string | string[]): Promise<Record<string, unknown>>;
|
|
389
|
+
set(items: Record<string, unknown>): Promise<void>;
|
|
390
|
+
remove(keys: string | string[]): Promise<void>;
|
|
391
|
+
/** `chrome.storage.session` only. */
|
|
392
|
+
setAccessLevel?(options: {
|
|
393
|
+
accessLevel: 'TRUSTED_CONTEXTS' | 'TRUSTED_AND_UNTRUSTED_CONTEXTS';
|
|
394
|
+
}): Promise<void>;
|
|
395
|
+
}
|
|
396
|
+
interface AlarmsLike {
|
|
397
|
+
create(name: string, info: {
|
|
398
|
+
when: number;
|
|
399
|
+
}): Promise<void> | void;
|
|
400
|
+
clear(name: string): Promise<boolean> | boolean | void;
|
|
401
|
+
onAlarm: {
|
|
402
|
+
addListener(callback: (alarm: {
|
|
403
|
+
name: string;
|
|
404
|
+
}) => void): void;
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/** Stable storage key: existing vaults live under it — never rename. */
|
|
408
|
+
declare const VAULT_STORAGE_KEY = "forgewallet.vault.v1";
|
|
409
|
+
/** The encrypted envelope in `chrome.storage.local` (survives browser restarts; ciphertext only). */
|
|
410
|
+
declare function chromeLocalVaultStorage(area: StorageAreaLike, key?: string): VaultStorage;
|
|
411
|
+
/**
|
|
412
|
+
* `chrome.storage.session`: memory-only, gone when the browser exits. Access is
|
|
413
|
+
* pinned to trusted contexts (the default; made explicit so a content script can
|
|
414
|
+
* never read the unlocked key even if a later change opens the area to them).
|
|
415
|
+
*/
|
|
416
|
+
declare function chromeSessionStorage(area: StorageAreaLike): SessionStorage;
|
|
417
|
+
/** `chrome.alarms` as one-shot named alarms. Chrome rounds alarms to ≥ 30 s in packed extensions. */
|
|
418
|
+
declare function chromeAlarms(alarms: AlarmsLike): SessionAlarms;
|
|
419
|
+
|
|
420
|
+
/** Stable storage key: existing keyrings live under it — never rename. */
|
|
421
|
+
declare const KEYRING_STORAGE_KEY = "forgewallet.keyring.v1";
|
|
422
|
+
/** Keyring metadata (no secrets) in `chrome.storage.local`. */
|
|
423
|
+
declare function chromeKeyringStorage(area: StorageAreaLike, key?: string): KeyringStorage;
|
|
424
|
+
/** Keyring secrets sealed in the vault; readable only while the session is unlocked. */
|
|
425
|
+
declare function vaultSecretStorage(vault: Vault, session: Session): SecretStorage;
|
|
426
|
+
|
|
427
|
+
/** One JSON value under one key — the shape every small store here needs. */
|
|
428
|
+
interface JsonStorage<T> {
|
|
429
|
+
get(): Promise<T | null>;
|
|
430
|
+
set(value: T): Promise<void>;
|
|
431
|
+
}
|
|
432
|
+
declare function chromeJsonStorage<T>(area: StorageAreaLike, key: string): JsonStorage<T>;
|
|
433
|
+
declare function memoryJsonStorage<T>(initial?: T | null): JsonStorage<T> & {
|
|
434
|
+
value: T | null;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
interface EvmNetwork {
|
|
438
|
+
/** CAIP-2, `eip155:<chainId>`. */
|
|
439
|
+
caip2: string;
|
|
440
|
+
chainId: number;
|
|
441
|
+
name: string;
|
|
442
|
+
/** Native currency symbol, 18 decimals on every chain here (on Arc that is the 18-decimal native view of USDC). */
|
|
443
|
+
symbol: string;
|
|
444
|
+
/** Tried in order; the client fails over. */
|
|
445
|
+
rpcs: string[];
|
|
446
|
+
explorer?: string;
|
|
447
|
+
/** Set when a SITE supplied the RPC list (wallet_addEthereumChain): every sheet on this network says so and simulations are not trusted. */
|
|
448
|
+
custom?: {
|
|
449
|
+
origin: string;
|
|
450
|
+
addedAt: number;
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
declare const DEFAULT_CHAIN = "eip155:1";
|
|
454
|
+
declare const DEFAULT_NETWORKS: readonly EvmNetwork[];
|
|
455
|
+
declare const chainHex: (caip2: string) => string;
|
|
456
|
+
/** `0x1` / `1` → `eip155:1`; `null` for anything that is not a positive integer chain id. */
|
|
457
|
+
declare function caip2FromChainId(value: unknown): string | null;
|
|
458
|
+
/** Whether the asset catalog knows this chain — the gate for `wallet_addEthereumChain`. */
|
|
459
|
+
declare function isCatalogChain(caip2: string): boolean;
|
|
460
|
+
/**
|
|
461
|
+
* Shipped networks plus the ones the user accepted from sites, persisted. Lookup
|
|
462
|
+
* by CAIP-2; a custom entry for a shipped chain is ignored (the build's RPC list
|
|
463
|
+
* wins — a site must not be able to replace Ethereum's endpoints).
|
|
464
|
+
*/
|
|
465
|
+
declare class NetworksTable {
|
|
466
|
+
private readonly storage;
|
|
467
|
+
private readonly defaults;
|
|
468
|
+
private _custom;
|
|
469
|
+
private _loading;
|
|
470
|
+
/** add/remove run one after another (scan F22, as PermissionStore). */
|
|
471
|
+
private _queue;
|
|
472
|
+
constructor(storage: JsonStorage<EvmNetwork[]>, defaults?: readonly EvmNetwork[]);
|
|
473
|
+
list(): Promise<EvmNetwork[]>;
|
|
474
|
+
get(caip2: string): Promise<EvmNetwork | null>;
|
|
475
|
+
add(network: EvmNetwork): Promise<void>;
|
|
476
|
+
remove(caip2: string): Promise<void>;
|
|
477
|
+
private serialize;
|
|
478
|
+
/** Cache first (readers see it at once), rolled back when storage refuses. */
|
|
479
|
+
private write;
|
|
480
|
+
private custom;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* EIP-3085 `wallet_addEthereumChain` parameter → {@link EvmNetwork}, or the error
|
|
484
|
+
* to answer with. https RPC urls only; the chain id must be in the catalog.
|
|
485
|
+
*/
|
|
486
|
+
declare function parseAddChainParams(params: unknown, known?: (caip2: string) => boolean): EvmNetwork | RelayError;
|
|
487
|
+
/** Solana mainnet as the catalog names it (`solana:mainnet`); the wallet package also accepts the genesis-hash form. */
|
|
488
|
+
declare const SOLANA_CHAIN = "solana:mainnet";
|
|
489
|
+
declare const SOLANA_RPCS: readonly string[];
|
|
490
|
+
declare const TON_CHAIN = "ton:-239";
|
|
491
|
+
declare const TON_ENDPOINTS: readonly string[];
|
|
492
|
+
declare const TRON_CHAIN = "tron:728126428";
|
|
493
|
+
declare const TRON_PROVIDERS: readonly string[];
|
|
494
|
+
declare const BTC_CHAIN = "bitcoin:mainnet";
|
|
495
|
+
declare const BTC_ESPLORA: readonly string[];
|
|
496
|
+
declare const NEAR_CHAIN = "near:mainnet";
|
|
497
|
+
declare const NEAR_PROVIDERS: readonly string[];
|
|
498
|
+
/** Zcash transparent (t1…) accounts over Blockchair's public API, as `@anywalletkit/wallet-zcash` reads and broadcasts them. */
|
|
499
|
+
declare const ZCASH_CHAIN = "zcash:mainnet";
|
|
500
|
+
declare const ZCASH_BLOCKCHAIR = "https://api.blockchair.com/zcash";
|
|
501
|
+
interface ChainDescriptor {
|
|
502
|
+
caip2: string;
|
|
503
|
+
key: 'solana' | 'ton' | 'tron' | 'btc' | 'near' | 'zcash';
|
|
504
|
+
name: string;
|
|
505
|
+
symbol: string;
|
|
506
|
+
decimals: number;
|
|
507
|
+
explorerTx: string;
|
|
508
|
+
explorerAddress: string;
|
|
509
|
+
}
|
|
510
|
+
declare const NON_EVM_CHAINS: readonly ChainDescriptor[];
|
|
511
|
+
declare const nonEvmChain: (caip2: string) => ChainDescriptor | undefined;
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Per-origin permissions: which accounts a site may see and which chain it is
|
|
515
|
+
* on. Nothing here is secret (`chrome.storage.local`, plain JSON); the origin
|
|
516
|
+
* key is the one the browser recorded for the port, never the page's claim.
|
|
517
|
+
*/
|
|
518
|
+
|
|
519
|
+
interface OriginPermission {
|
|
520
|
+
origin: string;
|
|
521
|
+
/** EVM addresses exposed to this origin (checksummed as the keyring stores them). Empty when only another chain is connected. */
|
|
522
|
+
accounts: string[];
|
|
523
|
+
/** CAIP-2 chain the origin currently sees (`eth_chainId`). */
|
|
524
|
+
chain: string;
|
|
525
|
+
/** Solana addresses exposed to this origin (Wallet Standard `standard:connect`). */
|
|
526
|
+
solanaAccounts?: string[];
|
|
527
|
+
/** TON addresses exposed to this origin (TON Connect). */
|
|
528
|
+
tonAccounts?: string[];
|
|
529
|
+
/** Tron (TIP-1193), Bitcoin (sats-connect) and NEAR (wallet-selector) addresses. */
|
|
530
|
+
tronAccounts?: string[];
|
|
531
|
+
btcAccounts?: string[];
|
|
532
|
+
nearAccounts?: string[];
|
|
533
|
+
connectedAt: number;
|
|
534
|
+
}
|
|
535
|
+
type PermissionsState = Record<string, OriginPermission>;
|
|
536
|
+
type ChainAccountsKey = 'solanaAccounts' | 'tonAccounts' | 'tronAccounts' | 'btcAccounts' | 'nearAccounts';
|
|
537
|
+
declare const CHAIN_ACCOUNT_KEYS: readonly ChainAccountsKey[];
|
|
538
|
+
declare class PermissionStore {
|
|
539
|
+
private readonly storage;
|
|
540
|
+
private _state;
|
|
541
|
+
private _loading;
|
|
542
|
+
/** Every mutation runs here, one after another (scan F22): a read-modify-write never starts from a snapshot another one is about to replace. */
|
|
543
|
+
private _queue;
|
|
544
|
+
private readonly _listeners;
|
|
545
|
+
constructor(storage: JsonStorage<PermissionsState>);
|
|
546
|
+
private state;
|
|
547
|
+
/**
|
|
548
|
+
* Run `fn` after every earlier mutation settled. Public mutators wrap their WHOLE read-modify-write
|
|
549
|
+
* in this; `write` itself is unlocked (setAccountsEverywhere calls it in a loop — a lock there too
|
|
550
|
+
* would deadlock). Nothing awaited inside may wait on the user: routers await the approval sheet
|
|
551
|
+
* first and only then call the store.
|
|
552
|
+
*/
|
|
553
|
+
private serialize;
|
|
554
|
+
get(origin: string): Promise<OriginPermission | null>;
|
|
555
|
+
list(): Promise<OriginPermission[]>;
|
|
556
|
+
connect(origin: string, accounts: string[], chain: string): Promise<OriginPermission>;
|
|
557
|
+
/** Connect another chain's accounts to the same origin (one "connected sites" list). */
|
|
558
|
+
connectChain(origin: string, key: ChainAccountsKey, accounts: string[], defaultChain: string): Promise<OriginPermission>;
|
|
559
|
+
/** Drop one chain's accounts; the origin stays connected while any chain remains. A no-op (no write) when there is nothing to drop. */
|
|
560
|
+
disconnectChain(origin: string, key: 'accounts' | ChainAccountsKey): Promise<void>;
|
|
561
|
+
setChain(origin: string, chain: string): Promise<OriginPermission | null>;
|
|
562
|
+
/**
|
|
563
|
+
* The active EVM account changed: every origin holding an EVM grant now sees `accounts` (MetaMask behaviour).
|
|
564
|
+
* An origin connected only on another chain (`accounts: []` — Solana/TON/Tron/BTC/NEAR providers, a WalletConnect
|
|
565
|
+
* session without eip155) is skipped: it never passed an EVM connect and must not learn the EVM address (scan F6/F8).
|
|
566
|
+
* Lock never writes `[]` here, so the skip cannot strip a real EVM grant.
|
|
567
|
+
*/
|
|
568
|
+
setAccountsEverywhere(accounts: string[]): Promise<OriginPermission[]>;
|
|
569
|
+
disconnect(origin: string): Promise<void>;
|
|
570
|
+
onChange(listener: (origin: string, permission: OriginPermission | null) => void): () => void;
|
|
571
|
+
/** Unlocked: callers hold the queue. The cache changes before the await (readers see it at once) and is rolled back when storage refuses. */
|
|
572
|
+
private write;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
declare class ReviewDeclined extends Error {
|
|
576
|
+
readonly code: 4001;
|
|
577
|
+
constructor(message?: string);
|
|
578
|
+
}
|
|
579
|
+
type ApprovalKind = 'connect' | 'switch-chain' | 'add-chain' | 'sign-message' | 'sign-typed-data' | 'send-transaction' | 'solana-send' | 'swap' | 'earn' | 'chain-send' | 'tron-sign' | 'btc-sign' | 'near-sign' | 'near-signin' | 'wc-session';
|
|
580
|
+
/** How a request reached the wallet when it did not come through a content script. */
|
|
581
|
+
interface OriginVia {
|
|
582
|
+
transport: 'walletconnect';
|
|
583
|
+
/** What the dApp declared about itself (self-reported, unverified unless `verify` says so). */
|
|
584
|
+
url: string;
|
|
585
|
+
name: string;
|
|
586
|
+
/** WalletConnect Verify: VALID = the relay saw the request come from `url`'s origin; INVALID = it came from somewhere else; UNKNOWN = not attested. */
|
|
587
|
+
verify: 'VALID' | 'INVALID' | 'UNKNOWN';
|
|
588
|
+
scam?: boolean;
|
|
589
|
+
}
|
|
590
|
+
/** The queue refused to enqueue (per-origin limits); carries an EIP-1474 code the page understands. */
|
|
591
|
+
declare class QueueLimitError extends Error {
|
|
592
|
+
readonly code: number;
|
|
593
|
+
constructor(code: number, message: string);
|
|
594
|
+
}
|
|
595
|
+
declare const APPROVALS_MAX_PENDING_PER_ORIGIN = 3;
|
|
596
|
+
interface ApprovalRequest<P = unknown> {
|
|
597
|
+
id: string;
|
|
598
|
+
origin: string;
|
|
599
|
+
chain: string;
|
|
600
|
+
kind: ApprovalKind;
|
|
601
|
+
/** What the window shows; shape per kind, public data only. */
|
|
602
|
+
payload: P;
|
|
603
|
+
createdAt: number;
|
|
604
|
+
/** Phishing / lookalike / punycode notes about the origin, from the queue's `annotate` hook. */
|
|
605
|
+
originWarnings?: string[];
|
|
606
|
+
/** Set when the origin is a transport pseudo-origin (`wc:<topic>`): what the sheet should say about where this came from. */
|
|
607
|
+
via?: OriginVia;
|
|
608
|
+
}
|
|
609
|
+
interface ApprovalQueueOptions {
|
|
610
|
+
/** Called for every request's origin; the lines are shown on the sheet (e.g. the shipped phishing list's verdict). */
|
|
611
|
+
annotate?: (origin: string) => string[];
|
|
612
|
+
/** Requests one origin may have waiting (current + queued). Default {@link APPROVALS_MAX_PENDING_PER_ORIGIN}. */
|
|
613
|
+
maxPendingPerOrigin?: number;
|
|
614
|
+
}
|
|
615
|
+
/** The window side: open for one request, close when it is answered. `memoryApprovalWindow()` for tests. */
|
|
616
|
+
interface ApprovalWindow {
|
|
617
|
+
open(request: ApprovalRequest): Promise<void>;
|
|
618
|
+
close(requestId: string): Promise<void>;
|
|
619
|
+
/** The user closed the window themself (chrome.windows.onRemoved): the queue declines the request. */
|
|
620
|
+
onClosed(handler: (requestId: string) => void): void;
|
|
621
|
+
}
|
|
622
|
+
declare class ApprovalQueue {
|
|
623
|
+
private readonly window;
|
|
624
|
+
private readonly newId;
|
|
625
|
+
private readonly options;
|
|
626
|
+
private _current;
|
|
627
|
+
private readonly _waiting;
|
|
628
|
+
private readonly _listeners;
|
|
629
|
+
private readonly _via;
|
|
630
|
+
constructor(window: ApprovalWindow, newId?: () => string, options?: ApprovalQueueOptions);
|
|
631
|
+
/** Enqueue and wait for the user's answer. Refuses (never queues) past the per-origin limits. */
|
|
632
|
+
request<T = unknown>(input: Omit<ApprovalRequest, 'id' | 'createdAt' | 'originWarnings' | 'via'>): Promise<T>;
|
|
633
|
+
/** Attach (or clear) the transport note every later request from `origin` carries. */
|
|
634
|
+
setVia(origin: string, via: OriginVia | null): void;
|
|
635
|
+
/** The transport note later requests from `origin` carry (WalletConnect Verify's verdict for `wc:<topic>`), if any. */
|
|
636
|
+
viaFor(origin: string): OriginVia | undefined;
|
|
637
|
+
/** Requests waiting for `origin` (current + queued). */
|
|
638
|
+
pendingFor(origin: string): number;
|
|
639
|
+
/** The request currently shown (the window asks for it by id on load). */
|
|
640
|
+
get current(): ApprovalRequest | null;
|
|
641
|
+
get queued(): number;
|
|
642
|
+
resolve(requestId: string, value: unknown): boolean;
|
|
643
|
+
decline(requestId: string, error?: Error): boolean;
|
|
644
|
+
/** Lock, disconnect, shutdown: everything pending is declined. */
|
|
645
|
+
declineAll(error?: Error): void;
|
|
646
|
+
onChange(listener: () => void): () => void;
|
|
647
|
+
private settle;
|
|
648
|
+
private next;
|
|
649
|
+
private emit;
|
|
650
|
+
}
|
|
651
|
+
/** In-memory window: tests answer through the queue; `opened` records every request shown. */
|
|
652
|
+
declare function memoryApprovalWindow(): ApprovalWindow & {
|
|
653
|
+
opened: ApprovalRequest[];
|
|
654
|
+
openIds: Set<string>;
|
|
655
|
+
userClose(id: string): void;
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* The request router: what a dApp may ask and what happens to each request.
|
|
660
|
+
* Policy ported from Vaulty's in-app browser (`mobile/lib/browser.ts`):
|
|
661
|
+
*
|
|
662
|
+
* - the origin is the browser's (checked by the service worker before this runs);
|
|
663
|
+
* - accounts are exposed only to origins the user connected (`eth_requestAccounts`
|
|
664
|
+
* → approval window); `eth_accounts` is `[]` otherwise;
|
|
665
|
+
* - chain switching and adding go through the approval window; a chain can be
|
|
666
|
+
* added only if the asset catalog knows it, with https RPCs;
|
|
667
|
+
* - read-only JSON-RPC is answered from the wallet's own clients (the networks
|
|
668
|
+
* table's failover list), never from a URL the page names;
|
|
669
|
+
* - `eth_sign` (blind hash signing) is never offered; the other signing methods
|
|
670
|
+
* go to the `sign` hook (the review flow) when the wallet provides one;
|
|
671
|
+
* - everything else is refused with 4200.
|
|
672
|
+
*
|
|
673
|
+
* Errors leave as {@link RouterError} (EIP-1193 codes) or {@link ReviewDeclined}
|
|
674
|
+
* (4001); the service worker turns both into a `RelayResponse.error`.
|
|
675
|
+
*/
|
|
676
|
+
|
|
677
|
+
declare class RouterError extends Error {
|
|
678
|
+
readonly code: number;
|
|
679
|
+
readonly data: unknown;
|
|
680
|
+
constructor(code: number, message: string, data?: unknown);
|
|
681
|
+
static from(e: RelayError): RouterError;
|
|
682
|
+
}
|
|
683
|
+
/** Any error → the `RelayError` for the page. Unknown errors keep their message but never their stack. */
|
|
684
|
+
declare function toRelayError(e: unknown): RelayError;
|
|
685
|
+
/** Read-only methods answered from our RPC without asking the user. */
|
|
686
|
+
declare const READ_METHODS: ReadonlySet<string>;
|
|
687
|
+
/** Methods that produce a signature — the review flow's business (step 4), never answered here. */
|
|
688
|
+
declare const SIGNING_METHODS: ReadonlySet<string>;
|
|
689
|
+
interface ConnectApproval {
|
|
690
|
+
/** Addresses the wallet can expose, active account first (empty while locked — the window unlocks first and reloads). */
|
|
691
|
+
accounts: string[];
|
|
692
|
+
}
|
|
693
|
+
interface SwitchChainApproval {
|
|
694
|
+
from: EvmNetwork | null;
|
|
695
|
+
to: EvmNetwork;
|
|
696
|
+
}
|
|
697
|
+
interface AddChainApproval {
|
|
698
|
+
network: EvmNetwork;
|
|
699
|
+
}
|
|
700
|
+
interface RouterDeps {
|
|
701
|
+
permissions: PermissionStore;
|
|
702
|
+
networks: NetworksTable;
|
|
703
|
+
approvals: ApprovalQueue;
|
|
704
|
+
/** EVM addresses the wallet exposes, active account first. Empty when locked or without a wallet. */
|
|
705
|
+
accounts(): Promise<string[]>;
|
|
706
|
+
/** Read-only JSON-RPC over the network's own RPC list. */
|
|
707
|
+
rpc(network: EvmNetwork, method: string, params: unknown[]): Promise<unknown>;
|
|
708
|
+
/** Push an EIP-1193 event to every page of `origin`. */
|
|
709
|
+
emit(origin: string, event: string, data: unknown): void;
|
|
710
|
+
/** The review flow for signing methods. Absent → 4200. */
|
|
711
|
+
sign?(input: {
|
|
712
|
+
origin: string;
|
|
713
|
+
network: EvmNetwork;
|
|
714
|
+
method: string;
|
|
715
|
+
params: unknown[];
|
|
716
|
+
accounts: string[];
|
|
717
|
+
}): Promise<unknown>;
|
|
718
|
+
defaultChain?: string;
|
|
719
|
+
}
|
|
720
|
+
declare class RequestRouter {
|
|
721
|
+
private readonly deps;
|
|
722
|
+
constructor(deps: RouterDeps);
|
|
723
|
+
/** The chain an origin currently sees (its permission's, else the default). */
|
|
724
|
+
chainFor(origin: string): Promise<EvmNetwork>;
|
|
725
|
+
handle(origin: string, chain: RelayChain, method: string, rawParams: unknown): Promise<unknown>;
|
|
726
|
+
/** What a connected origin sees right now: its permitted accounts that the wallet can expose (none while locked). */
|
|
727
|
+
private visibleAccounts;
|
|
728
|
+
/**
|
|
729
|
+
* Connected and unlocked → the accounts; otherwise the approval window (which
|
|
730
|
+
* unlocks first when needed) picks them. Emits `connect` + `accountsChanged`.
|
|
731
|
+
*/
|
|
732
|
+
private connect;
|
|
733
|
+
disconnect(origin: string): Promise<void>;
|
|
734
|
+
private switchChain;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Sign-In with Ethereum (EIP-4361) messages, parsed so the sheet can say what a
|
|
739
|
+
* `personal_sign` really is and check the fields a phishing site would lie
|
|
740
|
+
* about: the domain (scheme + host) must be the site asking, the address must
|
|
741
|
+
* be the signing account, the chain must be the site's, the message must not be
|
|
742
|
+
* expired or not-yet-valid, and the nonce must look like one (replay).
|
|
743
|
+
*
|
|
744
|
+
* Everything here is a WARNING: the sheet turns problems into warn checks and
|
|
745
|
+
* the user can still sign. Whenever the first line is an EIP-4361 header the
|
|
746
|
+
* text is treated as a sign-in (scan F9) — a message whose remaining fields do
|
|
747
|
+
* not parse is reported as malformed instead of silently becoming a plain
|
|
748
|
+
* message without the cross-site check.
|
|
749
|
+
*/
|
|
750
|
+
|
|
751
|
+
interface SiweMessage {
|
|
752
|
+
/** As written in the header: `host[:port]`, optionally prefixed by `scheme://`. */
|
|
753
|
+
domain: string;
|
|
754
|
+
address: string;
|
|
755
|
+
statement?: string;
|
|
756
|
+
uri: string;
|
|
757
|
+
version: string;
|
|
758
|
+
chainId: number;
|
|
759
|
+
nonce: string;
|
|
760
|
+
issuedAt: string;
|
|
761
|
+
expirationTime?: string;
|
|
762
|
+
notBefore?: string;
|
|
763
|
+
requestId?: string;
|
|
764
|
+
resources: string[];
|
|
765
|
+
}
|
|
766
|
+
interface SiweInfo {
|
|
767
|
+
message: SiweMessage;
|
|
768
|
+
/** Human lines, each a reason the sheet should warn; empty = the sign-in is consistent with the request. */
|
|
769
|
+
problems: string[];
|
|
770
|
+
/** The header said "sign-in" but the rest did not parse as EIP-4361 (`message` then holds what could be read). */
|
|
771
|
+
malformed?: boolean;
|
|
772
|
+
}
|
|
773
|
+
interface SiweContext {
|
|
774
|
+
/** The page that asked, as the browser recorded it (`https://…`), or a transport pseudo-origin (`wc:<topic>`). */
|
|
775
|
+
origin: string;
|
|
776
|
+
/** The signing address. */
|
|
777
|
+
account: string;
|
|
778
|
+
/** The site's chain. */
|
|
779
|
+
chainId: number;
|
|
780
|
+
now?: number;
|
|
781
|
+
/** Transport note for pseudo-origins (WalletConnect): the dApp's claimed URL and what Verify said about it. */
|
|
782
|
+
via?: OriginVia;
|
|
783
|
+
}
|
|
784
|
+
/** `null` when the text is not a well-formed EIP-4361 message (see {@link describeSiwe} for the tolerant form the sheet uses). */
|
|
785
|
+
declare function parseSiwe(text: string): SiweMessage | null;
|
|
786
|
+
/** Loopback / `*.localhost` (dev servers): plain http is normal there, so no scheme warnings. Takes `host[:port]`. */
|
|
787
|
+
declare function isLocalHost(host: string): boolean;
|
|
788
|
+
/** The checks; `origin` is the page that asked (as the browser recorded it), `account` the signing address, `chainId` the site's chain. */
|
|
789
|
+
declare function siweProblems(m: SiweMessage, ctx: SiweContext): string[];
|
|
790
|
+
/** `null` only when the text does not start with an EIP-4361 header; a header with unreadable fields comes back with a "malformed sign-in message" problem. */
|
|
791
|
+
declare function describeSiwe(text: string, ctx: SiweContext): SiweInfo | null;
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Signing requests from dApps (and from the wallet's own Send): classification,
|
|
795
|
+
* transaction preparation, the review stop, the signature.
|
|
796
|
+
*
|
|
797
|
+
* Classification is Vaulty's `signingRequest` (mobile/lib/browser.ts): the
|
|
798
|
+
* address a page names must be one the user connected; typed data bound to
|
|
799
|
+
* another chain than the one the site is on is refused (4901); a transaction
|
|
800
|
+
* must have a `to` (no deployments) and a `chainId`, when given, equal to the
|
|
801
|
+
* site's chain; `eth_sign` never reaches here (the router refuses it).
|
|
802
|
+
*
|
|
803
|
+
* Preparation fills nonce, gas and fees from the wallet's own RPC (never the
|
|
804
|
+
* page's): EIP-1559 cap = 2 × base fee + tip with the tip floored at 0.1 gwei
|
|
805
|
+
* (Vaulty's `evmFees`), legacy `gasPrice` where a chain has no base fee, gas =
|
|
806
|
+
* the page's limit or `eth_estimateGas` × 1.2.
|
|
807
|
+
*
|
|
808
|
+
* The review payload ({@link SignApproval}) carries what the window shows: the
|
|
809
|
+
* classified request, the prepared fields, the fee, and the preview the wallet
|
|
810
|
+
* attached (tx-preview-evm: decode, simulation, balance changes, checks). Only
|
|
811
|
+
* after the user approves does {@link SignDeps.signerFor} open the key; the
|
|
812
|
+
* handle is disposed (zeroized) in `finally`, the raw transaction goes out via
|
|
813
|
+
* `eth_sendRawTransaction` on the wallet's RPC.
|
|
814
|
+
*/
|
|
815
|
+
|
|
816
|
+
type SignRequestInfo = {
|
|
817
|
+
kind: 'sign-message';
|
|
818
|
+
method: 'personal_sign';
|
|
819
|
+
account: string; /** Decoded text when printable, else the hex. */
|
|
820
|
+
text: string; /** What is actually signed (hex or utf-8 string). */
|
|
821
|
+
raw: string; /** Parsed Sign-In with Ethereum (EIP-4361) with the checks against the asking origin, when the text is one. */
|
|
822
|
+
siwe?: SiweInfo;
|
|
823
|
+
} | {
|
|
824
|
+
kind: 'sign-typed-data';
|
|
825
|
+
method: string;
|
|
826
|
+
account: string; /** Pretty-printed JSON. */
|
|
827
|
+
text: string;
|
|
828
|
+
typedData: TypedData;
|
|
829
|
+
} | {
|
|
830
|
+
kind: 'send-transaction';
|
|
831
|
+
method: 'eth_sendTransaction';
|
|
832
|
+
account: string;
|
|
833
|
+
tx: {
|
|
834
|
+
to: string;
|
|
835
|
+
value: string;
|
|
836
|
+
data: string | null;
|
|
837
|
+
gas: string | null;
|
|
838
|
+
};
|
|
839
|
+
};
|
|
840
|
+
interface TypedData {
|
|
841
|
+
domain: Record<string, unknown>;
|
|
842
|
+
types: Record<string, Array<{
|
|
843
|
+
name: string;
|
|
844
|
+
type: string;
|
|
845
|
+
}>>;
|
|
846
|
+
message: Record<string, unknown>;
|
|
847
|
+
primaryType?: string;
|
|
848
|
+
}
|
|
849
|
+
interface PreparedTx {
|
|
850
|
+
chainId: number;
|
|
851
|
+
nonce: number;
|
|
852
|
+
gasLimit: string;
|
|
853
|
+
/** EIP-1559 fields, or `gasPrice` on legacy-fee chains. All 0x hex. */
|
|
854
|
+
maxFeePerGas?: string;
|
|
855
|
+
maxPriorityFeePerGas?: string;
|
|
856
|
+
gasPrice?: string;
|
|
857
|
+
/** gasLimit × max fee, base units. */
|
|
858
|
+
maxFee: string;
|
|
859
|
+
}
|
|
860
|
+
/** The approval window's payload for the three signing kinds. */
|
|
861
|
+
interface SignApproval {
|
|
862
|
+
request: SignRequestInfo;
|
|
863
|
+
network: EvmNetwork;
|
|
864
|
+
prepared?: PreparedTx;
|
|
865
|
+
/** What the wallet attached (tx-preview-evm `TxPreview`, typed-data description…). */
|
|
866
|
+
preview: unknown;
|
|
867
|
+
}
|
|
868
|
+
interface EvmSigner {
|
|
869
|
+
signMessage(message: string | Uint8Array): Promise<string>;
|
|
870
|
+
signTypedData(typed: TypedData): Promise<string>;
|
|
871
|
+
/** Fully specified tx (chainId, nonce, gas, fees) → signed raw transaction. */
|
|
872
|
+
signTransaction(tx: {
|
|
873
|
+
chainId: number;
|
|
874
|
+
nonce: number;
|
|
875
|
+
to: string;
|
|
876
|
+
value: bigint;
|
|
877
|
+
data: string;
|
|
878
|
+
gasLimit: bigint;
|
|
879
|
+
maxFeePerGas?: bigint;
|
|
880
|
+
maxPriorityFeePerGas?: bigint;
|
|
881
|
+
gasPrice?: bigint;
|
|
882
|
+
}): Promise<string>;
|
|
883
|
+
}
|
|
884
|
+
interface SignDeps {
|
|
885
|
+
approvals: ApprovalQueue;
|
|
886
|
+
rpc(network: EvmNetwork, method: string, params: unknown[]): Promise<unknown>;
|
|
887
|
+
/** Opens the key for `address`; `dispose()` zeroizes it. */
|
|
888
|
+
signerFor(address: string, network: EvmNetwork): Promise<{
|
|
889
|
+
signer: EvmSigner;
|
|
890
|
+
dispose(): void;
|
|
891
|
+
}>;
|
|
892
|
+
/** Review data for the window (errors inside are the wallet's to swallow → `null`). */
|
|
893
|
+
preview?(input: {
|
|
894
|
+
network: EvmNetwork;
|
|
895
|
+
request: SignRequestInfo;
|
|
896
|
+
accounts: string[];
|
|
897
|
+
}): Promise<unknown>;
|
|
898
|
+
/** Tip floor per chain id in wei (default 0.1 gwei everywhere). */
|
|
899
|
+
tipFloor?: (chainId: number) => bigint;
|
|
900
|
+
}
|
|
901
|
+
/** Vaulty's `messageText`: hex that decodes to printable text is shown decoded; binary payloads stay hex. */
|
|
902
|
+
declare function messageText(value: unknown): string;
|
|
903
|
+
/**
|
|
904
|
+
* The message as the signer will sign it: hex decoded when it is valid UTF-8, whatever
|
|
905
|
+
* its printability (scan 2026-10-23 F7 — the SIWE checks must read the bytes that get
|
|
906
|
+
* signed, not the hex fallback `messageText` shows for unprintable text).
|
|
907
|
+
*/
|
|
908
|
+
declare function decodedMessage(value: unknown): string | null;
|
|
909
|
+
/** Signing method + params → what the window reviews, or the error to answer with. */
|
|
910
|
+
declare function classifySignRequest(input: {
|
|
911
|
+
network: EvmNetwork;
|
|
912
|
+
method: string;
|
|
913
|
+
params: unknown[];
|
|
914
|
+
accounts: string[]; /** The asking page (browser-recorded) — needed for the SIWE domain check. */
|
|
915
|
+
origin?: string; /** Transport note for pseudo-origins (WalletConnect Verify), for the SIWE check. */
|
|
916
|
+
via?: OriginVia;
|
|
917
|
+
now?: number;
|
|
918
|
+
}): SignRequestInfo | RelayError;
|
|
919
|
+
/** Nonce, gas, fees from the wallet's RPC. */
|
|
920
|
+
declare function prepareTransaction(deps: Pick<SignDeps, 'rpc' | 'tipFloor'>, network: EvmNetwork, from: string, tx: {
|
|
921
|
+
to: string;
|
|
922
|
+
value: string;
|
|
923
|
+
data: string | null;
|
|
924
|
+
gas: string | null;
|
|
925
|
+
}): Promise<PreparedTx>;
|
|
926
|
+
/** The router's `sign` hook: classify → prepare → review → sign (→ broadcast). */
|
|
927
|
+
declare function createSignHandler(deps: SignDeps): NonNullable<RouterDeps['sign']>;
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Pre-approvals — the second half of Vaulty's `txGate` (mobile/lib/txGate.ts).
|
|
931
|
+
* A flow that reviewed a WHOLE plan in one sheet (a swap: approvals + source
|
|
932
|
+
* leg) registers one matcher per operation it is about to perform; the
|
|
933
|
+
* reviewed account then lets exactly those operations through without a second
|
|
934
|
+
* window, each once, within `ttlMs`. Anything else still stops at the review.
|
|
935
|
+
*/
|
|
936
|
+
type GatedOp = {
|
|
937
|
+
kind: 'evm-tx';
|
|
938
|
+
caip2: string;
|
|
939
|
+
to: string;
|
|
940
|
+
data?: string;
|
|
941
|
+
value: bigint;
|
|
942
|
+
} | {
|
|
943
|
+
kind: 'evm-typed-data';
|
|
944
|
+
caip2: string;
|
|
945
|
+
} | {
|
|
946
|
+
kind: 'solana-transfer';
|
|
947
|
+
to: string;
|
|
948
|
+
amount: bigint;
|
|
949
|
+
token?: string;
|
|
950
|
+
} | {
|
|
951
|
+
kind: 'solana-tx';
|
|
952
|
+
to: string;
|
|
953
|
+
value: bigint;
|
|
954
|
+
} | {
|
|
955
|
+
kind: 'solana-serialized';
|
|
956
|
+
serializedTx: string;
|
|
957
|
+
}
|
|
958
|
+
/** A module-built Solana transaction message (instructions), reviewed as a plan. */
|
|
959
|
+
| {
|
|
960
|
+
kind: 'solana-message';
|
|
961
|
+
}
|
|
962
|
+
/** TON / Tron / Bitcoin / NEAR send or token transfer (amount in base units; token = jetton master / TRC-20 / NEP-141 id). */
|
|
963
|
+
| {
|
|
964
|
+
kind: 'chain-tx';
|
|
965
|
+
chain: string;
|
|
966
|
+
to: string;
|
|
967
|
+
amount: bigint;
|
|
968
|
+
token?: string | undefined;
|
|
969
|
+
memo?: string | undefined;
|
|
970
|
+
};
|
|
971
|
+
type GateMatcher = (op: GatedOp) => boolean;
|
|
972
|
+
declare class ReviewGate {
|
|
973
|
+
private readonly now;
|
|
974
|
+
private approved;
|
|
975
|
+
constructor(now?: () => number);
|
|
976
|
+
/** Each matcher passes ONE operation, within `ttlMs` (default 10 min). */
|
|
977
|
+
preApprove(matchers: GateMatcher[], ttlMs?: number): void;
|
|
978
|
+
/** True (and the matcher is spent) when a pre-approval covers `op`. */
|
|
979
|
+
consume(op: GatedOp): boolean;
|
|
980
|
+
get pending(): number;
|
|
981
|
+
clear(): void;
|
|
982
|
+
}
|
|
983
|
+
/** Matcher for an exact EVM call: same chain, `to`, calldata and value. */
|
|
984
|
+
declare function evmCallMatcher(expected: {
|
|
985
|
+
caip2: string;
|
|
986
|
+
to: string;
|
|
987
|
+
data?: string | undefined;
|
|
988
|
+
value: bigint;
|
|
989
|
+
}): GateMatcher;
|
|
990
|
+
/** Matcher for a non-EVM send: same chain, recipient, amount, token and memo. */
|
|
991
|
+
declare function chainTxMatcher(expected: {
|
|
992
|
+
chain: string;
|
|
993
|
+
to: string;
|
|
994
|
+
amount: bigint;
|
|
995
|
+
token?: string | undefined;
|
|
996
|
+
memo?: string | undefined;
|
|
997
|
+
}): GateMatcher;
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Swaps for the extension, the DIRECT path of Vaulty mobile's `lib/swap2.ts`:
|
|
1001
|
+
* the providers run in the service worker and talk to their APIs themselves.
|
|
1002
|
+
*
|
|
1003
|
+
* - {@link quoteRoutes}: dry quotes from every provider that can serve the
|
|
1004
|
+
* pair, delivered progressively (`onUpdate` fires as each venue answers), with
|
|
1005
|
+
* BEST / FASTEST tags and every refusal named.
|
|
1006
|
+
* TODO: replace with `CrossChainRouter.getQuotes` (net-USD ranking,
|
|
1007
|
+
* RouteSet). Until then this fan-out over the public `CrossChainProvider`
|
|
1008
|
+
* interface ranks by output amount.
|
|
1009
|
+
* - {@link reviewSwapPlan}: the engine's `reviewExecution` hook — one sheet for
|
|
1010
|
+
* the whole plan (approvals + source leg) BEFORE anything is signed; on
|
|
1011
|
+
* approval exactly those operations are pre-approved in the {@link ReviewGate}
|
|
1012
|
+
* so the reviewed account lets them through once; a decline aborts with
|
|
1013
|
+
* nothing sent (`ReviewDeclined`, 4001).
|
|
1014
|
+
*/
|
|
1015
|
+
|
|
1016
|
+
interface RouteView {
|
|
1017
|
+
provider: string;
|
|
1018
|
+
destAmount: string;
|
|
1019
|
+
minDestAmount: string;
|
|
1020
|
+
feeUsd: number;
|
|
1021
|
+
/** On-chain pool venues take the fee inside the output; "$0.00" would mislead. */
|
|
1022
|
+
feeNote?: string;
|
|
1023
|
+
etaSeconds: number;
|
|
1024
|
+
validUntilMs: number;
|
|
1025
|
+
tags: ('best' | 'fastest')[];
|
|
1026
|
+
}
|
|
1027
|
+
interface RouteListView {
|
|
1028
|
+
routes: RouteView[];
|
|
1029
|
+
/** Providers still quoting. */
|
|
1030
|
+
pending: string[];
|
|
1031
|
+
/** Provider → why it sat out (refusal, error, timeout). */
|
|
1032
|
+
refusals: Record<string, string>;
|
|
1033
|
+
/** The quotes themselves, by provider, for execution with the same numbers the user saw. */
|
|
1034
|
+
quotes: Record<string, Quote>;
|
|
1035
|
+
}
|
|
1036
|
+
interface QuoteRoutesOptions {
|
|
1037
|
+
onUpdate?: (view: RouteListView) => void;
|
|
1038
|
+
/** Per-provider wall clock (default 20 s). */
|
|
1039
|
+
timeoutMs?: number;
|
|
1040
|
+
signal?: AbortSignal;
|
|
1041
|
+
now?: () => number;
|
|
1042
|
+
}
|
|
1043
|
+
/** Dry quotes from every enabled provider that claims the route, progressively. */
|
|
1044
|
+
declare function quoteRoutes(providers: readonly CrossChainProvider[], params: QuoteParams, options?: QuoteRoutesOptions): Promise<RouteListView>;
|
|
1045
|
+
/** Highest output first (same destination token for every quote); FASTEST = lowest ETA among them. */
|
|
1046
|
+
declare function rank(quotes: Quote[]): RouteView[];
|
|
1047
|
+
/** Vaulty's execution floor: 50 bps under the quoted minimum rides normal drift while the user reviews, still refuses a real reprice. */
|
|
1048
|
+
declare const FLOOR_TOLERANCE_BPS = 50n;
|
|
1049
|
+
declare function executionFloor(minDestAmount: bigint): bigint;
|
|
1050
|
+
interface SwapReviewMeta {
|
|
1051
|
+
/** Who runs the flow (the wallet's origin). */
|
|
1052
|
+
origin: string;
|
|
1053
|
+
providerLabel: string;
|
|
1054
|
+
fromSymbol: string;
|
|
1055
|
+
fromDecimals: number;
|
|
1056
|
+
toSymbol: string;
|
|
1057
|
+
toDecimals: number;
|
|
1058
|
+
fromChainName: string;
|
|
1059
|
+
toChainName: string;
|
|
1060
|
+
/** All of the user's addresses — recipient own/foreign. */
|
|
1061
|
+
own: string[];
|
|
1062
|
+
}
|
|
1063
|
+
/** What the swap sheet renders. Amounts are base-unit strings. */
|
|
1064
|
+
interface SwapApproval {
|
|
1065
|
+
provider: string;
|
|
1066
|
+
providerLabel: string;
|
|
1067
|
+
fromChain: string;
|
|
1068
|
+
toChain: string;
|
|
1069
|
+
fromChainName: string;
|
|
1070
|
+
toChainName: string;
|
|
1071
|
+
send: {
|
|
1072
|
+
symbol: string;
|
|
1073
|
+
decimals: number;
|
|
1074
|
+
amount: string;
|
|
1075
|
+
};
|
|
1076
|
+
receive: {
|
|
1077
|
+
symbol: string;
|
|
1078
|
+
decimals: number;
|
|
1079
|
+
amount: string;
|
|
1080
|
+
min: string;
|
|
1081
|
+
};
|
|
1082
|
+
recipient: {
|
|
1083
|
+
address: string;
|
|
1084
|
+
isOwn: boolean;
|
|
1085
|
+
};
|
|
1086
|
+
refundAddress: string;
|
|
1087
|
+
feeUsd: number;
|
|
1088
|
+
etaSeconds: number;
|
|
1089
|
+
validUntilMs: number;
|
|
1090
|
+
approvals: {
|
|
1091
|
+
token: string;
|
|
1092
|
+
spender: string;
|
|
1093
|
+
amount: string;
|
|
1094
|
+
}[];
|
|
1095
|
+
source: {
|
|
1096
|
+
kind: 'call';
|
|
1097
|
+
to: string;
|
|
1098
|
+
data?: string;
|
|
1099
|
+
value: string;
|
|
1100
|
+
deposit?: string;
|
|
1101
|
+
} | {
|
|
1102
|
+
kind: 'token-transfer';
|
|
1103
|
+
token: string;
|
|
1104
|
+
recipient: string;
|
|
1105
|
+
amount: string;
|
|
1106
|
+
memo?: string;
|
|
1107
|
+
} | {
|
|
1108
|
+
kind: 'serialized';
|
|
1109
|
+
} | {
|
|
1110
|
+
kind: 'typed-order';
|
|
1111
|
+
};
|
|
1112
|
+
checks: {
|
|
1113
|
+
status: 'ok' | 'warn' | 'unknown';
|
|
1114
|
+
label: string;
|
|
1115
|
+
}[];
|
|
1116
|
+
}
|
|
1117
|
+
declare const encodeApproveCalldata: (spender: string, amount: bigint) => string;
|
|
1118
|
+
declare function swapApprovalPayload(plan: Swap2ExecutionPlan, meta: SwapReviewMeta): SwapApproval;
|
|
1119
|
+
/** Matchers for exactly the operations the plan will perform, in the engine's order. */
|
|
1120
|
+
declare function planMatchers(plan: Swap2ExecutionPlan): GateMatcher[];
|
|
1121
|
+
/** The engine's `reviewExecution`: one sheet for the plan; approve → pre-approve exactly its operations; decline → nothing sent. */
|
|
1122
|
+
declare function reviewSwapPlan(approvals: ApprovalQueue, gate: ReviewGate, meta: SwapReviewMeta): (plan: Swap2ExecutionPlan) => Promise<void>;
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Pending swaps across service-worker restarts: records in `chrome.storage.local`,
|
|
1126
|
+
* a `chrome.alarms` tick every minute while anything is active, resumable by
|
|
1127
|
+
* the swap2 id (`awk1.<provider>.<intent>[.<sourceTxHash>]`), the provider's
|
|
1128
|
+
* `trackIntent` as the only source of truth.
|
|
1129
|
+
*
|
|
1130
|
+
* States (the provider's `IntentState` plus the wallet's own `claiming`):
|
|
1131
|
+
* pending ──deposit seen──▶ pending (depositReceived)
|
|
1132
|
+
* pending ──attested (CCTP)──▶ action-required ──user claims──▶ claiming ──mint seen──▶ completed
|
|
1133
|
+
* pending ──▶ completed | failed | refunded | refund-pending | partial | cancelled | expired
|
|
1134
|
+
* A tracker error leaves the record as it was (with `lastError`); a record older
|
|
1135
|
+
* than `giveUpAfterMs` with no progress becomes `expired` so the alarm stops.
|
|
1136
|
+
*/
|
|
1137
|
+
|
|
1138
|
+
type SwapRecordStatus = IntentStatus['state'] | 'claiming';
|
|
1139
|
+
interface SwapRecord {
|
|
1140
|
+
/** swap2 id — `awk1.<provider>.<intentId>[.<sourceTxHash>]`. */
|
|
1141
|
+
id: string;
|
|
1142
|
+
provider: string;
|
|
1143
|
+
fromChain: string;
|
|
1144
|
+
toChain: string;
|
|
1145
|
+
fromToken: string;
|
|
1146
|
+
toToken: string;
|
|
1147
|
+
/** Base units. */
|
|
1148
|
+
amountIn: string;
|
|
1149
|
+
destAmount: string;
|
|
1150
|
+
recipient: string;
|
|
1151
|
+
sourceTxHash: string | null;
|
|
1152
|
+
destTxHash: string | null;
|
|
1153
|
+
status: SwapRecordStatus;
|
|
1154
|
+
depositReceived?: boolean;
|
|
1155
|
+
/** Two-phase routes: where and whether the claim can be built now. */
|
|
1156
|
+
claim?: {
|
|
1157
|
+
chain: string;
|
|
1158
|
+
ready: boolean;
|
|
1159
|
+
};
|
|
1160
|
+
createdAt: number;
|
|
1161
|
+
updatedAt: number;
|
|
1162
|
+
/** Last tracker problem (RPC/API), kept until the next successful check. */
|
|
1163
|
+
lastError?: string;
|
|
1164
|
+
checks: number;
|
|
1165
|
+
}
|
|
1166
|
+
declare const ACTIVE_STATUSES: ReadonlySet<SwapRecordStatus>;
|
|
1167
|
+
declare function parseSwapId(id: string): {
|
|
1168
|
+
provider: string;
|
|
1169
|
+
intentId: string;
|
|
1170
|
+
sourceTxHash?: string;
|
|
1171
|
+
} | null;
|
|
1172
|
+
declare class SwapsStore {
|
|
1173
|
+
private readonly storage;
|
|
1174
|
+
private _records;
|
|
1175
|
+
private readonly _listeners;
|
|
1176
|
+
constructor(storage: JsonStorage<SwapRecord[]>);
|
|
1177
|
+
list(): Promise<SwapRecord[]>;
|
|
1178
|
+
get(id: string): Promise<SwapRecord | null>;
|
|
1179
|
+
active(): Promise<SwapRecord[]>;
|
|
1180
|
+
add(record: SwapRecord): Promise<void>;
|
|
1181
|
+
update(id: string, patch: Partial<SwapRecord>): Promise<SwapRecord | null>;
|
|
1182
|
+
remove(id: string): Promise<void>;
|
|
1183
|
+
onChange(listener: () => void): () => void;
|
|
1184
|
+
private write;
|
|
1185
|
+
}
|
|
1186
|
+
interface SwapTrackerDeps {
|
|
1187
|
+
store: SwapsStore;
|
|
1188
|
+
/** The provider's view of the swap (`CrossChainRouter.trackIntent`). */
|
|
1189
|
+
status(record: SwapRecord): Promise<IntentStatus>;
|
|
1190
|
+
/** Start / stop the periodic alarm (`chrome.alarms.create(name, { periodInMinutes: 1 })`). */
|
|
1191
|
+
alarm: {
|
|
1192
|
+
start(): Promise<void> | void;
|
|
1193
|
+
stop(): Promise<void> | void;
|
|
1194
|
+
};
|
|
1195
|
+
now?: () => number;
|
|
1196
|
+
/** No progress for this long → `expired` (default 48 h). */
|
|
1197
|
+
giveUpAfterMs?: number;
|
|
1198
|
+
}
|
|
1199
|
+
declare class SwapTracker {
|
|
1200
|
+
private readonly deps;
|
|
1201
|
+
private readonly now;
|
|
1202
|
+
private ticking;
|
|
1203
|
+
constructor(deps: SwapTrackerDeps);
|
|
1204
|
+
/** Record a just-executed swap and make sure the alarm runs. */
|
|
1205
|
+
track(input: Omit<SwapRecord, 'createdAt' | 'updatedAt' | 'checks' | 'status' | 'destTxHash'> & {
|
|
1206
|
+
status?: SwapRecordStatus;
|
|
1207
|
+
}): Promise<SwapRecord>;
|
|
1208
|
+
/** After the user's claim transaction went out: keep polling until the provider sees the mint. */
|
|
1209
|
+
claimed(id: string, destTxHash: string): Promise<void>;
|
|
1210
|
+
/** One alarm tick: check every active record once; stop the alarm when nothing is active. Concurrent ticks coalesce. */
|
|
1211
|
+
tick(): Promise<void>;
|
|
1212
|
+
private run;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Earn plan review — the Earn modules' counterpart of the swap sheet. A module
|
|
1217
|
+
* that exposes its plan (Aave / Morpho `planSupply` & co: ERC-20 approvals
|
|
1218
|
+
* first, then the call) gets ONE sheet for the whole plan; on approval exactly
|
|
1219
|
+
* those calls are pre-approved in the {@link ReviewGate} so the module's own
|
|
1220
|
+
* `supply()` / `withdraw()` / … passes through the reviewed account without a
|
|
1221
|
+
* second window. Modules without a plan API (Mellow, Lido, SSV, Jito) are
|
|
1222
|
+
* reviewed per transaction by the reviewed account itself — still every send.
|
|
1223
|
+
*/
|
|
1224
|
+
|
|
1225
|
+
interface EarnTx {
|
|
1226
|
+
to: string;
|
|
1227
|
+
data?: string | undefined;
|
|
1228
|
+
/** Base units, decimal string. */
|
|
1229
|
+
value: string;
|
|
1230
|
+
}
|
|
1231
|
+
interface EarnPlan {
|
|
1232
|
+
approvals: EarnTx[];
|
|
1233
|
+
tx: EarnTx;
|
|
1234
|
+
}
|
|
1235
|
+
interface EarnReviewMeta {
|
|
1236
|
+
origin: string;
|
|
1237
|
+
/** 'Aave v3', 'Morpho', … */
|
|
1238
|
+
module: string;
|
|
1239
|
+
/** 'Supply', 'Withdraw', 'Borrow', 'Repay', 'Stake', … */
|
|
1240
|
+
action: string;
|
|
1241
|
+
network: EvmNetwork;
|
|
1242
|
+
/** Human lines the sheet shows (asset, amount, APY, market/vault, health factor…). */
|
|
1243
|
+
facts: {
|
|
1244
|
+
k: string;
|
|
1245
|
+
v: string;
|
|
1246
|
+
}[];
|
|
1247
|
+
/** What the module says about the plan (its own refusals never reach here — they throw before). */
|
|
1248
|
+
checks?: {
|
|
1249
|
+
status: 'ok' | 'warn' | 'unknown';
|
|
1250
|
+
label: string;
|
|
1251
|
+
}[];
|
|
1252
|
+
/** tx-preview-evm result for approvals + call, attached by the wallet (JSON-safe) or null. */
|
|
1253
|
+
preview?: unknown;
|
|
1254
|
+
}
|
|
1255
|
+
interface EarnApproval {
|
|
1256
|
+
module: string;
|
|
1257
|
+
action: string;
|
|
1258
|
+
network: EvmNetwork;
|
|
1259
|
+
facts: {
|
|
1260
|
+
k: string;
|
|
1261
|
+
v: string;
|
|
1262
|
+
}[];
|
|
1263
|
+
approvals: EarnTx[];
|
|
1264
|
+
tx: EarnTx;
|
|
1265
|
+
checks: {
|
|
1266
|
+
status: 'ok' | 'warn' | 'unknown';
|
|
1267
|
+
label: string;
|
|
1268
|
+
}[];
|
|
1269
|
+
preview: unknown;
|
|
1270
|
+
}
|
|
1271
|
+
declare function earnPlanMatchers(network: EvmNetwork, plan: EarnPlan): GateMatcher[];
|
|
1272
|
+
declare function earnApprovalPayload(plan: EarnPlan, meta: EarnReviewMeta): EarnApproval;
|
|
1273
|
+
/** One sheet for the plan; approve → pre-approve exactly its calls; decline → ReviewDeclined, nothing sent. */
|
|
1274
|
+
declare function reviewEarnPlan(approvals: ApprovalQueue, gate: ReviewGate, meta: EarnReviewMeta, plan: EarnPlan): Promise<void>;
|
|
1275
|
+
|
|
1276
|
+
declare const TONCONNECT_ERRORS: {
|
|
1277
|
+
readonly unknown: 0;
|
|
1278
|
+
readonly badRequest: 1;
|
|
1279
|
+
readonly manifestNotFound: 2;
|
|
1280
|
+
readonly manifestContent: 3;
|
|
1281
|
+
readonly unknownApp: 100;
|
|
1282
|
+
readonly userDeclined: 300;
|
|
1283
|
+
readonly methodNotSupported: 400;
|
|
1284
|
+
};
|
|
1285
|
+
declare class TonConnectError extends Error {
|
|
1286
|
+
readonly code: number;
|
|
1287
|
+
constructor(code: number, message: string);
|
|
1288
|
+
}
|
|
1289
|
+
interface TonConnectRequest {
|
|
1290
|
+
manifestUrl: string;
|
|
1291
|
+
/** `ton_addr` always; `ton_proof` with the dApp's payload when asked. */
|
|
1292
|
+
proofPayload?: string;
|
|
1293
|
+
}
|
|
1294
|
+
declare function parseConnectRequest(message: unknown): TonConnectRequest;
|
|
1295
|
+
interface TonConnectMessage {
|
|
1296
|
+
/** Destination, as given (friendly or raw). */
|
|
1297
|
+
address: string;
|
|
1298
|
+
/** Nanotons, decimal string. */
|
|
1299
|
+
amount: string;
|
|
1300
|
+
/** base64 BoC body. */
|
|
1301
|
+
payload?: string;
|
|
1302
|
+
/** base64 BoC state init. */
|
|
1303
|
+
stateInit?: string;
|
|
1304
|
+
}
|
|
1305
|
+
interface TonConnectSendTransaction {
|
|
1306
|
+
validUntil?: number;
|
|
1307
|
+
network?: string;
|
|
1308
|
+
from?: string;
|
|
1309
|
+
messages: TonConnectMessage[];
|
|
1310
|
+
}
|
|
1311
|
+
declare const TONCONNECT_MAX_MESSAGES = 4;
|
|
1312
|
+
/** `sendTransaction` params[0] (a JSON string per the spec, or an object). */
|
|
1313
|
+
declare function parseSendTransaction(param: unknown, options?: {
|
|
1314
|
+
maxMessages?: number;
|
|
1315
|
+
network?: string;
|
|
1316
|
+
}): TonConnectSendTransaction;
|
|
1317
|
+
type TonSignDataPayload = {
|
|
1318
|
+
type: 'text';
|
|
1319
|
+
text: string;
|
|
1320
|
+
} | {
|
|
1321
|
+
type: 'binary';
|
|
1322
|
+
bytes: string;
|
|
1323
|
+
};
|
|
1324
|
+
/** `signData` params[0]: text or binary only — `cell` payloads need schema-aware rendering and are refused by name. */
|
|
1325
|
+
declare function parseSignData(param: unknown): TonSignDataPayload;
|
|
1326
|
+
interface TonAddressParts {
|
|
1327
|
+
workchain: number;
|
|
1328
|
+
hash: Uint8Array;
|
|
1329
|
+
}
|
|
1330
|
+
/** The bytes a wallet signs for `ton_proof` (already hashed — sign them raw with ed25519). */
|
|
1331
|
+
declare function tonProofMessage(input: {
|
|
1332
|
+
address: TonAddressParts;
|
|
1333
|
+
domain: string;
|
|
1334
|
+
timestamp: number;
|
|
1335
|
+
payload: string;
|
|
1336
|
+
}): Uint8Array;
|
|
1337
|
+
/** The bytes a wallet signs for `signData` (text / binary). */
|
|
1338
|
+
declare function tonSignDataMessage(input: {
|
|
1339
|
+
address: TonAddressParts;
|
|
1340
|
+
domain: string;
|
|
1341
|
+
timestamp: number;
|
|
1342
|
+
payload: TonSignDataPayload;
|
|
1343
|
+
}): Uint8Array;
|
|
1344
|
+
/** `0:abcd…` → parts; friendly addresses are the wallet package's job (it hands us the raw form). */
|
|
1345
|
+
declare function parseRawAddress(raw: string): TonAddressParts;
|
|
1346
|
+
/** The dApp's domain for proofs, from the page origin (host only, as the spec's `domain.value`). */
|
|
1347
|
+
declare function proofDomain(origin: string): string;
|
|
1348
|
+
/** Our relay errors → TON Connect error codes. */
|
|
1349
|
+
declare function tonConnectErrorCode(e: unknown): number;
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* Phishing / impersonation checks on a page's host, against a SHIPPED list
|
|
1353
|
+
* (never fetched at runtime — decision 10). The list format is MetaMask's
|
|
1354
|
+
* eth-phishing-detect `config.json`: `blacklist` / `whitelist` (hosts; a listed
|
|
1355
|
+
* domain covers its subdomains), `fuzzylist` + `tolerance` (well-known hosts a
|
|
1356
|
+
* lookalike is measured against by Levenshtein distance). On top: punycode
|
|
1357
|
+
* (`xn--`) labels are flagged, because mixed-script lookalikes are the common
|
|
1358
|
+
* impersonation shape.
|
|
1359
|
+
*/
|
|
1360
|
+
interface PhishingList {
|
|
1361
|
+
version?: number;
|
|
1362
|
+
tolerance?: number;
|
|
1363
|
+
blacklist: readonly string[];
|
|
1364
|
+
whitelist?: readonly string[];
|
|
1365
|
+
fuzzylist?: readonly string[];
|
|
1366
|
+
/** The wallet's own additions to the fuzzylist (the big dApps our users visit): same semantics. */
|
|
1367
|
+
extraFuzzylist?: readonly string[];
|
|
1368
|
+
}
|
|
1369
|
+
interface HostCheck {
|
|
1370
|
+
host: string;
|
|
1371
|
+
/** On the blocklist (or a subdomain of a listed domain): refuse to inject / connect. */
|
|
1372
|
+
blocked: boolean;
|
|
1373
|
+
/** Human lines for the approval window; empty when nothing is odd. */
|
|
1374
|
+
warnings: string[];
|
|
1375
|
+
}
|
|
1376
|
+
declare class PhishingDetector {
|
|
1377
|
+
private readonly black;
|
|
1378
|
+
private readonly white;
|
|
1379
|
+
private readonly fuzzy;
|
|
1380
|
+
private readonly tolerance;
|
|
1381
|
+
constructor(list: PhishingList);
|
|
1382
|
+
get size(): number;
|
|
1383
|
+
check(hostOrOrigin: string): HostCheck;
|
|
1384
|
+
/**
|
|
1385
|
+
* `metamask.io-login.com`, `app-uniswap.com`, `uniswap.org.evil.net`: a known site's name used as (part of) a label
|
|
1386
|
+
* of a host that is not that site. Names shorter than 5 characters are skipped (too many honest matches).
|
|
1387
|
+
*/
|
|
1388
|
+
brandInLabel(host: string): {
|
|
1389
|
+
known: string;
|
|
1390
|
+
label: string;
|
|
1391
|
+
} | null;
|
|
1392
|
+
/** eth-phishing-detect's fuzzy check: the host (minus the TLD and a www.) within `tolerance` edits of a known one. */
|
|
1393
|
+
lookalike(host: string): string | null;
|
|
1394
|
+
}
|
|
1395
|
+
declare const normalizeHost: (h: string) => string;
|
|
1396
|
+
declare function hostOf(hostOrOrigin: string): string;
|
|
1397
|
+
declare function levenshtein(a: string, b: string): number;
|
|
1398
|
+
|
|
1399
|
+
interface AssertionInput {
|
|
1400
|
+
/** `PublicKeyCredential.rawId`, base64url. */
|
|
1401
|
+
credentialId: string;
|
|
1402
|
+
/** `AuthenticatorAssertionResponse.authenticatorData`, base64url. */
|
|
1403
|
+
authenticatorData: string;
|
|
1404
|
+
/** `AuthenticatorAssertionResponse.clientDataJSON`, base64url. */
|
|
1405
|
+
clientDataJSON: string;
|
|
1406
|
+
/** `AuthenticatorAssertionResponse.signature` (DER), base64url. */
|
|
1407
|
+
signature: string;
|
|
1408
|
+
}
|
|
1409
|
+
interface AssertionExpectations {
|
|
1410
|
+
/** SPKI public key of the credential, base64 (what `getPublicKey()` returned at registration). */
|
|
1411
|
+
publicKey: string;
|
|
1412
|
+
/** COSE alg of the key; only -7 is accepted. */
|
|
1413
|
+
alg: number;
|
|
1414
|
+
/** The challenge the worker handed the page (raw bytes; the page sent it base64url in clientDataJSON). */
|
|
1415
|
+
challenge: Uint8Array;
|
|
1416
|
+
/** `chrome-extension://<id>` */
|
|
1417
|
+
origin: string;
|
|
1418
|
+
/** The RP id the credential was created for (the extension id); its SHA-256 must be the first 32 bytes of authenticatorData. */
|
|
1419
|
+
rpId: string;
|
|
1420
|
+
/** Require the UV flag (user verification: biometric / PIN) — on for signing. */
|
|
1421
|
+
requireUserVerification?: boolean;
|
|
1422
|
+
crypto?: Crypto;
|
|
1423
|
+
}
|
|
1424
|
+
type AssertionVerdict = {
|
|
1425
|
+
ok: true;
|
|
1426
|
+
counter: number;
|
|
1427
|
+
} | {
|
|
1428
|
+
ok: false;
|
|
1429
|
+
reason: string;
|
|
1430
|
+
};
|
|
1431
|
+
/** Verify one assertion. Never throws on bad input — a verdict with the reason, so the worker can refuse in-band. */
|
|
1432
|
+
declare function verifyAssertion(input: AssertionInput, expect: AssertionExpectations): Promise<AssertionVerdict>;
|
|
1433
|
+
/** DER `SEQUENCE { INTEGER r, INTEGER s }` → 64-byte r‖s; `null` when malformed. */
|
|
1434
|
+
declare function derToRaw(der: Uint8Array): Uint8Array | null;
|
|
1435
|
+
/** Raw r‖s → DER (tests build assertions with WebCrypto, which signs raw). */
|
|
1436
|
+
declare function rawToDer(raw: Uint8Array): Uint8Array;
|
|
1437
|
+
/**
|
|
1438
|
+
* The challenge for one approval request: SHA-256 over the request id, kind,
|
|
1439
|
+
* origin and payload, so an assertion made for one sheet cannot answer another.
|
|
1440
|
+
*/
|
|
1441
|
+
declare function approvalChallenge(request: {
|
|
1442
|
+
id: string;
|
|
1443
|
+
kind: string;
|
|
1444
|
+
origin: string;
|
|
1445
|
+
payload: unknown;
|
|
1446
|
+
}, c?: Crypto): Promise<Uint8Array>;
|
|
1447
|
+
|
|
1448
|
+
/**
|
|
1449
|
+
* Address poisoning: an attacker sends dust from an address whose first and
|
|
1450
|
+
* last characters match one the user actually paid, hoping the next copy-paste
|
|
1451
|
+
* picks the wrong one. The wallet has no transaction history, so it keeps its
|
|
1452
|
+
* own book of recipients the user confirmed (per chain) and warns when a new
|
|
1453
|
+
* recipient looks like one of them without being it. "Fresh address" (no
|
|
1454
|
+
* activity on chain) is the other half of the signal; the EVM preview computes it.
|
|
1455
|
+
*/
|
|
1456
|
+
|
|
1457
|
+
type RecipientsState = Record<string, string[]>;
|
|
1458
|
+
/** How many characters at each end must match (after any `0x`). */
|
|
1459
|
+
declare const LOOKALIKE_PREFIX = 4;
|
|
1460
|
+
declare const LOOKALIKE_SUFFIX = 4;
|
|
1461
|
+
/** A known recipient that `address` imitates (same start and end, different address), or `null`. */
|
|
1462
|
+
declare function lookalikeOf(address: string, known: readonly string[]): string | null;
|
|
1463
|
+
/** Recipients the user confirmed on each chain (`eip155`, `solana`, …), newest last, capped. */
|
|
1464
|
+
declare class RecipientBook {
|
|
1465
|
+
private readonly storage;
|
|
1466
|
+
private readonly cap;
|
|
1467
|
+
private _state;
|
|
1468
|
+
constructor(storage: JsonStorage<RecipientsState>, cap?: number);
|
|
1469
|
+
private state;
|
|
1470
|
+
list(namespace: string): Promise<string[]>;
|
|
1471
|
+
remember(namespace: string, address: string): Promise<void>;
|
|
1472
|
+
/** `known` → the address is one the user paid before; `lookalike` → it imitates one. */
|
|
1473
|
+
check(namespace: string, address: string): Promise<{
|
|
1474
|
+
known: boolean;
|
|
1475
|
+
lookalike: string | null;
|
|
1476
|
+
}>;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
export { ACTIVE_STATUSES, APPROVALS_MAX_PENDING_PER_ORIGIN, type AddChainApproval, type AlarmsLike, type ApprovalKind, ApprovalQueue, type ApprovalQueueOptions, type ApprovalRequest, type ApprovalWindow, type AssertionExpectations, type AssertionInput, type AssertionVerdict, BTC_CHAIN, BTC_ESPLORA, CHAIN_ACCOUNT_KEYS, type ChainAccountsKey, type ChainDescriptor, type ConnectApproval, DEFAULT_CHAIN, DEFAULT_NETWORKS, type EarnApproval, type EarnPlan, type EarnReviewMeta, type EarnTx, type EvmNetwork, type EvmSigner, FLOOR_TOLERANCE_BPS, type GateMatcher, type GatedOp, type HostCheck, type JsonStorage, KEYRING_STORAGE_KEY, LOOKALIKE_PREFIX, LOOKALIKE_SUFFIX, NEAR_CHAIN, NEAR_PROVIDERS, NON_EVM_CHAINS, NetworksTable, type OriginPermission, type OriginVia, PAGE_TARGET, PORT_NAME, type PageEvent, type PageRequest, type PageResponse, PermissionStore, type PermissionsState, PhishingDetector, type PhishingList, type PortSender, type PreparedTx, QueueLimitError, type QuoteRoutesOptions, READ_METHODS, RELAY_ERRORS, RecipientBook, type RecipientsState, type RelayChain, type RelayError, type RelayEvent, type RelayRequest, type RelayResponse, type RelayToContent, type RelayToWorker, RequestRouter, ReviewDeclined, ReviewGate, type RouteListView, type RouteView, type RouterDeps, RouterError, SESSION_DEFAULT_AUTOLOCK_MS, SIGNING_METHODS, SOLANA_CHAIN, SOLANA_RPCS, type SealedBox, Session, type SessionAlarms, type SessionOptions, type SessionStorage, type SignApproval, type SignDeps, type SignRequestInfo, type SiweContext, type SiweInfo, type SiweMessage, type StorageAreaLike, type SwapApproval, type SwapRecord, type SwapRecordStatus, type SwapReviewMeta, SwapTracker, type SwapTrackerDeps, SwapsStore, type SwitchChainApproval, TONCONNECT_ERRORS, TONCONNECT_MAX_MESSAGES, TON_CHAIN, TON_ENDPOINTS, TRON_CHAIN, TRON_PROVIDERS, type TonAddressParts, TonConnectError, type TonConnectMessage, type TonConnectRequest, type TonConnectSendTransaction, type TonSignDataPayload, type TypedData, VAULT_DEFAULT_ITERATIONS, VAULT_MIN_ITERATIONS, VAULT_MIN_PASSWORD_LENGTH, VAULT_MIN_SCRYPT, VAULT_SCRYPT, VAULT_STORAGE_KEY, Vault, type VaultEnvelope, type VaultEnvelopeV1, type VaultEnvelopeV2, VaultError, type VaultErrorCode, type VaultKdfInfo, type VaultOptions, type VaultPasskey, type VaultStorage, ZCASH_BLOCKCHAIR, ZCASH_CHAIN, approvalChallenge, caip2FromChainId, chainHex, chainTxMatcher, checkSenderOrigin, chromeAlarms, chromeJsonStorage, chromeKeyringStorage, chromeLocalVaultStorage, chromeSessionStorage, classifySignRequest, createSignHandler, decodedMessage, derToRaw, describeSiwe, earnApprovalPayload, earnPlanMatchers, encodeApproveCalldata, evmCallMatcher, executionFloor, fromBase64Url, hostOf, isCatalogChain, isEnvelopeV1, isEnvelopeV2, isLocalHost, isPageEvent, isPageRequest, isPageResponse, isRelayRequest, isRelayToContent, levenshtein, lookalikeOf, memoryAlarms, memoryApprovalWindow, memoryJsonStorage, memorySessionStorage, memoryVaultStorage, messageText, newRequestId, nonEvmChain, normalizeHost, parseAddChainParams, parseConnectRequest, parseRawAddress, parseSendTransaction, parseSignData, parseSiwe, parseSwapId, planMatchers, prepareTransaction, proofDomain, quoteRoutes, rank, rawToDer, relayError, reviewEarnPlan, reviewSwapPlan, siweProblems, stampOrigin, swapApprovalPayload, toBase64Url, toRelayError, tonConnectErrorCode, tonProofMessage, tonSignDataMessage, vaultSecretStorage, verifyAssertion };
|