-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add metamask-snap support #208
Changes from 7 commits
55ada0c
aa4422c
aba9d5d
0706e11
2db83df
d7308ab
d5c025e
f4e0155
8fb30cd
a299ff5
e5cd513
7c02312
537b0b2
71a9d09
1b9d7f6
8f457ad
e6fb82b
9319a74
05d95a1
f6d9d26
c6b2321
dd9ad50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { | ||
DisconnectedStarknetWindowObject, | ||
RpcMessage, | ||
} from "../StarknetWindowObject" | ||
import wallets, { WalletProvider } from "../discovery" | ||
import type { MetaMaskProvider } from "@consensys/get-starknet/dist/type" | ||
import detectEthereumProvider from "@metamask/detect-provider" | ||
import type { ProviderInterface } from "starknet" | ||
|
||
class EmptyMetaMaskProvider implements DisconnectedStarknetWindowObject { | ||
id: string | ||
name: string | ||
icon: string | ||
version = "0.0.0" | ||
isConnected = false as const | ||
provider: undefined | ProviderInterface | ||
constructor( | ||
private metamaskProvider: MetaMaskProvider, | ||
walletInfo: WalletProvider, | ||
) { | ||
this.id = walletInfo.id | ||
this.name = walletInfo.name | ||
this.icon = walletInfo.icon | ||
} | ||
request<T extends RpcMessage>(): Promise<T["result"]> { | ||
throw new Error("Wallet not enabled") | ||
} | ||
async enable(options?: { starknetVersion?: "v4" | "v5" }): Promise<string[]> { | ||
const { MetaMaskSnapWallet } = await import("@consensys/get-starknet") | ||
const metaMaskSnapWallet = new MetaMaskSnapWallet(this.metamaskProvider) | ||
naorye2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Object.assign(this, metaMaskSnapWallet) | ||
this.constructor.prototype = metaMaskSnapWallet.constructor.prototype | ||
naorye2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return await this.enable(options) | ||
naorye2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
isPreauthorized() { | ||
return Promise.resolve(false) | ||
} | ||
on() { | ||
throw new Error("Wallet not enabled") | ||
} | ||
off() { | ||
throw new Error("Wallet not enabled") | ||
} | ||
} | ||
|
||
async function waitForEthereumProvider( | ||
options: { timeout?: number; retries?: number } = {}, | ||
): Promise<MetaMaskProvider | null> { | ||
const { timeout = 3000, retries = 0 } = options | ||
|
||
let provider: MetaMaskProvider | null = null | ||
try { | ||
provider = await detectEthereumProvider({ timeout }) | ||
} catch { | ||
// Silent error - do nothing | ||
} | ||
|
||
if (provider) { | ||
return provider | ||
} | ||
|
||
if (retries === 0) { | ||
return null | ||
} | ||
|
||
provider = await waitForEthereumProvider({ timeout, retries: retries - 1 }) | ||
return provider | ||
} | ||
|
||
async function hasSnapSupport(provider: MetaMaskProvider) { | ||
try { | ||
await provider.request({ method: "wallet_getSnaps" }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use non-api call to check for starknet support (not just and snaps?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hi @avimak the life cycle of a snap is working like that way
so you can only cehck if the metamask support starknet by first checking if it has supported snap In addtional, metamask does not inject any extra method to the provider (window.ethereum) to verify if it is support snap, it means, you must using request method to determined does it help to clarify your questions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @avimak |
||
return true | ||
} catch { | ||
return false | ||
} | ||
} | ||
|
||
async function detectMetamaskSupport() { | ||
const provider = await waitForEthereumProvider({ retries: 3 }) | ||
if (!provider) { | ||
return null | ||
} | ||
|
||
const snapSupport = await hasSnapSupport(provider) | ||
if (!snapSupport) { | ||
return null | ||
} | ||
|
||
return provider | ||
} | ||
|
||
async function injectMetamaskBridge() { | ||
if (window.hasOwnProperty("starknet_metamask")) { | ||
return | ||
} | ||
|
||
const provider = await detectMetamaskSupport() | ||
if (!provider) { | ||
return | ||
} | ||
|
||
const metamaskWalletInfo = wallets.find((wallet) => wallet.id === "metamask") | ||
if (!metamaskWalletInfo) { | ||
return | ||
} | ||
|
||
window.starknet_metamask = new EmptyMetaMaskProvider( | ||
provider, | ||
metamaskWalletInfo, | ||
) | ||
} | ||
|
||
export { injectMetamaskBridge } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pass
ssrSafeWindow
to avoid assumingwindow
availability later insideinjectMetamaskBridge
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.