web3.js
web3.js copied to clipboard
Implement EIP-6963 - Multi Injected Provider Discovery
EIP-6963 offers an alternative approach to using window.ethereum
for injected providers within the browser. Instead of relying on window.ethereum
to be the intended injected provider to be used by libraries like web3.js, this EIP gives us the ability to request available injected providers made available by wallet browser extensions
One way to implement this EIP for web3.js is for us to add a function, eip6963RequestProvider()
, that initializes an event lister for eip6963:announceProvider
events, and dispatches the event: eip6963:requestProvider
:
/**
* Represents the assets needed to display a wallet
*/
interface EIP6963ProviderInfo {
uuid: string;
name: string;
icon: string;
}
interface EIP6963ProviderDetail {
info: EIP6963ProviderInfo;
provider: EIP1193Provider;
}
interface EIP6963AnnounceProviderEvent extends CustomEvent {
type: "eip6963:announceProvider";
detail: EIP6963ProviderDetail;
}
const eip6963ProviderDetails: EIP6963ProviderDetail[] = [];
const initializedEip6963Lister = false;
function eip6963RequestProvider() {
if (window === undefined)
throw new Error(
"window object not available, EIP-6963 is intended to be used within a browser"
);
if (!initializedEip6963Lister) {
// Not sure if this is necessary, but seemed like a good idea
initializedEip6963Lister = true;
window.addEventListener(
"eip6963:announceProvider",
(event: EIP6963AnnounceProviderEvent) => {
eip6963ProviderDetails.push(Object.freeze(event.detail));
}
);
}
window.dispatchEvent(new Event("eip6963:requestProvider"));
}
This function, eip6963RequestProvider
, could exist on the dApps instantiated Web3
instance, giving them access to the eip6963ProviderDetails
array so that they could display the available wallets/providers to the dApp user as desired
After the web3.js user calls eip6963RequestProvider()
and has access to a populated eip6963ProviderDetails
, the user could call web3.setProvider(eip6963ProviderDetail)
where eip6963ProviderDetail
is the dApp user selected provider
References
- https://eips.ethereum.org/EIPS/eip-6963
- https://ethereum-magicians.org/t/eip-6963-multi-injected-provider-discovery/14076/5
- https://github.com/walletconnect/eip6963
- https://twitter.com/i/spaces/1DXxyvprYjPKM
- https://eip6963.org/
why doesn't eip6963RequestProvider
return Web3PromiEvent so user could do something like
eip6963RequestProvider().on("provider", (providerDetail: EIP6963ProviderDetail) => {
if(bestProvider) {
web3 = new Web3(providerDetail.provider)
}
})
`˙`
With having more than one provider available, just thinking if we it would be good to have a removal function for EIP6963ProviderDetail
@mpetrunic Yea, that's a good point, we should utilize a promiEvent
to give the user the chance to respond to announced providers
So a refactored approach using a promiEvent
:
import { Web3PromiEvent } from "web3-core";
interface EIP6963ProviderInfo {
uuid: string;
name: string;
icon: string;
}
interface EIP6963ProviderDetail {
info: EIP6963ProviderInfo;
provider: EIP1193Provider;
}
interface EIP6963AnnounceProviderEvent extends CustomEvent {
type: "eip6963:announceProvider";
detail: EIP6963ProviderDetail;
}
export class Web3 {
static eip6963RequestProvider() {
const promiEvent = new Web3PromiEvent((resolve, reject) => {
if (window === undefined)
reject(
new Error(
"window object not available, EIP-6963 is intended to be used within a browser"
)
);
window.addEventListener(
"eip6963:announceProvider",
(event: EIP6963AnnounceProviderEvent) =>
promiEvent.emit(
"eip6963:announceProvider",
Object.freeze(event.detail)
)
);
window.dispatchEvent(new Event("eip6963:requestProvider"));
resolve();
});
return promiEvent;
}
}
And an example web3.js user's implementation:
import { Web3 } from "web3";
const eip6963ProviderDetails: EIP6963ProviderDetail[] = [];
Web3.eip6963RequestProvider().on(
"eip6963:announceProvider",
(providerDetail: EIP6963ProviderDetail) =>
eip6963ProviderDetails.push(providerDetail)
);
const selectedProvider = await askTheUserWhichWalletToUse(
eip6963ProviderDetails
);
const web3 = new Web3(selectedProvider);
@luu-alex With the refactored approach, since the web3.js user is now responsible for managing eip6963ProviderDetails
, maintaining this list is no longer our responsibility - Web3.js is merely a pass through for provider details