@tomo-inc/wallet-adaptor-base 0.0.1
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/CHANGELOG.md +3 -0
- package/README.md +60 -0
- package/dist/index.cjs +2485 -0
- package/dist/index.d.cts +184 -0
- package/dist/index.d.ts +184 -0
- package/dist/index.js +2471 -0
- package/package.json +31 -0
- package/project.json +67 -0
- package/src/chains/type.ts +3 -0
- package/src/chains/utils.ts +20 -0
- package/src/index.ts +93 -0
- package/src/svg.d.ts +4 -0
- package/src/type.ts +99 -0
- package/src/utils/browsers.ts +39 -0
- package/src/utils/chainId.ts +27 -0
- package/src/utils/isMobile.ts +22 -0
- package/src/utils/utils.ts +14 -0
- package/src/wallet-api/balance.ts +96 -0
- package/src/wallet-api/chain.ts +59 -0
- package/src/wallet-api/connect.ts +65 -0
- package/src/wallet-api/index.ts +5 -0
- package/src/wallet-api/sign-in.ts +103 -0
- package/src/wallet-api/sign-message.ts +60 -0
- package/src/wallets/Wallet.ts +37 -0
- package/src/wallets/default/backpackWallet/backpackWallet.svg +1 -0
- package/src/wallets/default/backpackWallet/backpackWallet.ts +23 -0
- package/src/wallets/default/berasigWallet/berasigWallet.svg +1 -0
- package/src/wallets/default/berasigWallet/berasigWallet.ts +22 -0
- package/src/wallets/default/binanceWallet/binanceWallet.svg +1 -0
- package/src/wallets/default/binanceWallet/binanceWallet.ts +23 -0
- package/src/wallets/default/bitgetWallet/bitgetWallet.svg +4 -0
- package/src/wallets/default/bitgetWallet/bitgetWallet.ts +30 -0
- package/src/wallets/default/bitskiWallet/bitskiWallet.svg +1 -0
- package/src/wallets/default/bitskiWallet/bitskiWallet.ts +17 -0
- package/src/wallets/default/bitverseWallet/bitverseWallet.svg +1 -0
- package/src/wallets/default/bitverseWallet/bitverseWallet.ts +17 -0
- package/src/wallets/default/bloomWallet/bloomWallet.svg +1 -0
- package/src/wallets/default/bloomWallet/bloomWallet.ts +15 -0
- package/src/wallets/default/braveWallet/braveWallet.svg +1 -0
- package/src/wallets/default/braveWallet/braveWallet.ts +20 -0
- package/src/wallets/default/bybitWallet/bybitWallet.svg +1 -0
- package/src/wallets/default/bybitWallet/bybitWallet.ts +23 -0
- package/src/wallets/default/clvWallet/clvWallet.svg +1 -0
- package/src/wallets/default/clvWallet/clvWallet.ts +21 -0
- package/src/wallets/default/coin98Wallet/coin98Wallet.svg +1 -0
- package/src/wallets/default/coin98Wallet/coin98Wallet.ts +25 -0
- package/src/wallets/default/coinbaseWallet/coinbaseWallet.svg +4 -0
- package/src/wallets/default/coinbaseWallet/coinbaseWallet.ts +25 -0
- package/src/wallets/default/compassWallet/compassWallet.svg +1 -0
- package/src/wallets/default/compassWallet/compassWallet.ts +19 -0
- package/src/wallets/default/coreWallet/coreWallet.svg +7 -0
- package/src/wallets/default/coreWallet/coreWallet.ts +24 -0
- package/src/wallets/default/ctrlWallet/ctrlWallet.svg +13 -0
- package/src/wallets/default/ctrlWallet/ctrlWallet.ts +24 -0
- package/src/wallets/default/dawnWallet/dawnWallet.svg +23 -0
- package/src/wallets/default/dawnWallet/dawnWallet.ts +16 -0
- package/src/wallets/default/desigWallet/desigWallet.svg +1 -0
- package/src/wallets/default/desigWallet/desigWallet.ts +21 -0
- package/src/wallets/default/enkryptWallet/enkryptWallet.svg +10 -0
- package/src/wallets/default/enkryptWallet/enkryptWallet.ts +24 -0
- package/src/wallets/default/foxWallet/foxWallet.svg +6 -0
- package/src/wallets/default/foxWallet/foxWallet.ts +19 -0
- package/src/wallets/default/frameWallet/frameWallet.svg +1 -0
- package/src/wallets/default/frameWallet/frameWallet.ts +17 -0
- package/src/wallets/default/gateWallet/gateWallet.svg +1 -0
- package/src/wallets/default/gateWallet/gateWallet.ts +24 -0
- package/src/wallets/default/imTokenWallet/imTokenWallet.svg +10 -0
- package/src/wallets/default/imTokenWallet/imTokenWallet.ts +18 -0
- package/src/wallets/default/injectedWallet/injectedWallet.svg +1 -0
- package/src/wallets/default/injectedWallet/injectedWallet.ts +12 -0
- package/src/wallets/default/iopayWallet/iopayWallet.svg +1 -0
- package/src/wallets/default/iopayWallet/iopayWallet.ts +18 -0
- package/src/wallets/default/kaiaWallet/kaiaWallet.svg +16 -0
- package/src/wallets/default/kaiaWallet/kaiaWallet.ts +22 -0
- package/src/wallets/default/kaikasWallet/kaikasWallet.svg +1 -0
- package/src/wallets/default/kaikasWallet/kaikasWallet.ts +22 -0
- package/src/wallets/default/krakenWallet/krakenWallet.svg +1 -0
- package/src/wallets/default/krakenWallet/krakenWallet.ts +17 -0
- package/src/wallets/default/kresusWallet/kresusWallet.svg +1 -0
- package/src/wallets/default/kresusWallet/kresusWallet.ts +17 -0
- package/src/wallets/default/ledgerWallet/ledgerWallet.svg +1 -0
- package/src/wallets/default/ledgerWallet/ledgerWallet.ts +23 -0
- package/src/wallets/default/magicEdenWallet/magicEden.svg +1 -0
- package/src/wallets/default/magicEdenWallet/magicEdenWallet.ts +25 -0
- package/src/wallets/default/mathWallet/icon.ts +2 -0
- package/src/wallets/default/mathWallet/mathWallet.ts +24 -0
- package/src/wallets/default/metaMaskWallet/icon.ts +2 -0
- package/src/wallets/default/metaMaskWallet/metaMaskWallet.svg +32 -0
- package/src/wallets/default/metaMaskWallet/metaMaskWallet.ts +88 -0
- package/src/wallets/default/myDogeWallet/icon.ts +2 -0
- package/src/wallets/default/myDogeWallet/mydogeWallet.svg +190 -0
- package/src/wallets/default/myDogeWallet/mydogeWallet.ts +20 -0
- package/src/wallets/default/nestWallet/nestWallet.svg +1 -0
- package/src/wallets/default/nestWallet/nestWallet.ts +17 -0
- package/src/wallets/default/novaWallet/novaWallet.svg +19 -0
- package/src/wallets/default/novaWallet/novaWallet.ts +30 -0
- package/src/wallets/default/oktoWallet/oktoWallet.svg +1 -0
- package/src/wallets/default/oktoWallet/oktoWallet.ts +18 -0
- package/src/wallets/default/okxWallet/okxWallet.svg +1 -0
- package/src/wallets/default/okxWallet/okxWallet.ts +32 -0
- package/src/wallets/default/omniWallet/omniWallet.svg +5 -0
- package/src/wallets/default/omniWallet/omniWallet.ts +18 -0
- package/src/wallets/default/oneInchWallet/oneInchWallet.svg +1 -0
- package/src/wallets/default/oneInchWallet/oneInchWallet.ts +18 -0
- package/src/wallets/default/oneKeyWallet/oneKeyWallet.svg +1 -0
- package/src/wallets/default/oneKeyWallet/oneKeyWallet.ts +25 -0
- package/src/wallets/default/paraSwapWallet/paraSwapWallet.svg +1 -0
- package/src/wallets/default/paraSwapWallet/paraswapWallet.ts +17 -0
- package/src/wallets/default/phantomWallet/phantomWallet.svg +1 -0
- package/src/wallets/default/phantomWallet/phantomWallet.ts +30 -0
- package/src/wallets/default/rabbyWallet/rabbyWallet.svg +1 -0
- package/src/wallets/default/rabbyWallet/rabbyWallet.ts +18 -0
- package/src/wallets/default/rainbowWallet/rainbowWallet.svg +54 -0
- package/src/wallets/default/rainbowWallet/rainbowWallet.ts +24 -0
- package/src/wallets/default/ramperWallet/ramperWallet.svg +1 -0
- package/src/wallets/default/ramperWallet/ramperWallet.ts +19 -0
- package/src/wallets/default/readyWallet/readyWallet.svg +4 -0
- package/src/wallets/default/readyWallet/readyWallet.ts +18 -0
- package/src/wallets/default/roninWallet/roninWallet.svg +1 -0
- package/src/wallets/default/roninWallet/roninWallet.ts +25 -0
- package/src/wallets/default/safeWallet/safeWallet.svg +6 -0
- package/src/wallets/default/safeWallet/safeWallet.ts +18 -0
- package/src/wallets/default/safeheronWallet/safeheronWallet.svg +1 -0
- package/src/wallets/default/safeheronWallet/safeheronWallet.ts +17 -0
- package/src/wallets/default/safepalWallet/safepalWallet.svg +1 -0
- package/src/wallets/default/safepalWallet/safepalWallet.ts +24 -0
- package/src/wallets/default/seifWallet/seifWallet.svg +12 -0
- package/src/wallets/default/seifWallet/seifWallet.ts +18 -0
- package/src/wallets/default/solflareWallet/icon.ts +2 -0
- package/src/wallets/default/solflareWallet/solflareWallet.ts +23 -0
- package/src/wallets/default/subWallet/subWallet.svg +1 -0
- package/src/wallets/default/subWallet/subWallet.ts +25 -0
- package/src/wallets/default/tahoWallet/tahoWallet.svg +1 -0
- package/src/wallets/default/tahoWallet/tahoWallet.ts +19 -0
- package/src/wallets/default/talismanWallet/talismanWallet.svg +1 -0
- package/src/wallets/default/talismanWallet/talismanWallet.ts +19 -0
- package/src/wallets/default/tokenPocketWallet/tokenPocketWallet.svg +1 -0
- package/src/wallets/default/tokenPocketWallet/tokenPocketWallet.ts +26 -0
- package/src/wallets/default/tokenaryWallet/tokenaryWallet.svg +1 -0
- package/src/wallets/default/tokenaryWallet/tokenaryWallet.ts +19 -0
- package/src/wallets/default/trezorWallet/icon.ts +2 -0
- package/src/wallets/default/trezorWallet/trezorWallet.ts +19 -0
- package/src/wallets/default/trustWallet/trustWallet.svg +1 -0
- package/src/wallets/default/trustWallet/trustWallet.ts +25 -0
- package/src/wallets/default/uniswapWallet/uniswapWallet.svg +1 -0
- package/src/wallets/default/uniswapWallet/uniswapWallet.ts +17 -0
- package/src/wallets/default/universalProfilesWallet/icon.ts +2 -0
- package/src/wallets/default/universalProfilesWallet/universalProfilesWallet.svg +1353 -0
- package/src/wallets/default/universalProfilesWallet/universalProfilesWallet.ts +22 -0
- package/src/wallets/default/valoraWallet/valoraWallet.svg +1 -0
- package/src/wallets/default/valoraWallet/valoraWallet.ts +18 -0
- package/src/wallets/default/walletConnectWallet/walletConnectWallet.svg +4 -0
- package/src/wallets/default/walletConnectWallet/walletConnectWallet.ts +18 -0
- package/src/wallets/default/wigwamWallet/wigwamWallet.svg +9 -0
- package/src/wallets/default/wigwamWallet/wigwamWallet.ts +20 -0
- package/src/wallets/default/xPortalWallet/xPortalWallet.svg +6 -0
- package/src/wallets/default/xPortalWallet/xPortalWallet.ts +22 -0
- package/src/wallets/default/zealWallet/zealWallet.svg +1 -0
- package/src/wallets/default/zealWallet/zealWallet.ts +25 -0
- package/src/wallets/default/zerionWallet/zerionWallet.svg +1 -0
- package/src/wallets/default/zerionWallet/zerionWallet.ts +24 -0
- package/src/wallets/default/zilPayWallet/zilPayWallet.svg +17 -0
- package/src/wallets/default/zilPayWallet/zilPayWallet.ts +21 -0
- package/src/wallets/detector.ts +136 -0
- package/src/wallets/index.ts +210 -0
- package/src/wallets/providers/WalletConnectProvider.ts +346 -0
- package/src/wallets/providers/WalletConnectSolanaProvider.ts +351 -0
- package/src/wallets/wallet-eip6963.ts +70 -0
- package/src/wallets/wallet-standard.ts +77 -0
- package/src/wallets/wallet-walletconnect.ts +202 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WalletConnect Provider
|
|
3
|
+
*
|
|
4
|
+
* Implements standard Ethereum provider interface using WalletConnect protocol
|
|
5
|
+
* Compatible with EIP-1193 provider interface
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { NamespaceConfig, SessionInfo } from "@tomo-inc/wallet-connect-protocol";
|
|
9
|
+
import { WalletConnectClient } from "@tomo-inc/wallet-connect-protocol";
|
|
10
|
+
|
|
11
|
+
export interface WalletConnectProviderConfig {
|
|
12
|
+
projectId: string;
|
|
13
|
+
metadata: {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
url: string;
|
|
17
|
+
icons: string[];
|
|
18
|
+
};
|
|
19
|
+
chains?: string[];
|
|
20
|
+
optionalChains?: string[];
|
|
21
|
+
methods?: string[];
|
|
22
|
+
events?: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const defaultNamespaces: Record<string, NamespaceConfig> = {
|
|
26
|
+
eip155: {
|
|
27
|
+
chains: ["eip155:1"],
|
|
28
|
+
methods: [
|
|
29
|
+
"eth_sendTransaction",
|
|
30
|
+
"eth_signTransaction",
|
|
31
|
+
"eth_sign",
|
|
32
|
+
"personal_sign",
|
|
33
|
+
"eth_signTypedData",
|
|
34
|
+
"eth_signTypedData_v4",
|
|
35
|
+
"eth_chainId",
|
|
36
|
+
],
|
|
37
|
+
events: ["chainChanged", "accountsChanged"],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export class WalletConnectProvider {
|
|
42
|
+
private client: WalletConnectClient;
|
|
43
|
+
private session: SessionInfo | null = null;
|
|
44
|
+
private accounts: string[] = [];
|
|
45
|
+
private chainId: string = "0x1"; // Default to Ethereum mainnet
|
|
46
|
+
private eventListeners: Map<string, Set<(...args: any[]) => void>> = new Map();
|
|
47
|
+
private sessionMap: Map<string, SessionInfo> = new Map();
|
|
48
|
+
public uri: string | null = null;
|
|
49
|
+
public qrCode: string | null = null;
|
|
50
|
+
|
|
51
|
+
constructor(config: WalletConnectProviderConfig) {
|
|
52
|
+
this.client = new WalletConnectClient({
|
|
53
|
+
projectId: config.projectId,
|
|
54
|
+
metadata: config.metadata,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
this.setupClientListeners();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Setup WalletConnect client event listeners
|
|
62
|
+
*/
|
|
63
|
+
private setupClientListeners() {
|
|
64
|
+
this.client.on("session_proposal", () => {
|
|
65
|
+
// Session connected
|
|
66
|
+
const sessions = this.client.getActiveSessions();
|
|
67
|
+
if (sessions.length > 0) {
|
|
68
|
+
this.session = sessions[0];
|
|
69
|
+
this.updateAccountsFromSession();
|
|
70
|
+
this.emit("connect", { chainId: this.chainId });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
this.client.on("session_delete", () => {
|
|
75
|
+
this.session = null;
|
|
76
|
+
this.accounts = [];
|
|
77
|
+
this.emit("disconnect");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
this.client.on("session_update", () => {
|
|
81
|
+
this.updateAccountsFromSession();
|
|
82
|
+
this.emit("accountsChanged", this.accounts);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
this.client.on("display_uri", (e: { uri: string }) => {
|
|
86
|
+
this.uri = e.uri;
|
|
87
|
+
this.emit("uri_changed", e);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Update accounts from current session
|
|
93
|
+
*/
|
|
94
|
+
private updateAccountsFromSession() {
|
|
95
|
+
if (!this.session) return;
|
|
96
|
+
|
|
97
|
+
const allAccounts = Object.values(this.session.namespaces).flatMap((ns: any) => ns.accounts || []);
|
|
98
|
+
|
|
99
|
+
this.accounts = allAccounts.map((account: string) => {
|
|
100
|
+
const parts = account.split(":");
|
|
101
|
+
return parts[2] || account;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (allAccounts.length > 0) {
|
|
105
|
+
const chainIdPart = allAccounts[0].split(":")[1];
|
|
106
|
+
this.chainId = "0x" + parseInt(chainIdPart).toString(16);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public async getUri(config: { requiredNamespaces: Record<string, NamespaceConfig> }): Promise<string> {
|
|
111
|
+
// Create connection and get URI
|
|
112
|
+
const uri = await this.client.connect({
|
|
113
|
+
requiredNamespaces: config.requiredNamespaces || defaultNamespaces,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.uri = uri;
|
|
117
|
+
return uri;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Initialize the provider
|
|
121
|
+
*/
|
|
122
|
+
async initialize(): Promise<void> {
|
|
123
|
+
await this.client.initialize();
|
|
124
|
+
// get all active sessions and store in sessionMap
|
|
125
|
+
const sessions = this.client.getActiveSessions();
|
|
126
|
+
sessions.forEach((session: SessionInfo) => {
|
|
127
|
+
this.sessionMap.set(session.topic, session);
|
|
128
|
+
});
|
|
129
|
+
this.uri = await this.getUri({
|
|
130
|
+
requiredNamespaces: defaultNamespaces,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Connect to wallet via WalletConnect
|
|
136
|
+
* Returns URI and QR code immediately for display
|
|
137
|
+
* Connection completion is notified via 'connect' event
|
|
138
|
+
*/
|
|
139
|
+
async connect(): Promise<string[] | undefined> {
|
|
140
|
+
// Start waiting for connection in background (don't await)
|
|
141
|
+
// Connection will emit 'connect' event when established
|
|
142
|
+
const connected = await this.waitForConnection().catch((error) => {
|
|
143
|
+
console.error("WalletConnect connection failed:", error);
|
|
144
|
+
this.emit("error", error);
|
|
145
|
+
return false;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (connected) {
|
|
149
|
+
return this.accounts;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Wait for WalletConnect session to be established
|
|
155
|
+
*/
|
|
156
|
+
private waitForConnection(): Promise<boolean | undefined> {
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const timeout = setTimeout(() => {
|
|
159
|
+
reject(new Error("Connection timeout"));
|
|
160
|
+
}, 300000); // 5 minutes
|
|
161
|
+
|
|
162
|
+
const checkConnection = () => {
|
|
163
|
+
const sessions = this.client.getActiveSessions();
|
|
164
|
+
const newSessions = sessions.filter((session: SessionInfo) => !this.sessionMap.has(session.topic));
|
|
165
|
+
if (newSessions.length > 0) {
|
|
166
|
+
clearTimeout(timeout);
|
|
167
|
+
this.session = newSessions[0];
|
|
168
|
+
this.updateAccountsFromSession();
|
|
169
|
+
this.emit("connect", { chainId: this.chainId });
|
|
170
|
+
resolve(true);
|
|
171
|
+
} else {
|
|
172
|
+
setTimeout(checkConnection, 500);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
checkConnection();
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Disconnect from wallet
|
|
182
|
+
*/
|
|
183
|
+
async disconnect(): Promise<void> {
|
|
184
|
+
if (this.session) {
|
|
185
|
+
await this.client.disconnectSession(this.session.topic);
|
|
186
|
+
this.session = null;
|
|
187
|
+
this.accounts = [];
|
|
188
|
+
this.emit("disconnect");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Send JSON-RPC request (EIP-1193)
|
|
194
|
+
*/
|
|
195
|
+
async request(args: { method: string; params?: any[] }): Promise<any> {
|
|
196
|
+
// eth_requestAccounts
|
|
197
|
+
if (args.method === "eth_requestAccounts") {
|
|
198
|
+
if (this.accounts.length > 0) {
|
|
199
|
+
return this.accounts;
|
|
200
|
+
}
|
|
201
|
+
if (this.uri === null) {
|
|
202
|
+
this.uri = await this.getUri({
|
|
203
|
+
requiredNamespaces: defaultNamespaces,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return this.connect();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!this.session) {
|
|
210
|
+
throw new Error("Not connected. Please call connect() first.");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (args.method === "eth_chainId") {
|
|
214
|
+
return this.chainId || "0x1"; // default to Ethereum mainnet
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const result = await this.client.sendRequest({
|
|
219
|
+
topic: this.session.topic,
|
|
220
|
+
chainId: `eip155:${parseInt(this.chainId, 16)}`,
|
|
221
|
+
request: {
|
|
222
|
+
method: args.method,
|
|
223
|
+
params: args.params || [],
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return result;
|
|
228
|
+
} catch (error: any) {
|
|
229
|
+
throw new Error(error.message || "Request failed");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get current accounts
|
|
235
|
+
*/
|
|
236
|
+
async getAccounts(): Promise<string[]> {
|
|
237
|
+
if (!this.session) {
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
240
|
+
return this.accounts;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get current chain ID
|
|
245
|
+
*/
|
|
246
|
+
async getChainId(): Promise<string> {
|
|
247
|
+
return this.chainId;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Check if connected
|
|
252
|
+
*/
|
|
253
|
+
isConnected(): boolean {
|
|
254
|
+
return this.session !== null && this.accounts.length > 0;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get active session
|
|
259
|
+
*/
|
|
260
|
+
getSession(): SessionInfo | null {
|
|
261
|
+
return this.session;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Event emitter - on
|
|
266
|
+
*/
|
|
267
|
+
on(event: string, listener: (...args: any[]) => void): void {
|
|
268
|
+
if (!this.eventListeners.has(event)) {
|
|
269
|
+
this.eventListeners.set(event, new Set());
|
|
270
|
+
}
|
|
271
|
+
this.eventListeners.get(event)!.add(listener);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Event emitter - off
|
|
276
|
+
*/
|
|
277
|
+
off(event: string, listener: (...args: any[]) => void): void {
|
|
278
|
+
const listeners = this.eventListeners.get(event);
|
|
279
|
+
if (listeners) {
|
|
280
|
+
listeners.delete(listener);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Event emitter - emit
|
|
286
|
+
*/
|
|
287
|
+
private emit(event: string, data?: any): void {
|
|
288
|
+
const listeners = this.eventListeners.get(event);
|
|
289
|
+
if (listeners) {
|
|
290
|
+
listeners.forEach((listener) => {
|
|
291
|
+
try {
|
|
292
|
+
listener(data);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error(`Error in ${event} listener:`, error);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Helper: Sign message
|
|
302
|
+
*/
|
|
303
|
+
async signMessage(message: string): Promise<string> {
|
|
304
|
+
if (!this.accounts[0]) {
|
|
305
|
+
throw new Error("No account connected");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return this.request({
|
|
309
|
+
method: "personal_sign",
|
|
310
|
+
params: [message, this.accounts[0]],
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Helper: Sign typed data
|
|
316
|
+
*/
|
|
317
|
+
async signTypedData(typedData: any): Promise<string> {
|
|
318
|
+
if (!this.accounts[0]) {
|
|
319
|
+
throw new Error("No account connected");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return this.request({
|
|
323
|
+
method: "eth_signTypedData_v4",
|
|
324
|
+
params: [this.accounts[0], JSON.stringify(typedData)],
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Helper: Send transaction
|
|
330
|
+
*/
|
|
331
|
+
async sendTransaction(transaction: any): Promise<string> {
|
|
332
|
+
return this.request({
|
|
333
|
+
method: "eth_sendTransaction",
|
|
334
|
+
params: [transaction],
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Helper: Switch chain
|
|
340
|
+
*/
|
|
341
|
+
async switchChain(_chainId: string): Promise<void> {
|
|
342
|
+
// WalletConnect doesn't support switching chains dynamically
|
|
343
|
+
// User needs to reconnect with different chain
|
|
344
|
+
throw new Error("Please reconnect to switch chains");
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WalletConnect Solana Provider
|
|
3
|
+
*
|
|
4
|
+
* Implements Solana provider interface using WalletConnect protocol
|
|
5
|
+
* Compatible with WalletConnect Solana JSON-RPC methods
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { NamespaceConfig, SessionInfo } from "@tomo-inc/wallet-connect-protocol";
|
|
9
|
+
import { WalletConnectClient } from "@tomo-inc/wallet-connect-protocol";
|
|
10
|
+
|
|
11
|
+
export interface WalletConnectSolanaProviderConfig {
|
|
12
|
+
projectId: string;
|
|
13
|
+
metadata: {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
url: string;
|
|
17
|
+
icons: string[];
|
|
18
|
+
};
|
|
19
|
+
chains?: string[];
|
|
20
|
+
methods?: string[];
|
|
21
|
+
events?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const defaultSolanaNamespaces: Record<string, NamespaceConfig> = {
|
|
25
|
+
solana: {
|
|
26
|
+
chains: ["solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"], // Solana mainnet
|
|
27
|
+
methods: [
|
|
28
|
+
"solana_getAccounts",
|
|
29
|
+
"solana_requestAccounts",
|
|
30
|
+
"solana_signMessage",
|
|
31
|
+
"solana_signTransaction",
|
|
32
|
+
"solana_signAllTransactions",
|
|
33
|
+
"solana_signAndSendTransaction",
|
|
34
|
+
],
|
|
35
|
+
events: ["accountsChanged"],
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class WalletConnectSolanaProvider {
|
|
40
|
+
private client: WalletConnectClient;
|
|
41
|
+
private session: SessionInfo | null = null;
|
|
42
|
+
private accounts: string[] = [];
|
|
43
|
+
private chainId: string = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; // Default to Solana mainnet
|
|
44
|
+
private eventListeners: Map<string, Set<(...args: any[]) => void>> = new Map();
|
|
45
|
+
private sessionMap: Map<string, SessionInfo> = new Map();
|
|
46
|
+
public uri: string | null = null;
|
|
47
|
+
public qrCode: string | null = null;
|
|
48
|
+
|
|
49
|
+
constructor(config: WalletConnectSolanaProviderConfig) {
|
|
50
|
+
this.client = new WalletConnectClient({
|
|
51
|
+
projectId: config.projectId,
|
|
52
|
+
metadata: config.metadata,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.setupClientListeners();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Setup WalletConnect client event listeners
|
|
60
|
+
*/
|
|
61
|
+
private setupClientListeners() {
|
|
62
|
+
this.client.on("session_proposal", () => {
|
|
63
|
+
// Session connected
|
|
64
|
+
const sessions = this.client.getActiveSessions();
|
|
65
|
+
if (sessions.length > 0) {
|
|
66
|
+
this.session = sessions[0];
|
|
67
|
+
this.updateAccountsFromSession();
|
|
68
|
+
this.emit("connect", { chainId: this.chainId });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
this.client.on("session_delete", () => {
|
|
73
|
+
this.session = null;
|
|
74
|
+
this.accounts = [];
|
|
75
|
+
this.emit("disconnect");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.client.on("session_update", () => {
|
|
79
|
+
this.updateAccountsFromSession();
|
|
80
|
+
this.emit("accountsChanged", this.accounts);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.client.on("display_uri", (e: { uri: string }) => {
|
|
84
|
+
this.uri = e.uri;
|
|
85
|
+
this.emit("uri_changed", e);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update accounts from current session
|
|
91
|
+
*/
|
|
92
|
+
private updateAccountsFromSession() {
|
|
93
|
+
if (!this.session) return;
|
|
94
|
+
|
|
95
|
+
const allAccounts = Object.values(this.session.namespaces).flatMap((ns: any) => ns.accounts || []);
|
|
96
|
+
|
|
97
|
+
this.accounts = allAccounts.map((account: string) => {
|
|
98
|
+
const parts = account.split(":");
|
|
99
|
+
// Solana account format: solana:chainId:pubkey
|
|
100
|
+
return parts[2] || account;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (allAccounts.length > 0) {
|
|
104
|
+
const chainIdPart = allAccounts[0].split(":")[1];
|
|
105
|
+
this.chainId = `solana:${chainIdPart}`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public async getUri(config: { requiredNamespaces: Record<string, NamespaceConfig> }): Promise<string> {
|
|
110
|
+
// Create connection and get URI
|
|
111
|
+
const uri = await this.client.connect({
|
|
112
|
+
requiredNamespaces: config.requiredNamespaces || defaultSolanaNamespaces,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
this.uri = uri;
|
|
116
|
+
return uri;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Initialize the provider
|
|
121
|
+
*/
|
|
122
|
+
async initialize(): Promise<void> {
|
|
123
|
+
await this.client.initialize();
|
|
124
|
+
// get all active sessions and store in sessionMap
|
|
125
|
+
const sessions = this.client.getActiveSessions();
|
|
126
|
+
sessions.forEach((session: SessionInfo) => {
|
|
127
|
+
this.sessionMap.set(session.topic, session);
|
|
128
|
+
});
|
|
129
|
+
this.uri = await this.getUri({
|
|
130
|
+
requiredNamespaces: defaultSolanaNamespaces,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Connect to wallet via WalletConnect
|
|
136
|
+
* Returns accounts when connected
|
|
137
|
+
*/
|
|
138
|
+
async connect(): Promise<string[] | undefined> {
|
|
139
|
+
// Start waiting for connection in background (don't await)
|
|
140
|
+
// Connection will emit 'connect' event when established
|
|
141
|
+
const connected = await this.waitForConnection().catch((error) => {
|
|
142
|
+
console.error("WalletConnect Solana connection failed:", error);
|
|
143
|
+
this.emit("error", error);
|
|
144
|
+
return false;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (connected) {
|
|
148
|
+
return this.accounts;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Wait for WalletConnect session to be established
|
|
154
|
+
*/
|
|
155
|
+
private waitForConnection(): Promise<boolean | undefined> {
|
|
156
|
+
return new Promise((resolve, reject) => {
|
|
157
|
+
const timeout = setTimeout(() => {
|
|
158
|
+
reject(new Error("Connection timeout"));
|
|
159
|
+
}, 300000); // 5 minutes
|
|
160
|
+
|
|
161
|
+
const checkConnection = () => {
|
|
162
|
+
const sessions = this.client.getActiveSessions();
|
|
163
|
+
const newSessions = sessions.filter((session: SessionInfo) => !this.sessionMap.has(session.topic));
|
|
164
|
+
if (newSessions.length > 0) {
|
|
165
|
+
clearTimeout(timeout);
|
|
166
|
+
this.session = newSessions[0];
|
|
167
|
+
this.updateAccountsFromSession();
|
|
168
|
+
this.emit("connect", { chainId: this.chainId });
|
|
169
|
+
resolve(true);
|
|
170
|
+
} else {
|
|
171
|
+
setTimeout(checkConnection, 500);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
checkConnection();
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Disconnect from wallet
|
|
181
|
+
*/
|
|
182
|
+
async disconnect(): Promise<void> {
|
|
183
|
+
if (this.session) {
|
|
184
|
+
await this.client.disconnectSession(this.session.topic);
|
|
185
|
+
this.session = null;
|
|
186
|
+
this.accounts = [];
|
|
187
|
+
this.emit("disconnect");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Send JSON-RPC request (Solana WalletConnect methods)
|
|
193
|
+
*/
|
|
194
|
+
async request(args: { method: string; params?: any }): Promise<any> {
|
|
195
|
+
// solana_requestAccounts
|
|
196
|
+
if (args.method === "solana_requestAccounts") {
|
|
197
|
+
if (this.accounts.length > 0) {
|
|
198
|
+
return this.accounts.map((account) => ({ pubkey: account }));
|
|
199
|
+
}
|
|
200
|
+
if (this.uri === null) {
|
|
201
|
+
this.uri = await this.getUri({
|
|
202
|
+
requiredNamespaces: defaultSolanaNamespaces,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const accounts = await this.connect();
|
|
206
|
+
return accounts?.map((account) => ({ pubkey: account })) || [];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// solana_getAccounts
|
|
210
|
+
if (args.method === "solana_getAccounts") {
|
|
211
|
+
return this.accounts.map((account) => ({ pubkey: account }));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!this.session) {
|
|
215
|
+
throw new Error("Not connected. Please call connect() first.");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const result = await this.client.sendRequest({
|
|
220
|
+
topic: this.session.topic,
|
|
221
|
+
chainId: this.chainId,
|
|
222
|
+
request: {
|
|
223
|
+
method: args.method,
|
|
224
|
+
params: args.params || [],
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return result;
|
|
229
|
+
} catch (error: any) {
|
|
230
|
+
throw new Error(error.message || "Request failed");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get current accounts
|
|
236
|
+
*/
|
|
237
|
+
async getAccounts(): Promise<string[]> {
|
|
238
|
+
if (!this.session) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
return this.accounts;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get current chain ID
|
|
246
|
+
*/
|
|
247
|
+
async getChainId(): Promise<string> {
|
|
248
|
+
return this.chainId;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Check if connected
|
|
253
|
+
*/
|
|
254
|
+
isConnected(): boolean {
|
|
255
|
+
return this.session !== null && this.accounts.length > 0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get active session
|
|
260
|
+
*/
|
|
261
|
+
getSession(): SessionInfo | null {
|
|
262
|
+
return this.session;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Event emitter - on
|
|
267
|
+
*/
|
|
268
|
+
on(event: string, listener: (...args: any[]) => void): void {
|
|
269
|
+
if (!this.eventListeners.has(event)) {
|
|
270
|
+
this.eventListeners.set(event, new Set());
|
|
271
|
+
}
|
|
272
|
+
this.eventListeners.get(event)!.add(listener);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Event emitter - off
|
|
277
|
+
*/
|
|
278
|
+
off(event: string, listener: (...args: any[]) => void): void {
|
|
279
|
+
const listeners = this.eventListeners.get(event);
|
|
280
|
+
if (listeners) {
|
|
281
|
+
listeners.delete(listener);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Event emitter - emit
|
|
287
|
+
*/
|
|
288
|
+
private emit(event: string, data?: any): void {
|
|
289
|
+
const listeners = this.eventListeners.get(event);
|
|
290
|
+
if (listeners) {
|
|
291
|
+
listeners.forEach((listener) => {
|
|
292
|
+
try {
|
|
293
|
+
listener(data);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.error(`Error in ${event} listener:`, error);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Helper: Sign message
|
|
303
|
+
*/
|
|
304
|
+
async signMessage(message: string, pubkey: string): Promise<string> {
|
|
305
|
+
const result = await this.request({
|
|
306
|
+
method: "solana_signMessage",
|
|
307
|
+
params: {
|
|
308
|
+
message,
|
|
309
|
+
pubkey,
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
return result.signature;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Helper: Sign transaction
|
|
317
|
+
*/
|
|
318
|
+
async signTransaction(transaction: string): Promise<{ signature: string; transaction?: string }> {
|
|
319
|
+
return this.request({
|
|
320
|
+
method: "solana_signTransaction",
|
|
321
|
+
params: {
|
|
322
|
+
transaction,
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Helper: Sign all transactions
|
|
329
|
+
*/
|
|
330
|
+
async signAllTransactions(transactions: string[]): Promise<{ transactions: string[] }> {
|
|
331
|
+
return this.request({
|
|
332
|
+
method: "solana_signAllTransactions",
|
|
333
|
+
params: {
|
|
334
|
+
transactions,
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Helper: Sign and send transaction
|
|
341
|
+
*/
|
|
342
|
+
async signAndSendTransaction(transaction: string, sendOptions?: any): Promise<{ signature: string }> {
|
|
343
|
+
return this.request({
|
|
344
|
+
method: "solana_signAndSendTransaction",
|
|
345
|
+
params: {
|
|
346
|
+
transaction,
|
|
347
|
+
sendOptions,
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|