@@ -36,14 +36,69 @@ export type DebugEventBrowserProvider = {
36
36
error : Error
37
37
} ;
38
38
39
+ /**
40
+ * Provider info provided by the [[link-eip-6963]] discovery mechanism.
41
+ */
42
+ export interface Eip6963ProviderInfo {
43
+ uuid : string ;
44
+ name : string ;
45
+ icon : string ;
46
+ rdns : string ;
47
+ }
48
+
49
+ interface Eip6963ProviderDetail {
50
+ info : Eip6963ProviderInfo ;
51
+ provider : Eip1193Provider ;
52
+ }
53
+
54
+ interface Eip6963Announcement {
55
+ type : "eip6963:announceProvider" ;
56
+ detail : Eip6963ProviderDetail
57
+ }
58
+
39
59
export type BrowserProviderOptions = {
40
60
polling ?: boolean ;
41
61
staticNetwork ?: null | boolean | Network ;
42
62
43
63
cacheTimeout ?: number ;
44
64
pollingInterval ?: number ;
65
+
66
+ providerInfo ?: Eip6963ProviderInfo ;
45
67
} ;
46
68
69
+ /**
70
+ * Specifies how [[link-eip-6963]] discovery should proceed.
71
+ *
72
+ * See: [[BrowserProvider-discover]]
73
+ */
74
+ export interface BrowserDiscoverOptions {
75
+ /**
76
+ * Override provider detection with this provider.
77
+ */
78
+ provider ?: Eip1193Provider ;
79
+
80
+ /**
81
+ * Duration to wait to detect providers. (default: 300ms)
82
+ */
83
+ timeout ?: number ;
84
+
85
+ /**
86
+ * Return the first detected provider. Otherwise wait for %%timeout%%
87
+ * and allowing filtering before selecting the desired provider.
88
+ */
89
+ anyProvider ?: boolean ;
90
+
91
+ /**
92
+ * Use the provided window context. Useful in non-standard
93
+ * environments or to hijack where a provider comes from.
94
+ */
95
+ window ?: any ;
96
+ }
97
+
98
+ // @TODO : Future, provide some sort of filter mechansm callback along
99
+ // with exposing the following types
100
+
101
+
47
102
/**
48
103
* A **BrowserProvider** is intended to wrap an injected provider which
49
104
* adheres to the [[link-eip-1193]] standard, which most (if not all)
@@ -52,11 +107,14 @@ export type BrowserProviderOptions = {
52
107
export class BrowserProvider extends JsonRpcApiPollingProvider {
53
108
#request: ( method : string , params : Array < any > | Record < string , any > ) => Promise < any > ;
54
109
110
+ #providerInfo: null | Eip6963ProviderInfo ;
111
+
55
112
/**
56
113
* Connect to the %%ethereum%% provider, optionally forcing the
57
114
* %%network%%.
58
115
*/
59
116
constructor ( ethereum : Eip1193Provider , network ?: Networkish , _options ?: BrowserProviderOptions ) {
117
+
60
118
// Copy the options
61
119
const options : JsonRpcApiProviderOptions = Object . assign ( { } ,
62
120
( ( _options != null ) ? _options : { } ) ,
@@ -66,6 +124,11 @@ export class BrowserProvider extends JsonRpcApiPollingProvider {
66
124
67
125
super ( network , options ) ;
68
126
127
+ this . #providerInfo = null ;
128
+ if ( _options && _options . providerInfo ) {
129
+ this . #providerInfo = _options . providerInfo ;
130
+ }
131
+
69
132
this . #request = async ( method : string , params : Array < any > | Record < string , any > ) => {
70
133
const payload = { method, params } ;
71
134
this . emit ( "debug" , { action : "sendEip1193Request" , payload } ) ;
@@ -84,6 +147,10 @@ export class BrowserProvider extends JsonRpcApiPollingProvider {
84
147
} ;
85
148
}
86
149
150
+ get providerInfo ( ) : null | Eip6963ProviderInfo {
151
+ return this . #providerInfo;
152
+ }
153
+
87
154
async send ( method : string , params : Array < any > | Record < string , any > ) : Promise < any > {
88
155
await this . _start ( ) ;
89
156
@@ -109,7 +176,7 @@ export class BrowserProvider extends JsonRpcApiPollingProvider {
109
176
error = JSON . parse ( JSON . stringify ( error ) ) ;
110
177
111
178
// EIP-1193 gives us some machine-readable error codes, so rewrite
112
- // them into
179
+ // them into Ethers standard errors.
113
180
switch ( error . error . code || - 1 ) {
114
181
case 4001 :
115
182
error . error . message = `ethers-user-denied: ${ error . error . message } ` ;
@@ -142,9 +209,7 @@ export class BrowserProvider extends JsonRpcApiPollingProvider {
142
209
143
210
if ( ! ( await this . hasSigner ( address ) ) ) {
144
211
try {
145
- //const resp =
146
212
await this . #request( "eth_requestAccounts" , [ ] ) ;
147
- //console.log("RESP", resp);
148
213
149
214
} catch ( error : any ) {
150
215
const payload = error . payload ;
@@ -154,4 +219,60 @@ export class BrowserProvider extends JsonRpcApiPollingProvider {
154
219
155
220
return await super . getSigner ( address ) ;
156
221
}
222
+
223
+ /**
224
+ * Discover and connect to a Provider in the Browser using the
225
+ * [[link-eip-6963]] discovery mechanism. If no providers are
226
+ * present, ``null`` is resolved.
227
+ */
228
+ static async discover ( options ?: BrowserDiscoverOptions ) : Promise < null | BrowserProvider > {
229
+ if ( options == null ) { options = { } ; }
230
+
231
+ if ( options . provider ) {
232
+ return new BrowserProvider ( options . provider ) ;
233
+ }
234
+
235
+ const context = options . window ? options . window :
236
+ ( typeof ( window ) !== "undefined" ) ? window : null ;
237
+
238
+ if ( context == null ) { return null ; }
239
+
240
+ const anyProvider = options . anyProvider ;
241
+ if ( anyProvider && context . ethereum ) {
242
+ return new BrowserProvider ( context . ethereum ) ;
243
+ }
244
+
245
+ const timeout = options . timeout ? options . timeout : 300 ;
246
+ if ( timeout === 0 ) { return null ; }
247
+
248
+ return await ( new Promise ( ( resolve ) => {
249
+ let found : Array < any > = [ ] ;
250
+
251
+ const addProvider = ( event : Eip6963Announcement ) => {
252
+ found . push ( event . detail ) ;
253
+ if ( anyProvider ) { finalize ( ) ; }
254
+ } ;
255
+
256
+ const finalize = ( ) => {
257
+ clearTimeout ( timer ) ;
258
+ if ( found . length ) {
259
+ const { provider, info } = found [ 0 ] ;
260
+ resolve ( new BrowserProvider ( provider , undefined , {
261
+ providerInfo : info
262
+ } ) ) ;
263
+ } else {
264
+ resolve ( null ) ;
265
+ }
266
+ context . removeEventListener ( < any > "eip6963:announceProvider" ,
267
+ addProvider ) ;
268
+ } ;
269
+
270
+ const timer = setTimeout ( ( ) => { finalize ( ) ; } , timeout ) ;
271
+
272
+ context . addEventListener ( < any > "eip6963:announceProvider" ,
273
+ addProvider ) ;
274
+
275
+ context . dispatchEvent ( new Event ( "eip6963:requestProvider" ) ) ;
276
+ } ) ) ;
277
+ }
157
278
}
0 commit comments