-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
index.ts
207 lines (176 loc) · 6.27 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import { AbstractConnectorArguments, ConnectorUpdate } from '@web3-react/types'
import { AbstractConnector } from '@web3-react/abstract-connector'
import warning from 'tiny-warning'
import { SendReturnResult, SendReturn, Send, SendOld } from './types'
function parseSendReturn(sendReturn: SendReturnResult | SendReturn): any {
return sendReturn.hasOwnProperty('result') ? sendReturn.result : sendReturn
}
export class NoEthereumProviderError extends Error {
public constructor() {
super()
this.name = this.constructor.name
this.message = 'No Ethereum provider was found on window.ethereum.'
}
}
export class UserRejectedRequestError extends Error {
public constructor() {
super()
this.name = this.constructor.name
this.message = 'The user rejected the request.'
}
}
export class InjectedConnector extends AbstractConnector {
constructor(kwargs: AbstractConnectorArguments) {
super(kwargs)
this.handleNetworkChanged = this.handleNetworkChanged.bind(this)
this.handleChainChanged = this.handleChainChanged.bind(this)
this.handleAccountsChanged = this.handleAccountsChanged.bind(this)
this.handleClose = this.handleClose.bind(this)
}
private handleChainChanged(chainId: string | number): void {
if (__DEV__) {
console.log("Handling 'chainChanged' event with payload", chainId)
}
this.emitUpdate({ chainId, provider: window.ethereum })
}
private handleAccountsChanged(accounts: string[]): void {
if (__DEV__) {
console.log("Handling 'accountsChanged' event with payload", accounts)
}
if (accounts.length === 0) {
this.emitDeactivate()
} else {
this.emitUpdate({ account: accounts[0] })
}
}
private handleClose(code: number, reason: string): void {
if (__DEV__) {
console.log("Handling 'close' event with payload", code, reason)
}
this.emitDeactivate()
}
private handleNetworkChanged(networkId: string | number): void {
if (__DEV__) {
console.log("Handling 'networkChanged' event with payload", networkId)
}
this.emitUpdate({ chainId: networkId, provider: window.ethereum })
}
public async activate(): Promise<ConnectorUpdate> {
if (!window.ethereum) {
throw new NoEthereumProviderError()
}
if (window.ethereum.on) {
window.ethereum.on('chainChanged', this.handleChainChanged)
window.ethereum.on('accountsChanged', this.handleAccountsChanged)
window.ethereum.on('close', this.handleClose)
window.ethereum.on('networkChanged', this.handleNetworkChanged)
}
if ((window.ethereum as any).isMetaMask) {
;(window.ethereum as any).autoRefreshOnNetworkChange = false
}
// try to activate + get account via eth_requestAccounts
let account
try {
account = await (window.ethereum.send as Send)('eth_requestAccounts').then(
sendReturn => parseSendReturn(sendReturn)[0]
)
} catch (error) {
if ((error as any).code === 4001) {
throw new UserRejectedRequestError()
}
warning(false, 'eth_requestAccounts was unsuccessful, falling back to enable')
}
// if unsuccessful, try enable
if (!account) {
// if enable is successful but doesn't return accounts, fall back to getAccount (not happy i have to do this...)
account = await window.ethereum.enable().then(sendReturn => sendReturn && parseSendReturn(sendReturn)[0])
}
return { provider: window.ethereum, ...(account ? { account } : {}) }
}
public async getProvider(): Promise<any> {
return window.ethereum
}
public async getChainId(): Promise<number | string> {
if (!window.ethereum) {
throw new NoEthereumProviderError()
}
let chainId
try {
chainId = await (window.ethereum.send as Send)('eth_chainId').then(parseSendReturn)
} catch {
warning(false, 'eth_chainId was unsuccessful, falling back to net_version')
}
if (!chainId) {
try {
chainId = await (window.ethereum.send as Send)('net_version').then(parseSendReturn)
} catch {
warning(false, 'net_version was unsuccessful, falling back to net version v2')
}
}
if (!chainId) {
try {
chainId = parseSendReturn((window.ethereum.send as SendOld)({ method: 'net_version' }))
} catch {
warning(false, 'net_version v2 was unsuccessful, falling back to manual matches and static properties')
}
}
if (!chainId) {
if ((window.ethereum as any).isDapper) {
chainId = parseSendReturn((window.ethereum as any).cachedResults.net_version)
} else {
chainId =
(window.ethereum as any).chainId ||
(window.ethereum as any).netVersion ||
(window.ethereum as any).networkVersion ||
(window.ethereum as any)._chainId
}
}
return chainId
}
public async getAccount(): Promise<null | string> {
if (!window.ethereum) {
throw new NoEthereumProviderError()
}
let account
try {
account = await (window.ethereum.send as Send)('eth_accounts').then(sendReturn => parseSendReturn(sendReturn)[0])
} catch {
warning(false, 'eth_accounts was unsuccessful, falling back to enable')
}
if (!account) {
try {
account = await window.ethereum.enable().then(sendReturn => parseSendReturn(sendReturn)[0])
} catch {
warning(false, 'enable was unsuccessful, falling back to eth_accounts v2')
}
}
if (!account) {
account = parseSendReturn((window.ethereum.send as SendOld)({ method: 'eth_accounts' }))[0]
}
return account
}
public deactivate() {
if (window.ethereum && window.ethereum.removeListener) {
window.ethereum.removeListener('chainChanged', this.handleChainChanged)
window.ethereum.removeListener('accountsChanged', this.handleAccountsChanged)
window.ethereum.removeListener('close', this.handleClose)
window.ethereum.removeListener('networkChanged', this.handleNetworkChanged)
}
}
public async isAuthorized(): Promise<boolean> {
if (!window.ethereum) {
return false
}
try {
return await (window.ethereum.send as Send)('eth_accounts').then(sendReturn => {
if (parseSendReturn(sendReturn).length > 0) {
return true
} else {
return false
}
})
} catch {
return false
}
}
}