@formo/analytics 1.11.11 → 1.11.13
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/README.md +1 -1
- package/dist/387.index.umd.min.js +125 -0
- package/dist/387.index.umd.min.js.map +1 -0
- package/dist/cjs/src/FormoAnalytics.d.ts +4 -10
- package/dist/cjs/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/cjs/src/FormoAnalytics.js +111 -225
- package/dist/cjs/src/FormoAnalytics.js.map +1 -1
- package/dist/cjs/src/types/base.d.ts +1 -1
- package/dist/cjs/src/types/base.d.ts.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/src/FormoAnalytics.d.ts +4 -10
- package/dist/esm/src/FormoAnalytics.d.ts.map +1 -1
- package/dist/esm/src/FormoAnalytics.js +114 -228
- package/dist/esm/src/FormoAnalytics.js.map +1 -1
- package/dist/esm/src/types/base.d.ts +1 -1
- package/dist/esm/src/types/base.d.ts.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/src/FormoAnalytics.ts +97 -192
- package/src/types/base.ts +1 -1
- package/dist/223.index.umd.min.js +0 -125
- package/dist/223.index.umd.min.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formo/analytics",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.13",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/getformo/sdk.git"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"sinon-chai": "^3.7.0",
|
|
64
64
|
"ts-loader": "^9.3.1",
|
|
65
65
|
"ts-node": "^10.8.2",
|
|
66
|
-
"typescript": "^5.
|
|
66
|
+
"typescript": "^5.6.3",
|
|
67
67
|
"webpack": "^5.74.0",
|
|
68
68
|
"webpack-cli": "^4.10.0"
|
|
69
69
|
},
|
package/src/FormoAnalytics.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import axios from
|
|
1
|
+
import axios from "axios";
|
|
2
2
|
import {
|
|
3
3
|
COUNTRY_LIST,
|
|
4
4
|
EVENTS_API_URL,
|
|
5
5
|
SESSION_STORAGE_ID_KEY,
|
|
6
6
|
Event,
|
|
7
|
-
} from
|
|
8
|
-
import { H } from
|
|
9
|
-
import { ChainID, EIP1193Provider, Options } from
|
|
7
|
+
} from "./constants";
|
|
8
|
+
import { H } from "highlight.run";
|
|
9
|
+
import { ChainID, EIP1193Provider, Options } from "./types";
|
|
10
10
|
|
|
11
11
|
interface IFormoAnalytics {
|
|
12
12
|
/**
|
|
13
13
|
* Initializes the FormoAnalytics instance with the provided API key and project ID.
|
|
14
14
|
*/
|
|
15
|
-
init(apiKey: string, options
|
|
15
|
+
init(apiKey: string, options: Options): Promise<FormoAnalytics>;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Tracks page visit events.
|
|
@@ -49,7 +49,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
49
49
|
(...args: unknown[]) => void
|
|
50
50
|
> = {};
|
|
51
51
|
|
|
52
|
-
private walletAddressSessionKey =
|
|
52
|
+
private walletAddressSessionKey = "walletAddress";
|
|
53
53
|
private config: Config;
|
|
54
54
|
private sessionIdKey: string = SESSION_STORAGE_ID_KEY;
|
|
55
55
|
private timezoneToCountry: Record<string, string> = COUNTRY_LIST;
|
|
@@ -72,10 +72,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
static async init(
|
|
76
|
-
apiKey: string,
|
|
77
|
-
options?: Options
|
|
78
|
-
): Promise<FormoAnalytics> {
|
|
75
|
+
static async init(apiKey: string, options: Options): Promise<FormoAnalytics> {
|
|
79
76
|
const config = {
|
|
80
77
|
token: apiKey,
|
|
81
78
|
};
|
|
@@ -101,7 +98,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
101
98
|
}
|
|
102
99
|
|
|
103
100
|
private getOrigin(): string {
|
|
104
|
-
return window.location.origin ||
|
|
101
|
+
return window.location.origin || "ORIGIN_NOT_FOUND";
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
// Function to set the session cookie
|
|
@@ -120,8 +117,8 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
120
117
|
|
|
121
118
|
// Function to get a cookie value by name
|
|
122
119
|
private getCookieValue(name: string): string | undefined {
|
|
123
|
-
const cookies = document.cookie.split(
|
|
124
|
-
const [key, value] = cookie.split(
|
|
120
|
+
const cookies = document.cookie.split(";").reduce((acc, cookie) => {
|
|
121
|
+
const [key, value] = cookie.split("=");
|
|
125
122
|
acc[key.trim()] = value;
|
|
126
123
|
return acc;
|
|
127
124
|
}, {} as Record<string, string>);
|
|
@@ -141,8 +138,12 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
141
138
|
session_id: this.getSessionId(),
|
|
142
139
|
timestamp: new Date().toISOString(),
|
|
143
140
|
action,
|
|
144
|
-
version:
|
|
145
|
-
payload
|
|
141
|
+
version: "1",
|
|
142
|
+
payload: {
|
|
143
|
+
// common fields
|
|
144
|
+
...this.getCommonTrackingFields(),
|
|
145
|
+
...payload,
|
|
146
|
+
},
|
|
146
147
|
};
|
|
147
148
|
|
|
148
149
|
const sendRequest = async (): Promise<void> => {
|
|
@@ -152,14 +153,14 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
152
153
|
JSON.stringify(requestData),
|
|
153
154
|
{
|
|
154
155
|
headers: {
|
|
155
|
-
|
|
156
|
+
"Content-Type": "application/json",
|
|
156
157
|
Authorization: `Bearer ${this.apiKey}`,
|
|
157
158
|
},
|
|
158
159
|
}
|
|
159
160
|
);
|
|
160
161
|
|
|
161
162
|
if (response.status >= 200 && response.status < 300) {
|
|
162
|
-
console.log(
|
|
163
|
+
console.log("Event sent successfully:", action);
|
|
163
164
|
} else {
|
|
164
165
|
throw new Error(`Failed with status: ${response.status}`);
|
|
165
166
|
}
|
|
@@ -189,83 +190,49 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
189
190
|
await sendRequest();
|
|
190
191
|
}
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const location = this.getUserLocation();
|
|
197
|
-
const language = this.getUserLanguage();
|
|
198
|
-
|
|
199
|
-
setTimeout(async () => {
|
|
200
|
-
const eventData = await this.buildPageEventData(location, language);
|
|
201
|
-
this.trackEvent(Event.PAGE, eventData);
|
|
202
|
-
}, 300);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private isAutomationEnvironment(): boolean {
|
|
206
|
-
return (
|
|
207
|
-
window.__nightmare ||
|
|
208
|
-
window.navigator.webdriver ||
|
|
209
|
-
window.Cypress ||
|
|
210
|
-
false
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private getUserLocation(): string | undefined {
|
|
193
|
+
private getCommonTrackingFields() {
|
|
194
|
+
let location: string | undefined;
|
|
195
|
+
let language: string = "en";
|
|
215
196
|
try {
|
|
216
197
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return undefined;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
private getUserLanguage(): string {
|
|
225
|
-
try {
|
|
226
|
-
return (
|
|
227
|
-
(navigator.languages && navigator.languages.length
|
|
198
|
+
location = this.timezoneToCountry[timezone];
|
|
199
|
+
language =
|
|
200
|
+
navigator.languages && navigator.languages.length
|
|
228
201
|
? navigator.languages[0]
|
|
229
|
-
: navigator.language
|
|
230
|
-
);
|
|
202
|
+
: navigator.language || "en";
|
|
231
203
|
} catch (error) {
|
|
232
|
-
console.error(
|
|
233
|
-
return 'en';
|
|
204
|
+
console.error("Error resolving timezone or language:", error);
|
|
234
205
|
}
|
|
235
|
-
}
|
|
236
206
|
|
|
237
|
-
async buildPageEventData(location: string | undefined, language: string): Promise<Record<string, unknown>> {
|
|
238
207
|
const url = new URL(window.location.href);
|
|
239
208
|
const params = new URLSearchParams(url.search);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (address === null) {
|
|
243
|
-
console.warn('Wallet address could not be retrieved.');
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const eventData: Record<string, unknown> = {
|
|
247
|
-
'user-agent': window.navigator.userAgent,
|
|
209
|
+
return {
|
|
210
|
+
"user-agent": window.navigator.userAgent,
|
|
248
211
|
locale: language,
|
|
249
212
|
location,
|
|
250
213
|
referrer: document.referrer,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
utm_campaign: params.get('utm_campaign'),
|
|
256
|
-
ref: params.get('ref'),
|
|
214
|
+
utm_source: params.get("utm_source"),
|
|
215
|
+
utm_medium: params.get("utm_medium"),
|
|
216
|
+
utm_campaign: params.get("utm_campaign"),
|
|
217
|
+
ref: params.get("ref"),
|
|
257
218
|
};
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Function to track page hits
|
|
222
|
+
private trackPageHit() {
|
|
223
|
+
if (window.__nightmare || window.navigator.webdriver || window.Cypress)
|
|
224
|
+
return;
|
|
225
|
+
|
|
226
|
+
setTimeout(() => {
|
|
227
|
+
this.trackEvent(Event.PAGE, {
|
|
228
|
+
pathname: window.location.pathname,
|
|
229
|
+
href: window.location.href,
|
|
230
|
+
});
|
|
231
|
+
}, 300);
|
|
264
232
|
}
|
|
265
233
|
|
|
266
234
|
private trackProvider(provider: EIP1193Provider) {
|
|
267
235
|
if (provider === this._provider) {
|
|
268
|
-
console.log('Provider already tracked.');
|
|
269
236
|
return;
|
|
270
237
|
}
|
|
271
238
|
|
|
@@ -283,7 +250,6 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
283
250
|
}
|
|
284
251
|
}
|
|
285
252
|
|
|
286
|
-
console.log('Tracking new provider:', provider);
|
|
287
253
|
this._provider = provider;
|
|
288
254
|
|
|
289
255
|
this.getCurrentWallet();
|
|
@@ -291,33 +257,14 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
291
257
|
this.registerChainChangedListener();
|
|
292
258
|
}
|
|
293
259
|
|
|
294
|
-
private async getAndStoreConnectedAddress(): Promise<string | null> {
|
|
295
|
-
console.warn(
|
|
296
|
-
'Session data missing. Attempting to fetch address from provider.'
|
|
297
|
-
);
|
|
298
|
-
try {
|
|
299
|
-
const accounts = await this.fetchAccounts();
|
|
300
|
-
if (accounts && accounts.length > 0) {
|
|
301
|
-
const address = accounts[0];
|
|
302
|
-
this.storeWalletAddress(address);
|
|
303
|
-
return address;
|
|
304
|
-
}
|
|
305
|
-
} catch (err) {
|
|
306
|
-
console.error('Failed to fetch accounts from provider:', err);
|
|
307
|
-
}
|
|
308
|
-
return null;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
260
|
private async getCurrentWallet() {
|
|
312
261
|
if (!this.provider) {
|
|
313
|
-
console.warn(
|
|
262
|
+
console.warn("FormoAnalytics::getCurrentWallet: the provider is not set");
|
|
314
263
|
return;
|
|
315
264
|
}
|
|
316
|
-
|
|
317
265
|
const sessionData = sessionStorage.getItem(this.walletAddressSessionKey);
|
|
318
|
-
|
|
319
266
|
if (!sessionData) {
|
|
320
|
-
return
|
|
267
|
+
return null;
|
|
321
268
|
}
|
|
322
269
|
|
|
323
270
|
const parsedData = JSON.parse(sessionData);
|
|
@@ -325,70 +272,22 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
325
272
|
const currentTime = Date.now();
|
|
326
273
|
|
|
327
274
|
if (currentTime - parsedData.timestamp > sessionExpiry) {
|
|
328
|
-
console.warn(
|
|
275
|
+
console.warn("Session expired. Ignoring wallet address.");
|
|
329
276
|
sessionStorage.removeItem(this.walletAddressSessionKey); // Clear expired session data
|
|
330
|
-
return
|
|
277
|
+
return "";
|
|
331
278
|
}
|
|
332
279
|
|
|
333
280
|
this.onAddressConnected(parsedData.address);
|
|
334
|
-
return parsedData.address ||
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Utility to fetch accounts
|
|
338
|
-
private async fetchAccounts(): Promise<string[] | null> {
|
|
339
|
-
try {
|
|
340
|
-
const res: string[] | null | undefined = await this.provider?.request({
|
|
341
|
-
method: 'eth_accounts',
|
|
342
|
-
});
|
|
343
|
-
if (!res || res.length === 0) {
|
|
344
|
-
console.error(
|
|
345
|
-
'error',
|
|
346
|
-
'FormoAnalytics::fetchAccounts: unable to get account. eth_accounts returned empty'
|
|
347
|
-
);
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
|
-
return res;
|
|
351
|
-
} catch (err) {
|
|
352
|
-
if ((err as any).code !== 4001) {
|
|
353
|
-
console.error(
|
|
354
|
-
'error',
|
|
355
|
-
'FormoAnalytics::fetchAccounts: eth_accounts threw an error',
|
|
356
|
-
err
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Utility to fetch chain ID
|
|
364
|
-
private async fetchChainId(): Promise<string | null> {
|
|
365
|
-
try {
|
|
366
|
-
const chainIdHex = await this.provider?.request<string>({
|
|
367
|
-
method: 'eth_chainId',
|
|
368
|
-
});
|
|
369
|
-
if (!chainIdHex) {
|
|
370
|
-
console.error(
|
|
371
|
-
'FormoAnalytics::fetchChainId: chainIdHex is null or undefined'
|
|
372
|
-
);
|
|
373
|
-
return null;
|
|
374
|
-
}
|
|
375
|
-
return chainIdHex;
|
|
376
|
-
} catch (err) {
|
|
377
|
-
console.error(
|
|
378
|
-
'error',
|
|
379
|
-
'FormoAnalytics::fetchChainId: eth_chainId threw an error',
|
|
380
|
-
err
|
|
381
|
-
);
|
|
382
|
-
return null;
|
|
383
|
-
}
|
|
281
|
+
return parsedData.address || "";
|
|
384
282
|
}
|
|
385
283
|
|
|
386
284
|
private async getCurrentChainId(): Promise<string> {
|
|
387
285
|
if (!this.provider) {
|
|
388
|
-
console.error(
|
|
286
|
+
console.error("FormoAnalytics::getCurrentChainId: provider not set");
|
|
389
287
|
}
|
|
390
|
-
|
|
391
|
-
|
|
288
|
+
const chainIdHex = await this.provider?.request<string>({
|
|
289
|
+
method: "eth_chainId",
|
|
290
|
+
});
|
|
392
291
|
// Because we're connected, the chainId cannot be null
|
|
393
292
|
if (!chainIdHex) {
|
|
394
293
|
console.error(
|
|
@@ -403,19 +302,19 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
403
302
|
const listener = (...args: unknown[]) =>
|
|
404
303
|
this.onAddressChanged(args[0] as string[]);
|
|
405
304
|
|
|
406
|
-
this._provider?.on(
|
|
407
|
-
this._registeredProviderListeners[
|
|
305
|
+
this._provider?.on("accountsChanged", listener);
|
|
306
|
+
this._registeredProviderListeners["accountsChanged"] = listener;
|
|
408
307
|
|
|
409
308
|
const onAddressDisconnected = this.onAddressDisconnected.bind(this);
|
|
410
|
-
this._provider?.on(
|
|
411
|
-
this._registeredProviderListeners[
|
|
309
|
+
this._provider?.on("disconnect", onAddressDisconnected);
|
|
310
|
+
this._registeredProviderListeners["disconnect"] = onAddressDisconnected;
|
|
412
311
|
}
|
|
413
312
|
|
|
414
313
|
private registerChainChangedListener() {
|
|
415
314
|
const listener = (...args: unknown[]) =>
|
|
416
315
|
this.onChainChanged(args[0] as string);
|
|
417
|
-
this.provider?.on(
|
|
418
|
-
this._registeredProviderListeners[
|
|
316
|
+
this.provider?.on("chainChanged", listener);
|
|
317
|
+
this._registeredProviderListeners["chainChanged"] = listener;
|
|
419
318
|
}
|
|
420
319
|
|
|
421
320
|
private async onAddressChanged(addresses: string[]) {
|
|
@@ -450,7 +349,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
450
349
|
|
|
451
350
|
const payload = {
|
|
452
351
|
chain_id: this.currentChainId,
|
|
453
|
-
address: this.
|
|
352
|
+
address: this.currentConnectedAddress,
|
|
454
353
|
};
|
|
455
354
|
this.currentChainId = undefined;
|
|
456
355
|
this.currentConnectedAddress = undefined;
|
|
@@ -464,37 +363,43 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
464
363
|
if (!this.currentConnectedAddress) {
|
|
465
364
|
if (!this.provider) {
|
|
466
365
|
console.error(
|
|
467
|
-
|
|
468
|
-
|
|
366
|
+
"error",
|
|
367
|
+
"FormoAnalytics::onChainChanged: provider not found. CHAIN_CHANGED not reported"
|
|
469
368
|
);
|
|
470
369
|
return;
|
|
471
370
|
}
|
|
472
371
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
372
|
+
try {
|
|
373
|
+
const res: string[] | null | undefined = await this.provider.request({
|
|
374
|
+
method: "eth_accounts",
|
|
375
|
+
});
|
|
376
|
+
if (!res || res.length === 0) {
|
|
377
|
+
console.error(
|
|
378
|
+
"error",
|
|
379
|
+
"FormoAnalytics::onChainChanged: unable to get account. eth_accounts returned empty"
|
|
380
|
+
);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
482
383
|
|
|
483
|
-
|
|
384
|
+
this.currentConnectedAddress = res[0];
|
|
385
|
+
} catch (err) {
|
|
386
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
387
|
+
if ((err as any).code !== 4001) {
|
|
388
|
+
// 4001: The request is rejected by the user , see https://docs.metamask.io/wallet/reference/provider-api/#errors
|
|
389
|
+
console.error(
|
|
390
|
+
"error",
|
|
391
|
+
`FormoAnalytics::onChainChanged: unable to get account. eth_accounts threw an error`,
|
|
392
|
+
err
|
|
393
|
+
);
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
484
397
|
}
|
|
485
398
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
address: this.currentConnectedAddress,
|
|
491
|
-
});
|
|
492
|
-
} else {
|
|
493
|
-
console.error(
|
|
494
|
-
'error',
|
|
495
|
-
'FormoAnalytics::onChainChanged: currentConnectedAddress is null despite fetch attempt'
|
|
496
|
-
);
|
|
497
|
-
}
|
|
399
|
+
return this.chain({
|
|
400
|
+
chainId: this.currentChainId,
|
|
401
|
+
address: this.currentConnectedAddress,
|
|
402
|
+
});
|
|
498
403
|
}
|
|
499
404
|
|
|
500
405
|
/**
|
|
@@ -503,7 +408,7 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
503
408
|
*/
|
|
504
409
|
private storeWalletAddress(address: string): void {
|
|
505
410
|
if (!address) {
|
|
506
|
-
console.error(
|
|
411
|
+
console.error("No wallet address provided to store.");
|
|
507
412
|
return;
|
|
508
413
|
}
|
|
509
414
|
|
|
@@ -532,10 +437,10 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
532
437
|
|
|
533
438
|
connect({ chainId, address }: { chainId: ChainID; address: string }) {
|
|
534
439
|
if (!chainId) {
|
|
535
|
-
throw new Error(
|
|
440
|
+
throw new Error("FormoAnalytics::connect: chain ID cannot be empty");
|
|
536
441
|
}
|
|
537
442
|
if (!address) {
|
|
538
|
-
throw new Error(
|
|
443
|
+
throw new Error("FormoAnalytics::connect: address cannot be empty");
|
|
539
444
|
}
|
|
540
445
|
|
|
541
446
|
this.currentChainId = chainId.toString();
|
|
@@ -567,16 +472,16 @@ export class FormoAnalytics implements IFormoAnalytics {
|
|
|
567
472
|
|
|
568
473
|
chain({ chainId, address }: { chainId: ChainID; address?: string }) {
|
|
569
474
|
if (!chainId || Number(chainId) === 0) {
|
|
570
|
-
throw new Error(
|
|
475
|
+
throw new Error("FormoAnalytics::chain: chainId cannot be empty or 0");
|
|
571
476
|
}
|
|
572
477
|
if (!address && !this.currentConnectedAddress) {
|
|
573
478
|
throw new Error(
|
|
574
|
-
|
|
479
|
+
"FormoAnalytics::chain: address was empty and no previous address has been recorded. You can either pass an address or call connect() first"
|
|
575
480
|
);
|
|
576
481
|
}
|
|
577
482
|
if (isNaN(Number(chainId))) {
|
|
578
483
|
throw new Error(
|
|
579
|
-
|
|
484
|
+
"FormoAnalytics::chain: chainId must be a valid hex or decimal number"
|
|
580
485
|
);
|
|
581
486
|
}
|
|
582
487
|
|