@effectstream/wallets 0.100.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 +1 -0
- package/package.json +30 -0
- package/src/IProvider.ts +62 -0
- package/src/algorand/algorand.ts +168 -0
- package/src/algorand/wrapper.ts +22 -0
- package/src/avail/avail.ts +81 -0
- package/src/avail/wrapper.ts +44 -0
- package/src/cardano/cardano.ts +194 -0
- package/src/cardano/wrapper.ts +34 -0
- package/src/effectstream.ts +382 -0
- package/src/evm/ethers.ts +104 -0
- package/src/evm/injected.ts +429 -0
- package/src/evm/types.ts +2 -0
- package/src/evm/wrapper-ethers.ts +49 -0
- package/src/evm/wrapper-injected.ts +103 -0
- package/src/midnight/midnight.ts +196 -0
- package/src/midnight/wrapper.ts +36 -0
- package/src/mina/mina.ts +175 -0
- package/src/mina/wrapper.ts +22 -0
- package/src/mod.ts +10 -0
- package/src/polkadot/polkadot.ts +166 -0
- package/src/polkadot/wrapper.ts +30 -0
- package/src/types.ts +61 -0
- package/src/utils.test.ts +64 -0
- package/src/utils.ts +133 -0
- package/src/wallet-modes.ts +124 -0
- package/src/wallets.ts +62 -0
- package/src/windows.ts +10 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import type { MetaMaskInpageProvider } from "@metamask/providers";
|
|
2
|
+
import { AddressType } from "@effectstream/utils";
|
|
3
|
+
import {
|
|
4
|
+
type ActiveConnection,
|
|
5
|
+
type AddressAndType,
|
|
6
|
+
type ConnectionOption,
|
|
7
|
+
type IInjectedConnector,
|
|
8
|
+
type IProvider,
|
|
9
|
+
optionToActive,
|
|
10
|
+
type UserSignature,
|
|
11
|
+
} from "../IProvider.ts";
|
|
12
|
+
import type { EvmAddress } from "./types.ts";
|
|
13
|
+
import { getWindow } from "../windows.ts";
|
|
14
|
+
import { utf8ToHex } from "web3-utils";
|
|
15
|
+
import * as CHAINS from "viem/chains";
|
|
16
|
+
import type { Chain } from "viem/chains";
|
|
17
|
+
|
|
18
|
+
type EIP1193Provider = MetaMaskInpageProvider;
|
|
19
|
+
|
|
20
|
+
interface EIP5749ProviderInfo {
|
|
21
|
+
uuid: string;
|
|
22
|
+
name: string;
|
|
23
|
+
icon: `data:image/svg+xml;base64,${string}`;
|
|
24
|
+
description: string;
|
|
25
|
+
}
|
|
26
|
+
interface EIP5749ProviderWithInfo extends EIP1193Provider {
|
|
27
|
+
info: EIP5749ProviderInfo;
|
|
28
|
+
}
|
|
29
|
+
interface EIP5749EVMProviders {
|
|
30
|
+
/**
|
|
31
|
+
* The key is RECOMMENDED to be the name of the extension in snake_case. It MUST contain only lowercase letters, numbers, and underscores.
|
|
32
|
+
*/
|
|
33
|
+
[index: string]: EIP5749ProviderWithInfo;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface EIP6963ProviderInfo {
|
|
37
|
+
uuid: string;
|
|
38
|
+
name: string;
|
|
39
|
+
icon: string;
|
|
40
|
+
rdns: string;
|
|
41
|
+
}
|
|
42
|
+
interface EIP6963ProviderDetail {
|
|
43
|
+
info: EIP6963ProviderInfo;
|
|
44
|
+
provider: EIP1193Provider;
|
|
45
|
+
}
|
|
46
|
+
interface EIP6963AnnounceProviderEvent extends CustomEvent {
|
|
47
|
+
type: "eip6963:announceProvider";
|
|
48
|
+
detail: EIP6963ProviderDetail;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const eip5953Providers: EIP6963ProviderDetail[] = [];
|
|
52
|
+
|
|
53
|
+
const windowObj = getWindow();
|
|
54
|
+
if (windowObj && (windowObj as any).addEventListener) {
|
|
55
|
+
(windowObj as any).addEventListener(
|
|
56
|
+
"eip6963:announceProvider",
|
|
57
|
+
(event: EIP6963AnnounceProviderEvent) => {
|
|
58
|
+
eip5953Providers.push(event.detail);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (windowObj && windowObj.dispatchEvent) {
|
|
62
|
+
windowObj?.dispatchEvent(new Event("eip6963:requestProvider"));
|
|
63
|
+
}
|
|
64
|
+
const CHAIN_NOT_ADDED_ERROR_CODE = 4902;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Type definition from EIP3085 (https://eips.ethereum.org/EIPS/eip-3085)
|
|
68
|
+
* I couldn't find this part of any NPM library for some reason
|
|
69
|
+
*/
|
|
70
|
+
interface AddEthereumChainParameter {
|
|
71
|
+
chainId: string;
|
|
72
|
+
blockExplorerUrls?: string[];
|
|
73
|
+
chainName?: string;
|
|
74
|
+
iconUrls?: string[];
|
|
75
|
+
nativeCurrency?: {
|
|
76
|
+
name: string;
|
|
77
|
+
symbol: string;
|
|
78
|
+
decimals: number;
|
|
79
|
+
};
|
|
80
|
+
rpcUrls?: string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type EvmApi = EIP1193Provider;
|
|
84
|
+
|
|
85
|
+
export class EvmInjectedConnector implements IInjectedConnector<EvmApi> {
|
|
86
|
+
private provider: EvmInjectedProvider | undefined;
|
|
87
|
+
private static INSTANCE: undefined | EvmInjectedConnector = undefined;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Wallets may inject themselves using multiple competing standards
|
|
91
|
+
* We have to track all of them (with duplicates) internally
|
|
92
|
+
* To make sure `connectNamed` calls properly find wallets no matter the name used
|
|
93
|
+
*/
|
|
94
|
+
static getPossiblyDuplicateWalletOptions(): ConnectionOption<EvmApi>[] {
|
|
95
|
+
const allWallets: ConnectionOption<EvmApi>[] = [];
|
|
96
|
+
|
|
97
|
+
const windowObj = getWindow();
|
|
98
|
+
if (windowObj == null) return [];
|
|
99
|
+
|
|
100
|
+
// 1) Add EIP6963 and prioritize it for deduplicating
|
|
101
|
+
{
|
|
102
|
+
const eip6963Options = eip5953Providers.map(({ info, provider }) => ({
|
|
103
|
+
metadata: {
|
|
104
|
+
name: info.rdns,
|
|
105
|
+
displayName: info.name,
|
|
106
|
+
icon: info.icon,
|
|
107
|
+
},
|
|
108
|
+
api: (): Promise<MetaMaskInpageProvider> => Promise.resolve(provider),
|
|
109
|
+
}));
|
|
110
|
+
allWallets.push(...eip6963Options);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 2) Add EIP5749
|
|
114
|
+
{
|
|
115
|
+
const eip5749Providers: EIP5749EVMProviders =
|
|
116
|
+
(windowObj as any).evmproviders ?? {};
|
|
117
|
+
const eip5749Options = Object.entries(eip5749Providers).map(
|
|
118
|
+
([key, provider]) => ({
|
|
119
|
+
metadata: {
|
|
120
|
+
name: key,
|
|
121
|
+
displayName: provider.info.name,
|
|
122
|
+
icon: provider.info.icon,
|
|
123
|
+
},
|
|
124
|
+
api: (): Promise<EIP5749ProviderWithInfo> =>
|
|
125
|
+
Promise.resolve(provider),
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
allWallets.push(...eip5749Options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// some wallets aggressively put themselves in the list of wallets the user has installed
|
|
132
|
+
// even if the user has never actually used these
|
|
133
|
+
// we put these wallets at the bottom of the priority
|
|
134
|
+
const aggressiveWallets = ["com.brave.wallet"];
|
|
135
|
+
allWallets.sort((a, b) => {
|
|
136
|
+
const aAggressive = aggressiveWallets.includes(a.metadata.name);
|
|
137
|
+
const bAggressive = aggressiveWallets.includes(b.metadata.name);
|
|
138
|
+
if (aAggressive && !bAggressive) return 1;
|
|
139
|
+
if (bAggressive && !aAggressive) return -1;
|
|
140
|
+
return 0;
|
|
141
|
+
});
|
|
142
|
+
return allWallets;
|
|
143
|
+
}
|
|
144
|
+
static getWalletOptions(): ConnectionOption<EvmApi>[] {
|
|
145
|
+
const withDuplicates = EvmInjectedConnector
|
|
146
|
+
.getPossiblyDuplicateWalletOptions();
|
|
147
|
+
const seenNames: Set<string> = new Set();
|
|
148
|
+
const seenDisplayNames: Set<string> = new Set();
|
|
149
|
+
|
|
150
|
+
const result: ConnectionOption<EvmApi>[] = [];
|
|
151
|
+
for (const option of withDuplicates) {
|
|
152
|
+
const lowerCaseName = option.metadata.displayName.toLowerCase();
|
|
153
|
+
if (seenNames.has(option.metadata.name)) continue;
|
|
154
|
+
if (seenDisplayNames.has(lowerCaseName)) continue;
|
|
155
|
+
seenNames.add(option.metadata.name);
|
|
156
|
+
seenDisplayNames.add(lowerCaseName);
|
|
157
|
+
result.push(option);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static instance(): EvmInjectedConnector {
|
|
164
|
+
if (EvmInjectedConnector.INSTANCE == null) {
|
|
165
|
+
const newInstance = new EvmInjectedConnector();
|
|
166
|
+
EvmInjectedConnector.INSTANCE = newInstance;
|
|
167
|
+
}
|
|
168
|
+
return EvmInjectedConnector.INSTANCE;
|
|
169
|
+
}
|
|
170
|
+
connectSimple = async (): Promise<EvmInjectedProvider> => {
|
|
171
|
+
if (this.provider != null) {
|
|
172
|
+
return this.provider;
|
|
173
|
+
}
|
|
174
|
+
const options = EvmInjectedConnector.getWalletOptions();
|
|
175
|
+
if (options.length === 0) {
|
|
176
|
+
throw new Error(`No EVM wallet found`);
|
|
177
|
+
}
|
|
178
|
+
return await this.connectExternal(await optionToActive(options[0]));
|
|
179
|
+
};
|
|
180
|
+
connectExternal = async (
|
|
181
|
+
conn: ActiveConnection<EvmApi>,
|
|
182
|
+
): Promise<EvmInjectedProvider> => {
|
|
183
|
+
if (this.provider?.getConnection().metadata?.name === conn.metadata.name) {
|
|
184
|
+
return this.provider;
|
|
185
|
+
}
|
|
186
|
+
this.provider = await EvmInjectedProvider.init(conn);
|
|
187
|
+
|
|
188
|
+
// Update the selected Eth address if the user changes after logging in.
|
|
189
|
+
// warning: not supported by all wallets (ex: Flint)
|
|
190
|
+
const ethereum: EIP1193Provider = (getWindow() as any).ethereum;
|
|
191
|
+
ethereum?.on("accountsChanged", (newAccounts) => {
|
|
192
|
+
const accounts = newAccounts as string[];
|
|
193
|
+
if (!accounts || !accounts[0] || accounts[0] !== this.provider?.address) {
|
|
194
|
+
this.provider = undefined;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
return this.provider;
|
|
198
|
+
};
|
|
199
|
+
connectNamed = async (name: string): Promise<EvmInjectedProvider> => {
|
|
200
|
+
if (this.provider?.getConnection().metadata?.name === name) {
|
|
201
|
+
return this.provider;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// the eip6963 name for Metamask is io.metamask
|
|
205
|
+
// but we want to keep simply "metamask" working for backwards compatibility with older versions of Paima
|
|
206
|
+
const adjustedName = name.toLocaleLowerCase() === "metamask"
|
|
207
|
+
? "io.metamask"
|
|
208
|
+
: name;
|
|
209
|
+
const provider = EvmInjectedConnector.getPossiblyDuplicateWalletOptions()
|
|
210
|
+
.find(
|
|
211
|
+
(entry) => entry.metadata.name === adjustedName,
|
|
212
|
+
);
|
|
213
|
+
if (provider == null) {
|
|
214
|
+
throw new Error(`EVM wallet ${name} not found`);
|
|
215
|
+
}
|
|
216
|
+
return await this.connectExternal(await optionToActive(provider));
|
|
217
|
+
};
|
|
218
|
+
getProvider = (): undefined | EvmInjectedProvider => {
|
|
219
|
+
return this.provider;
|
|
220
|
+
};
|
|
221
|
+
getOrThrowProvider = (): EvmInjectedProvider => {
|
|
222
|
+
if (this.provider == null) {
|
|
223
|
+
throw new Error(`EvmInjectedConnector not initialized yet`);
|
|
224
|
+
}
|
|
225
|
+
return this.provider;
|
|
226
|
+
};
|
|
227
|
+
isConnected = (): boolean => {
|
|
228
|
+
return this.provider != null;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
switchChain = async (hexChainId: string, chain?: Chain): Promise<void> => {
|
|
232
|
+
try {
|
|
233
|
+
// careful: not all wallets support switching chains
|
|
234
|
+
await this.provider?.getConnection().api.request({
|
|
235
|
+
method: "wallet_switchEthereumChain",
|
|
236
|
+
params: [{ chainId: hexChainId }],
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await this.verifyWalletChain(hexChainId);
|
|
240
|
+
} catch (se: any) {
|
|
241
|
+
// deno-lint-ignore no-prototype-builtins
|
|
242
|
+
if (se.hasOwnProperty("code") && se.code === CHAIN_NOT_ADDED_ERROR_CODE) {
|
|
243
|
+
let targetChain: Chain | undefined = undefined;
|
|
244
|
+
Object.entries(CHAINS).forEach(([key, value]) => {
|
|
245
|
+
const possibleChain = value as Chain;
|
|
246
|
+
if (
|
|
247
|
+
typeof possibleChain === "object" &&
|
|
248
|
+
// deno-lint-ignore no-prototype-builtins
|
|
249
|
+
possibleChain.hasOwnProperty("id") &&
|
|
250
|
+
possibleChain.id === parseInt(hexChainId, 16)
|
|
251
|
+
) {
|
|
252
|
+
targetChain = possibleChain;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
if (chain) {
|
|
257
|
+
targetChain = chain;
|
|
258
|
+
}
|
|
259
|
+
if (!targetChain) {
|
|
260
|
+
throw new Error(`[switchChain] error: Chain not found`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
await this.addChain({
|
|
264
|
+
chainId: hexChainId,
|
|
265
|
+
chainName: targetChain.name,
|
|
266
|
+
nativeCurrency: {
|
|
267
|
+
name: targetChain.nativeCurrency.name,
|
|
268
|
+
symbol: targetChain.nativeCurrency.symbol,
|
|
269
|
+
decimals: targetChain.nativeCurrency.decimals,
|
|
270
|
+
},
|
|
271
|
+
rpcUrls: [targetChain.rpcUrls.default.http[0]],
|
|
272
|
+
blockExplorerUrls: targetChain.blockExplorers?.default.url
|
|
273
|
+
? [targetChain.blockExplorers?.default.url]
|
|
274
|
+
: undefined,
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
throw new Error(`[switchChain] error: ${se?.message} ` + se?.code);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
verifyWalletChain = async (chainId: string): Promise<boolean> => {
|
|
282
|
+
try {
|
|
283
|
+
const walletChain = await this.provider?.getConnection().api.request({
|
|
284
|
+
method: "eth_chainId",
|
|
285
|
+
});
|
|
286
|
+
return parseInt(walletChain as string, 16) === parseInt(chainId, 16);
|
|
287
|
+
} catch (e: any) {
|
|
288
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
289
|
+
throw new Error(`[verifyWalletChain] error: ${e?.message} ` + e?.code);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
addChain = async (newChain: AddEthereumChainParameter): Promise<void> => {
|
|
293
|
+
try {
|
|
294
|
+
// careful: not all wallets support adding arbitrary chains
|
|
295
|
+
await this.provider?.getConnection().api.request({
|
|
296
|
+
method: "wallet_addEthereumChain",
|
|
297
|
+
params: [newChain],
|
|
298
|
+
});
|
|
299
|
+
} catch (e: any) {
|
|
300
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
301
|
+
throw new Error(`[addChain] error: ${e?.message} ` + e?.code);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* For some reason I can't find any official type definitions for this
|
|
308
|
+
* https://docs.metamask.io/wallet/reference/eth_sendtransaction/
|
|
309
|
+
*/
|
|
310
|
+
type Web3TransactionRequest = {
|
|
311
|
+
to?: string;
|
|
312
|
+
from: string;
|
|
313
|
+
|
|
314
|
+
gas?: string;
|
|
315
|
+
gasPrice?: string;
|
|
316
|
+
|
|
317
|
+
data: string;
|
|
318
|
+
value?: string;
|
|
319
|
+
|
|
320
|
+
maxPriorityFeePerGas?: string;
|
|
321
|
+
maxFeePerGas?: string;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
export class EvmInjectedProvider implements IProvider<EvmApi> {
|
|
325
|
+
constructor(
|
|
326
|
+
private readonly conn: ActiveConnection<EvmApi>,
|
|
327
|
+
readonly address: EvmAddress,
|
|
328
|
+
) {}
|
|
329
|
+
static init = async (
|
|
330
|
+
conn: ActiveConnection<EvmApi>,
|
|
331
|
+
): Promise<EvmInjectedProvider> => {
|
|
332
|
+
const accounts = (await conn.api.request({
|
|
333
|
+
method: "eth_requestAccounts",
|
|
334
|
+
})) as string[];
|
|
335
|
+
if (!accounts || accounts.length === 0) {
|
|
336
|
+
throw new Error("Unknown error while receiving EVM accounts");
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return new EvmInjectedProvider(conn, accounts[0]);
|
|
340
|
+
};
|
|
341
|
+
getConnection = (): ActiveConnection<EvmApi> => {
|
|
342
|
+
return this.conn;
|
|
343
|
+
};
|
|
344
|
+
getAddress = (): AddressAndType => {
|
|
345
|
+
return {
|
|
346
|
+
type: AddressType.EVM,
|
|
347
|
+
address: this.address.toLowerCase(),
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
signMessage = async (message: string): Promise<UserSignature> => {
|
|
351
|
+
const hexMessage = utf8ToHex(message);
|
|
352
|
+
const signature = await this.conn.api.request({
|
|
353
|
+
method: "personal_sign",
|
|
354
|
+
params: [hexMessage, this.getAddress().address, ""],
|
|
355
|
+
});
|
|
356
|
+
if (typeof signature !== "string") {
|
|
357
|
+
console.log("[signMessageEth] invalid signature:", signature);
|
|
358
|
+
throw new Error(
|
|
359
|
+
`[signMessageEth] Received signature of type ${typeof signature}`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
return signature;
|
|
363
|
+
};
|
|
364
|
+
verifyWalletChain = async (): Promise<boolean> => {
|
|
365
|
+
try {
|
|
366
|
+
const walletChain = await this.conn.api.request({
|
|
367
|
+
method: "eth_chainId",
|
|
368
|
+
});
|
|
369
|
+
return true;
|
|
370
|
+
// TODO We have to move this to the top level
|
|
371
|
+
// return (
|
|
372
|
+
// parseInt(walletChain as string, 16) ===
|
|
373
|
+
// parseInt(this.gameInfo.gameChainId, 16)
|
|
374
|
+
// );
|
|
375
|
+
} catch (e: any) {
|
|
376
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
377
|
+
throw new Error(`[verifyWalletChain] error: ${e?.message} ` + e?.code);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
addChain = async (newChain: AddEthereumChainParameter): Promise<void> => {
|
|
381
|
+
try {
|
|
382
|
+
// careful: not all wallets support adding arbitrary chains
|
|
383
|
+
await this.conn.api.request({
|
|
384
|
+
method: "wallet_addEthereumChain",
|
|
385
|
+
params: [newChain],
|
|
386
|
+
});
|
|
387
|
+
} catch (e: any) {
|
|
388
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
389
|
+
throw new Error(`[addChain] error: ${e?.message} ` + e?.code);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
switchChain = async (hexChainId: string): Promise<void> => {
|
|
393
|
+
try {
|
|
394
|
+
// careful: not all wallets support switching chains
|
|
395
|
+
await this.conn.api.request({
|
|
396
|
+
method: "wallet_switchEthereumChain",
|
|
397
|
+
params: [{ chainId: hexChainId }],
|
|
398
|
+
});
|
|
399
|
+
} catch (e: any) {
|
|
400
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
401
|
+
throw new Error(`[switchChain] error: ${e?.message} ` + e?.code);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
sendTransaction = async (
|
|
405
|
+
tx: Web3TransactionRequest,
|
|
406
|
+
): Promise<{
|
|
407
|
+
txHash: string;
|
|
408
|
+
}> => {
|
|
409
|
+
await this.verifyWalletChain();
|
|
410
|
+
try {
|
|
411
|
+
const hash = await this.conn.api.request({
|
|
412
|
+
method: "eth_sendTransaction",
|
|
413
|
+
params: [tx],
|
|
414
|
+
});
|
|
415
|
+
if (typeof hash !== "string") {
|
|
416
|
+
console.log("[sendTransaction] invalid signature:", hash);
|
|
417
|
+
throw new Error(
|
|
418
|
+
`[sendTransaction] Received "hash" of type ${typeof hash}`,
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
txHash: hash,
|
|
423
|
+
};
|
|
424
|
+
} catch (e: any) {
|
|
425
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
426
|
+
throw new Error(`[sendTransaction] error: ${e?.message} ` + e?.code);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
package/src/evm/types.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Result } from "@effectstream/utils";
|
|
2
|
+
import type { IProvider } from "../IProvider.ts";
|
|
3
|
+
import type { WalletMode, ApiForMode } from "../utils.ts";
|
|
4
|
+
import type { LoginInfoMap } from "../wallet-modes.ts";
|
|
5
|
+
import { type EthersApi, EthersConnector } from "./ethers.ts";
|
|
6
|
+
|
|
7
|
+
async function connectWallet(
|
|
8
|
+
loginInfo: LoginInfoMap[WalletMode.EvmEthers]
|
|
9
|
+
): Promise<Result<IProvider<EthersApi>>> {
|
|
10
|
+
const name = loginInfo.connection.metadata.name;
|
|
11
|
+
try {
|
|
12
|
+
console.log(`ethersLoginWrapper: Attempting to log into ${name}`);
|
|
13
|
+
const provider = await EthersConnector.instance().connectExternal(
|
|
14
|
+
loginInfo.connection.api
|
|
15
|
+
);
|
|
16
|
+
return {
|
|
17
|
+
success: true,
|
|
18
|
+
result: provider,
|
|
19
|
+
};
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.log(`ethersLoginWrapper: Error while logging into wallet name}`);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function ethersLoginWrapper(
|
|
30
|
+
loginInfo: LoginInfoMap[WalletMode.EvmEthers]
|
|
31
|
+
): Promise<Result<IProvider<ApiForMode<WalletMode.EvmEthers>>>> {
|
|
32
|
+
const loginResult = await connectWallet(loginInfo);
|
|
33
|
+
if (loginResult.success === false) {
|
|
34
|
+
return loginResult;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
// TODO Move this to the top level
|
|
38
|
+
// await updateFee();
|
|
39
|
+
} catch (err) {
|
|
40
|
+
// errorFxn(PaimaMiddlewareErrorCode.ERROR_UPDATING_FEE, err);
|
|
41
|
+
// The fee has a default value, so this is not fatal and we can continue.
|
|
42
|
+
// If the fee has increased beyond the default value, posting won't work.
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
result: loginResult.result,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Result } from "@effectstream/utils";
|
|
2
|
+
import type { IProvider } from "../IProvider.ts";
|
|
3
|
+
import type { ApiForMode, WalletMode } from "../utils.ts";
|
|
4
|
+
import { connectInjected, type LoginInfoMap } from "../wallet-modes.ts";
|
|
5
|
+
import { EvmInjectedConnector } from "./injected.ts";
|
|
6
|
+
import type { Chain } from "viem/chains";
|
|
7
|
+
interface SwitchError {
|
|
8
|
+
code: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function switchChain(chain: Chain | undefined): Promise<boolean> {
|
|
12
|
+
if (!chain) {
|
|
13
|
+
// We can't switch to a chain if we don't know what chain to switch to.
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const hexChainId = "0x" + chain.id.toString(16);
|
|
17
|
+
await EvmInjectedConnector.instance().switchChain(
|
|
18
|
+
hexChainId,
|
|
19
|
+
chain,
|
|
20
|
+
);
|
|
21
|
+
return await verifyWalletChain(chain);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function verifyWalletChain(chain: Chain | undefined): Promise<boolean> {
|
|
25
|
+
if (!chain) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const hexChainId = "0x" + chain.id.toString(16);
|
|
29
|
+
return await EvmInjectedConnector.instance().verifyWalletChain(hexChainId);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function checkEthWalletStatus(): Promise<Result<string>> {
|
|
33
|
+
// const errorFxn = buildEndpointErrorFxn('checkEthWalletStatus');
|
|
34
|
+
|
|
35
|
+
// if (!hasLogin(WalletMode.EvmInjected)) {
|
|
36
|
+
// return { success: true, message: '' };
|
|
37
|
+
// }
|
|
38
|
+
if (EvmInjectedConnector.instance().getProvider() === null) {
|
|
39
|
+
return { success: false, errorMessage: "No address selected" };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
if (!(await verifyWalletChain(undefined))) {
|
|
44
|
+
return { success: false, errorMessage: "EVM_WRONG_CHAIN" };
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return { success: false, errorMessage: "EVM_CHAIN_VERIFICATION" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { success: true, result: "OK" };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function evmLoginWrapper(
|
|
54
|
+
loginInfo: LoginInfoMap[WalletMode.EvmInjected],
|
|
55
|
+
): Promise<Result<IProvider<ApiForMode<WalletMode.EvmInjected>>>> {
|
|
56
|
+
// const errorFxn = buildEndpointErrorFxn('evmLoginWrapper');
|
|
57
|
+
|
|
58
|
+
// const evmConfig = await GlobalConfig.mainEvmConfig();
|
|
59
|
+
// if (evmConfig == null) {
|
|
60
|
+
// return errorFxn(
|
|
61
|
+
// PaimaMiddlewareErrorCode.EVM_LOGIN,
|
|
62
|
+
// new Error(`No EVM network found in configuration`)
|
|
63
|
+
// );
|
|
64
|
+
// }
|
|
65
|
+
// const [_, config] = evmConfig;
|
|
66
|
+
// const gameInfo = {
|
|
67
|
+
// gameName: getGameName(),
|
|
68
|
+
// gameChainId: '0x' + config.chainId.toString(16),
|
|
69
|
+
// };
|
|
70
|
+
const loginResult = await connectInjected(
|
|
71
|
+
"evmLoginWrapper",
|
|
72
|
+
loginInfo,
|
|
73
|
+
EvmInjectedConnector.instance(),
|
|
74
|
+
);
|
|
75
|
+
if (loginResult.success === false) {
|
|
76
|
+
return loginResult;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// TODO Move this to the top level
|
|
81
|
+
// await updateFee();
|
|
82
|
+
} catch (err) {
|
|
83
|
+
// errorFxn(PaimaMiddlewareErrorCode.ERROR_UPDATING_FEE, err);
|
|
84
|
+
// The fee has a default value, so this is not fatal and we can continue.
|
|
85
|
+
// If the fee has increased beyond the default value, posting won't work.
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
if (loginInfo.checkChainId !== false) {
|
|
89
|
+
if (!(await verifyWalletChain(loginInfo.chain))) {
|
|
90
|
+
if (!(await switchChain(loginInfo.chain))) {
|
|
91
|
+
return { success: false, errorMessage: "EVM_CHAIN_SWITCH" };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} catch (err) {
|
|
96
|
+
return { success: false, errorMessage: "EVM_CHAIN_SWITCH" };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
result: loginResult.result,
|
|
102
|
+
};
|
|
103
|
+
}
|