-
Notifications
You must be signed in to change notification settings - Fork 619
[SDK] Feature: Adds EIP-1193 Adapter #5354
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
6e69eee
abstract wallet support
joaquim-verges a12a0cb
feat(sdk/eip1193): emit events
gregfromstl d16fb21
Merge branch 'main' into 11-06-abstract_wallet_support
gregfromstl 8233b77
feat(sdk/eip1193): add switch chain handler
gregfromstl 6730c9c
test: Add test suite for fromProvider in EIP-1193 adapter
gregfromstl 87f49f2
test: Add comprehensive test suite for toProvider function
gregfromstl 418a83d
fix(sdk/eip1193): remove incorrect jsdoc
gregfromstl 6f62d1b
docs: Add comprehensive JSDoc for `toProvider` EIP-1193 adapter
gregfromstl 669071d
docs: Add comprehensive JSDoc for EIP-1193 provider adapter function
gregfromstl 1b5a98f
Merge branch 'main' into 11-06-abstract_wallet_support
gregfromstl ed14777
chore(sdk): adds eip1193 changeset
gregfromstl a7dc471
docs(sdk/eip1193): recommend named imports
gregfromstl 8460f6a
feat: Add comprehensive EIP1193 adapters with conversion and provider…
gregfromstl c044224
fix(sdk/eip1193): failing test
gregfromstl a7c5bb0
Merge branch 'main' into 11-06-abstract_wallet_support
gregfromstl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import { trackConnect } from "../../analytics/track/connect.js"; | ||
| import type { Chain } from "../../chains/types.js"; | ||
| import { getCachedChainIfExists } from "../../chains/utils.js"; | ||
| import { | ||
| autoConnectEip1193Wallet, | ||
| connectEip1193Wallet, | ||
| } from "../../wallets/injected/index.js"; | ||
| import type { Account, Wallet } from "../../wallets/interfaces/wallet.js"; | ||
| import { createWalletEmitter } from "../../wallets/wallet-emitter.js"; | ||
| import type { WalletId } from "../../wallets/wallet-types.js"; | ||
| import type { EIP1193Provider } from "./types.js"; | ||
|
|
||
| export type FromEip1193AdapterOptions = { | ||
| provider: EIP1193Provider | (() => Promise<EIP1193Provider>); | ||
| walletId?: WalletId; | ||
| }; | ||
|
|
||
| /** | ||
| * Converts an EIP1193 provider to a Thirdweb wallet. | ||
| * | ||
| * @param options - The options for converting an EIP1193 provider to a Thirdweb wallet. | ||
| * @returns A Thirdweb wallet. | ||
| * @example | ||
| * ```ts | ||
| * import { EIP1193 } from "thirdweb/wallets"; | ||
| * const wallet = EIP1193.fromProvider({ provider }); | ||
| * | ||
| * // ... now you can use wallet with ConnectButton, useConnect, etc | ||
| * ``` | ||
| * @walletUtils | ||
| */ | ||
| export function fromProvider(options: FromEip1193AdapterOptions): Wallet { | ||
| const id: WalletId = options.walletId ?? "adapter"; | ||
| const emitter = createWalletEmitter(); | ||
| let account: Account | undefined = undefined; | ||
| let chain: Chain | undefined = undefined; | ||
| let provider: EIP1193Provider | undefined = undefined; | ||
| const getProvider = async () => { | ||
| if (!provider) { | ||
| provider = | ||
| typeof options.provider === "function" | ||
| ? await options.provider() | ||
| : options.provider; | ||
| } | ||
| return provider; | ||
| }; | ||
|
|
||
| const unsubscribeChain = emitter.subscribe("chainChanged", (newChain) => { | ||
| chain = newChain; | ||
| }); | ||
|
|
||
| function reset() { | ||
| account = undefined; | ||
| chain = undefined; | ||
| } | ||
|
|
||
| let handleDisconnect = async () => {}; | ||
|
|
||
| const unsubscribeDisconnect = emitter.subscribe("disconnect", () => { | ||
| reset(); | ||
| unsubscribeChain(); | ||
| unsubscribeDisconnect(); | ||
| }); | ||
|
|
||
| emitter.subscribe("accountChanged", (_account) => { | ||
| account = _account; | ||
| }); | ||
|
|
||
| let handleSwitchChain: (chain: Chain) => Promise<void> = async () => { | ||
| throw new Error("Not implemented"); | ||
| }; | ||
|
|
||
| return { | ||
| id: options.walletId as WalletId, | ||
| subscribe: emitter.subscribe, | ||
| getConfig: () => undefined, | ||
| getChain() { | ||
| if (!chain) { | ||
| return undefined; | ||
| } | ||
|
|
||
| chain = getCachedChainIfExists(chain.id) || chain; | ||
| return chain; | ||
| }, | ||
| getAccount: () => account, | ||
| connect: async (connectOptions) => { | ||
| const [connectedAccount, connectedChain, doDisconnect, doSwitchChain] = | ||
| await connectEip1193Wallet({ | ||
| id, | ||
| provider: await getProvider(), | ||
| client: connectOptions.client, | ||
| chain: connectOptions.chain, | ||
| emitter, | ||
| }); | ||
| // set the states | ||
| account = connectedAccount; | ||
| chain = connectedChain; | ||
| handleDisconnect = doDisconnect; | ||
| handleSwitchChain = doSwitchChain; | ||
| trackConnect({ | ||
| client: connectOptions.client, | ||
| walletType: id, | ||
| walletAddress: account.address, | ||
| }); | ||
| // return account | ||
| return account; | ||
| }, | ||
| autoConnect: async (connectOptions) => { | ||
| const [connectedAccount, connectedChain, doDisconnect, doSwitchChain] = | ||
| await autoConnectEip1193Wallet({ | ||
| id, | ||
| provider: await getProvider(), | ||
| emitter, | ||
| chain: connectOptions.chain, | ||
| client: connectOptions.client, | ||
| }); | ||
| // set the states | ||
| account = connectedAccount; | ||
| chain = connectedChain; | ||
| handleDisconnect = doDisconnect; | ||
| handleSwitchChain = doSwitchChain; | ||
| trackConnect({ | ||
| client: connectOptions.client, | ||
| walletType: id, | ||
| walletAddress: account.address, | ||
| }); | ||
| // return account | ||
| return account; | ||
| }, | ||
| disconnect: async () => { | ||
| reset(); | ||
| await handleDisconnect(); | ||
| }, | ||
| switchChain: (c) => handleSwitchChain(c), | ||
gregfromstl marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| export { | ||
| type FromEip1193AdapterOptions, | ||
| fromProvider, | ||
| } from "./from-eip1193.js"; | ||
|
|
||
| export { | ||
| type ToEip1193ProviderOptions, | ||
| toProvider, | ||
| } from "./to-eip1193.js"; | ||
|
|
||
| export type { EIP1193Provider } from "./types.js"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| import type { Account } from "viem/accounts"; | ||
|
|
||
| import type { Chain } from "../../chains/types.js"; | ||
| import type { ThirdwebClient } from "../../client/client.js"; | ||
| import { getRpcClient } from "../../rpc/rpc.js"; | ||
| import { estimateGas } from "../../transaction/actions/estimate-gas.js"; | ||
| import { sendTransaction } from "../../transaction/actions/send-transaction.js"; | ||
| import { prepareTransaction } from "../../transaction/prepare-transaction.js"; | ||
| import type { Wallet } from "../../wallets/interfaces/wallet.js"; | ||
| import type { EIP1193Provider } from "./types.js"; | ||
|
|
||
| export type ToEip1193ProviderOptions = { | ||
| wallet: Wallet; | ||
| chain: Chain; | ||
| client: ThirdwebClient; | ||
| connectOverride?: (wallet: Wallet) => Promise<Account>; | ||
| }; | ||
|
|
||
| /** | ||
| * Converts an EIP1193 provider to a Thirdweb wallet. | ||
gregfromstl marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * | ||
| * @param options - The options for converting an EIP1193 provider to a Thirdweb wallet. | ||
| * @returns A Thirdweb wallet. | ||
| * @example | ||
| * ```ts | ||
| * import { EIP1193 } from "thirdweb/wallets"; | ||
| * const provider = EIP1193.toProvider({ wallet, chain, client }); | ||
| * | ||
| * // ... now you can use this providers with third party libraries | ||
| * ``` | ||
| * @walletUtils | ||
| */ | ||
| export function toProvider(options: ToEip1193ProviderOptions): EIP1193Provider { | ||
| const { chain, client, wallet, connectOverride } = options; | ||
| const rpcClient = getRpcClient({ client, chain }); | ||
| return { | ||
| on: wallet.subscribe, | ||
| removeListener: () => { | ||
| // should invoke the return fn from subscribe instead | ||
| }, | ||
| request: async (request) => { | ||
| if (request.method === "eth_sendTransaction") { | ||
| const account = wallet.getAccount(); | ||
| if (!account) { | ||
| throw new Error("Account not connected"); | ||
| } | ||
| const result = await sendTransaction({ | ||
| transaction: prepareTransaction({ | ||
| ...request.params[0], | ||
| chain, | ||
| client, | ||
| }), | ||
| account: account, | ||
| }); | ||
| return result.transactionHash; | ||
| } | ||
| if (request.method === "eth_estimateGas") { | ||
| const account = wallet.getAccount(); | ||
| if (!account) { | ||
| throw new Error("Account not connected"); | ||
| } | ||
| return estimateGas({ | ||
| transaction: prepareTransaction({ | ||
| ...request.params[0], | ||
| chain, | ||
| client, | ||
| }), | ||
| account, | ||
| }); | ||
| } | ||
| if (request.method === "personal_sign") { | ||
| const account = wallet.getAccount(); | ||
| if (!account) { | ||
| throw new Error("Account not connected"); | ||
| } | ||
| return account.signMessage({ | ||
| message: { | ||
| raw: request.params[0], | ||
| }, | ||
| }); | ||
| } | ||
| if (request.method === "eth_signTypedData_v4") { | ||
| const account = wallet.getAccount(); | ||
| if (!account) { | ||
| throw new Error("Account not connected"); | ||
| } | ||
| const data = JSON.parse(request.params[1]); | ||
| return account.signTypedData(data); | ||
| } | ||
| if (request.method === "eth_accounts") { | ||
| const account = wallet.getAccount(); | ||
| if (!account) { | ||
| throw new Error("Account not connected"); | ||
| } | ||
| return [account.address]; | ||
| } | ||
| if (request.method === "eth_requestAccounts") { | ||
| const account = connectOverride | ||
| ? await connectOverride(wallet) | ||
| : await wallet.connect({ | ||
| client, | ||
| }); | ||
| if (!account) { | ||
| throw new Error("Unable to connect wallet"); | ||
| } | ||
| return [account.address]; | ||
| } | ||
| return rpcClient(request); | ||
| }, | ||
| }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // loose interface on purpose to adapt to any version of viem, ethers, ox, web3js, etc | ||
| export type EIP1193Provider = { | ||
| // biome-ignore lint/suspicious/noExplicitAny: <explanation> | ||
| on(event: any, listener: (params: any) => any): void; | ||
| // biome-ignore lint/suspicious/noExplicitAny: <explanation> | ||
| removeListener(event: any, listener: (params: any) => any): void; | ||
| // biome-ignore lint/suspicious/noExplicitAny: <explanation> | ||
| request: (params: any) => Promise<any>; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.