@naculus/connect-appkit-core 0.2.3 → 0.2.5
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 +269 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +287 -2
- package/dist/index.d.ts +287 -2
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Namespace, UniversalWalletSession, TokenConfig, WalletError, WalletErrorCode, NameResolverConfig, NameResolver } from '@naculus/connect-core';
|
|
1
|
+
import { Namespace, UniversalWalletSession, WalletCapabilities, TokenConfig, WalletError, WalletErrorCode, NameResolverConfig, NameResolver, NotificationSettings, InAppChannel, NotificationItem } from '@naculus/connect-core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The types the framework-neutral half of appkit is built on.
|
|
@@ -42,6 +42,149 @@ interface Web3State {
|
|
|
42
42
|
error: Error | null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* EIP-5792 capability answers, normalised once.
|
|
47
|
+
*
|
|
48
|
+
* Deciding how to execute before sending is the whole point of
|
|
49
|
+
* `wallet_getCapabilities`, and the reasoning that turns a raw answer into
|
|
50
|
+
* that decision is not React's. It lived inside the React hook, which meant a
|
|
51
|
+
* Vue composable could only get it by copying — and a second copy of "absence
|
|
52
|
+
* is not a denial" is exactly the copy that drifts back into `false`.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether a wallet can execute a batch atomically.
|
|
57
|
+
*
|
|
58
|
+
* Three values, not two. A wallet that does not implement
|
|
59
|
+
* `wallet_getCapabilities` has not said no — it has said nothing, and EIP-5792
|
|
60
|
+
* is explicit that absence is not a denial. Collapsing that into `false` sends
|
|
61
|
+
* every such wallet down the sequential path, which is the one where an
|
|
62
|
+
* approve can land and the swap it was for can fail.
|
|
63
|
+
*/
|
|
64
|
+
type AtomicSupport = "supported" | "unsupported" | "unknown";
|
|
65
|
+
interface ChainCapabilities {
|
|
66
|
+
/** CAIP-2 chain this describes. */
|
|
67
|
+
chainId: string;
|
|
68
|
+
atomic: AtomicSupport;
|
|
69
|
+
/** Largest batch the wallet will accept, when it says. */
|
|
70
|
+
maxBatchSize?: number;
|
|
71
|
+
/** The raw entry, for capabilities this SDK does not model. */
|
|
72
|
+
raw: WalletCapabilities;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Turn a wallet's raw answer into per-chain entries.
|
|
76
|
+
*
|
|
77
|
+
* Entries that are not objects are dropped rather than coerced: a wallet
|
|
78
|
+
* answering `null` for a chain has said nothing about it, and inventing an
|
|
79
|
+
* `"unsupported"` there would be the same mistake as reading absence as denial.
|
|
80
|
+
*/
|
|
81
|
+
declare function normalizeCapabilities(raw: Record<string, unknown> | null | undefined): Record<string, ChainCapabilities>;
|
|
82
|
+
/**
|
|
83
|
+
* Find the entry for a chain, by CAIP-2 or by the hex form.
|
|
84
|
+
*
|
|
85
|
+
* Connectors normalise keys to CAIP-2, but a wallet answering in raw hex
|
|
86
|
+
* through a custom connector should still be found rather than reported as
|
|
87
|
+
* "unknown" next to an entry that is sitting right there.
|
|
88
|
+
*/
|
|
89
|
+
declare function selectChainCapabilities(capabilities: Record<string, ChainCapabilities> | null | undefined, chainId: string | null | undefined): ChainCapabilities | null;
|
|
90
|
+
/**
|
|
91
|
+
* Atomic support for a chain.
|
|
92
|
+
*
|
|
93
|
+
* `"unknown"` covers every case where we have not been told: no query yet, a
|
|
94
|
+
* wallet without the method, a failed query, one still in flight. A caller
|
|
95
|
+
* choosing between an atomic and a sequential path needs that distinction, not
|
|
96
|
+
* a confident `false`.
|
|
97
|
+
*/
|
|
98
|
+
declare function atomicSupportFor(capabilities: Record<string, ChainCapabilities> | null | undefined, chainId: string | null | undefined): AtomicSupport;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Solana signer roles, read once for both frameworks.
|
|
102
|
+
*
|
|
103
|
+
* `@naculus/connector-solana` splits a connected wallet into `identity`,
|
|
104
|
+
* `signer` and `payer`, each `null` when the wallet declared it cannot fill
|
|
105
|
+
* that job. That answer is the connector's. What this module owns is the part
|
|
106
|
+
* a UI has to decide before it can show anything: *why* there is no answer —
|
|
107
|
+
* because nothing is connected, because the connected wallet is not Solana,
|
|
108
|
+
* or because the connector predates the roles API and has not said. Those are
|
|
109
|
+
* three different screens, and a hook that hands back one `null` for all of
|
|
110
|
+
* them makes every consumer rediscover the distinction.
|
|
111
|
+
*/
|
|
112
|
+
/** Which account this is, with no ability to sign for it. */
|
|
113
|
+
interface SolanaIdentity {
|
|
114
|
+
/** Base58 public key. */
|
|
115
|
+
readonly address: string;
|
|
116
|
+
/** CAIP-2 chain this account is being used on. */
|
|
117
|
+
readonly chain: string;
|
|
118
|
+
}
|
|
119
|
+
/** An account that will contribute a signature without submitting anything. */
|
|
120
|
+
interface SolanaSigner extends SolanaIdentity {
|
|
121
|
+
signMessage?: (message: Uint8Array) => Promise<Uint8Array>;
|
|
122
|
+
signTransaction: (transaction: Uint8Array) => Promise<Uint8Array>;
|
|
123
|
+
/** Absent, not throwing, when the wallet has no batch feature. */
|
|
124
|
+
signAllTransactions?: (transactions: Uint8Array[]) => Promise<Uint8Array[]>;
|
|
125
|
+
}
|
|
126
|
+
/** An account that signs and broadcasts through the wallet's own RPC. */
|
|
127
|
+
interface SolanaPayer extends SolanaIdentity {
|
|
128
|
+
/** Returns the base58 transaction signature the cluster accepted. */
|
|
129
|
+
signAndSendTransaction: (transaction: Uint8Array) => Promise<string>;
|
|
130
|
+
}
|
|
131
|
+
/** What the wallet declared, recorded at discovery. Two-state on purpose. */
|
|
132
|
+
interface SolanaWalletFeatures {
|
|
133
|
+
signMessage: boolean;
|
|
134
|
+
signTransaction: boolean;
|
|
135
|
+
signAllTransactions: boolean;
|
|
136
|
+
signAndSendTransaction: boolean;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The roles a connected account can fill.
|
|
140
|
+
*
|
|
141
|
+
* Structurally identical to `SolanaRoles` in `@naculus/connector-solana`,
|
|
142
|
+
* declared here rather than imported because this package depends only on
|
|
143
|
+
* `@naculus/connect-core`, and the connector's own declaration is what its
|
|
144
|
+
* `getRoles` returns — a value of that type satisfies this one unchanged.
|
|
145
|
+
*/
|
|
146
|
+
interface SolanaRoles {
|
|
147
|
+
identity: SolanaIdentity;
|
|
148
|
+
signer: SolanaSigner | null;
|
|
149
|
+
payer: SolanaPayer | null;
|
|
150
|
+
features: SolanaWalletFeatures;
|
|
151
|
+
}
|
|
152
|
+
/** The part of a connector this needs. `getRoles` is optional on purpose. */
|
|
153
|
+
interface SolanaRolesSource {
|
|
154
|
+
getRoles?: (session: unknown) => SolanaRoles | null;
|
|
155
|
+
}
|
|
156
|
+
/** The part of a session this needs. */
|
|
157
|
+
interface SessionLike {
|
|
158
|
+
walletType?: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Why `roles` is null.
|
|
162
|
+
*
|
|
163
|
+
* - `no_session` — nothing connected, or the connector has no live session
|
|
164
|
+
* for the one the app holds (a restored session before reconnect).
|
|
165
|
+
* - `not_solana` — the connected wallet is on another namespace. Not an
|
|
166
|
+
* error; a Solana panel simply does not apply.
|
|
167
|
+
* - `unreported` — the connector does not expose `getRoles`. An older
|
|
168
|
+
* `@naculus/connector-solana`, or a custom one. The wallet has not said
|
|
169
|
+
* what it can do, which is not the same as saying it can do nothing.
|
|
170
|
+
*/
|
|
171
|
+
type SolanaRolesAbsence = "no_session" | "not_solana" | "unreported";
|
|
172
|
+
type SolanaRolesState = {
|
|
173
|
+
roles: SolanaRoles;
|
|
174
|
+
absence: null;
|
|
175
|
+
} | {
|
|
176
|
+
roles: null;
|
|
177
|
+
absence: SolanaRolesAbsence;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Read the roles for the session the app holds.
|
|
181
|
+
*
|
|
182
|
+
* Synchronous and pure: the connector already recorded what the wallet
|
|
183
|
+
* declared, so there is nothing to await and no prompt to trigger. Reading
|
|
184
|
+
* roles must never cost the user an approval dialog.
|
|
185
|
+
*/
|
|
186
|
+
declare function readSolanaRoles(connector: SolanaRolesSource | null | undefined, session: SessionLike | null | undefined): SolanaRolesState;
|
|
187
|
+
|
|
45
188
|
/**
|
|
46
189
|
* Which chains a session can actually switch to, and what the current one is.
|
|
47
190
|
*
|
|
@@ -282,6 +425,148 @@ declare function getResolver(config?: NameResolverConfig): NameResolver;
|
|
|
282
425
|
/** Test seam: drop the shared instance. */
|
|
283
426
|
declare function __resetResolverForTests(): void;
|
|
284
427
|
|
|
428
|
+
/** Framework-neutral in-app notification actions and settings persistence. */
|
|
429
|
+
|
|
430
|
+
interface NotificationSettingsStorage {
|
|
431
|
+
getItem<T>(key: string): Promise<T | null>;
|
|
432
|
+
setItem<T>(key: string, value: T): Promise<void>;
|
|
433
|
+
removeItem(key: string): Promise<void>;
|
|
434
|
+
}
|
|
435
|
+
declare const NOTIFICATION_SETTINGS_KEY = "naculus_notif_react_settings";
|
|
436
|
+
declare const DEFAULT_NOTIFICATION_SETTINGS: NotificationSettings;
|
|
437
|
+
/** Preserve the existing localStorage adapter and fail-open UI fallback. */
|
|
438
|
+
declare function createNotificationSettingsStorage(): NotificationSettingsStorage;
|
|
439
|
+
declare function loadNotificationSettings(storage: NotificationSettingsStorage): Promise<NotificationSettings | null>;
|
|
440
|
+
declare function saveNotificationSettings(storage: NotificationSettingsStorage, settings: NotificationSettings): void;
|
|
441
|
+
declare function updateNotificationSettings(current: NotificationSettings, update: Partial<NotificationSettings>): NotificationSettings;
|
|
442
|
+
declare function channelsUpdate(channels: string[]): Partial<NotificationSettings>;
|
|
443
|
+
declare function mutedChainSettings(settings: NotificationSettings, chainId: string, muted: boolean): Partial<NotificationSettings>;
|
|
444
|
+
declare function createNotification(item: Omit<NotificationItem, "id" | "timestamp">): NotificationItem;
|
|
445
|
+
declare function addInAppNotification(channel: InAppChannel, item: Omit<NotificationItem, "id" | "timestamp">): void;
|
|
446
|
+
/** InAppChannel has no remove-one method; preserve its clear/replay behavior. */
|
|
447
|
+
declare function clearOneInAppNotification(channel: InAppChannel, id: string): Promise<NotificationItem[]>;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Destination-address checks shared by the React hook and the Vue composable.
|
|
451
|
+
*
|
|
452
|
+
* Syntactic only. `"ok"` means none of these checks found anything wrong —
|
|
453
|
+
* not that the recipient is who the user thinks. This cannot see an
|
|
454
|
+
* address-poisoning lookalike, a contract that will not release the funds,
|
|
455
|
+
* or a known-malicious destination. Labelling that "safe" once put a green
|
|
456
|
+
* tick beside a scammer's address; the level is deliberately not "safe".
|
|
457
|
+
*/
|
|
458
|
+
type AddressValidationLevel = "ok" | "warning" | "blocked";
|
|
459
|
+
/** Machine-readable reason; the framework layer maps it to a user string. */
|
|
460
|
+
type DestinationIssue = "empty" | "invalid_format" | "zero_address" | "burn_address";
|
|
461
|
+
interface DestinationValidation {
|
|
462
|
+
isValid: boolean;
|
|
463
|
+
level: AddressValidationLevel;
|
|
464
|
+
issue: DestinationIssue | null;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Burn heuristic kept from the React hook: known sinks plus any well-formed
|
|
468
|
+
* address containing "dead". Stricter than connect-core's prefix-only
|
|
469
|
+
* `isBurnAddress`; unifying the two is a connect-lib change.
|
|
470
|
+
*/
|
|
471
|
+
declare function isBurnDestination(address: string): boolean;
|
|
472
|
+
declare function validateDestination(address: string): DestinationValidation;
|
|
473
|
+
/**
|
|
474
|
+
* The bare `0x…` account from either a CAIP-10 id (`eip155:1:0x…`) or a plain
|
|
475
|
+
* hex address; null when it is neither. Hooks and composables should not each
|
|
476
|
+
* carry their own copy of this split.
|
|
477
|
+
*/
|
|
478
|
+
declare function bareEvmAddress(account: string | null | undefined): `0x${string}` | null;
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Chain-abstraction (routing) domain types and the few decisions the React
|
|
482
|
+
* hooks and Vue composables share. The hooks inject the actual quote /
|
|
483
|
+
* compare / execute functions; nothing here talks to a network.
|
|
484
|
+
*/
|
|
485
|
+
interface RouteQuote {
|
|
486
|
+
routeId: string;
|
|
487
|
+
provider: string;
|
|
488
|
+
netReceiveFormatted: string;
|
|
489
|
+
toTokenSymbol: string;
|
|
490
|
+
}
|
|
491
|
+
interface RouteQuoteOptions {
|
|
492
|
+
sortBy?: "cost" | "time" | "balance";
|
|
493
|
+
maxSlippage?: number;
|
|
494
|
+
}
|
|
495
|
+
interface RouteQuoteInput {
|
|
496
|
+
fromChain: string;
|
|
497
|
+
toChain: string;
|
|
498
|
+
fromToken: string;
|
|
499
|
+
toToken?: string;
|
|
500
|
+
amount: string;
|
|
501
|
+
options?: RouteQuoteOptions;
|
|
502
|
+
}
|
|
503
|
+
type GetRouteQuotes = (from: string, to: string, token: string, amount: string, options?: RouteQuoteOptions, toToken?: string) => Promise<RouteQuote[]>;
|
|
504
|
+
/**
|
|
505
|
+
* `BigInt()` throws on anything that is not a decimal integer — "1.5", "abc",
|
|
506
|
+
* "1e18" — so a user typing into an amount field must not reach it. Only a
|
|
507
|
+
* positive base-unit integer is a quotable amount.
|
|
508
|
+
*/
|
|
509
|
+
declare function isPositiveIntegerAmount(amount: string): boolean;
|
|
510
|
+
/** True when every field a quote needs is present and the amount is quotable. */
|
|
511
|
+
declare function isQuotableInput(input: RouteQuoteInput): boolean;
|
|
512
|
+
interface CostComparison {
|
|
513
|
+
chain: string;
|
|
514
|
+
chainName: string;
|
|
515
|
+
totalCost: string;
|
|
516
|
+
gasCost: string;
|
|
517
|
+
estimatedTimeMs: number;
|
|
518
|
+
}
|
|
519
|
+
type CostComparisonOperation = "send_erc20" | "swap" | "bridge" | (string & {});
|
|
520
|
+
interface CostComparisonOptions {
|
|
521
|
+
amount?: string;
|
|
522
|
+
token?: string;
|
|
523
|
+
[key: string]: unknown;
|
|
524
|
+
}
|
|
525
|
+
interface CompareCostsInput {
|
|
526
|
+
operation: CostComparisonOperation;
|
|
527
|
+
chains: string[];
|
|
528
|
+
options?: CostComparisonOptions;
|
|
529
|
+
}
|
|
530
|
+
type CompareCosts = (operation: string, chains: string[], options?: CostComparisonOptions) => Promise<CostComparison[]>;
|
|
531
|
+
/**
|
|
532
|
+
* Value key for a compare-costs input. `chains` is an array and `options` an
|
|
533
|
+
* object; keying on identity re-fired the fetch on every render/reactive tick
|
|
534
|
+
* for a caller writing the natural inline literal — an unbounded loop against
|
|
535
|
+
* a cost API.
|
|
536
|
+
*/
|
|
537
|
+
declare function compareCostsKey(input: CompareCostsInput): string;
|
|
538
|
+
interface ExecutableQuote {
|
|
539
|
+
routeId: string;
|
|
540
|
+
provider: string;
|
|
541
|
+
totalCost?: bigint;
|
|
542
|
+
estimatedTimeMs?: number;
|
|
543
|
+
toChain?: string;
|
|
544
|
+
}
|
|
545
|
+
interface ExecuteRouteResult {
|
|
546
|
+
fromTxHash: string;
|
|
547
|
+
toTxHash?: string;
|
|
548
|
+
}
|
|
549
|
+
interface ExecuteRouteOptions {
|
|
550
|
+
timeoutMs?: number;
|
|
551
|
+
gasLimit?: bigint;
|
|
552
|
+
}
|
|
553
|
+
interface ExecuteRouteError {
|
|
554
|
+
code: string;
|
|
555
|
+
message: string;
|
|
556
|
+
details?: Record<string, unknown>;
|
|
557
|
+
}
|
|
558
|
+
type ExecuteRoute = (quote: ExecutableQuote, recipient: string, options?: ExecuteRouteOptions) => Promise<ExecuteRouteResult>;
|
|
559
|
+
/**
|
|
560
|
+
* Recipient check before a cross-chain execution. An empty recipient is wrong
|
|
561
|
+
* on every chain. Beyond that the destination namespace decides: when the
|
|
562
|
+
* quote names `toChain`, the address is validated for that namespace; when it
|
|
563
|
+
* does not, this returns null and the executor decides, rather than rejecting
|
|
564
|
+
* a valid non-EVM address on a guess.
|
|
565
|
+
*/
|
|
566
|
+
declare function validateRouteRecipient(quote: ExecutableQuote | null | undefined, recipient: string): ExecuteRouteError | null;
|
|
567
|
+
/** Normalise whatever an executor threw into the hook's error shape. */
|
|
568
|
+
declare function toExecuteRouteError(cause: unknown): ExecuteRouteError;
|
|
569
|
+
|
|
285
570
|
declare function withTimeout<T>(promise: Promise<T>, ms: number, label?: string): Promise<T>;
|
|
286
571
|
interface RetryOptions {
|
|
287
572
|
maxRetries: number;
|
|
@@ -322,4 +607,4 @@ type Web3Action = {
|
|
|
322
607
|
declare const initialWeb3State: Web3State;
|
|
323
608
|
declare function web3Reducer(state: Web3State, action: Web3Action): Web3State;
|
|
324
609
|
|
|
325
|
-
export { type ChainInfo, type ConnectionStatus, type DecimalsCache, type RetryOptions, type TokenIdentity, type ViemChainShape, type WalletChain, type Web3Action, type Web3State, __resetResolverForTests, chainNamespace, chainNumber, chainsForNamespace, createDecimalsCache, decodeRevertReason, describeChain, eip155ChainIdToNumber, getResolver, initialWeb3State, isEvmChainId, normalizeEip155ChainId, readDecimals, resolveChain, resolveEvmChainId, resolveRevertReason, toChainSwitchError, toEip155Accounts, toViemChain, tokenChainMismatch, tokenKey, web3Reducer, withRetry, withTimeout, writeDecimals };
|
|
610
|
+
export { type AddressValidationLevel, type AtomicSupport, type ChainCapabilities, type ChainInfo, type CompareCosts, type CompareCostsInput, type ConnectionStatus, type CostComparison, type CostComparisonOperation, type CostComparisonOptions, DEFAULT_NOTIFICATION_SETTINGS, type DecimalsCache, type DestinationIssue, type DestinationValidation, type ExecutableQuote, type ExecuteRoute, type ExecuteRouteError, type ExecuteRouteOptions, type ExecuteRouteResult, type GetRouteQuotes, NOTIFICATION_SETTINGS_KEY, type NotificationSettingsStorage, type RetryOptions, type RouteQuote, type RouteQuoteInput, type RouteQuoteOptions, type SolanaIdentity, type SolanaPayer, type SolanaRoles, type SolanaRolesAbsence, type SolanaRolesSource, type SolanaRolesState, type SolanaSigner, type SolanaWalletFeatures, type TokenIdentity, type ViemChainShape, type WalletChain, type Web3Action, type Web3State, __resetResolverForTests, addInAppNotification, atomicSupportFor, bareEvmAddress, chainNamespace, chainNumber, chainsForNamespace, channelsUpdate, clearOneInAppNotification, compareCostsKey, createDecimalsCache, createNotification, createNotificationSettingsStorage, decodeRevertReason, describeChain, eip155ChainIdToNumber, getResolver, initialWeb3State, isBurnDestination, isEvmChainId, isPositiveIntegerAmount, isQuotableInput, loadNotificationSettings, mutedChainSettings, normalizeCapabilities, normalizeEip155ChainId, readDecimals, readSolanaRoles, resolveChain, resolveEvmChainId, resolveRevertReason, saveNotificationSettings, selectChainCapabilities, toChainSwitchError, toEip155Accounts, toExecuteRouteError, toViemChain, tokenChainMismatch, tokenKey, updateNotificationSettings, validateDestination, validateRouteRecipient, web3Reducer, withRetry, withTimeout, writeDecimals };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Namespace, UniversalWalletSession, TokenConfig, WalletError, WalletErrorCode, NameResolverConfig, NameResolver } from '@naculus/connect-core';
|
|
1
|
+
import { Namespace, UniversalWalletSession, WalletCapabilities, TokenConfig, WalletError, WalletErrorCode, NameResolverConfig, NameResolver, NotificationSettings, InAppChannel, NotificationItem } from '@naculus/connect-core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The types the framework-neutral half of appkit is built on.
|
|
@@ -42,6 +42,149 @@ interface Web3State {
|
|
|
42
42
|
error: Error | null;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* EIP-5792 capability answers, normalised once.
|
|
47
|
+
*
|
|
48
|
+
* Deciding how to execute before sending is the whole point of
|
|
49
|
+
* `wallet_getCapabilities`, and the reasoning that turns a raw answer into
|
|
50
|
+
* that decision is not React's. It lived inside the React hook, which meant a
|
|
51
|
+
* Vue composable could only get it by copying — and a second copy of "absence
|
|
52
|
+
* is not a denial" is exactly the copy that drifts back into `false`.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether a wallet can execute a batch atomically.
|
|
57
|
+
*
|
|
58
|
+
* Three values, not two. A wallet that does not implement
|
|
59
|
+
* `wallet_getCapabilities` has not said no — it has said nothing, and EIP-5792
|
|
60
|
+
* is explicit that absence is not a denial. Collapsing that into `false` sends
|
|
61
|
+
* every such wallet down the sequential path, which is the one where an
|
|
62
|
+
* approve can land and the swap it was for can fail.
|
|
63
|
+
*/
|
|
64
|
+
type AtomicSupport = "supported" | "unsupported" | "unknown";
|
|
65
|
+
interface ChainCapabilities {
|
|
66
|
+
/** CAIP-2 chain this describes. */
|
|
67
|
+
chainId: string;
|
|
68
|
+
atomic: AtomicSupport;
|
|
69
|
+
/** Largest batch the wallet will accept, when it says. */
|
|
70
|
+
maxBatchSize?: number;
|
|
71
|
+
/** The raw entry, for capabilities this SDK does not model. */
|
|
72
|
+
raw: WalletCapabilities;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Turn a wallet's raw answer into per-chain entries.
|
|
76
|
+
*
|
|
77
|
+
* Entries that are not objects are dropped rather than coerced: a wallet
|
|
78
|
+
* answering `null` for a chain has said nothing about it, and inventing an
|
|
79
|
+
* `"unsupported"` there would be the same mistake as reading absence as denial.
|
|
80
|
+
*/
|
|
81
|
+
declare function normalizeCapabilities(raw: Record<string, unknown> | null | undefined): Record<string, ChainCapabilities>;
|
|
82
|
+
/**
|
|
83
|
+
* Find the entry for a chain, by CAIP-2 or by the hex form.
|
|
84
|
+
*
|
|
85
|
+
* Connectors normalise keys to CAIP-2, but a wallet answering in raw hex
|
|
86
|
+
* through a custom connector should still be found rather than reported as
|
|
87
|
+
* "unknown" next to an entry that is sitting right there.
|
|
88
|
+
*/
|
|
89
|
+
declare function selectChainCapabilities(capabilities: Record<string, ChainCapabilities> | null | undefined, chainId: string | null | undefined): ChainCapabilities | null;
|
|
90
|
+
/**
|
|
91
|
+
* Atomic support for a chain.
|
|
92
|
+
*
|
|
93
|
+
* `"unknown"` covers every case where we have not been told: no query yet, a
|
|
94
|
+
* wallet without the method, a failed query, one still in flight. A caller
|
|
95
|
+
* choosing between an atomic and a sequential path needs that distinction, not
|
|
96
|
+
* a confident `false`.
|
|
97
|
+
*/
|
|
98
|
+
declare function atomicSupportFor(capabilities: Record<string, ChainCapabilities> | null | undefined, chainId: string | null | undefined): AtomicSupport;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Solana signer roles, read once for both frameworks.
|
|
102
|
+
*
|
|
103
|
+
* `@naculus/connector-solana` splits a connected wallet into `identity`,
|
|
104
|
+
* `signer` and `payer`, each `null` when the wallet declared it cannot fill
|
|
105
|
+
* that job. That answer is the connector's. What this module owns is the part
|
|
106
|
+
* a UI has to decide before it can show anything: *why* there is no answer —
|
|
107
|
+
* because nothing is connected, because the connected wallet is not Solana,
|
|
108
|
+
* or because the connector predates the roles API and has not said. Those are
|
|
109
|
+
* three different screens, and a hook that hands back one `null` for all of
|
|
110
|
+
* them makes every consumer rediscover the distinction.
|
|
111
|
+
*/
|
|
112
|
+
/** Which account this is, with no ability to sign for it. */
|
|
113
|
+
interface SolanaIdentity {
|
|
114
|
+
/** Base58 public key. */
|
|
115
|
+
readonly address: string;
|
|
116
|
+
/** CAIP-2 chain this account is being used on. */
|
|
117
|
+
readonly chain: string;
|
|
118
|
+
}
|
|
119
|
+
/** An account that will contribute a signature without submitting anything. */
|
|
120
|
+
interface SolanaSigner extends SolanaIdentity {
|
|
121
|
+
signMessage?: (message: Uint8Array) => Promise<Uint8Array>;
|
|
122
|
+
signTransaction: (transaction: Uint8Array) => Promise<Uint8Array>;
|
|
123
|
+
/** Absent, not throwing, when the wallet has no batch feature. */
|
|
124
|
+
signAllTransactions?: (transactions: Uint8Array[]) => Promise<Uint8Array[]>;
|
|
125
|
+
}
|
|
126
|
+
/** An account that signs and broadcasts through the wallet's own RPC. */
|
|
127
|
+
interface SolanaPayer extends SolanaIdentity {
|
|
128
|
+
/** Returns the base58 transaction signature the cluster accepted. */
|
|
129
|
+
signAndSendTransaction: (transaction: Uint8Array) => Promise<string>;
|
|
130
|
+
}
|
|
131
|
+
/** What the wallet declared, recorded at discovery. Two-state on purpose. */
|
|
132
|
+
interface SolanaWalletFeatures {
|
|
133
|
+
signMessage: boolean;
|
|
134
|
+
signTransaction: boolean;
|
|
135
|
+
signAllTransactions: boolean;
|
|
136
|
+
signAndSendTransaction: boolean;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The roles a connected account can fill.
|
|
140
|
+
*
|
|
141
|
+
* Structurally identical to `SolanaRoles` in `@naculus/connector-solana`,
|
|
142
|
+
* declared here rather than imported because this package depends only on
|
|
143
|
+
* `@naculus/connect-core`, and the connector's own declaration is what its
|
|
144
|
+
* `getRoles` returns — a value of that type satisfies this one unchanged.
|
|
145
|
+
*/
|
|
146
|
+
interface SolanaRoles {
|
|
147
|
+
identity: SolanaIdentity;
|
|
148
|
+
signer: SolanaSigner | null;
|
|
149
|
+
payer: SolanaPayer | null;
|
|
150
|
+
features: SolanaWalletFeatures;
|
|
151
|
+
}
|
|
152
|
+
/** The part of a connector this needs. `getRoles` is optional on purpose. */
|
|
153
|
+
interface SolanaRolesSource {
|
|
154
|
+
getRoles?: (session: unknown) => SolanaRoles | null;
|
|
155
|
+
}
|
|
156
|
+
/** The part of a session this needs. */
|
|
157
|
+
interface SessionLike {
|
|
158
|
+
walletType?: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Why `roles` is null.
|
|
162
|
+
*
|
|
163
|
+
* - `no_session` — nothing connected, or the connector has no live session
|
|
164
|
+
* for the one the app holds (a restored session before reconnect).
|
|
165
|
+
* - `not_solana` — the connected wallet is on another namespace. Not an
|
|
166
|
+
* error; a Solana panel simply does not apply.
|
|
167
|
+
* - `unreported` — the connector does not expose `getRoles`. An older
|
|
168
|
+
* `@naculus/connector-solana`, or a custom one. The wallet has not said
|
|
169
|
+
* what it can do, which is not the same as saying it can do nothing.
|
|
170
|
+
*/
|
|
171
|
+
type SolanaRolesAbsence = "no_session" | "not_solana" | "unreported";
|
|
172
|
+
type SolanaRolesState = {
|
|
173
|
+
roles: SolanaRoles;
|
|
174
|
+
absence: null;
|
|
175
|
+
} | {
|
|
176
|
+
roles: null;
|
|
177
|
+
absence: SolanaRolesAbsence;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Read the roles for the session the app holds.
|
|
181
|
+
*
|
|
182
|
+
* Synchronous and pure: the connector already recorded what the wallet
|
|
183
|
+
* declared, so there is nothing to await and no prompt to trigger. Reading
|
|
184
|
+
* roles must never cost the user an approval dialog.
|
|
185
|
+
*/
|
|
186
|
+
declare function readSolanaRoles(connector: SolanaRolesSource | null | undefined, session: SessionLike | null | undefined): SolanaRolesState;
|
|
187
|
+
|
|
45
188
|
/**
|
|
46
189
|
* Which chains a session can actually switch to, and what the current one is.
|
|
47
190
|
*
|
|
@@ -282,6 +425,148 @@ declare function getResolver(config?: NameResolverConfig): NameResolver;
|
|
|
282
425
|
/** Test seam: drop the shared instance. */
|
|
283
426
|
declare function __resetResolverForTests(): void;
|
|
284
427
|
|
|
428
|
+
/** Framework-neutral in-app notification actions and settings persistence. */
|
|
429
|
+
|
|
430
|
+
interface NotificationSettingsStorage {
|
|
431
|
+
getItem<T>(key: string): Promise<T | null>;
|
|
432
|
+
setItem<T>(key: string, value: T): Promise<void>;
|
|
433
|
+
removeItem(key: string): Promise<void>;
|
|
434
|
+
}
|
|
435
|
+
declare const NOTIFICATION_SETTINGS_KEY = "naculus_notif_react_settings";
|
|
436
|
+
declare const DEFAULT_NOTIFICATION_SETTINGS: NotificationSettings;
|
|
437
|
+
/** Preserve the existing localStorage adapter and fail-open UI fallback. */
|
|
438
|
+
declare function createNotificationSettingsStorage(): NotificationSettingsStorage;
|
|
439
|
+
declare function loadNotificationSettings(storage: NotificationSettingsStorage): Promise<NotificationSettings | null>;
|
|
440
|
+
declare function saveNotificationSettings(storage: NotificationSettingsStorage, settings: NotificationSettings): void;
|
|
441
|
+
declare function updateNotificationSettings(current: NotificationSettings, update: Partial<NotificationSettings>): NotificationSettings;
|
|
442
|
+
declare function channelsUpdate(channels: string[]): Partial<NotificationSettings>;
|
|
443
|
+
declare function mutedChainSettings(settings: NotificationSettings, chainId: string, muted: boolean): Partial<NotificationSettings>;
|
|
444
|
+
declare function createNotification(item: Omit<NotificationItem, "id" | "timestamp">): NotificationItem;
|
|
445
|
+
declare function addInAppNotification(channel: InAppChannel, item: Omit<NotificationItem, "id" | "timestamp">): void;
|
|
446
|
+
/** InAppChannel has no remove-one method; preserve its clear/replay behavior. */
|
|
447
|
+
declare function clearOneInAppNotification(channel: InAppChannel, id: string): Promise<NotificationItem[]>;
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Destination-address checks shared by the React hook and the Vue composable.
|
|
451
|
+
*
|
|
452
|
+
* Syntactic only. `"ok"` means none of these checks found anything wrong —
|
|
453
|
+
* not that the recipient is who the user thinks. This cannot see an
|
|
454
|
+
* address-poisoning lookalike, a contract that will not release the funds,
|
|
455
|
+
* or a known-malicious destination. Labelling that "safe" once put a green
|
|
456
|
+
* tick beside a scammer's address; the level is deliberately not "safe".
|
|
457
|
+
*/
|
|
458
|
+
type AddressValidationLevel = "ok" | "warning" | "blocked";
|
|
459
|
+
/** Machine-readable reason; the framework layer maps it to a user string. */
|
|
460
|
+
type DestinationIssue = "empty" | "invalid_format" | "zero_address" | "burn_address";
|
|
461
|
+
interface DestinationValidation {
|
|
462
|
+
isValid: boolean;
|
|
463
|
+
level: AddressValidationLevel;
|
|
464
|
+
issue: DestinationIssue | null;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Burn heuristic kept from the React hook: known sinks plus any well-formed
|
|
468
|
+
* address containing "dead". Stricter than connect-core's prefix-only
|
|
469
|
+
* `isBurnAddress`; unifying the two is a connect-lib change.
|
|
470
|
+
*/
|
|
471
|
+
declare function isBurnDestination(address: string): boolean;
|
|
472
|
+
declare function validateDestination(address: string): DestinationValidation;
|
|
473
|
+
/**
|
|
474
|
+
* The bare `0x…` account from either a CAIP-10 id (`eip155:1:0x…`) or a plain
|
|
475
|
+
* hex address; null when it is neither. Hooks and composables should not each
|
|
476
|
+
* carry their own copy of this split.
|
|
477
|
+
*/
|
|
478
|
+
declare function bareEvmAddress(account: string | null | undefined): `0x${string}` | null;
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Chain-abstraction (routing) domain types and the few decisions the React
|
|
482
|
+
* hooks and Vue composables share. The hooks inject the actual quote /
|
|
483
|
+
* compare / execute functions; nothing here talks to a network.
|
|
484
|
+
*/
|
|
485
|
+
interface RouteQuote {
|
|
486
|
+
routeId: string;
|
|
487
|
+
provider: string;
|
|
488
|
+
netReceiveFormatted: string;
|
|
489
|
+
toTokenSymbol: string;
|
|
490
|
+
}
|
|
491
|
+
interface RouteQuoteOptions {
|
|
492
|
+
sortBy?: "cost" | "time" | "balance";
|
|
493
|
+
maxSlippage?: number;
|
|
494
|
+
}
|
|
495
|
+
interface RouteQuoteInput {
|
|
496
|
+
fromChain: string;
|
|
497
|
+
toChain: string;
|
|
498
|
+
fromToken: string;
|
|
499
|
+
toToken?: string;
|
|
500
|
+
amount: string;
|
|
501
|
+
options?: RouteQuoteOptions;
|
|
502
|
+
}
|
|
503
|
+
type GetRouteQuotes = (from: string, to: string, token: string, amount: string, options?: RouteQuoteOptions, toToken?: string) => Promise<RouteQuote[]>;
|
|
504
|
+
/**
|
|
505
|
+
* `BigInt()` throws on anything that is not a decimal integer — "1.5", "abc",
|
|
506
|
+
* "1e18" — so a user typing into an amount field must not reach it. Only a
|
|
507
|
+
* positive base-unit integer is a quotable amount.
|
|
508
|
+
*/
|
|
509
|
+
declare function isPositiveIntegerAmount(amount: string): boolean;
|
|
510
|
+
/** True when every field a quote needs is present and the amount is quotable. */
|
|
511
|
+
declare function isQuotableInput(input: RouteQuoteInput): boolean;
|
|
512
|
+
interface CostComparison {
|
|
513
|
+
chain: string;
|
|
514
|
+
chainName: string;
|
|
515
|
+
totalCost: string;
|
|
516
|
+
gasCost: string;
|
|
517
|
+
estimatedTimeMs: number;
|
|
518
|
+
}
|
|
519
|
+
type CostComparisonOperation = "send_erc20" | "swap" | "bridge" | (string & {});
|
|
520
|
+
interface CostComparisonOptions {
|
|
521
|
+
amount?: string;
|
|
522
|
+
token?: string;
|
|
523
|
+
[key: string]: unknown;
|
|
524
|
+
}
|
|
525
|
+
interface CompareCostsInput {
|
|
526
|
+
operation: CostComparisonOperation;
|
|
527
|
+
chains: string[];
|
|
528
|
+
options?: CostComparisonOptions;
|
|
529
|
+
}
|
|
530
|
+
type CompareCosts = (operation: string, chains: string[], options?: CostComparisonOptions) => Promise<CostComparison[]>;
|
|
531
|
+
/**
|
|
532
|
+
* Value key for a compare-costs input. `chains` is an array and `options` an
|
|
533
|
+
* object; keying on identity re-fired the fetch on every render/reactive tick
|
|
534
|
+
* for a caller writing the natural inline literal — an unbounded loop against
|
|
535
|
+
* a cost API.
|
|
536
|
+
*/
|
|
537
|
+
declare function compareCostsKey(input: CompareCostsInput): string;
|
|
538
|
+
interface ExecutableQuote {
|
|
539
|
+
routeId: string;
|
|
540
|
+
provider: string;
|
|
541
|
+
totalCost?: bigint;
|
|
542
|
+
estimatedTimeMs?: number;
|
|
543
|
+
toChain?: string;
|
|
544
|
+
}
|
|
545
|
+
interface ExecuteRouteResult {
|
|
546
|
+
fromTxHash: string;
|
|
547
|
+
toTxHash?: string;
|
|
548
|
+
}
|
|
549
|
+
interface ExecuteRouteOptions {
|
|
550
|
+
timeoutMs?: number;
|
|
551
|
+
gasLimit?: bigint;
|
|
552
|
+
}
|
|
553
|
+
interface ExecuteRouteError {
|
|
554
|
+
code: string;
|
|
555
|
+
message: string;
|
|
556
|
+
details?: Record<string, unknown>;
|
|
557
|
+
}
|
|
558
|
+
type ExecuteRoute = (quote: ExecutableQuote, recipient: string, options?: ExecuteRouteOptions) => Promise<ExecuteRouteResult>;
|
|
559
|
+
/**
|
|
560
|
+
* Recipient check before a cross-chain execution. An empty recipient is wrong
|
|
561
|
+
* on every chain. Beyond that the destination namespace decides: when the
|
|
562
|
+
* quote names `toChain`, the address is validated for that namespace; when it
|
|
563
|
+
* does not, this returns null and the executor decides, rather than rejecting
|
|
564
|
+
* a valid non-EVM address on a guess.
|
|
565
|
+
*/
|
|
566
|
+
declare function validateRouteRecipient(quote: ExecutableQuote | null | undefined, recipient: string): ExecuteRouteError | null;
|
|
567
|
+
/** Normalise whatever an executor threw into the hook's error shape. */
|
|
568
|
+
declare function toExecuteRouteError(cause: unknown): ExecuteRouteError;
|
|
569
|
+
|
|
285
570
|
declare function withTimeout<T>(promise: Promise<T>, ms: number, label?: string): Promise<T>;
|
|
286
571
|
interface RetryOptions {
|
|
287
572
|
maxRetries: number;
|
|
@@ -322,4 +607,4 @@ type Web3Action = {
|
|
|
322
607
|
declare const initialWeb3State: Web3State;
|
|
323
608
|
declare function web3Reducer(state: Web3State, action: Web3Action): Web3State;
|
|
324
609
|
|
|
325
|
-
export { type ChainInfo, type ConnectionStatus, type DecimalsCache, type RetryOptions, type TokenIdentity, type ViemChainShape, type WalletChain, type Web3Action, type Web3State, __resetResolverForTests, chainNamespace, chainNumber, chainsForNamespace, createDecimalsCache, decodeRevertReason, describeChain, eip155ChainIdToNumber, getResolver, initialWeb3State, isEvmChainId, normalizeEip155ChainId, readDecimals, resolveChain, resolveEvmChainId, resolveRevertReason, toChainSwitchError, toEip155Accounts, toViemChain, tokenChainMismatch, tokenKey, web3Reducer, withRetry, withTimeout, writeDecimals };
|
|
610
|
+
export { type AddressValidationLevel, type AtomicSupport, type ChainCapabilities, type ChainInfo, type CompareCosts, type CompareCostsInput, type ConnectionStatus, type CostComparison, type CostComparisonOperation, type CostComparisonOptions, DEFAULT_NOTIFICATION_SETTINGS, type DecimalsCache, type DestinationIssue, type DestinationValidation, type ExecutableQuote, type ExecuteRoute, type ExecuteRouteError, type ExecuteRouteOptions, type ExecuteRouteResult, type GetRouteQuotes, NOTIFICATION_SETTINGS_KEY, type NotificationSettingsStorage, type RetryOptions, type RouteQuote, type RouteQuoteInput, type RouteQuoteOptions, type SolanaIdentity, type SolanaPayer, type SolanaRoles, type SolanaRolesAbsence, type SolanaRolesSource, type SolanaRolesState, type SolanaSigner, type SolanaWalletFeatures, type TokenIdentity, type ViemChainShape, type WalletChain, type Web3Action, type Web3State, __resetResolverForTests, addInAppNotification, atomicSupportFor, bareEvmAddress, chainNamespace, chainNumber, chainsForNamespace, channelsUpdate, clearOneInAppNotification, compareCostsKey, createDecimalsCache, createNotification, createNotificationSettingsStorage, decodeRevertReason, describeChain, eip155ChainIdToNumber, getResolver, initialWeb3State, isBurnDestination, isEvmChainId, isPositiveIntegerAmount, isQuotableInput, loadNotificationSettings, mutedChainSettings, normalizeCapabilities, normalizeEip155ChainId, readDecimals, readSolanaRoles, resolveChain, resolveEvmChainId, resolveRevertReason, saveNotificationSettings, selectChainCapabilities, toChainSwitchError, toEip155Accounts, toExecuteRouteError, toViemChain, tokenChainMismatch, tokenKey, updateNotificationSettings, validateDestination, validateRouteRecipient, web3Reducer, withRetry, withTimeout, writeDecimals };
|