@formo/analytics 1.38.2 → 1.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/FormoAnalytics.d.ts +4 -1
- package/dist/cjs/src/FormoAnalytics.js +56 -36
- package/dist/cjs/src/FormoAnalyticsProvider.js +20 -0
- package/dist/cjs/src/event/EventFactory.d.ts +9 -1
- package/dist/cjs/src/event/EventFactory.js +31 -16
- package/dist/cjs/src/event/EventManager.d.ts +3 -1
- package/dist/cjs/src/event/EventManager.js +33 -4
- package/dist/cjs/src/event/cancellation.d.ts +4 -0
- package/dist/cjs/src/event/cancellation.js +7 -0
- package/dist/cjs/src/event/type.d.ts +1 -0
- package/dist/cjs/src/event/utils.d.ts +5 -1
- package/dist/cjs/src/event/utils.js +57 -2
- package/dist/cjs/src/evm/EvmEventTracker.d.ts +7 -0
- package/dist/cjs/src/evm/EvmEventTracker.js +31 -5
- package/dist/cjs/src/queue/EventQueue.d.ts +1 -0
- package/dist/cjs/src/queue/EventQueue.js +11 -6
- package/dist/cjs/src/solana/SolanaManager.d.ts +37 -9
- package/dist/cjs/src/solana/SolanaManager.js +135 -20
- package/dist/cjs/src/solana/SolanaStoreHandler.d.ts +6 -0
- package/dist/cjs/src/solana/SolanaStoreHandler.js +17 -8
- package/dist/cjs/src/solana/SolanaWalletStandardRegistry.d.ts +163 -0
- package/dist/cjs/src/solana/SolanaWalletStandardRegistry.js +447 -0
- package/dist/cjs/src/solana/index.d.ts +10 -5
- package/dist/cjs/src/solana/index.js +12 -6
- package/dist/cjs/src/solana/storeTypes.d.ts +20 -1
- package/dist/cjs/src/solana/types.d.ts +32 -9
- package/dist/cjs/src/solana/types.js +15 -0
- package/dist/cjs/src/solana/walletStandardTypes.d.ts +54 -0
- package/dist/cjs/src/solana/walletStandardTypes.js +21 -0
- package/dist/cjs/src/storage/StorageManager.d.ts +2 -0
- package/dist/cjs/src/storage/StorageManager.js +6 -0
- package/dist/cjs/src/storage/index.d.ts +1 -0
- package/dist/cjs/src/storage/index.js +7 -1
- package/dist/cjs/src/types/base.d.ts +15 -5
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +4 -1
- package/dist/esm/src/FormoAnalytics.js +54 -34
- package/dist/esm/src/FormoAnalyticsProvider.js +20 -0
- package/dist/esm/src/event/EventFactory.d.ts +9 -1
- package/dist/esm/src/event/EventFactory.js +31 -16
- package/dist/esm/src/event/EventManager.d.ts +3 -1
- package/dist/esm/src/event/EventManager.js +33 -4
- package/dist/esm/src/event/cancellation.d.ts +4 -0
- package/dist/esm/src/event/cancellation.js +4 -0
- package/dist/esm/src/event/type.d.ts +1 -0
- package/dist/esm/src/event/utils.d.ts +5 -1
- package/dist/esm/src/event/utils.js +56 -3
- package/dist/esm/src/evm/EvmEventTracker.d.ts +7 -0
- package/dist/esm/src/evm/EvmEventTracker.js +31 -5
- package/dist/esm/src/queue/EventQueue.d.ts +1 -0
- package/dist/esm/src/queue/EventQueue.js +11 -6
- package/dist/esm/src/solana/SolanaManager.d.ts +37 -9
- package/dist/esm/src/solana/SolanaManager.js +135 -20
- package/dist/esm/src/solana/SolanaStoreHandler.d.ts +6 -0
- package/dist/esm/src/solana/SolanaStoreHandler.js +18 -9
- package/dist/esm/src/solana/SolanaWalletStandardRegistry.d.ts +163 -0
- package/dist/esm/src/solana/SolanaWalletStandardRegistry.js +444 -0
- package/dist/esm/src/solana/index.d.ts +10 -5
- package/dist/esm/src/solana/index.js +10 -5
- package/dist/esm/src/solana/storeTypes.d.ts +20 -1
- package/dist/esm/src/solana/types.d.ts +32 -9
- package/dist/esm/src/solana/types.js +14 -0
- package/dist/esm/src/solana/walletStandardTypes.d.ts +54 -0
- package/dist/esm/src/solana/walletStandardTypes.js +18 -0
- package/dist/esm/src/storage/StorageManager.d.ts +2 -0
- package/dist/esm/src/storage/StorageManager.js +6 -0
- package/dist/esm/src/storage/index.d.ts +1 -0
- package/dist/esm/src/storage/index.js +5 -0
- package/dist/esm/src/types/base.d.ts +15 -5
- package/dist/esm/src/version.d.ts +1 -1
- package/dist/esm/src/version.js +1 -1
- package/dist/index.umd.min.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SolanaWalletStandardRegistry
|
|
3
|
+
*
|
|
4
|
+
* Which Solana wallets exist, and whether each is connected. The Solana
|
|
5
|
+
* analogue of `EvmProviderRegistry` + EIP-6963.
|
|
6
|
+
*
|
|
7
|
+
* Compatible wallets (Phantom, Solflare, Backpack, ...) announce themselves
|
|
8
|
+
* through the Wallet Standard's window-event handshake. Solana Kit,
|
|
9
|
+
* wallet-adapter, and framework-kit use these registered wallets. Observing
|
|
10
|
+
* the handshake covers those wallets without coupling Formo to the host
|
|
11
|
+
* library: the registry announces `wallet-standard:app-ready`, listens for
|
|
12
|
+
* `wallet-standard:register-wallet`, and subscribes to each wallet's
|
|
13
|
+
* `standard:events` `change` event.
|
|
14
|
+
*
|
|
15
|
+
* What it reports:
|
|
16
|
+
* - `detect` when a wallet registers (session-deduped by the SDK on rdns).
|
|
17
|
+
* - `connect` when a wallet's Solana accounts go from none to some, or the
|
|
18
|
+
* first account changes; `disconnect` when they go from some to none.
|
|
19
|
+
* - `chain` when the configured cluster changes while a wallet is connected.
|
|
20
|
+
*
|
|
21
|
+
* What it does not do: wrap any wallet method, issue any request to a
|
|
22
|
+
* wallet, or depend on `@wallet-standard/*` (the handshake is a few lines,
|
|
23
|
+
* and the SDK's dependency policy is two runtime dependencies, forever).
|
|
24
|
+
*
|
|
25
|
+
* Cluster: the Wallet Standard lists every cluster a wallet SUPPORTS, not
|
|
26
|
+
* the one the app is using, so the cluster is `options.solana.cluster` when
|
|
27
|
+
* given, else mainnet-beta when the wallet supports it, else the first
|
|
28
|
+
* Solana cluster it lists. A framework-kit app gets the cluster from its
|
|
29
|
+
* store instead, through `SolanaStoreHandler`.
|
|
30
|
+
*
|
|
31
|
+
* @see https://github.com/wallet-standard/wallet-standard
|
|
32
|
+
*/
|
|
33
|
+
import type { AutocaptureEventType } from "../tracking/TrackingPolicy";
|
|
34
|
+
import { SolanaCluster, UnsubscribeFn } from "./types";
|
|
35
|
+
/** What the registry needs from the SDK that owns it. */
|
|
36
|
+
export interface SolanaWalletStandardRegistryDeps {
|
|
37
|
+
isAutocaptureEnabled(eventType: AutocaptureEventType): boolean;
|
|
38
|
+
willTrackEvent(chainId: number): boolean;
|
|
39
|
+
detect(params: {
|
|
40
|
+
providerName: string;
|
|
41
|
+
rdns: string;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
connect(params: {
|
|
44
|
+
chainId: number;
|
|
45
|
+
address: string;
|
|
46
|
+
}, properties: {
|
|
47
|
+
providerName: string;
|
|
48
|
+
rdns: string;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
disconnect(params: {
|
|
51
|
+
chainId: number;
|
|
52
|
+
address: string;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
chain(params: {
|
|
55
|
+
chainId: number;
|
|
56
|
+
address: string;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Record wallet and chain state centrally WITHOUT emitting an event.
|
|
60
|
+
*
|
|
61
|
+
* Observing a chain is not the same as reporting one: the exclusion gate
|
|
62
|
+
* keys off the central chain, so a cluster switch has to land there even
|
|
63
|
+
* when `autocapture.chain` is off. The EVM tracker separates the two the
|
|
64
|
+
* same way.
|
|
65
|
+
* @see FormoAnalytics.syncWalletState
|
|
66
|
+
*/
|
|
67
|
+
syncWalletState(params: {
|
|
68
|
+
chainId: number;
|
|
69
|
+
address: string;
|
|
70
|
+
}): void;
|
|
71
|
+
/** The wallet the SDK currently treats as active, across namespaces. */
|
|
72
|
+
currentAddress(): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Whether THIS registry reports connections.
|
|
75
|
+
*
|
|
76
|
+
* A framework-kit app connects through the very same Wallet Standard
|
|
77
|
+
* wallet, so its store and this registry both see every connection. The
|
|
78
|
+
* store is the better witness there (it knows the cluster and connector),
|
|
79
|
+
* so once it has observed or adopted a connection the registry keeps
|
|
80
|
+
* discovering wallets and emitting `detect`, but leaves connect and
|
|
81
|
+
* disconnect to the store. Exactly one connect per connection, either way.
|
|
82
|
+
*/
|
|
83
|
+
ownsWalletEvents(): boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface SolanaWalletStandardRegistryOptions {
|
|
86
|
+
/** The cluster the app uses. Overrides what is derived from the wallet. */
|
|
87
|
+
cluster?: SolanaCluster;
|
|
88
|
+
}
|
|
89
|
+
export declare class SolanaWalletStandardRegistry {
|
|
90
|
+
private readonly deps;
|
|
91
|
+
private wallets;
|
|
92
|
+
private cluster?;
|
|
93
|
+
private removeWindowListener?;
|
|
94
|
+
/** Set by cleanup(); a torn-down registry refuses late registrations. */
|
|
95
|
+
private isCleanedUp;
|
|
96
|
+
/**
|
|
97
|
+
* Handed to every wallet. Wallets keep it and may call `register` long
|
|
98
|
+
* after the handshake, which is why `register` checks `isCleanedUp`.
|
|
99
|
+
*/
|
|
100
|
+
private readonly api;
|
|
101
|
+
constructor(deps: SolanaWalletStandardRegistryDeps, options?: SolanaWalletStandardRegistryOptions);
|
|
102
|
+
/**
|
|
103
|
+
* The Wallet Standard handshake, both directions.
|
|
104
|
+
*
|
|
105
|
+
* A wallet injected before us hears `app-ready` and registers at once; a
|
|
106
|
+
* wallet injected after us dispatches `register-wallet`, which we answer.
|
|
107
|
+
* Neither is optional: an extension's content script and the app's bundle
|
|
108
|
+
* race, and which one wins differs per page load.
|
|
109
|
+
*/
|
|
110
|
+
private listen;
|
|
111
|
+
/**
|
|
112
|
+
* Add wallets once. Returns an unregister function, as the standard asks.
|
|
113
|
+
*
|
|
114
|
+
* Public so an integration that already holds a wallet object (its own
|
|
115
|
+
* `getWallets()` call, say) can hand it over without the window handshake.
|
|
116
|
+
*/
|
|
117
|
+
register(...wallets: unknown[]): UnsubscribeFn;
|
|
118
|
+
private track;
|
|
119
|
+
private untrack;
|
|
120
|
+
private onChange;
|
|
121
|
+
/**
|
|
122
|
+
* Compare what the wallet now authorizes with what was last reported.
|
|
123
|
+
*
|
|
124
|
+
* none → some is a connect, some → none a disconnect, and a different
|
|
125
|
+
* first account is a disconnect followed by a connect, matching what the
|
|
126
|
+
* framework-kit store handler reports for the same transitions.
|
|
127
|
+
*/
|
|
128
|
+
private reconcile;
|
|
129
|
+
private reportConnect;
|
|
130
|
+
private reportDisconnect;
|
|
131
|
+
/**
|
|
132
|
+
* The chain id to report for a wallet.
|
|
133
|
+
*
|
|
134
|
+
* The Wallet Standard cannot say which cluster the app is on, only which
|
|
135
|
+
* ones the wallet supports, so an explicit cluster wins, then mainnet-beta
|
|
136
|
+
* if supported (nearly every wallet lists every cluster, and production
|
|
137
|
+
* traffic is mainnet), then the first cluster listed.
|
|
138
|
+
*/
|
|
139
|
+
private chainIdFor;
|
|
140
|
+
/**
|
|
141
|
+
* Set the cluster the app uses. A connected wallet's chain id follows,
|
|
142
|
+
* with a `chain` event, the same as the store handler on a cluster switch.
|
|
143
|
+
*/
|
|
144
|
+
setCluster(cluster: SolanaCluster): void;
|
|
145
|
+
/** Names of every wallet discovered so far, for the debug helpers. */
|
|
146
|
+
get walletNames(): string[];
|
|
147
|
+
/**
|
|
148
|
+
* The rdns this registry reported a still-live connect for `address`
|
|
149
|
+
* under, if any. Lets a failed store adoption name both identities.
|
|
150
|
+
*/
|
|
151
|
+
reportedConnectionRdns(address: string): string | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* Transfer a connect already emitted by this registry to a store handler.
|
|
154
|
+
* The state remains connected, but the same transition must not be emitted
|
|
155
|
+
* a second time when framework-kit's store catches up.
|
|
156
|
+
*/
|
|
157
|
+
takeReportedConnection(address: string, rdns: string): {
|
|
158
|
+
address: string;
|
|
159
|
+
chainId: number;
|
|
160
|
+
} | undefined;
|
|
161
|
+
cleanup(): void;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=SolanaWalletStandardRegistry.d.ts.map
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SolanaWalletStandardRegistry
|
|
4
|
+
*
|
|
5
|
+
* Which Solana wallets exist, and whether each is connected. The Solana
|
|
6
|
+
* analogue of `EvmProviderRegistry` + EIP-6963.
|
|
7
|
+
*
|
|
8
|
+
* Compatible wallets (Phantom, Solflare, Backpack, ...) announce themselves
|
|
9
|
+
* through the Wallet Standard's window-event handshake. Solana Kit,
|
|
10
|
+
* wallet-adapter, and framework-kit use these registered wallets. Observing
|
|
11
|
+
* the handshake covers those wallets without coupling Formo to the host
|
|
12
|
+
* library: the registry announces `wallet-standard:app-ready`, listens for
|
|
13
|
+
* `wallet-standard:register-wallet`, and subscribes to each wallet's
|
|
14
|
+
* `standard:events` `change` event.
|
|
15
|
+
*
|
|
16
|
+
* What it reports:
|
|
17
|
+
* - `detect` when a wallet registers (session-deduped by the SDK on rdns).
|
|
18
|
+
* - `connect` when a wallet's Solana accounts go from none to some, or the
|
|
19
|
+
* first account changes; `disconnect` when they go from some to none.
|
|
20
|
+
* - `chain` when the configured cluster changes while a wallet is connected.
|
|
21
|
+
*
|
|
22
|
+
* What it does not do: wrap any wallet method, issue any request to a
|
|
23
|
+
* wallet, or depend on `@wallet-standard/*` (the handshake is a few lines,
|
|
24
|
+
* and the SDK's dependency policy is two runtime dependencies, forever).
|
|
25
|
+
*
|
|
26
|
+
* Cluster: the Wallet Standard lists every cluster a wallet SUPPORTS, not
|
|
27
|
+
* the one the app is using, so the cluster is `options.solana.cluster` when
|
|
28
|
+
* given, else mainnet-beta when the wallet supports it, else the first
|
|
29
|
+
* Solana cluster it lists. A framework-kit app gets the cluster from its
|
|
30
|
+
* store instead, through `SolanaStoreHandler`.
|
|
31
|
+
*
|
|
32
|
+
* @see https://github.com/wallet-standard/wallet-standard
|
|
33
|
+
*/
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.SolanaWalletStandardRegistry = void 0;
|
|
36
|
+
var logger_1 = require("../logger");
|
|
37
|
+
var address_1 = require("./address");
|
|
38
|
+
var types_1 = require("./types");
|
|
39
|
+
var walletStandardTypes_1 = require("./walletStandardTypes");
|
|
40
|
+
var SOLANA_CHAIN_PREFIX = "solana:";
|
|
41
|
+
/** Wallet Standard chain identifiers, keyed to Formo cluster names. */
|
|
42
|
+
var CLUSTER_BY_CHAIN = {
|
|
43
|
+
"solana:mainnet": "mainnet-beta",
|
|
44
|
+
"solana:devnet": "devnet",
|
|
45
|
+
"solana:testnet": "testnet",
|
|
46
|
+
"solana:localnet": "localnet",
|
|
47
|
+
};
|
|
48
|
+
/** Whether a chain identifier belongs to the Solana namespace. */
|
|
49
|
+
function isSolanaChain(chain) {
|
|
50
|
+
return typeof chain === "string" && chain.startsWith(SOLANA_CHAIN_PREFIX);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Accept only what looks like a Wallet Standard wallet that speaks Solana.
|
|
54
|
+
*
|
|
55
|
+
* A multichain wallet may register once and list Solana beside other
|
|
56
|
+
* namespaces; that is fine. A wallet that lists no Solana chain at all is
|
|
57
|
+
* not ours to track.
|
|
58
|
+
*/
|
|
59
|
+
function asSolanaWallet(candidate) {
|
|
60
|
+
if (!candidate || typeof candidate !== "object")
|
|
61
|
+
return undefined;
|
|
62
|
+
var wallet = candidate;
|
|
63
|
+
if (typeof wallet.name !== "string" || !wallet.name)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (!Array.isArray(wallet.chains) || !wallet.chains.some(isSolanaChain)) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
if (!wallet.features || typeof wallet.features !== "object")
|
|
69
|
+
return undefined;
|
|
70
|
+
if (!Array.isArray(wallet.accounts))
|
|
71
|
+
return undefined;
|
|
72
|
+
return wallet;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The first account that is a Solana wallet account, or undefined.
|
|
76
|
+
*
|
|
77
|
+
* An account declares the chains it signs for. On a multichain wallet the
|
|
78
|
+
* list can hold an EVM account beside a Solana one, and the SDK's connect
|
|
79
|
+
* validates the address against the chain id, so only a Solana account may
|
|
80
|
+
* be reported under a Solana chain id. An account with no chain list is
|
|
81
|
+
* judged on its address alone.
|
|
82
|
+
*/
|
|
83
|
+
function firstSolanaAccount(accounts) {
|
|
84
|
+
for (var _i = 0, accounts_1 = accounts; _i < accounts_1.length; _i++) {
|
|
85
|
+
var candidate = accounts_1[_i];
|
|
86
|
+
if (!candidate || typeof candidate !== "object")
|
|
87
|
+
continue;
|
|
88
|
+
var account = candidate;
|
|
89
|
+
if (Array.isArray(account.chains) && !account.chains.some(isSolanaChain)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!(0, address_1.isSolanaAddress)(account.address))
|
|
93
|
+
continue;
|
|
94
|
+
if ((0, address_1.isBlockedSolanaAddress)(account.address))
|
|
95
|
+
continue;
|
|
96
|
+
return account;
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
var SolanaWalletStandardRegistry = /** @class */ (function () {
|
|
101
|
+
function SolanaWalletStandardRegistry(deps, options) {
|
|
102
|
+
var _this = this;
|
|
103
|
+
this.deps = deps;
|
|
104
|
+
this.wallets = new Map();
|
|
105
|
+
/** Set by cleanup(); a torn-down registry refuses late registrations. */
|
|
106
|
+
this.isCleanedUp = false;
|
|
107
|
+
this.cluster = options === null || options === void 0 ? void 0 : options.cluster;
|
|
108
|
+
this.api = Object.freeze({
|
|
109
|
+
register: function () {
|
|
110
|
+
var wallets = [];
|
|
111
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
112
|
+
wallets[_i] = arguments[_i];
|
|
113
|
+
}
|
|
114
|
+
return _this.register.apply(_this, wallets);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
this.listen();
|
|
118
|
+
}
|
|
119
|
+
// ── discovery ───────────────────────────────────────────────────────────
|
|
120
|
+
/**
|
|
121
|
+
* The Wallet Standard handshake, both directions.
|
|
122
|
+
*
|
|
123
|
+
* A wallet injected before us hears `app-ready` and registers at once; a
|
|
124
|
+
* wallet injected after us dispatches `register-wallet`, which we answer.
|
|
125
|
+
* Neither is optional: an extension's content script and the app's bundle
|
|
126
|
+
* race, and which one wins differs per page load.
|
|
127
|
+
*/
|
|
128
|
+
SolanaWalletStandardRegistry.prototype.listen = function () {
|
|
129
|
+
var _this = this;
|
|
130
|
+
if (typeof window === "undefined" ||
|
|
131
|
+
typeof window.addEventListener !== "function") {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
var onRegisterWallet = function (event) {
|
|
135
|
+
var callback = event.detail;
|
|
136
|
+
if (typeof callback !== "function")
|
|
137
|
+
return;
|
|
138
|
+
try {
|
|
139
|
+
callback(_this.api);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
logger_1.logger.warn("SolanaWalletStandardRegistry: A wallet threw while registering", error);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
try {
|
|
146
|
+
window.addEventListener(walletStandardTypes_1.WALLET_STANDARD_REGISTER_WALLET_EVENT, onRegisterWallet);
|
|
147
|
+
this.removeWindowListener = function () {
|
|
148
|
+
return window.removeEventListener(walletStandardTypes_1.WALLET_STANDARD_REGISTER_WALLET_EVENT, onRegisterWallet);
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
logger_1.logger.warn("SolanaWalletStandardRegistry: Could not listen for wallet registrations", error);
|
|
153
|
+
}
|
|
154
|
+
if (typeof CustomEvent !== "function")
|
|
155
|
+
return;
|
|
156
|
+
try {
|
|
157
|
+
window.dispatchEvent(new CustomEvent(walletStandardTypes_1.WALLET_STANDARD_APP_READY_EVENT, { detail: this.api }));
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
logger_1.logger.warn("SolanaWalletStandardRegistry: Could not announce app-ready", error);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Add wallets once. Returns an unregister function, as the standard asks.
|
|
165
|
+
*
|
|
166
|
+
* Public so an integration that already holds a wallet object (its own
|
|
167
|
+
* `getWallets()` call, say) can hand it over without the window handshake.
|
|
168
|
+
*/
|
|
169
|
+
SolanaWalletStandardRegistry.prototype.register = function () {
|
|
170
|
+
var _this = this;
|
|
171
|
+
var wallets = [];
|
|
172
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
173
|
+
wallets[_i] = arguments[_i];
|
|
174
|
+
}
|
|
175
|
+
var added = [];
|
|
176
|
+
if (this.isCleanedUp)
|
|
177
|
+
return function () { return undefined; };
|
|
178
|
+
for (var _a = 0, wallets_1 = wallets; _a < wallets_1.length; _a++) {
|
|
179
|
+
var candidate = wallets_1[_a];
|
|
180
|
+
var wallet = asSolanaWallet(candidate);
|
|
181
|
+
if (!wallet || this.wallets.has(wallet))
|
|
182
|
+
continue;
|
|
183
|
+
var tracked = {
|
|
184
|
+
wallet: wallet,
|
|
185
|
+
name: wallet.name,
|
|
186
|
+
rdns: (0, types_1.solanaWalletRdns)(wallet.name),
|
|
187
|
+
connectWasReported: false,
|
|
188
|
+
};
|
|
189
|
+
this.wallets.set(wallet, tracked);
|
|
190
|
+
added.push(tracked);
|
|
191
|
+
this.track(tracked);
|
|
192
|
+
}
|
|
193
|
+
return function () {
|
|
194
|
+
for (var _i = 0, added_1 = added; _i < added_1.length; _i++) {
|
|
195
|
+
var tracked = added_1[_i];
|
|
196
|
+
_this.untrack(tracked);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
SolanaWalletStandardRegistry.prototype.track = function (tracked) {
|
|
201
|
+
var _this = this;
|
|
202
|
+
logger_1.logger.info("SolanaWalletStandardRegistry: Discovered wallet", {
|
|
203
|
+
name: tracked.name,
|
|
204
|
+
chains: tracked.wallet.chains,
|
|
205
|
+
});
|
|
206
|
+
this.deps
|
|
207
|
+
.detect({ providerName: tracked.name, rdns: tracked.rdns })
|
|
208
|
+
.catch(function (error) {
|
|
209
|
+
logger_1.logger.error("SolanaWalletStandardRegistry: Error emitting detect", error);
|
|
210
|
+
});
|
|
211
|
+
var events = tracked.wallet.features[walletStandardTypes_1.WALLET_STANDARD_EVENTS_FEATURE];
|
|
212
|
+
if (events && typeof events.on === "function") {
|
|
213
|
+
try {
|
|
214
|
+
tracked.unsubscribe = events.on("change", function (properties) {
|
|
215
|
+
return _this.onChange(tracked, properties);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
logger_1.logger.warn("SolanaWalletStandardRegistry: Could not subscribe to ".concat(tracked.name, " events"), error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
logger_1.logger.info("SolanaWalletStandardRegistry: ".concat(tracked.name, " has no standard:events feature; connections cannot be observed"));
|
|
224
|
+
}
|
|
225
|
+
// A wallet that is already authorized when we first see it (the app
|
|
226
|
+
// connected before the SDK initialised, or the wallet restored a trusted
|
|
227
|
+
// session on load) will never fire a change event for that connection.
|
|
228
|
+
this.reconcile(tracked, tracked.wallet.accounts);
|
|
229
|
+
};
|
|
230
|
+
SolanaWalletStandardRegistry.prototype.untrack = function (tracked) {
|
|
231
|
+
var _a;
|
|
232
|
+
try {
|
|
233
|
+
(_a = tracked.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(tracked);
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
logger_1.logger.warn("SolanaWalletStandardRegistry: Could not unsubscribe from ".concat(tracked.name), error);
|
|
237
|
+
}
|
|
238
|
+
tracked.unsubscribe = undefined;
|
|
239
|
+
this.wallets.delete(tracked.wallet);
|
|
240
|
+
};
|
|
241
|
+
// ── connection state ────────────────────────────────────────────────────
|
|
242
|
+
SolanaWalletStandardRegistry.prototype.onChange = function (tracked, properties) {
|
|
243
|
+
if (this.isCleanedUp)
|
|
244
|
+
return;
|
|
245
|
+
// `change` carries only what changed. A wallet that omits `accounts` is
|
|
246
|
+
// reporting something else (chains, features); the wallet's own
|
|
247
|
+
// `accounts` is the ground truth either way, so read that when the
|
|
248
|
+
// event does not say.
|
|
249
|
+
var accounts = Array.isArray(properties === null || properties === void 0 ? void 0 : properties.accounts)
|
|
250
|
+
? properties.accounts
|
|
251
|
+
: tracked.wallet.accounts;
|
|
252
|
+
this.reconcile(tracked, Array.isArray(accounts) ? accounts : []);
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Compare what the wallet now authorizes with what was last reported.
|
|
256
|
+
*
|
|
257
|
+
* none → some is a connect, some → none a disconnect, and a different
|
|
258
|
+
* first account is a disconnect followed by a connect, matching what the
|
|
259
|
+
* framework-kit store handler reports for the same transitions.
|
|
260
|
+
*/
|
|
261
|
+
SolanaWalletStandardRegistry.prototype.reconcile = function (tracked, accounts) {
|
|
262
|
+
var next = firstSolanaAccount(accounts);
|
|
263
|
+
var previous = tracked.connected;
|
|
264
|
+
if (!previous && !next)
|
|
265
|
+
return;
|
|
266
|
+
if (previous && next && previous.address === next.address)
|
|
267
|
+
return;
|
|
268
|
+
if (!this.deps.ownsWalletEvents()) {
|
|
269
|
+
// The store handler reports this connection. Still record it, so a
|
|
270
|
+
// later change is judged against what the wallet actually did rather
|
|
271
|
+
// than against a stale snapshot.
|
|
272
|
+
tracked.connected = next
|
|
273
|
+
? { address: next.address, chainId: this.chainIdFor(tracked) }
|
|
274
|
+
: undefined;
|
|
275
|
+
tracked.connectWasReported = false;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (previous)
|
|
279
|
+
this.reportDisconnect(tracked, previous);
|
|
280
|
+
if (next)
|
|
281
|
+
this.reportConnect(tracked, next.address);
|
|
282
|
+
};
|
|
283
|
+
SolanaWalletStandardRegistry.prototype.reportConnect = function (tracked, address) {
|
|
284
|
+
var chainId = this.chainIdFor(tracked);
|
|
285
|
+
tracked.connected = { address: address, chainId: chainId };
|
|
286
|
+
tracked.connectWasReported = false;
|
|
287
|
+
logger_1.logger.info("SolanaWalletStandardRegistry: Wallet connected", {
|
|
288
|
+
name: tracked.name,
|
|
289
|
+
address: address,
|
|
290
|
+
chainId: chainId,
|
|
291
|
+
});
|
|
292
|
+
if (!this.deps.isAutocaptureEnabled("connect"))
|
|
293
|
+
return;
|
|
294
|
+
// FormoAnalytics.connect() deliberately resolves without enqueueing when
|
|
295
|
+
// tracking is suppressed or this chain is excluded. Only a connect that
|
|
296
|
+
// can actually be accepted may suppress the authoritative store event
|
|
297
|
+
// that can follow.
|
|
298
|
+
if (!this.deps.willTrackEvent(chainId))
|
|
299
|
+
return;
|
|
300
|
+
tracked.connectWasReported = true;
|
|
301
|
+
this.deps
|
|
302
|
+
.connect({ chainId: chainId, address: address }, { providerName: tracked.name, rdns: tracked.rdns })
|
|
303
|
+
.catch(function (error) {
|
|
304
|
+
logger_1.logger.error("SolanaWalletStandardRegistry: Error emitting connect", error);
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
SolanaWalletStandardRegistry.prototype.reportDisconnect = function (tracked, previous) {
|
|
308
|
+
tracked.connected = undefined;
|
|
309
|
+
tracked.connectWasReported = false;
|
|
310
|
+
logger_1.logger.info("SolanaWalletStandardRegistry: Wallet disconnected", {
|
|
311
|
+
name: tracked.name,
|
|
312
|
+
address: previous.address,
|
|
313
|
+
chainId: previous.chainId,
|
|
314
|
+
});
|
|
315
|
+
if (!this.deps.isAutocaptureEnabled("disconnect"))
|
|
316
|
+
return;
|
|
317
|
+
this.deps.disconnect(previous).catch(function (error) {
|
|
318
|
+
logger_1.logger.error("SolanaWalletStandardRegistry: Error emitting disconnect", error);
|
|
319
|
+
});
|
|
320
|
+
};
|
|
321
|
+
// ── cluster ─────────────────────────────────────────────────────────────
|
|
322
|
+
/**
|
|
323
|
+
* The chain id to report for a wallet.
|
|
324
|
+
*
|
|
325
|
+
* The Wallet Standard cannot say which cluster the app is on, only which
|
|
326
|
+
* ones the wallet supports, so an explicit cluster wins, then mainnet-beta
|
|
327
|
+
* if supported (nearly every wallet lists every cluster, and production
|
|
328
|
+
* traffic is mainnet), then the first cluster listed.
|
|
329
|
+
*/
|
|
330
|
+
SolanaWalletStandardRegistry.prototype.chainIdFor = function (tracked) {
|
|
331
|
+
if (this.cluster)
|
|
332
|
+
return types_1.SOLANA_CHAIN_IDS[this.cluster];
|
|
333
|
+
var chains = tracked.wallet.chains;
|
|
334
|
+
if (chains.includes("solana:mainnet"))
|
|
335
|
+
return types_1.DEFAULT_SOLANA_CHAIN_ID;
|
|
336
|
+
for (var _i = 0, chains_1 = chains; _i < chains_1.length; _i++) {
|
|
337
|
+
var chain = chains_1[_i];
|
|
338
|
+
var cluster = CLUSTER_BY_CHAIN[chain];
|
|
339
|
+
if (cluster)
|
|
340
|
+
return types_1.SOLANA_CHAIN_IDS[cluster];
|
|
341
|
+
}
|
|
342
|
+
return types_1.DEFAULT_SOLANA_CHAIN_ID;
|
|
343
|
+
};
|
|
344
|
+
/**
|
|
345
|
+
* Set the cluster the app uses. A connected wallet's chain id follows,
|
|
346
|
+
* with a `chain` event, the same as the store handler on a cluster switch.
|
|
347
|
+
*/
|
|
348
|
+
SolanaWalletStandardRegistry.prototype.setCluster = function (cluster) {
|
|
349
|
+
var _a;
|
|
350
|
+
if (this.cluster === cluster)
|
|
351
|
+
return;
|
|
352
|
+
this.cluster = cluster;
|
|
353
|
+
var chainId = types_1.SOLANA_CHAIN_IDS[cluster];
|
|
354
|
+
var all = Array.from(this.wallets.values());
|
|
355
|
+
// Central state follows ONE wallet. Two wallets can hold an authorized
|
|
356
|
+
// account at once, and writing each of them here would hand the wallet
|
|
357
|
+
// slot to the last REGISTERED wallet rather than to the one the SDK
|
|
358
|
+
// already treats as active, which is the last CONNECTED one.
|
|
359
|
+
var active = this.deps.currentAddress();
|
|
360
|
+
var owner = (_a = all.find(function (t) { return t.connected && t.connected.address === active; })) !== null && _a !== void 0 ? _a : all.find(function (t) { return t.connected; });
|
|
361
|
+
for (var _i = 0, all_1 = all; _i < all_1.length; _i++) {
|
|
362
|
+
var tracked = all_1[_i];
|
|
363
|
+
var connected = tracked.connected;
|
|
364
|
+
if (!connected || connected.chainId === chainId)
|
|
365
|
+
continue;
|
|
366
|
+
tracked.connected = { address: connected.address, chainId: chainId };
|
|
367
|
+
if (!this.deps.ownsWalletEvents())
|
|
368
|
+
continue;
|
|
369
|
+
// Central state moves first, so a suppressed chain event still leaves
|
|
370
|
+
// the SDK on the cluster the wallet is actually on. `chain()` writes
|
|
371
|
+
// the cluster itself, but only when the event is not suppressed.
|
|
372
|
+
if (tracked === owner) {
|
|
373
|
+
this.deps.syncWalletState({ chainId: chainId, address: connected.address });
|
|
374
|
+
}
|
|
375
|
+
if (!this.deps.isAutocaptureEnabled("chain"))
|
|
376
|
+
continue;
|
|
377
|
+
this.deps
|
|
378
|
+
.chain({ chainId: chainId, address: connected.address })
|
|
379
|
+
.catch(function (error) {
|
|
380
|
+
logger_1.logger.error("SolanaWalletStandardRegistry: Error emitting chain event", error);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
Object.defineProperty(SolanaWalletStandardRegistry.prototype, "walletNames", {
|
|
385
|
+
// ── introspection ───────────────────────────────────────────────────────
|
|
386
|
+
/** Names of every wallet discovered so far, for the debug helpers. */
|
|
387
|
+
get: function () {
|
|
388
|
+
return Array.from(this.wallets.values()).map(function (t) { return t.name; });
|
|
389
|
+
},
|
|
390
|
+
enumerable: false,
|
|
391
|
+
configurable: true
|
|
392
|
+
});
|
|
393
|
+
/**
|
|
394
|
+
* The rdns this registry reported a still-live connect for `address`
|
|
395
|
+
* under, if any. Lets a failed store adoption name both identities.
|
|
396
|
+
*/
|
|
397
|
+
SolanaWalletStandardRegistry.prototype.reportedConnectionRdns = function (address) {
|
|
398
|
+
var _a;
|
|
399
|
+
for (var _i = 0, _b = Array.from(this.wallets.values()); _i < _b.length; _i++) {
|
|
400
|
+
var tracked = _b[_i];
|
|
401
|
+
if (tracked.connectWasReported &&
|
|
402
|
+
((_a = tracked.connected) === null || _a === void 0 ? void 0 : _a.address) === address) {
|
|
403
|
+
return tracked.rdns;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return undefined;
|
|
407
|
+
};
|
|
408
|
+
/**
|
|
409
|
+
* Transfer a connect already emitted by this registry to a store handler.
|
|
410
|
+
* The state remains connected, but the same transition must not be emitted
|
|
411
|
+
* a second time when framework-kit's store catches up.
|
|
412
|
+
*/
|
|
413
|
+
SolanaWalletStandardRegistry.prototype.takeReportedConnection = function (address, rdns) {
|
|
414
|
+
var _a;
|
|
415
|
+
for (var _i = 0, _b = Array.from(this.wallets.values()); _i < _b.length; _i++) {
|
|
416
|
+
var tracked = _b[_i];
|
|
417
|
+
if (tracked.connectWasReported &&
|
|
418
|
+
((_a = tracked.connected) === null || _a === void 0 ? void 0 : _a.address) === address &&
|
|
419
|
+
tracked.rdns === rdns) {
|
|
420
|
+
tracked.connectWasReported = false;
|
|
421
|
+
return tracked.connected;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return undefined;
|
|
425
|
+
};
|
|
426
|
+
// ── teardown ────────────────────────────────────────────────────────────
|
|
427
|
+
SolanaWalletStandardRegistry.prototype.cleanup = function () {
|
|
428
|
+
var _a;
|
|
429
|
+
this.isCleanedUp = true;
|
|
430
|
+
try {
|
|
431
|
+
(_a = this.removeWindowListener) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
logger_1.logger.warn("SolanaWalletStandardRegistry: Could not remove window listener", error);
|
|
435
|
+
}
|
|
436
|
+
this.removeWindowListener = undefined;
|
|
437
|
+
for (var _i = 0, _b = Array.from(this.wallets.values()); _i < _b.length; _i++) {
|
|
438
|
+
var tracked = _b[_i];
|
|
439
|
+
this.untrack(tracked);
|
|
440
|
+
}
|
|
441
|
+
this.wallets.clear();
|
|
442
|
+
logger_1.logger.debug("SolanaWalletStandardRegistry: Cleanup complete");
|
|
443
|
+
};
|
|
444
|
+
return SolanaWalletStandardRegistry;
|
|
445
|
+
}());
|
|
446
|
+
exports.SolanaWalletStandardRegistry = SolanaWalletStandardRegistry;
|
|
447
|
+
//# sourceMappingURL=SolanaWalletStandardRegistry.js.map
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Solana integration module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Wallets are discovered through the Wallet Standard and their connect /
|
|
5
|
+
* disconnect events captured automatically for wallets registered with the
|
|
6
|
+
* page through Wallet Standard.
|
|
7
|
+
* Framework-kit apps can additionally pass their zustand store for
|
|
8
|
+
* transaction lifecycle events. Signature events (signMessage /
|
|
9
|
+
* signTransaction) require explicit tracking via formo.signature(), since
|
|
10
|
+
* neither source reports them.
|
|
9
11
|
*
|
|
12
|
+
* @see https://github.com/wallet-standard/wallet-standard
|
|
10
13
|
* @see https://github.com/solana-foundation/framework-kit
|
|
11
14
|
*/
|
|
12
15
|
export { SolanaStoreHandler } from "./SolanaStoreHandler";
|
|
16
|
+
export { SolanaWalletStandardRegistry } from "./SolanaWalletStandardRegistry";
|
|
13
17
|
export { SolanaManager } from "./SolanaManager";
|
|
14
18
|
export * from "./types";
|
|
15
19
|
export * from "./storeTypes";
|
|
20
|
+
export * from "./walletStandardTypes";
|
|
16
21
|
export * from "./address";
|
|
17
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Solana integration module
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Wallets are discovered through the Wallet Standard and their connect /
|
|
6
|
+
* disconnect events captured automatically for wallets registered with the
|
|
7
|
+
* page through Wallet Standard.
|
|
8
|
+
* Framework-kit apps can additionally pass their zustand store for
|
|
9
|
+
* transaction lifecycle events. Signature events (signMessage /
|
|
10
|
+
* signTransaction) require explicit tracking via formo.signature(), since
|
|
11
|
+
* neither source reports them.
|
|
10
12
|
*
|
|
13
|
+
* @see https://github.com/wallet-standard/wallet-standard
|
|
11
14
|
* @see https://github.com/solana-foundation/framework-kit
|
|
12
15
|
*/
|
|
13
16
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -25,12 +28,15 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
25
28
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
26
29
|
};
|
|
27
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.SolanaManager = exports.SolanaStoreHandler = void 0;
|
|
31
|
+
exports.SolanaManager = exports.SolanaWalletStandardRegistry = exports.SolanaStoreHandler = void 0;
|
|
29
32
|
var SolanaStoreHandler_1 = require("./SolanaStoreHandler");
|
|
30
33
|
Object.defineProperty(exports, "SolanaStoreHandler", { enumerable: true, get: function () { return SolanaStoreHandler_1.SolanaStoreHandler; } });
|
|
34
|
+
var SolanaWalletStandardRegistry_1 = require("./SolanaWalletStandardRegistry");
|
|
35
|
+
Object.defineProperty(exports, "SolanaWalletStandardRegistry", { enumerable: true, get: function () { return SolanaWalletStandardRegistry_1.SolanaWalletStandardRegistry; } });
|
|
31
36
|
var SolanaManager_1 = require("./SolanaManager");
|
|
32
37
|
Object.defineProperty(exports, "SolanaManager", { enumerable: true, get: function () { return SolanaManager_1.SolanaManager; } });
|
|
33
38
|
__exportStar(require("./types"), exports);
|
|
34
39
|
__exportStar(require("./storeTypes"), exports);
|
|
40
|
+
__exportStar(require("./walletStandardTypes"), exports);
|
|
35
41
|
__exportStar(require("./address"), exports);
|
|
36
42
|
//# sourceMappingURL=index.js.map
|