@formo/analytics 1.35.1 → 1.36.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 +90 -240
- package/dist/cjs/src/FormoAnalytics.js +432 -1840
- package/dist/cjs/src/constants/base.d.ts +0 -1
- package/dist/cjs/src/constants/base.js +1 -3
- package/dist/cjs/src/event/EventFactory.js +1 -1
- package/dist/cjs/src/event/EventManager.d.ts +3 -1
- package/dist/cjs/src/event/EventManager.js +5 -1
- package/dist/cjs/src/event/type.d.ts +1 -0
- package/dist/cjs/src/evm/EvmEventTracker.d.ts +175 -0
- package/dist/cjs/src/evm/EvmEventTracker.js +1030 -0
- package/dist/cjs/src/evm/EvmProviderRegistry.d.ts +132 -0
- package/dist/cjs/src/evm/EvmProviderRegistry.js +348 -0
- package/dist/cjs/src/evm/EvmRequestTracker.d.ts +120 -0
- package/dist/cjs/src/evm/EvmRequestTracker.js +756 -0
- package/dist/cjs/src/queue/EventQueue.d.ts +28 -0
- package/dist/cjs/src/queue/EventQueue.js +97 -19
- package/dist/cjs/src/queue/type.d.ts +1 -0
- package/dist/cjs/src/tracking/TrackingPolicy.d.ts +146 -0
- package/dist/cjs/src/tracking/TrackingPolicy.js +200 -0
- package/dist/cjs/src/utils/mergeDeepRight.d.ts +22 -0
- package/dist/cjs/src/utils/mergeDeepRight.js +54 -0
- package/dist/cjs/src/validators/address.d.ts +1 -1
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +193 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +1317 -77
- package/dist/cjs/src/wagmi/types.d.ts +6 -0
- package/dist/cjs/src/wallet/WalletStateStore.d.ts +223 -0
- package/dist/cjs/src/wallet/WalletStateStore.js +515 -0
- package/dist/esm/src/FormoAnalytics.d.ts +90 -240
- package/dist/esm/src/FormoAnalytics.js +433 -1841
- package/dist/esm/src/constants/base.d.ts +0 -1
- package/dist/esm/src/constants/base.js +0 -2
- package/dist/esm/src/event/EventFactory.js +1 -1
- package/dist/esm/src/event/EventManager.d.ts +3 -1
- package/dist/esm/src/event/EventManager.js +5 -1
- package/dist/esm/src/event/type.d.ts +1 -0
- package/dist/esm/src/evm/EvmEventTracker.d.ts +175 -0
- package/dist/esm/src/evm/EvmEventTracker.js +1027 -0
- package/dist/esm/src/evm/EvmProviderRegistry.d.ts +132 -0
- package/dist/esm/src/evm/EvmProviderRegistry.js +345 -0
- package/dist/esm/src/evm/EvmRequestTracker.d.ts +120 -0
- package/dist/esm/src/evm/EvmRequestTracker.js +753 -0
- package/dist/esm/src/queue/EventQueue.d.ts +28 -0
- package/dist/esm/src/queue/EventQueue.js +97 -19
- package/dist/esm/src/queue/type.d.ts +1 -0
- package/dist/esm/src/tracking/TrackingPolicy.d.ts +146 -0
- package/dist/esm/src/tracking/TrackingPolicy.js +197 -0
- package/dist/esm/src/utils/mergeDeepRight.d.ts +22 -0
- package/dist/esm/src/utils/mergeDeepRight.js +51 -0
- package/dist/esm/src/version.d.ts +1 -1
- package/dist/esm/src/version.js +1 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +193 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.js +1315 -76
- package/dist/esm/src/wagmi/types.d.ts +6 -0
- package/dist/esm/src/wallet/WalletStateStore.d.ts +223 -0
- package/dist/esm/src/wallet/WalletStateStore.js +512 -0
- package/dist/index.umd.min.js +1 -1
- package/package.json +5 -13
- package/dist/cjs/src/ramda/internal/_curry1.d.ts +0 -10
- package/dist/cjs/src/ramda/internal/_curry1.js +0 -26
- package/dist/cjs/src/ramda/internal/_curry2.d.ts +0 -10
- package/dist/cjs/src/ramda/internal/_curry2.js +0 -43
- package/dist/cjs/src/ramda/internal/_curry3.d.ts +0 -10
- package/dist/cjs/src/ramda/internal/_curry3.js +0 -74
- package/dist/cjs/src/ramda/internal/_has.d.ts +0 -2
- package/dist/cjs/src/ramda/internal/_has.js +0 -7
- package/dist/cjs/src/ramda/internal/_isObject.d.ts +0 -2
- package/dist/cjs/src/ramda/internal/_isObject.js +0 -7
- package/dist/cjs/src/ramda/internal/_isPlaceholder.d.ts +0 -2
- package/dist/cjs/src/ramda/internal/_isPlaceholder.js +0 -7
- package/dist/cjs/src/ramda/mergeDeepRight.d.ts +0 -3
- package/dist/cjs/src/ramda/mergeDeepRight.js +0 -14
- package/dist/cjs/src/ramda/mergeDeepWithKey.d.ts +0 -3
- package/dist/cjs/src/ramda/mergeDeepWithKey.js +0 -20
- package/dist/cjs/src/ramda/mergeWithKey.d.ts +0 -3
- package/dist/cjs/src/ramda/mergeWithKey.js +0 -26
- package/dist/esm/src/ramda/internal/_curry1.d.ts +0 -10
- package/dist/esm/src/ramda/internal/_curry1.js +0 -20
- package/dist/esm/src/ramda/internal/_curry2.d.ts +0 -10
- package/dist/esm/src/ramda/internal/_curry2.js +0 -37
- package/dist/esm/src/ramda/internal/_curry3.d.ts +0 -10
- package/dist/esm/src/ramda/internal/_curry3.js +0 -68
- package/dist/esm/src/ramda/internal/_has.d.ts +0 -2
- package/dist/esm/src/ramda/internal/_has.js +0 -4
- package/dist/esm/src/ramda/internal/_isObject.d.ts +0 -2
- package/dist/esm/src/ramda/internal/_isObject.js +0 -4
- package/dist/esm/src/ramda/internal/_isPlaceholder.d.ts +0 -2
- package/dist/esm/src/ramda/internal/_isPlaceholder.js +0 -4
- package/dist/esm/src/ramda/mergeDeepRight.d.ts +0 -3
- package/dist/esm/src/ramda/mergeDeepRight.js +0 -9
- package/dist/esm/src/ramda/mergeDeepWithKey.d.ts +0 -3
- package/dist/esm/src/ramda/mergeDeepWithKey.js +0 -15
- package/dist/esm/src/ramda/mergeWithKey.d.ts +0 -3
- package/dist/esm/src/ramda/mergeWithKey.js +0 -21
|
@@ -0,0 +1,1027 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { createStore } from "mipd";
|
|
38
|
+
import { logger } from "../logger";
|
|
39
|
+
import { parseChainId } from "../utils/chain";
|
|
40
|
+
import { validateAndChecksumAddress } from "../utils/address";
|
|
41
|
+
import { detectInjectedProviderInfo, isValidProvider } from "../provider";
|
|
42
|
+
/** Why the tracker moved the active slot to another provider. Log copy. */
|
|
43
|
+
var PROVIDER_SWITCH_REASONS = {
|
|
44
|
+
ADDRESS_MISMATCH: "Address mismatch indicates wallet switch",
|
|
45
|
+
NO_ACCOUNTS: "Current provider has no accounts",
|
|
46
|
+
CHECK_FAILED: "Could not check current provider accounts",
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The EIP-1193 side of wallet tracking: which providers to watch, and what
|
|
50
|
+
* their events mean.
|
|
51
|
+
*
|
|
52
|
+
* Split out of `FormoAnalytics` (#336). It holds no wallet state and no
|
|
53
|
+
* provider registry of its own - those have owners already - so what is left
|
|
54
|
+
* here is the part that is genuinely about interpreting wallet events:
|
|
55
|
+
* deciding when a connect has to be reported, when a switch is stale, and
|
|
56
|
+
* when a provider has stopped being the one we follow.
|
|
57
|
+
*/
|
|
58
|
+
var EvmEventTracker = /** @class */ (function () {
|
|
59
|
+
function EvmEventTracker(wallet, registry, deps) {
|
|
60
|
+
this.wallet = wallet;
|
|
61
|
+
this.registry = registry;
|
|
62
|
+
this.deps = deps;
|
|
63
|
+
/**
|
|
64
|
+
* The connect this SDK has already reported for a provider.
|
|
65
|
+
*
|
|
66
|
+
* Connection REPORTING, which is why it lives with the handlers rather than
|
|
67
|
+
* with wallet state. The store tells us when a provider stops being active,
|
|
68
|
+
* which is when a record must lapse.
|
|
69
|
+
*/
|
|
70
|
+
this._announcedConnect = new WeakMap();
|
|
71
|
+
}
|
|
72
|
+
/** Stop listening for wallet announcements. Called from SDK teardown. */
|
|
73
|
+
EvmEventTracker.prototype.cleanup = function () {
|
|
74
|
+
var _a;
|
|
75
|
+
try {
|
|
76
|
+
(_a = this.unsubscribeDiscovery) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
logger.warn("Failed to unsubscribe from provider discovery", e);
|
|
80
|
+
}
|
|
81
|
+
this.unsubscribeDiscovery = undefined;
|
|
82
|
+
};
|
|
83
|
+
/** Drop a provider's reported connect. Called when it stops being active. */
|
|
84
|
+
EvmEventTracker.prototype.forgetAnnouncedConnect = function (provider) {
|
|
85
|
+
this._announcedConnect.delete(provider);
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Helper method to check if a provider is different from the currently active one
|
|
89
|
+
* @param provider The provider to check
|
|
90
|
+
* @returns true if there's a provider mismatch, false otherwise
|
|
91
|
+
*/
|
|
92
|
+
EvmEventTracker.prototype.isProviderMismatch = function (provider) {
|
|
93
|
+
// Only consider it a mismatch if we have an active provider AND the provider is different
|
|
94
|
+
// This allows legitimate provider switching while preventing race conditions
|
|
95
|
+
return this.wallet.provider != null && this.wallet.provider !== provider;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Track an EIP-1193 provider by wrapping its request method and adding event listeners
|
|
99
|
+
* Note: This is only used in non-Wagmi mode. When Wagmi is enabled, all tracking
|
|
100
|
+
* happens through Wagmi's connector system instead of EIP-1193/EIP-6963.
|
|
101
|
+
* @param provider The EIP-1193 provider to track
|
|
102
|
+
*/
|
|
103
|
+
EvmEventTracker.prototype.trackEIP1193Provider = function (provider) {
|
|
104
|
+
logger.info("trackEIP1193Provider", provider);
|
|
105
|
+
// Defensive check: Skip provider tracking in Wagmi mode
|
|
106
|
+
// This should never be called in Wagmi mode due to guards in init(),
|
|
107
|
+
// but we check here for safety in case of future code changes
|
|
108
|
+
if (this.deps.isWagmiMode()) {
|
|
109
|
+
logger.debug("trackEIP1193Provider: Skipping EIP-1193 provider tracking (Wagmi mode - using connector system instead)");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
// Validate provider exists and has required methods
|
|
114
|
+
if (!isValidProvider(provider)) {
|
|
115
|
+
logger.warn("trackEIP1193Provider: Invalid provider - missing required methods");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (this.registry.isTracked(provider)) {
|
|
119
|
+
logger.warn("trackEIP1193Provider: Provider already tracked");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// CRITICAL: Always register accountsChanged for state management
|
|
123
|
+
// This ensures currentAddress, currentChainId, and _provider are always up-to-date
|
|
124
|
+
// Event emission is controlled conditionally inside the handlers
|
|
125
|
+
this.registerAccountsChangedListener(provider);
|
|
126
|
+
// `chainChanged` and `connect` are registered UNCONDITIONALLY: they are
|
|
127
|
+
// how this provider's chain is observed, and every signature and
|
|
128
|
+
// transaction has to be labelled with it. Gating registration on
|
|
129
|
+
// `autocapture.chain` conflated observing a chain with reporting one, so
|
|
130
|
+
// `{ chain: false, signature: true }` left the chain frozen at whatever
|
|
131
|
+
// was first seen - a switch to an excluded chain went unnoticed and its
|
|
132
|
+
// signatures were emitted under the old, allowed chain. Whether an
|
|
133
|
+
// event is emitted is decided inside each handler.
|
|
134
|
+
this.registerChainChangedListener(provider);
|
|
135
|
+
if (this.deps.isAutocaptureEnabled("connect")) {
|
|
136
|
+
this.registerConnectListener(provider);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Observation only. The full connect handler calls `getAddress()`,
|
|
140
|
+
// which issues `eth_accounts`, and nothing analytics-only may go on
|
|
141
|
+
// the wallet's transport - a stalled request there sits in front of
|
|
142
|
+
// the next signature the dapp makes. The chain rides along on the
|
|
143
|
+
// event itself, so it costs nothing to record.
|
|
144
|
+
this.registerConnectChainObserver(provider);
|
|
145
|
+
}
|
|
146
|
+
// Seed the chain from the provider's own synchronous state if it exposes
|
|
147
|
+
// one. Deliberately a property read and never an RPC: see
|
|
148
|
+
// resolveChainIdForProvider for why nothing analytics-only may go on the
|
|
149
|
+
// wallet's transport.
|
|
150
|
+
this.seedProviderChainFromState(provider);
|
|
151
|
+
// Wrapped UNCONDITIONALLY. The wrapper checks the autocapture flags per
|
|
152
|
+
// request, so skipping it here bought nothing and cost a real case: an
|
|
153
|
+
// app that turned signature or transaction capture on after init had a
|
|
154
|
+
// provider that was never wrapped, and never would be.
|
|
155
|
+
if (!this.deps.registerRequestListeners(provider)) {
|
|
156
|
+
// Not tracked: discovery would otherwise never retry it, and every
|
|
157
|
+
// signature and transaction from this wallet would be missed.
|
|
158
|
+
logger.warn("TrackProvider: Could not wrap provider; leaving it untracked");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// Registered UNCONDITIONALLY: this listener also ends the provider's
|
|
162
|
+
// reported-connect record, and with `{ connect: true, disconnect: false }`
|
|
163
|
+
// a wallet that disconnected and reconnected would otherwise find its
|
|
164
|
+
// old record still standing and have the new connect suppressed. Whether
|
|
165
|
+
// a disconnect EVENT is emitted is decided inside the handler.
|
|
166
|
+
{
|
|
167
|
+
this.registerDisconnectListener(provider);
|
|
168
|
+
}
|
|
169
|
+
// Only add to tracked providers after all listeners are successfully registered
|
|
170
|
+
this.registry.markTracked(provider);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
logger.error("Error tracking provider:", error);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
EvmEventTracker.prototype.trackProviders = function (providers) {
|
|
177
|
+
try {
|
|
178
|
+
for (var _i = 0, providers_1 = providers; _i < providers_1.length; _i++) {
|
|
179
|
+
var eip6963ProviderDetail = providers_1[_i];
|
|
180
|
+
var provider = eip6963ProviderDetail === null || eip6963ProviderDetail === void 0 ? void 0 : eip6963ProviderDetail.provider;
|
|
181
|
+
if (provider && !this.registry.isTracked(provider)) {
|
|
182
|
+
this.trackEIP1193Provider(provider);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
logger.error("Failed to track EIP-6963 providers during initialization:", error);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
EvmEventTracker.prototype.registerAccountsChangedListener = function (provider) {
|
|
191
|
+
var _this = this;
|
|
192
|
+
logger.info("registerAccountsChangedListener");
|
|
193
|
+
var listener = function () {
|
|
194
|
+
var args = [];
|
|
195
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
196
|
+
args[_i] = arguments[_i];
|
|
197
|
+
}
|
|
198
|
+
return _this.onAccountsChanged(provider, args[0]);
|
|
199
|
+
};
|
|
200
|
+
provider.on("accountsChanged", listener);
|
|
201
|
+
this.registry.addListener(provider, "accountsChanged", listener);
|
|
202
|
+
};
|
|
203
|
+
EvmEventTracker.prototype.onAccountsChanged = function (provider, accounts) {
|
|
204
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
205
|
+
var claims, observation;
|
|
206
|
+
return __generator(this, function (_a) {
|
|
207
|
+
switch (_a.label) {
|
|
208
|
+
case 0:
|
|
209
|
+
logger.info("onAccountsChanged", accounts);
|
|
210
|
+
claims = accounts.length > 0 || this.wallet.provider === provider;
|
|
211
|
+
observation = claims
|
|
212
|
+
? this.wallet.observe("evm")
|
|
213
|
+
: this.wallet.currentObservation("evm");
|
|
214
|
+
// Take the ticket BEFORE any async work, so the order recorded is the
|
|
215
|
+
// order the wallet's signals arrived in. This path used to DROP a second
|
|
216
|
+
// `accountsChanged` while the first was in flight; dropping is not
|
|
217
|
+
// ordering, and a wallet that disconnected and came straight back had its
|
|
218
|
+
// reconnect thrown away.
|
|
219
|
+
return [4 /*yield*/, this._handleAccountsChanged(provider, accounts, observation)];
|
|
220
|
+
case 1:
|
|
221
|
+
// Take the ticket BEFORE any async work, so the order recorded is the
|
|
222
|
+
// order the wallet's signals arrived in. This path used to DROP a second
|
|
223
|
+
// `accountsChanged` while the first was in flight; dropping is not
|
|
224
|
+
// ordering, and a wallet that disconnected and came straight back had its
|
|
225
|
+
// reconnect thrown away.
|
|
226
|
+
_a.sent();
|
|
227
|
+
return [2 /*return*/];
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Handles changes to the accounts of a given EIP-1193 provider.
|
|
234
|
+
*
|
|
235
|
+
* @param provider - The EIP-1193 provider whose accounts have changed.
|
|
236
|
+
* @param accounts - The new array of account addresses. An empty array indicates a disconnect.
|
|
237
|
+
* @returns A promise that resolves when the account change has been processed.
|
|
238
|
+
*
|
|
239
|
+
* If the accounts array is empty and the provider is the active provider, this method triggers
|
|
240
|
+
* a disconnect flow. Otherwise, it updates the state to reflect the new accounts as needed.
|
|
241
|
+
*/
|
|
242
|
+
EvmEventTracker.prototype._handleAccountsChanged = function (provider, accounts, observation) {
|
|
243
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
244
|
+
var error_1, address, currentStoredAddress, newProviderAddress, activeProviderAccounts, error_2, nextChainId, wasDisconnected, providerInfo, effectiveChainId;
|
|
245
|
+
return __generator(this, function (_a) {
|
|
246
|
+
switch (_a.label) {
|
|
247
|
+
case 0:
|
|
248
|
+
if (!(accounts.length === 0)) return [3 /*break*/, 9];
|
|
249
|
+
if (!(this.wallet.provider === provider)) return [3 /*break*/, 7];
|
|
250
|
+
logger.info("OnAccountsChanged: Detecting disconnect, current state:", {
|
|
251
|
+
evmAddress: this.wallet.evmAddress,
|
|
252
|
+
evmChainId: this.wallet.evmChainId,
|
|
253
|
+
providerMatch: this.wallet.provider === provider,
|
|
254
|
+
});
|
|
255
|
+
// The reported connect ends with the connection, so a genuine
|
|
256
|
+
// reconnect later reports again rather than being taken for a
|
|
257
|
+
// duplicate.
|
|
258
|
+
this._announcedConnect.delete(provider);
|
|
259
|
+
if (!this.deps.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 5];
|
|
260
|
+
_a.label = 1;
|
|
261
|
+
case 1:
|
|
262
|
+
_a.trys.push([1, 3, , 4]);
|
|
263
|
+
// Pass EVM state explicitly to ensure we have the data for the disconnect event
|
|
264
|
+
return [4 /*yield*/, this.deps.disconnect({
|
|
265
|
+
chainId: this.wallet.evmChainId,
|
|
266
|
+
address: this.wallet.evmAddress,
|
|
267
|
+
})];
|
|
268
|
+
case 2:
|
|
269
|
+
// Pass EVM state explicitly to ensure we have the data for the disconnect event
|
|
270
|
+
_a.sent();
|
|
271
|
+
return [3 /*break*/, 4];
|
|
272
|
+
case 3:
|
|
273
|
+
error_1 = _a.sent();
|
|
274
|
+
logger.error("Failed to disconnect provider on accountsChanged", error_1);
|
|
275
|
+
return [3 /*break*/, 4];
|
|
276
|
+
case 4: return [3 /*break*/, 6];
|
|
277
|
+
case 5:
|
|
278
|
+
logger.debug("OnAccountsChanged: Disconnect event skipped (autocapture.disconnect: false)");
|
|
279
|
+
// Whether the app opted into the EVENT has no bearing on ordering.
|
|
280
|
+
this.wallet.beginDisconnect("evm");
|
|
281
|
+
// Still clear state even if not tracking the event
|
|
282
|
+
this.wallet.clear('evm');
|
|
283
|
+
_a.label = 6;
|
|
284
|
+
case 6: return [3 /*break*/, 8];
|
|
285
|
+
case 7:
|
|
286
|
+
logger.info("OnAccountsChanged: Ignoring disconnect for non-active provider");
|
|
287
|
+
_a.label = 8;
|
|
288
|
+
case 8: return [2 /*return*/];
|
|
289
|
+
case 9:
|
|
290
|
+
address = validateAndChecksumAddress(accounts[0]);
|
|
291
|
+
if (!address) {
|
|
292
|
+
logger.warn("onAccountsChanged: Invalid address received", accounts[0]);
|
|
293
|
+
return [2 /*return*/];
|
|
294
|
+
}
|
|
295
|
+
if (!(this.wallet.provider && this.wallet.provider !== provider)) return [3 /*break*/, 26];
|
|
296
|
+
currentStoredAddress = this.wallet.evmAddress;
|
|
297
|
+
newProviderAddress = validateAndChecksumAddress(address);
|
|
298
|
+
logger.info("OnAccountsChanged: Different provider attempting to connect", {
|
|
299
|
+
activeProvider: this.registry.infoFor(this.wallet.provider).name,
|
|
300
|
+
eventProvider: this.registry.infoFor(provider).name,
|
|
301
|
+
currentStoredAddress: currentStoredAddress,
|
|
302
|
+
newProviderAddress: newProviderAddress,
|
|
303
|
+
});
|
|
304
|
+
_a.label = 10;
|
|
305
|
+
case 10:
|
|
306
|
+
_a.trys.push([10, 22, , 26]);
|
|
307
|
+
return [4 /*yield*/, this.registry.accountsOf(this.wallet.provider)];
|
|
308
|
+
case 11:
|
|
309
|
+
activeProviderAccounts = _a.sent();
|
|
310
|
+
// The probe is asynchronous too, so check before issuing anything.
|
|
311
|
+
// Every branch below reads the CURRENT evm state, so a switch that
|
|
312
|
+
// went stale during the probe would emit a false disconnect for
|
|
313
|
+
// whoever claimed the namespace, and clear them.
|
|
314
|
+
if (!this.wallet.isCurrent(observation))
|
|
315
|
+
return [2 /*return*/];
|
|
316
|
+
logger.info("OnAccountsChanged: Checking current provider accounts", {
|
|
317
|
+
activeProvider: this.registry.infoFor(this.wallet.provider).name,
|
|
318
|
+
accountsLength: activeProviderAccounts
|
|
319
|
+
? activeProviderAccounts.length
|
|
320
|
+
: 0,
|
|
321
|
+
accounts: activeProviderAccounts,
|
|
322
|
+
});
|
|
323
|
+
if (!(activeProviderAccounts && activeProviderAccounts.length > 0)) return [3 /*break*/, 17];
|
|
324
|
+
if (!(newProviderAddress &&
|
|
325
|
+
currentStoredAddress &&
|
|
326
|
+
newProviderAddress !== currentStoredAddress)) return [3 /*break*/, 15];
|
|
327
|
+
logger.info("OnAccountsChanged: Different address detected, switching providers despite current provider having accounts", {
|
|
328
|
+
activeProvider: this.registry.infoFor(this.wallet.provider).name,
|
|
329
|
+
eventProvider: this.registry.infoFor(provider).name,
|
|
330
|
+
currentAddress: currentStoredAddress,
|
|
331
|
+
newAddress: newProviderAddress,
|
|
332
|
+
reason: PROVIDER_SWITCH_REASONS.ADDRESS_MISMATCH,
|
|
333
|
+
});
|
|
334
|
+
if (!this.deps.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 13];
|
|
335
|
+
return [4 /*yield*/, this.deps.disconnect({
|
|
336
|
+
chainId: this.wallet.evmChainId,
|
|
337
|
+
address: this.wallet.evmAddress,
|
|
338
|
+
})];
|
|
339
|
+
case 12:
|
|
340
|
+
_a.sent();
|
|
341
|
+
return [3 /*break*/, 14];
|
|
342
|
+
case 13:
|
|
343
|
+
logger.debug("OnAccountsChanged: Disconnect event skipped during provider switch (autocapture.disconnect: false)");
|
|
344
|
+
// Still clear state even if not tracking the event
|
|
345
|
+
this.wallet.clear('evm');
|
|
346
|
+
_a.label = 14;
|
|
347
|
+
case 14:
|
|
348
|
+
if (!this.wallet.isCurrent(observation))
|
|
349
|
+
return [2 /*return*/];
|
|
350
|
+
// Clear state and let the new provider become active
|
|
351
|
+
this.wallet.clearProvider();
|
|
352
|
+
return [3 /*break*/, 16];
|
|
353
|
+
case 15:
|
|
354
|
+
logger.info("OnAccountsChanged: Current provider still has accounts and same address, ignoring new provider", {
|
|
355
|
+
activeProvider: this.registry.infoFor(this.wallet.provider).name,
|
|
356
|
+
eventProvider: this.registry.infoFor(provider).name,
|
|
357
|
+
activeProviderAccountsCount: activeProviderAccounts.length,
|
|
358
|
+
currentAddress: currentStoredAddress,
|
|
359
|
+
newAddress: newProviderAddress,
|
|
360
|
+
});
|
|
361
|
+
return [2 /*return*/];
|
|
362
|
+
case 16: return [3 /*break*/, 21];
|
|
363
|
+
case 17:
|
|
364
|
+
logger.info("OnAccountsChanged: Current provider has no accounts, switching to new provider", {
|
|
365
|
+
oldProvider: this.registry.infoFor(this.wallet.provider).name,
|
|
366
|
+
newProvider: this.registry.infoFor(provider).name,
|
|
367
|
+
reason: PROVIDER_SWITCH_REASONS.NO_ACCOUNTS,
|
|
368
|
+
});
|
|
369
|
+
if (!this.deps.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 19];
|
|
370
|
+
return [4 /*yield*/, this.deps.disconnect({
|
|
371
|
+
chainId: this.wallet.evmChainId,
|
|
372
|
+
address: this.wallet.evmAddress,
|
|
373
|
+
})];
|
|
374
|
+
case 18:
|
|
375
|
+
_a.sent();
|
|
376
|
+
return [3 /*break*/, 20];
|
|
377
|
+
case 19:
|
|
378
|
+
logger.debug("OnAccountsChanged: Disconnect event skipped for old provider (autocapture.disconnect: false)");
|
|
379
|
+
// Still clear state even if not tracking the event
|
|
380
|
+
this.wallet.clear('evm');
|
|
381
|
+
_a.label = 20;
|
|
382
|
+
case 20:
|
|
383
|
+
if (!this.wallet.isCurrent(observation))
|
|
384
|
+
return [2 /*return*/];
|
|
385
|
+
_a.label = 21;
|
|
386
|
+
case 21: return [3 /*break*/, 26];
|
|
387
|
+
case 22:
|
|
388
|
+
error_2 = _a.sent();
|
|
389
|
+
logger.warn("OnAccountsChanged: Could not check current provider accounts, switching to new provider", {
|
|
390
|
+
error: error_2 instanceof Error ? error_2.message : String(error_2),
|
|
391
|
+
errorType: error_2 instanceof Error ? error_2.constructor.name : typeof error_2,
|
|
392
|
+
oldProvider: this.wallet.provider
|
|
393
|
+
? this.registry.infoFor(this.wallet.provider).name
|
|
394
|
+
: "unknown",
|
|
395
|
+
newProvider: this.registry.infoFor(provider).name,
|
|
396
|
+
reason: PROVIDER_SWITCH_REASONS.CHECK_FAILED,
|
|
397
|
+
});
|
|
398
|
+
if (!this.deps.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 24];
|
|
399
|
+
return [4 /*yield*/, this.deps.disconnect({
|
|
400
|
+
chainId: this.wallet.evmChainId,
|
|
401
|
+
address: this.wallet.evmAddress,
|
|
402
|
+
})];
|
|
403
|
+
case 23:
|
|
404
|
+
_a.sent();
|
|
405
|
+
return [3 /*break*/, 25];
|
|
406
|
+
case 24:
|
|
407
|
+
logger.debug("OnAccountsChanged: Disconnect event skipped for failed provider check (autocapture.disconnect: false)");
|
|
408
|
+
// Still clear state even if not tracking the event
|
|
409
|
+
this.wallet.clear('evm');
|
|
410
|
+
_a.label = 25;
|
|
411
|
+
case 25:
|
|
412
|
+
if (!this.wallet.isCurrent(observation))
|
|
413
|
+
return [2 /*return*/];
|
|
414
|
+
return [3 /*break*/, 26];
|
|
415
|
+
case 26:
|
|
416
|
+
// Set provider if none exists (first connection)
|
|
417
|
+
if (!this.wallet.provider) {
|
|
418
|
+
this.wallet.provider = provider;
|
|
419
|
+
}
|
|
420
|
+
// If both the provider and address are the same, no-op
|
|
421
|
+
if (this.wallet.provider === provider && address === this.wallet.evmAddress) {
|
|
422
|
+
return [2 /*return*/];
|
|
423
|
+
}
|
|
424
|
+
nextChainId = this.registry.resolveChainId(provider);
|
|
425
|
+
wasDisconnected = !this.wallet.evmAddress;
|
|
426
|
+
// Update state regardless of whether connect *event* tracking is enabled,
|
|
427
|
+
// so disconnect events keep valid address/chainId values. (excludeChains is
|
|
428
|
+
// NOT suppression - it still updates state so currentChainId can gate
|
|
429
|
+
// events.)
|
|
430
|
+
if (this.deps.isTrackingSuppressed()) {
|
|
431
|
+
this.wallet.clearStaleEvmWalletOnSwitchWhileSuppressed(address);
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
this.wallet.set('evm', { address: address, chainId: nextChainId });
|
|
435
|
+
}
|
|
436
|
+
providerInfo = this.registry.infoFor(provider);
|
|
437
|
+
effectiveChainId = nextChainId || 0;
|
|
438
|
+
if (this.deps.isAutocaptureEnabled("connect") &&
|
|
439
|
+
this.shouldReportConnect(provider, address)) {
|
|
440
|
+
logger.info("OnAccountsChanged: Detected wallet connection, emitting connect event", {
|
|
441
|
+
chainId: nextChainId,
|
|
442
|
+
address: address,
|
|
443
|
+
wasDisconnected: wasDisconnected,
|
|
444
|
+
providerName: providerInfo.name,
|
|
445
|
+
rdns: providerInfo.rdns,
|
|
446
|
+
hasChainId: !!nextChainId,
|
|
447
|
+
});
|
|
448
|
+
if (effectiveChainId === 0) {
|
|
449
|
+
logger.info("OnAccountsChanged: Using fallback chainId 0 for connect event");
|
|
450
|
+
}
|
|
451
|
+
this.markConnectReported(provider, address, effectiveChainId);
|
|
452
|
+
this.deps.connect({
|
|
453
|
+
chainId: effectiveChainId,
|
|
454
|
+
address: address,
|
|
455
|
+
}, {
|
|
456
|
+
providerName: providerInfo.name,
|
|
457
|
+
rdns: providerInfo.rdns,
|
|
458
|
+
}).catch(function (error) {
|
|
459
|
+
logger.error("Failed to track connect event during account change:", error);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
logger.debug("OnAccountsChanged: Connect event skipped (autocapture.connect: false)", {
|
|
464
|
+
chainId: nextChainId,
|
|
465
|
+
address: address,
|
|
466
|
+
providerName: providerInfo.name,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
return [2 /*return*/];
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
EvmEventTracker.prototype.registerChainChangedListener = function (provider) {
|
|
475
|
+
var _this = this;
|
|
476
|
+
logger.info("registerChainChangedListener");
|
|
477
|
+
var listener = function () {
|
|
478
|
+
var args = [];
|
|
479
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
480
|
+
args[_i] = arguments[_i];
|
|
481
|
+
}
|
|
482
|
+
return _this.onChainChanged(provider, args[0]);
|
|
483
|
+
};
|
|
484
|
+
provider.on("chainChanged", listener);
|
|
485
|
+
this.registry.addListener(provider, "chainChanged", listener);
|
|
486
|
+
};
|
|
487
|
+
EvmEventTracker.prototype.onChainChanged = function (provider, chainIdHex) {
|
|
488
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
489
|
+
var nextChainId, error_3;
|
|
490
|
+
return __generator(this, function (_a) {
|
|
491
|
+
switch (_a.label) {
|
|
492
|
+
case 0:
|
|
493
|
+
logger.info("onChainChanged", chainIdHex);
|
|
494
|
+
nextChainId = parseChainId(chainIdHex);
|
|
495
|
+
// Record it for THIS provider regardless of which one is active. This is
|
|
496
|
+
// the only way an autocaptured event from a non-active wallet can learn
|
|
497
|
+
// its chain without putting an RPC on that wallet's transport.
|
|
498
|
+
this.registry.rememberChain(provider, nextChainId);
|
|
499
|
+
// Beyond that, a chain event from a NON-active provider is observation
|
|
500
|
+
// only when chain autocapture is off.
|
|
501
|
+
//
|
|
502
|
+
// This listener is now registered unconditionally, so that a signature can
|
|
503
|
+
// be labelled with its signer's chain. `handleProviderMismatch()` treats a
|
|
504
|
+
// chain event from another wallet as a wallet switch and clears the active
|
|
505
|
+
// wallet's address and chain. That is the established behaviour of the
|
|
506
|
+
// chain feature and stays exactly as it was, but it must not start firing
|
|
507
|
+
// for apps that never asked for chain tracking: a second wallet switching
|
|
508
|
+
// network would silently erase the active wallet's attribution.
|
|
509
|
+
// Observation only when chain autocapture is off, whether or not an
|
|
510
|
+
// active provider has been established yet.
|
|
511
|
+
//
|
|
512
|
+
// `isProviderMismatch()` is false while `_provider` is undefined, which is
|
|
513
|
+
// exactly the state left by restoring a wallet from the active-wallet
|
|
514
|
+
// cookie. A background wallet's `chainChanged` could therefore claim the
|
|
515
|
+
// active slot and overwrite the restored wallet's chain - suppressing
|
|
516
|
+
// allowed events, or letting excluded ones through. The active provider is
|
|
517
|
+
// established by an actual account/connect/request association, not by
|
|
518
|
+
// another wallet changing network.
|
|
519
|
+
if (!this.deps.isAutocaptureEnabled("chain") && provider !== this.wallet.provider) {
|
|
520
|
+
return [2 /*return*/];
|
|
521
|
+
}
|
|
522
|
+
// Only handle chain changes for the active provider (or if none is set yet)
|
|
523
|
+
// A chain event from a non-active EVM provider is only a wallet switch
|
|
524
|
+
// on the EVM side. While Solana holds the active slot it is background
|
|
525
|
+
// noise: letting it through relabelled the session as EVM with no wallet
|
|
526
|
+
// and wiped the EVM wallet we were tracking, on the strength of a network
|
|
527
|
+
// change in a wallet nobody was using. The chain itself is still
|
|
528
|
+
// recorded above, so a later request through that provider is labelled
|
|
529
|
+
// correctly.
|
|
530
|
+
if (this.isProviderMismatch(provider)) {
|
|
531
|
+
if (this.wallet.activeNamespace === "solana")
|
|
532
|
+
return [2 /*return*/];
|
|
533
|
+
this.handleProviderMismatch(provider);
|
|
534
|
+
}
|
|
535
|
+
// Chain changes only matter for connected users
|
|
536
|
+
if (!this.wallet.evmAddress) {
|
|
537
|
+
logger.info("OnChainChanged: No current address, user appears disconnected");
|
|
538
|
+
return [2 /*return*/, Promise.resolve()];
|
|
539
|
+
}
|
|
540
|
+
// Set provider if none exists
|
|
541
|
+
if (!this.wallet.provider) {
|
|
542
|
+
this.wallet.provider = provider;
|
|
543
|
+
}
|
|
544
|
+
this.wallet.set('evm', { chainId: nextChainId });
|
|
545
|
+
_a.label = 1;
|
|
546
|
+
case 1:
|
|
547
|
+
_a.trys.push([1, 5, , 6]);
|
|
548
|
+
if (!this.deps.isAutocaptureEnabled("chain")) return [3 /*break*/, 3];
|
|
549
|
+
// Awaited, so a failing emission is caught below rather than escaping
|
|
550
|
+
// as an unhandled rejection out of the provider's event listener.
|
|
551
|
+
// `return`ing the promise left the catch here unreachable.
|
|
552
|
+
return [4 /*yield*/, this.deps.chain({
|
|
553
|
+
chainId: nextChainId,
|
|
554
|
+
address: this.wallet.evmAddress,
|
|
555
|
+
})];
|
|
556
|
+
case 2:
|
|
557
|
+
// Awaited, so a failing emission is caught below rather than escaping
|
|
558
|
+
// as an unhandled rejection out of the provider's event listener.
|
|
559
|
+
// `return`ing the promise left the catch here unreachable.
|
|
560
|
+
_a.sent();
|
|
561
|
+
return [3 /*break*/, 4];
|
|
562
|
+
case 3:
|
|
563
|
+
logger.debug("OnChainChanged: Chain event skipped (autocapture.chain: false)", {
|
|
564
|
+
chainId: this.wallet.evmChainId,
|
|
565
|
+
address: this.wallet.evmAddress,
|
|
566
|
+
});
|
|
567
|
+
_a.label = 4;
|
|
568
|
+
case 4: return [3 /*break*/, 6];
|
|
569
|
+
case 5:
|
|
570
|
+
error_3 = _a.sent();
|
|
571
|
+
logger.error("OnChainChanged: Failed to emit chain event:", error_3);
|
|
572
|
+
return [3 /*break*/, 6];
|
|
573
|
+
case 6: return [2 /*return*/];
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
};
|
|
578
|
+
/**
|
|
579
|
+
* Record a provider's chain from its `connect` event, and nothing else.
|
|
580
|
+
*
|
|
581
|
+
* Used when connect autocapture is off. `connect` carries `chainId` in its
|
|
582
|
+
* payload, so this needs no RPC - unlike the full handler, which resolves
|
|
583
|
+
* the account.
|
|
584
|
+
*/
|
|
585
|
+
EvmEventTracker.prototype.registerConnectChainObserver = function (provider) {
|
|
586
|
+
var _this = this;
|
|
587
|
+
var listener = function () {
|
|
588
|
+
var args = [];
|
|
589
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
590
|
+
args[_i] = arguments[_i];
|
|
591
|
+
}
|
|
592
|
+
var connection = args[0];
|
|
593
|
+
if (typeof (connection === null || connection === void 0 ? void 0 : connection.chainId) !== "string")
|
|
594
|
+
return;
|
|
595
|
+
_this.registry.rememberChain(provider, parseChainId(connection.chainId));
|
|
596
|
+
};
|
|
597
|
+
provider.on("connect", listener);
|
|
598
|
+
this.registry.addListener(provider, "connect", listener);
|
|
599
|
+
};
|
|
600
|
+
/**
|
|
601
|
+
* Whether a connect for this wallet still needs reporting.
|
|
602
|
+
*
|
|
603
|
+
* True when nothing has been reported for this provider, or when the account
|
|
604
|
+
* changed. A wallet already reported is not reported again.
|
|
605
|
+
*
|
|
606
|
+
* Deliberately does NOT re-report to correct a chain. When `accountsChanged`
|
|
607
|
+
* wins the race on a provider that exposes no synchronous `chainId`, the
|
|
608
|
+
* connect carries 0 - honestly, since the chain is unknown at that instant -
|
|
609
|
+
* and the `connect` payload that follows knows the real one. Emitting again
|
|
610
|
+
* to relabel would mean two connects for one connection, which is the bug
|
|
611
|
+
* this whole path exists to prevent. That payload still corrects
|
|
612
|
+
* `currentChainId`, so everything after it is attributed properly.
|
|
613
|
+
*/
|
|
614
|
+
EvmEventTracker.prototype.shouldReportConnect = function (provider, address) {
|
|
615
|
+
var reported = this._announcedConnect.get(provider);
|
|
616
|
+
if (!reported)
|
|
617
|
+
return true;
|
|
618
|
+
return reported.address.toLowerCase() !== address.toLowerCase();
|
|
619
|
+
};
|
|
620
|
+
/**
|
|
621
|
+
* Record a connect as reported - but only if it will actually be sent.
|
|
622
|
+
*
|
|
623
|
+
* `connect()` passes through `shouldTrack()`, which refuses an unresolvable
|
|
624
|
+
* chain when `tracking.excludeChains` is configured. Marking a refused event
|
|
625
|
+
* as reported would suppress the authoritative one that follows.
|
|
626
|
+
*/
|
|
627
|
+
EvmEventTracker.prototype.markConnectReported = function (provider, address, chainId) {
|
|
628
|
+
if (!this.deps.willTrackEvent(chainId))
|
|
629
|
+
return;
|
|
630
|
+
this._announcedConnect.set(provider, { address: address, chainId: chainId });
|
|
631
|
+
};
|
|
632
|
+
EvmEventTracker.prototype.registerConnectListener = function (provider) {
|
|
633
|
+
var _this = this;
|
|
634
|
+
logger.info("registerConnectListener");
|
|
635
|
+
var listener = function () {
|
|
636
|
+
var args = [];
|
|
637
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
638
|
+
args[_i] = arguments[_i];
|
|
639
|
+
}
|
|
640
|
+
var connection = args[0];
|
|
641
|
+
_this.onConnected(provider, connection);
|
|
642
|
+
};
|
|
643
|
+
provider.on("connect", listener);
|
|
644
|
+
this.registry.addListener(provider, "connect", listener);
|
|
645
|
+
};
|
|
646
|
+
EvmEventTracker.prototype.registerDisconnectListener = function (provider) {
|
|
647
|
+
var _this = this;
|
|
648
|
+
logger.info("registerDisconnectListener");
|
|
649
|
+
var listener = function (_error) { return __awaiter(_this, void 0, void 0, function () {
|
|
650
|
+
var e_1;
|
|
651
|
+
return __generator(this, function (_a) {
|
|
652
|
+
switch (_a.label) {
|
|
653
|
+
case 0:
|
|
654
|
+
if (this.wallet.provider !== provider)
|
|
655
|
+
return [2 /*return*/];
|
|
656
|
+
// As in the accountsChanged disconnect path: the reported connect ends
|
|
657
|
+
// with the connection.
|
|
658
|
+
this._announcedConnect.delete(provider);
|
|
659
|
+
logger.info("OnDisconnect: Wallet disconnect event received, current state:", {
|
|
660
|
+
currentAddress: this.wallet.evmAddress,
|
|
661
|
+
currentChainId: this.wallet.evmChainId,
|
|
662
|
+
});
|
|
663
|
+
if (!this.deps.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 5];
|
|
664
|
+
_a.label = 1;
|
|
665
|
+
case 1:
|
|
666
|
+
_a.trys.push([1, 3, , 4]);
|
|
667
|
+
// Pass current state explicitly to ensure we have the data for the disconnect event
|
|
668
|
+
return [4 /*yield*/, this.deps.disconnect({
|
|
669
|
+
chainId: this.wallet.evmChainId,
|
|
670
|
+
address: this.wallet.evmAddress,
|
|
671
|
+
})];
|
|
672
|
+
case 2:
|
|
673
|
+
// Pass current state explicitly to ensure we have the data for the disconnect event
|
|
674
|
+
_a.sent();
|
|
675
|
+
return [3 /*break*/, 4];
|
|
676
|
+
case 3:
|
|
677
|
+
e_1 = _a.sent();
|
|
678
|
+
logger.error("Error during disconnect in disconnect listener", e_1);
|
|
679
|
+
return [3 /*break*/, 4];
|
|
680
|
+
case 4: return [3 /*break*/, 6];
|
|
681
|
+
case 5:
|
|
682
|
+
logger.debug("OnDisconnect: Disconnect event skipped (autocapture.disconnect: false)");
|
|
683
|
+
this.wallet.beginDisconnect("evm");
|
|
684
|
+
// Still clear state even if not tracking the event
|
|
685
|
+
this.wallet.clear('evm');
|
|
686
|
+
_a.label = 6;
|
|
687
|
+
case 6: return [2 /*return*/];
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
}); };
|
|
691
|
+
provider.on("disconnect", listener);
|
|
692
|
+
this.registry.addListener(provider, "disconnect", listener);
|
|
693
|
+
};
|
|
694
|
+
EvmEventTracker.prototype.onConnected = function (provider, connection) {
|
|
695
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
696
|
+
var disconnectsBefore, stateTicket, chainId, address, committed, movedElsewhere, wasDisconnected, isActiveProvider, providerInfo, effectiveChainId, providerInfo, e_2;
|
|
697
|
+
return __generator(this, function (_a) {
|
|
698
|
+
switch (_a.label) {
|
|
699
|
+
case 0:
|
|
700
|
+
logger.info("onConnected", connection);
|
|
701
|
+
disconnectsBefore = this.wallet.disconnectsSoFar("evm");
|
|
702
|
+
stateTicket = this.wallet.currentObservation("evm");
|
|
703
|
+
_a.label = 1;
|
|
704
|
+
case 1:
|
|
705
|
+
_a.trys.push([1, 3, , 4]);
|
|
706
|
+
if (!(connection === null || connection === void 0 ? void 0 : connection.chainId) || typeof connection.chainId !== "string")
|
|
707
|
+
return [2 /*return*/];
|
|
708
|
+
chainId = parseChainId(connection.chainId);
|
|
709
|
+
// Record it for this provider before anything can bail out below.
|
|
710
|
+
this.registry.rememberChain(provider, chainId);
|
|
711
|
+
return [4 /*yield*/, this.registry.addressOf(provider)];
|
|
712
|
+
case 2:
|
|
713
|
+
address = _a.sent();
|
|
714
|
+
// A newer signal arrived while we were resolving the address. Claiming
|
|
715
|
+
// the namespace now would write this stale view over it, and would make
|
|
716
|
+
// a disconnect still in flight look stale so it skipped its cleanup. A
|
|
717
|
+
// reconnect that started AFTER the disconnect holds a newer ticket and
|
|
718
|
+
// is reported normally.
|
|
719
|
+
if (this.wallet.disconnectsSoFar("evm") !== disconnectsBefore) {
|
|
720
|
+
logger.info("onConnected: The wallet disconnected after this observation began; dropping it");
|
|
721
|
+
return [2 /*return*/];
|
|
722
|
+
}
|
|
723
|
+
committed = this.wallet.evmAddress;
|
|
724
|
+
movedElsewhere = !!committed &&
|
|
725
|
+
!!address &&
|
|
726
|
+
committed.toLowerCase() !== address.toLowerCase();
|
|
727
|
+
if (provider === this.wallet.provider &&
|
|
728
|
+
!this.wallet.isCurrent(stateTicket) &&
|
|
729
|
+
movedElsewhere) {
|
|
730
|
+
logger.info("onConnected: A newer signal from this provider has overtaken this connect observation; dropping it");
|
|
731
|
+
return [2 /*return*/];
|
|
732
|
+
}
|
|
733
|
+
if (chainId && address) {
|
|
734
|
+
wasDisconnected = !this.wallet.evmAddress;
|
|
735
|
+
// Set provider if none exists
|
|
736
|
+
if (!this.wallet.provider) {
|
|
737
|
+
this.wallet.provider = provider;
|
|
738
|
+
}
|
|
739
|
+
isActiveProvider = this.wallet.provider === provider;
|
|
740
|
+
// Update state from active provider so disconnect events keep valid
|
|
741
|
+
// address/chainId values - except while suppressed, where we must not
|
|
742
|
+
// LEARN identity (only drop a stale EVM wallet on a switch).
|
|
743
|
+
if (isActiveProvider) {
|
|
744
|
+
if (this.deps.isTrackingSuppressed()) {
|
|
745
|
+
this.wallet.clearStaleEvmWalletOnSwitchWhileSuppressed(address);
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
this.wallet.set('evm', {
|
|
749
|
+
chainId: chainId,
|
|
750
|
+
address: validateAndChecksumAddress(address) || undefined,
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
// Conditionally emit connect event based on tracking configuration.
|
|
755
|
+
//
|
|
756
|
+
// Both handlers observe one connection, so `shouldReportConnect()`
|
|
757
|
+
// decides which of them reports it. It keys on what was actually
|
|
758
|
+
// REPORTED, not on whether an address is known: an address can be
|
|
759
|
+
// present with no connect ever sent - restored from the active-wallet
|
|
760
|
+
// cookie, or reported with an unresolved chain and then refused by
|
|
761
|
+
// `excludeChains` - and this payload carries the authoritative chain,
|
|
762
|
+
// so it must be able to supersede such a report.
|
|
763
|
+
if (isActiveProvider &&
|
|
764
|
+
this.wallet.evmAddress &&
|
|
765
|
+
this.shouldReportConnect(provider, address)) {
|
|
766
|
+
providerInfo = this.registry.infoFor(provider);
|
|
767
|
+
effectiveChainId = chainId || 0;
|
|
768
|
+
if (this.deps.isAutocaptureEnabled("connect")) {
|
|
769
|
+
logger.info("OnConnected: Detected wallet connection, emitting connect event", {
|
|
770
|
+
chainId: chainId,
|
|
771
|
+
wasDisconnected: wasDisconnected,
|
|
772
|
+
providerName: providerInfo.name,
|
|
773
|
+
rdns: providerInfo.rdns,
|
|
774
|
+
hasChainId: !!chainId,
|
|
775
|
+
isActiveProvider: isActiveProvider,
|
|
776
|
+
});
|
|
777
|
+
if (effectiveChainId === 0) {
|
|
778
|
+
logger.info("OnConnected: Using fallback chainId 0 for connect event");
|
|
779
|
+
}
|
|
780
|
+
this.markConnectReported(provider, address, effectiveChainId);
|
|
781
|
+
this.deps.connect({
|
|
782
|
+
chainId: effectiveChainId,
|
|
783
|
+
address: address,
|
|
784
|
+
}, {
|
|
785
|
+
providerName: providerInfo.name,
|
|
786
|
+
rdns: providerInfo.rdns,
|
|
787
|
+
}).catch(function (error) {
|
|
788
|
+
logger.error("Failed to track connect event during provider connection:", error);
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
logger.debug("OnConnected: Connect event skipped (autocapture.connect: false)", {
|
|
793
|
+
chainId: chainId,
|
|
794
|
+
address: address,
|
|
795
|
+
providerName: providerInfo.name,
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
else if (address && !isActiveProvider) {
|
|
800
|
+
providerInfo = this.registry.infoFor(provider);
|
|
801
|
+
logger.debug("OnConnected: Skipping connect event for non-active provider", {
|
|
802
|
+
chainId: chainId,
|
|
803
|
+
providerName: providerInfo.name,
|
|
804
|
+
rdns: providerInfo.rdns,
|
|
805
|
+
isActiveProvider: isActiveProvider,
|
|
806
|
+
activeProviderInfo: this.wallet.provider
|
|
807
|
+
? this.registry.infoFor(this.wallet.provider)
|
|
808
|
+
: null,
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return [3 /*break*/, 4];
|
|
813
|
+
case 3:
|
|
814
|
+
e_2 = _a.sent();
|
|
815
|
+
logger.error("Error handling connect event", e_2);
|
|
816
|
+
return [3 /*break*/, 4];
|
|
817
|
+
case 4: return [2 /*return*/];
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
};
|
|
822
|
+
EvmEventTracker.prototype.getProviders = function () {
|
|
823
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
824
|
+
var store, providers, injected, injectedProviderInfo, injectedDetail, uniqueProviders, _i, uniqueProviders_1, detail;
|
|
825
|
+
var _this = this;
|
|
826
|
+
return __generator(this, function (_a) {
|
|
827
|
+
store = createStore();
|
|
828
|
+
providers = store.getProviders();
|
|
829
|
+
this.unsubscribeDiscovery = store.subscribe(function (providerDetails) {
|
|
830
|
+
providers = providerDetails;
|
|
831
|
+
// Process newly added providers with proper deduplication
|
|
832
|
+
var newlyAddedDetails = providerDetails.filter(function (detail) {
|
|
833
|
+
var provider = detail === null || detail === void 0 ? void 0 : detail.provider;
|
|
834
|
+
return provider && !_this.registry.isSeen(provider);
|
|
835
|
+
});
|
|
836
|
+
// Add new providers to the array without overwriting existing ones
|
|
837
|
+
for (var _i = 0, newlyAddedDetails_1 = newlyAddedDetails; _i < newlyAddedDetails_1.length; _i++) {
|
|
838
|
+
var detail = newlyAddedDetails_1[_i];
|
|
839
|
+
_this.registry.add(detail);
|
|
840
|
+
}
|
|
841
|
+
// Track listeners for newly discovered providers only
|
|
842
|
+
var newDetails = providerDetails.filter(function (detail) {
|
|
843
|
+
var p = detail === null || detail === void 0 ? void 0 : detail.provider;
|
|
844
|
+
return !!p && !_this.registry.isTracked(p);
|
|
845
|
+
});
|
|
846
|
+
if (newDetails.length > 0) {
|
|
847
|
+
_this.trackProviders(newDetails);
|
|
848
|
+
// Detect newly discovered wallets (session de-dupes) with error handling
|
|
849
|
+
(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
850
|
+
var e_3;
|
|
851
|
+
return __generator(this, function (_a) {
|
|
852
|
+
switch (_a.label) {
|
|
853
|
+
case 0:
|
|
854
|
+
_a.trys.push([0, 2, , 3]);
|
|
855
|
+
return [4 /*yield*/, this.detectWallets(newDetails)];
|
|
856
|
+
case 1:
|
|
857
|
+
_a.sent();
|
|
858
|
+
return [3 /*break*/, 3];
|
|
859
|
+
case 2:
|
|
860
|
+
e_3 = _a.sent();
|
|
861
|
+
logger.error("Formo: Failed to detect wallets", e_3);
|
|
862
|
+
return [3 /*break*/, 3];
|
|
863
|
+
case 3: return [2 /*return*/];
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
}); })();
|
|
867
|
+
}
|
|
868
|
+
// Clean up providers that are no longer available. Compared against
|
|
869
|
+
// THIS announcement: the registry's list is historical and append-only,
|
|
870
|
+
// so comparing against it could never find anything missing.
|
|
871
|
+
_this.cleanupUnavailableProviders(providerDetails);
|
|
872
|
+
});
|
|
873
|
+
// Fallback to injected provider if no providers are found
|
|
874
|
+
if (providers.length === 0) {
|
|
875
|
+
injected = typeof window !== "undefined" ? window.ethereum : undefined;
|
|
876
|
+
if (injected) {
|
|
877
|
+
// If we have already detected and cached the injected provider, and it's the same instance, return the cached result
|
|
878
|
+
if (this.registry.injected &&
|
|
879
|
+
this.registry.injected.provider === injected) {
|
|
880
|
+
// Ensure it's tracked
|
|
881
|
+
if (!this.registry.isTracked(injected)) {
|
|
882
|
+
this.trackEIP1193Provider(injected);
|
|
883
|
+
}
|
|
884
|
+
// Merge with existing providers instead of overwriting
|
|
885
|
+
this.registry.add(this.registry.injected);
|
|
886
|
+
return [2 /*return*/, this.registry.all];
|
|
887
|
+
}
|
|
888
|
+
// Re-check if the injected provider is already tracked just before tracking
|
|
889
|
+
if (!this.registry.isTracked(injected)) {
|
|
890
|
+
this.trackEIP1193Provider(injected);
|
|
891
|
+
}
|
|
892
|
+
injectedProviderInfo = detectInjectedProviderInfo(injected);
|
|
893
|
+
injectedDetail = {
|
|
894
|
+
provider: injected,
|
|
895
|
+
info: injectedProviderInfo,
|
|
896
|
+
};
|
|
897
|
+
// Cache the detected injected provider detail
|
|
898
|
+
this.registry.injected = injectedDetail;
|
|
899
|
+
// Merge with existing providers instead of overwriting
|
|
900
|
+
this.registry.add(injectedDetail);
|
|
901
|
+
}
|
|
902
|
+
return [2 /*return*/, this.registry.all];
|
|
903
|
+
}
|
|
904
|
+
uniqueProviders = providers.filter(function (detail) {
|
|
905
|
+
var provider = detail === null || detail === void 0 ? void 0 : detail.provider;
|
|
906
|
+
return provider && !_this.registry.isSeen(provider);
|
|
907
|
+
});
|
|
908
|
+
// Add to seen providers and instances, ensuring no duplicates in _providers
|
|
909
|
+
for (_i = 0, uniqueProviders_1 = uniqueProviders; _i < uniqueProviders_1.length; _i++) {
|
|
910
|
+
detail = uniqueProviders_1[_i];
|
|
911
|
+
this.registry.add(detail);
|
|
912
|
+
}
|
|
913
|
+
return [2 /*return*/, this.registry.all];
|
|
914
|
+
});
|
|
915
|
+
});
|
|
916
|
+
};
|
|
917
|
+
EvmEventTracker.prototype.detectWallets = function (providers) {
|
|
918
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
919
|
+
var _i, providers_2, eip6963ProviderDetail, err_1;
|
|
920
|
+
return __generator(this, function (_a) {
|
|
921
|
+
switch (_a.label) {
|
|
922
|
+
case 0:
|
|
923
|
+
_a.trys.push([0, 5, , 6]);
|
|
924
|
+
_i = 0, providers_2 = providers;
|
|
925
|
+
_a.label = 1;
|
|
926
|
+
case 1:
|
|
927
|
+
if (!(_i < providers_2.length)) return [3 /*break*/, 4];
|
|
928
|
+
eip6963ProviderDetail = providers_2[_i];
|
|
929
|
+
return [4 /*yield*/, this.deps.detect({
|
|
930
|
+
providerName: eip6963ProviderDetail === null || eip6963ProviderDetail === void 0 ? void 0 : eip6963ProviderDetail.info.name,
|
|
931
|
+
rdns: eip6963ProviderDetail === null || eip6963ProviderDetail === void 0 ? void 0 : eip6963ProviderDetail.info.rdns,
|
|
932
|
+
})];
|
|
933
|
+
case 2:
|
|
934
|
+
_a.sent();
|
|
935
|
+
_a.label = 3;
|
|
936
|
+
case 3:
|
|
937
|
+
_i++;
|
|
938
|
+
return [3 /*break*/, 1];
|
|
939
|
+
case 4: return [3 /*break*/, 6];
|
|
940
|
+
case 5:
|
|
941
|
+
err_1 = _a.sent();
|
|
942
|
+
logger.error("Error detect all wallets:", err_1);
|
|
943
|
+
return [3 /*break*/, 6];
|
|
944
|
+
case 6: return [2 /*return*/];
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
};
|
|
949
|
+
/**
|
|
950
|
+
* Seed a provider's chain from whatever it already exposes synchronously.
|
|
951
|
+
*
|
|
952
|
+
* Most EIP-1193 implementations carry a `chainId` property (MetaMask,
|
|
953
|
+
* WalletConnect, Coinbase). Reading it costs nothing and cannot block.
|
|
954
|
+
*
|
|
955
|
+
* There is deliberately no RPC fallback. An earlier version probed with
|
|
956
|
+
* `eth_chainId` when a provider was first tracked, on the theory that
|
|
957
|
+
* tracking time is off the user's critical path. It is not: a serialized
|
|
958
|
+
* transport has ONE queue, so a stalled probe sits in front of every later
|
|
959
|
+
* signature and transaction the dapp makes. It could also land out of order
|
|
960
|
+
* - a slow probe response overwriting a newer `chainChanged` - and relabel
|
|
961
|
+
* events onto a chain the wallet had already left.
|
|
962
|
+
*
|
|
963
|
+
* A provider that exposes nothing stays unknown until it emits
|
|
964
|
+
* `chainChanged` or `connect`, and unknown is reported honestly as 0.
|
|
965
|
+
*/
|
|
966
|
+
EvmEventTracker.prototype.seedProviderChainFromState = function (provider) {
|
|
967
|
+
var raw = provider.chainId;
|
|
968
|
+
var chainId = typeof raw === "string"
|
|
969
|
+
? parseChainId(raw)
|
|
970
|
+
: typeof raw === "number"
|
|
971
|
+
? raw
|
|
972
|
+
: undefined;
|
|
973
|
+
this.registry.rememberChain(provider, chainId);
|
|
974
|
+
};
|
|
975
|
+
EvmEventTracker.prototype.untrackProvider = function (provider) {
|
|
976
|
+
try {
|
|
977
|
+
this.registry.removeListeners(provider);
|
|
978
|
+
// Only stop tracking it if the listeners actually came off. "Tracked"
|
|
979
|
+
// means "has our listeners wired up", which is still true when removal
|
|
980
|
+
// threw, and `cleanup()` iterates the tracked set. Forgetting it here
|
|
981
|
+
// regardless is what made a retained listener unreachable: the entry
|
|
982
|
+
// survived but nothing ever looked at it again, so the retry that
|
|
983
|
+
// retention exists for could never happen.
|
|
984
|
+
if (this.registry.attachedEvents(provider).length === 0) {
|
|
985
|
+
this.registry.forgetTracked(provider);
|
|
986
|
+
}
|
|
987
|
+
if (this.wallet.provider === provider) {
|
|
988
|
+
this.wallet.clearProvider();
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
catch (e) {
|
|
992
|
+
logger.warn("Failed to untrack provider", e);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
/**
|
|
996
|
+
* Clean up providers that are no longer available
|
|
997
|
+
* This helps maintain consistent state and prevents memory leaks
|
|
998
|
+
*/
|
|
999
|
+
EvmEventTracker.prototype.cleanupUnavailableProviders = function (current) {
|
|
1000
|
+
// Remove providers that are no longer in the current providers list
|
|
1001
|
+
var currentProviderInstances = new Set(current.map(function (detail) { return detail.provider; }));
|
|
1002
|
+
for (var _i = 0, _a = this.registry.trackedProviders(); _i < _a.length; _i++) {
|
|
1003
|
+
var provider = _a[_i];
|
|
1004
|
+
if (!currentProviderInstances.has(provider)) {
|
|
1005
|
+
logger.info("Cleaning up unavailable provider: ".concat(provider.constructor.name));
|
|
1006
|
+
this.untrackProvider(provider);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
/**
|
|
1011
|
+
* Handle provider mismatch by switching to the new provider and invalidating old tokens
|
|
1012
|
+
* @param provider The new provider to switch to
|
|
1013
|
+
*/
|
|
1014
|
+
EvmEventTracker.prototype.handleProviderMismatch = function (provider) {
|
|
1015
|
+
// If this is a different provider, allow the switch
|
|
1016
|
+
if (this.wallet.provider) {
|
|
1017
|
+
// Clear any provider-specific state when switching
|
|
1018
|
+
this.wallet.set('evm', { address: undefined, chainId: undefined, provider: provider });
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
this.wallet.provider = provider;
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
return EvmEventTracker;
|
|
1025
|
+
}());
|
|
1026
|
+
export { EvmEventTracker };
|
|
1027
|
+
//# sourceMappingURL=EvmEventTracker.js.map
|