-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.ts
143 lines (128 loc) · 4.5 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { InjectedConnector } from 'wagmi/connectors/injected'
import { WindowProvider, ConnectorNotFoundError } from 'wagmi'
import {
ProviderRpcError,
ResourceNotFoundRpcError,
UserRejectedRequestError,
getAddress,
} from "viem";
import type { Address } from "abitype";
import type { Chain } from "@wagmi/core/chains";
type InjectedConnectorOptions = {
/** Name of connector */
name?: string | ((detectedName: string | string[]) => string)
/**
* [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider to target
*
* @default
* () => typeof window !== 'undefined' ? window.ethereum : undefined
*/
getProvider?: () => WindowProvider | undefined
/**
* MetaMask and other injected providers do not support programmatic disconnect.
* This flag simulates the disconnect behavior by keeping track of connection status in storage. See [GitHub issue](https://github.com/MetaMask/metamask-extension/issues/10353) for more info.
* @default true
*/
shimDisconnect?: boolean
}
declare global {
interface Window {
phantom: {
ethereum: WindowProvider;
};
}
}
export class PhantomConnector extends InjectedConnector {
readonly id = "phantom";
protected shimDisconnectKey = `${this.id}.shimDisconnect`;
constructor({
chains,
options: options_,
}: {
chains?: Chain[];
options?: InjectedConnectorOptions;
} = {}) {
const options = {
name: "Phantom",
shimDisconnect: true,
getProvider() {
function getReady(ethereum?: WindowProvider) {
const isPhantom = !!ethereum?.isPhantom;
if (!isPhantom) return;
return ethereum;
}
if (typeof window === "undefined") return;
const ethereum = window?.phantom?.ethereum as
| WindowProvider
| undefined;
if (ethereum?.providers) return ethereum.providers.find(getReady);
return getReady(ethereum);
},
...options_,
};
super({ chains, options });
}
async connect({ chainId }: { chainId?: number } = {}) {
try {
const provider = await this.getProvider();
if (!provider) throw new ConnectorNotFoundError();
if (provider.on) {
provider.on("accountsChanged", this.onAccountsChanged);
provider.on("chainChanged", this.onChainChanged);
provider.on("disconnect", this.onDisconnect);
}
this.emit("message", { type: "connecting" });
// Attempt to show wallet select prompt with `wallet_requestPermissions` when
// `shimDisconnect` is active and account is in disconnected state (flag in storage)
let account: Address | null = null;
if (
this.options?.shimDisconnect &&
!this.storage?.getItem(this.shimDisconnectKey)
) {
account = await this.getAccount().catch(() => null);
const isConnected = !!account;
if (isConnected)
// Attempt to show another prompt for selecting wallet if already connected
try {
await provider.request({
method: "wallet_requestPermissions",
params: [{ eth_accounts: {} }],
});
// User may have selected a different account so we will need to revalidate here.
account = await this.getAccount();
} catch (error) {
if (this.isUserRejectedRequestError(error))
throw new UserRejectedRequestError(error as Error);
if (
(error as ProviderRpcError).code ===
new ResourceNotFoundRpcError(error as ProviderRpcError).code
)
throw error;
}
}
if (!account) {
const accounts = await provider.request({
method: "eth_requestAccounts",
});
account = getAddress(accounts[0] as string);
}
// Switch to chain if provided
let id = await this.getChainId();
let unsupported = this.isChainUnsupported(id);
if (chainId && id !== chainId) {
const chain = await this.switchChain(chainId);
id = chain.id;
unsupported = this.isChainUnsupported(id);
}
if (this.options?.shimDisconnect)
this.storage?.setItem(this.shimDisconnectKey, true);
return { account, chain: { id, unsupported }, provider };
} catch (error) {
if (this.isUserRejectedRequestError(error))
throw new UserRejectedRequestError(error as Error);
if ((error as ProviderRpcError).code === -32002)
throw new ResourceNotFoundRpcError(error as ProviderRpcError);
throw error;
}
}
}