@flarenetwork/multichain-wallet-connector 0.0.1-rc.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 +690 -0
- package/dist/core/index.d.mts +2 -0
- package/dist/core/index.mjs +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +5 -0
- package/dist/multichain-C5wKiJke.mjs +2863 -0
- package/dist/multichain-C5wKiJke.mjs.map +1 -0
- package/dist/react/index.d.mts +3 -0
- package/dist/react/index.mjs +5 -0
- package/dist/use-wallets-BfbbQmDw.d.mts +311 -0
- package/dist/use-wallets-BfbbQmDw.d.mts.map +1 -0
- package/dist/use-wallets-DXRPNM7H.mjs +885 -0
- package/dist/use-wallets-DXRPNM7H.mjs.map +1 -0
- package/dist/utils-C64Z4fWz.d.mts +974 -0
- package/dist/utils-C64Z4fWz.d.mts.map +1 -0
- package/package.json +131 -0
|
@@ -0,0 +1,2863 @@
|
|
|
1
|
+
import { createPublicClient, createWalletClient, custom, defineChain, getAddress, getTypesForEIP712Domain, hashDomain, hashStruct, http, serializeSignature, serializeTransaction } from "viem";
|
|
2
|
+
import { flare, flareTestnet, mainnet, sepolia, songbird } from "viem/chains";
|
|
3
|
+
import { DisconnectedDevice, DisconnectedDeviceDuringOperation, LockedDeviceError, TransportInterfaceNotAvailable, TransportOpenUserCancelled, UserRefusedOnDevice } from "@ledgerhq/errors";
|
|
4
|
+
import { Client, RippledError, encode } from "xrpl";
|
|
5
|
+
import Eth from "@ledgerhq/hw-app-eth";
|
|
6
|
+
import * as secp256k1 from "@noble/secp256k1";
|
|
7
|
+
import { publicKeyToAddress, toAccount } from "viem/accounts";
|
|
8
|
+
import { bytesToHex } from "viem/utils";
|
|
9
|
+
import { FlareApp } from "@zondax/ledger-flare";
|
|
10
|
+
import Xrp from "@ledgerhq/hw-app-xrp";
|
|
11
|
+
import TransportWebHID from "@ledgerhq/hw-transport-webhid";
|
|
12
|
+
import { MetaMaskSDK } from "@metamask/sdk";
|
|
13
|
+
import { createAppKit } from "@reown/appkit";
|
|
14
|
+
import UniversalProvider from "@walletconnect/universal-provider";
|
|
15
|
+
import { Xumm } from "xumm";
|
|
16
|
+
import { createStore } from "@xstate/store";
|
|
17
|
+
|
|
18
|
+
//#region src/core/shared/type-utils.ts
|
|
19
|
+
var TypeUtils = class {
|
|
20
|
+
static isError(value) {
|
|
21
|
+
return value instanceof Error;
|
|
22
|
+
}
|
|
23
|
+
static isObject(value) {
|
|
24
|
+
return typeof value === "object" && value !== null;
|
|
25
|
+
}
|
|
26
|
+
static isString(value) {
|
|
27
|
+
return typeof value === "string";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/core/utils/utils.ts
|
|
33
|
+
/**
|
|
34
|
+
* Shortens an address for display (e.g., "0x1234...abcd").
|
|
35
|
+
* @param address - The full address string
|
|
36
|
+
* @param prefixLength - Number of characters to show at start (default: 6)
|
|
37
|
+
* @param suffixLength - Number of characters to show at end (default: 4)
|
|
38
|
+
*/
|
|
39
|
+
function shortenAddress(address, prefixLength = 6, suffixLength = 4) {
|
|
40
|
+
const minLength = prefixLength + suffixLength + 3;
|
|
41
|
+
if (address.length <= minLength) return address;
|
|
42
|
+
return `${address.slice(0, prefixLength)}...${address.slice(-suffixLength)}`;
|
|
43
|
+
}
|
|
44
|
+
/** Type-safe `Object.keys()` that preserves key types. */
|
|
45
|
+
function objectKeys(obj) {
|
|
46
|
+
return Object.keys(obj);
|
|
47
|
+
}
|
|
48
|
+
/** Type-safe `Object.values()` that preserves value types. */
|
|
49
|
+
function objectValues(obj) {
|
|
50
|
+
return Object.values(obj);
|
|
51
|
+
}
|
|
52
|
+
/** Type-safe `Object.entries()` that preserves key and value types. */
|
|
53
|
+
function objectEntries(obj) {
|
|
54
|
+
return Object.entries(obj);
|
|
55
|
+
}
|
|
56
|
+
/** Type-safe `Object.fromEntries()` that preserves key and value types. */
|
|
57
|
+
function objectFromEntries(entries) {
|
|
58
|
+
return Object.fromEntries(entries);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/core/chains/chain.constants.ts
|
|
63
|
+
const CAIP2_NAMESPACES = {
|
|
64
|
+
eip155: "eip155",
|
|
65
|
+
xrpl: "xrpl",
|
|
66
|
+
bip122: "bip122"
|
|
67
|
+
};
|
|
68
|
+
const CAIP2 = {
|
|
69
|
+
[CAIP2_NAMESPACES.eip155]: {
|
|
70
|
+
"1": "Ethereum Mainnet",
|
|
71
|
+
"11155111": "Ethereum Sepolia",
|
|
72
|
+
"14": "Flare Mainnet",
|
|
73
|
+
"16": "Flare Testnet Coston",
|
|
74
|
+
"114": "Flare Testnet Coston2",
|
|
75
|
+
"19": "Songbird Canary-Network"
|
|
76
|
+
},
|
|
77
|
+
[CAIP2_NAMESPACES.xrpl]: {
|
|
78
|
+
"0": "XRP Ledger Mainnet",
|
|
79
|
+
"1": "XRP Ledger Testnet"
|
|
80
|
+
},
|
|
81
|
+
[CAIP2_NAMESPACES.bip122]: { "1a91e3dace36e2be3bf030a65679fe821": "Dogecoin Mainnet" }
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/core/chains/chain.utils.ts
|
|
86
|
+
var ChainGuards = class {
|
|
87
|
+
static isRpcError(error) {
|
|
88
|
+
return TypeUtils.isObject(error) && Object.hasOwn(error, "code");
|
|
89
|
+
}
|
|
90
|
+
static isEvmCaip2(caip2) {
|
|
91
|
+
return caip2.startsWith("eip155:");
|
|
92
|
+
}
|
|
93
|
+
static isXrplCaip2(caip2) {
|
|
94
|
+
return caip2.startsWith("xrpl:");
|
|
95
|
+
}
|
|
96
|
+
static isEvmChain(chain) {
|
|
97
|
+
return chain.isEvm;
|
|
98
|
+
}
|
|
99
|
+
static isXrplChain(chain) {
|
|
100
|
+
return chain.caip2.startsWith("xrpl:");
|
|
101
|
+
}
|
|
102
|
+
static {
|
|
103
|
+
this.isEvmNamespace = (namespace) => namespace === "eip155";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function getEvmChainIdFromReference(reference) {
|
|
107
|
+
const referenceAsNumber = Number.parseInt(reference);
|
|
108
|
+
return {
|
|
109
|
+
id: referenceAsNumber.toString(),
|
|
110
|
+
hex: `0x${referenceAsNumber.toString(16)}`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function getChainName(namespace, reference) {
|
|
114
|
+
return CAIP2[namespace][reference];
|
|
115
|
+
}
|
|
116
|
+
function parseCaip2(caip2) {
|
|
117
|
+
const [namespace, reference] = caip2.split(":");
|
|
118
|
+
return {
|
|
119
|
+
namespace,
|
|
120
|
+
reference
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function parseCaip10(account) {
|
|
124
|
+
const [namespace, reference, address] = account.split(":");
|
|
125
|
+
return {
|
|
126
|
+
namespace,
|
|
127
|
+
reference,
|
|
128
|
+
address
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function createChainConfig(config) {
|
|
132
|
+
return objectFromEntries(objectEntries(config).map(([caip2, chain]) => {
|
|
133
|
+
const { namespace, reference } = parseCaip2(caip2);
|
|
134
|
+
const name = getChainName(namespace, reference);
|
|
135
|
+
if (ChainGuards.isEvmNamespace(namespace)) return [caip2, {
|
|
136
|
+
...chain,
|
|
137
|
+
namespace,
|
|
138
|
+
caip2,
|
|
139
|
+
name,
|
|
140
|
+
isEvm: true,
|
|
141
|
+
evmChain: getEvmChainIdFromReference(reference)
|
|
142
|
+
}];
|
|
143
|
+
return [caip2, {
|
|
144
|
+
...chain,
|
|
145
|
+
namespace,
|
|
146
|
+
caip2,
|
|
147
|
+
isEvm: false,
|
|
148
|
+
name
|
|
149
|
+
}];
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/core/chains/viem/custom-chains.ts
|
|
155
|
+
const flareCoston = defineChain({
|
|
156
|
+
id: 16,
|
|
157
|
+
name: "Flare Testnet Coston",
|
|
158
|
+
nativeCurrency: {
|
|
159
|
+
decimals: 18,
|
|
160
|
+
name: "Coston Flare",
|
|
161
|
+
symbol: "CFLR"
|
|
162
|
+
},
|
|
163
|
+
rpcUrls: { default: { http: ["https://coston-api.flare.network/ext/C/rpc"] } },
|
|
164
|
+
blockExplorers: { default: {
|
|
165
|
+
name: "Coston Explorer",
|
|
166
|
+
url: "https://coston-explorer.flare.network",
|
|
167
|
+
apiUrl: "https://coston-explorer.flare.network/api"
|
|
168
|
+
} },
|
|
169
|
+
testnet: true
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/core/chains/chain-registry.ts
|
|
174
|
+
function defineChainRegistry(registry) {
|
|
175
|
+
return registry;
|
|
176
|
+
}
|
|
177
|
+
const chainRegistry = defineChainRegistry({
|
|
178
|
+
caip2Names: CAIP2,
|
|
179
|
+
chains: {
|
|
180
|
+
"eip155:1": {
|
|
181
|
+
testnet: false,
|
|
182
|
+
nativeCurrency: {
|
|
183
|
+
name: "Ethereum",
|
|
184
|
+
symbol: "ETH",
|
|
185
|
+
decimals: 18
|
|
186
|
+
},
|
|
187
|
+
rpcUrls: ["https://mainnet.infura.io/v3/"],
|
|
188
|
+
bip44Part: 60,
|
|
189
|
+
blockExplorerUrls: ["https://etherscan.io"]
|
|
190
|
+
},
|
|
191
|
+
"eip155:11155111": {
|
|
192
|
+
testnet: true,
|
|
193
|
+
nativeCurrency: {
|
|
194
|
+
name: "Ethereum",
|
|
195
|
+
symbol: "ETH",
|
|
196
|
+
decimals: 18
|
|
197
|
+
},
|
|
198
|
+
bip44Part: 60,
|
|
199
|
+
rpcUrls: ["https://sepolia.infura.io/v3/"],
|
|
200
|
+
blockExplorerUrls: ["https://sepolia.etherscan.io"]
|
|
201
|
+
},
|
|
202
|
+
"eip155:14": {
|
|
203
|
+
testnet: false,
|
|
204
|
+
nativeCurrency: {
|
|
205
|
+
name: "Flare",
|
|
206
|
+
symbol: "FLR",
|
|
207
|
+
decimals: 18
|
|
208
|
+
},
|
|
209
|
+
ledgerHrp: "flare",
|
|
210
|
+
bip44Part: 60,
|
|
211
|
+
rpcUrls: ["https://flare-api.flare.network/ext/C/rpc"],
|
|
212
|
+
blockExplorerUrls: ["https://flare-explorer.flare.network"]
|
|
213
|
+
},
|
|
214
|
+
"eip155:16": {
|
|
215
|
+
testnet: true,
|
|
216
|
+
nativeCurrency: {
|
|
217
|
+
name: "Coston Spark",
|
|
218
|
+
symbol: "CFLR",
|
|
219
|
+
decimals: 18
|
|
220
|
+
},
|
|
221
|
+
rpcUrls: ["https://coston-api.flare.network/ext/C/rpc"],
|
|
222
|
+
ledgerHrp: "coston",
|
|
223
|
+
bip44Part: 60,
|
|
224
|
+
blockExplorerUrls: ["https://coston-explorer.flare.network"]
|
|
225
|
+
},
|
|
226
|
+
"eip155:114": {
|
|
227
|
+
testnet: true,
|
|
228
|
+
nativeCurrency: {
|
|
229
|
+
name: "Coston2 Spark",
|
|
230
|
+
symbol: "C2FLR",
|
|
231
|
+
decimals: 18
|
|
232
|
+
},
|
|
233
|
+
ledgerHrp: "costwo",
|
|
234
|
+
bip44Part: 60,
|
|
235
|
+
rpcUrls: ["https://coston2-api.flare.network/ext/C/rpc"],
|
|
236
|
+
blockExplorerUrls: ["https://coston2-explorer.flare.network"]
|
|
237
|
+
},
|
|
238
|
+
"eip155:19": {
|
|
239
|
+
testnet: false,
|
|
240
|
+
nativeCurrency: {
|
|
241
|
+
name: "Songbird",
|
|
242
|
+
symbol: "SGB",
|
|
243
|
+
decimals: 18
|
|
244
|
+
},
|
|
245
|
+
ledgerHrp: "songbird",
|
|
246
|
+
bip44Part: 60,
|
|
247
|
+
rpcUrls: ["https://songbird-api.flare.network/ext/C/rpc"],
|
|
248
|
+
blockExplorerUrls: ["https://songbird-explorer.flare.network"]
|
|
249
|
+
},
|
|
250
|
+
"xrpl:0": {
|
|
251
|
+
testnet: false,
|
|
252
|
+
nativeCurrency: {
|
|
253
|
+
name: "XRP",
|
|
254
|
+
symbol: "XRP",
|
|
255
|
+
decimals: 15
|
|
256
|
+
},
|
|
257
|
+
bip44Part: 144,
|
|
258
|
+
rpcUrls: ["wss://xrplcluster.com"],
|
|
259
|
+
blockExplorerUrls: ["https://xrpscan.com"]
|
|
260
|
+
},
|
|
261
|
+
"xrpl:1": {
|
|
262
|
+
testnet: true,
|
|
263
|
+
nativeCurrency: {
|
|
264
|
+
name: "XRP",
|
|
265
|
+
symbol: "XRP",
|
|
266
|
+
decimals: 15
|
|
267
|
+
},
|
|
268
|
+
bip44Part: 144,
|
|
269
|
+
rpcUrls: ["wss://s.altnet.rippletest.net:51233"],
|
|
270
|
+
blockExplorerUrls: ["https://testnet.xrpscan.com"]
|
|
271
|
+
},
|
|
272
|
+
"bip122:1a91e3dace36e2be3bf030a65679fe821": {
|
|
273
|
+
testnet: false,
|
|
274
|
+
nativeCurrency: {
|
|
275
|
+
name: "Dogecoin",
|
|
276
|
+
symbol: "DOGE",
|
|
277
|
+
decimals: 8
|
|
278
|
+
},
|
|
279
|
+
bip44Part: 3,
|
|
280
|
+
rpcUrls: ["https://dogecoin-rpc.com"],
|
|
281
|
+
blockExplorerUrls: ["https://dogechain.info"]
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
viem: { chains: {
|
|
285
|
+
"eip155:1": mainnet,
|
|
286
|
+
"eip155:11155111": sepolia,
|
|
287
|
+
"eip155:14": flare,
|
|
288
|
+
"eip155:16": flareCoston,
|
|
289
|
+
"eip155:114": flareTestnet,
|
|
290
|
+
"eip155:19": songbird
|
|
291
|
+
} }
|
|
292
|
+
});
|
|
293
|
+
const chainConfig = createChainConfig(chainRegistry.chains);
|
|
294
|
+
const evmCaip2s = objectKeys(chainConfig).filter(ChainGuards.isEvmCaip2);
|
|
295
|
+
const evmChains = objectValues(chainConfig).filter(ChainGuards.isEvmChain);
|
|
296
|
+
const isCaip2 = (caip2) => {
|
|
297
|
+
if (!caip2) return false;
|
|
298
|
+
return Object.hasOwn(chainConfig, caip2);
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
//#endregion
|
|
302
|
+
//#region src/core/config/config-resolver.ts
|
|
303
|
+
var ConfigResolver = class ConfigResolver {
|
|
304
|
+
constructor(options) {
|
|
305
|
+
this.globalOverrides = new Map(objectEntries(options.chainOverrides ?? {}));
|
|
306
|
+
this.walletOverrides = /* @__PURE__ */ new Map();
|
|
307
|
+
this.walletOptions = /* @__PURE__ */ new Map();
|
|
308
|
+
for (const [walletType, walletConfig] of objectEntries(options.wallets)) {
|
|
309
|
+
if (!walletConfig) continue;
|
|
310
|
+
this.walletOptions.set(walletType, walletConfig);
|
|
311
|
+
if (walletConfig.chainOverrides) this.walletOverrides.set(walletType, new Map(objectEntries(walletConfig.chainOverrides)));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
getWalletOptions(walletType) {
|
|
315
|
+
return this.walletOptions.get(walletType);
|
|
316
|
+
}
|
|
317
|
+
resolveForWallet(walletType) {
|
|
318
|
+
const resolvedChains = {};
|
|
319
|
+
for (const [caip2, defaultConfig] of objectEntries(chainConfig)) resolvedChains[caip2] = this.resolveChain(caip2, defaultConfig, walletType);
|
|
320
|
+
return ConfigResolver.createResolvedConfig(walletType, resolvedChains);
|
|
321
|
+
}
|
|
322
|
+
resolveChain(caip2, defaultConfig, walletType) {
|
|
323
|
+
const globalOverride = this.globalOverrides.get(caip2);
|
|
324
|
+
const walletOverride = this.walletOverrides.get(walletType)?.get(caip2);
|
|
325
|
+
if (!globalOverride && !walletOverride) return defaultConfig;
|
|
326
|
+
let rpcUrls = defaultConfig.rpcUrls;
|
|
327
|
+
let blockExplorerUrls = defaultConfig.blockExplorerUrls;
|
|
328
|
+
let rpcOverride;
|
|
329
|
+
let explorerOverride;
|
|
330
|
+
if (globalOverride) {
|
|
331
|
+
if (globalOverride.rpcUrls?.length) {
|
|
332
|
+
rpcOverride = {
|
|
333
|
+
source: "global",
|
|
334
|
+
default: defaultConfig.rpcUrls[0]
|
|
335
|
+
};
|
|
336
|
+
rpcUrls = globalOverride.rpcUrls;
|
|
337
|
+
}
|
|
338
|
+
if (globalOverride.blockExplorerUrls?.length) {
|
|
339
|
+
explorerOverride = {
|
|
340
|
+
source: "global",
|
|
341
|
+
default: defaultConfig.blockExplorerUrls[0]
|
|
342
|
+
};
|
|
343
|
+
blockExplorerUrls = globalOverride.blockExplorerUrls;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (walletOverride) {
|
|
347
|
+
if (walletOverride.rpcUrls?.length) {
|
|
348
|
+
rpcOverride = {
|
|
349
|
+
source: "wallet",
|
|
350
|
+
default: rpcOverride?.default ?? defaultConfig.rpcUrls[0]
|
|
351
|
+
};
|
|
352
|
+
rpcUrls = walletOverride.rpcUrls;
|
|
353
|
+
}
|
|
354
|
+
if (walletOverride.blockExplorerUrls?.length) {
|
|
355
|
+
explorerOverride = {
|
|
356
|
+
source: "wallet",
|
|
357
|
+
default: explorerOverride?.default ?? defaultConfig.blockExplorerUrls[0]
|
|
358
|
+
};
|
|
359
|
+
blockExplorerUrls = walletOverride.blockExplorerUrls;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const hasOverride = rpcOverride || explorerOverride;
|
|
363
|
+
return Object.freeze({
|
|
364
|
+
...defaultConfig,
|
|
365
|
+
rpcUrls,
|
|
366
|
+
blockExplorerUrls,
|
|
367
|
+
_override: hasOverride ? {
|
|
368
|
+
rpcUrls: rpcOverride,
|
|
369
|
+
blockExplorerUrls: explorerOverride
|
|
370
|
+
} : void 0
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
static createResolvedConfig(walletType, chains) {
|
|
374
|
+
const evmHexMap = /* @__PURE__ */ new Map();
|
|
375
|
+
for (const chain of objectValues(chains)) if (ChainGuards.isEvmChain(chain)) evmHexMap.set(chain.evmChain.hex, chain.caip2);
|
|
376
|
+
return Object.freeze({
|
|
377
|
+
walletType,
|
|
378
|
+
chains,
|
|
379
|
+
evmHexMap,
|
|
380
|
+
getChain(caip2) {
|
|
381
|
+
return chains[caip2];
|
|
382
|
+
},
|
|
383
|
+
getEvmChain(caip2) {
|
|
384
|
+
return chains[caip2];
|
|
385
|
+
},
|
|
386
|
+
getXrplChain(caip2) {
|
|
387
|
+
return chains[caip2];
|
|
388
|
+
},
|
|
389
|
+
getRpcUrl(caip2) {
|
|
390
|
+
return chains[caip2].rpcUrls[0];
|
|
391
|
+
},
|
|
392
|
+
getSupportedChains() {
|
|
393
|
+
return objectKeys(chains);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/core/errors/error-codes.ts
|
|
401
|
+
/**
|
|
402
|
+
* Error codes for all connectors.
|
|
403
|
+
* Numbers as keys = duplicates impossible.
|
|
404
|
+
*
|
|
405
|
+
* Ranges:
|
|
406
|
+
* - 4000-4099: Common (all connectors)
|
|
407
|
+
* - 4100-4199: MetaMask
|
|
408
|
+
* - 4200-4299: Ledger
|
|
409
|
+
* - 4300-4399: WalletConnect
|
|
410
|
+
* - 4400-4499: Xaman
|
|
411
|
+
* - 4500-4599: D'CENT
|
|
412
|
+
*/
|
|
413
|
+
const errorCodeMap = {
|
|
414
|
+
4001: "COMMON_USER_REJECTED",
|
|
415
|
+
4002: "COMMON_NOT_CONNECTED",
|
|
416
|
+
4003: "COMMON_CHAIN_NOT_CONFIGURED",
|
|
417
|
+
4004: "COMMON_CHAIN_NOT_SUPPORTED",
|
|
418
|
+
4005: "COMMON_FEATURE_NOT_SUPPORTED",
|
|
419
|
+
4006: "COMMON_CONNECTION_FAILED",
|
|
420
|
+
4007: "COMMON_UNKNOWN_CHAIN_ID",
|
|
421
|
+
4008: "COMMON_NO_ACCOUNTS",
|
|
422
|
+
4009: "COMMON_PROVIDER_ERROR",
|
|
423
|
+
4010: "COMMON_CONNECTION_IN_PROGRESS",
|
|
424
|
+
4100: "METAMASK_PROVIDER_NOT_FOUND",
|
|
425
|
+
4101: "METAMASK_CHAIN_NOT_ADDED",
|
|
426
|
+
4102: "METAMASK_NOT_INSTALLED",
|
|
427
|
+
4200: "LEDGER_DEVICE_LOCKED",
|
|
428
|
+
4201: "LEDGER_APP_NOT_OPEN",
|
|
429
|
+
4202: "LEDGER_WRONG_APP",
|
|
430
|
+
4203: "LEDGER_TRANSPORT_ERROR",
|
|
431
|
+
4204: "LEDGER_CONNECTION_FAILED",
|
|
432
|
+
4205: "LEDGER_TX_REJECTED",
|
|
433
|
+
4300: "WALLET_CONNECT_SESSION_EXPIRED",
|
|
434
|
+
4301: "WALLET_CONNECT_PAIRING_FAILED",
|
|
435
|
+
4302: "WALLET_CONNECT_SESSION_NOT_FOUND",
|
|
436
|
+
4303: "WALLET_CONNECT_PROVIDER_NOT_INITIALIZED",
|
|
437
|
+
4400: "XAMAN_SIGN_REJECTED",
|
|
438
|
+
4401: "XAMAN_QR_TIMEOUT",
|
|
439
|
+
4402: "XAMAN_SDK_NOT_READY",
|
|
440
|
+
4403: "XAMAN_AUTHORIZATION_FAILED",
|
|
441
|
+
4500: "DCENT_NOT_IN_BROWSER",
|
|
442
|
+
4501: "DCENT_SIGN_REJECTED"
|
|
443
|
+
};
|
|
444
|
+
function buildReverseMap() {
|
|
445
|
+
const entries = [];
|
|
446
|
+
for (const key in errorCodeMap) {
|
|
447
|
+
const num = Number(key);
|
|
448
|
+
const name = errorCodeMap[num];
|
|
449
|
+
entries.push([name, num]);
|
|
450
|
+
}
|
|
451
|
+
return objectFromEntries(entries);
|
|
452
|
+
}
|
|
453
|
+
const ErrorCode = buildReverseMap();
|
|
454
|
+
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/core/errors/error-messages.ts
|
|
457
|
+
/**
|
|
458
|
+
* Human-readable error messages for each error code.
|
|
459
|
+
*/
|
|
460
|
+
const ErrorMessages = {
|
|
461
|
+
COMMON_USER_REJECTED: "User rejected the request",
|
|
462
|
+
COMMON_NOT_CONNECTED: "Wallet is not connected",
|
|
463
|
+
COMMON_CHAIN_NOT_CONFIGURED: "Chain is not configured",
|
|
464
|
+
COMMON_CHAIN_NOT_SUPPORTED: "Chain is not supported by this wallet",
|
|
465
|
+
COMMON_FEATURE_NOT_SUPPORTED: "Feature is not supported by this wallet",
|
|
466
|
+
COMMON_CONNECTION_FAILED: "Connection failed",
|
|
467
|
+
COMMON_UNKNOWN_CHAIN_ID: "Unknown chain ID",
|
|
468
|
+
COMMON_NO_ACCOUNTS: "No accounts available",
|
|
469
|
+
COMMON_PROVIDER_ERROR: "Provider error",
|
|
470
|
+
COMMON_CONNECTION_IN_PROGRESS: "Connection already in progress",
|
|
471
|
+
METAMASK_PROVIDER_NOT_FOUND: "MetaMask provider not found",
|
|
472
|
+
METAMASK_CHAIN_NOT_ADDED: "Chain is not added to MetaMask",
|
|
473
|
+
METAMASK_NOT_INSTALLED: "MetaMask is not installed",
|
|
474
|
+
LEDGER_DEVICE_LOCKED: "Ledger device is locked",
|
|
475
|
+
LEDGER_APP_NOT_OPEN: "Please open the correct app on your Ledger",
|
|
476
|
+
LEDGER_WRONG_APP: "Wrong app is open on Ledger device",
|
|
477
|
+
LEDGER_TRANSPORT_ERROR: "Ledger transport error",
|
|
478
|
+
LEDGER_CONNECTION_FAILED: "Failed to connect to Ledger device",
|
|
479
|
+
LEDGER_TX_REJECTED: "Transaction rejected on Ledger device",
|
|
480
|
+
WALLET_CONNECT_SESSION_EXPIRED: "WalletConnect session has expired",
|
|
481
|
+
WALLET_CONNECT_PAIRING_FAILED: "WalletConnect pairing failed",
|
|
482
|
+
WALLET_CONNECT_SESSION_NOT_FOUND: "WalletConnect session not found",
|
|
483
|
+
WALLET_CONNECT_PROVIDER_NOT_INITIALIZED: "WalletConnect provider not initialized",
|
|
484
|
+
XAMAN_SIGN_REJECTED: "Sign request rejected in Xaman",
|
|
485
|
+
XAMAN_QR_TIMEOUT: "Xaman QR scan timed out",
|
|
486
|
+
XAMAN_SDK_NOT_READY: "Xaman SDK is not ready",
|
|
487
|
+
XAMAN_AUTHORIZATION_FAILED: "Xaman authorization failed",
|
|
488
|
+
DCENT_NOT_IN_BROWSER: "D'CENT in-app browser not detected",
|
|
489
|
+
DCENT_SIGN_REJECTED: "Transaction rejected in D'CENT wallet"
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
//#endregion
|
|
493
|
+
//#region src/core/errors/wallet-error.ts
|
|
494
|
+
var WalletError = class extends Error {
|
|
495
|
+
constructor(walletType, codeKey, cause) {
|
|
496
|
+
const message = ErrorMessages[codeKey];
|
|
497
|
+
super(message, { cause });
|
|
498
|
+
this.name = "WalletError";
|
|
499
|
+
this.walletType = walletType;
|
|
500
|
+
this.codeKey = codeKey;
|
|
501
|
+
this.code = ErrorCode[codeKey];
|
|
502
|
+
this.cause = cause;
|
|
503
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
//#endregion
|
|
508
|
+
//#region src/core/connectors/dcent/dcent-error.ts
|
|
509
|
+
var DcentError = class extends WalletError {
|
|
510
|
+
constructor(code, cause) {
|
|
511
|
+
super("dcent", code, cause);
|
|
512
|
+
this.name = "DcentError";
|
|
513
|
+
}
|
|
514
|
+
static fromError(_error) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
//#endregion
|
|
520
|
+
//#region src/core/connectors/ledger/ledger-error.ts
|
|
521
|
+
var LedgerError = class LedgerError extends WalletError {
|
|
522
|
+
static {
|
|
523
|
+
this.TRANSPORT_MAPPINGS = [
|
|
524
|
+
{
|
|
525
|
+
match: (e) => e instanceof LockedDeviceError,
|
|
526
|
+
code: "LEDGER_DEVICE_LOCKED"
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
match: (e) => e instanceof UserRefusedOnDevice,
|
|
530
|
+
code: "LEDGER_TX_REJECTED"
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
match: (e) => e instanceof TransportOpenUserCancelled,
|
|
534
|
+
code: "COMMON_USER_REJECTED"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
match: (e) => e instanceof DisconnectedDevice,
|
|
538
|
+
code: "LEDGER_CONNECTION_FAILED"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
match: (e) => e instanceof DisconnectedDeviceDuringOperation,
|
|
542
|
+
code: "LEDGER_CONNECTION_FAILED"
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
match: (e) => e instanceof TransportInterfaceNotAvailable,
|
|
546
|
+
code: "LEDGER_CONNECTION_FAILED"
|
|
547
|
+
}
|
|
548
|
+
];
|
|
549
|
+
}
|
|
550
|
+
constructor(code, cause) {
|
|
551
|
+
super("ledger", code, cause);
|
|
552
|
+
this.name = "LedgerError";
|
|
553
|
+
}
|
|
554
|
+
/** Maps raw @ledgerhq transport errors to LedgerError. Returns null for unknown errors. */
|
|
555
|
+
static fromError(error) {
|
|
556
|
+
for (const mapping of LedgerError.TRANSPORT_MAPPINGS) if (mapping.match(error)) return new LedgerError(mapping.code, error);
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region src/core/utils/chain-utils.ts
|
|
563
|
+
/**
|
|
564
|
+
* Chain utilities - extends ChainGuards with additional helpers.
|
|
565
|
+
* For basic type guards (isEvmCaip2, isXrplCaip2), use ChainGuards directly.
|
|
566
|
+
*/
|
|
567
|
+
var ChainUtils = class ChainUtils {
|
|
568
|
+
static {
|
|
569
|
+
this.isEvmCaip2 = ChainGuards.isEvmCaip2;
|
|
570
|
+
}
|
|
571
|
+
static {
|
|
572
|
+
this.isXrplCaip2 = ChainGuards.isXrplCaip2;
|
|
573
|
+
}
|
|
574
|
+
static {
|
|
575
|
+
this.isEvmChain = ChainGuards.isEvmChain;
|
|
576
|
+
}
|
|
577
|
+
static isRpcError(error) {
|
|
578
|
+
return TypeUtils.isObject(error) && "code" in error && typeof error.code === "number";
|
|
579
|
+
}
|
|
580
|
+
static isRpcErrorWithCode(error, code) {
|
|
581
|
+
return ChainUtils.isRpcError(error) && error.code === code;
|
|
582
|
+
}
|
|
583
|
+
static buildEvmHexMap(chains) {
|
|
584
|
+
const map = /* @__PURE__ */ new Map();
|
|
585
|
+
for (const chain of chains) if (ChainGuards.isEvmChain(chain)) map.set(chain.evmChain.hex, chain.caip2);
|
|
586
|
+
return map;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
//#endregion
|
|
591
|
+
//#region src/core/connectors/metamask/metamask-error.ts
|
|
592
|
+
var MetaMaskError = class MetaMaskError extends WalletError {
|
|
593
|
+
constructor(code, cause) {
|
|
594
|
+
super("metamask", code, cause);
|
|
595
|
+
this.name = "MetaMaskError";
|
|
596
|
+
}
|
|
597
|
+
static fromError(error) {
|
|
598
|
+
if (ChainUtils.isRpcErrorWithCode(error, 4001)) return new MetaMaskError("COMMON_USER_REJECTED", error);
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
//#endregion
|
|
604
|
+
//#region src/core/connectors/wallet-connect/wallet-connect-error.ts
|
|
605
|
+
var WalletConnectError = class WalletConnectError extends WalletError {
|
|
606
|
+
constructor(code, cause) {
|
|
607
|
+
super("wallet-connect", code, cause);
|
|
608
|
+
this.name = "WalletConnectError";
|
|
609
|
+
}
|
|
610
|
+
static fromError(error) {
|
|
611
|
+
if (ChainUtils.isRpcErrorWithCode(error, 4001)) return new WalletConnectError("COMMON_USER_REJECTED", error);
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
//#endregion
|
|
617
|
+
//#region src/core/connectors/xaman/xaman-error.ts
|
|
618
|
+
var XamanError = class extends WalletError {
|
|
619
|
+
constructor(code, cause) {
|
|
620
|
+
super("xaman", code, cause);
|
|
621
|
+
this.name = "XamanError";
|
|
622
|
+
}
|
|
623
|
+
static fromError(_error) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
//#endregion
|
|
629
|
+
//#region src/core/errors/wallet-error-helper.ts
|
|
630
|
+
var WalletErrorHelper = class {
|
|
631
|
+
static isWalletError(error) {
|
|
632
|
+
return error instanceof WalletError;
|
|
633
|
+
}
|
|
634
|
+
static from(error, walletType) {
|
|
635
|
+
if (error instanceof WalletError) return error;
|
|
636
|
+
switch (walletType) {
|
|
637
|
+
case "ledger": return LedgerError.fromError(error);
|
|
638
|
+
case "wallet-connect": return WalletConnectError.fromError(error);
|
|
639
|
+
case "metamask": return MetaMaskError.fromError(error);
|
|
640
|
+
case "xaman": return XamanError.fromError(error);
|
|
641
|
+
case "dcent": return DcentError.fromError(error);
|
|
642
|
+
default: return null;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
//#endregion
|
|
648
|
+
//#region src/core/utils/error-utils.ts
|
|
649
|
+
var ErrorUtils = class ErrorUtils {
|
|
650
|
+
static extractMessage(error) {
|
|
651
|
+
if (TypeUtils.isString(error)) return error;
|
|
652
|
+
if (TypeUtils.isError(error)) return error.message;
|
|
653
|
+
if (TypeUtils.isObject(error) && "message" in error && TypeUtils.isString(error.message)) return error.message;
|
|
654
|
+
try {
|
|
655
|
+
return JSON.stringify(error);
|
|
656
|
+
} catch {
|
|
657
|
+
return "Unknown error";
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
static ensureError(error) {
|
|
661
|
+
if (TypeUtils.isError(error)) return error;
|
|
662
|
+
return new Error(ErrorUtils.extractMessage(error));
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
//#endregion
|
|
667
|
+
//#region src/core/connectors/connector-events.ts
|
|
668
|
+
var ConnectorEventEmitter = class {
|
|
669
|
+
constructor(store, walletType) {
|
|
670
|
+
this.store = store;
|
|
671
|
+
this.walletType = walletType;
|
|
672
|
+
}
|
|
673
|
+
startConnecting() {
|
|
674
|
+
this.store.send({ type: "startConnecting" });
|
|
675
|
+
}
|
|
676
|
+
connected(data) {
|
|
677
|
+
this.store.send({
|
|
678
|
+
type: "connectionEstablished",
|
|
679
|
+
activeAddress: data.address,
|
|
680
|
+
availableAddresses: this.resolveAddresses(data.addresses, data.caip2),
|
|
681
|
+
caip2: data.caip2
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
reconnected(data) {
|
|
685
|
+
this.store.send({
|
|
686
|
+
type: "connectionRestored",
|
|
687
|
+
activeAddress: data.address,
|
|
688
|
+
availableAddresses: this.resolveAddresses(data.addresses, data.caip2),
|
|
689
|
+
caip2: data.caip2
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
startDisconnecting() {
|
|
693
|
+
this.store.send({ type: "startDisconnecting" });
|
|
694
|
+
}
|
|
695
|
+
disconnected() {
|
|
696
|
+
this.store.send({ type: "disconnected" });
|
|
697
|
+
}
|
|
698
|
+
chainChanged(caip2) {
|
|
699
|
+
this.store.send({
|
|
700
|
+
type: "chainChanged",
|
|
701
|
+
caip2
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
addressChanged(data) {
|
|
705
|
+
this.store.send({
|
|
706
|
+
type: "addressChanged",
|
|
707
|
+
activeAddress: data.address,
|
|
708
|
+
availableAddresses: this.resolveAddresses(data.addresses, data.caip2)
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
error(error) {
|
|
712
|
+
this.store.send({
|
|
713
|
+
type: "error",
|
|
714
|
+
error
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
resolveAddresses(addresses, caip2) {
|
|
718
|
+
if (addresses.length === 0) return [];
|
|
719
|
+
if (typeof addresses[0] === "string") {
|
|
720
|
+
const isEvm = ChainUtils.isEvmCaip2(caip2);
|
|
721
|
+
return addresses.map((address) => ({
|
|
722
|
+
type: this.walletType,
|
|
723
|
+
address,
|
|
724
|
+
caip2,
|
|
725
|
+
isEvm
|
|
726
|
+
}));
|
|
727
|
+
}
|
|
728
|
+
return addresses;
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
//#endregion
|
|
733
|
+
//#region src/core/connectors/base-connector.ts
|
|
734
|
+
var BaseConnector = class {
|
|
735
|
+
constructor(store, config) {
|
|
736
|
+
this.initPromise = null;
|
|
737
|
+
this.initResolved = false;
|
|
738
|
+
this.store = store;
|
|
739
|
+
this.config = config;
|
|
740
|
+
this.walletType = config.walletType;
|
|
741
|
+
this.resolvedConfig = config.resolvedConfig;
|
|
742
|
+
this.events = new ConnectorEventEmitter(store, config.walletType);
|
|
743
|
+
this.connect = this.wrapAsync(this.connect);
|
|
744
|
+
this.disconnect = this.wrapAsync(this.disconnect);
|
|
745
|
+
this.restoreConnection = this.wrapAsync(this.restoreConnection);
|
|
746
|
+
this.switchChain = this.wrapAsync(this.switchChain);
|
|
747
|
+
}
|
|
748
|
+
get initialized() {
|
|
749
|
+
return this.initResolved;
|
|
750
|
+
}
|
|
751
|
+
async initialize() {
|
|
752
|
+
if (!this.initPromise) this.initPromise = this.onInitialize().then(() => {
|
|
753
|
+
this.initResolved = true;
|
|
754
|
+
}).catch((error) => {
|
|
755
|
+
this.initPromise = null;
|
|
756
|
+
throw error;
|
|
757
|
+
});
|
|
758
|
+
return this.initPromise;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Reset init state so next operation re-initializes the SDK.
|
|
762
|
+
* Required for MetaMask/Xaman (SDK terminates on disconnect).
|
|
763
|
+
* WalletConnect should NOT call this — its provider survives disconnect.
|
|
764
|
+
*/
|
|
765
|
+
resetInitialization() {
|
|
766
|
+
this.initPromise = null;
|
|
767
|
+
this.initResolved = false;
|
|
768
|
+
}
|
|
769
|
+
emitError(error) {
|
|
770
|
+
const message = ErrorUtils.extractMessage(error);
|
|
771
|
+
const code = WalletErrorHelper.isWalletError(error) ? error.code : "unknown";
|
|
772
|
+
this.events.error({
|
|
773
|
+
code,
|
|
774
|
+
message
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
wrapAsync(method) {
|
|
778
|
+
return (async (...args) => {
|
|
779
|
+
try {
|
|
780
|
+
return await method.apply(this, args);
|
|
781
|
+
} catch (error) {
|
|
782
|
+
this.emitError(error);
|
|
783
|
+
throw error;
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
//#endregion
|
|
790
|
+
//#region src/core/clients/xrp/base-xrpl-client.ts
|
|
791
|
+
var BaseXrplClient = class extends Client {
|
|
792
|
+
async onDestroy() {
|
|
793
|
+
if (this.isConnected()) await this.disconnect();
|
|
794
|
+
}
|
|
795
|
+
async ensureConnected() {
|
|
796
|
+
if (!this.isConnected()) await this.connect();
|
|
797
|
+
}
|
|
798
|
+
async getBalance(address) {
|
|
799
|
+
try {
|
|
800
|
+
await this.ensureConnected();
|
|
801
|
+
return await this.getXrpBalance(address);
|
|
802
|
+
} catch (error) {
|
|
803
|
+
if (error instanceof RippledError && error.message === "Account not found.") return 0;
|
|
804
|
+
throw error;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
//#endregion
|
|
810
|
+
//#region src/core/clients/xrp/xrpl-client.ts
|
|
811
|
+
var XrplClient = class extends BaseXrplClient {
|
|
812
|
+
constructor(chain) {
|
|
813
|
+
super(chain.rpcUrls[0]);
|
|
814
|
+
this.chain = chain;
|
|
815
|
+
}
|
|
816
|
+
async getCaip2() {
|
|
817
|
+
return this.chain.caip2;
|
|
818
|
+
}
|
|
819
|
+
async onDestroy() {
|
|
820
|
+
await super.onDestroy();
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
//#endregion
|
|
825
|
+
//#region src/core/connectors/dcent/dcent-xrpl-client.ts
|
|
826
|
+
var DcentXrpClient = class {
|
|
827
|
+
constructor(provider, resolvedConfig) {
|
|
828
|
+
this.provider = provider;
|
|
829
|
+
this.resolvedConfig = resolvedConfig;
|
|
830
|
+
}
|
|
831
|
+
async sendTx(tx) {
|
|
832
|
+
const caip2 = await this.getCaip2();
|
|
833
|
+
const result = await this.provider.request({
|
|
834
|
+
method: "xrpl_signTransaction",
|
|
835
|
+
params: [{
|
|
836
|
+
tx_json: tx,
|
|
837
|
+
submit: true,
|
|
838
|
+
chainId: caip2,
|
|
839
|
+
autofill: true
|
|
840
|
+
}]
|
|
841
|
+
});
|
|
842
|
+
if (!result?.tx_hash) throw new DcentError("DCENT_SIGN_REJECTED");
|
|
843
|
+
return result.tx_hash;
|
|
844
|
+
}
|
|
845
|
+
async getBalance(address) {
|
|
846
|
+
const caip2 = await this.getCaip2();
|
|
847
|
+
return new XrplClient(this.resolvedConfig.getXrplChain(caip2)).getBalance(address);
|
|
848
|
+
}
|
|
849
|
+
async getCaip2() {
|
|
850
|
+
return (await this.provider.getWalletInfo()).chainId;
|
|
851
|
+
}
|
|
852
|
+
async onDestroy() {}
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
//#endregion
|
|
856
|
+
//#region src/core/connectors/dcent/dcent-connector.ts
|
|
857
|
+
const DCENT_NETWORK_MAP = {
|
|
858
|
+
"xrpl:0": "xrpl-mainnet",
|
|
859
|
+
"xrpl:1": "xrpl-testnet"
|
|
860
|
+
};
|
|
861
|
+
var DcentConnector = class DcentConnector extends BaseConnector {
|
|
862
|
+
constructor(store, config) {
|
|
863
|
+
super(store, config);
|
|
864
|
+
}
|
|
865
|
+
static isAvailable() {
|
|
866
|
+
return typeof window !== "undefined" && typeof window.xrpl !== "undefined";
|
|
867
|
+
}
|
|
868
|
+
static getDynamicLink(targetUrl, caip2) {
|
|
869
|
+
const url = new URL("https://link.dcentwallet.com/DAppBrowser/");
|
|
870
|
+
url.searchParams.set("url", targetUrl);
|
|
871
|
+
url.searchParams.set("network", DCENT_NETWORK_MAP[caip2]);
|
|
872
|
+
return url.toString();
|
|
873
|
+
}
|
|
874
|
+
async onInitialize() {
|
|
875
|
+
if (!DcentConnector.isAvailable()) return;
|
|
876
|
+
this.provider = window.xrpl;
|
|
877
|
+
this.client = new DcentXrpClient(this.provider, this.resolvedConfig);
|
|
878
|
+
}
|
|
879
|
+
async connect(_chains) {
|
|
880
|
+
if (!DcentConnector.isAvailable()) throw new DcentError("DCENT_NOT_IN_BROWSER");
|
|
881
|
+
if (this.connectionState === "connecting") throw new DcentError("COMMON_CONNECTION_IN_PROGRESS");
|
|
882
|
+
await this.initialize();
|
|
883
|
+
this.events.startConnecting();
|
|
884
|
+
const accounts = await this.provider.request({ method: "xrpl_accounts" });
|
|
885
|
+
if (!accounts?.length) throw new DcentError("COMMON_NO_ACCOUNTS");
|
|
886
|
+
const address = accounts[0];
|
|
887
|
+
const caip2 = await this.client.getCaip2();
|
|
888
|
+
this.events.connected({
|
|
889
|
+
address,
|
|
890
|
+
addresses: [address],
|
|
891
|
+
caip2
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
async disconnect() {
|
|
895
|
+
this.events.startDisconnecting();
|
|
896
|
+
this.events.disconnected();
|
|
897
|
+
}
|
|
898
|
+
async restoreConnection() {
|
|
899
|
+
if (!DcentConnector.isAvailable()) return;
|
|
900
|
+
await this.initialize();
|
|
901
|
+
const accounts = await this.provider.request({ method: "xrpl_accounts" });
|
|
902
|
+
if (!accounts?.length) return;
|
|
903
|
+
const address = accounts[0];
|
|
904
|
+
const caip2 = await this.client.getCaip2();
|
|
905
|
+
this.events.reconnected({
|
|
906
|
+
address,
|
|
907
|
+
addresses: [address],
|
|
908
|
+
caip2
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
async switchChain(caip2) {
|
|
912
|
+
await this.provider.request({
|
|
913
|
+
method: "xrpl_switchChain",
|
|
914
|
+
params: [{ chainId: caip2 }]
|
|
915
|
+
});
|
|
916
|
+
this.events.chainChanged(caip2);
|
|
917
|
+
}
|
|
918
|
+
getClient() {
|
|
919
|
+
return this.client;
|
|
920
|
+
}
|
|
921
|
+
getProvider() {
|
|
922
|
+
return this.provider;
|
|
923
|
+
}
|
|
924
|
+
get connectionState() {
|
|
925
|
+
return this.store.getSnapshot().context.connectionState;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
//#endregion
|
|
930
|
+
//#region src/core/shared/bip44.ts
|
|
931
|
+
const BIP44_HARDENED_OFFSET = 2147483648;
|
|
932
|
+
var Bip44 = class Bip44 {
|
|
933
|
+
constructor(path) {
|
|
934
|
+
const segments = (path.startsWith("m/") ? path.slice(2) : path).split("/");
|
|
935
|
+
if (segments.length !== 5) throw new Error(`Invalid BIP44 path: ${path}. Expected 5 components.`);
|
|
936
|
+
if (!segments[0].startsWith("44")) throw new Error(`Invalid BIP44 purpose: ${segments[0]}. Must be 44'.`);
|
|
937
|
+
this._purpose = this.parseComponent(segments[0]);
|
|
938
|
+
this._coinType = this.parseComponent(segments[1]);
|
|
939
|
+
this._account = this.parseComponent(segments[2]);
|
|
940
|
+
this._change = this.parseComponent(segments[3]);
|
|
941
|
+
this._addressIndex = this.parseComponent(segments[4]);
|
|
942
|
+
if (this._change.value !== 0 && this._change.value !== 1) throw new Error(`Invalid change value: ${this._change.value}. Must be 0 or 1.`);
|
|
943
|
+
if (!this._purpose.hardened || !this._coinType.hardened || !this._account.hardened) throw new Error("BIP44 requires purpose, coin type, and account to be hardened.");
|
|
944
|
+
}
|
|
945
|
+
get purpose() {
|
|
946
|
+
return this._purpose.value;
|
|
947
|
+
}
|
|
948
|
+
get coinType() {
|
|
949
|
+
return this._coinType.value;
|
|
950
|
+
}
|
|
951
|
+
get account() {
|
|
952
|
+
return this._account.value;
|
|
953
|
+
}
|
|
954
|
+
get change() {
|
|
955
|
+
return this._change.value;
|
|
956
|
+
}
|
|
957
|
+
get addressIndex() {
|
|
958
|
+
return this._addressIndex.value;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Generate paginated BIP44 paths.
|
|
962
|
+
* - bip44 standard: m/44'/coin'/0'/0/i — increments address index
|
|
963
|
+
* - Ledger Live: m/44'/coin'/i'/0/0 — increments account
|
|
964
|
+
*/
|
|
965
|
+
static getPaginatedBip44s({ from, to, ledgerHDStandard, caip2 }) {
|
|
966
|
+
const paths = [];
|
|
967
|
+
const coinType = Bip44.fromCaip2ToCoinType(caip2);
|
|
968
|
+
if (ledgerHDStandard === "bip44") for (let i = from; i < to; i++) {
|
|
969
|
+
const path = new Bip44(`44'/${coinType}'/0'/0/${i}`);
|
|
970
|
+
paths.push(path);
|
|
971
|
+
}
|
|
972
|
+
else for (let i = from; i < to; i++) {
|
|
973
|
+
const path = new Bip44(`44'/${coinType}'/${i}'/0/0`);
|
|
974
|
+
paths.push(path);
|
|
975
|
+
}
|
|
976
|
+
return {
|
|
977
|
+
paths,
|
|
978
|
+
from,
|
|
979
|
+
to,
|
|
980
|
+
ledgerHDStandard
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
static fromCaip2ToCoinType(caip2) {
|
|
984
|
+
if (!isCaip2(caip2)) throw new Error(`Invalid CAIP2: ${caip2}`);
|
|
985
|
+
const chain = chainConfig[caip2];
|
|
986
|
+
if (!chain.bip44Part) throw new Error(`Chain ${caip2} does not define a BIP44 part.`);
|
|
987
|
+
return chain.bip44Part;
|
|
988
|
+
}
|
|
989
|
+
static fromCaip2(caip2, account = 0, change = 0, addressIndex = 0) {
|
|
990
|
+
return Bip44.fromCoin(Bip44.fromCaip2ToCoinType(caip2), account, change, addressIndex);
|
|
991
|
+
}
|
|
992
|
+
static fromCoin(coinType, account = 0, change = 0, addressIndex = 0) {
|
|
993
|
+
return new Bip44(`44'/${coinType}'/${account}'/${change}/${addressIndex}`);
|
|
994
|
+
}
|
|
995
|
+
static bitcoin(account = 0, change = 0, addressIndex = 0) {
|
|
996
|
+
return Bip44.fromCoin(0, account, change, addressIndex);
|
|
997
|
+
}
|
|
998
|
+
static ethereum(account = 0, change = 0, addressIndex = 0) {
|
|
999
|
+
return Bip44.fromCoin(60, account, change, addressIndex);
|
|
1000
|
+
}
|
|
1001
|
+
static ledgerLiveEthereum(accountIndex = 0) {
|
|
1002
|
+
return new Bip44(`44'/60'/${accountIndex}'/0/0`);
|
|
1003
|
+
}
|
|
1004
|
+
getIndexForStandard(standard) {
|
|
1005
|
+
if (standard === "bip44") return this.addressIndex;
|
|
1006
|
+
return this.account;
|
|
1007
|
+
}
|
|
1008
|
+
isChangeAddress() {
|
|
1009
|
+
return this._change.value === 1;
|
|
1010
|
+
}
|
|
1011
|
+
isReceivingAddress() {
|
|
1012
|
+
return this._change.value === 0;
|
|
1013
|
+
}
|
|
1014
|
+
getFullPath() {
|
|
1015
|
+
return `m/${this.getPathWithoutMaster()}`;
|
|
1016
|
+
}
|
|
1017
|
+
getPathWithoutMaster() {
|
|
1018
|
+
return `${this._purpose}/${this._coinType}/${this._account}/${this._change}/${this._addressIndex}`;
|
|
1019
|
+
}
|
|
1020
|
+
toPathArray() {
|
|
1021
|
+
return [
|
|
1022
|
+
this.hardenedValue(this._purpose),
|
|
1023
|
+
this.hardenedValue(this._coinType),
|
|
1024
|
+
this.hardenedValue(this._account),
|
|
1025
|
+
this._change.value,
|
|
1026
|
+
this._addressIndex.value
|
|
1027
|
+
];
|
|
1028
|
+
}
|
|
1029
|
+
deriveFromAddressIndex(newAddressIndex) {
|
|
1030
|
+
return new Bip44(`${this._purpose}/${this._coinType}/${this._account}/${this._change}/${newAddressIndex}`);
|
|
1031
|
+
}
|
|
1032
|
+
nextAddress(standard = "bip44") {
|
|
1033
|
+
if (standard === "bip44") return this.deriveFromAddressIndex(this._addressIndex.value + 1);
|
|
1034
|
+
return new Bip44(`${this._purpose}/${this._coinType}/${this._account.value + 1}'/${this._change}/${this._addressIndex}`);
|
|
1035
|
+
}
|
|
1036
|
+
toReceivingAddress(addressIndex) {
|
|
1037
|
+
const index = addressIndex !== void 0 ? addressIndex : this._addressIndex.value;
|
|
1038
|
+
return new Bip44(`${this._purpose}/${this._coinType}/${this._account}/0/${index}`);
|
|
1039
|
+
}
|
|
1040
|
+
toChangeAddress(addressIndex) {
|
|
1041
|
+
const index = addressIndex !== void 0 ? addressIndex : this._addressIndex.value;
|
|
1042
|
+
return new Bip44(`${this._purpose}/${this._coinType}/${this._account}/1/${index}`);
|
|
1043
|
+
}
|
|
1044
|
+
deriveAccount(accountIndex) {
|
|
1045
|
+
return new Bip44(`${this._purpose}/${this._coinType}/${accountIndex}'/${this._change}/${this._addressIndex}`);
|
|
1046
|
+
}
|
|
1047
|
+
toString() {
|
|
1048
|
+
return this.getFullPath();
|
|
1049
|
+
}
|
|
1050
|
+
getNextPaginatedBip44s(caip2, ledgerHDStandard) {
|
|
1051
|
+
const currentAccountIndex = this.getIndexForStandard(ledgerHDStandard);
|
|
1052
|
+
return Bip44.getPaginatedBip44s({
|
|
1053
|
+
from: currentAccountIndex + 1,
|
|
1054
|
+
to: currentAccountIndex + 6,
|
|
1055
|
+
ledgerHDStandard,
|
|
1056
|
+
caip2
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
isValidCaip2(caip2) {
|
|
1060
|
+
return Bip44.fromCaip2ToCoinType(caip2) === this.coinType;
|
|
1061
|
+
}
|
|
1062
|
+
parseComponent(component) {
|
|
1063
|
+
const hardened = component.endsWith("'") || component.endsWith("h");
|
|
1064
|
+
const value = Number.parseInt(hardened ? component.slice(0, -1) : component, 10);
|
|
1065
|
+
if (Number.isNaN(value)) throw new TypeError(`Invalid path component: ${component}`);
|
|
1066
|
+
return {
|
|
1067
|
+
value,
|
|
1068
|
+
hardened,
|
|
1069
|
+
toString: () => hardened ? `${value}'` : `${value}`
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
hardenedValue(component) {
|
|
1073
|
+
return component.hardened ? component.value + BIP44_HARDENED_OFFSET : component.value;
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
//#endregion
|
|
1078
|
+
//#region src/core/shared/connection-mutex.ts
|
|
1079
|
+
/**
|
|
1080
|
+
* Async mutex for serializing connection operations.
|
|
1081
|
+
* Uses promise chaining to ensure FIFO ordering.
|
|
1082
|
+
*/
|
|
1083
|
+
var ConnectionMutex = class {
|
|
1084
|
+
constructor() {
|
|
1085
|
+
this.queue = Promise.resolve();
|
|
1086
|
+
}
|
|
1087
|
+
async run(fn) {
|
|
1088
|
+
let release;
|
|
1089
|
+
const waitForTurn = this.queue;
|
|
1090
|
+
this.queue = new Promise((r) => {
|
|
1091
|
+
release = r;
|
|
1092
|
+
});
|
|
1093
|
+
await waitForTurn;
|
|
1094
|
+
try {
|
|
1095
|
+
return await fn();
|
|
1096
|
+
} finally {
|
|
1097
|
+
release();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
//#endregion
|
|
1103
|
+
//#region src/core/connectors/ledger/internal/ledger-hex-utils.ts
|
|
1104
|
+
var LedgerHexUtils = class {
|
|
1105
|
+
static ensureLeading0x(input) {
|
|
1106
|
+
return input.startsWith("0x") ? input : `0x${input}`;
|
|
1107
|
+
}
|
|
1108
|
+
static trimLeading0x(input) {
|
|
1109
|
+
return input.startsWith("0x") ? input.substring(2) : input;
|
|
1110
|
+
}
|
|
1111
|
+
static fromBuffer(buffer) {
|
|
1112
|
+
return `0x${Array.from(buffer).map((item) => item.toString(16).padStart(2, "0")).join("")}`;
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
//#endregion
|
|
1117
|
+
//#region src/core/connectors/ledger/internal/ledger-address-helper.ts
|
|
1118
|
+
var LedgerAddressHelper = class {
|
|
1119
|
+
static async getComputedAddresses(app, bip44s, hrp) {
|
|
1120
|
+
const addresses = [];
|
|
1121
|
+
for (const bip44 of bip44s) {
|
|
1122
|
+
const computedAddress = await app.getComputedAddress(bip44, hrp);
|
|
1123
|
+
addresses.push(computedAddress);
|
|
1124
|
+
}
|
|
1125
|
+
return addresses;
|
|
1126
|
+
}
|
|
1127
|
+
static compressedPKToAddress(compressedPK) {
|
|
1128
|
+
const publicKeyHex = bytesToHex(compressedPK);
|
|
1129
|
+
const point = secp256k1.Point.fromHex(LedgerHexUtils.trimLeading0x(publicKeyHex));
|
|
1130
|
+
return publicKeyToAddress(LedgerHexUtils.fromBuffer(point.toBytes(false)));
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
//#endregion
|
|
1135
|
+
//#region src/core/connectors/ledger/apps/ethereum/ethereum-viem-adapter.ts
|
|
1136
|
+
async function ledgerToEthAccount({ ledger, derivationPath }) {
|
|
1137
|
+
const resolvedPath = derivationPath ?? `44'/60'/0'/0/0`;
|
|
1138
|
+
const { address, publicKey } = await ledger.getAddress(resolvedPath, false);
|
|
1139
|
+
return {
|
|
1140
|
+
...toAccount({
|
|
1141
|
+
address: getAddress(address),
|
|
1142
|
+
async signTransaction(transaction) {
|
|
1143
|
+
const hash = serializeTransaction(transaction);
|
|
1144
|
+
const signedTxn = await ledger.signTransaction(resolvedPath, LedgerHexUtils.trimLeading0x(hash), null);
|
|
1145
|
+
let { v: _v } = signedTxn;
|
|
1146
|
+
const { r, s } = signedTxn;
|
|
1147
|
+
if (typeof _v === "string" && (_v === "" || _v === "0x")) _v = "0x0";
|
|
1148
|
+
let v;
|
|
1149
|
+
try {
|
|
1150
|
+
v = BigInt(typeof _v === "string" ? LedgerHexUtils.ensureLeading0x(_v) : _v);
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
throw new Error(`Ledger signature \`v\` was malformed and couldn't be parsed \`${_v}\` (Original error: ${error})`);
|
|
1153
|
+
}
|
|
1154
|
+
return serializeTransaction(transaction, {
|
|
1155
|
+
r: LedgerHexUtils.ensureLeading0x(r),
|
|
1156
|
+
s: LedgerHexUtils.ensureLeading0x(s),
|
|
1157
|
+
v
|
|
1158
|
+
});
|
|
1159
|
+
},
|
|
1160
|
+
async signMessage({ message }) {
|
|
1161
|
+
const { r, s, v } = await ledger.signPersonalMessage(resolvedPath, `0x${Buffer.from(message).toString("hex")}`);
|
|
1162
|
+
return serializeSignature({
|
|
1163
|
+
r: LedgerHexUtils.ensureLeading0x(r),
|
|
1164
|
+
s: LedgerHexUtils.ensureLeading0x(s),
|
|
1165
|
+
v: BigInt(v)
|
|
1166
|
+
});
|
|
1167
|
+
},
|
|
1168
|
+
async signTypedData(_parameters) {
|
|
1169
|
+
const { domain = {}, message, primaryType } = _parameters;
|
|
1170
|
+
const types = {
|
|
1171
|
+
EIP712Domain: getTypesForEIP712Domain({ domain }),
|
|
1172
|
+
..._parameters.types
|
|
1173
|
+
};
|
|
1174
|
+
const domainSeparator = hashDomain({
|
|
1175
|
+
domain,
|
|
1176
|
+
types
|
|
1177
|
+
});
|
|
1178
|
+
const messageHash = hashStruct({
|
|
1179
|
+
data: message,
|
|
1180
|
+
primaryType,
|
|
1181
|
+
types
|
|
1182
|
+
});
|
|
1183
|
+
const { r, s, v } = await ledger.signEIP712HashedMessage(resolvedPath, domainSeparator, messageHash);
|
|
1184
|
+
return serializeSignature({
|
|
1185
|
+
r: LedgerHexUtils.ensureLeading0x(r),
|
|
1186
|
+
s: LedgerHexUtils.ensureLeading0x(s),
|
|
1187
|
+
v: BigInt(v)
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
}),
|
|
1191
|
+
publicKey: LedgerHexUtils.ensureLeading0x(publicKey),
|
|
1192
|
+
source: "ledger"
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
//#endregion
|
|
1197
|
+
//#region src/core/connectors/ledger/apps/ethereum/ethereum-ledger-app.ts
|
|
1198
|
+
var EthereumLedgerApp = class extends Eth {
|
|
1199
|
+
async getComputedAddress(bip44, _hrp) {
|
|
1200
|
+
return {
|
|
1201
|
+
address: (await this.getAddress(bip44.getFullPath())).address,
|
|
1202
|
+
bip44
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
async getComputedAddresses(bip44s, hrp) {
|
|
1206
|
+
return LedgerAddressHelper.getComputedAddresses(this, bip44s, hrp);
|
|
1207
|
+
}
|
|
1208
|
+
async getViemAccount(bip44) {
|
|
1209
|
+
return ledgerToEthAccount({
|
|
1210
|
+
ledger: this,
|
|
1211
|
+
derivationPath: bip44.getPathWithoutMaster()
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1216
|
+
//#endregion
|
|
1217
|
+
//#region src/core/connectors/ledger/apps/flare/flare-viem-adapter.ts
|
|
1218
|
+
async function ledgerToFlareAccount({ ledger, derivationPath }) {
|
|
1219
|
+
const resolvedPath = derivationPath ?? `44'/60'/0'/0/0`;
|
|
1220
|
+
const { address, publicKey } = await ledger.getEVMAddress(resolvedPath, false);
|
|
1221
|
+
return {
|
|
1222
|
+
...toAccount({
|
|
1223
|
+
address: getAddress(address),
|
|
1224
|
+
async signTransaction(transaction) {
|
|
1225
|
+
const hash = serializeTransaction(transaction);
|
|
1226
|
+
const signedTxn = await ledger.signEVMTransaction(resolvedPath, LedgerHexUtils.trimLeading0x(hash), null);
|
|
1227
|
+
let { v: _v } = signedTxn;
|
|
1228
|
+
const { r, s } = signedTxn;
|
|
1229
|
+
if (typeof _v === "string" && (_v === "" || _v === "0x")) _v = "0x0";
|
|
1230
|
+
let v;
|
|
1231
|
+
try {
|
|
1232
|
+
v = BigInt(typeof _v === "string" ? LedgerHexUtils.ensureLeading0x(_v) : _v);
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
throw new Error(`Ledger signature \`v\` was malformed and couldn't be parsed \`${_v}\` (Original error: ${error})`);
|
|
1235
|
+
}
|
|
1236
|
+
return serializeTransaction(transaction, {
|
|
1237
|
+
r: LedgerHexUtils.ensureLeading0x(r),
|
|
1238
|
+
s: LedgerHexUtils.ensureLeading0x(s),
|
|
1239
|
+
v
|
|
1240
|
+
});
|
|
1241
|
+
},
|
|
1242
|
+
async signMessage({ message }) {
|
|
1243
|
+
const { r, s, v } = await ledger.signPersonalMessage(resolvedPath, `0x${Buffer.from(message).toString("hex")}`);
|
|
1244
|
+
return serializeSignature({
|
|
1245
|
+
r: LedgerHexUtils.ensureLeading0x(r),
|
|
1246
|
+
s: LedgerHexUtils.ensureLeading0x(s),
|
|
1247
|
+
v: BigInt(v)
|
|
1248
|
+
});
|
|
1249
|
+
},
|
|
1250
|
+
async signTypedData() {
|
|
1251
|
+
throw new Error("signTypedData not supported for Flare Ledger");
|
|
1252
|
+
}
|
|
1253
|
+
}),
|
|
1254
|
+
publicKey: LedgerHexUtils.ensureLeading0x(publicKey),
|
|
1255
|
+
source: "ledger"
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
//#endregion
|
|
1260
|
+
//#region src/core/connectors/ledger/apps/flare/flare-ledger-app.ts
|
|
1261
|
+
var FlareLedgerApp = class extends FlareApp {
|
|
1262
|
+
async getComputedAddress(bip44, hrp) {
|
|
1263
|
+
const addressData = await this.getAddressAndPubKey(bip44.getFullPath(), hrp);
|
|
1264
|
+
return {
|
|
1265
|
+
address: LedgerAddressHelper.compressedPKToAddress(addressData.compressed_pk),
|
|
1266
|
+
bip44
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
async getComputedAddresses(bip44s, hrp) {
|
|
1270
|
+
return LedgerAddressHelper.getComputedAddresses(this, bip44s, hrp);
|
|
1271
|
+
}
|
|
1272
|
+
async getViemAccount(bip44) {
|
|
1273
|
+
return ledgerToFlareAccount({
|
|
1274
|
+
ledger: this,
|
|
1275
|
+
derivationPath: bip44.getPathWithoutMaster()
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
//#endregion
|
|
1281
|
+
//#region src/core/connectors/ledger/apps/xrp/xrp-ledger-app.ts
|
|
1282
|
+
var XrpLedgerApp = class extends Xrp {
|
|
1283
|
+
async getComputedAddress(bip44, _hrp) {
|
|
1284
|
+
const pathWithoutMaster = bip44.getPathWithoutMaster();
|
|
1285
|
+
return {
|
|
1286
|
+
address: (await this.getAddress(pathWithoutMaster)).address,
|
|
1287
|
+
bip44
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
async getComputedAddresses(bip44s, hrp) {
|
|
1291
|
+
return LedgerAddressHelper.getComputedAddresses(this, bip44s, hrp);
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
//#endregion
|
|
1296
|
+
//#region src/core/connectors/ledger/internal/ledger-app-factory.ts
|
|
1297
|
+
const CHAIN_TO_APP = {
|
|
1298
|
+
"eip155:1": ["Ethereum"],
|
|
1299
|
+
"eip155:11155111": ["Ethereum"],
|
|
1300
|
+
"eip155:14": ["Flare Network"],
|
|
1301
|
+
"eip155:16": ["Flare Network"],
|
|
1302
|
+
"eip155:114": ["Flare Network"],
|
|
1303
|
+
"eip155:19": ["Flare Network"],
|
|
1304
|
+
"xrpl:0": ["XRP"],
|
|
1305
|
+
"xrpl:1": ["XRP"]
|
|
1306
|
+
};
|
|
1307
|
+
var LedgerAppFactory = class {
|
|
1308
|
+
static getRequiredAppName(chain) {
|
|
1309
|
+
const apps = CHAIN_TO_APP[chain];
|
|
1310
|
+
if (!apps?.length) throw new LedgerError("COMMON_CHAIN_NOT_SUPPORTED");
|
|
1311
|
+
return apps[0];
|
|
1312
|
+
}
|
|
1313
|
+
static createApp(chain, transport) {
|
|
1314
|
+
const validApps = CHAIN_TO_APP[chain];
|
|
1315
|
+
if (!validApps?.length) throw new LedgerError("COMMON_CHAIN_NOT_SUPPORTED");
|
|
1316
|
+
switch (validApps[0]) {
|
|
1317
|
+
case "XRP": return new XrpLedgerApp(transport);
|
|
1318
|
+
case "Ethereum": return new EthereumLedgerApp(transport);
|
|
1319
|
+
case "Flare Network": return new FlareLedgerApp(transport);
|
|
1320
|
+
default: throw new LedgerError("COMMON_CHAIN_NOT_SUPPORTED");
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
//#endregion
|
|
1326
|
+
//#region src/core/chains/viem/viem-helper.ts
|
|
1327
|
+
var ViemHelper = class ViemHelper {
|
|
1328
|
+
static {
|
|
1329
|
+
this.caip2ToViemChain = ViemHelper.buildChainMap();
|
|
1330
|
+
}
|
|
1331
|
+
static toViemChain(chain) {
|
|
1332
|
+
return defineChain({
|
|
1333
|
+
id: Number(chain.evmChain.id),
|
|
1334
|
+
name: chain.name,
|
|
1335
|
+
nativeCurrency: chain.nativeCurrency,
|
|
1336
|
+
rpcUrls: { default: { http: chain.rpcUrls } },
|
|
1337
|
+
blockExplorers: chain.blockExplorerUrls.length ? { default: {
|
|
1338
|
+
name: "Explorer",
|
|
1339
|
+
url: chain.blockExplorerUrls[0]
|
|
1340
|
+
} } : void 0,
|
|
1341
|
+
testnet: chain.testnet
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
static buildChainMap() {
|
|
1345
|
+
return {
|
|
1346
|
+
...objectFromEntries(evmChains.map((chain) => [chain.caip2, ViemHelper.toViemChain(chain)])),
|
|
1347
|
+
...chainRegistry.viem?.chains ?? {}
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
static {
|
|
1351
|
+
this.getViemWalletClientFromAccount = (caip2, account) => {
|
|
1352
|
+
return createWalletClient({
|
|
1353
|
+
chain: ViemHelper.caip2ToViemChain[caip2],
|
|
1354
|
+
transport: http(),
|
|
1355
|
+
account
|
|
1356
|
+
});
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
static {
|
|
1360
|
+
this.getViemPublicClientFromAccount = (caip2, _account) => {
|
|
1361
|
+
return createPublicClient({
|
|
1362
|
+
chain: ViemHelper.caip2ToViemChain[caip2],
|
|
1363
|
+
transport: http()
|
|
1364
|
+
});
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
//#endregion
|
|
1370
|
+
//#region src/core/clients/base-evm-client.ts
|
|
1371
|
+
var BaseEvmClient = class {
|
|
1372
|
+
constructor(chainConfig) {
|
|
1373
|
+
this.chainConfig = chainConfig;
|
|
1374
|
+
}
|
|
1375
|
+
async getCaip2() {
|
|
1376
|
+
return this.chainConfig.caip2;
|
|
1377
|
+
}
|
|
1378
|
+
async onDestroy() {}
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
//#endregion
|
|
1382
|
+
//#region src/core/clients/evm/evm-client.ts
|
|
1383
|
+
/**
|
|
1384
|
+
* EVM chain client.
|
|
1385
|
+
* Provides Viem public and wallet clients for EVM chain interactions.
|
|
1386
|
+
*/
|
|
1387
|
+
var EvmClient = class EvmClient extends BaseEvmClient {
|
|
1388
|
+
constructor(chainConfig, publicClient, walletClient) {
|
|
1389
|
+
super(chainConfig);
|
|
1390
|
+
this.publicClient = publicClient;
|
|
1391
|
+
this.walletClient = walletClient;
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Create an EVM client from chain config and provider.
|
|
1395
|
+
*/
|
|
1396
|
+
static create(chainConfig, provider) {
|
|
1397
|
+
const viemChain = ViemHelper.toViemChain(chainConfig);
|
|
1398
|
+
return new EvmClient(chainConfig, createPublicClient({
|
|
1399
|
+
chain: viemChain,
|
|
1400
|
+
transport: custom(provider)
|
|
1401
|
+
}), createWalletClient({
|
|
1402
|
+
chain: viemChain,
|
|
1403
|
+
transport: custom(provider)
|
|
1404
|
+
}));
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* Create an EVM client with HTTP transport (for read-only operations).
|
|
1408
|
+
*/
|
|
1409
|
+
static createReadOnly(chainConfig) {
|
|
1410
|
+
const viemChain = ViemHelper.toViemChain(chainConfig);
|
|
1411
|
+
return new EvmClient(chainConfig, createPublicClient({
|
|
1412
|
+
chain: viemChain,
|
|
1413
|
+
transport: http(chainConfig.rpcUrls[0])
|
|
1414
|
+
}), createWalletClient({
|
|
1415
|
+
chain: viemChain,
|
|
1416
|
+
transport: http(chainConfig.rpcUrls[0])
|
|
1417
|
+
}));
|
|
1418
|
+
}
|
|
1419
|
+
getPublicClient() {
|
|
1420
|
+
return this.publicClient;
|
|
1421
|
+
}
|
|
1422
|
+
getWalletClient() {
|
|
1423
|
+
return this.walletClient;
|
|
1424
|
+
}
|
|
1425
|
+
async getAccount() {
|
|
1426
|
+
if (this.walletClient.account) return this.walletClient.account;
|
|
1427
|
+
const [address] = await this.walletClient.getAddresses();
|
|
1428
|
+
return address;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Get balance for an address in wei.
|
|
1432
|
+
*/
|
|
1433
|
+
async getBalance(address) {
|
|
1434
|
+
return this.publicClient.getBalance({ address });
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Get the EVM chain config.
|
|
1438
|
+
*/
|
|
1439
|
+
get evmChainConfig() {
|
|
1440
|
+
return this.chainConfig;
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1444
|
+
//#endregion
|
|
1445
|
+
//#region src/core/connectors/ledger/clients/ledger-evm-client.ts
|
|
1446
|
+
var LedgerEvmClient = class LedgerEvmClient extends EvmClient {
|
|
1447
|
+
constructor(chain, publicClient, walletClient, app) {
|
|
1448
|
+
super(chain, publicClient, walletClient);
|
|
1449
|
+
this._app = app;
|
|
1450
|
+
}
|
|
1451
|
+
static createForLedger(args, chain, account) {
|
|
1452
|
+
return new LedgerEvmClient(chain, ViemHelper.getViemPublicClientFromAccount(chain.caip2, account), ViemHelper.getViemWalletClientFromAccount(chain.caip2, account), args.app);
|
|
1453
|
+
}
|
|
1454
|
+
getLedgerApp() {
|
|
1455
|
+
return this._app;
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
//#endregion
|
|
1460
|
+
//#region src/core/connectors/ledger/clients/ledger-xrp-client.ts
|
|
1461
|
+
var LedgerXrpClient = class extends XrplClient {
|
|
1462
|
+
constructor(chain, args) {
|
|
1463
|
+
super(chain);
|
|
1464
|
+
this.args = args;
|
|
1465
|
+
}
|
|
1466
|
+
getLedgerApp() {
|
|
1467
|
+
return this.args.app;
|
|
1468
|
+
}
|
|
1469
|
+
async getAddressInfo() {
|
|
1470
|
+
const app = this.getLedgerApp();
|
|
1471
|
+
const path = this.args.bip44.getPathWithoutMaster();
|
|
1472
|
+
return app.getAddress(path);
|
|
1473
|
+
}
|
|
1474
|
+
async sendTx(tx) {
|
|
1475
|
+
await this.ensureConnected();
|
|
1476
|
+
const { publicKey } = await this.getAddressInfo();
|
|
1477
|
+
const prepared = await this.autofill(tx);
|
|
1478
|
+
prepared.SigningPubKey = publicKey.toUpperCase();
|
|
1479
|
+
const blob = encode(prepared);
|
|
1480
|
+
const app = this.getLedgerApp();
|
|
1481
|
+
const path = this.args.bip44.getPathWithoutMaster();
|
|
1482
|
+
prepared.TxnSignature = (await app.signTransaction(path, blob)).toUpperCase();
|
|
1483
|
+
return (await this.submitAndWait(prepared)).result.hash;
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
|
|
1487
|
+
//#endregion
|
|
1488
|
+
//#region src/core/connectors/ledger/internal/ledger-client-factory.ts
|
|
1489
|
+
var LedgerClientFactory = class {
|
|
1490
|
+
constructor(resolvedConfig) {
|
|
1491
|
+
this.resolvedConfig = resolvedConfig;
|
|
1492
|
+
}
|
|
1493
|
+
async create(chain, app, bip44) {
|
|
1494
|
+
if (ChainGuards.isXrplCaip2(chain)) return new LedgerXrpClient(this.resolvedConfig.getXrplChain(chain), {
|
|
1495
|
+
app,
|
|
1496
|
+
caip2: chain,
|
|
1497
|
+
bip44
|
|
1498
|
+
});
|
|
1499
|
+
if (ChainGuards.isEvmCaip2(chain)) {
|
|
1500
|
+
const evmChain = this.resolvedConfig.getEvmChain(chain);
|
|
1501
|
+
const viemAccount = await app.getViemAccount(bip44);
|
|
1502
|
+
return LedgerEvmClient.createForLedger({
|
|
1503
|
+
app,
|
|
1504
|
+
caip2: chain,
|
|
1505
|
+
bip44
|
|
1506
|
+
}, evmChain, viemAccount);
|
|
1507
|
+
}
|
|
1508
|
+
throw new LedgerError("COMMON_CHAIN_NOT_SUPPORTED");
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1512
|
+
//#endregion
|
|
1513
|
+
//#region src/core/storage/local-storage-manager.ts
|
|
1514
|
+
var LocalStorageManager = class {
|
|
1515
|
+
constructor(options) {
|
|
1516
|
+
this.prefix = options.prefix;
|
|
1517
|
+
this.separator = options.separator;
|
|
1518
|
+
}
|
|
1519
|
+
getPrefixedKey(key) {
|
|
1520
|
+
return `${this.prefix}${this.separator}${String(key)}`;
|
|
1521
|
+
}
|
|
1522
|
+
getKey(key) {
|
|
1523
|
+
return this.getPrefixedKey(key);
|
|
1524
|
+
}
|
|
1525
|
+
set(key, value) {
|
|
1526
|
+
const fullKey = this.getPrefixedKey(key);
|
|
1527
|
+
const stringifiedValue = typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
1528
|
+
try {
|
|
1529
|
+
localStorage.setItem(fullKey, stringifiedValue);
|
|
1530
|
+
} catch (error) {
|
|
1531
|
+
console.error(`LocalStorageManager: Failed to set "${fullKey}"`, error);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
get(key) {
|
|
1535
|
+
const fullKey = this.getPrefixedKey(key);
|
|
1536
|
+
const storedValue = localStorage.getItem(fullKey);
|
|
1537
|
+
if (storedValue === null) return null;
|
|
1538
|
+
try {
|
|
1539
|
+
return JSON.parse(storedValue);
|
|
1540
|
+
} catch {
|
|
1541
|
+
return storedValue;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
remove(key) {
|
|
1545
|
+
localStorage.removeItem(this.getPrefixedKey(key));
|
|
1546
|
+
}
|
|
1547
|
+
has(key) {
|
|
1548
|
+
return localStorage.getItem(this.getPrefixedKey(key)) !== null;
|
|
1549
|
+
}
|
|
1550
|
+
clear() {
|
|
1551
|
+
const prefixWithSeparator = `${this.prefix}${this.separator}`;
|
|
1552
|
+
const keysToRemove = [];
|
|
1553
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
1554
|
+
const key = localStorage.key(i);
|
|
1555
|
+
if (key?.startsWith(prefixWithSeparator)) keysToRemove.push(key);
|
|
1556
|
+
}
|
|
1557
|
+
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
//#endregion
|
|
1562
|
+
//#region src/core/storage/local-storage-schema.ts
|
|
1563
|
+
/**
|
|
1564
|
+
* Main storage instance for MWC.
|
|
1565
|
+
* All keys are prefixed with "mwc:".
|
|
1566
|
+
*/
|
|
1567
|
+
const mwcStorage = new LocalStorageManager({
|
|
1568
|
+
prefix: "mwc",
|
|
1569
|
+
separator: ":"
|
|
1570
|
+
});
|
|
1571
|
+
|
|
1572
|
+
//#endregion
|
|
1573
|
+
//#region src/core/connectors/ledger/internal/ledger-storage.ts
|
|
1574
|
+
var LedgerStorage = class {
|
|
1575
|
+
constructor(storage) {
|
|
1576
|
+
this.storage = storage;
|
|
1577
|
+
}
|
|
1578
|
+
setCaip2(caip2) {
|
|
1579
|
+
this.storage.set("ledger:caip2", caip2);
|
|
1580
|
+
}
|
|
1581
|
+
getCaip2() {
|
|
1582
|
+
return this.storage.get("ledger:caip2");
|
|
1583
|
+
}
|
|
1584
|
+
setBip44(bip44) {
|
|
1585
|
+
this.storage.set("ledger:bip44", bip44);
|
|
1586
|
+
}
|
|
1587
|
+
getBip44() {
|
|
1588
|
+
return this.storage.get("ledger:bip44");
|
|
1589
|
+
}
|
|
1590
|
+
setHDStandard(standard) {
|
|
1591
|
+
this.storage.set("ledger:hd-standard", standard);
|
|
1592
|
+
}
|
|
1593
|
+
getHDStandard() {
|
|
1594
|
+
return this.storage.get("ledger:hd-standard");
|
|
1595
|
+
}
|
|
1596
|
+
clear() {
|
|
1597
|
+
this.storage.remove("ledger:caip2");
|
|
1598
|
+
this.storage.remove("ledger:bip44");
|
|
1599
|
+
this.storage.remove("ledger:hd-standard");
|
|
1600
|
+
this.storage.remove("ledger:device-hid");
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
const ledgerStorage = new LedgerStorage(mwcStorage);
|
|
1604
|
+
|
|
1605
|
+
//#endregion
|
|
1606
|
+
//#region src/core/connectors/ledger/internal/ledger-session-manager.ts
|
|
1607
|
+
var LedgerSessionManager = class {
|
|
1608
|
+
constructor() {
|
|
1609
|
+
this.current = null;
|
|
1610
|
+
}
|
|
1611
|
+
save(session) {
|
|
1612
|
+
this.current = session;
|
|
1613
|
+
ledgerStorage.setCaip2(session.caip2);
|
|
1614
|
+
ledgerStorage.setBip44(session.bip44.getFullPath());
|
|
1615
|
+
ledgerStorage.setHDStandard(session.hdStandard);
|
|
1616
|
+
}
|
|
1617
|
+
loadSaved() {
|
|
1618
|
+
const caip2 = ledgerStorage.getCaip2();
|
|
1619
|
+
const bip44Path = ledgerStorage.getBip44();
|
|
1620
|
+
const hdStandard = ledgerStorage.getHDStandard();
|
|
1621
|
+
if (!caip2 || !bip44Path || !hdStandard) return null;
|
|
1622
|
+
try {
|
|
1623
|
+
return {
|
|
1624
|
+
caip2,
|
|
1625
|
+
bip44: new Bip44(bip44Path),
|
|
1626
|
+
hdStandard
|
|
1627
|
+
};
|
|
1628
|
+
} catch {
|
|
1629
|
+
ledgerStorage.clear();
|
|
1630
|
+
return null;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
getSession() {
|
|
1634
|
+
return this.current;
|
|
1635
|
+
}
|
|
1636
|
+
requireSession() {
|
|
1637
|
+
if (!this.current) throw new LedgerError("COMMON_NOT_CONNECTED");
|
|
1638
|
+
return this.current;
|
|
1639
|
+
}
|
|
1640
|
+
clear() {
|
|
1641
|
+
this.current = null;
|
|
1642
|
+
ledgerStorage.clear();
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
|
|
1646
|
+
//#endregion
|
|
1647
|
+
//#region src/core/connectors/ledger/internal/ledger-transport-manager.ts
|
|
1648
|
+
var LedgerTransportManager = class {
|
|
1649
|
+
constructor() {
|
|
1650
|
+
this.transport = null;
|
|
1651
|
+
this.disconnectHandler = null;
|
|
1652
|
+
}
|
|
1653
|
+
async open() {
|
|
1654
|
+
if (this.transport) {
|
|
1655
|
+
console.debug("[Ledger] TransportManager.open(): reusing existing transport");
|
|
1656
|
+
return this.transport;
|
|
1657
|
+
}
|
|
1658
|
+
console.debug("[Ledger] TransportManager.open(): checking for connected device");
|
|
1659
|
+
const existing = await TransportWebHID.openConnected();
|
|
1660
|
+
if (existing) console.debug("[Ledger] TransportManager.open(): found connected device");
|
|
1661
|
+
else console.debug("[Ledger] TransportManager.open(): creating new transport (will prompt user)");
|
|
1662
|
+
this.transport = existing ?? await TransportWebHID.create();
|
|
1663
|
+
this.attachDisconnectHandler();
|
|
1664
|
+
console.debug("[Ledger] TransportManager.open(): transport ready");
|
|
1665
|
+
return this.transport;
|
|
1666
|
+
}
|
|
1667
|
+
async checkDeviceStatus() {
|
|
1668
|
+
const devices = await TransportWebHID.list();
|
|
1669
|
+
console.debug("[Ledger] TransportManager.checkDeviceStatus(): devices found", devices.length);
|
|
1670
|
+
if (devices.length === 0) return { available: false };
|
|
1671
|
+
try {
|
|
1672
|
+
console.debug("[Ledger] TransportManager.checkDeviceStatus(): opening transport");
|
|
1673
|
+
const transport = await TransportWebHID.openConnected();
|
|
1674
|
+
if (!transport) {
|
|
1675
|
+
console.debug("[Ledger] TransportManager.checkDeviceStatus(): could not open transport");
|
|
1676
|
+
return { available: false };
|
|
1677
|
+
}
|
|
1678
|
+
this.transport = transport;
|
|
1679
|
+
this.attachDisconnectHandler();
|
|
1680
|
+
console.debug("[Ledger] TransportManager.checkDeviceStatus(): getting current app");
|
|
1681
|
+
const currentApp = await this.getCurrentApp();
|
|
1682
|
+
console.debug("[Ledger] TransportManager.checkDeviceStatus(): current app", currentApp.name);
|
|
1683
|
+
return {
|
|
1684
|
+
available: true,
|
|
1685
|
+
currentApp
|
|
1686
|
+
};
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
console.debug("[Ledger] TransportManager.checkDeviceStatus(): failed", error);
|
|
1689
|
+
return { available: false };
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
async getCurrentApp() {
|
|
1693
|
+
const transport = await this.open();
|
|
1694
|
+
console.debug("[Ledger] TransportManager.getCurrentApp(): sending APDU command");
|
|
1695
|
+
const response = await transport.send(176, 1, 0, 0);
|
|
1696
|
+
let index = 0;
|
|
1697
|
+
if (response[index++] !== 1) throw new LedgerError("LEDGER_APP_NOT_OPEN");
|
|
1698
|
+
const nameLength = response[index++];
|
|
1699
|
+
const nameStartIndex = index;
|
|
1700
|
+
index += nameLength;
|
|
1701
|
+
const name = response.subarray(nameStartIndex, index).toString("ascii");
|
|
1702
|
+
const versionLength = response[index++];
|
|
1703
|
+
const versionStartIndex = index;
|
|
1704
|
+
index += versionLength;
|
|
1705
|
+
const version = response.subarray(versionStartIndex, index).toString("ascii");
|
|
1706
|
+
const flagLength = response[index++];
|
|
1707
|
+
const flagsStartIndex = index;
|
|
1708
|
+
index += flagLength;
|
|
1709
|
+
const flags = response.subarray(flagsStartIndex, index);
|
|
1710
|
+
console.debug("[Ledger] TransportManager.getCurrentApp(): received", {
|
|
1711
|
+
name,
|
|
1712
|
+
version
|
|
1713
|
+
});
|
|
1714
|
+
return {
|
|
1715
|
+
name,
|
|
1716
|
+
version,
|
|
1717
|
+
flags
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
async close() {
|
|
1721
|
+
if (this.transport) {
|
|
1722
|
+
console.debug("[Ledger] TransportManager.close(): closing transport");
|
|
1723
|
+
await this.transport.close();
|
|
1724
|
+
this.transport = null;
|
|
1725
|
+
console.debug("[Ledger] TransportManager.close(): transport closed");
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
onDisconnect(handler) {
|
|
1729
|
+
this.disconnectHandler = handler;
|
|
1730
|
+
this.attachDisconnectHandler();
|
|
1731
|
+
}
|
|
1732
|
+
attachDisconnectHandler() {
|
|
1733
|
+
if (this.transport && this.disconnectHandler) this.transport.on("disconnect", this.disconnectHandler);
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
|
|
1737
|
+
//#endregion
|
|
1738
|
+
//#region src/core/connectors/ledger/ledger-connector.ts
|
|
1739
|
+
var LedgerConnector = class extends BaseConnector {
|
|
1740
|
+
constructor(store, config) {
|
|
1741
|
+
super(store, config);
|
|
1742
|
+
this.transportManager = new LedgerTransportManager();
|
|
1743
|
+
this.sessionManager = new LedgerSessionManager();
|
|
1744
|
+
this.mutex = new ConnectionMutex();
|
|
1745
|
+
this.client = null;
|
|
1746
|
+
this.clientFactory = new LedgerClientFactory(this.resolvedConfig);
|
|
1747
|
+
this.transportManager.onDisconnect(() => this.handleDisconnect());
|
|
1748
|
+
}
|
|
1749
|
+
async onInitialize() {}
|
|
1750
|
+
async connect(chain, options) {
|
|
1751
|
+
console.debug("[Ledger] connect() called", {
|
|
1752
|
+
chain,
|
|
1753
|
+
options
|
|
1754
|
+
});
|
|
1755
|
+
await this.mutex.run(() => this.performConnect(chain, options ?? {
|
|
1756
|
+
bip44: Bip44.fromCaip2(chain, 0, 0, 0),
|
|
1757
|
+
ledgerHDStandard: "bip44"
|
|
1758
|
+
}));
|
|
1759
|
+
}
|
|
1760
|
+
async disconnect() {
|
|
1761
|
+
this.events.startDisconnecting();
|
|
1762
|
+
await this.transportManager.close();
|
|
1763
|
+
this.client = null;
|
|
1764
|
+
this.sessionManager.clear();
|
|
1765
|
+
this.events.disconnected();
|
|
1766
|
+
}
|
|
1767
|
+
async restoreConnection() {
|
|
1768
|
+
console.debug("[Ledger] restoreConnection() called");
|
|
1769
|
+
if (this.client) {
|
|
1770
|
+
console.debug("[Ledger] restoreConnection(): client already exists, skipping");
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
const saved = this.sessionManager.loadSaved();
|
|
1774
|
+
if (!saved) {
|
|
1775
|
+
console.debug("[Ledger] restoreConnection(): no saved session, skipping");
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
const status = await this.transportManager.checkDeviceStatus();
|
|
1779
|
+
if (!status.available) {
|
|
1780
|
+
console.debug("[Ledger] restoreConnection(): device not available, skipping");
|
|
1781
|
+
return;
|
|
1782
|
+
}
|
|
1783
|
+
const requiredApp = LedgerAppFactory.getRequiredAppName(saved.caip2);
|
|
1784
|
+
if (status.currentApp?.name !== requiredApp) {
|
|
1785
|
+
console.debug("[Ledger] restoreConnection(): wrong app", {
|
|
1786
|
+
required: requiredApp,
|
|
1787
|
+
current: status.currentApp?.name
|
|
1788
|
+
});
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
console.debug("[Ledger] restoreConnection(): starting restore", { chain: saved.caip2 });
|
|
1792
|
+
await this.mutex.run(() => this.performRestore(saved));
|
|
1793
|
+
}
|
|
1794
|
+
async switchChain(chain) {
|
|
1795
|
+
const { bip44, hdStandard } = this.sessionManager.requireSession();
|
|
1796
|
+
await this.connect(chain, {
|
|
1797
|
+
bip44: Bip44.fromCaip2(chain, bip44.account, bip44.change, bip44.addressIndex),
|
|
1798
|
+
ledgerHDStandard: hdStandard
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
getSession() {
|
|
1802
|
+
return this.sessionManager.getSession();
|
|
1803
|
+
}
|
|
1804
|
+
getClient() {
|
|
1805
|
+
if (!this.client) throw new LedgerError("COMMON_NOT_CONNECTED");
|
|
1806
|
+
return this.client;
|
|
1807
|
+
}
|
|
1808
|
+
getProvider() {
|
|
1809
|
+
throw new LedgerError("COMMON_FEATURE_NOT_SUPPORTED");
|
|
1810
|
+
}
|
|
1811
|
+
async fetchAddressesForSelection(chain, paths) {
|
|
1812
|
+
const transport = await this.transportManager.open();
|
|
1813
|
+
await this.requireApp(chain);
|
|
1814
|
+
return LedgerAppFactory.createApp(chain, transport).getComputedAddresses(paths, this.resolvedConfig.getChain(chain).ledgerHrp);
|
|
1815
|
+
}
|
|
1816
|
+
async performConnect(chain, options) {
|
|
1817
|
+
console.debug("[Ledger] performConnect() started", {
|
|
1818
|
+
chain,
|
|
1819
|
+
bip44: options.bip44
|
|
1820
|
+
});
|
|
1821
|
+
this.events.startConnecting();
|
|
1822
|
+
try {
|
|
1823
|
+
const { account, client } = await this.establishConnection(chain, options);
|
|
1824
|
+
this.client = client;
|
|
1825
|
+
this.sessionManager.save({
|
|
1826
|
+
caip2: chain,
|
|
1827
|
+
bip44: options.bip44,
|
|
1828
|
+
hdStandard: options.ledgerHDStandard,
|
|
1829
|
+
account
|
|
1830
|
+
});
|
|
1831
|
+
this.events.connected({
|
|
1832
|
+
address: account.address,
|
|
1833
|
+
addresses: [account.address],
|
|
1834
|
+
caip2: chain
|
|
1835
|
+
});
|
|
1836
|
+
console.debug("[Ledger] performConnect() completed", { address: account.address });
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
console.debug("[Ledger] performConnect() failed", error);
|
|
1839
|
+
this.events.disconnected();
|
|
1840
|
+
throw error;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
async performRestore(saved) {
|
|
1844
|
+
console.debug("[Ledger] performRestore() started", {
|
|
1845
|
+
chain: saved.caip2,
|
|
1846
|
+
bip44: saved.bip44
|
|
1847
|
+
});
|
|
1848
|
+
try {
|
|
1849
|
+
const { account, client } = await this.establishConnection(saved.caip2, {
|
|
1850
|
+
bip44: saved.bip44,
|
|
1851
|
+
ledgerHDStandard: saved.hdStandard
|
|
1852
|
+
});
|
|
1853
|
+
this.client = client;
|
|
1854
|
+
this.sessionManager.save({
|
|
1855
|
+
caip2: saved.caip2,
|
|
1856
|
+
bip44: saved.bip44,
|
|
1857
|
+
hdStandard: saved.hdStandard,
|
|
1858
|
+
account
|
|
1859
|
+
});
|
|
1860
|
+
this.events.reconnected({
|
|
1861
|
+
address: account.address,
|
|
1862
|
+
addresses: [account.address],
|
|
1863
|
+
caip2: saved.caip2
|
|
1864
|
+
});
|
|
1865
|
+
console.debug("[Ledger] performRestore() completed", { address: account.address });
|
|
1866
|
+
} catch (error) {
|
|
1867
|
+
console.debug("[Ledger] performRestore() failed", error);
|
|
1868
|
+
await this.transportManager.close();
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
async establishConnection(chain, options) {
|
|
1872
|
+
console.debug("[Ledger] establishConnection() started", { chain });
|
|
1873
|
+
try {
|
|
1874
|
+
const transport = await this.transportManager.open();
|
|
1875
|
+
console.debug("[Ledger] establishConnection(): transport opened");
|
|
1876
|
+
await this.requireApp(chain);
|
|
1877
|
+
console.debug("[Ledger] establishConnection(): app verified");
|
|
1878
|
+
const app = LedgerAppFactory.createApp(chain, transport);
|
|
1879
|
+
const hrp = this.resolvedConfig.getChain(chain).ledgerHrp;
|
|
1880
|
+
const result = {
|
|
1881
|
+
account: await app.getComputedAddress(options.bip44, hrp),
|
|
1882
|
+
client: await this.clientFactory.create(chain, app, options.bip44)
|
|
1883
|
+
};
|
|
1884
|
+
console.debug("[Ledger] establishConnection() completed");
|
|
1885
|
+
return result;
|
|
1886
|
+
} catch (error) {
|
|
1887
|
+
throw LedgerError.fromError(error) ?? error;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
handleDisconnect() {
|
|
1891
|
+
this.client = null;
|
|
1892
|
+
this.sessionManager.clear();
|
|
1893
|
+
this.events.disconnected();
|
|
1894
|
+
}
|
|
1895
|
+
async requireApp(chain) {
|
|
1896
|
+
const requiredApp = LedgerAppFactory.getRequiredAppName(chain);
|
|
1897
|
+
const currentApp = await this.transportManager.getCurrentApp();
|
|
1898
|
+
if (currentApp.name !== requiredApp) throw new LedgerError("LEDGER_WRONG_APP", /* @__PURE__ */ new Error(`Please open the ${requiredApp} app on your Ledger. Currently running: ${currentApp.name}`));
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
|
|
1902
|
+
//#endregion
|
|
1903
|
+
//#region src/core/connectors/metamask/metamask-connector.ts
|
|
1904
|
+
const RPC_ERROR = {
|
|
1905
|
+
USER_REJECTED: 4001,
|
|
1906
|
+
REQUEST_PENDING: -32002,
|
|
1907
|
+
CHAIN_NOT_ADDED: 4902
|
|
1908
|
+
};
|
|
1909
|
+
var MetaMaskConnector = class extends BaseConnector {
|
|
1910
|
+
constructor(..._args) {
|
|
1911
|
+
super(..._args);
|
|
1912
|
+
this.client = null;
|
|
1913
|
+
}
|
|
1914
|
+
async onInitialize() {
|
|
1915
|
+
this.sdk = new MetaMaskSDK({ dappMetadata: {
|
|
1916
|
+
name: this.config.metadata.name,
|
|
1917
|
+
url: this.config.metadata.url
|
|
1918
|
+
} });
|
|
1919
|
+
await this.sdk.sdkInitPromise;
|
|
1920
|
+
const provider = this.sdk.getProvider();
|
|
1921
|
+
if (!provider) throw new MetaMaskError("METAMASK_NOT_INSTALLED");
|
|
1922
|
+
this.provider = provider;
|
|
1923
|
+
this.provider.on("accountsChanged", (accounts) => this.onAccountsChanged(accounts));
|
|
1924
|
+
this.provider.on("chainChanged", (hex) => this.onChainChanged(hex));
|
|
1925
|
+
this.provider.on("disconnect", () => {
|
|
1926
|
+
if (this.getConnectionState() === "connected") {
|
|
1927
|
+
this.client = null;
|
|
1928
|
+
this.events.disconnected();
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
async connect(chain) {
|
|
1933
|
+
if (this.getConnectionState() === "connecting") throw new MetaMaskError("COMMON_CONNECTION_IN_PROGRESS");
|
|
1934
|
+
await this.initialize();
|
|
1935
|
+
this.events.startConnecting();
|
|
1936
|
+
let accounts = await this.provider.request({ method: "eth_accounts" });
|
|
1937
|
+
if (accounts.length === 0) {
|
|
1938
|
+
const connected = await this.sdk.connect().catch((error) => {
|
|
1939
|
+
this.events.disconnected();
|
|
1940
|
+
if (ChainUtils.isRpcErrorWithCode(error, RPC_ERROR.USER_REJECTED)) throw new MetaMaskError("COMMON_USER_REJECTED", error);
|
|
1941
|
+
if (ChainUtils.isRpcErrorWithCode(error, RPC_ERROR.REQUEST_PENDING)) throw new MetaMaskError("COMMON_CONNECTION_IN_PROGRESS", error);
|
|
1942
|
+
throw new MetaMaskError("COMMON_CONNECTION_FAILED", error);
|
|
1943
|
+
});
|
|
1944
|
+
if (!connected?.length) {
|
|
1945
|
+
this.events.disconnected();
|
|
1946
|
+
throw new MetaMaskError("COMMON_NO_ACCOUNTS");
|
|
1947
|
+
}
|
|
1948
|
+
accounts = connected;
|
|
1949
|
+
}
|
|
1950
|
+
if (chain && !ChainGuards.isEvmCaip2(chain)) {
|
|
1951
|
+
this.events.disconnected();
|
|
1952
|
+
throw new MetaMaskError("COMMON_CHAIN_NOT_SUPPORTED");
|
|
1953
|
+
}
|
|
1954
|
+
const hex = await this.provider.request({ method: "eth_chainId" });
|
|
1955
|
+
const currentChain = this.resolvedConfig.evmHexMap.get(hex);
|
|
1956
|
+
if (chain && currentChain !== chain) await this.switchChain(chain);
|
|
1957
|
+
else if (!currentChain) {
|
|
1958
|
+
this.events.disconnected();
|
|
1959
|
+
throw new MetaMaskError("COMMON_UNKNOWN_CHAIN_ID");
|
|
1960
|
+
}
|
|
1961
|
+
const caip2 = chain ?? currentChain;
|
|
1962
|
+
this.createClient(caip2);
|
|
1963
|
+
this.events.connected({
|
|
1964
|
+
address: accounts[0],
|
|
1965
|
+
addresses: accounts,
|
|
1966
|
+
caip2
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
async disconnect() {
|
|
1970
|
+
this.events.startDisconnecting();
|
|
1971
|
+
if (this.initialized) await this.sdk.terminate();
|
|
1972
|
+
this.client = null;
|
|
1973
|
+
this.resetInitialization();
|
|
1974
|
+
this.events.disconnected();
|
|
1975
|
+
}
|
|
1976
|
+
async restoreConnection() {
|
|
1977
|
+
if (this.getConnectionState() !== "disconnected") return;
|
|
1978
|
+
await this.initialize();
|
|
1979
|
+
if (!this.provider) return;
|
|
1980
|
+
if (this.getConnectionState() !== "disconnected") return;
|
|
1981
|
+
const accounts = await this.provider.request({ method: "eth_accounts" });
|
|
1982
|
+
if (accounts.length === 0 || this.getConnectionState() !== "disconnected") return;
|
|
1983
|
+
const hex = await this.provider.request({ method: "eth_chainId" });
|
|
1984
|
+
const caip2 = this.resolvedConfig.evmHexMap.get(hex);
|
|
1985
|
+
if (!caip2) return;
|
|
1986
|
+
this.createClient(caip2);
|
|
1987
|
+
this.events.reconnected({
|
|
1988
|
+
address: accounts[0],
|
|
1989
|
+
addresses: accounts,
|
|
1990
|
+
caip2
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
async switchChain(caip2) {
|
|
1994
|
+
const chain = this.resolvedConfig.getEvmChain(caip2);
|
|
1995
|
+
try {
|
|
1996
|
+
await this.provider.request({
|
|
1997
|
+
method: "wallet_switchEthereumChain",
|
|
1998
|
+
params: [{ chainId: chain.evmChain.hex }]
|
|
1999
|
+
});
|
|
2000
|
+
} catch (error) {
|
|
2001
|
+
if (ChainUtils.isRpcErrorWithCode(error, RPC_ERROR.CHAIN_NOT_ADDED)) await this.addChain(chain);
|
|
2002
|
+
else if (ChainUtils.isRpcErrorWithCode(error, RPC_ERROR.USER_REJECTED)) throw new MetaMaskError("COMMON_USER_REJECTED", error);
|
|
2003
|
+
else throw new MetaMaskError("COMMON_CHAIN_NOT_SUPPORTED", error);
|
|
2004
|
+
}
|
|
2005
|
+
this.createClient(caip2);
|
|
2006
|
+
}
|
|
2007
|
+
getClient() {
|
|
2008
|
+
if (!this.client) throw new MetaMaskError("COMMON_NOT_CONNECTED");
|
|
2009
|
+
return this.client;
|
|
2010
|
+
}
|
|
2011
|
+
getProvider() {
|
|
2012
|
+
return this.provider;
|
|
2013
|
+
}
|
|
2014
|
+
onAccountsChanged(accounts) {
|
|
2015
|
+
if (accounts.length === 0) {
|
|
2016
|
+
if (this.getConnectionState() !== "connected") return;
|
|
2017
|
+
this.events.startDisconnecting();
|
|
2018
|
+
this.events.disconnected();
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
this.provider.request({ method: "eth_chainId" }).then((hex) => {
|
|
2022
|
+
const caip2 = this.resolvedConfig.evmHexMap.get(hex);
|
|
2023
|
+
if (!caip2) return;
|
|
2024
|
+
if (this.getConnectionState() === "disconnected") {
|
|
2025
|
+
this.events.startConnecting();
|
|
2026
|
+
this.createClient(caip2);
|
|
2027
|
+
this.events.connected({
|
|
2028
|
+
address: accounts[0],
|
|
2029
|
+
addresses: accounts,
|
|
2030
|
+
caip2
|
|
2031
|
+
});
|
|
2032
|
+
} else this.events.addressChanged({
|
|
2033
|
+
address: accounts[0],
|
|
2034
|
+
addresses: accounts,
|
|
2035
|
+
caip2
|
|
2036
|
+
});
|
|
2037
|
+
}).catch((error) => {
|
|
2038
|
+
this.emitError(error);
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
onChainChanged(hex) {
|
|
2042
|
+
const caip2 = this.resolvedConfig.evmHexMap.get(hex);
|
|
2043
|
+
if (!caip2) {
|
|
2044
|
+
this.client = null;
|
|
2045
|
+
this.events.disconnected();
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
if (this.getCurrentCaip2() !== caip2) {
|
|
2049
|
+
this.createClient(caip2);
|
|
2050
|
+
this.events.chainChanged(caip2);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
createClient(caip2) {
|
|
2054
|
+
this.client = EvmClient.create(this.resolvedConfig.getEvmChain(caip2), this.provider);
|
|
2055
|
+
}
|
|
2056
|
+
async addChain(chain) {
|
|
2057
|
+
await this.provider.request({
|
|
2058
|
+
method: "wallet_addEthereumChain",
|
|
2059
|
+
params: [{
|
|
2060
|
+
chainId: chain.evmChain.hex,
|
|
2061
|
+
chainName: chain.name,
|
|
2062
|
+
nativeCurrency: chain.nativeCurrency,
|
|
2063
|
+
rpcUrls: chain.rpcUrls,
|
|
2064
|
+
blockExplorerUrls: chain.blockExplorerUrls
|
|
2065
|
+
}]
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
getConnectionState() {
|
|
2069
|
+
return this.store.getSnapshot().context.connectionState;
|
|
2070
|
+
}
|
|
2071
|
+
getCurrentCaip2() {
|
|
2072
|
+
return this.store.getSnapshot().context.caip2;
|
|
2073
|
+
}
|
|
2074
|
+
};
|
|
2075
|
+
|
|
2076
|
+
//#endregion
|
|
2077
|
+
//#region src/core/connectors/wallet-connect/clients/wallet-connect-evm-client.ts
|
|
2078
|
+
/**
|
|
2079
|
+
* WalletConnect EVM client with hybrid transport.
|
|
2080
|
+
* Reads go direct to RPC (avoids Reown unsupported chain issue),
|
|
2081
|
+
* writes/signing go through WalletConnect provider.
|
|
2082
|
+
*/
|
|
2083
|
+
var WalletConnectEvmClient = class WalletConnectEvmClient extends EvmClient {
|
|
2084
|
+
constructor(chain, publicClient, walletClient) {
|
|
2085
|
+
super(chain, publicClient, walletClient);
|
|
2086
|
+
}
|
|
2087
|
+
static createWithProvider(chainConfig, provider) {
|
|
2088
|
+
const viemChain = ViemHelper.toViemChain(chainConfig);
|
|
2089
|
+
return new WalletConnectEvmClient(chainConfig, createPublicClient({
|
|
2090
|
+
chain: viemChain,
|
|
2091
|
+
transport: http(chainConfig.rpcUrls[0])
|
|
2092
|
+
}), createWalletClient({
|
|
2093
|
+
chain: viemChain,
|
|
2094
|
+
transport: custom(provider)
|
|
2095
|
+
}));
|
|
2096
|
+
}
|
|
2097
|
+
};
|
|
2098
|
+
|
|
2099
|
+
//#endregion
|
|
2100
|
+
//#region src/core/connectors/wallet-connect/clients/wallet-connect-xrpl-client.ts
|
|
2101
|
+
var WalletConnectXrpClient = class extends XrplClient {
|
|
2102
|
+
constructor(chain, provider) {
|
|
2103
|
+
super(chain);
|
|
2104
|
+
this.provider = provider;
|
|
2105
|
+
}
|
|
2106
|
+
async sendTx(tx) {
|
|
2107
|
+
await this.connect();
|
|
2108
|
+
try {
|
|
2109
|
+
const prepared = await this.autofill(tx);
|
|
2110
|
+
const xrpCaip2 = await this.getCaip2();
|
|
2111
|
+
const result = await this.provider.request({
|
|
2112
|
+
method: "xrpl_signTransaction",
|
|
2113
|
+
params: {
|
|
2114
|
+
tx_json: prepared,
|
|
2115
|
+
submit: false
|
|
2116
|
+
}
|
|
2117
|
+
}, xrpCaip2);
|
|
2118
|
+
return (await this.submitAndWait(result.tx_json)).result.hash;
|
|
2119
|
+
} finally {
|
|
2120
|
+
await this.disconnect();
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
};
|
|
2124
|
+
|
|
2125
|
+
//#endregion
|
|
2126
|
+
//#region src/core/connectors/wallet-connect/internal/wallet-connect-client-factory.ts
|
|
2127
|
+
var WalletConnectClientFactory = class {
|
|
2128
|
+
constructor(provider, resolvedConfig) {
|
|
2129
|
+
this.provider = provider;
|
|
2130
|
+
this.resolvedConfig = resolvedConfig;
|
|
2131
|
+
}
|
|
2132
|
+
create(caip2) {
|
|
2133
|
+
if (ChainUtils.isEvmCaip2(caip2)) {
|
|
2134
|
+
const chain = this.resolvedConfig.getEvmChain(caip2);
|
|
2135
|
+
return WalletConnectEvmClient.createWithProvider(chain, this.provider);
|
|
2136
|
+
}
|
|
2137
|
+
if (ChainUtils.isXrplCaip2(caip2)) return new WalletConnectXrpClient(this.resolvedConfig.getXrplChain(caip2), this.provider);
|
|
2138
|
+
throw new WalletConnectError("COMMON_CHAIN_NOT_SUPPORTED");
|
|
2139
|
+
}
|
|
2140
|
+
};
|
|
2141
|
+
|
|
2142
|
+
//#endregion
|
|
2143
|
+
//#region src/core/connectors/wallet-connect/internal/wallet-connect-session-storage.ts
|
|
2144
|
+
var SessionStorage = class {
|
|
2145
|
+
constructor(storage) {
|
|
2146
|
+
this.storage = storage;
|
|
2147
|
+
}
|
|
2148
|
+
setLastSession(session) {
|
|
2149
|
+
this.storage.set("wc:last-session", session);
|
|
2150
|
+
}
|
|
2151
|
+
getLastSession() {
|
|
2152
|
+
return this.storage.get("wc:last-session");
|
|
2153
|
+
}
|
|
2154
|
+
setLastAddress(address) {
|
|
2155
|
+
this.storage.set("wc:last-address", address);
|
|
2156
|
+
}
|
|
2157
|
+
getLastAddress() {
|
|
2158
|
+
return this.storage.get("wc:last-address");
|
|
2159
|
+
}
|
|
2160
|
+
setLastChain(caip2) {
|
|
2161
|
+
this.storage.set("wc:last-chain", caip2);
|
|
2162
|
+
}
|
|
2163
|
+
getLastChain() {
|
|
2164
|
+
return this.storage.get("wc:last-chain");
|
|
2165
|
+
}
|
|
2166
|
+
clear() {
|
|
2167
|
+
this.storage.remove("wc:last-session");
|
|
2168
|
+
this.storage.remove("wc:last-address");
|
|
2169
|
+
this.storage.remove("wc:last-chain");
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
const sessionStorage = new SessionStorage(mwcStorage);
|
|
2173
|
+
|
|
2174
|
+
//#endregion
|
|
2175
|
+
//#region src/core/connectors/wallet-connect/internal/wallet-connect-session-manager.ts
|
|
2176
|
+
var WalletConnectSessionManager = class {
|
|
2177
|
+
constructor(provider) {
|
|
2178
|
+
this.provider = provider;
|
|
2179
|
+
}
|
|
2180
|
+
parseSession() {
|
|
2181
|
+
const session = this.provider.session;
|
|
2182
|
+
if (!session) throw new WalletConnectError("WALLET_CONNECT_SESSION_NOT_FOUND");
|
|
2183
|
+
const availableAddresses = [];
|
|
2184
|
+
const eip155 = session.namespaces.eip155;
|
|
2185
|
+
if (eip155?.accounts) for (const account of eip155.accounts) {
|
|
2186
|
+
const { namespace, reference, address } = parseCaip10(account);
|
|
2187
|
+
availableAddresses.push({
|
|
2188
|
+
type: "wallet-connect",
|
|
2189
|
+
address,
|
|
2190
|
+
caip2: `${namespace}:${reference}`,
|
|
2191
|
+
isEvm: true
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
const xrpl = session.namespaces.xrpl;
|
|
2195
|
+
if (xrpl?.accounts) for (const account of xrpl.accounts) {
|
|
2196
|
+
const { namespace, reference, address } = parseCaip10(account);
|
|
2197
|
+
availableAddresses.push({
|
|
2198
|
+
type: "wallet-connect",
|
|
2199
|
+
address,
|
|
2200
|
+
caip2: `${namespace}:${reference}`,
|
|
2201
|
+
isEvm: false
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
return {
|
|
2205
|
+
availableAddresses,
|
|
2206
|
+
selectedAddress: availableAddresses[0]
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
resolveSessionWithStorage(parsedSession) {
|
|
2210
|
+
const lastAddress = sessionStorage.getLastAddress();
|
|
2211
|
+
const lastChain = sessionStorage.getLastChain();
|
|
2212
|
+
if (lastAddress && lastChain) {
|
|
2213
|
+
const validAddress = parsedSession.availableAddresses.find((addressState) => addressState.address === lastAddress && addressState.caip2 === lastChain);
|
|
2214
|
+
if (validAddress) return {
|
|
2215
|
+
availableAddresses: parsedSession.availableAddresses,
|
|
2216
|
+
selectedAddress: validAddress
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
return parsedSession;
|
|
2220
|
+
}
|
|
2221
|
+
saveLastConnection(address) {
|
|
2222
|
+
sessionStorage.setLastAddress(address.address);
|
|
2223
|
+
sessionStorage.setLastChain(address.caip2);
|
|
2224
|
+
}
|
|
2225
|
+
buildNamespaces(chains) {
|
|
2226
|
+
const evmChains = chains.filter(ChainUtils.isEvmCaip2);
|
|
2227
|
+
const xrplChains = chains.filter(ChainUtils.isXrplCaip2);
|
|
2228
|
+
const namespaces = {};
|
|
2229
|
+
if (evmChains.length > 0) namespaces.eip155 = {
|
|
2230
|
+
chains: evmChains,
|
|
2231
|
+
methods: [
|
|
2232
|
+
"eth_sendTransaction",
|
|
2233
|
+
"eth_signTransaction",
|
|
2234
|
+
"eth_sign",
|
|
2235
|
+
"personal_sign",
|
|
2236
|
+
"eth_signTypedData"
|
|
2237
|
+
],
|
|
2238
|
+
events: ["chainChanged", "accountsChanged"]
|
|
2239
|
+
};
|
|
2240
|
+
if (xrplChains.length > 0) namespaces.xrpl = {
|
|
2241
|
+
chains: xrplChains,
|
|
2242
|
+
methods: ["xrpl_signTransaction"],
|
|
2243
|
+
events: []
|
|
2244
|
+
};
|
|
2245
|
+
return namespaces;
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2249
|
+
//#endregion
|
|
2250
|
+
//#region src/core/connectors/wallet-connect/wallet-connect-connector.ts
|
|
2251
|
+
/**
|
|
2252
|
+
* WalletConnect connector using Reown AppKit for QR modal.
|
|
2253
|
+
*
|
|
2254
|
+
* - connect() opens AppKit first (spinner), then provider.connect() emits display_uri
|
|
2255
|
+
* which auto-populates the QR in the modal. The empty display_uri handler is required.
|
|
2256
|
+
* - disconnect() cleans up the WC session, but provider and AppKit survive —
|
|
2257
|
+
* no re-initialization needed (unlike MetaMask/Xaman).
|
|
2258
|
+
* - Only EVM chains are registered with AppKit (ChainNamespace doesn't include "xrpl").
|
|
2259
|
+
* XRPL sessions still work fine without AppKit awareness.
|
|
2260
|
+
*/
|
|
2261
|
+
var WalletConnectConnector = class extends BaseConnector {
|
|
2262
|
+
constructor(store, config) {
|
|
2263
|
+
super(store, config);
|
|
2264
|
+
this.client = null;
|
|
2265
|
+
this.config = config;
|
|
2266
|
+
}
|
|
2267
|
+
async onInitialize() {
|
|
2268
|
+
this.provider = await UniversalProvider.init({
|
|
2269
|
+
projectId: this.config.projectId,
|
|
2270
|
+
metadata: {
|
|
2271
|
+
name: this.config.metadata.name,
|
|
2272
|
+
description: this.config.metadata.description ?? "",
|
|
2273
|
+
url: this.config.metadata.url,
|
|
2274
|
+
icons: this.config.metadata.icons ?? []
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2277
|
+
this.sessionManager = new WalletConnectSessionManager(this.provider);
|
|
2278
|
+
this.clientFactory = new WalletConnectClientFactory(this.provider, this.resolvedConfig);
|
|
2279
|
+
this.appKit = createAppKit({
|
|
2280
|
+
projectId: this.config.projectId,
|
|
2281
|
+
universalProvider: this.provider,
|
|
2282
|
+
manualWCControl: true,
|
|
2283
|
+
networks: this.buildAppKitNetworks(),
|
|
2284
|
+
customRpcUrls: this.buildCustomRpcUrls()
|
|
2285
|
+
});
|
|
2286
|
+
this.registerProviderEvents();
|
|
2287
|
+
this.registerModalCloseHandler();
|
|
2288
|
+
}
|
|
2289
|
+
async connect(chains) {
|
|
2290
|
+
if (this.connectionState === "connecting") throw new WalletConnectError("COMMON_CONNECTION_IN_PROGRESS");
|
|
2291
|
+
if (!chains?.length) throw new WalletConnectError("COMMON_CHAIN_NOT_CONFIGURED");
|
|
2292
|
+
await this.initialize();
|
|
2293
|
+
this.events.startConnecting();
|
|
2294
|
+
await this.appKit.open();
|
|
2295
|
+
this.getProvider().connect({ optionalNamespaces: this.sessionManager.buildNamespaces([...chains]) }).then((session) => this.onSessionCreated(session)).catch((error) => this.onConnectionFailed(error));
|
|
2296
|
+
}
|
|
2297
|
+
async disconnect() {
|
|
2298
|
+
this.events.startDisconnecting();
|
|
2299
|
+
sessionStorage.clear();
|
|
2300
|
+
if (this.initialized && this.provider?.session) await this.provider.disconnect();
|
|
2301
|
+
this.client = null;
|
|
2302
|
+
this.events.disconnected();
|
|
2303
|
+
}
|
|
2304
|
+
async restoreConnection() {
|
|
2305
|
+
if (this.connectionState !== "disconnected") return;
|
|
2306
|
+
await this.initialize();
|
|
2307
|
+
if (!this.provider?.session) return;
|
|
2308
|
+
if (this.connectionState !== "disconnected") return;
|
|
2309
|
+
const parsedSession = this.sessionManager.parseSession();
|
|
2310
|
+
const resolved = this.sessionManager.resolveSessionWithStorage(parsedSession);
|
|
2311
|
+
if (!resolved.selectedAddress) return;
|
|
2312
|
+
this.client = this.clientFactory.create(resolved.selectedAddress.caip2);
|
|
2313
|
+
this.events.reconnected({
|
|
2314
|
+
address: resolved.selectedAddress.address,
|
|
2315
|
+
addresses: resolved.availableAddresses.map((address) => address.address),
|
|
2316
|
+
caip2: resolved.selectedAddress.caip2
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
async switchChain(caip2) {
|
|
2320
|
+
if (!this.provider.session) throw new WalletConnectError("WALLET_CONNECT_SESSION_NOT_FOUND");
|
|
2321
|
+
const targetAddress = this.sessionManager.parseSession().availableAddresses.find((address) => address.caip2 === caip2);
|
|
2322
|
+
if (!targetAddress) return;
|
|
2323
|
+
this.sessionManager.saveLastConnection(targetAddress);
|
|
2324
|
+
this.client = this.clientFactory.create(caip2);
|
|
2325
|
+
this.events.chainChanged(caip2);
|
|
2326
|
+
}
|
|
2327
|
+
getClient() {
|
|
2328
|
+
if (!this.client) throw new WalletConnectError("COMMON_NOT_CONNECTED");
|
|
2329
|
+
return this.client;
|
|
2330
|
+
}
|
|
2331
|
+
getProvider() {
|
|
2332
|
+
return this.provider;
|
|
2333
|
+
}
|
|
2334
|
+
registerProviderEvents() {
|
|
2335
|
+
this.provider.on("display_uri", (_uri) => {});
|
|
2336
|
+
this.provider.on("disconnect", (error) => this.onDisconnect(error));
|
|
2337
|
+
this.provider.on("session_delete", () => this.onDisconnect());
|
|
2338
|
+
this.provider.on("chainChanged", (chainId) => this.onChainChanged(chainId));
|
|
2339
|
+
this.provider.on("accountsChanged", (accounts) => this.onAccountsChanged(accounts));
|
|
2340
|
+
this.provider.client?.on("proposal_expire", (event) => this.onProposalExpire(event));
|
|
2341
|
+
}
|
|
2342
|
+
registerModalCloseHandler() {
|
|
2343
|
+
this.appKit.subscribeState((state) => {
|
|
2344
|
+
if (!state.open && this.connectionState === "connecting") this.events.disconnected();
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
onSessionCreated(session) {
|
|
2348
|
+
this.appKit.close();
|
|
2349
|
+
if (!session) {
|
|
2350
|
+
this.events.disconnected();
|
|
2351
|
+
this.emitError(new WalletConnectError("WALLET_CONNECT_PAIRING_FAILED"));
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
const parsedSession = this.sessionManager.parseSession();
|
|
2355
|
+
if (!parsedSession.selectedAddress) {
|
|
2356
|
+
this.events.disconnected();
|
|
2357
|
+
this.emitError(new WalletConnectError("COMMON_NO_ACCOUNTS"));
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
this.client = this.clientFactory.create(parsedSession.selectedAddress.caip2);
|
|
2361
|
+
this.sessionManager.saveLastConnection(parsedSession.selectedAddress);
|
|
2362
|
+
this.events.connected({
|
|
2363
|
+
address: parsedSession.selectedAddress.address,
|
|
2364
|
+
addresses: parsedSession.availableAddresses.map((address) => address.address),
|
|
2365
|
+
caip2: parsedSession.selectedAddress.caip2
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2368
|
+
onConnectionFailed(error) {
|
|
2369
|
+
this.appKit.close();
|
|
2370
|
+
if (this.connectionState === "connecting") this.events.disconnected();
|
|
2371
|
+
if (error instanceof Error && error.message.includes("Proposal expired")) return;
|
|
2372
|
+
this.emitError(error);
|
|
2373
|
+
}
|
|
2374
|
+
onProposalExpire(event) {
|
|
2375
|
+
console.log("[WC] proposal_expire", event);
|
|
2376
|
+
if (this.connectionState !== "connecting") return;
|
|
2377
|
+
this.appKit.close();
|
|
2378
|
+
this.events.disconnected();
|
|
2379
|
+
}
|
|
2380
|
+
onAccountsChanged(accounts) {
|
|
2381
|
+
if (this.connectionState !== "connected") return;
|
|
2382
|
+
if (!accounts.length) {
|
|
2383
|
+
this.events.disconnected();
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
const parsedSession = this.sessionManager.parseSession();
|
|
2387
|
+
const newAddress = accounts[0];
|
|
2388
|
+
const matched = parsedSession.availableAddresses.find((address) => address.address === newAddress);
|
|
2389
|
+
if (!matched) return;
|
|
2390
|
+
this.events.addressChanged({
|
|
2391
|
+
address: newAddress,
|
|
2392
|
+
addresses: parsedSession.availableAddresses.map((address) => address.address),
|
|
2393
|
+
caip2: matched.caip2
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
onChainChanged(chainId) {
|
|
2397
|
+
const caip2 = chainId;
|
|
2398
|
+
if (this.storedCaip2 === caip2) return;
|
|
2399
|
+
this.client = this.clientFactory.create(caip2);
|
|
2400
|
+
this.events.chainChanged(caip2);
|
|
2401
|
+
}
|
|
2402
|
+
onDisconnect(error) {
|
|
2403
|
+
if (error) console.warn("[WC] disconnect error:", error);
|
|
2404
|
+
this.client = null;
|
|
2405
|
+
this.events.disconnected();
|
|
2406
|
+
}
|
|
2407
|
+
buildAppKitNetworks() {
|
|
2408
|
+
const networks = [];
|
|
2409
|
+
for (const [caip2, chain] of objectEntries(this.resolvedConfig.chains)) {
|
|
2410
|
+
if (!chain.isEvm) continue;
|
|
2411
|
+
const { reference } = parseCaip2(caip2);
|
|
2412
|
+
if (!reference) continue;
|
|
2413
|
+
networks.push({
|
|
2414
|
+
id: Number(reference),
|
|
2415
|
+
name: chain.name,
|
|
2416
|
+
chainNamespace: "eip155",
|
|
2417
|
+
caipNetworkId: `eip155:${reference}`,
|
|
2418
|
+
nativeCurrency: chain.nativeCurrency,
|
|
2419
|
+
rpcUrls: { default: { http: [...chain.rpcUrls] } }
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
const [first, ...rest] = networks;
|
|
2423
|
+
if (!first) throw new WalletConnectError("COMMON_CHAIN_NOT_CONFIGURED");
|
|
2424
|
+
return [first, ...rest];
|
|
2425
|
+
}
|
|
2426
|
+
buildCustomRpcUrls() {
|
|
2427
|
+
const rpcUrls = {};
|
|
2428
|
+
for (const [caip2, chain] of objectEntries(this.resolvedConfig.chains)) rpcUrls[caip2] = [{ url: chain.rpcUrls[0] }];
|
|
2429
|
+
return rpcUrls;
|
|
2430
|
+
}
|
|
2431
|
+
get connectionState() {
|
|
2432
|
+
return this.store.getSnapshot().context.connectionState;
|
|
2433
|
+
}
|
|
2434
|
+
get storedCaip2() {
|
|
2435
|
+
return this.store.getSnapshot().context.caip2;
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
|
|
2439
|
+
//#endregion
|
|
2440
|
+
//#region src/core/connectors/xaman/xaman-xrpl-client.ts
|
|
2441
|
+
var XamanClient = class XamanClient extends Xumm {
|
|
2442
|
+
static {
|
|
2443
|
+
this.NETWORK_KEYS = {
|
|
2444
|
+
"0": "MAINNET",
|
|
2445
|
+
"1": "TESTNET"
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
constructor(config, resolvedConfig) {
|
|
2449
|
+
super(config.apiKey);
|
|
2450
|
+
this.resolvedConfig = resolvedConfig;
|
|
2451
|
+
}
|
|
2452
|
+
async sendTx(tx) {
|
|
2453
|
+
if (!this.payload) throw new XamanError("XAMAN_SDK_NOT_READY");
|
|
2454
|
+
const { reference } = parseCaip2(await this.getCaip2());
|
|
2455
|
+
const networkKey = XamanClient.NETWORK_KEYS[reference];
|
|
2456
|
+
const subscription = await this.payload.createAndSubscribe({
|
|
2457
|
+
txjson: {
|
|
2458
|
+
...tx,
|
|
2459
|
+
TransactionType: tx.TransactionType
|
|
2460
|
+
},
|
|
2461
|
+
options: networkKey ? { force_network: networkKey } : {}
|
|
2462
|
+
}, (event) => "signed" in event.data ? event : void 0);
|
|
2463
|
+
if (!subscription?.created) throw new XamanError("XAMAN_SDK_NOT_READY");
|
|
2464
|
+
const txid = (await subscription.resolved)?.payload?.response?.txid;
|
|
2465
|
+
if (!txid) throw new XamanError("XAMAN_SIGN_REJECTED");
|
|
2466
|
+
return txid;
|
|
2467
|
+
}
|
|
2468
|
+
async getCaip2() {
|
|
2469
|
+
const networkId = await this.user.networkId;
|
|
2470
|
+
return this.toCaip2Network(networkId);
|
|
2471
|
+
}
|
|
2472
|
+
async onDestroy() {
|
|
2473
|
+
await super.logout();
|
|
2474
|
+
}
|
|
2475
|
+
async getBalance(address) {
|
|
2476
|
+
return (await this.getSimpleClient()).getBalance(address);
|
|
2477
|
+
}
|
|
2478
|
+
async getSimpleClient() {
|
|
2479
|
+
const caip2 = await this.getCaip2();
|
|
2480
|
+
return new XrplClient(this.resolvedConfig.getXrplChain(caip2));
|
|
2481
|
+
}
|
|
2482
|
+
toCaip2Network(networkId) {
|
|
2483
|
+
return `xrpl:${networkId}`;
|
|
2484
|
+
}
|
|
2485
|
+
};
|
|
2486
|
+
|
|
2487
|
+
//#endregion
|
|
2488
|
+
//#region src/core/connectors/xaman/xaman-connector.ts
|
|
2489
|
+
var XamanConnector = class extends BaseConnector {
|
|
2490
|
+
constructor(store, config) {
|
|
2491
|
+
super(store, config);
|
|
2492
|
+
this.config = config;
|
|
2493
|
+
}
|
|
2494
|
+
async onInitialize() {
|
|
2495
|
+
this.client = new XamanClient({ apiKey: this.config.apiKey }, this.resolvedConfig);
|
|
2496
|
+
this.client.on("ready", () => this.onReady());
|
|
2497
|
+
this.client.on("success", () => this.onSuccess().catch((error) => this.emitError(error)));
|
|
2498
|
+
this.client.on("error", (error) => this.onError(error));
|
|
2499
|
+
this.client.on("logout", () => this.onLogout());
|
|
2500
|
+
this.client.on("loggedout", () => this.onLogout());
|
|
2501
|
+
this.client.on("networkswitch", (event) => event.network && this.onNetworkSwitch(event.network));
|
|
2502
|
+
}
|
|
2503
|
+
/**
|
|
2504
|
+
* Xaman doesn't support session restoration — authorize() is fire-and-forget,
|
|
2505
|
+
* with state changes arriving via SDK events (ready, success, error, logout).
|
|
2506
|
+
*/
|
|
2507
|
+
async restoreConnection() {}
|
|
2508
|
+
async connect(_chains) {
|
|
2509
|
+
if (this.connectionState === "connecting") throw new XamanError("COMMON_CONNECTION_IN_PROGRESS");
|
|
2510
|
+
await this.initialize();
|
|
2511
|
+
this.events.startConnecting();
|
|
2512
|
+
this.getClient().authorize().catch((error) => this.emitError(error));
|
|
2513
|
+
}
|
|
2514
|
+
async disconnect() {
|
|
2515
|
+
this.events.startDisconnecting();
|
|
2516
|
+
if (this.initialized) await this.client.logout().catch((error) => {
|
|
2517
|
+
throw new XamanError("COMMON_CONNECTION_FAILED", error);
|
|
2518
|
+
});
|
|
2519
|
+
this.resetInitialization();
|
|
2520
|
+
this.events.disconnected();
|
|
2521
|
+
}
|
|
2522
|
+
async switchChain(_caip2) {
|
|
2523
|
+
throw new XamanError("COMMON_FEATURE_NOT_SUPPORTED");
|
|
2524
|
+
}
|
|
2525
|
+
getClient() {
|
|
2526
|
+
return this.client;
|
|
2527
|
+
}
|
|
2528
|
+
getProvider() {
|
|
2529
|
+
return this.client;
|
|
2530
|
+
}
|
|
2531
|
+
onReady() {
|
|
2532
|
+
if (this.connectionState === "connected" && !this.isLoggedIn()) this.events.disconnected();
|
|
2533
|
+
}
|
|
2534
|
+
async onSuccess() {
|
|
2535
|
+
const client = this.getClient();
|
|
2536
|
+
const address = await client.user.account;
|
|
2537
|
+
if (!await client.user.networkId) throw new XamanError("COMMON_PROVIDER_ERROR");
|
|
2538
|
+
if (!address) throw new XamanError("COMMON_NO_ACCOUNTS");
|
|
2539
|
+
const caip2 = await client.getCaip2();
|
|
2540
|
+
const isReconnect = this.connectionState !== "connecting";
|
|
2541
|
+
const eventData = {
|
|
2542
|
+
address,
|
|
2543
|
+
addresses: [address],
|
|
2544
|
+
caip2
|
|
2545
|
+
};
|
|
2546
|
+
isReconnect ? this.events.reconnected(eventData) : this.events.connected(eventData);
|
|
2547
|
+
}
|
|
2548
|
+
onError(error) {
|
|
2549
|
+
if (this.connectionState === "connecting") this.events.disconnected();
|
|
2550
|
+
this.emitError(error);
|
|
2551
|
+
}
|
|
2552
|
+
onLogout() {
|
|
2553
|
+
if (this.connectionState === "connected") this.events.disconnected();
|
|
2554
|
+
}
|
|
2555
|
+
onNetworkSwitch(network) {
|
|
2556
|
+
const networkId = network === "1" ? 1 : 0;
|
|
2557
|
+
const caip2 = this.getClient().toCaip2Network(networkId);
|
|
2558
|
+
if (this.storedCaip2 !== caip2) this.events.chainChanged(caip2);
|
|
2559
|
+
}
|
|
2560
|
+
isLoggedIn() {
|
|
2561
|
+
return this.initialized ? !!this.client.runtime.xapp : false;
|
|
2562
|
+
}
|
|
2563
|
+
get connectionState() {
|
|
2564
|
+
return this.store.getSnapshot().context.connectionState;
|
|
2565
|
+
}
|
|
2566
|
+
get storedCaip2() {
|
|
2567
|
+
return this.store.getSnapshot().context.caip2;
|
|
2568
|
+
}
|
|
2569
|
+
};
|
|
2570
|
+
|
|
2571
|
+
//#endregion
|
|
2572
|
+
//#region src/core/connectors/connector-factory.ts
|
|
2573
|
+
const DEFAULT_METADATA = {
|
|
2574
|
+
name: "App",
|
|
2575
|
+
url: typeof window !== "undefined" ? window.location.origin : "https://localhost"
|
|
2576
|
+
};
|
|
2577
|
+
var ConnectorFactory = class {
|
|
2578
|
+
static create(options) {
|
|
2579
|
+
const { walletType, store, resolvedConfig, walletOptions } = options;
|
|
2580
|
+
const metadata = walletOptions.metadata ?? DEFAULT_METADATA;
|
|
2581
|
+
switch (walletType) {
|
|
2582
|
+
case "metamask": return new MetaMaskConnector(store, {
|
|
2583
|
+
walletType: "metamask",
|
|
2584
|
+
metadata,
|
|
2585
|
+
resolvedConfig
|
|
2586
|
+
});
|
|
2587
|
+
case "wallet-connect": return new WalletConnectConnector(store, {
|
|
2588
|
+
walletType: "wallet-connect",
|
|
2589
|
+
metadata,
|
|
2590
|
+
resolvedConfig,
|
|
2591
|
+
projectId: walletOptions.projectId
|
|
2592
|
+
});
|
|
2593
|
+
case "ledger": return new LedgerConnector(store, {
|
|
2594
|
+
walletType: "ledger",
|
|
2595
|
+
metadata,
|
|
2596
|
+
resolvedConfig
|
|
2597
|
+
});
|
|
2598
|
+
case "xaman": return new XamanConnector(store, {
|
|
2599
|
+
walletType: "xaman",
|
|
2600
|
+
metadata,
|
|
2601
|
+
resolvedConfig,
|
|
2602
|
+
apiKey: walletOptions.apiKey
|
|
2603
|
+
});
|
|
2604
|
+
case "dcent": return new DcentConnector(store, {
|
|
2605
|
+
walletType: "dcent",
|
|
2606
|
+
metadata,
|
|
2607
|
+
resolvedConfig
|
|
2608
|
+
});
|
|
2609
|
+
default: throw new Error(`Unknown wallet type: ${walletType}`);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
};
|
|
2613
|
+
|
|
2614
|
+
//#endregion
|
|
2615
|
+
//#region src/core/stores/wallet-store/aggregate-wallet-store.ts
|
|
2616
|
+
const createAggregateWalletStore = (stores) => {
|
|
2617
|
+
return createStore({
|
|
2618
|
+
context: { wallets: objectFromEntries(Array.from(stores.entries()).map(([type, store]) => [type, store.getSnapshot().context])) },
|
|
2619
|
+
on: { walletUpdated: (state, { walletType, context }) => ({ wallets: {
|
|
2620
|
+
...state.wallets,
|
|
2621
|
+
[walletType]: context
|
|
2622
|
+
} }) }
|
|
2623
|
+
});
|
|
2624
|
+
};
|
|
2625
|
+
|
|
2626
|
+
//#endregion
|
|
2627
|
+
//#region src/core/stores/wallet-store/wallet-store.ts
|
|
2628
|
+
const INITIAL_STATE = {
|
|
2629
|
+
connectionState: "disconnected",
|
|
2630
|
+
activeAddress: void 0,
|
|
2631
|
+
availableAddresses: [],
|
|
2632
|
+
caip2: void 0,
|
|
2633
|
+
error: void 0
|
|
2634
|
+
};
|
|
2635
|
+
const createWalletStore = (walletType) => {
|
|
2636
|
+
return createStore({
|
|
2637
|
+
context: INITIAL_STATE,
|
|
2638
|
+
on: {
|
|
2639
|
+
startConnecting: (context) => ({
|
|
2640
|
+
...context,
|
|
2641
|
+
connectionState: "connecting"
|
|
2642
|
+
}),
|
|
2643
|
+
connectionEstablished: (context, { activeAddress, availableAddresses, caip2 }, enqueue) => {
|
|
2644
|
+
enqueue.emit.walletConnected({
|
|
2645
|
+
activeAddress,
|
|
2646
|
+
availableAddresses,
|
|
2647
|
+
caip2,
|
|
2648
|
+
walletType,
|
|
2649
|
+
isReconnect: false
|
|
2650
|
+
});
|
|
2651
|
+
return {
|
|
2652
|
+
...context,
|
|
2653
|
+
connectionState: "connected",
|
|
2654
|
+
activeAddress,
|
|
2655
|
+
availableAddresses,
|
|
2656
|
+
caip2,
|
|
2657
|
+
error: void 0
|
|
2658
|
+
};
|
|
2659
|
+
},
|
|
2660
|
+
connectionRestored: (context, { activeAddress, availableAddresses, caip2 }, enqueue) => {
|
|
2661
|
+
enqueue.emit.walletConnected({
|
|
2662
|
+
activeAddress,
|
|
2663
|
+
availableAddresses,
|
|
2664
|
+
caip2,
|
|
2665
|
+
walletType,
|
|
2666
|
+
isReconnect: true
|
|
2667
|
+
});
|
|
2668
|
+
return {
|
|
2669
|
+
...context,
|
|
2670
|
+
connectionState: "connected",
|
|
2671
|
+
activeAddress,
|
|
2672
|
+
availableAddresses,
|
|
2673
|
+
caip2,
|
|
2674
|
+
error: void 0
|
|
2675
|
+
};
|
|
2676
|
+
},
|
|
2677
|
+
error: (context, { error }, enqueue) => {
|
|
2678
|
+
enqueue.emit.walletError({
|
|
2679
|
+
error,
|
|
2680
|
+
walletType
|
|
2681
|
+
});
|
|
2682
|
+
return {
|
|
2683
|
+
...context,
|
|
2684
|
+
connectionState: "error",
|
|
2685
|
+
error
|
|
2686
|
+
};
|
|
2687
|
+
},
|
|
2688
|
+
startDisconnecting: (context) => ({
|
|
2689
|
+
...context,
|
|
2690
|
+
connectionState: "disconnecting"
|
|
2691
|
+
}),
|
|
2692
|
+
disconnected: (context, _, enqueue) => {
|
|
2693
|
+
enqueue.emit.walletDisconnected({ walletType });
|
|
2694
|
+
return {
|
|
2695
|
+
...context,
|
|
2696
|
+
connectionState: "disconnected",
|
|
2697
|
+
activeAddress: void 0,
|
|
2698
|
+
availableAddresses: [],
|
|
2699
|
+
caip2: void 0,
|
|
2700
|
+
error: void 0
|
|
2701
|
+
};
|
|
2702
|
+
},
|
|
2703
|
+
chainChanged: (context, { caip2 }, enqueue) => {
|
|
2704
|
+
enqueue.emit.walletChainChanged({
|
|
2705
|
+
caip2,
|
|
2706
|
+
walletType
|
|
2707
|
+
});
|
|
2708
|
+
return {
|
|
2709
|
+
...context,
|
|
2710
|
+
caip2
|
|
2711
|
+
};
|
|
2712
|
+
},
|
|
2713
|
+
addressChanged: (context, { activeAddress, availableAddresses }, enqueue) => {
|
|
2714
|
+
enqueue.emit.walletAddressChanged({
|
|
2715
|
+
activeAddress,
|
|
2716
|
+
walletType
|
|
2717
|
+
});
|
|
2718
|
+
return {
|
|
2719
|
+
...context,
|
|
2720
|
+
availableAddresses,
|
|
2721
|
+
activeAddress
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
});
|
|
2726
|
+
};
|
|
2727
|
+
|
|
2728
|
+
//#endregion
|
|
2729
|
+
//#region src/core/multichain/multichain.ts
|
|
2730
|
+
var MultiChain = class {
|
|
2731
|
+
constructor(options, inspector) {
|
|
2732
|
+
this.stores = /* @__PURE__ */ new Map();
|
|
2733
|
+
this.connectors = /* @__PURE__ */ new Map();
|
|
2734
|
+
this.subscriptions = [];
|
|
2735
|
+
this.mounted = false;
|
|
2736
|
+
this.configResolver = new ConfigResolver(options);
|
|
2737
|
+
this.events = options.events ?? {};
|
|
2738
|
+
for (const [walletType, walletOptions] of objectEntries(options.wallets)) {
|
|
2739
|
+
if (!walletOptions) continue;
|
|
2740
|
+
const wType = walletType;
|
|
2741
|
+
const store = createWalletStore(wType);
|
|
2742
|
+
this.stores.set(wType, store);
|
|
2743
|
+
if (inspector) store.inspect(inspector.inspect);
|
|
2744
|
+
const resolvedConfig = this.configResolver.resolveForWallet(wType);
|
|
2745
|
+
const connector = ConnectorFactory.create({
|
|
2746
|
+
walletType: wType,
|
|
2747
|
+
store,
|
|
2748
|
+
resolvedConfig,
|
|
2749
|
+
walletOptions
|
|
2750
|
+
});
|
|
2751
|
+
this.connectors.set(wType, connector);
|
|
2752
|
+
this.registerGlobalListeners(wType);
|
|
2753
|
+
}
|
|
2754
|
+
this.aggregateStore = createAggregateWalletStore(this.stores);
|
|
2755
|
+
for (const [walletType, store] of this.stores) this.registerAggregateSync(walletType, store);
|
|
2756
|
+
if (inspector) this.aggregateStore.inspect(inspector.inspect);
|
|
2757
|
+
}
|
|
2758
|
+
async mount() {
|
|
2759
|
+
if (this.mounted) return;
|
|
2760
|
+
this.mounted = true;
|
|
2761
|
+
const results = await Promise.allSettled(Array.from(this.connectors.entries()).map(async ([walletType, connector]) => {
|
|
2762
|
+
await connector.initialize();
|
|
2763
|
+
await connector.restoreConnection();
|
|
2764
|
+
return walletType;
|
|
2765
|
+
}));
|
|
2766
|
+
for (const result of results) if (result.status === "rejected") console.warn("[MultiChain] Connector failed to mount:", result.reason);
|
|
2767
|
+
}
|
|
2768
|
+
async disconnect(walletType) {
|
|
2769
|
+
const connector = this.connectors.get(walletType);
|
|
2770
|
+
if (!connector) throw new Error(`Wallet "${walletType}" is not configured`);
|
|
2771
|
+
await connector.disconnect();
|
|
2772
|
+
}
|
|
2773
|
+
async disconnectAll() {
|
|
2774
|
+
const disconnectPromises = Array.from(this.connectors.values()).map((connector) => connector.disconnect());
|
|
2775
|
+
await Promise.all(disconnectPromises);
|
|
2776
|
+
}
|
|
2777
|
+
/**
|
|
2778
|
+
* Connect a wallet with type-safe discriminated union arguments.
|
|
2779
|
+
*
|
|
2780
|
+
* Each wallet type has its own connection signature:
|
|
2781
|
+
* - wallet-connect: { wallet: "wallet-connect", chains: [...] }
|
|
2782
|
+
* - metamask: { wallet: "metamask", chain: "eip155:..." }
|
|
2783
|
+
* - ledger: { wallet: "ledger", chain: "...", options?: {...} }
|
|
2784
|
+
* - xaman: { wallet: "xaman" }
|
|
2785
|
+
* - dcent: { wallet: "dcent" }
|
|
2786
|
+
*/
|
|
2787
|
+
async connect(args) {
|
|
2788
|
+
switch (args.wallet) {
|
|
2789
|
+
case "wallet-connect": return this.getConnector("wallet-connect").connect(args.chains);
|
|
2790
|
+
case "metamask": return this.getConnector("metamask").connect(args.chain);
|
|
2791
|
+
case "ledger": return this.getConnector("ledger").connect(args.chain, args.options);
|
|
2792
|
+
case "xaman": return this.getConnector("xaman").connect();
|
|
2793
|
+
case "dcent": return this.getConnector("dcent").connect();
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
getAggregateStore() {
|
|
2797
|
+
return this.aggregateStore;
|
|
2798
|
+
}
|
|
2799
|
+
getWalletTypes() {
|
|
2800
|
+
return Array.from(this.stores.keys());
|
|
2801
|
+
}
|
|
2802
|
+
getConnector(walletType) {
|
|
2803
|
+
const connector = this.connectors.get(walletType);
|
|
2804
|
+
if (!connector) throw new Error(`Wallet "${walletType}" is not configured`);
|
|
2805
|
+
return connector;
|
|
2806
|
+
}
|
|
2807
|
+
onWalletEvent(walletType, eventType, callback) {
|
|
2808
|
+
const store = this.stores.get(walletType);
|
|
2809
|
+
if (!store) return void 0;
|
|
2810
|
+
return store.on(eventType, callback);
|
|
2811
|
+
}
|
|
2812
|
+
getConfigResolver() {
|
|
2813
|
+
return this.configResolver;
|
|
2814
|
+
}
|
|
2815
|
+
/** Check if any wallet is currently connected to a specific chain. */
|
|
2816
|
+
isConnectedByCaip2(caip2) {
|
|
2817
|
+
const wallets = this.aggregateStore.getSnapshot().context.wallets;
|
|
2818
|
+
return objectValues(wallets).some((state) => state.connectionState === "connected" && state.caip2 === caip2);
|
|
2819
|
+
}
|
|
2820
|
+
registerGlobalListeners(walletType) {
|
|
2821
|
+
const store = this.stores.get(walletType);
|
|
2822
|
+
if (!store) return;
|
|
2823
|
+
this.subscriptions.push(store.on("walletConnected", (event) => {
|
|
2824
|
+
this.events.onConnect?.({
|
|
2825
|
+
walletType,
|
|
2826
|
+
caip2: event.caip2,
|
|
2827
|
+
address: event.activeAddress,
|
|
2828
|
+
availableAddresses: event.availableAddresses,
|
|
2829
|
+
isReconnect: event.isReconnect
|
|
2830
|
+
});
|
|
2831
|
+
}));
|
|
2832
|
+
this.subscriptions.push(store.on("walletDisconnected", () => {
|
|
2833
|
+
this.events.onDisconnect?.({ walletType });
|
|
2834
|
+
}));
|
|
2835
|
+
this.subscriptions.push(store.on("walletError", (event) => {
|
|
2836
|
+
this.events.onError?.({
|
|
2837
|
+
walletType,
|
|
2838
|
+
error: new Error(event.error.message)
|
|
2839
|
+
});
|
|
2840
|
+
}));
|
|
2841
|
+
this.subscriptions.push(store.on("walletChainChanged", (event) => {
|
|
2842
|
+
this.events.onChainChanged?.({
|
|
2843
|
+
walletType,
|
|
2844
|
+
caip2: event.caip2
|
|
2845
|
+
});
|
|
2846
|
+
}));
|
|
2847
|
+
}
|
|
2848
|
+
registerAggregateSync(walletType, store) {
|
|
2849
|
+
const sync = () => {
|
|
2850
|
+
this.aggregateStore.send({
|
|
2851
|
+
type: "walletUpdated",
|
|
2852
|
+
walletType,
|
|
2853
|
+
context: store.getSnapshot().context
|
|
2854
|
+
});
|
|
2855
|
+
};
|
|
2856
|
+
sync();
|
|
2857
|
+
this.subscriptions.push(store.subscribe(sync));
|
|
2858
|
+
}
|
|
2859
|
+
};
|
|
2860
|
+
|
|
2861
|
+
//#endregion
|
|
2862
|
+
export { objectFromEntries as _, WalletErrorHelper as a, shortenAddress as b, MetaMaskError as c, WalletError as d, ErrorCode as f, objectEntries as g, ChainGuards as h, DcentConnector as i, LedgerError as l, evmCaip2s as m, EvmClient as n, XamanError as o, ConfigResolver as p, Bip44 as r, WalletConnectError as s, MultiChain as t, DcentError as u, objectKeys as v, objectValues as y };
|
|
2863
|
+
//# sourceMappingURL=multichain-C5wKiJke.mjs.map
|