@shadow-corp/nearconnect 1.0.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/README.md +546 -0
- package/build/InjectedWallet.d.ts +22 -0
- package/build/InjectedWallet.js +58 -0
- package/build/InjectedWallet.js.map +1 -0
- package/build/NearConnector.d.ts +151 -0
- package/build/NearConnector.js +536 -0
- package/build/NearConnector.js.map +1 -0
- package/build/ParentFrameWallet.d.ts +22 -0
- package/build/ParentFrameWallet.js +66 -0
- package/build/ParentFrameWallet.js.map +1 -0
- package/build/SandboxedWallet/code.d.ts +7 -0
- package/build/SandboxedWallet/code.js +324 -0
- package/build/SandboxedWallet/code.js.map +1 -0
- package/build/SandboxedWallet/executor.d.ts +23 -0
- package/build/SandboxedWallet/executor.js +338 -0
- package/build/SandboxedWallet/executor.js.map +1 -0
- package/build/SandboxedWallet/iframe.d.ts +18 -0
- package/build/SandboxedWallet/iframe.js +78 -0
- package/build/SandboxedWallet/iframe.js.map +1 -0
- package/build/SandboxedWallet/index.d.ts +24 -0
- package/build/SandboxedWallet/index.js +54 -0
- package/build/SandboxedWallet/index.js.map +1 -0
- package/build/actions/index.d.ts +3 -0
- package/build/actions/index.js +105 -0
- package/build/actions/index.js.map +1 -0
- package/build/actions/types.d.ts +76 -0
- package/build/actions/types.js +3 -0
- package/build/actions/types.js.map +1 -0
- package/build/connection/health.d.ts +213 -0
- package/build/connection/health.js +391 -0
- package/build/connection/health.js.map +1 -0
- package/build/connection/index.d.ts +4 -0
- package/build/connection/index.js +48 -0
- package/build/connection/index.js.map +1 -0
- package/build/connection/reconnect.d.ts +261 -0
- package/build/connection/reconnect.js +454 -0
- package/build/connection/reconnect.js.map +1 -0
- package/build/connection/retry.d.ts +187 -0
- package/build/connection/retry.js +427 -0
- package/build/connection/retry.js.map +1 -0
- package/build/connection/state.d.ts +222 -0
- package/build/connection/state.js +431 -0
- package/build/connection/state.js.map +1 -0
- package/build/errors.d.ts +177 -0
- package/build/errors.js +546 -0
- package/build/errors.js.map +1 -0
- package/build/hardware/errors.d.ts +36 -0
- package/build/hardware/errors.js +127 -0
- package/build/hardware/errors.js.map +1 -0
- package/build/hardware/index.d.ts +7 -0
- package/build/hardware/index.js +39 -0
- package/build/hardware/index.js.map +1 -0
- package/build/hardware/near-app.d.ts +95 -0
- package/build/hardware/near-app.js +291 -0
- package/build/hardware/near-app.js.map +1 -0
- package/build/hardware/transport.d.ts +94 -0
- package/build/hardware/transport.js +267 -0
- package/build/hardware/transport.js.map +1 -0
- package/build/hardware/types.d.ts +98 -0
- package/build/hardware/types.js +72 -0
- package/build/hardware/types.js.map +1 -0
- package/build/helpers/analytics.d.ts +191 -0
- package/build/helpers/analytics.js +304 -0
- package/build/helpers/analytics.js.map +1 -0
- package/build/helpers/base58.d.ts +6 -0
- package/build/helpers/base58.js +47 -0
- package/build/helpers/base58.js.map +1 -0
- package/build/helpers/events.d.ts +42 -0
- package/build/helpers/events.js +68 -0
- package/build/helpers/events.js.map +1 -0
- package/build/helpers/html.d.ts +8 -0
- package/build/helpers/html.js +30 -0
- package/build/helpers/html.js.map +1 -0
- package/build/helpers/indexdb.d.ts +14 -0
- package/build/helpers/indexdb.js +166 -0
- package/build/helpers/indexdb.js.map +1 -0
- package/build/helpers/manifest.d.ts +147 -0
- package/build/helpers/manifest.js +329 -0
- package/build/helpers/manifest.js.map +1 -0
- package/build/helpers/queue.d.ts +11 -0
- package/build/helpers/queue.js +48 -0
- package/build/helpers/queue.js.map +1 -0
- package/build/helpers/session.d.ts +119 -0
- package/build/helpers/session.js +289 -0
- package/build/helpers/session.js.map +1 -0
- package/build/helpers/simulation.d.ts +128 -0
- package/build/helpers/simulation.js +441 -0
- package/build/helpers/simulation.js.map +1 -0
- package/build/helpers/storage.d.ts +58 -0
- package/build/helpers/storage.js +190 -0
- package/build/helpers/storage.js.map +1 -0
- package/build/helpers/trust.d.ts +157 -0
- package/build/helpers/trust.js +340 -0
- package/build/helpers/trust.js.map +1 -0
- package/build/helpers/url.d.ts +1 -0
- package/build/helpers/url.js +13 -0
- package/build/helpers/url.js.map +1 -0
- package/build/helpers/uuid.d.ts +1 -0
- package/build/helpers/uuid.js +14 -0
- package/build/helpers/uuid.js.map +1 -0
- package/build/index.d.ts +21 -0
- package/build/index.js +167 -0
- package/build/index.js.map +1 -0
- package/build/popups/IframeWalletPopup.d.ts +16 -0
- package/build/popups/IframeWalletPopup.js +38 -0
- package/build/popups/IframeWalletPopup.js.map +1 -0
- package/build/popups/NearWalletsPopup.d.ts +25 -0
- package/build/popups/NearWalletsPopup.js +153 -0
- package/build/popups/NearWalletsPopup.js.map +1 -0
- package/build/popups/Popup.d.ts +22 -0
- package/build/popups/Popup.js +94 -0
- package/build/popups/Popup.js.map +1 -0
- package/build/popups/styles.d.ts +1 -0
- package/build/popups/styles.js +257 -0
- package/build/popups/styles.js.map +1 -0
- package/build/security/audit-log.d.ts +123 -0
- package/build/security/audit-log.js +268 -0
- package/build/security/audit-log.js.map +1 -0
- package/build/security/csp.d.ts +68 -0
- package/build/security/csp.js +328 -0
- package/build/security/csp.js.map +1 -0
- package/build/security/index.d.ts +10 -0
- package/build/security/index.js +42 -0
- package/build/security/index.js.map +1 -0
- package/build/security/origin-guard.d.ts +90 -0
- package/build/security/origin-guard.js +244 -0
- package/build/security/origin-guard.js.map +1 -0
- package/build/security/rate-limiter.d.ts +84 -0
- package/build/security/rate-limiter.js +212 -0
- package/build/security/rate-limiter.js.map +1 -0
- package/build/security/secure-storage.d.ts +77 -0
- package/build/security/secure-storage.js +242 -0
- package/build/security/secure-storage.js.map +1 -0
- package/build/security/transaction-guard.d.ts +71 -0
- package/build/security/transaction-guard.js +239 -0
- package/build/security/transaction-guard.js.map +1 -0
- package/build/types.d.ts +508 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/build/ui/AccountSwitcherModal.d.ts +53 -0
- package/build/ui/AccountSwitcherModal.js +239 -0
- package/build/ui/AccountSwitcherModal.js.map +1 -0
- package/build/ui/Modal.d.ts +84 -0
- package/build/ui/Modal.js +278 -0
- package/build/ui/Modal.js.map +1 -0
- package/build/ui/TransactionModal.d.ts +84 -0
- package/build/ui/TransactionModal.js +406 -0
- package/build/ui/TransactionModal.js.map +1 -0
- package/build/ui/WalletSelectorModal.d.ts +97 -0
- package/build/ui/WalletSelectorModal.js +481 -0
- package/build/ui/WalletSelectorModal.js.map +1 -0
- package/build/ui/icons.d.ts +19 -0
- package/build/ui/icons.js +65 -0
- package/build/ui/icons.js.map +1 -0
- package/build/ui/index.d.ts +10 -0
- package/build/ui/index.js +31 -0
- package/build/ui/index.js.map +1 -0
- package/build/ui/styles.d.ts +5 -0
- package/build/ui/styles.js +973 -0
- package/build/ui/styles.js.map +1 -0
- package/build/ui/theme.d.ts +133 -0
- package/build/ui/theme.js +204 -0
- package/build/ui/theme.js.map +1 -0
- package/build/wallets/external/index.d.ts +4 -0
- package/build/wallets/external/index.js +9 -0
- package/build/wallets/external/index.js.map +1 -0
- package/build/wallets/external/manager.d.ts +152 -0
- package/build/wallets/external/manager.js +586 -0
- package/build/wallets/external/manager.js.map +1 -0
- package/build/wallets/privileged/index.d.ts +5 -0
- package/build/wallets/privileged/index.js +12 -0
- package/build/wallets/privileged/index.js.map +1 -0
- package/build/wallets/privileged/ledger.d.ts +132 -0
- package/build/wallets/privileged/ledger.js +563 -0
- package/build/wallets/privileged/ledger.js.map +1 -0
- package/build/wallets/privileged/manager.d.ts +54 -0
- package/build/wallets/privileged/manager.js +174 -0
- package/build/wallets/privileged/manager.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { EventEmitter } from "./helpers/events";
|
|
2
|
+
import { DataStorage } from "./helpers/storage";
|
|
3
|
+
import { SessionManager, Session } from "./helpers/session";
|
|
4
|
+
import IndexedDB from "./helpers/indexdb";
|
|
5
|
+
import { WalletManifest, Network, WalletFeatures, Logger, NearWalletBase, AbstractWalletConnect, Account } from "./types";
|
|
6
|
+
import { EventMap } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* Connected account with wallet metadata
|
|
9
|
+
*/
|
|
10
|
+
export interface ConnectedAccount extends Account {
|
|
11
|
+
walletId: string;
|
|
12
|
+
walletName: string;
|
|
13
|
+
walletIcon?: string;
|
|
14
|
+
network: Network;
|
|
15
|
+
connectedAt: number;
|
|
16
|
+
}
|
|
17
|
+
interface PersistenceOptions {
|
|
18
|
+
enabled?: boolean;
|
|
19
|
+
storage?: DataStorage;
|
|
20
|
+
storageKey?: string;
|
|
21
|
+
maxAge?: number;
|
|
22
|
+
autoReconnect?: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface NearConnectorOptions {
|
|
25
|
+
providers?: {
|
|
26
|
+
mainnet?: string[];
|
|
27
|
+
testnet?: string[];
|
|
28
|
+
};
|
|
29
|
+
features?: Partial<WalletFeatures>;
|
|
30
|
+
excludedWallets?: string[];
|
|
31
|
+
autoConnect?: boolean;
|
|
32
|
+
network?: Network;
|
|
33
|
+
manifest?: string | {
|
|
34
|
+
wallets: WalletManifest[];
|
|
35
|
+
version: string;
|
|
36
|
+
};
|
|
37
|
+
walletConnect?: Promise<AbstractWalletConnect> | AbstractWalletConnect;
|
|
38
|
+
events?: EventEmitter<EventMap>;
|
|
39
|
+
storage?: DataStorage;
|
|
40
|
+
logger?: Logger;
|
|
41
|
+
/**
|
|
42
|
+
* Session persistence options for auto-reconnect on page reload
|
|
43
|
+
*/
|
|
44
|
+
persistence?: PersistenceOptions;
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated
|
|
47
|
+
* Some wallets allow adding a limited-access key to a contract as soon as the user connects their wallet.
|
|
48
|
+
* This enables the app to sign non-payable transactions without requiring wallet approval each time.
|
|
49
|
+
* However, this approach requires the user to submit an on-chain transaction during the initial connection, which may negatively affect the user experience.
|
|
50
|
+
* A better practice is to add the limited-access key after the user has already begun actively interacting with your application.
|
|
51
|
+
*/
|
|
52
|
+
signIn?: {
|
|
53
|
+
contractId?: string;
|
|
54
|
+
methodNames?: Array<string>;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export declare class NearConnector {
|
|
58
|
+
private storage;
|
|
59
|
+
readonly events: EventEmitter<EventMap>;
|
|
60
|
+
readonly db: IndexedDB;
|
|
61
|
+
readonly session: SessionManager;
|
|
62
|
+
logger?: Logger;
|
|
63
|
+
wallets: NearWalletBase[];
|
|
64
|
+
manifest: {
|
|
65
|
+
wallets: WalletManifest[];
|
|
66
|
+
version: string;
|
|
67
|
+
};
|
|
68
|
+
features: Partial<WalletFeatures>;
|
|
69
|
+
network: Network;
|
|
70
|
+
providers: {
|
|
71
|
+
mainnet?: string[];
|
|
72
|
+
testnet?: string[];
|
|
73
|
+
};
|
|
74
|
+
signInData?: {
|
|
75
|
+
contractId?: string;
|
|
76
|
+
methodNames?: Array<string>;
|
|
77
|
+
};
|
|
78
|
+
walletConnect?: Promise<AbstractWalletConnect> | AbstractWalletConnect;
|
|
79
|
+
excludedWallets: string[];
|
|
80
|
+
autoConnect?: boolean;
|
|
81
|
+
private persistenceEnabled;
|
|
82
|
+
private connectedAccounts;
|
|
83
|
+
private activeAccountId;
|
|
84
|
+
readonly whenManifestLoaded: Promise<void>;
|
|
85
|
+
readonly whenSessionRestored: Promise<Session | null>;
|
|
86
|
+
constructor(options?: NearConnectorOptions);
|
|
87
|
+
get availableWallets(): NearWalletBase[];
|
|
88
|
+
private _handleNearWalletInjected;
|
|
89
|
+
private _loadManifest;
|
|
90
|
+
switchNetwork(network: "mainnet" | "testnet", signInData?: {
|
|
91
|
+
contractId?: string;
|
|
92
|
+
methodNames?: Array<string>;
|
|
93
|
+
}): Promise<void>;
|
|
94
|
+
registerWallet(manifest: WalletManifest): Promise<void>;
|
|
95
|
+
registerDebugWallet(json: string | WalletManifest): Promise<WalletManifest>;
|
|
96
|
+
removeDebugWallet(id: string): Promise<void>;
|
|
97
|
+
selectWallet(): Promise<string>;
|
|
98
|
+
connect(id?: string): Promise<NearWalletBase>;
|
|
99
|
+
disconnect(wallet?: NearWalletBase): Promise<void>;
|
|
100
|
+
getConnectedWallet(): Promise<{
|
|
101
|
+
wallet: NearWalletBase;
|
|
102
|
+
accounts: Account[];
|
|
103
|
+
}>;
|
|
104
|
+
wallet(id?: string | null): Promise<NearWalletBase>;
|
|
105
|
+
on<K extends keyof EventMap>(event: K, callback: (payload: EventMap[K]) => void): void;
|
|
106
|
+
once<K extends keyof EventMap>(event: K, callback: (payload: EventMap[K]) => void): void;
|
|
107
|
+
off<K extends keyof EventMap>(event: K, callback: (payload: EventMap[K]) => void): void;
|
|
108
|
+
removeAllListeners<K extends keyof EventMap>(event?: K): void;
|
|
109
|
+
/**
|
|
110
|
+
* Get all connected accounts across all wallets
|
|
111
|
+
*/
|
|
112
|
+
getConnectedAccounts(): ConnectedAccount[];
|
|
113
|
+
/**
|
|
114
|
+
* Get the currently active account
|
|
115
|
+
*/
|
|
116
|
+
getActiveAccount(): ConnectedAccount | null;
|
|
117
|
+
/**
|
|
118
|
+
* Switch to a different connected account
|
|
119
|
+
*/
|
|
120
|
+
switchAccount(accountId: string): Promise<ConnectedAccount>;
|
|
121
|
+
/**
|
|
122
|
+
* Add a new account by connecting another wallet
|
|
123
|
+
* Returns the newly added accounts
|
|
124
|
+
*/
|
|
125
|
+
addAccount(walletId?: string): Promise<ConnectedAccount[]>;
|
|
126
|
+
/**
|
|
127
|
+
* Remove a connected account
|
|
128
|
+
*/
|
|
129
|
+
removeAccount(accountId: string): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Check if multi-account mode has any accounts
|
|
132
|
+
*/
|
|
133
|
+
hasConnectedAccounts(): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Get accounts for a specific wallet
|
|
136
|
+
*/
|
|
137
|
+
getAccountsForWallet(walletId: string): ConnectedAccount[];
|
|
138
|
+
/**
|
|
139
|
+
* Load multi-account state from storage
|
|
140
|
+
*/
|
|
141
|
+
private loadMultiAccountState;
|
|
142
|
+
/**
|
|
143
|
+
* Save multi-account state to storage
|
|
144
|
+
*/
|
|
145
|
+
private saveMultiAccountState;
|
|
146
|
+
/**
|
|
147
|
+
* Clear all connected accounts
|
|
148
|
+
*/
|
|
149
|
+
clearAllAccounts(): Promise<void>;
|
|
150
|
+
}
|
|
151
|
+
export {};
|
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NearConnector = void 0;
|
|
7
|
+
const events_1 = require("./helpers/events");
|
|
8
|
+
const NearWalletsPopup_1 = require("./popups/NearWalletsPopup");
|
|
9
|
+
const storage_1 = require("./helpers/storage");
|
|
10
|
+
const session_1 = require("./helpers/session");
|
|
11
|
+
const indexdb_1 = __importDefault(require("./helpers/indexdb"));
|
|
12
|
+
const ParentFrameWallet_1 = require("./ParentFrameWallet");
|
|
13
|
+
const InjectedWallet_1 = require("./InjectedWallet");
|
|
14
|
+
const SandboxedWallet_1 = require("./SandboxedWallet");
|
|
15
|
+
const errors_1 = require("./errors");
|
|
16
|
+
const defaultManifests = [
|
|
17
|
+
"https://raw.githubusercontent.com/hot-dao/near-selector/refs/heads/main/repository/manifest.json",
|
|
18
|
+
"https://cdn.jsdelivr.net/gh/azbang/hot-connector/repository/manifest.json",
|
|
19
|
+
];
|
|
20
|
+
const MULTI_ACCOUNT_STORAGE_KEY = "near-connect-accounts";
|
|
21
|
+
class NearConnector {
|
|
22
|
+
storage;
|
|
23
|
+
events;
|
|
24
|
+
db;
|
|
25
|
+
session;
|
|
26
|
+
logger;
|
|
27
|
+
wallets = [];
|
|
28
|
+
manifest = { wallets: [], version: "1.0.0" };
|
|
29
|
+
features = {};
|
|
30
|
+
network = "mainnet";
|
|
31
|
+
providers = { mainnet: [], testnet: [] };
|
|
32
|
+
signInData;
|
|
33
|
+
walletConnect;
|
|
34
|
+
excludedWallets = [];
|
|
35
|
+
autoConnect;
|
|
36
|
+
persistenceEnabled;
|
|
37
|
+
// Multi-account state
|
|
38
|
+
connectedAccounts = [];
|
|
39
|
+
activeAccountId = null;
|
|
40
|
+
whenManifestLoaded;
|
|
41
|
+
whenSessionRestored;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.db = new indexdb_1.default("hot-connector", "wallets");
|
|
44
|
+
this.storage = options?.storage ?? new storage_1.LocalStorage();
|
|
45
|
+
this.events = options?.events ?? new events_1.EventEmitter();
|
|
46
|
+
this.logger = options?.logger;
|
|
47
|
+
this.network = options?.network ?? "mainnet";
|
|
48
|
+
this.walletConnect = options?.walletConnect;
|
|
49
|
+
this.autoConnect = options?.autoConnect ?? true;
|
|
50
|
+
this.providers = options?.providers ?? { mainnet: [], testnet: [] };
|
|
51
|
+
this.excludedWallets = options?.excludedWallets ?? [];
|
|
52
|
+
this.features = options?.features ?? {};
|
|
53
|
+
this.signInData = options?.signIn;
|
|
54
|
+
// Session persistence
|
|
55
|
+
this.persistenceEnabled = options?.persistence?.enabled ?? true;
|
|
56
|
+
this.session = new session_1.SessionManager({
|
|
57
|
+
storage: options?.persistence?.storage ?? this.storage,
|
|
58
|
+
storageKey: options?.persistence?.storageKey,
|
|
59
|
+
maxAge: options?.persistence?.maxAge,
|
|
60
|
+
autoReconnect: options?.persistence?.autoReconnect ?? true,
|
|
61
|
+
onSessionRestored: (session) => {
|
|
62
|
+
this.logger?.log("Session restored", session);
|
|
63
|
+
this.events.emit("session:restored", { session });
|
|
64
|
+
},
|
|
65
|
+
onSessionExpired: (session) => {
|
|
66
|
+
this.logger?.log("Session expired", session);
|
|
67
|
+
this.events.emit("session:expired", { session });
|
|
68
|
+
},
|
|
69
|
+
onSessionCleared: () => {
|
|
70
|
+
this.logger?.log("Session cleared");
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
this.whenManifestLoaded = new Promise(async (resolve) => {
|
|
74
|
+
if (options?.manifest == null || typeof options.manifest === "string") {
|
|
75
|
+
this.manifest = await this._loadManifest(options?.manifest).catch(() => ({ wallets: [], version: "1.0.0" }));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.manifest = options?.manifest ?? { wallets: [], version: "1.0.0" };
|
|
79
|
+
}
|
|
80
|
+
const set = new Set(this.excludedWallets);
|
|
81
|
+
set.delete("hot-wallet"); // always include hot-wallet
|
|
82
|
+
this.manifest.wallets = this.manifest.wallets.filter((wallet) => {
|
|
83
|
+
// Remove wallet with walletConnect permission but no projectId is provided
|
|
84
|
+
if (wallet.permissions.walletConnect && !this.walletConnect)
|
|
85
|
+
return false;
|
|
86
|
+
if (set.has(wallet.id))
|
|
87
|
+
return false; // excluded wallets
|
|
88
|
+
return true;
|
|
89
|
+
});
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
91
|
+
resolve();
|
|
92
|
+
});
|
|
93
|
+
if (typeof window !== "undefined") {
|
|
94
|
+
window.addEventListener("near-wallet-injected", this._handleNearWalletInjected);
|
|
95
|
+
window.dispatchEvent(new Event("near-selector-ready"));
|
|
96
|
+
window.addEventListener("message", async (event) => {
|
|
97
|
+
if (event.data.type === "near-wallet-injected") {
|
|
98
|
+
await this.whenManifestLoaded.catch(() => { });
|
|
99
|
+
this.wallets = this.wallets.filter((wallet) => wallet.manifest.id !== event.data.manifest.id);
|
|
100
|
+
this.wallets.unshift(new ParentFrameWallet_1.ParentFrameWallet(this, event.data.manifest));
|
|
101
|
+
this.events.emit("selector:walletsChanged", {});
|
|
102
|
+
if (this.autoConnect)
|
|
103
|
+
this.connect(event.data.manifest.id);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
this.whenManifestLoaded.then(() => {
|
|
108
|
+
if (typeof window !== "undefined") {
|
|
109
|
+
window.parent.postMessage({ type: "near-selector-ready" }, "*");
|
|
110
|
+
}
|
|
111
|
+
this.manifest.wallets.forEach((wallet) => this.registerWallet(wallet));
|
|
112
|
+
this.storage.get("debug-wallets").then((json) => {
|
|
113
|
+
const debugWallets = JSON.parse(json ?? "[]");
|
|
114
|
+
debugWallets.forEach((wallet) => this.registerDebugWallet(wallet));
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
// Try to restore session after manifest loads
|
|
118
|
+
this.whenSessionRestored = this.whenManifestLoaded.then(async () => {
|
|
119
|
+
if (!this.persistenceEnabled)
|
|
120
|
+
return null;
|
|
121
|
+
// Load multi-account state
|
|
122
|
+
await this.loadMultiAccountState();
|
|
123
|
+
const session = await this.session.tryRestore();
|
|
124
|
+
if (session) {
|
|
125
|
+
// Validate wallet still exists and try to reconnect
|
|
126
|
+
try {
|
|
127
|
+
const wallet = this.wallets.find((w) => w.manifest.id === session.walletId);
|
|
128
|
+
if (wallet) {
|
|
129
|
+
const accounts = await wallet.getAccounts({ network: session.network });
|
|
130
|
+
if (accounts?.length) {
|
|
131
|
+
this.network = session.network;
|
|
132
|
+
await this.storage.set("selected-wallet", session.walletId);
|
|
133
|
+
this.events.emit("wallet:signIn", { wallet, accounts, success: true });
|
|
134
|
+
return session;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Wallet not found or no accounts - clear stale session
|
|
138
|
+
await this.session.clear();
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
this.logger?.log("Failed to restore session", e);
|
|
142
|
+
await this.session.clear();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
get availableWallets() {
|
|
149
|
+
const wallets = this.wallets.filter((wallet) => {
|
|
150
|
+
return Object.entries(this.features).every(([key, value]) => {
|
|
151
|
+
if (value && !wallet.manifest.features?.[key])
|
|
152
|
+
return false;
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
return wallets.filter((wallet) => {
|
|
157
|
+
if (this.network === "testnet" && !wallet.manifest.features?.testnet)
|
|
158
|
+
return false;
|
|
159
|
+
return true;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
_handleNearWalletInjected = (event) => {
|
|
163
|
+
this.wallets = this.wallets.filter((wallet) => wallet.manifest.id !== event.detail.manifest.id);
|
|
164
|
+
this.wallets.unshift(new InjectedWallet_1.InjectedWallet(this, event.detail));
|
|
165
|
+
this.events.emit("selector:walletsChanged", {});
|
|
166
|
+
};
|
|
167
|
+
async _loadManifest(manifestUrl) {
|
|
168
|
+
const manifestEndpoints = manifestUrl ? [manifestUrl] : defaultManifests;
|
|
169
|
+
for (const endpoint of manifestEndpoints) {
|
|
170
|
+
const res = await fetch(endpoint).catch(() => null);
|
|
171
|
+
if (!res || !res.ok)
|
|
172
|
+
continue;
|
|
173
|
+
return await res.json(); // TODO: Validate this
|
|
174
|
+
}
|
|
175
|
+
throw new errors_1.WalletError({
|
|
176
|
+
code: errors_1.ErrorCode.MANIFEST_LOAD_FAILED,
|
|
177
|
+
message: "Failed to load wallet manifest from any endpoint",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async switchNetwork(network, signInData) {
|
|
181
|
+
if (this.network === network)
|
|
182
|
+
return;
|
|
183
|
+
await this.disconnect().catch(() => { });
|
|
184
|
+
if (signInData)
|
|
185
|
+
this.signInData = signInData;
|
|
186
|
+
this.network = network;
|
|
187
|
+
await this.connect();
|
|
188
|
+
}
|
|
189
|
+
async registerWallet(manifest) {
|
|
190
|
+
if (manifest.type !== "sandbox")
|
|
191
|
+
throw new Error("Only sandbox wallets are supported");
|
|
192
|
+
if (this.wallets.find((wallet) => wallet.manifest.id === manifest.id))
|
|
193
|
+
return;
|
|
194
|
+
this.wallets.push(new SandboxedWallet_1.SandboxWallet(this, manifest));
|
|
195
|
+
this.events.emit("selector:walletsChanged", {});
|
|
196
|
+
}
|
|
197
|
+
async registerDebugWallet(json) {
|
|
198
|
+
const manifest = typeof json === "string" ? JSON.parse(json) : json;
|
|
199
|
+
if (manifest.type !== "sandbox")
|
|
200
|
+
throw new Error("Only sandbox wallets type are supported");
|
|
201
|
+
if (!manifest.id)
|
|
202
|
+
throw new Error("Manifest must have an id");
|
|
203
|
+
if (!manifest.name)
|
|
204
|
+
throw new Error("Manifest must have a name");
|
|
205
|
+
if (!manifest.icon)
|
|
206
|
+
throw new Error("Manifest must have an icon");
|
|
207
|
+
if (!manifest.website)
|
|
208
|
+
throw new Error("Manifest must have a website");
|
|
209
|
+
if (!manifest.version)
|
|
210
|
+
throw new Error("Manifest must have a version");
|
|
211
|
+
if (!manifest.executor)
|
|
212
|
+
throw new Error("Manifest must have an executor");
|
|
213
|
+
if (!manifest.features)
|
|
214
|
+
throw new Error("Manifest must have features");
|
|
215
|
+
if (!manifest.permissions)
|
|
216
|
+
throw new Error("Manifest must have permissions");
|
|
217
|
+
if (this.wallets.find((wallet) => wallet.manifest.id === manifest.id))
|
|
218
|
+
throw new Error("Wallet already registered");
|
|
219
|
+
manifest.debug = true;
|
|
220
|
+
this.wallets.unshift(new SandboxedWallet_1.SandboxWallet(this, manifest));
|
|
221
|
+
this.events.emit("selector:walletsChanged", {});
|
|
222
|
+
const debugWallets = this.wallets.filter((wallet) => wallet.manifest.debug).map((wallet) => wallet.manifest);
|
|
223
|
+
this.storage.set("debug-wallets", JSON.stringify(debugWallets));
|
|
224
|
+
return manifest;
|
|
225
|
+
}
|
|
226
|
+
async removeDebugWallet(id) {
|
|
227
|
+
this.wallets = this.wallets.filter((wallet) => wallet.manifest.id !== id);
|
|
228
|
+
const debugWallets = this.wallets.filter((wallet) => wallet.manifest.debug).map((wallet) => wallet.manifest);
|
|
229
|
+
this.storage.set("debug-wallets", JSON.stringify(debugWallets));
|
|
230
|
+
this.events.emit("selector:walletsChanged", {});
|
|
231
|
+
}
|
|
232
|
+
async selectWallet() {
|
|
233
|
+
await this.whenManifestLoaded.catch(() => { });
|
|
234
|
+
return new Promise((resolve, reject) => {
|
|
235
|
+
const popup = new NearWalletsPopup_1.NearWalletsPopup({
|
|
236
|
+
wallets: this.availableWallets.map((wallet) => wallet.manifest),
|
|
237
|
+
onRemoveDebugManifest: async (id) => this.removeDebugWallet(id),
|
|
238
|
+
onAddDebugManifest: async (wallet) => this.registerDebugWallet(wallet),
|
|
239
|
+
onReject: () => {
|
|
240
|
+
const error = new errors_1.UserRejectedError(undefined, "wallet selection");
|
|
241
|
+
reject(error);
|
|
242
|
+
popup.destroy();
|
|
243
|
+
},
|
|
244
|
+
onSelect: (id) => (resolve(id), popup.destroy()),
|
|
245
|
+
});
|
|
246
|
+
popup.create();
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async connect(id) {
|
|
250
|
+
await this.whenManifestLoaded.catch(() => { });
|
|
251
|
+
if (!id)
|
|
252
|
+
id = await this.selectWallet();
|
|
253
|
+
try {
|
|
254
|
+
const wallet = await this.wallet(id);
|
|
255
|
+
this.logger?.log(`Wallet available to connect`, wallet);
|
|
256
|
+
await this.storage.set("selected-wallet", id);
|
|
257
|
+
this.logger?.log(`Set preferred wallet, try to signIn`, id);
|
|
258
|
+
const accounts = await wallet.signIn({
|
|
259
|
+
contractId: this.signInData?.contractId,
|
|
260
|
+
methodNames: this.signInData?.methodNames,
|
|
261
|
+
network: this.network,
|
|
262
|
+
});
|
|
263
|
+
if (!accounts?.length) {
|
|
264
|
+
throw new errors_1.WalletError({
|
|
265
|
+
code: errors_1.ErrorCode.NO_ACCOUNTS,
|
|
266
|
+
message: "Wallet returned no accounts after sign in",
|
|
267
|
+
walletId: id,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
this.logger?.log(`Signed in to wallet`, id, accounts);
|
|
271
|
+
// Save session for persistence
|
|
272
|
+
if (this.persistenceEnabled) {
|
|
273
|
+
await this.session.save({
|
|
274
|
+
walletId: id,
|
|
275
|
+
accounts,
|
|
276
|
+
network: this.network,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
this.events.emit("wallet:signIn", { wallet, accounts, success: true });
|
|
280
|
+
return wallet;
|
|
281
|
+
}
|
|
282
|
+
catch (e) {
|
|
283
|
+
const walletError = (0, errors_1.wrapError)(e, id);
|
|
284
|
+
this.logger?.log("Failed to connect to wallet", walletError);
|
|
285
|
+
this.events.emit("wallet:error", { error: walletError, walletId: id, action: "connect" });
|
|
286
|
+
throw walletError;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async disconnect(wallet) {
|
|
290
|
+
try {
|
|
291
|
+
if (!wallet)
|
|
292
|
+
wallet = await this.wallet();
|
|
293
|
+
await wallet.signOut({ network: this.network });
|
|
294
|
+
await this.storage.remove("selected-wallet");
|
|
295
|
+
// Clear persisted session
|
|
296
|
+
if (this.persistenceEnabled) {
|
|
297
|
+
await this.session.clear();
|
|
298
|
+
}
|
|
299
|
+
this.events.emit("wallet:signOut", { success: true });
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
const walletError = (0, errors_1.wrapError)(e, wallet?.manifest.id);
|
|
303
|
+
this.logger?.log("Failed to disconnect wallet", walletError);
|
|
304
|
+
this.events.emit("wallet:error", { error: walletError, walletId: wallet?.manifest.id, action: "disconnect" });
|
|
305
|
+
throw walletError;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async getConnectedWallet() {
|
|
309
|
+
await this.whenManifestLoaded.catch(() => { });
|
|
310
|
+
const id = await this.storage.get("selected-wallet");
|
|
311
|
+
const wallet = this.wallets.find((wallet) => wallet.manifest.id === id);
|
|
312
|
+
if (!wallet) {
|
|
313
|
+
throw new errors_1.WalletError({
|
|
314
|
+
code: errors_1.ErrorCode.NO_ACTIVE_SESSION,
|
|
315
|
+
message: "No wallet currently selected",
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
const accounts = await wallet.getAccounts();
|
|
319
|
+
if (!accounts?.length) {
|
|
320
|
+
throw new errors_1.WalletError({
|
|
321
|
+
code: errors_1.ErrorCode.NO_ACCOUNTS,
|
|
322
|
+
message: "No accounts found in connected wallet",
|
|
323
|
+
walletId: id ?? undefined,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return { wallet, accounts };
|
|
327
|
+
}
|
|
328
|
+
async wallet(id) {
|
|
329
|
+
await this.whenManifestLoaded.catch(() => { });
|
|
330
|
+
if (!id) {
|
|
331
|
+
return this.getConnectedWallet()
|
|
332
|
+
.then(({ wallet }) => wallet)
|
|
333
|
+
.catch(async (e) => {
|
|
334
|
+
await this.storage.remove("selected-wallet");
|
|
335
|
+
if (e instanceof errors_1.WalletError)
|
|
336
|
+
throw e;
|
|
337
|
+
throw new errors_1.WalletError({
|
|
338
|
+
code: errors_1.ErrorCode.NO_ACCOUNTS,
|
|
339
|
+
message: "No accounts found",
|
|
340
|
+
originalError: e instanceof Error ? e : undefined,
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
const wallet = this.wallets.find((wallet) => wallet.manifest.id === id);
|
|
345
|
+
if (!wallet) {
|
|
346
|
+
// Try to find install URL from manifest
|
|
347
|
+
const manifestWallet = this.manifest.wallets.find((w) => w.id === id);
|
|
348
|
+
throw new errors_1.WalletNotFoundError(id, manifestWallet?.website);
|
|
349
|
+
}
|
|
350
|
+
return wallet;
|
|
351
|
+
}
|
|
352
|
+
on(event, callback) {
|
|
353
|
+
this.events.on(event, callback);
|
|
354
|
+
}
|
|
355
|
+
once(event, callback) {
|
|
356
|
+
this.events.once(event, callback);
|
|
357
|
+
}
|
|
358
|
+
off(event, callback) {
|
|
359
|
+
this.events.off(event, callback);
|
|
360
|
+
}
|
|
361
|
+
removeAllListeners(event) {
|
|
362
|
+
this.events.removeAllListeners(event);
|
|
363
|
+
}
|
|
364
|
+
// ===== Multi-Account Management =====
|
|
365
|
+
/**
|
|
366
|
+
* Get all connected accounts across all wallets
|
|
367
|
+
*/
|
|
368
|
+
getConnectedAccounts() {
|
|
369
|
+
return [...this.connectedAccounts];
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Get the currently active account
|
|
373
|
+
*/
|
|
374
|
+
getActiveAccount() {
|
|
375
|
+
if (!this.activeAccountId)
|
|
376
|
+
return null;
|
|
377
|
+
return this.connectedAccounts.find((a) => a.accountId === this.activeAccountId) ?? null;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Switch to a different connected account
|
|
381
|
+
*/
|
|
382
|
+
async switchAccount(accountId) {
|
|
383
|
+
const account = this.connectedAccounts.find((a) => a.accountId === accountId);
|
|
384
|
+
if (!account) {
|
|
385
|
+
throw new errors_1.WalletError({
|
|
386
|
+
code: errors_1.ErrorCode.NO_ACCOUNTS,
|
|
387
|
+
message: `Account ${accountId} is not connected`,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
// Switch network if needed
|
|
391
|
+
if (account.network !== this.network) {
|
|
392
|
+
this.network = account.network;
|
|
393
|
+
}
|
|
394
|
+
// Update selected wallet
|
|
395
|
+
await this.storage.set("selected-wallet", account.walletId);
|
|
396
|
+
this.activeAccountId = accountId;
|
|
397
|
+
await this.saveMultiAccountState();
|
|
398
|
+
this.events.emit("account:switched", { account, previousAccountId: this.activeAccountId });
|
|
399
|
+
return account;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Add a new account by connecting another wallet
|
|
403
|
+
* Returns the newly added accounts
|
|
404
|
+
*/
|
|
405
|
+
async addAccount(walletId) {
|
|
406
|
+
await this.whenManifestLoaded.catch(() => { });
|
|
407
|
+
if (!walletId) {
|
|
408
|
+
walletId = await this.selectWallet();
|
|
409
|
+
}
|
|
410
|
+
const wallet = await this.wallet(walletId);
|
|
411
|
+
const accounts = await wallet.signIn({
|
|
412
|
+
contractId: this.signInData?.contractId,
|
|
413
|
+
methodNames: this.signInData?.methodNames,
|
|
414
|
+
network: this.network,
|
|
415
|
+
});
|
|
416
|
+
if (!accounts?.length) {
|
|
417
|
+
throw new errors_1.WalletError({
|
|
418
|
+
code: errors_1.ErrorCode.NO_ACCOUNTS,
|
|
419
|
+
message: "Wallet returned no accounts after sign in",
|
|
420
|
+
walletId,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
const newAccounts = [];
|
|
424
|
+
for (const account of accounts) {
|
|
425
|
+
// Check if account already connected
|
|
426
|
+
const existing = this.connectedAccounts.find((a) => a.accountId === account.accountId);
|
|
427
|
+
if (existing) {
|
|
428
|
+
this.logger?.log(`Account ${account.accountId} already connected`);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const connectedAccount = {
|
|
432
|
+
...account,
|
|
433
|
+
walletId,
|
|
434
|
+
walletName: wallet.manifest.name,
|
|
435
|
+
walletIcon: wallet.manifest.icon,
|
|
436
|
+
network: this.network,
|
|
437
|
+
connectedAt: Date.now(),
|
|
438
|
+
};
|
|
439
|
+
this.connectedAccounts.push(connectedAccount);
|
|
440
|
+
newAccounts.push(connectedAccount);
|
|
441
|
+
}
|
|
442
|
+
// Set first new account as active if no active account
|
|
443
|
+
if (!this.activeAccountId && newAccounts.length > 0) {
|
|
444
|
+
this.activeAccountId = newAccounts[0].accountId;
|
|
445
|
+
await this.storage.set("selected-wallet", walletId);
|
|
446
|
+
}
|
|
447
|
+
await this.saveMultiAccountState();
|
|
448
|
+
this.events.emit("account:added", { accounts: newAccounts });
|
|
449
|
+
return newAccounts;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Remove a connected account
|
|
453
|
+
*/
|
|
454
|
+
async removeAccount(accountId) {
|
|
455
|
+
const accountIndex = this.connectedAccounts.findIndex((a) => a.accountId === accountId);
|
|
456
|
+
if (accountIndex === -1) {
|
|
457
|
+
throw new errors_1.WalletError({
|
|
458
|
+
code: errors_1.ErrorCode.NO_ACCOUNTS,
|
|
459
|
+
message: `Account ${accountId} is not connected`,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
const account = this.connectedAccounts[accountIndex];
|
|
463
|
+
this.connectedAccounts.splice(accountIndex, 1);
|
|
464
|
+
// If removing active account, switch to another
|
|
465
|
+
if (this.activeAccountId === accountId) {
|
|
466
|
+
if (this.connectedAccounts.length > 0) {
|
|
467
|
+
this.activeAccountId = this.connectedAccounts[0].accountId;
|
|
468
|
+
await this.storage.set("selected-wallet", this.connectedAccounts[0].walletId);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
this.activeAccountId = null;
|
|
472
|
+
await this.storage.remove("selected-wallet");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
await this.saveMultiAccountState();
|
|
476
|
+
this.events.emit("account:removed", { account });
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Check if multi-account mode has any accounts
|
|
480
|
+
*/
|
|
481
|
+
hasConnectedAccounts() {
|
|
482
|
+
return this.connectedAccounts.length > 0;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Get accounts for a specific wallet
|
|
486
|
+
*/
|
|
487
|
+
getAccountsForWallet(walletId) {
|
|
488
|
+
return this.connectedAccounts.filter((a) => a.walletId === walletId);
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Load multi-account state from storage
|
|
492
|
+
*/
|
|
493
|
+
async loadMultiAccountState() {
|
|
494
|
+
try {
|
|
495
|
+
const raw = await this.storage.get(MULTI_ACCOUNT_STORAGE_KEY);
|
|
496
|
+
if (!raw)
|
|
497
|
+
return;
|
|
498
|
+
const state = JSON.parse(raw);
|
|
499
|
+
this.connectedAccounts = state.accounts ?? [];
|
|
500
|
+
this.activeAccountId = state.activeAccountId;
|
|
501
|
+
}
|
|
502
|
+
catch (e) {
|
|
503
|
+
this.logger?.log("Failed to load multi-account state", e);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Save multi-account state to storage
|
|
508
|
+
*/
|
|
509
|
+
async saveMultiAccountState() {
|
|
510
|
+
try {
|
|
511
|
+
const state = {
|
|
512
|
+
accounts: this.connectedAccounts,
|
|
513
|
+
activeAccountId: this.activeAccountId,
|
|
514
|
+
};
|
|
515
|
+
await this.storage.set(MULTI_ACCOUNT_STORAGE_KEY, JSON.stringify(state));
|
|
516
|
+
}
|
|
517
|
+
catch (e) {
|
|
518
|
+
this.logger?.log("Failed to save multi-account state", e);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Clear all connected accounts
|
|
523
|
+
*/
|
|
524
|
+
async clearAllAccounts() {
|
|
525
|
+
this.connectedAccounts = [];
|
|
526
|
+
this.activeAccountId = null;
|
|
527
|
+
await this.storage.remove(MULTI_ACCOUNT_STORAGE_KEY);
|
|
528
|
+
await this.storage.remove("selected-wallet");
|
|
529
|
+
if (this.persistenceEnabled) {
|
|
530
|
+
await this.session.clear();
|
|
531
|
+
}
|
|
532
|
+
this.events.emit("account:cleared", {});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
exports.NearConnector = NearConnector;
|
|
536
|
+
//# sourceMappingURL=NearConnector.js.map
|