@cartridge/controller 0.13.9 → 0.13.10
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/.turbo/turbo-build$colon$deps.log +13 -13
- package/dist/controller.d.ts +2 -1
- package/dist/iframe/keychain.d.ts +1 -1
- package/dist/{index-CJNujYxo.js → index-C7KGk-LM.js} +473 -198
- package/dist/index-C7KGk-LM.js.map +1 -0
- package/dist/index.js +834 -796
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/dist/session.js +4 -4
- package/dist/stats.html +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/utils.d.ts +0 -1
- package/dist/wallets/ethereum-base.d.ts +5 -1
- package/dist/wallets/metamask/index.d.ts +1 -0
- package/dist/wallets/phantom-evm/index.d.ts +1 -0
- package/package.json +3 -4
- package/src/__tests__/disconnect.test.ts +18 -0
- package/src/controller.ts +47 -4
- package/src/iframe/keychain.ts +5 -0
- package/src/types.ts +14 -0
- package/src/utils.ts +0 -8
- package/src/wallets/ethereum-base.ts +35 -48
- package/src/wallets/metamask/index.ts +6 -0
- package/src/wallets/phantom-evm/index.ts +4 -0
- package/dist/index-CJNujYxo.js.map +0 -1
- package/dist/telegram/backend.d.ts +0 -30
- package/dist/telegram/provider.d.ts +0 -24
- package/src/telegram/backend.ts +0 -43
- package/src/telegram/provider.ts +0 -148
package/dist/types.d.ts
CHANGED
|
@@ -91,6 +91,7 @@ export interface Keychain {
|
|
|
91
91
|
deploy(): Promise<DeployReply | ConnectError>;
|
|
92
92
|
execute(calls: Call | Call[], abis?: Abi[], transactionsDetail?: InvocationsDetails, sync?: boolean, feeSource?: any, error?: ControllerError): Promise<ExecuteReply | ConnectError>;
|
|
93
93
|
signMessage(typedData: TypedData, account: string, async?: boolean): Promise<Signature | ConnectError>;
|
|
94
|
+
updateSession(policies?: SessionPolicies, preset?: string): Promise<ConnectReply | ConnectError>;
|
|
94
95
|
openSettings(): Promise<void | ConnectError>;
|
|
95
96
|
session(): Promise<KeychainSession>;
|
|
96
97
|
sessions(): Promise<{
|
|
@@ -161,6 +162,8 @@ export type KeychainOptions = IFrameOptions & {
|
|
|
161
162
|
tokens?: Tokens;
|
|
162
163
|
/** When true, defer iframe mounting until connect() is called. Reduces initial load and resource fetching. */
|
|
163
164
|
lazyload?: boolean;
|
|
165
|
+
/** When true, force WebAuthn operations to run in a popup window instead of the iframe. Useful for development and testing. */
|
|
166
|
+
webauthnPopup?: boolean;
|
|
164
167
|
};
|
|
165
168
|
export type ProfileContextTypeVariant = "inventory" | "trophies" | "achievements" | "quests" | "leaderboard" | "activity";
|
|
166
169
|
export type Token = "eth" | "strk" | "lords" | "usdc" | "usdt";
|
|
@@ -187,6 +190,13 @@ export interface ConnectOptions {
|
|
|
187
190
|
/** Required when signer is "password" */
|
|
188
191
|
password?: string;
|
|
189
192
|
}
|
|
193
|
+
/** Options for updating session policies at runtime */
|
|
194
|
+
export type UpdateSessionOptions = {
|
|
195
|
+
/** Session policies to set */
|
|
196
|
+
policies?: SessionPolicies;
|
|
197
|
+
/** Preset name to resolve policies from */
|
|
198
|
+
preset?: string;
|
|
199
|
+
};
|
|
190
200
|
export type HeadlessConnectOptions = Required<Pick<ConnectOptions, "username" | "signer">> & Pick<ConnectOptions, "password">;
|
|
191
201
|
export type HeadlessConnectReply = {
|
|
192
202
|
code: ResponseCodes.SUCCESS;
|
package/dist/utils.d.ts
CHANGED
|
@@ -23,5 +23,4 @@ export declare function toWasmPolicies(policies: ParsedSessionPolicies): Policy[
|
|
|
23
23
|
export declare function toArray<T>(val: T | T[]): T[];
|
|
24
24
|
export declare function humanizeString(str: string): string;
|
|
25
25
|
export declare function parseChainId(url: URL): ChainId;
|
|
26
|
-
export declare function isMobile(): boolean;
|
|
27
26
|
export declare function sanitizeImageSrc(src: string): string;
|
|
@@ -6,12 +6,16 @@ export declare abstract class EthereumWalletBase implements WalletAdapter {
|
|
|
6
6
|
abstract readonly displayName: string;
|
|
7
7
|
platform: ExternalPlatform | undefined;
|
|
8
8
|
protected account: string | undefined;
|
|
9
|
-
protected store: import('mipd').Store;
|
|
10
9
|
protected provider: EIP6963ProviderDetail | undefined;
|
|
11
10
|
protected connectedAccounts: string[];
|
|
12
11
|
constructor();
|
|
13
12
|
private getProvider;
|
|
14
13
|
private getEthereumProvider;
|
|
14
|
+
/**
|
|
15
|
+
* Fallback provider detection when EIP-6963 announcement is missed.
|
|
16
|
+
* Subclasses can override to provide wallet-specific fallback logic.
|
|
17
|
+
*/
|
|
18
|
+
protected getFallbackProvider(): any;
|
|
15
19
|
private initializeIfAvailable;
|
|
16
20
|
private initialized;
|
|
17
21
|
private initializeProvider;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cartridge/controller",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.10",
|
|
4
4
|
"description": "Cartridge Controller",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,14 +26,13 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@cartridge/controller-wasm": "0.9.
|
|
29
|
+
"@cartridge/controller-wasm": "0.9.6",
|
|
30
30
|
"@cartridge/penpal": "^6.2.4",
|
|
31
31
|
"micro-sol-signer": "^0.5.0",
|
|
32
32
|
"bs58": "^6.0.0",
|
|
33
33
|
"ethers": "^6.13.5",
|
|
34
34
|
"@starknet-io/get-starknet-wallet-standard": "5.0.0-beta.0",
|
|
35
35
|
"@starknet-io/types-js": "0.9.1",
|
|
36
|
-
"@telegram-apps/sdk": "^2.4.0",
|
|
37
36
|
"@turnkey/sdk-browser": "^4.0.0",
|
|
38
37
|
"cbor-x": "^1.5.0",
|
|
39
38
|
"starknet": "^8.5.2",
|
|
@@ -56,7 +55,7 @@
|
|
|
56
55
|
"vite-plugin-node-polyfills": "^0.23.0",
|
|
57
56
|
"vite-plugin-top-level-await": "^1.4.4",
|
|
58
57
|
"vite-plugin-wasm": "^3.4.1",
|
|
59
|
-
"@cartridge/tsconfig": "0.13.
|
|
58
|
+
"@cartridge/tsconfig": "0.13.10"
|
|
60
59
|
},
|
|
61
60
|
"scripts": {
|
|
62
61
|
"build:deps": "pnpm build:browser && pnpm build:node",
|
|
@@ -98,6 +98,24 @@ describe("ControllerProvider.disconnect", () => {
|
|
|
98
98
|
expect(keychainDisconnect).toHaveBeenCalledTimes(1);
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
+
test("closes iframe to reset keychain state for subsequent connect", async () => {
|
|
102
|
+
const controller = new ControllerProvider({});
|
|
103
|
+
const keychainDisconnect = jest.fn().mockResolvedValue(undefined);
|
|
104
|
+
(controller as any).keychain = {
|
|
105
|
+
disconnect: keychainDisconnect,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const mockClose = jest.fn();
|
|
109
|
+
(controller as any).iframes = {
|
|
110
|
+
keychain: { close: mockClose },
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
await controller.disconnect();
|
|
114
|
+
|
|
115
|
+
expect(keychainDisconnect).toHaveBeenCalledTimes(1);
|
|
116
|
+
expect(mockClose).toHaveBeenCalledTimes(1);
|
|
117
|
+
});
|
|
118
|
+
|
|
101
119
|
test("does not throw when localStorage is unavailable", async () => {
|
|
102
120
|
delete (global as any).localStorage;
|
|
103
121
|
const controller = new ControllerProvider({});
|
package/src/controller.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
OpenOptions,
|
|
32
32
|
HeadlessUsernameLookupResult,
|
|
33
33
|
StarterpackOptions,
|
|
34
|
+
UpdateSessionOptions,
|
|
34
35
|
} from "./types";
|
|
35
36
|
import { validateRedirectUrl } from "./url-validator";
|
|
36
37
|
import { parseChainId } from "./utils";
|
|
@@ -308,8 +309,9 @@ export default class ControllerProvider extends BaseProvider {
|
|
|
308
309
|
return this.account;
|
|
309
310
|
}
|
|
310
311
|
|
|
311
|
-
|
|
312
|
-
|
|
312
|
+
if (!headless) {
|
|
313
|
+
this.iframes.keychain.open();
|
|
314
|
+
}
|
|
313
315
|
|
|
314
316
|
// Use connect() parameter if provided, otherwise fall back to constructor options
|
|
315
317
|
const effectiveOptions = Array.isArray(options)
|
|
@@ -352,7 +354,6 @@ export default class ControllerProvider extends BaseProvider {
|
|
|
352
354
|
}
|
|
353
355
|
console.log(e);
|
|
354
356
|
} finally {
|
|
355
|
-
// Only close modal if it was opened (not headless)
|
|
356
357
|
if (!headless) {
|
|
357
358
|
this.iframes.keychain.close();
|
|
358
359
|
}
|
|
@@ -412,7 +413,8 @@ export default class ControllerProvider extends BaseProvider {
|
|
|
412
413
|
return;
|
|
413
414
|
}
|
|
414
415
|
|
|
415
|
-
|
|
416
|
+
await this.keychain.disconnect();
|
|
417
|
+
return this.close();
|
|
416
418
|
}
|
|
417
419
|
|
|
418
420
|
async openProfile(tab: ProfileContextTypeVariant = "inventory") {
|
|
@@ -508,6 +510,47 @@ export default class ControllerProvider extends BaseProvider {
|
|
|
508
510
|
this.iframes.keychain.close();
|
|
509
511
|
}
|
|
510
512
|
|
|
513
|
+
async updateSession(options: UpdateSessionOptions = {}) {
|
|
514
|
+
if (!options.policies && !options.preset) {
|
|
515
|
+
throw new Error("Either `policies` or `preset` must be provided");
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (!this.iframes) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Ensure iframe is created if using lazy loading
|
|
523
|
+
if (!this.iframes.keychain) {
|
|
524
|
+
this.iframes.keychain = this.createKeychainIframe();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
await this.waitForKeychain();
|
|
528
|
+
|
|
529
|
+
if (!this.keychain || !this.iframes.keychain) {
|
|
530
|
+
console.error(new NotReadyToConnect().message);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
this.iframes.keychain.open();
|
|
535
|
+
|
|
536
|
+
try {
|
|
537
|
+
const response = await this.keychain.updateSession(
|
|
538
|
+
options.policies,
|
|
539
|
+
options.preset,
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
if (response.code !== ResponseCodes.SUCCESS) {
|
|
543
|
+
throw new Error((response as ConnectError).message);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return response as ConnectReply;
|
|
547
|
+
} catch (e) {
|
|
548
|
+
console.error(e);
|
|
549
|
+
} finally {
|
|
550
|
+
this.iframes.keychain.close();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
511
554
|
revoke(origin: string, _policy: Policy[]) {
|
|
512
555
|
if (!this.keychain) {
|
|
513
556
|
console.error(new NotReadyToConnect().message);
|
package/src/iframe/keychain.ts
CHANGED
|
@@ -40,6 +40,7 @@ export class KeychainIFrame extends IFrame<Keychain> {
|
|
|
40
40
|
encryptedBlob,
|
|
41
41
|
propagateSessionErrors,
|
|
42
42
|
errorDisplayMode,
|
|
43
|
+
webauthnPopup,
|
|
43
44
|
...iframeOptions
|
|
44
45
|
}: KeychainIframeOptions) {
|
|
45
46
|
let onStarterpackPlayHandler: (() => Promise<void>) | undefined;
|
|
@@ -101,6 +102,10 @@ export class KeychainIFrame extends IFrame<Keychain> {
|
|
|
101
102
|
_url.searchParams.set("should_override_preset_policies", "true");
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
if (webauthnPopup) {
|
|
106
|
+
_url.searchParams.set("webauthn_popup", "true");
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
// Policy precedence logic:
|
|
105
110
|
// 1. If shouldOverridePresetPolicies is true and policies are provided, use policies
|
|
106
111
|
// 2. Otherwise, if preset is defined, ignore provided policies
|
package/src/types.ts
CHANGED
|
@@ -157,6 +157,10 @@ export interface Keychain {
|
|
|
157
157
|
account: string,
|
|
158
158
|
async?: boolean,
|
|
159
159
|
): Promise<Signature | ConnectError>;
|
|
160
|
+
updateSession(
|
|
161
|
+
policies?: SessionPolicies,
|
|
162
|
+
preset?: string,
|
|
163
|
+
): Promise<ConnectReply | ConnectError>;
|
|
160
164
|
openSettings(): Promise<void | ConnectError>;
|
|
161
165
|
session(): Promise<KeychainSession>;
|
|
162
166
|
sessions(): Promise<{
|
|
@@ -255,6 +259,8 @@ export type KeychainOptions = IFrameOptions & {
|
|
|
255
259
|
tokens?: Tokens;
|
|
256
260
|
/** When true, defer iframe mounting until connect() is called. Reduces initial load and resource fetching. */
|
|
257
261
|
lazyload?: boolean;
|
|
262
|
+
/** When true, force WebAuthn operations to run in a popup window instead of the iframe. Useful for development and testing. */
|
|
263
|
+
webauthnPopup?: boolean;
|
|
258
264
|
};
|
|
259
265
|
|
|
260
266
|
export type ProfileContextTypeVariant =
|
|
@@ -295,6 +301,14 @@ export interface ConnectOptions {
|
|
|
295
301
|
password?: string;
|
|
296
302
|
}
|
|
297
303
|
|
|
304
|
+
/** Options for updating session policies at runtime */
|
|
305
|
+
export type UpdateSessionOptions = {
|
|
306
|
+
/** Session policies to set */
|
|
307
|
+
policies?: SessionPolicies;
|
|
308
|
+
/** Preset name to resolve policies from */
|
|
309
|
+
preset?: string;
|
|
310
|
+
};
|
|
311
|
+
|
|
298
312
|
export type HeadlessConnectOptions = Required<
|
|
299
313
|
Pick<ConnectOptions, "username" | "signer">
|
|
300
314
|
> &
|
package/src/utils.ts
CHANGED
|
@@ -256,14 +256,6 @@ export function parseChainId(url: URL): ChainId {
|
|
|
256
256
|
throw new Error(`Chain ${url.toString()} not supported`);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
export function isMobile() {
|
|
260
|
-
return (
|
|
261
|
-
window.matchMedia("(max-width: 768px)").matches ||
|
|
262
|
-
"ontouchstart" in window ||
|
|
263
|
-
navigator.maxTouchPoints > 0
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
259
|
// Sanitize image src to prevent XSS
|
|
268
260
|
export function sanitizeImageSrc(src: string): string {
|
|
269
261
|
// Allow only http/https URLs (absolute)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getAddress } from "ethers/address";
|
|
2
|
-
import { createStore, EIP6963ProviderDetail } from "mipd";
|
|
3
|
-
import { isMobile } from "../utils";
|
|
2
|
+
import { createStore, EIP6963ProviderDetail, Store } from "mipd";
|
|
4
3
|
import { chainIdToPlatform } from "./platform";
|
|
5
4
|
import {
|
|
6
5
|
ExternalPlatform,
|
|
@@ -10,6 +9,17 @@ import {
|
|
|
10
9
|
WalletAdapter,
|
|
11
10
|
} from "./types";
|
|
12
11
|
|
|
12
|
+
// Shared store across all EthereumWalletBase instances so late EIP-6963
|
|
13
|
+
// announcements are captured once and visible to every wallet adapter.
|
|
14
|
+
let sharedStore: Store | undefined;
|
|
15
|
+
|
|
16
|
+
function getSharedStore(): Store {
|
|
17
|
+
if (!sharedStore) {
|
|
18
|
+
sharedStore = createStore();
|
|
19
|
+
}
|
|
20
|
+
return sharedStore;
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
export abstract class EthereumWalletBase implements WalletAdapter {
|
|
14
24
|
abstract readonly type: ExternalWalletType;
|
|
15
25
|
abstract readonly rdns: string;
|
|
@@ -17,7 +27,6 @@ export abstract class EthereumWalletBase implements WalletAdapter {
|
|
|
17
27
|
|
|
18
28
|
platform: ExternalPlatform | undefined;
|
|
19
29
|
protected account: string | undefined = undefined;
|
|
20
|
-
protected store = createStore();
|
|
21
30
|
protected provider: EIP6963ProviderDetail | undefined;
|
|
22
31
|
protected connectedAccounts: string[] = [];
|
|
23
32
|
|
|
@@ -26,10 +35,10 @@ export abstract class EthereumWalletBase implements WalletAdapter {
|
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
private getProvider(): EIP6963ProviderDetail | undefined {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
// Use shared store's findProvider which reflects late announcements
|
|
39
|
+
const found = getSharedStore().findProvider({ rdns: this.rdns as any });
|
|
40
|
+
if (found) {
|
|
41
|
+
this.provider = found;
|
|
33
42
|
}
|
|
34
43
|
return this.provider;
|
|
35
44
|
}
|
|
@@ -40,15 +49,14 @@ export abstract class EthereumWalletBase implements WalletAdapter {
|
|
|
40
49
|
return provider.provider;
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.rdns === "io.metamask" &&
|
|
46
|
-
typeof window !== "undefined" &&
|
|
47
|
-
(window as any).ethereum?.isMetaMask
|
|
48
|
-
) {
|
|
49
|
-
return (window as any).ethereum;
|
|
50
|
-
}
|
|
52
|
+
return this.getFallbackProvider();
|
|
53
|
+
}
|
|
51
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Fallback provider detection when EIP-6963 announcement is missed.
|
|
57
|
+
* Subclasses can override to provide wallet-specific fallback logic.
|
|
58
|
+
*/
|
|
59
|
+
protected getFallbackProvider(): any {
|
|
52
60
|
return null;
|
|
53
61
|
}
|
|
54
62
|
|
|
@@ -101,29 +109,20 @@ export abstract class EthereumWalletBase implements WalletAdapter {
|
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
isAvailable(): boolean {
|
|
104
|
-
if (isMobile()) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
112
|
// Check dynamically each time, as the provider might be announced after instantiation
|
|
109
113
|
const provider = this.getProvider();
|
|
110
114
|
|
|
111
|
-
// Also check for MetaMask via window.ethereum as a fallback for MetaMask specifically
|
|
112
|
-
if (
|
|
113
|
-
!provider &&
|
|
114
|
-
this.rdns === "io.metamask" &&
|
|
115
|
-
typeof window !== "undefined"
|
|
116
|
-
) {
|
|
117
|
-
// MetaMask might be available via window.ethereum even if not announced via EIP-6963 yet
|
|
118
|
-
return !!(window as any).ethereum?.isMetaMask;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
115
|
// Initialize if we just found the provider
|
|
122
116
|
if (provider && !this.initialized) {
|
|
123
117
|
this.initializeIfAvailable();
|
|
124
118
|
}
|
|
125
119
|
|
|
126
|
-
|
|
120
|
+
if (provider) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fall back to wallet-specific detection when EIP-6963 announcement is missed
|
|
125
|
+
return typeof window !== "undefined" && !!this.getFallbackProvider();
|
|
127
126
|
}
|
|
128
127
|
|
|
129
128
|
getInfo(): ExternalWallet {
|
|
@@ -158,18 +157,7 @@ export abstract class EthereumWalletBase implements WalletAdapter {
|
|
|
158
157
|
throw new Error(`${this.displayName} is not available`);
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
const provider = this.getProvider();
|
|
163
|
-
|
|
164
|
-
if (provider) {
|
|
165
|
-
ethereum = provider.provider;
|
|
166
|
-
} else if (
|
|
167
|
-
this.rdns === "io.metamask" &&
|
|
168
|
-
(window as any).ethereum?.isMetaMask
|
|
169
|
-
) {
|
|
170
|
-
// Fallback for MetaMask when not announced via EIP-6963
|
|
171
|
-
ethereum = (window as any).ethereum;
|
|
172
|
-
}
|
|
160
|
+
const ethereum = this.getEthereumProvider();
|
|
173
161
|
|
|
174
162
|
if (!ethereum) {
|
|
175
163
|
throw new Error(`${this.displayName} provider not found`);
|
|
@@ -183,15 +171,14 @@ export abstract class EthereumWalletBase implements WalletAdapter {
|
|
|
183
171
|
this.account = getAddress(accounts[0]);
|
|
184
172
|
this.connectedAccounts = accounts.map(getAddress);
|
|
185
173
|
|
|
186
|
-
// If we used
|
|
187
|
-
if (!
|
|
188
|
-
// Create a mock EIP6963ProviderDetail for consistency
|
|
174
|
+
// If we used a fallback provider, store it for future use
|
|
175
|
+
if (!this.getProvider()) {
|
|
189
176
|
this.provider = {
|
|
190
177
|
info: {
|
|
191
|
-
uuid:
|
|
192
|
-
name:
|
|
178
|
+
uuid: `${this.rdns}-fallback`,
|
|
179
|
+
name: this.displayName,
|
|
193
180
|
icon: "data:image/svg+xml;base64,",
|
|
194
|
-
rdns:
|
|
181
|
+
rdns: this.rdns,
|
|
195
182
|
},
|
|
196
183
|
provider: ethereum,
|
|
197
184
|
} as EIP6963ProviderDetail;
|
|
@@ -5,4 +5,10 @@ export class MetaMaskWallet extends EthereumWalletBase {
|
|
|
5
5
|
readonly type: ExternalWalletType = "metamask";
|
|
6
6
|
readonly rdns = "io.metamask";
|
|
7
7
|
readonly displayName = "MetaMask";
|
|
8
|
+
|
|
9
|
+
protected getFallbackProvider(): any {
|
|
10
|
+
return (window as any).ethereum?.isMetaMask
|
|
11
|
+
? (window as any).ethereum
|
|
12
|
+
: null;
|
|
13
|
+
}
|
|
8
14
|
}
|
|
@@ -5,4 +5,8 @@ export class PhantomEVMWallet extends EthereumWalletBase {
|
|
|
5
5
|
readonly type: ExternalWalletType = "phantom-evm";
|
|
6
6
|
readonly rdns = "app.phantom";
|
|
7
7
|
readonly displayName = "Phantom";
|
|
8
|
+
|
|
9
|
+
protected getFallbackProvider(): any {
|
|
10
|
+
return (window as any).phantom?.ethereum ?? null;
|
|
11
|
+
}
|
|
8
12
|
}
|