Files
metamask-integration/chain138-snap/packages/site/src/utils/metamask.ts

121 lines
3.0 KiB
TypeScript

import type {
EIP6963AnnounceProviderEvent,
MetaMaskInpageProvider,
} from '@metamask/providers';
/**
* Check if the current provider supports snaps by calling `wallet_getSnaps`.
*
* @param provider - The provider to use to check for snaps support. Defaults to
* `window.ethereum`.
* @returns True if the provider supports snaps, false otherwise.
*/
export async function hasSnapsSupport(
provider: MetaMaskInpageProvider = window.ethereum,
) {
try {
await provider.request({
method: 'wallet_getSnaps',
});
return true;
} catch {
return false;
}
}
/**
* Get a MetaMask provider using EIP6963. This will return the first provider
* reporting as MetaMask. If no provider is found after 500ms, this will
* return null instead.
*
* @returns A MetaMask provider if found, otherwise null.
*/
export async function getMetaMaskEIP6963Provider() {
return new Promise<MetaMaskInpageProvider | null>((resolve) => {
// Timeout looking for providers after 500ms
const timeout = setTimeout(() => {
resolveWithCleanup(null);
}, 500);
/**
* Resolve the promise with a MetaMask provider and clean up.
*
* @param provider - A MetaMask provider if found, otherwise null.
*/
function resolveWithCleanup(provider: MetaMaskInpageProvider | null) {
window.removeEventListener(
'eip6963:announceProvider',
onAnnounceProvider,
);
clearTimeout(timeout);
resolve(provider);
}
/**
* Listener for the EIP6963 announceProvider event.
*
* Resolves the promise if a MetaMask provider is found.
*
* @param event - The EIP6963 announceProvider event.
* @param event.detail - The details of the EIP6963 announceProvider event.
*/
function onAnnounceProvider({ detail }: EIP6963AnnounceProviderEvent) {
if (!detail) {
return;
}
const { info, provider } = detail;
if (info.rdns.includes('io.metamask')) {
resolveWithCleanup(provider);
}
}
window.addEventListener('eip6963:announceProvider', onAnnounceProvider);
window.dispatchEvent(new Event('eip6963:requestProvider'));
});
}
/**
* Get a provider that supports snaps. This will loop through all the detected
* providers and return the first one that supports snaps.
*
* @returns The provider, or `null` if no provider supports snaps.
*/
export async function getSnapsProvider() {
if (typeof window === 'undefined') {
return null;
}
if (await hasSnapsSupport()) {
return window.ethereum;
}
if (window.ethereum?.detected) {
for (const provider of window.ethereum.detected) {
if (await hasSnapsSupport(provider)) {
return provider;
}
}
}
if (window.ethereum?.providers) {
for (const provider of window.ethereum.providers) {
if (await hasSnapsSupport(provider)) {
return provider;
}
}
}
const eip6963Provider = await getMetaMaskEIP6963Provider();
if (eip6963Provider && (await hasSnapsSupport(eip6963Provider))) {
return eip6963Provider;
}
return null;
}