diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3da5ba2b586..8f6dd8785c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,9 +11,6 @@ guide in some places. - [Preview locally](#preview-locally) - [Style guide](#style-guide) - [Add images](#add-images) -- [Update the interactive API reference](#update-the-interactive-api-reference) - - [Update `MetaMask/api-specs`](#update-metamaskapi-specs) - - [Update `ethereum/execution-apis`](#update-ethereumexecution-apis) - [Test analytics](#test-analytics) ## Contribution workflow @@ -125,114 +122,6 @@ When adding a new image, such as a screenshot or diagram, make sure the image ha Additionally, follow the [Consensys guidelines on adding images](https://docs-template.consensys.net/contribute/add-images). -## Update the interactive API reference - -The [Wallet JSON-RPC API reference](https://docs.metamask.io/wallet/reference/json-rpc-api/) uses -an internal plugin to import and parse OpenRPC -specifications from [`MetaMask/api-specs`](https://github.com/MetaMask/api-specs) (MetaMask-specific -methods) and [`ethereum/execution-apis`](https://github.com/ethereum/execution-apis) (standard -Ethereum methods). -The site renders documentation for each method based on the specification, and displays an -interactive module to test the methods in your browser. - -### Update `MetaMask/api-specs` - -To update documentation for MetaMask-specific JSON-RPC API methods: - -1. Fork [`MetaMask/api-specs`](https://github.com/MetaMask/api-specs), clone the forked repository - to your computer, and navigate into it: - - ```bash - git clone git@github.com:/api-specs.git - cd api-specs - ``` - -2. Follow the repository's [`README.md`](https://github.com/MetaMask/api-specs/blob/main/README.md) - instructions to edit the OpenRPC specification and generate the output file, `openrpc.json`. - -3. To test the API updates in the MetaMask doc site's interactive reference: - - 1. Create and switch to a temporary local branch of the doc site, [`MetaMask/metamask-docs`](https://github.com/MetaMask/metamask-docs). - For example, to create and switch to a branch named `test-api-updates`: - ```bash - cd metamask-docs - git checkout -b test-api-updates - ``` - 2. Copy and paste the output file `openrpc.json` into the root directory of `metamask-docs`. - 3. Use [`http-server`](https://www.npmjs.com/package/http-server) to serve `openrpc.json` locally. - Install `http-server` if you haven't yet, and start the server: - ```bash - npm install --global http-server - http-server - ``` - The `openrpc.json` file is now served at [`http://127.0.0.1:8080/openrpc.json`](http://127.0.0.1:8080/openrpc.json). - 4. In `src/plugins/plugin-json-rpc.ts`, update the following line to point to the locally served `openrpc.json` file: - ```diff - - export const MM_RPC_URL = "https://metamask.github.io/api-specs/latest/openrpc.json"; - + export const MM_RPC_URL = "http://127.0.0.1:8080/openrpc.json"; - ``` - 5. In a new terminal window, preview the doc site locally: - ```bash - cd metamask-docs - npm start - ``` - 6. Navigate to the API reference, and view your updates. - -4. Add and commit your changes to `api-specs`, and create a PR. - -5. Once your PR is approved and merged, the following must happen to publish the changes to the - MetaMask doc site: - - 1. A new version of `api-specs` must be released by a user with write access to the repository. - To release, go to the [Create Release Pull Request](https://github.com/MetaMask/api-specs/actions/workflows/create-release-pr.yml) - action, select **Run workflow**, and enter a specific version to bump to in the last text box - (for example, `0.10.6`). This creates a PR releasing a version of `api-specs`. - 2. Once the release PR is merged, the [Publish Release](https://github.com/MetaMask/api-specs/actions/workflows/publish-release.yml) - action must be approved by an npm publisher. - You can request an approval in the **#metamask-dev** Slack channel tagging - **@metamask-npm-publishers**. - For example: - > @metamask-npm-publishers `@metamask/api-specs@0.10.6` is awaiting deployment :rocketship: - https://github.com/MetaMask/api-specs/actions/runs/10615788573 - -### Update `ethereum/execution-apis` - -To update documentation for standard Ethereum JSON-RPC API methods: - -1. Fork [`ethereum/execution-apis`](https://github.com/ethereum/execution-apis), clone the forked - repository to your computer, and navigate into it: - - ```bash - git clone git@github.com:/execution-apis.git - cd execution-apis - ``` - -2. Follow the repository's [`README.md`](https://github.com/ethereum/execution-apis/blob/main/README.md) - instructions to edit the OpenRPC specification and generate the output file, `openrpc.json`. - -3. To test the API updates in the MetaMask doc site's interactive reference, complete Step 3 in - [Update `MetaMask/api-specs`](#update-metamaskapi-specs). - -4. Add and commit your changes to `execution-apis`, and create a PR. - -5. Once your PR is approved and merged, the following must happen to publish the changes to the - MetaMask doc site: - - 1. `api-specs` must import the updated Ethereum API specification. - Go to the [commit history](https://github.com/ethereum/execution-apis/commits/assembled-spec/) - of the `assembled-spec` branch of `execution-apis`. - Copy the full commit hash of the latest commit titled "assemble openrpc.json." - Update the following line in `merge-openrpc.js` of `api-specs` with the updated commit hash, - and create a PR: - ```diff - const getFilteredExecutionAPIs = () => { - - return fetch("https://raw.githubusercontent.com/ethereum/execution-apis/ac19b518a2596221cd7cd6421ee3dc654d7ff3b7/refs-openrpc.json") - + return fetch("https://raw.githubusercontent.com/ethereum/execution-apis/f75d4cc8eeb5d1952bd69f901954686b74c34c9b/refs-openrpc.json") - ``` - 2. Once the change to `merge-openrpc.js` is merged, Step 5 in - [Update `MetaMask/api-specs`](#update-metamaskapi-specs) must be completed to publish the - changes to the MetaMask doc site. - ## Test analytics The [`docusaurus-plugin-segment`](https://github.com/xer0x/docusaurus-plugin-segment) plugin enables diff --git a/docusaurus.config.js b/docusaurus.config.js index eb2803e77e6..d1e87580735 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -5,12 +5,6 @@ import fs from 'fs' require('dotenv').config() const { themes } = require('prism-react-renderer') const { REF_ALLOW_LOGIN_PATH } = require('./src/lib/constants') -const { - fetchAndGenerateDynamicSidebarItems, - NETWORK_NAMES, - MM_REF_PATH, - MM_RPC_URL, -} = require('./src/plugins/plugin-json-rpc') const codeTheme = themes.dracula const productsDropdown = fs.readFileSync('./src/components/NavDropdown/Products.html', 'utf-8') const baseUrl = process.env.DEST || '/' @@ -254,31 +248,6 @@ const config = { rehypePlugins, }, ], - [ - '@docusaurus/plugin-content-docs', - { - id: 'wallet', - path: 'wallet', - routeBasePath: 'wallet', - editUrl: 'https://github.com/MetaMask/metamask-docs/edit/main/', - sidebarPath: require.resolve('./wallet-sidebar.js'), - breadcrumbs: false, - remarkPlugins, - rehypePlugins, - sidebarItemsGenerator: async function ({ defaultSidebarItemsGenerator, ...args }) { - const sidebarItems = await defaultSidebarItemsGenerator(args) - const dynamicItems = await fetchAndGenerateDynamicSidebarItems( - MM_RPC_URL, - MM_REF_PATH, - NETWORK_NAMES.metamask - ) - if (args.item.dirName === 'reference/json-rpc-methods') { - return [...sidebarItems, ...dynamicItems] - } - return sidebarItems - }, - }, - ], [ '@docusaurus/plugin-content-docs', { @@ -326,7 +295,7 @@ const config = { { name: 'keywords', content: - 'MetaMask, Embedded Wallets, Quickstart, Web3 Development, SDK, Wallet Integration, API, Dapp Development, Blockchain Development, Ethereum Development, Smart Contract, Account Abstraction, Snaps, Crypto Wallet, DeFi, NFT, Infura, Services, Dashboard', + 'MetaMask, Embedded Wallets, Quickstart, Web3 Development, SDK, MM Connect, Wallet Integration, API, Dapp Development, Blockchain Development, Ethereum Development, Smart Contract, Account Abstraction, Snaps, Crypto Wallet, DeFi, NFT, Infura, Services, Dashboard', }, // Twitter-specific meta tags { @@ -445,21 +414,17 @@ const config = { title: 'Documentation', items: [ { - label: 'SDK', + label: 'MM Connect', to: '/sdk', }, { - label: 'Wallet API', - to: '/wallet', + label: 'Embedded Wallets', + to: '/embedded-wallets', }, { label: 'Smart Accounts Kit', to: '/smart-accounts-kit', }, - { - label: 'Embedded Wallets', - to: '/embedded-wallets', - }, { label: 'Snaps', to: '/snaps', @@ -486,7 +451,7 @@ const config = { href: 'https://github.com/MetaMask/metamask-extension/', }, { - label: 'MetaMask SDK GitHub', + label: 'MM Connect GitHub', href: 'https://github.com/MetaMask/metamask-sdk/', }, { diff --git a/gator_versioned_docs/version-0.13.0/get-started/smart-account-quickstart/eip7702.md b/gator_versioned_docs/version-0.13.0/get-started/smart-account-quickstart/eip7702.md index 76169e33e02..c8eaf905a60 100644 --- a/gator_versioned_docs/version-0.13.0/get-started/smart-account-quickstart/eip7702.md +++ b/gator_versioned_docs/version-0.13.0/get-started/smart-account-quickstart/eip7702.md @@ -168,4 +168,4 @@ const userOperationHash = await bundlerClient.sendUserOperation({ - To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-smart-accounts-behalf.md). - To quickly bootstrap a MetaMask Smart Accounts project, [use the CLI](../use-the-cli.md). -- You can also [use MetaMask SDK to upgrade a MetaMask account to a smart account](/tutorials/upgrade-eoa-to-smart-account). +- You can also [use MM Connect to upgrade a MetaMask account to a smart account](/tutorials/upgrade-eoa-to-smart-account). diff --git a/gator_versioned_docs/version-0.13.0/guides/erc7715/execute-on-metamask-users-behalf.md b/gator_versioned_docs/version-0.13.0/guides/erc7715/execute-on-metamask-users-behalf.md index 4abdb7309a7..0968976d6cc 100644 --- a/gator_versioned_docs/version-0.13.0/guides/erc7715/execute-on-metamask-users-behalf.md +++ b/gator_versioned_docs/version-0.13.0/guides/erc7715/execute-on-metamask-users-behalf.md @@ -100,7 +100,7 @@ const sessionAccount = privateKeyToAccount("0x..."); Currently, ERC-7715 does not support automatically upgrading a MetaMask user's account to a [MetaMask smart account](../../concepts/smart-accounts.md). Therefore, you must ensure that the user is upgraded to a smart account before requesting ERC-7715 permissions. -If the user has not yet been upgraded, you can handle the upgrade [programmatically](/wallet/how-to/send-transactions/send-batch-transactions/#about-atomic-batch-transactions) or ask the +If the user has not yet been upgraded, you can handle the upgrade [programmatically](/sdk/evm/connect/guides/javascript/send-transactions/batch-transactions) or ask the user to [switch to a smart account manually](https://support.metamask.io/configure/accounts/switch-to-or-revert-from-a-smart-account/#how-to-switch-to-a-metamask-smart-account). :::info Why is a Smart Account upgrade is required? diff --git a/sdk-sidebar.js b/sdk-sidebar.js index 17b07f121d2..20e3b20f1c7 100644 --- a/sdk-sidebar.js +++ b/sdk-sidebar.js @@ -1,41 +1,124 @@ // @ts-check /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebar = { - sdkSidebar: [ +const sdkSidebar = { + overview: [ + 'index', + 'about', { - type: 'doc', - label: 'Introduction', - id: "index", + type: 'category', + label: 'Reference', + collapsible: false, + collapsed: false, + items: [ + 'reference/options', + ], }, + ], + multichain: [ + 'multichain/index', { type: 'category', - label: 'Connect to MetaMask', + label: 'Guides', collapsible: false, collapsed: false, items: [ - 'connect/javascript-wagmi', - 'connect/javascript', - 'connect/javascript-rainbowkit', - 'connect/javascript-connectkit', - 'connect/javascript-dynamic', - 'connect/javascript-web3auth', - 'connect/react-native', + 'multichain/connect/guides/connect-to-multichain', + 'multichain/connect/guides/send-transactions', + 'multichain/connect/guides/connector-libraries', ], }, + { + type: 'category', + label: 'Tutorials', + collapsible: false, + collapsed: false, + items: [ + 'multichain/connect/tutorials/create-multichain-dapp', + ], + }, + { + type: 'category', + label: 'Reference', + collapsible: false, + collapsed: false, + items: [ + 'multichain/connect/reference/api', + ], + }, + ], + evm: [ + 'evm/index', { type: 'category', label: 'Guides', collapsible: false, collapsed: false, items: [ - 'guides/authenticate-users', - 'guides/manage-networks', - 'guides/handle-transactions', - 'guides/interact-with-contracts', - 'guides/use-deeplinks', - 'guides/batch-requests', - 'guides/production-readiness', + { + type: 'category', + label: 'JavaScript', + collapsible: true, + collapsed: true, + link: { type: "doc", id: "evm/connect/guides/javascript/index" }, + items: [ + 'evm/connect/guides/javascript/manage-user-accounts', + 'evm/connect/guides/javascript/manage-networks', + { + type: 'category', + label: 'Send transactions', + collapsible: true, + collapsed: true, + link: { type: "doc", id: "evm/connect/guides/javascript/send-transactions/index" }, + items: [ + 'evm/connect/guides/javascript/send-transactions/batch-transactions', + ], + }, + { + type: 'category', + label: 'Sign data', + collapsible: true, + collapsed: true, + link: { type: "doc", id: "evm/connect/guides/javascript/sign-data/index" }, + items: [ + 'evm/connect/guides/javascript/sign-data/siwe', + ], + }, + 'evm/connect/guides/javascript/batch-requests', + 'evm/connect/guides/javascript/interact-with-contracts', + 'evm/connect/guides/javascript/use-deeplinks', + 'evm/connect/guides/javascript/display-tokens', + { + type: 'category', + label: 'Best practices', + collapsible: true, + collapsed: true, + items: [ + 'evm/connect/guides/javascript/best-practices/display', + 'evm/connect/guides/javascript/best-practices/run-devnet', + 'evm/connect/guides/javascript/best-practices/production-readiness', + ], + }, + ], + }, + { + type: 'category', + label: 'Wagmi', + collapsible: true, + collapsed: true, + link: { type: "doc", id: "evm/connect/guides/wagmi/index" }, + items: [ + 'evm/connect/guides/wagmi/manage-user-accounts', + 'evm/connect/guides/wagmi/manage-networks', + 'evm/connect/guides/wagmi/send-transactions', + 'evm/connect/guides/wagmi/interact-with-contracts', + ], + }, + 'evm/connect/guides/rainbowkit', + 'evm/connect/guides/connectkit', + 'evm/connect/guides/react-native', + 'evm/connect/guides/dynamic', + 'evm/connect/guides/web3auth', ], }, { @@ -49,7 +132,7 @@ const sidebar = { label: "Create a wallet AI agent", href: "/tutorials/create-wallet-ai-agent" }, - { + { type: "link", label: "Upgrade an EOA to a smart account", href: "/tutorials/upgrade-eoa-to-smart-account" @@ -62,13 +145,36 @@ const sidebar = { collapsible: false, collapsed: false, items: [ - 'reference/llm-prompt', - 'reference/supported-platforms', - 'reference/sdk-options', - 'reference/sdk-methods', + 'evm/connect/reference/methods', + 'evm/connect/reference/provider-api', + { + type: "category", + label: "JSON-RPC API", + collapsible: true, + collapsed: true, + link: { type: "doc", id: "evm/connect/reference/json-rpc-api/index" }, + items: [ + "evm/connect/reference/json-rpc-api/wallet_sendCalls", + "evm/connect/reference/json-rpc-api/eth_signTypedData_v4", + ], + }, + ], + }, + ], + solana: [ + 'solana/index', + { + type: 'category', + label: 'Guides', + collapsible: false, + collapsed: false, + items: [ + 'solana/connect/guides/use-wallet-adapter', + 'solana/connect/guides/send-legacy-transaction', + 'solana/connect/guides/send-versioned-transaction', ], }, ], } -module.exports = sidebar +module.exports = sdkSidebar diff --git a/wallet/assets/web3-architecture.png b/sdk/_assets/architecture.png similarity index 100% rename from wallet/assets/web3-architecture.png rename to sdk/_assets/architecture.png diff --git a/sdk/reference/supported-platforms.md b/sdk/about.md similarity index 63% rename from sdk/reference/supported-platforms.md rename to sdk/about.md index f8637c8cb91..f913987cb85 100644 --- a/sdk/reference/supported-platforms.md +++ b/sdk/about.md @@ -1,19 +1,15 @@ ---- -description: Supported dapp platforms for MetaMask SDK. -keywords: [SDK, supported, platforms, desktop, mobile, dapp] ---- +# About MM Connect -# Supported platforms +## Supported platforms -With MetaMask SDK, you can connect your dapp to MetaMask in the following ways: +With MM Connect, you can connect your dapp to MetaMask in the following ways: - **Desktop web dapps** - Automatically connect to the MetaMask extension, or connect to the MetaMask mobile app using a QR code. +- **Mobile dapps** - MM Connect generates a deeplink that takes users directly to the MetaMask mobile app. -- **Mobile dapps** - The SDK generates a deeplink that takes users directly to the MetaMask mobile app. +The following table expands on the supported connection methods: -The following table expands on the SDK's connection methods: - -| Dapp location | User wallet location | Connection method | MetaMask SDK | Other SDKs | +| Dapp location | User wallet location | Connection method | MM Connect | Other SDKs | |---------------|-------------|------------------|--------------------------|--------------------------| | Desktop web | Wallet browser extension | Automatic connection via browser extension | Supported | Supported | | Desktop web | Wallet mobile app | QR code scan with wallet mobile app | Supported | Limited | @@ -26,3 +22,13 @@ The following table expands on the SDK's connection methods: For a better user experience on mobile, it's important to use reliable RPC providers instead of public nodes. We recommend using services like [MetaMask Developer](https://developer.metamask.io/) to ensure better reliability and performance. ::: + +## Architecture + +[Old architecture diagram as an example, this should be updated:] + +

+ +![SDK architecture](_assets/architecture.png) + +

diff --git a/sdk/_assets/connect.gif b/sdk/evm/connect/_assets/connect.gif similarity index 100% rename from sdk/_assets/connect.gif rename to sdk/evm/connect/_assets/connect.gif diff --git a/sdk/_assets/network.gif b/sdk/evm/connect/_assets/network.gif similarity index 100% rename from sdk/_assets/network.gif rename to sdk/evm/connect/_assets/network.gif diff --git a/wallet/assets/personal_sign.png b/sdk/evm/connect/_assets/personal_sign.png similarity index 100% rename from wallet/assets/personal_sign.png rename to sdk/evm/connect/_assets/personal_sign.png diff --git a/sdk/_assets/quickstart-connectkit.png b/sdk/evm/connect/_assets/quickstart-connectkit.png similarity index 100% rename from sdk/_assets/quickstart-connectkit.png rename to sdk/evm/connect/_assets/quickstart-connectkit.png diff --git a/sdk/_assets/quickstart-dynamic.png b/sdk/evm/connect/_assets/quickstart-dynamic.png similarity index 100% rename from sdk/_assets/quickstart-dynamic.png rename to sdk/evm/connect/_assets/quickstart-dynamic.png diff --git a/sdk/_assets/quickstart-javascript.png b/sdk/evm/connect/_assets/quickstart-javascript.png similarity index 100% rename from sdk/_assets/quickstart-javascript.png rename to sdk/evm/connect/_assets/quickstart-javascript.png diff --git a/sdk/_assets/quickstart-rainbowkit.png b/sdk/evm/connect/_assets/quickstart-rainbowkit.png similarity index 100% rename from sdk/_assets/quickstart-rainbowkit.png rename to sdk/evm/connect/_assets/quickstart-rainbowkit.png diff --git a/sdk/_assets/quickstart-web3auth.png b/sdk/evm/connect/_assets/quickstart-web3auth.png similarity index 100% rename from sdk/_assets/quickstart-web3auth.png rename to sdk/evm/connect/_assets/quickstart-web3auth.png diff --git a/sdk/_assets/quickstart.jpg b/sdk/evm/connect/_assets/quickstart.jpg similarity index 100% rename from sdk/_assets/quickstart.jpg rename to sdk/evm/connect/_assets/quickstart.jpg diff --git a/wallet/assets/signTypedData.png b/sdk/evm/connect/_assets/signTypedData.png similarity index 100% rename from wallet/assets/signTypedData.png rename to sdk/evm/connect/_assets/signTypedData.png diff --git a/wallet/assets/siwe-bad-domain-2.png b/sdk/evm/connect/_assets/siwe-bad-domain-2.png similarity index 100% rename from wallet/assets/siwe-bad-domain-2.png rename to sdk/evm/connect/_assets/siwe-bad-domain-2.png diff --git a/wallet/assets/siwe-bad-domain.png b/sdk/evm/connect/_assets/siwe-bad-domain.png similarity index 100% rename from wallet/assets/siwe-bad-domain.png rename to sdk/evm/connect/_assets/siwe-bad-domain.png diff --git a/wallet/assets/siwe.png b/sdk/evm/connect/_assets/siwe.png similarity index 100% rename from wallet/assets/siwe.png rename to sdk/evm/connect/_assets/siwe.png diff --git a/wallet/assets/watchasset-nft-2.png b/sdk/evm/connect/_assets/watchasset-nft-2.png similarity index 100% rename from wallet/assets/watchasset-nft-2.png rename to sdk/evm/connect/_assets/watchasset-nft-2.png diff --git a/wallet/assets/watchasset-nft.png b/sdk/evm/connect/_assets/watchasset-nft.png similarity index 100% rename from wallet/assets/watchasset-nft.png rename to sdk/evm/connect/_assets/watchasset-nft.png diff --git a/sdk/connect/javascript-connectkit.md b/sdk/evm/connect/guides/connectkit.md similarity index 90% rename from sdk/connect/javascript-connectkit.md rename to sdk/evm/connect/guides/connectkit.md index 324a1451782..814056687ad 100644 --- a/sdk/connect/javascript-connectkit.md +++ b/sdk/evm/connect/guides/connectkit.md @@ -1,13 +1,13 @@ --- -description: Quickstart guide for using the MetaMask SDK with a JavaScript and ConnectKit dapp. +description: Quickstart guide for using MM Connect with a JavaScript and ConnectKit dapp. toc_max_heading_level: 3 -sidebar_label: JavaScript + ConnectKit +sidebar_label: ConnectKit keywords: [connect, MetaMask, JavaScript, ConnectKit, SDK, dapp, Wallet SDK] --- # Connect to MetaMask using JavaScript + ConnectKit -Get started with MetaMask SDK in a JavaScript and ConnectKit dapp. +Get started with MM Connect in a JavaScript and ConnectKit dapp. You can [download the quickstart template](#set-up-using-a-template) or [manually set up the SDK](#set-up-manually) in an existing dapp.

@@ -25,7 +25,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up using a template -1. Download the [MetaMask SDK ConnectKit template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/connectkit): +1. Download the [MM Connect ConnectKit template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/connectkit): ```bash npx degit MetaMask/metamask-sdk-examples/quickstarts/connectkit metamask-connectkit @@ -80,9 +80,9 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up manually -### 1. Install the SDK +### 1. Install MM Connect -Install MetaMask SDK along with its peer dependencies to an existing React project: +Install MM Connect along with its peer dependencies to an existing React project: ```bash npm2yarn npm install connectkit wagmi viem@2.x @tanstack/react-query diff --git a/sdk/connect/javascript-dynamic.md b/sdk/evm/connect/guides/dynamic.md similarity index 83% rename from sdk/connect/javascript-dynamic.md rename to sdk/evm/connect/guides/dynamic.md index 6ad58e262e1..be6e8e49213 100644 --- a/sdk/connect/javascript-dynamic.md +++ b/sdk/evm/connect/guides/dynamic.md @@ -1,14 +1,14 @@ --- sidebar_label: Dynamic SDK -description: Quickstart guide for using MetaMask SDK and Dynamic SDK. +description: Quickstart guide for using MM Connect and Dynamic SDK. toc_max_heading_level: 3 keywords: [connect, MetaMask, Dynamic, SDK, dapp, Wallet SDK] --- # Connect to MetaMask using Dynamic SDK -Get started with MetaMask SDK and [Dynamic SDK](https://docs.dynamic.xyz/introduction/welcome). -You can use MetaMask SDK features directly within Dynamic SDK. +Get started with MM Connect and [Dynamic SDK](https://docs.dynamic.xyz/introduction/welcome). +You can use MM Connect features directly within Dynamic SDK. You can [download the quickstart template](#set-up-using-a-template) or [manually set up the SDKs](#set-up-manually) in an existing dapp.

@@ -26,7 +26,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up using a template -1. Download the [MetaMask SDK + Dynamic SDK template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/partners/dynamic): +1. Download the [MM Connect + Dynamic SDK template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/partners/dynamic): ```bash npx degit MetaMask/metamask-sdk-examples/partners/dynamic metamask-dynamic @@ -42,15 +42,15 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall

Degit vs. Git clone
- `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. + `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. + + Alternatively, you can use `git clone`, which will download the entire repository. + To do so, clone the MM Connect examples repository and navigate into the `partners/dynamic` directory: - Alternatively, you can use `git clone`, which will download the entire repository. - To do so, clone the MetaMask SDK examples repository and navigate into the `partners/dynamic` directory: - - ```bash - git clone https://github.com/MetaMask/metamask-sdk-examples - cd metamask-sdk-examples/partners/dynamic - ``` + ```bash + git clone https://github.com/MetaMask/metamask-sdk-examples + cd metamask-sdk-examples/partners/dynamic + ```
@@ -79,14 +79,14 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall pnpm dev ``` -You've successfully set up MetaMask SDK and Dynamic SDK. +You've successfully set up MM Connect and Dynamic SDK. See how to [use the combined SDKs](#usage). ## Set up manually ### 1. Install dependencies -Install the SDK and the required dependencies to an existing project: +Install MM Connect and the required dependencies to an existing project: ```bash npm2yarn npm install @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @dynamic-labs/wagmi-connector wagmi viem @tanstack/react-query @@ -191,7 +191,7 @@ Before deploying your project to production: 1. Update your `next.config.ts` with production domains. 2. Set up proper environment variables. 3. Configure your Dynamic SDK environment ID. -4. Ensure MetaMask SDK is properly initialized. +4. Ensure MM Connect is properly initialized. ## Troubleshoot diff --git a/sdk/evm/connect/guides/javascript/batch-requests.md b/sdk/evm/connect/guides/javascript/batch-requests.md new file mode 100644 index 00000000000..ed1b777ea8d --- /dev/null +++ b/sdk/evm/connect/guides/javascript/batch-requests.md @@ -0,0 +1,82 @@ +--- +description: Batch multiple JSON-RPC requests using MM Connect. +keywords: [SDK, batch, JSON-RPC, RPC, requests, methods, dapp] +--- + +# Batch requests + +MM Connect provides a `metamask_batch` method to send multiple JSON-RPC requests in a single call. +These requests can be contract calls or other JSON-RPC methods (for example, signing messages or sending transactions). +Despite being batched into one HTTP request, each call still requires individual user approval, and if any request is rejected, the entire batch fails. + +:::info +"Batching" can also refer to [Wagmi contract read batching](../wagmi/interact-with-contracts.md#batch-contract-reads) or +[sending atomic batch transactions](send-transactions/batch-transactions.md) in MetaMask. +::: + +## Batch JSON-RPC requests + +You can directly use MM Connect's `metamask_batch` method to group multiple JSON-RPC requests into a single HTTP call. + +Use cases include: + +- **Batching multiple signatures** - Send multiple signing requests in one batch. +- **Switching networks** - Switch the EVM network, perform an action such as sending a transaction, and switch back, all in one batch. +- **Mixed transactions and signatures** - Combine transaction sending and signing requests in one batch. + +:::note +When using `metamask_batch`, keep in mind the following: + +- Even though the requests are batched, each individual request still requires user approval. +- If any request in the batch is rejected, the entire batch will fail. +::: + +The following is an example of batching JSON-RPC requests using `metamask_batch`: + +```js +import { createEVMClient } from "@metamask/connect/evm"; + +const evmClient = createEVMClient(); +const provider = evmClient.getProvider(); + +async function handleBatchRequests() { + // Example batch: one personal_sign call and one eth_sendTransaction call. + const requests = [ + { method: "personal_sign", params: ["Hello from batch!", "0x1234..."] }, + { + method: "eth_sendTransaction", + params: [ + { + from: "0x1234...", + to: "0xABCD...", + // Additional transaction parameters. + }, + ], + }, + ]; + + try { + const results = await provider.request({ + method: "metamask_batch", + params: [requests], + }); + console.log("Batch Results:", results); + } catch (err) { + console.error("Batch request failed:", err); + } +} + +document.getElementById("batchBtn").addEventListener("click", handleBatchRequests); +``` + +The following HTML displays a **Send Batch** button: + +```html + +``` + +:::tip Tips +- For a better user experience, it's important to use reliable RPC providers instead of public nodes. + We recommend using services like [MetaMask Developer](https://developer.metamask.io/) to ensure better reliability and performance. +- Ensure that requests in a batch do not depend on one another's chain context, as mid-batch state changes can affect outcomes. +::: diff --git a/sdk/evm/connect/guides/javascript/best-practices/display.md b/sdk/evm/connect/guides/javascript/best-practices/display.md new file mode 100644 index 00000000000..63c98c68f80 --- /dev/null +++ b/sdk/evm/connect/guides/javascript/best-practices/display.md @@ -0,0 +1,30 @@ +# Display in MetaMask + +## Display icons + +When your dapp makes a login request to a MetaMask user, MetaMask may render a modal that displays +your dapp icon. + +MetaMask retrieves this icon using the HTML selector ` link[rel="shortcut icon"]`, so you can +follow the [favicon standard](https://en.wikipedia.org/wiki/Favicon) to customize your dapp icon. +Make sure to have a `link` tag within your dapp's `head` with `rel = "shortcut icon"`, as in the +following example. +The tag's `href` attribute is used for assigning the dapp icon. + +```html + + + +``` + +## Display method names + +MetaMask uses the [Ethereum Signature Database](https://www.4byte.directory/) to display +method names on the confirmation screen. +For many common method names, such as token methods, this allows MetaMask to look up the method +names by their [method signature](https://solidity.readthedocs.io/en/v0.4.21/abi-spec.html). +However, sometimes you're using a method that isn't in that database, and MetaMask simply +displays **Contract Interaction** to the user. + +To register your contract's method names so they show in the MetaMask interface, +[submit each method's signature to the Ethereum Signature Database](https://www.4byte.directory/submit/). \ No newline at end of file diff --git a/sdk/guides/production-readiness.md b/sdk/evm/connect/guides/javascript/best-practices/production-readiness.md similarity index 78% rename from sdk/guides/production-readiness.md rename to sdk/evm/connect/guides/javascript/best-practices/production-readiness.md index ca62ef98c05..7b414191acb 100644 --- a/sdk/guides/production-readiness.md +++ b/sdk/evm/connect/guides/javascript/best-practices/production-readiness.md @@ -1,12 +1,12 @@ --- -description: MetaMask-specific production readiness checklist for dapps using MetaMask SDK. +description: MetaMask-specific production readiness checklist for dapps using MM Connect. keywords: [SDK, production, readiness, checklist, compatibility, errors, dapp] toc_max_heading_level: 2 --- # Production readiness -When using MetaMask SDK, ensure your dapp is production ready by focusing on these key areas unique to MetaMask: +When using MM Connect, ensure your dapp is production ready by focusing on these key areas unique to MetaMask: - [Wallet connection and mobile compatibility](#wallet-connection-and-mobile-compatibility) - [Reliable RPC endpoints](#reliable-rpc-endpoints) @@ -23,7 +23,7 @@ When using MetaMask SDK, ensure your dapp is production ready by focusing on the - **Custom RPC setup** - Use production-grade RPC endpoints and custom API keys by signing up on [MetaMask Developer](https://developer.metamask.io/). This improves reliability over public nodes. -- **Configuration** - Configure your Wagmi (or MetaMask SDK) setup with your custom RPC URL using environment variables. +- **Configuration** - Configure your Wagmi (or MM Connect) setup with your custom RPC URL using environment variables. For example: ```tsx title="Configure custom RPC endpoint" @@ -44,6 +44,6 @@ For example: - **Clear feedback** - Display user friendly messages when wallet connection or transaction errors occur (for example, network switch failures or user rejections). -- **Event management** - If you're using Vanilla JavaScript, handle MetaMask events such as [`chainChanged`](/wallet/reference/provider-api/#chainchanged) - and [`accountsChanged`](/wallet/reference/provider-api/#accountschanged) to promptly update the UI and internal state. +- **Event management** - If you're using Vanilla JavaScript, handle MetaMask events such as [`chainChanged`](../../../reference/provider-api.md#chainchanged) + and [`accountsChanged`](../../../reference/provider-api.md#accountschanged) to promptly update the UI and internal state. If you're using Wagmi, you generally don't need to handle MetaMask events, because the hooks will handle the events for you. diff --git a/wallet/how-to/run-devnet.md b/sdk/evm/connect/guides/javascript/best-practices/run-devnet.md similarity index 80% rename from wallet/how-to/run-devnet.md rename to sdk/evm/connect/guides/javascript/best-practices/run-devnet.md index 31b4fead3e5..89a054ce0f2 100644 --- a/wallet/how-to/run-devnet.md +++ b/sdk/evm/connect/guides/javascript/best-practices/run-devnet.md @@ -83,23 +83,14 @@ Follow these steps to connect MetaMask to Hardhat Network. :::tip Alternatively, you can add Hardhat Network to MetaMask using - [`wallet_addEthereumChain`](/wallet/reference/json-rpc-methods/wallet_addethereumchain/?AddEthereumChainParameter[rpcUrls][0]=http://127.0.0.1:8545&AddEthereumChainParameter[chainId]=0x539&AddEthereumChainParameter[chainName]=Hardhat&AddEthereumChainParameter[nativeCurrency][name]=testEth&AddEthereumChainParameter[nativeCurrency][symbol]=testEth&AddEthereumChainParameter[nativeCurrency][decimals]=18) - in the interactive API playground. + [`wallet_addEthereumChain`](../../../reference/json-rpc-api/index.md). ::: ## Reset your local nonce calculation If you restart your development network, you can accidentally confuse MetaMask -because it calculates the next [nonce](./send-transactions/index.md#nonce) based on both the +because it calculates the next nonce based on both the network state _and_ the known sent transactions. To clear MetaMask's transaction queue and reset its nonce calculation, go to **Settings > Advanced** and select **Reset account**. - -## Next steps - -Once you have your development environment set up and development network running, you can -[connect your dapp to MetaMask](/sdk) by setting up MetaMask SDK. - -For an end-to-end example, you can also follow the -[Create a simple React dapp](../tutorials/react-dapp-local-state.md) tutorial. diff --git a/wallet/how-to/display/tokens.md b/sdk/evm/connect/guides/javascript/display-tokens.md similarity index 72% rename from wallet/how-to/display/tokens.md rename to sdk/evm/connect/guides/javascript/display-tokens.md index e7c18948ea2..63471b28168 100644 --- a/wallet/how-to/display/tokens.md +++ b/sdk/evm/connect/guides/javascript/display-tokens.md @@ -14,7 +14,7 @@ Manually adding tokens involves the user interacting with contract addresses, an You can improve the security and experience of displaying your [ERC-20 token](#display-an-erc-20-token) or users' [NFTs](#display-nfts) in MetaMask by using the -[`wallet_watchAsset`](/wallet/reference/json-rpc-methods/wallet_watchasset) RPC method. +[`wallet_watchAsset`](../../reference/json-rpc-api/index.md) RPC method. `wallet_watchAsset` provides a friendly interface that prompts users to register tokens to their MetaMask wallet, without having to interact with contract addresses. @@ -38,6 +38,11 @@ extension (not on mobile). To prompt users to add an ERC-20 token, you can add something like the following to your project script: ```javascript +import { createEVMClient } from "@metamask/connect/evm"; + +const evmClient = createEVMClient(); +const provider = evmClient.getProvider(); + const tokenAddress = "0xd00981105e61274c8a5cd5a88fe7e037d935b513" const tokenSymbol = "TUT" const tokenDecimals = 18 @@ -45,23 +50,22 @@ const tokenImage = "http://placekitten.com/200/300" try { // 'wasAdded' is a boolean. Like any RPC method, an error can be thrown. - const wasAdded = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_watchAsset", - params: { - type: "ERC20", - options: { - // The address of the token. - address: tokenAddress, - // A ticker symbol or shorthand, up to 5 characters. - symbol: tokenSymbol, - // The number of decimals in the token. - decimals: tokenDecimals, - // A string URL of the token logo. - image: tokenImage, - }, + const wasAdded = await provider.request({ + method: "wallet_watchAsset", + params: { + type: "ERC20", + options: { + // The address of the token. + address: tokenAddress, + // A ticker symbol or shorthand, up to 5 characters. + symbol: tokenSymbol, + // The number of decimals in the token. + decimals: tokenDecimals, + // A string URL of the token logo. + image: tokenImage, }, - }) + }, + }) if (wasAdded) { console.log("Thanks for your interest!") @@ -75,8 +79,8 @@ try { :::note If the chain ID of your token doesn't match the user's network, they can get unexpected results. -We recommend [detecting the user's network chain ID](../manage-networks/detect-network.md) and -[prompting them to switch chains](/wallet/reference/json-rpc-methods/wallet_switchethereumchain), if necessary. +We recommend [detecting the user's network chain ID](manage-networks.md) and +prompting them to switch chains, if necessary. ::: For another example, [WatchToken](https://vittominacori.github.io/watch-token/create/) is a @@ -97,10 +101,10 @@ The add NFT interfaces look like the following:
- NFT confirmation + NFT confirmation
- Multiple NFTs confirmation + Multiple NFTs confirmation
@@ -110,21 +114,25 @@ To prompt users to add a single NFT, add something like the following to your pr `wallet_watchAsset` supports both ERC-721 and ERC-1155 NFT standards. ```javascript +import { createEVMClient } from "@metamask/connect/evm"; + +const evmClient = createEVMClient(); +const provider = evmClient.getProvider(); + try { // wasAdded is a boolean. Like any RPC method, an error can be thrown. - const wasAdded = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_watchAsset", - params: { - type: "ERC721", // Or "ERC1155". - options: { - // The address of the token. - address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", - // ERC-721 or ERC-1155 token ID. - tokenId: "1", - }, + const wasAdded = await provider.request({ + method: "wallet_watchAsset", + params: { + type: "ERC721", // Or "ERC1155". + options: { + // The address of the token. + address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", + // ERC-721 or ERC-1155 token ID. + tokenId: "1", }, - }) + }, + }) if (wasAdded) { console.log("User successfully added the token!") @@ -143,8 +151,7 @@ To prompt users to add multiple NFTs, use `sendAsync()` instead of For example: ```javascript -provider // Or window.ethereum if you don't support EIP-6963. - .sendAsync([{ +provider.sendAsync([{ method: "wallet_watchAsset", params: { type: "ERC721", @@ -164,5 +171,5 @@ provider // Or window.ethereum if you don't support EIP-6963. }, }, ... - ]) +]) ``` diff --git a/sdk/connect/javascript.md b/sdk/evm/connect/guides/javascript/index.md similarity index 63% rename from sdk/connect/javascript.md rename to sdk/evm/connect/guides/javascript/index.md index 0abd1a13ae3..6bdb709ac9d 100644 --- a/sdk/connect/javascript.md +++ b/sdk/evm/connect/guides/javascript/index.md @@ -1,5 +1,5 @@ --- -description: Quickstart guide for using the MetaMask SDK with a JavaScript dapp. +description: Quickstart guide for using MM Connect with a JavaScript dapp. sidebar_label: JavaScript keywords: [connect, MetaMask, JavaScript, SDK, dapp, Wallet SDK] --- @@ -9,12 +9,12 @@ import TabItem from "@theme/TabItem"; # Connect to MetaMask using JavaScript -Get started with MetaMask SDK in your JavaScript dapp. +Get started with MM Connect in your JavaScript dapp. You can [download the quickstart template](#set-up-using-a-template) or [manually set up the SDK](#set-up-manually) in an existing dapp.

- JavaScript SDK Quickstart + JavaScript SDK Quickstart

@@ -27,7 +27,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up using a template -1. Download the [MetaMask SDK JavaScript template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/javascript): +1. Download the [MM Connect JavaScript template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/javascript): ```bash npx degit MetaMask/metamask-sdk-examples/quickstarts/javascript metamask-javascript @@ -46,7 +46,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. Alternatively, you can use `git clone`, which will download the entire repository. - To do so, clone the MetaMask SDK examples repository and navigate into the `quickstarts/javascript` directory: + To do so, clone the MM Connect examples repository and navigate into the `quickstarts/javascript` directory: ```bash git clone https://github.com/MetaMask/metamask-sdk-examples @@ -80,29 +80,29 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall pnpm dev ``` -You've successfully set up MetaMask SDK. +You've successfully set up MM Connect. ## Set up manually -### 1. Install the SDK +### 1. Install MM Connect -Install the SDK in an existing JavaScript project: +Install MM Connect in an existing JavaScript project: ```bash npm2yarn -npm install @metamask/sdk +npm install @metamask/connect/evm ``` -### 2. Initialize the SDK +### 2. Initialize MM Connect -The following are examples of using the SDK in various JavaScript environments: +The following are examples of using MM Connect in various JavaScript environments: ```javascript -import { MetaMaskSDK } from "@metamask/sdk" +import { createEVMClient } from "@metamask/connect/evm" -const MMSDK = new MetaMaskSDK({ +const evmClient = createEVMClient({ dappMetadata: { name: "Example JavaScript dapp", url: window.location.href, @@ -119,7 +119,7 @@ const MMSDK = new MetaMaskSDK({ ``` -### Advanced transaction with gas estimation +## Send an advanced transaction with gas estimation -To add gas estimation, use the [`eth_estimateGas`](/wallet/reference/json-rpc-methods/eth_estimategas) +To add gas estimation, use the [`eth_estimateGas`](../../../reference/json-rpc-api/index.md) RPC method. ```javascript +import { createEVMClient } from "@metamask/connect/evm"; + +const evmClient = createEVMClient(); +const provider = evmClient.getProvider(); + async function estimateGas(transaction) { try { - const gasEstimate = await ethereum.request({ + const gasEstimate = await provider.request({ method: "eth_estimateGas", params: [transaction] }); @@ -270,6 +189,6 @@ Follow these best practices when handling transactions. See the following guides to add more functionality to your dapp: -- [Authenticate users](authenticate-users.md) -- [Manage networks](manage-networks.md) -- [Interact with smart contracts](interact-with-contracts.md) +- [Manage user accounts](../manage-user-accounts.md) +- [Manage networks](../manage-networks.md) +- [Interact with smart contracts](../interact-with-contracts.md) diff --git a/wallet/how-to/sign-data/index.md b/sdk/evm/connect/guides/javascript/sign-data/index.md similarity index 82% rename from wallet/how-to/sign-data/index.md rename to sdk/evm/connect/guides/javascript/sign-data/index.md index 4b4190218af..502f69c8ec6 100644 --- a/wallet/how-to/sign-data/index.md +++ b/sdk/evm/connect/guides/javascript/sign-data/index.md @@ -13,7 +13,8 @@ You can use the following RPC methods to request cryptographic signatures from u signatures that don't need to be efficiently processed onchain. :::caution -[`eth_sign`](../../concepts/signing-methods.md#eth_sign) is deprecated. +`eth_sign` is deprecated. +See [MIP-3](https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-3.md) for more information about this deprecation. ::: :::note @@ -25,7 +26,7 @@ sign data using an unsupported method, in which case we recommend using your sta ## Use `eth_signTypedData_v4` -[`eth_signTypedData_v4`](/wallet/reference/json-rpc-methods/eth_signtypeddata_v4) +[`eth_signTypedData_v4`](../../../reference/json-rpc-api/index.md) provides the most human-readable signatures that are efficient to process onchain. It follows the [EIP-712](https://eips.ethereum.org/EIPS/eip-712) specification to allow users to sign typed structured data that can be verified onchain. @@ -34,7 +35,7 @@ account names in place of addresses).

-![eth_signTypedData_v4](../../assets/signTypedData.png) +![eth_signTypedData_v4](../../../_assets/signTypedData.png)

@@ -62,6 +63,11 @@ Ensure your contract is as readable as possible to the user. The following is an example of using `eth_signTypedData_v4` with MetaMask: ```javascript title="index.js" +import { createEVMClient } from "@metamask/connect/evm"; + +const evmClient = createEVMClient(); +const provider = evmClient.getProvider(); + signTypedDataV4Button.addEventListener("click", async function (event) { event.preventDefault() @@ -136,38 +142,37 @@ signTypedDataV4Button.addEventListener("click", async function (event) { var params = [from[0], msgParams] var method = "eth_signTypedData_v4" - provider // Or window.ethereum if you don't support EIP-6963. - .sendAsync( - { - method, - params, - from: from[0], - }, - function (err, result) { - if (err) return console.dir(err) - if (result.error) { - alert(result.error.message) - } - if (result.error) return console.error("ERROR", result) - console.log("TYPED SIGNED:" + JSON.stringify(result.result)) - - const recovered = sigUtil.recoverTypedSignature_v4({ - data: JSON.parse(msgParams), - sig: result.result, - }) - - if ( - ethUtil.toChecksumAddress(recovered) === - ethUtil.toChecksumAddress(from) - ) { - alert("Successfully recovered signer as " + from) - } else { - alert( - "Failed to verify signer when comparing " + result + " to " + from - ) - } + provider.sendAsync( + { + method, + params, + from: from[0], + }, + function (err, result) { + if (err) return console.dir(err) + if (result.error) { + alert(result.error.message) } - ) + if (result.error) return console.error("ERROR", result) + console.log("TYPED SIGNED:" + JSON.stringify(result.result)) + + const recovered = sigUtil.recoverTypedSignature_v4({ + data: JSON.parse(msgParams), + sig: result.result, + }) + + if ( + ethUtil.toChecksumAddress(recovered) === + ethUtil.toChecksumAddress(from) + ) { + alert("Successfully recovered signer as " + from) + } else { + alert( + "Failed to verify signer when comparing " + result + " to " + from + ) + } + } + ) }) ``` @@ -183,14 +188,14 @@ See the [live example](https://metamask.github.io/test-dapp/#signTypedDataV4) an ## Use `personal_sign` -[`personal_sign`](/wallet/reference/json-rpc-methods/personal_sign) is the +[`personal_sign`](../../../reference/json-rpc-api/index.md) is the easiest way to request human-readable signatures that don't need to be efficiently processed onchain. It's often used for signature challenges that are authenticated on a web server, such as [Sign-In with Ethereum](siwe.md).

-![Personal sign](../../assets/personal_sign.png) +![Personal sign](../../../_assets/personal_sign.png)

@@ -209,6 +214,11 @@ It's often used for signature challenges that are authenticated on a web server, The following is an example of using `personal_sign` with MetaMask: ```javascript title="index.js" +import { createEVMClient } from "@metamask/connect/evm"; + +const evmClient = createEVMClient(); +const provider = evmClient.getProvider(); + personalSignButton.addEventListener("click", async function (event) { event.preventDefault() const exampleMessage = "Example `personal_sign` message." @@ -217,7 +227,7 @@ personalSignButton.addEventListener("click", async function (event) { // For historical reasons, you must submit the message to sign in hex-encoded UTF-8. // This uses a Node.js-style buffer shim in the browser. const msg = `0x${Buffer.from(exampleMessage, "utf8").toString("hex")}` - const sign = await ethereum.request({ + const sign = await provider.request({ method: "personal_sign", params: [msg, from], }) diff --git a/wallet/how-to/sign-data/siwe.md b/sdk/evm/connect/guides/javascript/sign-data/siwe.md similarity index 78% rename from wallet/how-to/sign-data/siwe.md rename to sdk/evm/connect/guides/javascript/sign-data/siwe.md index 4daff5dcee2..6edab221a46 100644 --- a/wallet/how-to/sign-data/siwe.md +++ b/sdk/evm/connect/guides/javascript/sign-data/siwe.md @@ -13,7 +13,7 @@ MetaMask parses the message and gives the user a friendly interface prompting th your dapp:

- Sign-in with Ethereum request + Sign-in with Ethereum request

## Domain binding @@ -31,28 +31,32 @@ This is to not break existing dapps that may have use cases for mismatched domai
- Sign-in bad domain + Sign-in bad domain
- Sign-in bad domain pop-up + Sign-in bad domain pop-up
## Example The following is an example of setting up SIWE with MetaMask using -[`personal_sign`](/wallet/reference/json-rpc-methods/personal_sign): +[`personal_sign`](../../../reference/json-rpc-api/index.md): ```javascript title="index.js" +import { createEVMClient } from "@metamask/connect/evm"; + +const evmClient = createEVMClient(); +const provider = evmClient.getProvider(); + const siweSign = async (siweMessage) => { try { const from = accounts[0] const msg = `0x${Buffer.from(siweMessage, "utf8").toString("hex")}` - const sign = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "personal_sign", - params: [msg, from], - }) + const sign = await provider.request({ + method: "personal_sign", + params: [msg, from], + }) siweResult.innerHTML = sign } catch (err) { console.error(err) diff --git a/sdk/guides/use-deeplinks.md b/sdk/evm/connect/guides/javascript/use-deeplinks.md similarity index 100% rename from sdk/guides/use-deeplinks.md rename to sdk/evm/connect/guides/javascript/use-deeplinks.md diff --git a/sdk/connect/javascript-rainbowkit.md b/sdk/evm/connect/guides/rainbowkit.md similarity index 87% rename from sdk/connect/javascript-rainbowkit.md rename to sdk/evm/connect/guides/rainbowkit.md index 5a5805552ad..b0426698bc0 100644 --- a/sdk/connect/javascript-rainbowkit.md +++ b/sdk/evm/connect/guides/rainbowkit.md @@ -1,13 +1,13 @@ --- -description: Quickstart guide for using the MetaMask SDK with a JavaScript and RainbowKit dapp. +description: Quickstart guide for using MM Connect with a JavaScript and RainbowKit dapp. toc_max_heading_level: 3 -sidebar_label: JavaScript + RainbowKit +sidebar_label: RainbowKit keywords: [connect, MetaMask, JavaScript, RainbowKit, SDK, dapp, Wallet SDK] --- # Connect to MetaMask using JavaScript + RainbowKit -Get started with MetaMask SDK in a JavaScript and RainbowKit dapp. +Get started with MM Connect in a JavaScript and RainbowKit dapp. You can [download the quickstart template](#set-up-using-a-template) or [manually set up the SDK](#set-up-manually) in an existing dapp.

@@ -25,7 +25,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up using a template -1. Download the [MetaMask SDK RainbowKit template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/rainbowkit): +1. Download the [MM Connect RainbowKit template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/rainbowkit): ```bash npx degit MetaMask/metamask-sdk-examples/quickstarts/rainbowkit metamask-rainbowkit @@ -44,7 +44,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. Alternatively, you can use `git clone`, which will download the entire repository. - To do so, clone the MetaMask SDK examples repository and navigate into the `quickstarts/rainbowkit` directory: + To do so, clone the MM Connect examples repository and navigate into the `quickstarts/rainbowkit` directory: ```bash git clone https://github.com/MetaMask/metamask-sdk-examples @@ -80,9 +80,9 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up manually -### 1. Install the SDK +### 1. Install MM Connect -Install MetaMask SDK along with its peer dependencies to an existing React project: +Install MM Connect along with its peer dependencies to an existing React project: ```bash npm2yarn npm install @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query @@ -108,7 +108,7 @@ In the following example, replace `` with your WalletConnect pr ```jsx const config = getDefaultConfig({ - appName: 'MetaMask SDK RainbowKit Quickstart', + appName: 'MM Connect RainbowKit Quickstart', projectId: '', chains: [mainnet, linea, sepolia, lineaSepolia], wallets: [ diff --git a/sdk/connect/react-native.md b/sdk/evm/connect/guides/react-native.md similarity index 96% rename from sdk/connect/react-native.md rename to sdk/evm/connect/guides/react-native.md index 46926ec2191..0bad8b53d29 100644 --- a/sdk/connect/react-native.md +++ b/sdk/evm/connect/guides/react-native.md @@ -9,7 +9,7 @@ import TabItem from "@theme/TabItem"; # Connect to MetaMask using React Native -Get started with MetaMask SDK in your React Native or Expo dapp. +Get started with MM Connect in your React Native or Expo dapp. ## Steps @@ -36,7 +36,7 @@ npx create-expo-app devexpo --template ### 2. Install dependencies -Install the SDK and its dependencies using the following commands: +Install MM Connect and its dependencies using the following commands: diff --git a/sdk/connect/javascript-wagmi.md b/sdk/evm/connect/guides/wagmi/index.md similarity index 73% rename from sdk/connect/javascript-wagmi.md rename to sdk/evm/connect/guides/wagmi/index.md index 71d60fe2b28..1fd20ee774c 100644 --- a/sdk/connect/javascript-wagmi.md +++ b/sdk/evm/connect/guides/wagmi/index.md @@ -1,18 +1,18 @@ --- -description: Quickstart guide for using the MetaMask SDK with a JavaScript and Wagmi dapp. +description: Quickstart guide for using MM Connect with a JavaScript and Wagmi dapp. toc_max_heading_level: 3 -sidebar_label: JavaScript + Wagmi (recommended) +sidebar_label: Wagmi keywords: [connect, MetaMask, JavaScript, Wagmi, SDK, dapp, Wallet SDK] --- # Connect to MetaMask using JavaScript + Wagmi -Get started with MetaMask SDK in a JavaScript and Wagmi dapp. +Get started with MM Connect in a JavaScript and Wagmi dapp. You can [download the quickstart template](#set-up-using-a-template) or [manually set up the SDK](#set-up-manually) in an existing dapp.

- Quickstart + Quickstart

@@ -24,7 +24,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up using a template -1. Download the [MetaMask SDK Wagmi template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/wagmi): +1. Download the [MM Connect Wagmi template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/quickstarts/wagmi): ```bash npx degit MetaMask/metamask-sdk-examples/quickstarts/wagmi metamask-wagmi @@ -40,15 +40,15 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall Degit vs. Git clone
- `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. + `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. - Alternatively, you can use `git clone`, which will download the entire repository. - To do so, clone the MetaMask SDK examples repository and navigate into the `quickstarts/wagmi` directory: + Alternatively, you can use `git clone`, which will download the entire repository. + To do so, clone the MM Connect examples repository and navigate into the `quickstarts/wagmi` directory: - ```bash - git clone https://github.com/MetaMask/metamask-sdk-examples - cd metamask-sdk-examples/quickstarts/wagmi - ``` + ```bash + git clone https://github.com/MetaMask/metamask-sdk-examples + cd metamask-sdk-examples/quickstarts/wagmi + ```
@@ -67,12 +67,12 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up manually -### 1. Install the SDK +### 1. Install MM Connect -Install MetaMask SDK along with its peer dependencies to an existing React project: +Install MM Connect along with its peer dependencies to an existing React project: ```bash npm2yarn -npm install @metamask/sdk wagmi viem@2.x @tanstack/react-query +npm install @metamask/connect wagmi viem@2.x @tanstack/react-query ``` ### 2. Import required dependencies @@ -89,7 +89,7 @@ import { metaMask } from 'wagmi/connectors' ### 3. Configure your project Set up your configuration with the desired chains and connectors. -In the following example, set the [`infuraAPIKey`](../reference/sdk-options.md#infuraapikey) option to your [Infura API key](/developer-tools/dashboard/get-started/create-api) to use for RPC requests: +In the following example, set the [`infuraAPIKey`](../../../../reference/options.md#infuraapikey) option to your [Infura API key](/developer-tools/dashboard/get-started/create-api) to use for RPC requests: ```jsx const config = createConfig({ @@ -179,10 +179,10 @@ const config = createConfig({ After completing the basic setup, you can follow these guides to add your own functionality: -- [Authenticate users](../guides/authenticate-users.md) -- [Manage networks](../guides/manage-networks.md) -- [Handle transactions](../guides/handle-transactions.md) -- [Interact with smart contracts](../guides/interact-with-contracts.md) +- [Manage user accounts](manage-user-accounts.md) +- [Manage networks](manage-networks.md) +- [Send transactions](send-transactions.md) +- [Interact with smart contracts](interact-with-contracts.md) ## Live example diff --git a/sdk/evm/connect/guides/wagmi/interact-with-contracts.md b/sdk/evm/connect/guides/wagmi/interact-with-contracts.md new file mode 100644 index 00000000000..05775f41a84 --- /dev/null +++ b/sdk/evm/connect/guides/wagmi/interact-with-contracts.md @@ -0,0 +1,221 @@ +--- +description: Interact with contracts with the SDK in your Wagmi dapp. +keywords: [SDK, Wagmi, JavaScript, batch, read, write, smart, contract, contracts, dapp] +sidebar_label: Interact with contracts +--- + +# Interact with smart contracts + +Interact with smart contracts in your Wagmi dapp. +With the SDK, you can: + +- **Read data** from smart contracts. +- **Batch contract reads**. +- **Write data** to smart contracts. +- **Handle contract events**. +- **Manage transaction states**. +- **Handle contract errors**. + +## Read contracts + +Wagmi provides dedicated hooks for smart contract interactions. +The following example reads contract data using the [`useReadContract`](https://wagmi.sh/react/api/hooks/useReadContract) hook: + +```tsx +import { useReadContract } from "wagmi" + +function TokenBalance() { + const { + data: balance, + isError, + isLoading + } = useReadContract({ + address: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + abi: [ + { + name: "balanceOf", + type: "function", + stateMutability: "view", + inputs: [{ name: "owner", type: "address" }], + outputs: [{ name: "balance", type: "uint256" }], + }, + ], + functionName: "balanceOf", + args: ["0x03A71968491d55603FFe1b11A9e23eF013f75bCF"], + }) + + if (isLoading) return
Loading balance...
+ if (isError) return
Error fetching balance
+ + return
Balance: {balance?.toString()}
+} +``` + +### Batch contract reads + +You can perform multiple contract read operations using the [`useReadContracts`](https://wagmi.sh/react/api/hooks/useReadContracts) hook. +This hook batches contract calls internally, returning the results as an array. +For example: + +```js +import { useReadContracts } from "wagmi"; + +// Example contract definitions with their address and ABI +const contractA = { + address: "0xContractAddress1", + abi: contractABI1, +} as const; + +const contractB = { + address: "0xContractAddress2", + abi: contractABI2, +} as const; + +function MyBatchReadComponent() { + const { data, isError, isLoading } = useReadContracts({ + contracts: [ + { + ...contractA, + functionName: "getValueA", + }, + { + ...contractA, + functionName: "getValueB", + }, + { + ...contractB, + functionName: "getValueX", + args: [42], + }, + { + ...contractB, + functionName: "getValueY", + args: [42], + }, + ], + }); + + if (isLoading) return
Loading...
; + if (isError) return
Error fetching data.
; + + return ( +
+

getValueA: {data?.[0]?.toString()}

+

getValueB: {data?.[1]?.toString()}

+

getValueX: {data?.[2]?.toString()}

+

getValueY: {data?.[3]?.toString()}

+
+ ); +} +``` + +In this example, four contract read calls are batched together. +The results are returned as an array in the same order as the calls, allowing you to process each result accordingly. + +:::info +"Batching" can also refer to [batching JSON-RPC requests](../javascript/batch-requests.md) using MM Connect's `metamask_batch` method, or [sending atomic batch transactions](../javascript/send-transactions/batch-transactions.md) in MetaMask. +::: + +## Write to contracts + +The following example writes to contracts using the [`useWriteContract`](https://wagmi.sh/react/api/hooks/useWriteContract) hook: + +```tsx +import { useWriteContract, useWaitForTransactionReceipt } from "wagmi" + +function MintNFT() { + const { + writeContract, + data: hash, + error, + isPending + } = useWriteContract() + + const { + isLoading: isConfirming, + isSuccess: isConfirmed + } = useWaitForTransactionReceipt({ + hash + }) + + function mint() { + writeContract({ + address: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2", + abi: [ + { + name: "mint", + type: "function", + stateMutability: "nonpayable", + inputs: [{ name: "tokenId", type: "uint256" }], + outputs: [], + }, + ], + functionName: "mint", + args: [123n], // Token ID + }) + } + + return ( +
+ + + {hash && ( +
+ Transaction Hash: {hash} + {isConfirming &&
Waiting for confirmation...
} + {isConfirmed &&
NFT Minted Successfully!
} +
+ )} + + {error &&
Error: {error.message}
} +
+ ) +} +``` + +## Best practices + +Follow these best practices when interacting with smart contracts. + +#### Contract validation + +- Always **verify contract addresses**. +- Double check **ABI correctness**. +- **Validate input data** before sending. +- Use **typed data** when possible (for example, using [Viem](https://viem.sh/)). + +#### Error handling + +- Handle [common errors](#common-errors) like **user rejection** and **contract reverts**. +- Provide **clear error messages** to users. +- Implement proper **error recovery** flows. +- Consider **gas estimation failures**. + +#### User experience + +- Show **clear loading states**. +- Display **transaction progress**. +- Provide **confirmation feedback**. +- Enable proper **error recovery**. + +## Common errors + +| Error code | Description | Solution | +|------------|-------------|----------| +| `4001` | User rejected transaction | Show a retry option and a clear error message. | +| `-32000` | Invalid input | Validate the input data before sending. | +| `-32603` | Contract execution reverted | Check the contract conditions and handle the error gracefully. | +| `-32002` | Request already pending | Prevent multiple concurrent transactions. | + +## Next steps + +See the following guides to add more functionality to your dapp: + +- [Manage user accounts](manage-user-accounts.md) +- [Manage networks](manage-networks.md) +- [Send transactions](send-transactions.md) diff --git a/sdk/evm/connect/guides/wagmi/manage-networks.md b/sdk/evm/connect/guides/wagmi/manage-networks.md new file mode 100644 index 00000000000..fd2eb70c3b6 --- /dev/null +++ b/sdk/evm/connect/guides/wagmi/manage-networks.md @@ -0,0 +1,126 @@ +--- +description: Manage networks with the SDK in your Wagmi dapp. +keywords: [SDK, Wagmi, JavaScript, detect, switch, add, network, networks, dapp] +toc_max_heading_level: 2 +--- + +# Manage networks + +Manage networks in your Wagmi dapp. +With the SDK, you can: + +- **Detect the current network** and monitor network changes. +- **Switch between networks** programmatically. +- **Add new networks** to MetaMask. +- **Handle common network-related errors**. + +

+ + Switch Networks + +

+ +## Detect and switch networks + +Wagmi provides intuitive hooks for several network-related operations. +The following are examples of using these hooks. + +Detect the current network: + +```tsx +import { useChainId, useChains } from "wagmi" + +function NetworkStatus() { + const chainId = useChainId() + const chains = useChains() + + const currentChain = chains.find(c => c.id === chainId) + + if (!currentChain) { + return
Not connected to any network
+ } + + return ( +
+
Connected to {currentChain.name}
+
Chain ID: {chainId}
+
Supported chains: {chains.map(c => c.name).join(", ")}
+
+ ) +} +``` + +Switch networks: + +```tsx +import { useSwitchChain } from "wagmi" + +function NetworkSwitcher() { + const { chains, switchChain } = useSwitchChain() + + return ( +
+ {chains.map((chain) => ( + + ))} +
+ ) +} +``` + +Handle network changes: + +```tsx +import { useChainId } from "wagmi" +import { useEffect } from "react" + +function NetworkWatcher() { + const chainId = useChainId() + + useEffect(() => { + console.log("Chain ID changed:", chainId) + }, [chainId]) + + return null +} +``` + +## Best practices + +Follow these best practices when managing networks. + +#### Error handling + +- Implement error handling for network switching operations. +- Provide **clear feedback messages** to users when network operations fail. +- Handle cases where networks need to be **added before switching**. + +#### User experience + +- Display **loading states** during network switches. +- Show **clear network status information** at all times. +- Consider **warning users** before initiating network switches. +- Use an **RPC provider** that supports your target networks. + +## Common errors + +The following table lists common network management errors and their codes: + +| Error code | Description | Solution | +|------------|-------------|----------| +| `4902` | Network not added | Use [`wallet_addEthereumChain`](../../reference/json-rpc-api/index.md) to add the network first. | +| `4001` | User rejected request | Show a message asking the user to approve the network switch. | +| `-32002` | Request already pending | Disable the switch network button while the request is pending. | + +## Next steps + +See the following guides to add more functionality to your dapp: + +- [Manage user accounts](manage-user-accounts.md) +- [Send transactions](send-transactions.md) +- [Interact with smart contracts](interact-with-contracts.md) diff --git a/sdk/evm/connect/guides/wagmi/manage-user-accounts.md b/sdk/evm/connect/guides/wagmi/manage-user-accounts.md new file mode 100644 index 00000000000..12cbf3fde92 --- /dev/null +++ b/sdk/evm/connect/guides/wagmi/manage-user-accounts.md @@ -0,0 +1,132 @@ +--- +description: Authenticate users with the SDK in your Wagmi dapp. +keywords: [SDK, Wagmi, JavaScript, authenticate, connect, sign, accounts, wallet, dapp] +toc_max_heading_level: 3 +--- + +# Manage user accounts + +Connect and manage user wallet sessions in your Wagmi dapp. +With the SDK, you can: + +- **Connect users' wallets** to your dapp. +- **Access user accounts** (addresses). +- **Handle connection states** (connected/disconnected). +- **Listen for account changes** in real time. +- **Manage wallet sessions** (connect/disconnect). +- **Support multiple wallet types** (extension, mobile app). + +

+ + Connect to MetaMask + +

+ +## Connect wallet + +Wagmi provides a simple, hook-based approach for handling wallet connections. +For example: + +```tsx title="Handle wallet connections" +import { useAccount, useConnect, useDisconnect } from "wagmi" + +function ConnectWallet() { + const { address, isConnected } = useAccount() + const { connectors, connect, isPending } = useConnect() + const { disconnect } = useDisconnect() + + if (isConnected) { + return ( +
+
Connected to {address}
+ +
+ ) + } + + return ( +
+ {connectors.map((connector) => ( + + ))} +
+ ) +} +``` + +Wagmi provides a dedicated hook for handling account lifecycle events: + +```tsx +import { useAccountEffect } from "wagmi" + +function WatchAccount() { + useAccountEffect({ + onConnect(data) { + console.log("Connected!", { + address: data.address, + chainId: data.chainId, + isReconnected: data.isReconnected + }) + }, + onDisconnect() { + console.log("Disconnected!") + } + }) + + return
Watching for account changes...
+} +``` + +## Best practices + +Follow these best practices when authenticating users. + +#### User interaction + +- Only trigger connection requests in response to user actions (like selecting a button). +- Never auto-connect on page load. +- Provide clear feedback during connection states. + +#### Error handling + +- Handle [common errors](#common-errors) like user rejection (code `4001`). +- Provide clear error messages to users. +- Fall back gracefully when MetaMask is not installed. + +#### Account changes + +- Always listen for account changes. +- Update your UI when accounts change. +- Handle disconnection events. + +#### Chain support + +- Listen for network/chain changes. +- Verify the current chain meets your requirements. +- Provide clear messaging when users need to switch networks. + +Learn how to [manage networks](manage-networks.md). + +## Common errors + +The following table lists common authentication errors and their codes: + +| Error code | Description | Solution | +|------------|-------------|----------| +| `4001` | User rejected request | Show a message asking the user to approve the connection. | +| `-32002` | Request already pending | Disable the connect button while the request is pending. | +| `-32603` | Internal JSON-RPC error | Check if MetaMask is properly installed. | + +## Next steps + +See the following guides to add more functionality to your dapp: + +- [Manage networks](manage-networks.md) +- [Send transactions](send-transactions.md) +- [Interact with smart contracts](interact-with-contracts.md) diff --git a/sdk/evm/connect/guides/wagmi/send-transactions.md b/sdk/evm/connect/guides/wagmi/send-transactions.md new file mode 100644 index 00000000000..33817aa71c8 --- /dev/null +++ b/sdk/evm/connect/guides/wagmi/send-transactions.md @@ -0,0 +1,147 @@ +--- +description: Handle transactions with the SDK in your Wagmi dapp. +keywords: [SDK, Wagmi, JavaScript, send, transaction, transactions, status, estimate, gas, dapp] +toc_max_heading_level: 2 +--- + +# Send transactions + +Handle EVM transactions in your Wagmi dapp. +With the SDK, you can: + +- **Send transactions**. +- **Track transaction status** in real time. +- **Estimate gas costs** accurately. +- **Handle transaction errors** gracefully. +- **Manage complex transaction patterns**. + +Wagmi provides hooks for sending transactions and tracking their status. +The following are examples of sending a [basic transaction](#send-a-basic-transaction) and an +[advanced transaction with gas estimation](#send-an-advanced-transaction-with-gas-estimation). + +:::info Send batch transactions +This page describes how to send one transaction at a time using Wagmi. +You can also follow [this tutorial to send atomic batch transactions](/tutorials/upgrade-eoa-to-smart-account) with Wagmi and MM Connect, upgrading an externally owned account to a smart account (specified by [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) and [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)). +::: + +## Send a basic transaction + +```tsx +import { parseEther } from "viem" +import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi" + +function SendTransaction() { + const { + data: hash, + error, + isPending, + sendTransaction + } = useSendTransaction() + + const { + isLoading: isConfirming, + isSuccess: isConfirmed + } = useWaitForTransactionReceipt({ + hash + }) + + async function handleSend() { + sendTransaction({ + to: "0x...", + value: parseEther("0.1") // 0.1 ETH + }) + } + + return ( +
+ + + {hash && ( +
+ Transaction Hash: {hash} + {isConfirming &&
Waiting for confirmation...
} + {isConfirmed &&
Transaction confirmed!
} +
+ )} + + {error &&
Error: {error.message}
} +
+ ) +} +``` + +## Send an advanced transaction with gas estimation + +```tsx +import { parseEther } from "viem" +import { + useSendTransaction, + useWaitForTransactionReceipt, + useEstimateGas +} from "wagmi" + +function AdvancedTransaction() { + const transaction = { + to: "0x...", + value: parseEther("0.1"), + data: "0x..." // Optional contract interaction data + } + + // Estimate gas + const { data: gasEstimate } = useEstimateGas(transaction) + + const { sendTransaction } = useSendTransaction({ + ...transaction, + gas: gasEstimate, + onSuccess: (hash) => { + console.log("Transaction sent:", hash) + } + }) + + return +} +``` + +## Best practices + +Follow these best practices when handling transactions. + +#### Transaction security + +- Always **validate inputs** before sending transactions. +- Check wallet balances to **ensure sufficient** funds. +- **Verify addresses** are valid. + +#### Error handling + +- Handle [common errors](#common-errors) like **user rejection** and **insufficient funds**. +- Provide **clear error messages** to users. +- Implement proper **error recovery** flows. +- Consider **network congestion** in gas estimates. + +#### User experience + +- Display **clear loading states** during transactions. +- Show **transaction progress** in real time. +- Provide **detailed transaction information**. +## Common errors + +| Error code | Description | Solution | +|------------|-------------|----------| +| `4001` | User rejected transaction | Show a retry option and a clear error message. | +| `-32603` | Insufficient funds | Check the balance before sending a transaction. | +| `-32000` | Gas too low | Increase the gas limit or add a buffer to the estimation. | +| `-32002` | Request already pending | Prevent multiple concurrent transactions. | + +## Next steps + +See the following guides to add more functionality to your dapp: + +- [Manage user accounts](manage-user-accounts.md) +- [Manage networks](manage-networks.md) +- [Interact with smart contracts](interact-with-contracts.md) diff --git a/sdk/connect/javascript-web3auth.md b/sdk/evm/connect/guides/web3auth.md similarity index 82% rename from sdk/connect/javascript-web3auth.md rename to sdk/evm/connect/guides/web3auth.md index 779baa7872f..cdf07a91786 100644 --- a/sdk/connect/javascript-web3auth.md +++ b/sdk/evm/connect/guides/web3auth.md @@ -1,15 +1,15 @@ --- sidebar_label: Embedded Wallets SDK -description: Quickstart guide for using MetaMask SDK and Embedded Wallets SDK. +description: Quickstart guide for using MM Connect and Embedded Wallets SDK. toc_max_heading_level: 3 keywords: [connect, MetaMask, Embedded Wallets, SDK, dapp, Wallet SDK] --- # Connect to MetaMask using Embedded Wallets SDK -Get started with MetaMask SDK and [Embedded Wallets SDK (previously Web3Auth)](/embedded-wallets), +Get started with MM Connect and [Embedded Wallets SDK (previously Web3Auth)](/embedded-wallets), enabling users to sign in with an email or social media account. -You can use MetaMask SDK features directly within Embedded Wallets SDK. +You can use MM Connect features directly within Embedded Wallets SDK. You can [download the quickstart template](#set-up-using-a-template) or [manually set up the SDKs](#set-up-manually) in an existing dapp.

@@ -27,7 +27,7 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall ## Set up using a template -1. Download the [MetaMask SDK + Web3Auth SDK template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/partners/web3auth): +1. Download the [MM Connect + Web3Auth SDK template](https://github.com/MetaMask/metamask-sdk-examples/tree/main/partners/web3auth): ```bash npx degit MetaMask/metamask-sdk-examples/partners/web3auth metamask-web3auth @@ -43,15 +43,15 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall

Degit vs. Git clone
- `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. + `degit` is a tool that enables cloning only the directory structure from a GitHub repository, without retrieving the entire repository. + + Alternatively, you can use `git clone`, which will download the entire repository. + To do so, clone the MM Connect examples repository and navigate into the `partners/web3auth` directory: - Alternatively, you can use `git clone`, which will download the entire repository. - To do so, clone the MetaMask SDK examples repository and navigate into the `partners/web3auth` directory: - - ```bash - git clone https://github.com/MetaMask/metamask-sdk-examples - cd metamask-sdk-examples/partners/web3auth - ``` + ```bash + git clone https://github.com/MetaMask/metamask-sdk-examples + cd metamask-sdk-examples/partners/web3auth + ```
@@ -80,14 +80,14 @@ You can [download the quickstart template](#set-up-using-a-template) or [manuall pnpm dev ``` -You've successfully set up MetaMask SDK and MetaMask Embedded Wallets. +You've successfully set up MM Connect and MetaMask Embedded Wallets. See how to [use Embedded Wallets](#usage). ## Set up manually ### 1. Install dependencies -Install the SDK and the required dependencies to an existing project: +Install MM Connect and the required dependencies to an existing project: ```bash npm2yarn npm install viem wagmi @tanstack/react-query @web3auth/modal@10 diff --git a/sdk/evm/connect/reference/json-rpc-api/eth_signTypedData_v4.mdx b/sdk/evm/connect/reference/json-rpc-api/eth_signTypedData_v4.mdx new file mode 100644 index 00000000000..ceff8df67ff --- /dev/null +++ b/sdk/evm/connect/reference/json-rpc-api/eth_signTypedData_v4.mdx @@ -0,0 +1,142 @@ +--- +title: eth_signTypedData_v4 +description: Sign structured data according to EIP-712 +hide_title: true +hide_table_of_contents: true +--- + +import SimplifiedApiReference from '@site/src/components/SimplifiedApiReference' + + diff --git a/sdk/evm/connect/reference/json-rpc-api/index.md b/sdk/evm/connect/reference/json-rpc-api/index.md new file mode 100644 index 00000000000..4624aee96a1 --- /dev/null +++ b/sdk/evm/connect/reference/json-rpc-api/index.md @@ -0,0 +1,10 @@ +--- +sidebar_class_name: 'hidden' +description: See the MetaMask JSON-RPC API reference. +--- + +# JSON-RPC API + +This section provides an interactive reference for MetaMask's JSON-RPC API. +The API builds on a set of standard Ethereum methods with MetaMask-specific enhancements, and is designed for seamless integration into dapps. +View the JSON-RPC API methods by selecting a method in the left sidebar. diff --git a/sdk/evm/connect/reference/json-rpc-api/wallet_sendCalls.mdx b/sdk/evm/connect/reference/json-rpc-api/wallet_sendCalls.mdx new file mode 100644 index 00000000000..dd8e1d608a6 --- /dev/null +++ b/sdk/evm/connect/reference/json-rpc-api/wallet_sendCalls.mdx @@ -0,0 +1,155 @@ +--- +title: wallet_sendCalls +description: Submit a batch of calls to the wallet for execution +hide_title: true +hide_table_of_contents: true +--- + +import SimplifiedApiReference from '@site/src/components/SimplifiedApiReference' + + \ No newline at end of file diff --git a/sdk/reference/sdk-methods.md b/sdk/evm/connect/reference/methods.md similarity index 73% rename from sdk/reference/sdk-methods.md rename to sdk/evm/connect/reference/methods.md index 418c3109918..05c2dcb61b9 100644 --- a/sdk/reference/sdk-methods.md +++ b/sdk/evm/connect/reference/methods.md @@ -1,12 +1,12 @@ --- -description: Methods reference for MetaMask SDK. +description: Methods reference for MM Connect. keywords: [SDK, method, methods, dapp] toc_max_heading_level: 2 --- -# SDK methods +# MM Connect methods -MetaMask SDK provides several convenience methods for connecting to and interacting with MetaMask, including the following. +MM Connect provides several convenience methods for connecting to and interacting with MetaMask, including the following. ## `connect` @@ -19,7 +19,7 @@ A promise that resolves to an array of account addresses. ### Example ```javascript -const accounts = await sdk.connect(); +const accounts = await evmClient.connect(); console.log("Connected accounts:", accounts); ``` @@ -38,7 +38,7 @@ A promise that resolves to the signature of the signed message. ### Example ```javascript -const signature = await sdk.connectAndSign({ +const signature = await evmClient.connectAndSign({ msg: "Hello from my dapp!" }); console.log("Signature:", signature); @@ -46,7 +46,7 @@ console.log("Signature:", signature); ## `connectWith` -Connects to MetaMask and executes a specific [JSON-RPC method](/wallet/reference/json-rpc-methods). +Connects to MetaMask and executes a specific [JSON-RPC method](json-rpc-api/index.md). ### Parameters @@ -61,7 +61,7 @@ A promise that resolves to the result of the RPC call. ### Example ```javascript -const result = await sdk.connectWith({ +const result = await evmClient.connectWith({ rpc: { method: "eth_getBalance", params: ["0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", "latest"] @@ -81,7 +81,7 @@ The active provider, or undefined if no provider is found. ### Example ```javascript -const provider = sdk.getProvider(); +const provider = evmClient.getProvider(); if (provider) { // Use the provider for RPC calls const accounts = await provider.request({ @@ -92,16 +92,16 @@ if (provider) { ## `isInitialized` -Checks if the SDK has been initialized. +Checks if MM Connect has been initialized. ### Returns -`True` if the SDK is initialized, `false` otherwise. +`True` if MM Connect is initialized, `false` otherwise. ### Example ```javascript -if (sdk.isInitialized()) { +if (evmClient.isInitialized()) { console.log("SDK is ready to use"); } ``` @@ -118,6 +118,6 @@ Use `terminate()` instead. ### Example ```javascript -await sdk.terminate(); +await evmClient.terminate(); console.log("Connection terminated"); ``` diff --git a/wallet/reference/provider-api.md b/sdk/evm/connect/reference/provider-api.md similarity index 91% rename from wallet/reference/provider-api.md rename to sdk/evm/connect/reference/provider-api.md index c5be480d94a..152c3250637 100644 --- a/wallet/reference/provider-api.md +++ b/sdk/evm/connect/reference/provider-api.md @@ -4,16 +4,16 @@ description: See the MetaMask Ethereum provider API reference. # Ethereum provider API -This page is a reference for the Ethereum provider API of MetaMask's [Wallet API](../concepts/wallet-api.md). +This page is a reference for the Ethereum provider API of MetaMask's Wallet API. MetaMask injects the provider API into websites visited by its users using the `window.ethereum` provider object. You can use the provider [properties](#properties), [methods](#methods), and [events](#events) in your dapp. :::info Note MetaMask supports [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963), which introduces an alternative wallet detection mechanism to the `window.ethereum` injected provider. -This alternative mechanism enables dapps to support [wallet interoperability](../concepts/wallet-interoperability.md) +This alternative mechanism enables dapps to support wallet interoperability by discovering multiple injected wallet providers in a user's browser. -We recommend [using this mechanism to connect to MetaMask](../how-to/connect-extension.md). +We recommend using this mechanism to connect to MetaMask. You can access the provider API using the selected EIP-6963 provider object. Throughout this documentation, we refer to the selected provider using `provider`. @@ -45,7 +45,7 @@ If the provider isn't connected, the page must be reloaded to re-establish the c See the [`connect`](#connect) and [`disconnect`](#disconnect) events for more information. :::note -This method is unrelated to [accessing a user's accounts](../how-to/access-accounts.md). +This method is unrelated to [managing user accounts](../guides/javascript/manage-user-accounts.md). In the provider interface, "connected" and "disconnected" refer to whether the provider can make RPC requests to the current chain. ::: @@ -66,7 +66,7 @@ provider.isConnected() // Or window.ethereum.isConnected() if you don't support ### `request()` -This method is used to submit [JSON-RPC API requests](/wallet/reference/json-rpc-methods) to Ethereum using MetaMask. +This method is used to submit [JSON-RPC API requests](json-rpc-api/index.md) to Ethereum using MetaMask. #### Parameters @@ -84,7 +84,7 @@ If the request fails, the promise rejects with an [error](#errors). #### Example The following is an example of using `request()` to call -[`eth_sendTransaction`](/wallet/reference/json-rpc-methods/eth_sendtransaction): +[`eth_sendTransaction`](json-rpc-api/index.md): ```javascript provider // Or window.ethereum if you don't support EIP-6963. @@ -166,7 +166,7 @@ provider // Or window.ethereum if you don't support EIP-6963. ``` The provider emits this event when the return value of the -[`eth_accounts`](/wallet/reference/json-rpc-methods/eth_accounts) RPC +[`eth_accounts`](json-rpc-api/index.md) RPC method changes. `eth_accounts` returns either an empty array, or an array that contains the addresses of the accounts the caller is permitted to access with the most recently used account first. @@ -174,7 +174,7 @@ Callers are identified by their URL origin, which means that all sites with the the same permissions. This means that the provider emits `accountsChanged` when the user's exposed account address changes. -Listen to this event to [handle accounts](../how-to/access-accounts.md#handle-accounts). +Listen to this event to [handle accounts](../guides/javascript/manage-user-accounts.md). ### `chainChanged` @@ -184,7 +184,7 @@ provider // Or window.ethereum if you don't support EIP-6963. ``` The provider emits this event when the currently connected chain changes. -Listen to this event to [detect a user's network](../how-to/manage-networks/detect-network.md). +Listen to this event to [detect a user's network](../guides/javascript/manage-networks.md). ### `connect` @@ -234,7 +234,7 @@ The `type` property identifies the kind of message. RPC subscription updates are a common use case for this event. For example, if you create a subscription using -[`eth_subscribe`](/wallet/reference/json-rpc-methods/eth_subscribe), each +[`eth_subscribe`](json-rpc-api/index.md), each subscription update is emitted as a `message` event with a `type` of `eth_subscription`. ### Remove event listeners diff --git a/sdk/evm/index.md b/sdk/evm/index.md new file mode 100644 index 00000000000..5d30ead7449 --- /dev/null +++ b/sdk/evm/index.md @@ -0,0 +1,58 @@ +--- +sidebar_label: Introduction +--- + +import CardList from '@site/src/components/CardList' + +# Connect to EVM networks + +## Supported platforms and libraries + +MM Connect is available in a variety of ways to make integration as easy as possible. +You can access it directly via npm, through popular developer libraries like Wagmi, or as part of popular convenience libraries. + + diff --git a/sdk/guides/batch-requests.md b/sdk/guides/batch-requests.md deleted file mode 100644 index 219669cf115..00000000000 --- a/sdk/guides/batch-requests.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -description: Batch multiple JSON-RPC requests using MetaMask SDK or Wagmi. -keywords: [SDK, Wagmi, batch, JSON-RPC, RPC, requests, methods, dapp] ---- - -# Batch requests - -MetaMask SDK provides mechanisms to send multiple JSON-RPC requests in a single call. -However, "batching" can be used in a few different contexts: - -- [**Wagmi batching for contract reads**](#use-wagmi-usereadcontracts) - Wagmi does not support MetaMask's generic batching mechanism. - Instead, it provides the [`useReadContracts`](https://wagmi.sh/react/api/hooks/useReadContracts) hook to perform multiple contract read operations in a single hook call. - This is specialized for retrieving data from smart contracts and returns an array of results corresponding to each read call. - `useReadContracts` does not support batching JSON-RPC methods. - -- [**Vanilla JavaScript batching with `metamask_batch`**](#use-vanilla-javascript-metamask_batch) - - This approach uses MetaMask SDK's `metamask_batch` method to group any JSON-RPC requests together, whether they are contract calls or other JSON-RPC methods (for example, signing messages or sending transactions). - Despite being batched into one HTTP request, each call still requires individual user approval, and if any request is rejected, the entire batch fails. - -:::info -"Batching" can also refer to [sending atomic batch transactions](/wallet/how-to/send-transactions/send-batch-transactions) in MetaMask. -Use the methods introduced by EIP-5792 to send atomic batches. -::: - -## Use Wagmi (`useReadContracts`) - -When using Wagmi, you can perform multiple contract read operations in a single hook call using `useReadContracts`. -This method is designed specifically for contract calls and batches them together internally, returning the results as an array. -It is not a generic JSON-RPC batching tool but rather a specialized solution for reading from smart contracts. - -For more information, see the [Wagmi documentation](https://wagmi.sh/react/api/hooks/useReadContracts). - -The following is an example of batching read operations using `useReadContracts`: - -```js -import { useReadContracts } from "wagmi"; - -// Example contract definitions with their address and ABI -const contractA = { - address: "0xContractAddress1", - abi: contractABI1, -} as const; - -const contractB = { - address: "0xContractAddress2", - abi: contractABI2, -} as const; - -function MyBatchReadComponent() { - const { data, isError, isLoading } = useReadContracts({ - contracts: [ - { - ...contractA, - functionName: "getValueA", - }, - { - ...contractA, - functionName: "getValueB", - }, - { - ...contractB, - functionName: "getValueX", - args: [42], - }, - { - ...contractB, - functionName: "getValueY", - args: [42], - }, - ], - }); - - if (isLoading) return
Loading...
; - if (isError) return
Error fetching data.
; - - return ( -
-

getValueA: {data?.[0]?.toString()}

-

getValueB: {data?.[1]?.toString()}

-

getValueX: {data?.[2]?.toString()}

-

getValueY: {data?.[3]?.toString()}

-
- ); -} -``` - -In this example, four contract read calls are batched together. -The results are returned as an array in the same order as the calls, allowing you to process each result accordingly. - -:::tip -For a better user experience, it's important to use reliable RPC providers instead of public nodes. -We recommend using services like [MetaMask Developer](https://developer.metamask.io/) to ensure better reliability and performance. -::: - -## Use Vanilla JavaScript (`metamask_batch`) - -If you're not using Wagmi, you can directly use MetaMask SDK's `metamask_batch` method to group multiple JSON-RPC requests into a single HTTP call. - -Use cases include: - -- **Batching multiple signatures** - Send multiple signing requests in one batch. -- **Switching networks** - Switch the EVM network, perform an action such as sending a transaction, and switch back, all in one batch. -- **Mixed transactions and signatures** - Combine transaction sending and signing requests in one batch. - -:::note -When using `metamask_batch`, keep in mind the following: - -- Even though the requests are batched, each individual request still requires user approval. -- If any request in the batch is rejected, the entire batch will fail. -::: - -The following is an example of batching JSON-RPC requests using `metamask_batch`: - -```js -import { MetaMaskSDK } from "@metamask/sdk"; - -const MMSDK = new MetaMaskSDK(); -const provider = MMSDK.getProvider(); - -async function handleBatchRequests() { - // Example batch: one personal_sign call and one eth_sendTransaction call. - const requests = [ - { method: "personal_sign", params: ["Hello from batch!", "0x1234..."] }, - { - method: "eth_sendTransaction", - params: [ - { - from: "0x1234...", - to: "0xABCD...", - // Additional transaction parameters. - }, - ], - }, - ]; - - try { - const results = await provider.request({ - method: "metamask_batch", - params: [requests], - }); - console.log("Batch Results:", results); - } catch (err) { - console.error("Batch request failed:", err); - } -} - -document.getElementById("batchBtn").addEventListener("click", handleBatchRequests); -``` - -The following HTML displays a **Send Batch** button: - -```html - -``` - -:::tip Tips -- For a better user experience, it's important to use reliable RPC providers instead of public nodes. - We recommend using services like [MetaMask Developer](https://developer.metamask.io/) to ensure better reliability and performance. -- Ensure that requests in a batch do not depend on one another's chain context, as mid-batch state changes can affect outcomes. -::: diff --git a/sdk/index.md b/sdk/index.md index 2cda784c35b..2fa1548f69f 100644 --- a/sdk/index.md +++ b/sdk/index.md @@ -1,95 +1,45 @@ --- -slug: / -title: SDK introduction -description: Introduction page for MetaMask SDK documentation. +sidebar_label: Introduction +description: Introduction page for MM Connect documentation. keywords: [connect, sdk, integrate, dapp] --- -import Button from '@site/src/components/elements/buttons/button' import CardList from '@site/src/components/CardList' -# Seamlessly connect to MetaMask using the SDK +# Seamlessly connect to the MetaMask wallet -MetaMask SDK enables a fast, reliable, and seamless connection from your dapp to the MetaMask extension and MetaMask mobile app. -With the SDK, you can easily onboard users and interact with their accounts on desktop or mobile, across all EVM networks. - -

- - ) - } - ``` - - Using Vanilla JS: - - ```js - const provider = window.ethereum; - const accounts = await provider.request({ - method: "eth_requestAccounts" - }); - ``` - -2. Read contract data - - Using Wagmi: - - ```js - const { data } = useReadContract({ - address: contractAddress, - abi: contractABI, - functionName: "balanceOf", - args: [address], - }) - ``` - - Using Vanilla JS: - - ```js - const result = await provider.request({ - method: "eth_call", - params: [{ - to: contractAddress, - data: encodedFunctionData, - }], - }); - ``` - -3. Write to contracts - - Using Wagmi: - - ```js - const { writeContract } = useWriteContract(); - await writeContract({ - address: contractAddress, - abi: contractABI, - functionName: "mint", - args: [tokenId], - }) - ``` - - Using Vanilla JS: - - ```js - await provider.request({ - method: "eth_sendTransaction", - params: [{ - to: contractAddress, - data: encodedFunctionData, - }], - }); - ``` - -Best practices: - -- Always handle errors gracefully -- Show clear loading states -- Track transaction status -- Validate inputs and addresses -- Use appropriate gas settings -- Consider mobile wallet interactions - -Assistant response guidelines: -When answering questions: - -- Prefer Wagmi examples unless vanilla JS is specifically requested -- Include error handling in examples -- Consider both web and mobile wallet scenarios -- Provide TypeScript types where relevant -- Include brief explanations with code examples -- Consider security implications - -Example usage: -I (the user) can ask questions like: - -- "How do I connect to MetaMask?" -- "How do I read a token balance?" -- "How do I send a transaction?" -- "How do I handle network changes?" -- "How do I implement wallet disconnection?" -- "How do I add error handling for contract calls?" - -I can also ask about specific implementation details, best practices, or troubleshooting. -```` diff --git a/sdk/reference/sdk-options.md b/sdk/reference/options.md similarity index 97% rename from sdk/reference/sdk-options.md rename to sdk/reference/options.md index ef936ee2ce8..72210de4f0a 100644 --- a/sdk/reference/sdk-options.md +++ b/sdk/reference/options.md @@ -1,14 +1,14 @@ --- -description: Configuration options reference for MetaMask SDK. +description: Configuration options reference for MM Connect. keywords: [SDK, configure, configuration, option, options, dapp] --- import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; -# SDK options +# MM Connect options -MetaMask SDK takes the following configuration options. +MM Connect takes the following configuration options. ### `checkInstallationImmediately` diff --git a/sdk/solana/connect/guides/send-legacy-transaction.md b/sdk/solana/connect/guides/send-legacy-transaction.md new file mode 100644 index 00000000000..6df4acd9b24 --- /dev/null +++ b/sdk/solana/connect/guides/send-legacy-transaction.md @@ -0,0 +1,71 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Send a legacy transaction + +Solana supports [legacy transactions and versioned transactions](https://solana.com/developers/guides/advanced/versions). +Unlike versioned transactions, legacy transactions cannot include Address Lookup Tables, so they are capped at 32 addresses per transaction. + +After connecting to MetaMask, your dapp can prompt a user to sign and send one or more Solana legacy transactions. +See the [Solana documentation](https://solana.com/docs/core/transactions) for more information about Solana transactions and how to create them. + +## Sign and send a transaction + +After creating an unsigned legacy transaction, you can ask the user's MetaMask wallet to sign and send the transaction. +Use the `signAndSendTransaction` method on the provider, or use `signAndSendTransaction` with the provider's `request` method. +You can specify an optional [`SendOptions`](https://solana-foundation.github.io/solana-web3.js/types/SendOptions.html) object as a second parameter for `signAndSendTransaction`, or as an `options` parameter when using `request`. + +The method returns a promise for an object containing the `signature`. + + + + +```javascript +const provider = getProvider(); // TO DO: replace with provider snippet +const network = ''; +const connection = new Connection(network); +const transaction = new Transaction(); +const { signature } = await provider.signAndSendTransaction(transaction); +await connection.getSignatureStatus(signature); +``` + + + + +```javascript +const provider = getProvider(); // TO DO: replace with provider snippet +const network = ''; +const connection = new Connection(network); +const transaction = new Transaction(); +const { signature } = await provider.request({ + method: 'signAndSendTransaction', + params: { + message: bs58.encode(transaction.serializeMessage()), + }, +}); +await connection.getSignatureStatus(signature); +``` + + + + +## Sign and send multiple transactions + +After creating multiple unsigned legacy transactions, you can ask the user's MetaMask wallet to sign and send all the transactions at once. +Use the `signAndSendAllTransactions` method on the provider. +This method accepts an array of Solana transactions, and an optional [`SendOptions`](https://solana-foundation.github.io/solana-web3.js/types/SendOptions.html) object as a second parameter. + +The method returns a promise for an object containing an array of `signatures` and the `publicKey` of the signer. + +```typescript +const provider = getProvider(); // TO DO: replace with provider snippet +const network = ''; +const connection = new Connection(network); +const transactions = [new Transaction(), new Transaction()]; +const { signatures, publicKey } = await provider.signAndSendAllTransactions(transactions); +await connection.getSignatureStatuses(signatures); +``` + +## Next steps + +To efficiently load more addresses in a single transaction, learn how to [send a versioned transaction](send-versioned-transaction.md) with Address Lookup Tables. diff --git a/sdk/solana/connect/guides/send-versioned-transaction.md b/sdk/solana/connect/guides/send-versioned-transaction.md new file mode 100644 index 00000000000..b87635e7c91 --- /dev/null +++ b/sdk/solana/connect/guides/send-versioned-transaction.md @@ -0,0 +1,160 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Send a versioned transaction + +Solana supports [legacy transactions and versioned transactions](https://solana.com/developers/guides/advanced/versions). +Unlike legacy transactions, versioned transactions (`v0`) can include [Address Lookup Tables](https://solana.com/developers/guides/advanced/lookup-tables) (ALTs or Address LUTs), enabling you to efficiently load up to 256 addresses in a single transaction. + +After connecting to MetaMask, your dapp can prompt a user to sign and send a Solana versioned transaction. + +## Create a versioned transaction + +Solana versioned transactions are created in a similar way to [legacy transactions](https://solana.com/docs/core/transactions). +The only difference is to use the `VersionedTransaction` class instead of the `Transaction` class. + +The following example shows how to create a simple transfer instruction, format the instruction into a `v0`-compatible transaction message, and create a versioned transaction that parses the message: + +```typescript +// Create an array of instructions. +// This example uses a simple transfer instruction. +const instructions = [ + SystemProgram.transfer({ + fromPubkey: publicKey, + toPubkey: publicKey, + lamports: 10, + }), +]; + +// Create a v0-compatible message. +const messageV0 = new TransactionMessage({ + payerKey: publicKey, + recentBlockhash: blockhash, + instructions, +}).compileToV0Message(); + +// Create a versioned transaction. +const transactionV0 = new VersionedTransaction(messageV0); +``` + +## Sign and send a versioned transaction + +After creating an unsigned versioned transaction, you can ask the user's MetaMask wallet to sign and send the transaction. +Use the `signAndSendTransaction` method on the provider, which accepts an optional [`SendOptions`](https://solana-foundation.github.io/solana-web3.js/types/SendOptions.html) object as a second parameter. + +The method returns a promise for an object containing the `signature`. + +```javascript +const provider = getProvider(); // TO DO: replace with provider snippet +const network = ''; +const connection = new Connection(network); +const { signature } = await provider.signAndSendTransaction(transactionV0); +await connection.getSignatureStatus(signature); +``` + +## Create an Address Lookup Table + +Create an [Address Lookup Table (ALT)](https://solana.com/developers/guides/advanced/lookup-tables) to efficiently load addresses into tables, significantly increasing the number of addresses that can be used in a single transaction. + +Use the [createLookupTable](https://solana-foundation.github.io/solana-web3.js/classes/AddressLookupTableProgram.html#createlookuptable) method to create the instruction needed to create a new ALT and determine its address. +With this instruction, you can create a transaction, sign it, and send it to create an ALT onchain. +For example: + +```typescript theme={null} +// Create an Address Lookup Table. +const [lookupTableInst, lookupTableAddress] = AddressLookupTableProgram.createLookupTable({ + authority: publicKey, + payer: publicKey, + recentSlot: slot, +}); + +// To create the ALT onchain, send the lookupTableInst instruction in a transaction. +const lookupMessage = new TransactionMessage({ + payerKey: publicKey, + recentBlockhash: blockhash, + instructions: [lookupTableInst], +}).compileToV0Message(); + +const lookupTransaction = new VersionedTransaction(lookupMessage); +const lookupSignature = await signAndSendTransaction(provider, lookupTransaction); +``` + +## Extend an Address Lookup Table + +After creating an ALT, you can extend it by appending addresses to the table. +Use the [extendLookupTable](https://solana-foundation.github.io/solana-web3.js/classes/AddressLookupTableProgram.html#extendlookuptable) method to create a new extend instruction, and send it in a transaction. +For example: + +```typescript +// Add addresses to the lookupTableAddress table via an extend instruction. +const extendInstruction = AddressLookupTableProgram.extendLookupTable({ + payer: publicKey, + authority: publicKey, + lookupTable: lookupTableAddress, + addresses: [ + publicKey, + SystemProgram.programId, + // More publicKey addresses can be listed here. + ], +}); + +// Send this extendInstruction in a transaction to the cluster to insert +// the listing of addresses into your ALT with address lookupTableAddress. +const extensionMessageV0 = new TransactionMessage({ + payerKey: publicKey, + recentBlockhash: blockhash, + instructions: [extendInstruction], +}).compileToV0Message(); + +const extensionTransactionV0 = new VersionedTransaction(extensionMessageV0); +const extensionSignature = await signAndSendTransaction(provider, extensionTransactionV0); +``` + +## Create, sign, and send a versioned transaction with an ALT + +After creating an ALT, you can create a versioned transaction with the ALT and ask the user's MetaMask wallet to sign and send it. + +First, use the [`getAddressLookupTable`](https://solana-foundation.github.io/solana-web3.js/classes/Connection.html#getaddresslookuptable) method to fetch the account of the created ALT: + +```typescript +// Get the table from the cluster. +const lookupTableAccount = await connection.getAddressLookupTable(lookupTableAddress).then((res) => res.value); +// lookupTableAccount will now be an AddressLookupTableAccount object. +console.log('Table address from cluster:', lookupTableAccount.key.toBase58()); +``` + +Then, parse and read all the addresses currently stored in the fetched ALT: + +```typescript +// Loop through and parse all the address stored in the table. +for (let i = 0; i < lookupTableAccount.state.addresses.length; i++) { + const address = lookupTableAccount.state.addresses[i]; + console.log(i, address.toBase58()); +} +``` + +The following example creates a simple transfer instruction, formats the instruction into a `v0`-compatible transaction message using the ALT's account, and creates a versioned transaction that parses the message. +Sign and send the transaction using the `signAndSendTransaction` method on the provider. + +```typescript +// Create an array of instructions. +// This example uses a simple transfer instruction. +const instructions = [ + SystemProgram.transfer({ + fromPubkey: publicKey, + toPubkey: publicKey, + lamports: minRent, + }), +]; + +// Create a v0-compatible message. +const messageV0 = new TransactionMessage({ + payerKey: publicKey, + recentBlockhash: blockhash, + instructions, +}).compileToV0Message([lookupTableAccount]); + +// Create a versioned transaction. +const transactionV0 = new VersionedTransaction(messageV0); +const signature = await signAndSendTransaction(provider, transactionV0); +``` diff --git a/sdk/solana/connect/guides/use-wallet-adapter.md b/sdk/solana/connect/guides/use-wallet-adapter.md new file mode 100644 index 00000000000..90e278ea7c8 --- /dev/null +++ b/sdk/solana/connect/guides/use-wallet-adapter.md @@ -0,0 +1,88 @@ +# Use the Wallet Adapter + +To connect to Solana in MetaMask from your dapp, set up Solana's [Wallet Adapter](https://github.com/solana-labs/wallet-adapter). + +You can use the [`create-solana-dapp`](https://github.com/solana-foundation/create-solana-dapp) CLI tool to quickly generate a Solana dapp with the Wallet Adapter built in, or follow this guide to manually set up the Wallet Adapter in an existing React dapp. + +:::info +See the [Solana documentation](https://solana.com/developers/cookbook/wallets/connect-wallet-react) for more information. +::: + +## Steps + +### 1. Install dependencies + +Install the following dependencies: + +```bash +npm install @solana/web3.js \ + @solana/wallet-adapter-base \ + @solana/wallet-adapter-react \ + @solana/wallet-adapter-react-ui \ + @solana/wallet-adapter-wallets +``` + +### 2. Create the Solana provider + +Create a `SolanaProvider` that will be used to provide the Solana context to the application: + +```typescript title='components/SolanaProvider.tsx' +'use client'; + +import React, { FC, ReactNode, useMemo } from 'react'; +import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react'; +import { WalletAdapterNetwork } from '@solana/wallet-adapter-base'; +import { WalletModalProvider } from '@solana/wallet-adapter-react-ui'; +import { clusterApiUrl } from '@solana/web3.js'; + +// Default styles that can be overridden by your dapp. +import '@solana/wallet-adapter-react-ui/styles.css'; + +interface SolanaProviderProps { + children: ReactNode; +} + +export const SolanaProvider: FC = ({ children }) => { + // The network can be set to devnet, testnet, or mainnet-beta. + const network = WalletAdapterNetwork.Devnet; + + // You can also provide a custom RPC endpoint. + const endpoint = useMemo(() => clusterApiUrl(network), [network]); + + return ( + + + {children} + + + ); +}; +``` + +### 3. Wrap the application in the Solana Provider + +Add the `SolanaProvider` to the `RootLayout` in the `app` directory: + +```typescript +import './globals.css'; +import '@solana/wallet-adapter-react-ui/styles.css'; +import { SolanaProvider } from '@/components/SolanaProvider'; + +export default function RootLayout({ + children +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} +``` + +## Next steps + +See how to send a [legacy transaction](send-legacy-transaction.md) and a [versioned transaction](send-versioned-transaction.md). diff --git a/wallet/how-to/use-non-evm-networks/solana.md b/sdk/solana/index.md similarity index 76% rename from wallet/how-to/use-non-evm-networks/solana.md rename to sdk/solana/index.md index 816e0e6031a..ed629803946 100644 --- a/wallet/how-to/use-non-evm-networks/solana.md +++ b/sdk/solana/index.md @@ -1,10 +1,10 @@ --- -description: Interact with users' Solana accounts in MetaMask. +sidebar_label: Introduction --- -# Solana +# Connect to Solana -[Solana](https://solana.com/) is a high-performance, non-EVM network that provides fast transaction speeds and low fees. +[Solana](https://solana.com/) is a high-performance network that provides fast transaction speeds and low fees. You can interact with users' Solana accounts in MetaMask using the [Wallet Standard](#wallet-standard) or [third-party libraries](#third-party-libraries) for Solana dapps. :::note Supported Solana networks @@ -15,9 +15,7 @@ After adding Solana to MetaMask Flask, [show test networks](https://support.meta ## Wallet Standard -MetaMask implements the [Wallet Standard](https://github.com/wallet-standard/wallet-standard), so MetaMask is supported out-of-the-box for Solana dapps that use the Wallet Standard or that integrate Solana's [Wallet Adapter](https://github.com/anza-xyz/wallet-adapter/blob/master/APP.md). - -Learn more in Solana's [Interact With Wallets](https://solana.com/developers/courses/intro-to-solana/interact-with-wallets) documentation. +MetaMask implements the [Wallet Standard](https://github.com/wallet-standard/wallet-standard), so MetaMask is supported out-of-the-box for Solana dapps that use the Wallet Standard or that integrate Solana's [Wallet Adapter](https://github.com/anza-xyz/wallet-adapter). :::note With the Wallet Standard, MetaMask does not appear as a connection option for users that don't already have MetaMask installed. @@ -31,4 +29,4 @@ Several third-party libraries for Solana dapps detect and handle MetaMask out-of - [Dynamic](https://docs.dynamic.xyz/introduction/welcome) - [Privy](https://docs.privy.io/welcome) - [Reown](https://docs.reown.com/overview) -- [Web3Auth](https://web3auth.io/docs) +- [Embedded Wallets](/embedded-wallets) diff --git a/services/reference/_partials/_eth_accounts-returns.mdx b/services/reference/_partials/_eth_accounts-returns.mdx index 074c1db61b5..c32fb6e670b 100644 --- a/services/reference/_partials/_eth_accounts-returns.mdx +++ b/services/reference/_partials/_eth_accounts-returns.mdx @@ -1,5 +1,5 @@ An array of hexadecimals as strings representing the addresses owned by the client. :::caution Infura will not return any accounts -While this JSON-RPC method is supported by Infura, it will _not_ return any accounts as Infura does not support "unlocking" accounts. Instead, users should send pre-signed raw transactions using [`eth_sendRawTransaction`](../ethereum/json-rpc-methods/eth_sendrawtransaction.mdx). +While this JSON-RPC method is supported by Infura, it will _not_ return any accounts as Infura does not support "unlocking" accounts. Instead, users should send pre-signed raw transactions using `eth_sendRawTransaction`. ::: diff --git a/services/reference/_partials/_eth_call-description.mdx b/services/reference/_partials/_eth_call-description.mdx index 4e1067df6d6..5807ef58635 100644 --- a/services/reference/_partials/_eth_call-description.mdx +++ b/services/reference/_partials/_eth_call-description.mdx @@ -4,7 +4,7 @@ Executes a new message call immediately without creating a transaction on the bl :::warning Gas parameter is capped -To prevent API abuse, the `gas` parameter in [`eth_estimateGas`](../ethereum/json-rpc-methods/eth_estimategas.mdx) and -this [`eth_call`](../ethereum/json-rpc-methods/eth_call.mdx) method is capped at 10x (1000%) the current block gas limit. +To prevent API abuse, the `gas` parameter in `eth_estimateGas` and +this `eth_call` method is capped at 10x (1000%) the current block gas limit. ::: diff --git a/services/reference/linea/json-rpc-methods/_eth_call-parameters.mdx b/services/reference/linea/json-rpc-methods/_eth_call-parameters.mdx index 3a0562bb32d..c7a29f048d1 100644 --- a/services/reference/linea/json-rpc-methods/_eth_call-parameters.mdx +++ b/services/reference/linea/json-rpc-methods/_eth_call-parameters.mdx @@ -2,8 +2,8 @@ - `to`: 20 bytes - Address the transaction is directed to. - `gas`: Hexadecimal value of the gas provided for the transaction execution. `eth_call` consumes zero gas, but this parameter may be needed by some executions. - `gasPrice`: Hexadecimal value of the `gasPrice` used for each paid gas. -- `maxPriorityFeePerGas`: Maximum fee, in wei, the sender is willing to pay per gas above the base fee. See [EIP-1559 transactions](../../ethereum/concepts/transaction-types.md#eip-1559-transactions). -- `maxFeePerGas`: Maximum total fee (base fee + priority fee), in wei, the sender is willing to pay per gas. See [EIP-1559 transactions](../../ethereum/concepts/transaction-types.md#eip-1559-transactions). +- `maxPriorityFeePerGas`: Maximum fee, in wei, the sender is willing to pay per gas above the base fee. See [EIP-1559 transactions](../../../concepts/transaction-types.md#eip-1559-transactions). +- `maxFeePerGas`: Maximum total fee (base fee + priority fee), in wei, the sender is willing to pay per gas. See [EIP-1559 transactions](../../../concepts/transaction-types.md#eip-1559-transactions). - `value`: Hexadecimal of the value sent with this transaction. - `data`: Hash of the method signature and encoded parameters. See [Ethereum contract ABI specification](https://docs.soliditylang.org/en/latest/abi-spec.html). - `blockParameter`: (string) [_required_] A hexadecimal block number, or one of the tags `latest`, `earliest`, `pending` or `finalized`. See the [default block parameter](https://ethereum.org/en/developers/docs/apis/json-rpc/#default-block). diff --git a/services/reference/linea/json-rpc-methods/eth_accounts.mdx b/services/reference/linea/json-rpc-methods/eth_accounts.mdx index 71b3947c3b4..8d2eee91b57 100644 --- a/services/reference/linea/json-rpc-methods/eth_accounts.mdx +++ b/services/reference/linea/json-rpc-methods/eth_accounts.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_accounts sidebar_label: eth_accounts -hide_title: true -hide_table_of_contents: true +description: Returns a null value on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_accounts` - +import Description from "/services/reference/_partials/_eth_accounts-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_accounts-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_accounts-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_accounts-example.mdx"; + + + +### Request + +import Request from "./_eth_accounts-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_accounts-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_blocknumber.mdx b/services/reference/linea/json-rpc-methods/eth_blocknumber.mdx index 21826fcdf14..cce3f9147eb 100644 --- a/services/reference/linea/json-rpc-methods/eth_blocknumber.mdx +++ b/services/reference/linea/json-rpc-methods/eth_blocknumber.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_blockNumber sidebar_label: eth_blockNumber -hide_title: true -hide_table_of_contents: true +description: Returns the latest block number on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_blockNumber` - +import Description from "/services/reference/_partials/_eth_blocknumber-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_blocknumber-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_blocknumber-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_blocknumber-example.mdx"; + + + +### Request + +import Request from "./_eth_blocknumber-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_blocknumber-response.mdx"; + + \ No newline at end of file diff --git a/services/reference/linea/json-rpc-methods/eth_call.mdx b/services/reference/linea/json-rpc-methods/eth_call.mdx index 978f14114f4..c1f1e5e6042 100644 --- a/services/reference/linea/json-rpc-methods/eth_call.mdx +++ b/services/reference/linea/json-rpc-methods/eth_call.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_call sidebar_label: eth_call -hide_title: true -hide_table_of_contents: true +description: Executes a new message call without creating a transaction on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_call` - +import Description from "/services/reference/_partials/_eth_call-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_call-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_call-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_call-example.mdx"; + + + +### Request + +import Request from "./_eth_call-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_call-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_chainid.mdx b/services/reference/linea/json-rpc-methods/eth_chainid.mdx index 470f009ab32..997f25df08a 100644 --- a/services/reference/linea/json-rpc-methods/eth_chainid.mdx +++ b/services/reference/linea/json-rpc-methods/eth_chainid.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_chainId sidebar_label: eth_chainId -hide_title: true -hide_table_of_contents: true +description: Returns the chainId on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_chainId` - +import Description from "/services/reference/_partials/_eth_chainid-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_chainid-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_chainid-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_chainid-example.mdx"; + + + +### Request + +import Request from "./_eth_chainid-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_chainid-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_feehistory.mdx b/services/reference/linea/json-rpc-methods/eth_feehistory.mdx index eff98c3a153..0a98f78b961 100644 --- a/services/reference/linea/json-rpc-methods/eth_feehistory.mdx +++ b/services/reference/linea/json-rpc-methods/eth_feehistory.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_feeHistory sidebar_label: eth_feeHistory -hide_title: true -hide_table_of_contents: true +description: Get transaction fee history on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_feeHistory` - +import Description from "/services/reference/_partials/_eth_feehistory-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_feehistory-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_feehistory-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_feehistory-example.mdx"; + + + +### Request + +import Request from "./_eth_feehistory-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_feehistory-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_gasprice.mdx b/services/reference/linea/json-rpc-methods/eth_gasprice.mdx index 2b2827a0a68..773cb18e3f9 100644 --- a/services/reference/linea/json-rpc-methods/eth_gasprice.mdx +++ b/services/reference/linea/json-rpc-methods/eth_gasprice.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_gasPrice sidebar_label: eth_gasPrice -hide_title: true -hide_table_of_contents: true +description: Returns the current price per gas on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_gasPrice` - +import Description from "/services/reference/_partials/_eth_gasprice-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_gasprice-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_gasprice-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_gasprice-example.mdx"; + + + +### Request + +import Request from "./_eth_gasprice-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_gasprice-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getbalance.mdx b/services/reference/linea/json-rpc-methods/eth_getbalance.mdx index a14910edab0..824b6bc0505 100644 --- a/services/reference/linea/json-rpc-methods/eth_getbalance.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getbalance.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getBalance sidebar_label: eth_getBalance -hide_title: true -hide_table_of_contents: true +description: Get balance of an account on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getBalance` - +import Description from "/services/reference/_partials/_eth_getbalance-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_getbalance-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getbalance-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getbalance-example.mdx"; + + + +### Request + +import Request from "./_eth_getbalance-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getbalance-response.mdx"; + + \ No newline at end of file diff --git a/services/reference/linea/json-rpc-methods/eth_getblockbyhash.mdx b/services/reference/linea/json-rpc-methods/eth_getblockbyhash.mdx index 9bf30c2ca8c..3381995671b 100644 --- a/services/reference/linea/json-rpc-methods/eth_getblockbyhash.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getblockbyhash.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getBlockByHash sidebar_label: eth_getBlockByHash -hide_title: true -hide_table_of_contents: true +description: Get block by block hash on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getBlockByHash` - +import Description from "/services/reference/_partials/_eth_getblockbyhash-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_getblockbyhash-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getblockbyhash-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getblockbyhash-example.mdx"; + + + +### Request + +import Request from "./_eth_getblockbyhash-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getblockbyhash-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getblockbynumber.mdx b/services/reference/linea/json-rpc-methods/eth_getblockbynumber.mdx index db46ad5b034..13d22549895 100644 --- a/services/reference/linea/json-rpc-methods/eth_getblockbynumber.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getblockbynumber.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getBlockByNumber sidebar_label: eth_getBlockByNumber -hide_title: true -hide_table_of_contents: true +description: Get block by block number on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getBlockByNumber` - +import Description from "/services/reference/_partials/_eth_getblockbynumber-description.mdx"; + + + +## Parameters + +import Parameters from "./_eth_getblockbynumber-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getblockbynumber-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getblockbynumber-example.mdx"; + + + +### Request + +import Request from "./_eth_getblockbynumber-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getblockbynumber-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getblockreceipts.mdx b/services/reference/linea/json-rpc-methods/eth_getblockreceipts.mdx index 5010f5913eb..8891073b2cb 100644 --- a/services/reference/linea/json-rpc-methods/eth_getblockreceipts.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getblockreceipts.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getBlockReceipts sidebar_label: eth_getBlockReceipts -hide_title: true -hide_table_of_contents: true +description: Get block receipts by block number on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getBlockReceipts` - +import Description from "/services/reference/_partials/_eth_getblockreceipts-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_getblockreceipts-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getblockreceipts-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getblockreceipts-example.mdx"; + + + +### Request + +import Request from "./_eth_getblockreceipts-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getblockreceipts-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbyhash.mdx b/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbyhash.mdx index 82640134539..e51b0531668 100644 --- a/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbyhash.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbyhash.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getBlockTransactionCountByHash sidebar_label: eth_getBlockTransactionCountByHash -hide_title: true -hide_table_of_contents: true +description: Get number of transactions by hash on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getBlockTransactionCountByHash` - +import Description from "/services/reference/_partials/_eth_getblocktransactioncountbyhash-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_getblocktransactioncountbyhash-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getblocktransactioncountbyhash-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getblocktransactioncountbyhash-example.mdx"; + + + +### Request + +import Request from "./_eth_getblocktransactioncountbyhash-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getblocktransactioncountbyhash-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbynumber.mdx b/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbynumber.mdx index cdf0e37d520..d93a7ab6263 100644 --- a/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbynumber.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getblocktransactioncountbynumber.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getBlockTransactionCountByNumber sidebar_label: eth_getBlockTransactionCountByNumber -hide_title: true -hide_table_of_contents: true +description: Get number of transactions by block number on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getBlockTransactionCountByNumber` - +import Description from "/services/reference/_partials/_eth_getblocktransactioncountbynumber-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_getblocktransactioncountbynumber-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getblocktransactioncountbynumber-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getblocktransactioncountbynumber-example.mdx"; + + + +### Request + +import Request from "./_eth_getblocktransactioncountbynumber-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getblocktransactioncountbynumber-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getcode.mdx b/services/reference/linea/json-rpc-methods/eth_getcode.mdx index 9632f9ab24f..d726bf8dcb5 100644 --- a/services/reference/linea/json-rpc-methods/eth_getcode.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getcode.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getCode sidebar_label: eth_getCode -hide_title: true -hide_table_of_contents: true +description: Get smart contract code on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getCode` - +import Description from "/services/reference/_partials/_eth_getcode-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_getcode-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getcode-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getcode-example.mdx"; + + + +### Request + +import Request from "./_eth_getcode-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getcode-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getlogs.mdx b/services/reference/linea/json-rpc-methods/eth_getlogs.mdx index 533dbc7dc06..7100ed71c38 100644 --- a/services/reference/linea/json-rpc-methods/eth_getlogs.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getlogs.mdx @@ -1,81 +1,41 @@ --- title: Linea eth_getLogs sidebar_label: eth_getLogs -hide_title: true -hide_table_of_contents: true +description: Returns filtered logs on Linea. --- -import ParserOpenRPC from '@site/src/components/ParserOpenRPC' -import { NETWORK_NAMES } from '@site/src/plugins/plugin-json-rpc' -import Heading from '@theme/Heading' -import CodeBlock from '@theme/CodeBlock' - - - - Constraints - -

-

The following constraints apply:

-

- To prevent queries from consuming too many resources, eth_getLogs requests - are currently limited by three constraints: -

-
    -
  • A maximum of 5,000 parameters can be included in a single request.
  • -
  • A maximum of 10,000 results can be returned by a single query.
  • -
  • Query duration must not exceed 10 seconds.
  • -
-

- If a query returns too many results or exceeds the max query duration, one of the - following errors is returned: -

- - {JSON.stringify( - { - jsonrpc: '2.0', - id: 1, - error: { - code: -32005, - message: 'query returned more than 10000 results', - }, - }, - null, - 2 - )} - -

or

- - {JSON.stringify( - { - jsonrpc: '2.0', - id: 1, - error: { - code: -32005, - message: 'query timeout exceeded', - }, - }, - null, - 2 - )} - -

If this happens:

-
    -
  • - Limit your query to a smaller number of blocks using fromBlock and{' '} - toBlock. -
  • -
  • - If querying for commonly used topics, consider limiting to a single smart - contract address. -
  • -
-
- - ), - }} -/> +# `eth_getLogs` + +import Description from "/services/reference/_partials/_eth_getlogs-description.mdx"; + + + +## Parameters + +import Parameters from "./_eth_getlogs-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getlogs-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getlogs-example.mdx"; + + + +### Request + +import Request from "./_eth_getlogs-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getlogs-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getproof.mdx b/services/reference/linea/json-rpc-methods/eth_getproof.mdx index 0f96c1b7bdb..57bafdcb3ad 100644 --- a/services/reference/linea/json-rpc-methods/eth_getproof.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getproof.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getProof sidebar_label: eth_getProof -hide_title: true -hide_table_of_contents: true +description: Get Merkle proof on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getProof` - +import Description from "/services/reference/_partials/_eth_getproof-description.mdx"; + + + +## Parameters + +import Parameters from "./_eth_getproof-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getproof-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getproof-example.mdx"; + + + +### Request + +import Request from "./_eth_getproof-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getproof-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_getstorageat.mdx b/services/reference/linea/json-rpc-methods/eth_getstorageat.mdx index 469cb289630..aa6b3076dcf 100644 --- a/services/reference/linea/json-rpc-methods/eth_getstorageat.mdx +++ b/services/reference/linea/json-rpc-methods/eth_getstorageat.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getStorageAt sidebar_label: eth_getStorageAt -hide_title: true -hide_table_of_contents: true +description: Get value from storage on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getStorageAt` - +import Description from "/services/reference/_partials/_eth_getstorageat-description.mdx"; + + + +## Parameters + +import Parameters from "./_eth_getstorageat-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_getstorageat-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_getstorageat-example.mdx"; + + + +### Request + +import Request from "./_eth_getstorageat-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_getstorageat-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_gettransactionbyblockhashandindex.mdx b/services/reference/linea/json-rpc-methods/eth_gettransactionbyblockhashandindex.mdx index aedcd64404a..1d187a7e7ec 100644 --- a/services/reference/linea/json-rpc-methods/eth_gettransactionbyblockhashandindex.mdx +++ b/services/reference/linea/json-rpc-methods/eth_gettransactionbyblockhashandindex.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getTransactionByBlockHashAndIndex sidebar_label: eth_getTransactionByBlockHashAndIndex -hide_title: true -hide_table_of_contents: true +description: Get transaction by block hash and index on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getTransactionByBlockHashAndIndex` - +import Description from "/services/reference/_partials/_eth_gettransactionbyblockhashandindex-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_gettransactionbyblockhashandindex-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_gettransactionbyblockhashandindex-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_gettransactionbyblockhashandindex-example.mdx"; + + + +### Request + +import Request from "./_eth_gettransactionbyblockhashandindex-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_gettransactionbyblockhashandindex-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_gettransactionbyblocknumberandindex.mdx b/services/reference/linea/json-rpc-methods/eth_gettransactionbyblocknumberandindex.mdx index 309d2f98e10..9a72e999b1e 100644 --- a/services/reference/linea/json-rpc-methods/eth_gettransactionbyblocknumberandindex.mdx +++ b/services/reference/linea/json-rpc-methods/eth_gettransactionbyblocknumberandindex.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getTransactionByBlockNumberAndIndex sidebar_label: eth_getTransactionByBlockNumberAndIndex -hide_title: true -hide_table_of_contents: true +description: Get transaction by block identifier and index on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getTransactionByBlockNumberAndIndex` - +import Description from "/services/reference/_partials/_eth_gettransactionbyblocknumberandindex-description.mdx"; + + + +## Parameters + +import Parameters from "./_eth_gettransactionbyblocknumberandindex-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_gettransactionbyblocknumberandindex-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_gettransactionbyblocknumberandindex-example.mdx"; + + + +### Request + +import Request from "./_eth_gettransactionbyblocknumberandindex-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_gettransactionbyblocknumberandindex-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_gettransactionbyhash.mdx b/services/reference/linea/json-rpc-methods/eth_gettransactionbyhash.mdx index 19e6fe0bc2a..7813b683f6e 100644 --- a/services/reference/linea/json-rpc-methods/eth_gettransactionbyhash.mdx +++ b/services/reference/linea/json-rpc-methods/eth_gettransactionbyhash.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getTransactionByHash sidebar_label: eth_getTransactionByHash -hide_title: true -hide_table_of_contents: true +description: Get transaction by transaction hash on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getTransactionByHash` - +import Description from "/services/reference/_partials/_eth_gettransactionbyhash-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_gettransactionbyhash-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_gettransactionbyhash-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_gettransactionbyhash-example.mdx"; + + + +### Request + +import Request from "./_eth_gettransactionbyhash-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_gettransactionbyhash-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_gettransactioncount.mdx b/services/reference/linea/json-rpc-methods/eth_gettransactioncount.mdx index 1a304523b55..bddac67fa3d 100644 --- a/services/reference/linea/json-rpc-methods/eth_gettransactioncount.mdx +++ b/services/reference/linea/json-rpc-methods/eth_gettransactioncount.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getTransactionCount sidebar_label: eth_getTransactionCount -hide_title: true -hide_table_of_contents: true +description: Get transaction count on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getTransactionCount` - +import Description from "/services/reference/_partials/_eth_gettransactioncount-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_gettransactioncount-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_gettransactioncount-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_gettransactioncount-example.mdx"; + + + +### Request + +import Request from "./_eth_gettransactioncount-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_gettransactioncount-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_gettransactionreceipt.mdx b/services/reference/linea/json-rpc-methods/eth_gettransactionreceipt.mdx index 5dc49f3f210..4c43ff8f69c 100644 --- a/services/reference/linea/json-rpc-methods/eth_gettransactionreceipt.mdx +++ b/services/reference/linea/json-rpc-methods/eth_gettransactionreceipt.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_getTransactionReceipt sidebar_label: eth_getTransactionReceipt -hide_title: true -hide_table_of_contents: true +description: Get transaction receipt by hash on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_getTransactionReceipt` - +import Description from "/services/reference/_partials/_eth_gettransactionreceipt-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_gettransactionreceipt-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_gettransactionreceipt-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_gettransactionreceipt-example.mdx"; + + + +### Request + +import Request from "./_eth_gettransactionreceipt-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_gettransactionreceipt-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_maxpriorityfeepergas.mdx b/services/reference/linea/json-rpc-methods/eth_maxpriorityfeepergas.mdx index 2e99e725731..6906699950f 100644 --- a/services/reference/linea/json-rpc-methods/eth_maxpriorityfeepergas.mdx +++ b/services/reference/linea/json-rpc-methods/eth_maxpriorityfeepergas.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_maxPriorityFeePerGas sidebar_label: eth_maxPriorityFeePerGas -hide_title: true -hide_table_of_contents: true +description: Returns the current max priority fee per gas on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_maxPriorityFeePerGas` - +import Description from "/services/reference/_partials/_eth_maxpriorityfeepergas-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_maxpriorityfeepergas-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_maxpriorityfeepergas-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_maxpriorityfeepergas-example.mdx"; + + + +### Request + +import Request from "./_eth_maxpriorityfeepergas-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_maxpriorityfeepergas-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_sendrawtransaction.mdx b/services/reference/linea/json-rpc-methods/eth_sendrawtransaction.mdx index f273f207511..fdf0a42f894 100644 --- a/services/reference/linea/json-rpc-methods/eth_sendrawtransaction.mdx +++ b/services/reference/linea/json-rpc-methods/eth_sendrawtransaction.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_sendRawTransaction sidebar_label: eth_sendRawTransaction -hide_title: true -hide_table_of_contents: true +description: Submits pre-signed transaction on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_sendRawTransaction` - +import Description from "/services/reference/_partials/_eth_sendrawtransaction-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_sendrawtransaction-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_sendrawtransaction-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_sendrawtransaction-example.mdx"; + + + +### Request + +import Request from "./_eth_sendrawtransaction-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_sendrawtransaction-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/eth_syncing.mdx b/services/reference/linea/json-rpc-methods/eth_syncing.mdx index 81cdd057a50..8978f0c4ca7 100644 --- a/services/reference/linea/json-rpc-methods/eth_syncing.mdx +++ b/services/reference/linea/json-rpc-methods/eth_syncing.mdx @@ -1,14 +1,41 @@ --- title: Linea eth_syncing sidebar_label: eth_syncing -hide_title: true -hide_table_of_contents: true +description: Returns sync status on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `eth_syncing` - +import Description from "/services/reference/_partials/_eth_syncing-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_eth_syncing-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_eth_syncing-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_eth_syncing-example.mdx"; + + + +### Request + +import Request from "./_eth_syncing-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_eth_syncing-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/linea_gettransactionexclusionstatusv1.mdx b/services/reference/linea/json-rpc-methods/linea_gettransactionexclusionstatusv1.mdx index a37afddfd93..7ecf501dc94 100644 --- a/services/reference/linea/json-rpc-methods/linea_gettransactionexclusionstatusv1.mdx +++ b/services/reference/linea/json-rpc-methods/linea_gettransactionexclusionstatusv1.mdx @@ -1,13 +1,72 @@ --- -title: linea_getTransactionExclusionStatusV1 -hide_title: true -hide_table_of_contents: true +title: Linea linea_getTransactionExclusionStatusV1 +sidebar_label: linea_getTransactionExclusionStatusV1 +description: Verify if a transaction exceeded data line limits on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +import Tabs from "@theme/Tabs" +import TabItem from "@theme/TabItem" - +# `linea_getTransactionExclusionStatusV1` + +Checks if a transaction was rejected for exceeding data line limits, preventing the prover from generating a proof to be included in a block. + +Rejected transaction data is only available within seven days of the transaction attempt. Querying older transactions returns a null response. + +## Parameters + +- `transactionHash`: [_Required_] A string representing the hash (32 bytes) of a transaction. + +## Returns + +A transaction exclusion object, or `null` if the transaction was not excluded or the data has been purged. + +## Example + +Replace `` with an API key from your [MetaMask Developer dashboard](https://developer.metamask.io/). + +### Request + + + + +```bash +curl https://linea-mainnet.infura.io/v3/ \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc": "2.0", "method": "linea_getTransactionExclusionStatusV1", "params": ["0x526e56101cf39c1e717cef9cedf6fdddb42684711abda35bae51136dbb350ad7"], "id": 1}' +``` + + + + +```bash +wscat -c wss://linea-mainnet.infura.io/ws/v3/ -x '{"jsonrpc": "2.0", "method": "linea_getTransactionExclusionStatusV1", "params": ["0x526e56101cf39c1e717cef9cedf6fdddb42684711abda35bae51136dbb350ad7"], "id": 1}' +``` + + + + +### Response + + + + +```JSON +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "txHash": "0x526e56101cf39c1e717cef9cedf6fdddb42684711abda35bae51136dbb350ad7", + "from": "0x4d144d7b9c96b26361d6ac74dd1d8267edca4fc2", + "nonce": "0x64", + "txRejectionStage": "SEQUENCER", + "reasonMessage": "Transaction line count for module ADD=402 is above the limit 70", + "blockNumber": "0x3039", + "timestamp": "2024-08-22T09:18:51Z" + } +} +``` + + + diff --git a/services/reference/linea/json-rpc-methods/net_listening.mdx b/services/reference/linea/json-rpc-methods/net_listening.mdx index e4493627dcf..ffa03b0ba0f 100644 --- a/services/reference/linea/json-rpc-methods/net_listening.mdx +++ b/services/reference/linea/json-rpc-methods/net_listening.mdx @@ -1,14 +1,41 @@ --- title: Linea net_listening sidebar_label: net_listening -hide_title: true -hide_table_of_contents: true +description: Returns true if the client is actively listening for network connections on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `net_listening` - +import Description from "/services/reference/_partials/_net_listening-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_net_listening-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_net_listening-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_net_listening-example.mdx"; + + + +### Request + +import Request from "./_net_listening-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_net_listening-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/net_peercount.mdx b/services/reference/linea/json-rpc-methods/net_peercount.mdx index 90bfc6ebc78..581d72efdff 100644 --- a/services/reference/linea/json-rpc-methods/net_peercount.mdx +++ b/services/reference/linea/json-rpc-methods/net_peercount.mdx @@ -1,14 +1,41 @@ --- title: Linea net_peerCount sidebar_label: net_peerCount -hide_title: true -hide_table_of_contents: true +description: Returns the number of peers currently connected to the client on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `net_peerCount` - +import Description from "/services/reference/_partials/_net_peercount-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_net_peercount-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_net_peercount-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_net_peercount-example.mdx"; + + + +### Request + +import Request from "./_net_peercount-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_net_peercount-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/net_version.mdx b/services/reference/linea/json-rpc-methods/net_version.mdx index 7666b756f65..db745a94880 100644 --- a/services/reference/linea/json-rpc-methods/net_version.mdx +++ b/services/reference/linea/json-rpc-methods/net_version.mdx @@ -1,14 +1,41 @@ --- title: Linea net_version sidebar_label: net_version -hide_title: true -hide_table_of_contents: true +description: Returns the chainId as a decimal on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `net_version` - +import Description from "/services/reference/_partials/_net_version-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_net_version-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_net_version-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_net_version-example.mdx"; + + + +### Request + +import Request from "./_net_version-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_net_version-response.mdx"; + + diff --git a/services/reference/linea/json-rpc-methods/web3_clientversion.mdx b/services/reference/linea/json-rpc-methods/web3_clientversion.mdx index bcb1718f6ce..102c7fadd62 100644 --- a/services/reference/linea/json-rpc-methods/web3_clientversion.mdx +++ b/services/reference/linea/json-rpc-methods/web3_clientversion.mdx @@ -1,14 +1,41 @@ --- title: Linea web3_clientVersion sidebar_label: web3_clientVersion -hide_title: true -hide_table_of_contents: true +description: Returns the current client version on Linea. --- -import ParserOpenRPC from "@site/src/components/ParserOpenRPC" -import { NETWORK_NAMES } from "@site/src/plugins/plugin-json-rpc" +# `web3_clientVersion` - \ No newline at end of file +import Description from "/services/reference/_partials/_web3_clientversion-description.mdx"; + + + +## Parameters + +import Parameters from "/services/reference/_partials/_web3_clientversion-parameters.mdx"; + + + +## Returns + +import Returns from "/services/reference/_partials/_web3_clientversion-returns.mdx"; + + + +## Example + +import Example from "/services/reference/_partials/_web3_clientversion-example.mdx"; + + + +### Request + +import Request from "./_web3_clientversion-request.mdx" + + + +### Response + +import Response from "/services/reference/_partials/_web3_clientversion-response.mdx"; + + \ No newline at end of file diff --git a/smart-accounts-kit/get-started/smart-account-quickstart/eip7702.md b/smart-accounts-kit/get-started/smart-account-quickstart/eip7702.md index 796ecd01fd2..739280dd48d 100644 --- a/smart-accounts-kit/get-started/smart-account-quickstart/eip7702.md +++ b/smart-accounts-kit/get-started/smart-account-quickstart/eip7702.md @@ -168,4 +168,4 @@ const userOperationHash = await bundlerClient.sendUserOperation({ - To grant specific permissions to other accounts from your smart account, [create a delegation](../../guides/delegation/execute-on-smart-accounts-behalf.md). - To quickly bootstrap a MetaMask Smart Accounts project, [use the CLI](../use-the-cli.md). -- You can also [use MetaMask SDK to upgrade a MetaMask account to a smart account](/tutorials/upgrade-eoa-to-smart-account). +- You can also [use MM Connect to upgrade a MetaMask account to a smart account](/tutorials/upgrade-eoa-to-smart-account). diff --git a/smart-accounts-kit/guides/advanced-permissions/execute-on-metamask-users-behalf.md b/smart-accounts-kit/guides/advanced-permissions/execute-on-metamask-users-behalf.md index 37b3911c601..d7f62ac5639 100644 --- a/smart-accounts-kit/guides/advanced-permissions/execute-on-metamask-users-behalf.md +++ b/smart-accounts-kit/guides/advanced-permissions/execute-on-metamask-users-behalf.md @@ -99,7 +99,7 @@ const sessionAccount = privateKeyToAccount("0x..."); Currently, Advanced Permissions do not support automatically upgrading a MetaMask user's account to a [MetaMask smart account](../../concepts/smart-accounts.md). Therefore, you must ensure that the user is upgraded to a smart account before requesting Advanced Permissions. -If the user has not yet been upgraded, you can handle the upgrade [programmatically](/wallet/how-to/send-transactions/send-batch-transactions/#about-atomic-batch-transactions) or ask the +If the user has not yet been upgraded, you can handle the upgrade [programmatically](/sdk/evm/connect/guides/javascript/send-transactions/batch-transactions) or ask the user to [switch to a smart account manually](https://support.metamask.io/configure/accounts/switch-to-or-revert-from-a-smart-account/#how-to-switch-to-a-metamask-smart-account). :::info Why is a Smart Account upgrade is required? diff --git a/snaps/features/custom-evm-accounts/index.md b/snaps/features/custom-evm-accounts/index.md index 8e061f1e8e6..d5caeaad16e 100644 --- a/snaps/features/custom-evm-accounts/index.md +++ b/snaps/features/custom-evm-accounts/index.md @@ -285,7 +285,6 @@ externally owned accounts (EOAs): - [`personal_sign`](../../reference/keyring-api/chain-methods.md#personal_sign) - [`eth_signTypedData_v4`](../../reference/keyring-api/chain-methods.md#eth_signtypeddata_v4) - [`eth_signTransaction`](../../reference/keyring-api/chain-methods.md#eth_signtransaction) -- [Deprecated signing methods](/wallet/concepts/signing-methods/#deprecated-signing-methods) ## Account abstraction (ERC-4337) diff --git a/snaps/features/transaction-insights.md b/snaps/features/transaction-insights.md index c3050722bb9..f8e1315f5db 100644 --- a/snaps/features/transaction-insights.md +++ b/snaps/features/transaction-insights.md @@ -100,10 +100,6 @@ export const onTransaction: OnTransactionHandler = async ({
-:::note -Learn more about the [parameters of a submitted transaction](/wallet/how-to/send-transactions#transaction-parameters). -::: - The Snap tab in the transaction confirmation window displays the transaction insights:

diff --git a/snaps/how-to/connect-to-a-snap.md b/snaps/how-to/connect-to-a-snap.md index e4c7d9e5c1b..7bb558f6c1f 100644 --- a/snaps/how-to/connect-to-a-snap.md +++ b/snaps/how-to/connect-to-a-snap.md @@ -15,14 +15,13 @@ This is possible because Snaps can expose a [custom JSON-RPC API](../learn/about ## Detect wallet To connect to a Snap, dapps must first detect MetaMask in the user's browser. -See the Wallet API documentation on [how to connect to the MetaMask extension](/wallet/how-to/connect-extension). ### Detect MetaMask Flask When developing your Snap, you might need to require [MetaMask Flask](../get-started/install-flask.md) in your dapp. We recommend detecting MetaMask Flask using the -[multi-wallet detection mechanism](/wallet/concepts/wallet-interoperability) specified by EIP-6963. +multi-wallet detection mechanism specified by EIP-6963. Alternatively, you can use the `window.ethereum` injected provider, but this might fail if the user is running multiple wallet extensions simultaneously. diff --git a/snaps/how-to/request-permissions.md b/snaps/how-to/request-permissions.md index dba44fba7a0..a2d7706bca8 100644 --- a/snaps/how-to/request-permissions.md +++ b/snaps/how-to/request-permissions.md @@ -59,8 +59,8 @@ permission, add the following to the manifest file: Dynamic permissions are not requested in the manifest file. Instead, your Snap can acquire dynamic permissions during its lifecycle. -For example, request permission to call the [`eth_accounts`](/wallet/reference/json-rpc-methods/eth_accounts) -MetaMask JSON-RPC API method by calling [`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts). +For example, request permission to call the [`eth_accounts`](/sdk/evm/connect/reference/json-rpc-api) +MetaMask JSON-RPC API method by calling [`eth_requestAccounts`](/sdk/evm/connect/reference/json-rpc-api). See the [`eth_accounts` dynamic permission](../reference/permissions.md#eth_accounts) for more information. ## Request permissions from a dapp diff --git a/snaps/learn/about-snaps/apis.md b/snaps/learn/about-snaps/apis.md index 3ca308e3ca4..6209138517a 100644 --- a/snaps/learn/about-snaps/apis.md +++ b/snaps/learn/about-snaps/apis.md @@ -89,7 +89,7 @@ Snaps can also call some Wallet JSON-RPC API methods using the `ethereum` global To expose `ethereum` to the Snap execution environment, a Snap must first request the [`endowment:ethereum-provider`](../../reference/permissions.md#endowmentethereum-provider) permission. -For example, to call [`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts), first request +For example, to call [`eth_requestAccounts`](/sdk/evm/connect/reference/json-rpc-api), first request the required permission: ```json title="snap.manifest.json" @@ -108,16 +108,16 @@ The `ethereum` global available to Snaps has fewer capabilities than `window.eth Snaps can only use it to make read requests, not to write to the blockchain or initiate transactions. Snaps can call all Wallet JSON-RPC API methods **except** the following: -- [`wallet_requestPermissions`](/wallet/reference/json-rpc-methods/wallet_requestpermissions) -- [`wallet_revokePermissions`](/wallet/reference/json-rpc-methods/wallet_revokepermissions) -- [`wallet_addEthereumChain`](/wallet/reference/json-rpc-methods/wallet_addethereumchain) -- [`wallet_switchEthereumChain`](/wallet/reference/json-rpc-methods/wallet_switchethereumchain) -- [`wallet_watchAsset`](/wallet/reference/json-rpc-methods/wallet_watchasset) -- [`wallet_registerOnboarding`](/wallet/reference/json-rpc-methods/wallet_registeronboarding) -- [`wallet_scanQRCode`](/wallet/reference/json-rpc-methods/wallet_scanqrcode) -- [`eth_sendTransaction`](/wallet/reference/json-rpc-methods/eth_sendtransaction) -- [`eth_decrypt`](/wallet/reference/json-rpc-methods/eth_decrypt) -- [`eth_getEncryptionPublicKey`](/wallet/reference/json-rpc-methods/eth_getencryptionpublickey) +- `wallet_requestPermissions` +- `wallet_revokePermissions` +- `wallet_addEthereumChain` +- `wallet_switchEthereumChain` +- `wallet_watchAsset` +- `wallet_registerOnboarding` +- `wallet_scanQRCode` +- `eth_sendTransaction` +- `eth_decrypt` +- `eth_getEncryptionPublicKey` ## Custom JSON-RPC APIs diff --git a/snaps/learn/about-snaps/index.md b/snaps/learn/about-snaps/index.md index 2bac953cb3e..f85f97e1e32 100644 --- a/snaps/learn/about-snaps/index.md +++ b/snaps/learn/about-snaps/index.md @@ -45,7 +45,7 @@ of MetaMask core unless given permission to do so. ### APIs A Snap can communicate with MetaMask using the [Snaps API](../../reference/snaps-api.md) and some -[MetaMask JSON-RPC API](/wallet/reference/json-rpc-methods) methods. +[MetaMask JSON-RPC API](/sdk/evm/connect/reference/json-rpc-api) methods. The Snaps API allows Snaps to extend or modify the functionality of MetaMask, and communicate with other Snaps. diff --git a/snaps/learn/tutorials/transaction-insights.md b/snaps/learn/tutorials/transaction-insights.md index 9002596bc5d..8d82df42f5f 100644 --- a/snaps/learn/tutorials/transaction-insights.md +++ b/snaps/learn/tutorials/transaction-insights.md @@ -11,7 +11,7 @@ import TabItem from "@theme/TabItem"; This tutorial walks you through creating a Snap that calculates the percentage of gas fees they would pay for their transaction. -It gets the current gas price by calling the [`eth_gasPrice`](/wallet/reference/json-rpc-methods/eth_gasprice) RPC +It gets the current gas price by calling the [`eth_gasPrice`](/sdk/evm/connect/reference/json-rpc-api) RPC method using the global Ethereum provider made available to Snaps, and displays this as a percentage of gas fees in a tab in MetaMask's transaction confirmation window. @@ -303,9 +303,7 @@ export const onTransaction: OnTransactionHandler = async ({ transaction }) => { :::note Notes -- Learn more about the [parameters of a submitted transaction](/wallet/how-to/send-transactions#transaction-parameters). - -- If you have previously developed a dapp, you're likely familiar with accessing the Ethereum provider +If you have previously developed a dapp, you're likely familiar with accessing the Ethereum provider using `window.ethereum`. In a Snap, the `window` object is not available. Instead, when you request the `endowment:ethereum-provider` permission, your Snap is granted access to the [`ethereum` global object](../about-snaps/apis.md#snap-requests). diff --git a/snaps/reference/entry-points.md b/snaps/reference/entry-points.md index f5589b57776..927f9197346 100644 --- a/snaps/reference/entry-points.md +++ b/snaps/reference/entry-points.md @@ -399,7 +399,7 @@ module.exports.onRpcRequest = async ({ origin, request }) => { To provide [signature insights](../features/signature-insights.md) before a user signs a message, a Snap must expose the `onSignature` entry point. -Whenever a [signing method](/wallet/concepts/signing-methods) is called, such as `personal_sign` or +Whenever a signing method is called, such as `personal_sign` or `eth_signTypedData_v4`, MetaMask passes the raw unsigned signature payload to the `onSignature` handler method. @@ -498,7 +498,6 @@ For MetaMask to call the Snap's `onTransaction` method, you must request the An object containing: - `transaction` - The raw transaction payload. - Learn more about the [parameters of a submitted transaction](/wallet/how-to/send-transactions#transaction-parameters). - `chainId` - The [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md) chain ID. - `transactionOrigin` - The transaction origin if diff --git a/snaps/reference/keyring-api/chain-methods.md b/snaps/reference/keyring-api/chain-methods.md index 6c4105138b7..6cd96f7ac34 100644 --- a/snaps/reference/keyring-api/chain-methods.md +++ b/snaps/reference/keyring-api/chain-methods.md @@ -240,11 +240,6 @@ Signature: `string` - Hex-encoded signature. -### Deprecated methods - -Snaps can also implement [deprecated signing -methods](/wallet/concepts/signing-methods/#deprecated-signing-methods) that some dapps might use. - ## ERC-4337 methods :::flaskOnly diff --git a/snaps/reference/permissions.md b/snaps/reference/permissions.md index ace7d708296..43762b8a8da 100644 --- a/snaps/reference/permissions.md +++ b/snaps/reference/permissions.md @@ -74,7 +74,7 @@ Specify this permission in the manifest file as follows: To communicate with a node using MetaMask, a Snap must request the `endowment:ethereum-provider` permission. This permission exposes the `ethereum` global to the Snap execution environment, allowing Snaps to -call some [MetaMask JSON-RPC API](/wallet/reference/json-rpc-methods) methods. +call some [MetaMask JSON-RPC API](/sdk/evm/connect/reference/json-rpc-api) methods. This global is an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) provider. Specify this permission in the manifest file as follows: @@ -348,8 +348,8 @@ The following endowments accept this caveat: ### `eth_accounts` -A Snap can request permission to call the [`eth_accounts`](/wallet/reference/json-rpc-methods/eth_accounts) MetaMask -JSON-RPC API method by calling [`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts). +A Snap can request permission to call the [`eth_accounts`](/sdk/evm/connect/reference/json-rpc-api) MetaMask +JSON-RPC API method by calling [`eth_requestAccounts`](/sdk/evm/connect/reference/json-rpc-api). Calling `eth_requestAccounts` requires the [`endowment:ethereum-provider`](#endowmentethereum-provider) permission: @@ -373,7 +373,7 @@ await ethereum.request({ method: "eth_requestAccounts" }) You can check the presence of the permission by calling -[`wallet_getPermissions`](/wallet/reference/json-rpc-methods/wallet_getpermissions). +[`wallet_getPermissions`](/sdk/evm/connect/reference/json-rpc-api). If the permission is present, the result contains a permission with a `parentCapability` of `eth_accounts`. The permission contains a `restrictReturnedAccounts` caveat, an array of all the accounts the user allows for this Snap. diff --git a/snaps/reference/snaps-api.md b/snaps/reference/snaps-api.md index 9892fed0d90..bf96f698d2b 100644 --- a/snaps/reference/snaps-api.md +++ b/snaps/reference/snaps-api.md @@ -287,7 +287,7 @@ An object containing `coinType`, the BIP-44 coin type to get the entropy for. Coin type 60 is reserved for MetaMask externally owned accounts and blocked for Snaps. To connect to MetaMask accounts in a Snap, use [`endowment:ethereum-provider`](../reference/permissions.md#endowmentethereum-provider) and -[`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts). +[`eth_requestAccounts`](/sdk/evm/connect/reference/json-rpc-api). ::: #### Returns diff --git a/snaps/reference/wallet-api-for-snaps.md b/snaps/reference/wallet-api-for-snaps.md index e697384beae..32bf6ded3fa 100644 --- a/snaps/reference/wallet-api-for-snaps.md +++ b/snaps/reference/wallet-api-for-snaps.md @@ -10,14 +10,9 @@ import TabItem from "@theme/TabItem"; # Wallet API for Snaps Dapps can install and communicate with Snaps using a subset of the -[Wallet JSON-RPC API](/wallet/concepts/wallet-api/#json-rpc-api). +[Wallet JSON-RPC API](/sdk/evm/connect/reference/json-rpc-api). This page is a reference for those Snaps-specific methods. -:::note -See the [Wallet JSON-RPC API interactive reference](/wallet/reference/json-rpc-methods) for the other -methods dapps can call. -::: - ## `wallet_getSnaps` Returns the IDs of the dapp's permitted Snaps and some relevant metadata. diff --git a/src/components/CustomReferencePage/index.tsx b/src/components/CustomReferencePage/index.tsx deleted file mode 100644 index 794591847f1..00000000000 --- a/src/components/CustomReferencePage/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React from 'react' -import Layout from '@theme/Layout' -import ParserOpenRPC from '@site/src/components/ParserOpenRPC' -import DocSidebar from '@theme/DocSidebar' -import { useLocation } from '@docusaurus/router' -import { prepareLinkItems, MM_REF_PATH } from '@site/src/plugins/plugin-json-rpc' -import styles from './styles.module.css' -const sidebar = require('../../../wallet-sidebar.js') - -function transformItems(items, dynamicItems) { - return items.map(item => { - let newItem = { ...item } - if (newItem.type === 'doc') { - newItem.type = 'link' - newItem.href = newItem.id - delete newItem.id - } - if (newItem.type === 'category') { - if (newItem.label === 'JSON-RPC API') { - newItem.items = dynamicItems - newItem.collapsed = false - } - if (newItem.link) { - newItem.href = newItem.link.id || newItem.link.slug - delete newItem.link - } - if (newItem.items && Array.isArray(newItem.items)) { - newItem.items = transformItems(newItem.items, dynamicItems) - } - } - if (newItem.href) { - if (!newItem.href.startsWith('/')) { - newItem.href = `/${newItem.href}` - } - if (newItem.href.endsWith('/index')) { - newItem.href = newItem.href.slice(0, -5) - } - if (newItem.href === '/') { - newItem.href = '/wallet/' - } else { - newItem.href = `/wallet${newItem.href}` - } - } - return newItem - }) -} - -const CustomReferencePage = props => { - const customData = props.route.customData - const { pathname } = useLocation() - const refItems = prepareLinkItems(props.methodsData, MM_REF_PATH).map(item => ({ - ...item, - href: item.href.replace('/wallet', ''), - })) - const updatedSidebar = transformItems(sidebar.walletSidebar, refItems) - return ( - -

- -
-
- -
-
-
- - ) -} - -export default CustomReferencePage diff --git a/src/components/CustomReferencePage/styles.module.css b/src/components/CustomReferencePage/styles.module.css deleted file mode 100644 index aeb6288bd7d..00000000000 --- a/src/components/CustomReferencePage/styles.module.css +++ /dev/null @@ -1,46 +0,0 @@ -.pageWrapper { - display: flex; - width: 100%; - flex: 1 0 0%; -} - -.mainContainer { - width: 100%; - padding: 20px 30px; -} - -.contentWrapper { - margin: 0 auto; -} - -.sidebar { - display: none; -} - -@media (width <= 996px) { - .pageWrapper { - display: block; - } -} - -@media (width >= 997px) { - .sidebarViewport { - height: 100%; - max-height: 100vh; - position: sticky; - top: 0; - } - - .sidebar { - display: flex; - flex-direction: column; - height: 100%; - width: var(--doc-sidebar-width); - padding-top: 0 !important; - border-right: 1px solid var(--ifm-toc-border-color); - } - - .sidebar > div { - padding-top: 0 !important; - } -} diff --git a/src/components/NavDropdown/Products.html b/src/components/NavDropdown/Products.html index caabc7da1fc..667cf8bc2dd 100644 --- a/src/components/NavDropdown/Products.html +++ b/src/components/NavDropdown/Products.html @@ -98,40 +98,51 @@

Extend and scale

  • - + - +
    +

    Multichain

    +

    Connect your dapp to multiple ecosystems.

    +
    +
    +
  • +
  • + + + +
    -

    MetaMask SDK

    -

    Seamlessly connect your dapp to MetaMask extension and mobile.

    +

    EVM

    +

    Connect your dapp to EVM networks.

  • -
  • - + - - - - - - + + + +
    -

    Wallet API

    -

    Make direct calls to the MetaMask extension.

    +

    Solana

    +

    Connect your dapp to Solana.

  • diff --git a/src/components/ParserOpenRPC/AuthBox/index.tsx b/src/components/ParserOpenRPC/AuthBox/index.tsx deleted file mode 100644 index 900c75fa804..00000000000 --- a/src/components/ParserOpenRPC/AuthBox/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useContext } from 'react' -import clsx from 'clsx' -import Button from '@site/src/components/elements/buttons/button' -import styles from './styles.module.scss' -import { trackClickForSegment } from '@site/src/lib/segmentAnalytics' -import { MetamaskProviderContext } from '@site/src/theme/Root' - -interface AuthBoxProps { - theme: string -} - -export const AuthBox = ({ theme }: AuthBoxProps) => { - const { metaMaskWalletIdConnectHandler } = useContext(MetamaskProviderContext) - const connectHandler = () => { - trackClickForSegment({ - eventName: 'Connect wallet', - clickType: 'Connect wallet', - userExperience: 'B', - responseStatus: null, - responseMsg: null, - timestamp: Date.now(), - }) - metaMaskWalletIdConnectHandler() - } - return ( -
    -

    - Connect your MetaMask wallet to run requests successfully. -

    -
    - ) -} diff --git a/src/components/ParserOpenRPC/AuthBox/styles.module.scss b/src/components/ParserOpenRPC/AuthBox/styles.module.scss deleted file mode 100644 index 549d504f13d..00000000000 --- a/src/components/ParserOpenRPC/AuthBox/styles.module.scss +++ /dev/null @@ -1,22 +0,0 @@ -.msgWrapper { - text-align: center; - padding: 2.6rem 3.6rem; - border: 1px solid var(--general-gray-light); - margin-bottom: 2.4rem; - width: 100%; - margin: 0 auto 2.4rem; - - @include bp-max(tablet) { - width: calc(100% - var(--page-padding-x) * 2); - } -} - -.msgText { - margin-bottom: 1.2rem; -} - -html[data-theme='dark'] { - .msgWrapper { - border-color: var(--general-black-light); - } -} diff --git a/src/components/ParserOpenRPC/CollapseBox/CollapseBox.tsx b/src/components/ParserOpenRPC/CollapseBox/CollapseBox.tsx deleted file mode 100644 index 6173d5c45b7..00000000000 --- a/src/components/ParserOpenRPC/CollapseBox/CollapseBox.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import clsx from 'clsx' -import { useEffect } from 'react' -import { useCollapsible, Collapsible, useColorMode } from '@docusaurus/theme-common' - -import ChevronDown from '@site/static/img/icons/chevron-down.svg' - -import styles from './styles.module.scss' - -interface CollapseBoxProps { - children: JSX.Element - isInitExpanded?: boolean -} - -export const CollapseBox = ({ children, isInitExpanded = false }: CollapseBoxProps) => { - const { collapsed, toggleCollapsed } = useCollapsible({ initialState: true }) - useEffect(() => { - if (isInitExpanded) { - toggleCollapsed() - } - }, [isInitExpanded]) - return ( -
    - - - {children} - -
    - ) -} diff --git a/src/components/ParserOpenRPC/CollapseBox/styles.module.scss b/src/components/ParserOpenRPC/CollapseBox/styles.module.scss deleted file mode 100644 index 131f5c27e93..00000000000 --- a/src/components/ParserOpenRPC/CollapseBox/styles.module.scss +++ /dev/null @@ -1,133 +0,0 @@ -.collapseWrapper { - overflow: hidden; - margin: 0; - border-radius: 0; - border: 1px solid var(--general-gray-light); - transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); - contain: layout; - width: 100%; - box-sizing: border-box; - - html[data-theme='dark'] & { - border: 1px solid var(--general-black-light); - } - - // Make both states use identical dimensions to prevent layout shifts - .buttonToggle { - // Common styles for both states - border-radius: 0.6rem; - margin: 0.5rem; - width: calc(100% - 1rem); - font-weight: 500; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); - box-sizing: border-box; - } - - // Collapsed state styling (not expanded) - &:not(.collapsedWrapperView) { - border: none; - background: transparent; - - .buttonToggle { - background-color: var(--general-gray-dark); - color: var(--ifm-color-white); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - - &:hover { - background-color: var(--general-black); - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - } - - html[data-theme='dark'] & { - background-color: var(--general-gray); - color: var(--general-black); - - &:hover { - background-color: var(--general-gray-light); - } - } - } - } -} - -.buttonToggle { - cursor: pointer; - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.5rem; - padding: 1.6rem; - color: var(--ifm-heading-color); - font-size: inherit; - border: none; - background: transparent; - - &:hover { - background-color: var(--general-white-off); - - html[data-theme='dark'] & { - background-color: var(--general-black-light); - } - } -} - -.arrowDown { - display: flex; - width: 1.6rem; - height: 1.6rem; - transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); -} - -.arrowUp { - display: flex; - width: 1.6rem; - height: 1.6rem; - transform: scale(-1); - transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); -} - -.collapsedWrapperView { - > div { - border-top: 1px solid var(--general-gray-light); - border-radius: 0; - transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); - } - - > button { - background-color: var(--general-gray-light); - color: var(--general-gray-dark); - - &:hover { - background-color: var(--general-gray); - color: var(--general-black); - transform: translateY(-1px); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - } - - html[data-theme='dark'] & { - background-color: var(--general-black-light); - color: var(--general-gray); - - &:hover { - background-color: var(--general-gray-dark); - color: var(--general-gray-light); - } - } - } - - html[data-theme='dark'] & { - border: 1px solid var(--general-black-light); - } -} - -html[data-theme='dark'] { - .collapsedWrapperView { - > div { - border-color: var(--general-black-light); - } - } -} diff --git a/src/components/ParserOpenRPC/DetailsBox/MDContent.tsx b/src/components/ParserOpenRPC/DetailsBox/MDContent.tsx deleted file mode 100644 index c455be28027..00000000000 --- a/src/components/ParserOpenRPC/DetailsBox/MDContent.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react' - -const parseLists = (text: string) => { - const lines = text.split('\n') - let result = '' - let inList = false - let inSubList = false - - lines.forEach(line => { - const trimmed = line.trim() - const isListItem = trimmed.startsWith('- ') - const isSubListItem = line.startsWith(' - ') - - if (isListItem && !isSubListItem) { - if (!inList) { - result += '
      \n' - inList = true - } else if (inSubList) { - result += '
    \n' - inSubList = false - } - result += `
  • ${trimmed.slice(2).trim()}
  • \n` - } else if (isSubListItem) { - if (!inSubList) { - result = result.replace(/<\/li>\n$/, '') - result += '
      \n' - inSubList = true - } - result += `
    • ${trimmed.slice(4).trim()}
    • \n` - } else { - if (inSubList) { - result += '
    \n' - inSubList = false - } - if (inList) { - result += '
\n' - inList = false - } - result += `${line}\n` - } - }) - if (inSubList) result += '\n' - if (inList) result += '\n' - return result -} - -const parseMarkdown = (content: string) => { - return parseLists( - content - .replace(/\[(.*?)\]\((.*?)\)/g, (match, text, url) => { - const isExternal = /^(https?:\/\/)/.test(url) - const targetAttr = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '' - return `${text}` - }) - .replace(/`(.*?)`/g, '$1') - .replace(/\*\*(.*?)\*\*/g, '$1') - .replace(/\*(.*?)\*/g, '$1') - ) -} - -interface MDContentProps { - content?: string -} - -export const MDContent = ({ content = '' }: MDContentProps) => ( - -) diff --git a/src/components/ParserOpenRPC/DetailsBox/RenderParams.tsx b/src/components/ParserOpenRPC/DetailsBox/RenderParams.tsx deleted file mode 100644 index 89b5d96594f..00000000000 --- a/src/components/ParserOpenRPC/DetailsBox/RenderParams.tsx +++ /dev/null @@ -1,478 +0,0 @@ -import React from 'react' -import clsx from 'clsx' -import { SchemaProperty } from './SchemaProperty' -import { CollapseBox } from '../CollapseBox/CollapseBox' -import { MDContent } from './MDContent' -import styles from './styles.module.scss' -import { SchemaPropertyType } from '@site/src/components/ParserOpenRPC/interfaces' - -const getRefSchemaFromComponents = (initRef, components) => { - const ref = initRef.replace('#/components/schemas/', '') - return components[ref] -} - -const getArrayTypeDescription = (items, schemas) => { - if (!items) return 'array' - - // Handle reference types - default to just "array" - if (items.$ref || items.schema?.$ref) { - return 'array' - } - - // Handle nested arrays - show as "multidimensional array" - if (items.type === 'array' || items.schema?.type === 'array') { - return 'multidimensional array' - } - - // Handle object types - if (items.type === 'object' || items.schema?.type === 'object') { - return 'array of objects' - } - - // Handle combination types - if (items.oneOf || items.schema?.oneOf) { - return 'array' - } - if (items.allOf || items.schema?.allOf) { - return 'array' - } - if (items.anyOf || items.schema?.anyOf) { - return 'array' - } - - // Handle enum types - if (items.enum || items.schema?.enum) { - return 'array' - } - - // Handle basic types with lookup table - const typeLabels: Record = { - string: 'array of strings', - number: 'array of numbers', - integer: 'array of integers', - boolean: 'array of booleans', - null: 'array of null values', - } - - const type = typeof items === 'string' ? items : items.type || items.schema?.type - return typeLabels[type] || 'array' -} - -const renderSchema = ( - schemaItem, - schemas, - name, - showRequired = true, - isExpandedByDefault = true -) => { - if (!schemaItem) return
Invalid schema
- - const resolveRef = (ref, originalItem) => { - const newSchema = getRefSchemaFromComponents(ref, schemas) - // Preserve the original parameter's description when resolving references - const resolvedSchema = { - ...newSchema, - description: originalItem?.description || newSchema.description || newSchema.title || '', - } - return renderSchema(resolvedSchema, schemas, name, showRequired, isExpandedByDefault) - } - - if (schemaItem?.schema?.$ref) return resolveRef(schemaItem.schema.$ref, schemaItem) - if (schemaItem?.$ref) return resolveRef(schemaItem.$ref, schemaItem) - - const renderCombinations = (item, itemName, type) => ( -
- -
- - {item[type].map((option, index) => ( -
- {renderSchema(option, schemas, option.title, showRequired, isExpandedByDefault)} -
- ))} -
-
-
- ) - - const renderObject = (item, itemName) => { - const requiredFields = Array.isArray(item.required) ? item.required : [] - const elements = [] - - // First render direct properties - if (item.properties) { - Object.entries(item.properties).forEach(([key, value]: [string, SchemaPropertyType]) => { - elements.push( -
- {renderSchema( - { - ...value, - required: requiredFields.includes(key), - }, - schemas, - key, - showRequired, - isExpandedByDefault - )} -
- ) - }) - } - - // Then render combination schemas (oneOf/anyOf/allOf) within the same object - if (item.oneOf) { - elements.push( -
-
Transaction Types:
- {renderCombinations(item, 'Transaction Types', 'oneOf')} -
- ) - } - - if (item.anyOf) { - elements.push( -
-
Transaction Types:
- {renderCombinations(item, 'Transaction Types', 'anyOf')} -
- ) - } - - if (item.allOf) { - elements.push( -
- {renderCombinations(item, 'Combined Types', 'allOf')} -
- ) - } - - return ( -
- -
- - <>{elements} - -
-
- ) - } - - if (schemaItem?.schema?.type === 'object' && schemaItem?.schema?.properties) { - return renderObject(schemaItem.schema, name || schemaItem?.schema?.title) - } - - if (schemaItem.type === 'object' && schemaItem.properties) { - return renderObject(schemaItem, name || schemaItem.title) - } - - // Helper function to check if oneOf should be flattened for results - const shouldFlattenOneOf = (oneOfArray, isResultSchema) => { - if (!isResultSchema || !Array.isArray(oneOfArray) || oneOfArray.length !== 2) { - return null - } - - // Filter out "empty" responses (notFound, null, etc.) - const meaningfulOptions = oneOfArray.filter(option => { - // Skip notFound references - if (option.$ref?.includes('notFound')) return false - if (option.$ref?.includes('null')) return false - // Skip null types - if (option.type === 'null') return false - // Keep actual data structures - return true - }) - - // If exactly one meaningful option remains, return it for flattening - if (meaningfulOptions.length === 1) { - return meaningfulOptions[0] - } - - return null - } - - const renderArray = (item, itemName) => { - const arrayType = getArrayTypeDescription(item.items, schemas) - - // Simple array types that don't need dropdown details - const simpleArrayTypes = [ - 'array of strings', - 'array of numbers', - 'array of integers', - 'array of booleans', - 'array of null values', - ] - - const shouldShowDetails = !simpleArrayTypes.includes(arrayType) - - // Helper function to render object properties directly (flatter structure) - const renderObjectProperties = objectSchema => { - if (!objectSchema) return null - - const elements = [] - - // Render direct properties first - if (objectSchema.properties) { - const requiredFields = Array.isArray(objectSchema.required) ? objectSchema.required : [] - Object.entries(objectSchema.properties).forEach( - ([key, value]: [string, SchemaPropertyType]) => { - elements.push( -
- {renderSchema( - { - ...value, - required: requiredFields.includes(key), - }, - schemas, - key, - showRequired, - isExpandedByDefault - )} -
- ) - } - ) - } - - // Handle combination schemas (oneOf/anyOf/allOf) within the same object - // This is important for complex structures like Linea transaction objects - if (objectSchema.oneOf) { - elements.push( -
-
Transaction Types:
- {renderCombinations(objectSchema, 'Transaction Types', 'oneOf')} -
- ) - } - - if (objectSchema.anyOf) { - elements.push( -
- {renderCombinations(objectSchema, 'Variations', 'anyOf')} -
- ) - } - - if (objectSchema.allOf) { - elements.push( -
- {renderCombinations(objectSchema, 'Combined Types', 'allOf')} -
- ) - } - - return elements.length > 0 ? <>{elements} : null - } - - return ( -
- - {shouldShowDetails && ( -
- - {(() => { - // Check if array items are objects - if so, render properties directly (flatter structure) - if (arrayType === 'array of objects') { - // Handle referenced objects - if (item.items?.$ref) { - const refSchema = getRefSchemaFromComponents(item.items.$ref, schemas) - const objectProperties = renderObjectProperties(refSchema) - if (objectProperties) return objectProperties - } - // Handle inline objects - else if (item.items?.type === 'object' && item.items.properties) { - const objectProperties = renderObjectProperties(item.items) - if (objectProperties) return objectProperties - } - } - - // For all other array types, use the original rendering approach - return ( -
- {renderSchema(item.items, schemas, '', showRequired, isExpandedByDefault)} -
- ) - })()} -
-
- )} -
- ) - } - - if (schemaItem.type === 'array' && schemaItem.items) { - return renderArray(schemaItem, name || schemaItem.title) - } - - if (schemaItem?.schema?.type === 'array' && schemaItem?.schema?.items) { - return renderArray(schemaItem.schema, name || schemaItem.schema.title) - } - - // Handle oneOf flattening for results (when showRequired is false) - const isResultSchema = !showRequired - - if (schemaItem?.schema?.oneOf) { - const flattenedOption = shouldFlattenOneOf(schemaItem.schema.oneOf, isResultSchema) - if (flattenedOption) { - // Preserve the parent description when flattening - const flattenedWithDescription = { - ...flattenedOption, - description: - schemaItem.description || schemaItem.schema.description || flattenedOption.description, - title: name || schemaItem.name || schemaItem.schema.title || flattenedOption.title, - } - - return renderSchema( - flattenedWithDescription, - schemas, - name || schemaItem.name, - showRequired, - isExpandedByDefault - ) - } - return renderCombinations(schemaItem.schema, name, 'oneOf') - } - - if (schemaItem?.schema?.allOf) return renderCombinations(schemaItem.schema, name, 'allOf') - if (schemaItem?.schema?.anyOf) return renderCombinations(schemaItem.schema, name, 'anyOf') - - if (schemaItem.oneOf) { - const flattenedOption = shouldFlattenOneOf(schemaItem.oneOf, isResultSchema) - if (flattenedOption) { - // Preserve the parent description when flattening - const flattenedWithDescription = { - ...flattenedOption, - description: schemaItem.description || flattenedOption.description, - title: name || schemaItem.name || schemaItem.title || flattenedOption.title, - } - - return renderSchema( - flattenedWithDescription, - schemas, - name || schemaItem.name, - showRequired, - isExpandedByDefault - ) - } - return renderCombinations(schemaItem, name, 'oneOf') - } - - if (schemaItem.allOf) return renderCombinations(schemaItem, name, 'allOf') - if (schemaItem.anyOf) return renderCombinations(schemaItem, name, 'anyOf') - - const formatEnumType = enumValues => { - const values = enumValues.map(value => String(value)).join(' | ') - return `enum: ${values}` - } - - if (schemaItem?.schema) { - return ( -
- -
- ) - } - - return ( -
- -
- ) -} - -export const renderParamSchemas = (inputSchema, schemas) => { - return ( - <> - {inputSchema.map((item, i) => { - return ( -
- {renderSchema(item, schemas, item.name, true, true)} - {i < inputSchema.length - 1 &&
} -
- ) - })} - - ) -} - -export const renderResultSchemas = (inputSchema, schemas) => { - const customResult = inputSchema?.schema?.maxPriorityFeePerGas - if (customResult) { - return <>{renderSchema(customResult, schemas, inputSchema.name, false, false)} - } - return <>{renderSchema(inputSchema, schemas, inputSchema.name, false, false)} -} diff --git a/src/components/ParserOpenRPC/DetailsBox/SchemaProperty.tsx b/src/components/ParserOpenRPC/DetailsBox/SchemaProperty.tsx deleted file mode 100644 index ab327466b8a..00000000000 --- a/src/components/ParserOpenRPC/DetailsBox/SchemaProperty.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import clsx from 'clsx' -import { CSSProperties } from 'react' -import { MDContent } from './MDContent' -import Heading from '@theme/Heading' -import CutOffCorners from '@site/src/components/elements/cut-off-corners' -import styles from './styles.module.scss' - -interface SchemaPropertyProps { - title: string - type?: string - required?: boolean - description?: string - pattern?: string - defaultVal?: string - showRequired?: boolean -} - -interface TagProps { - name: string -} - -export const SchemaProperty = ({ - title, - type, - required, - description, - pattern, - defaultVal, - showRequired = true, -}: SchemaPropertyProps) => { - return ( -
-
-
- - {title} - - {type} - {pattern && ( -
- Pattern: - {pattern} -
- )} -
- {required && showRequired && ( - required - )} -
-

- - {defaultVal && ( -

- Default: - {defaultVal} -
- )} -

-
- ) -} - -export const Tag = ({ name }: TagProps) => { - const bgStyle = { - MetaMask: 'var(--consumer-green)', - Restricted: 'var(--consumer-orange)', - Deprecated: 'var(--consumer-purple)', - } - return ( -
- - - {name} - - -
- ) -} diff --git a/src/components/ParserOpenRPC/DetailsBox/index.tsx b/src/components/ParserOpenRPC/DetailsBox/index.tsx deleted file mode 100644 index 2c1a9b3f0ef..00000000000 --- a/src/components/ParserOpenRPC/DetailsBox/index.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React from 'react' -import Heading from '@theme/Heading' -import { MDContent } from './MDContent' -import { renderParamSchemas, renderResultSchemas } from './RenderParams' -import clsx from 'clsx' -import styles from './styles.module.scss' -import { MethodParam, SchemaComponents } from '@site/src/components/ParserOpenRPC/interfaces' -import { Tag } from '@site/src/components/ParserOpenRPC/DetailsBox/SchemaProperty' - -interface TagItem { - name: string - $ref: string -} - -interface DetailsBoxProps { - method: string - description: string | null - summary: string | null - params: MethodParam[] - components: SchemaComponents - result: any - tags: TagItem[] - extraContent?: JSX.Element | Record -} - -export default function DetailsBox({ - method, - description, - summary, - params, - components, - result, - tags, - extraContent, -}: DetailsBoxProps) { - // Helper function to render extraContent at specific positions - const renderExtraContent = (position: string) => { - if (React.isValidElement(extraContent)) { - // Backward compatibility - old syntax only renders at after-description - return position === 'after-description' ? extraContent : null - } - // New syntax - render at specified position - return extraContent?.[position] || null - } - - return ( - <> - {tags.length > 0 && ( -
- {tags.map(tag => ( -
- -
- ))} -
- )} - {method} - {summary !== null && ( -

- Summary: - - - {/* after-summary content is inline appended */} - {renderExtraContent('after-summary')} - -

- )} - {/* after-summary content when summary is null */} - {summary === null && renderExtraContent('after-summary')} - {description !== null && ( -
- -
- )} - {/* after-description content */} - {renderExtraContent('after-description') && ( -
- {renderExtraContent('after-description')} -
- )} - - Parameters - -
- {params.length === 0 ? ( -
- This method doesn't accept any parameters. -
- ) : ( - params && renderParamSchemas(params, components) - )} -
- {/* after-parameters content */} - {renderExtraContent('after-parameters') && ( -
- {renderExtraContent('after-parameters')} -
- )} - - Returns - -
- {result && renderResultSchemas(result, components)} -
- {/* after-returns content */} - {renderExtraContent('after-returns') && ( -
- {renderExtraContent('after-returns')} -
- )} - - ) -} diff --git a/src/components/ParserOpenRPC/DetailsBox/styles.module.scss b/src/components/ParserOpenRPC/DetailsBox/styles.module.scss deleted file mode 100644 index b483251469b..00000000000 --- a/src/components/ParserOpenRPC/DetailsBox/styles.module.scss +++ /dev/null @@ -1,596 +0,0 @@ -.paramWrapper { - border-bottom: 1px solid var(--general-gray-light); - padding-bottom: 1rem; - margin-bottom: 0.8rem; -} - -.borderWrapper { - border-bottom: 1px solid var(--general-gray-light); - padding-top: 1.5rem; - padding-bottom: 0.8rem; -} - -.schemaWrapper { - padding: 1rem 0; -} - -.schemaHeader { - display: flex; - align-items: baseline; - gap: 0.8rem; - margin-bottom: 0.2rem; - - h5 { - font-size: inherit; - } -} - -.textAltColor { - color: var(--general-gray-dark); - font-style: italic; - font-size: calc(var(--parser-font-size) * 0.85); - - [data-theme='dark'] & { - color: var(--general-gray); - } -} - -.textErrorColor { - font-size: calc(var(--parser-font-size) * 0.85); - color: var(--error); -} - -.description { - color: var(--general-gray-dark); - font-size: inherit; - - [data-theme='dark'] & { - color: var(--general-gray); - } -} - -.paramItemWrapper { - padding: 0 1.2rem; - - .borderTopLine { - border-top: 1px solid transparent; - } -} - -.paramItemWrapper:not(:first-child) { - border-top: 1px solid var(--general-gray-light); -} - -.propItemWrapper { - margin-top: 0.5rem; -} - -.propItemLabel { - font-weight: 500; - font-size: calc(var(--parser-font-size) * 0.85); - color: var(--general-gray-dark); - margin-right: 0.4rem; - - [data-theme='dark'] & { - color: var(--general-gray); - } -} - -.propItemValue { - font-family: var(--font-mm-sans-mono); - font-size: calc(var(--parser-font-size) * 0.85); - color: var(--general-black); - background-color: var(--ifm-code-background); - padding: 0.2rem 0.4rem; - border-radius: 0.3rem; - word-break: break-word; - - [data-theme='dark'] & { - color: var(--general-white); - } -} - -.patternWrapper { - display: inline-block; - background-color: var(--general-white-off); - border: 1px solid var(--general-gray-light); - border-radius: 0.3rem; - padding: 0.1rem 0.4rem; - margin-left: 0.5rem; - font-size: 0.9rem; - line-height: 1.1; - vertical-align: baseline; - - [data-theme='dark'] & { - background-color: var(--general-black-light); - border-color: var(--general-black-light); - } -} - -.patternLabel { - font-weight: 500; - color: var(--general-gray-dark); - margin-right: 0.2rem; - - [data-theme='dark'] & { - color: var(--general-gray); - } -} - -.patternValue { - font-family: var(--font-mm-sans-mono); - color: var(--general-gray-dark); - - [data-theme='dark'] & { - color: var(--general-gray); - } -} - -.paramSeparator { - border: none; - border-top: 1px dotted var(--general-gray-light); - margin: 0.6rem 0; - height: 0; - opacity: 0.6; - - [data-theme='dark'] & { - border-top-color: var(--general-black-light); - opacity: 0.7; - } -} - -.borderTopLine { - border-top: 1px solid var(--general-gray-light); -} - -.borderTopLine:first-child { - border-top: 1px solid transparent; - - html[data-theme='dark'] & { - border-top: 1px solid transparent !important; - } -} - -.borderBottomLine { - border-bottom: 1px solid var(--general-gray-light); -} - -.enumWrapper { - border: 1px solid var(--general-gray-light); - margin-bottom: 2rem; - font-size: inherit; -} - -.enumItem { - border-top: 1px solid var(--general-gray-light); - padding: 1.6rem; -} - -.enumHeader { - padding: 1.6rem; -} - -.enumTitle { - display: inline-block; - font-size: calc(var(--parser-font-size) * 0.85); - line-height: 1; - padding: 0.4rem; - border: 1px solid var(--general-gray-light); - border-radius: 0.4rem; -} - -.heading1 { - font-size: calc(var(--parser-font-size) * 1.93) !important; - line-height: 133.333%; -} - -.heading2 { - font-size: calc(var(--parser-font-size) * 1.64) !important; - line-height: 140%; -} - -.headingSmall { - font-size: calc(var(--parser-font-size) * 1.07) !important; -} - -// Target the actual HTML structure in the parser component -:global(.colContentWrap_src-components-ParserOpenRPC-global-module) { - h1 { - font-size: calc(var(--parser-font-size) * 1.93) !important; - } - - h2 { - font-size: calc(var(--parser-font-size) * 1.64) !important; - } - - h3 { - font-size: calc(var(--parser-font-size) * 1.07) !important; - } - - h4 { - font-size: inherit !important; - } - - h5 { - font-size: inherit !important; - } -} - -// High specificity override for extraContent h2 to override global type-heading-sm -:global(.colContentWrap_src-components-ParserOpenRPC-global-module) { - .extraContent { - h2 { - font-size: calc(var(--parser-font-size) * 1.64) !important; - font-family: var(--ifm-font-family-base) !important; - font-weight: 500 !important; - line-height: 140% !important; - - @include bp('desktop') { - line-height: 125% !important; - } - } - } -} - -// Target utility classes that might be overriding -:global(.type-paragraph-m) { - h1 { - font-size: calc(var(--parser-font-size) * 1.93) !important; - } - - h2 { - font-size: calc(var(--parser-font-size) * 1.64) !important; - } - - h3 { - font-size: calc(var(--parser-font-size) * 1.07) !important; - } - - h4 { - font-size: inherit !important; - } - - h5 { - font-size: inherit !important; - } -} - -// More specific targeting for the parser content area -:global([id='centerContent']) { - h1 { - font-size: calc(var(--parser-font-size) * 1.93) !important; - } - - h2 { - font-size: calc(var(--parser-font-size) * 1.64) !important; - } - - h3 { - font-size: calc(var(--parser-font-size) * 1.07) !important; - } -} - -// Additional specificity for heading elements within the parser component -:global(.parserOpenRPC) { - h1 { - font-size: calc(var(--parser-font-size) * 1.93) !important; - } - - h2 { - font-size: calc(var(--parser-font-size) * 1.64) !important; - } - - h3 { - font-size: calc(var(--parser-font-size) * 1.07) !important; - } -} - -// Target specific heading classes that might be used -:global(.type-heading-l), -:global(.type-heading-m), -:global(.type-heading-s) { - &.heading1 { - font-size: calc(var(--parser-font-size) * 1.93) !important; - } - - &.heading2 { - font-size: calc(var(--parser-font-size) * 1.64) !important; - } - - &.headingSmall { - font-size: calc(var(--parser-font-size) * 1.07) !important; - } -} - -.tagList { - display: flex; - gap: 0.8rem; - margin-bottom: 1.2rem; -} - -.tag { - display: flex; - align-items: center; - - width: auto; - height: 2.6rem; - font-weight: 500; - line-height: 1.1; - padding: 0.7rem 0.9rem; - - line-height: 0.8; - color: var(--general-black); - - background: var(--color-palette); - - @include bp('tablet') { - height: 2.6rem; - line-height: 1.15; - } - - @include bp('desktop') { - height: 2.3rem; - line-height: 0.85; - } -} - -.methodSummary { - font-style: italic; - font-size: inherit; -} - -.methodDescription, -.noParamsDescription { - font-size: inherit; -} - -// Styles for markdown content rendered by MDContent component - scoped to ParserOpenRPC -:global(.colContentWrap_src-components-ParserOpenRPC-global-module .md-content) { - font-size: inherit; - line-height: inherit; - - a { - color: var(--ifm-link-color); - text-decoration: underline; - text-decoration-color: var(--ifm-link-color); - text-decoration-thickness: 0.1rem; - text-underline-offset: 0.4rem; - transition: color var(--ifm-transition-fast) var(--ifm-transition-timing-default); - font-weight: 500; - - &:hover { - color: var(--ifm-link-hover-color); - text-decoration: underline; - text-decoration-color: var(--ifm-link-hover-color); - text-decoration-thickness: 0.1rem; - text-underline-offset: 0.4rem; - } - } - - code { - font-family: var(--font-mm-sans-mono); - font-size: var(--parser-code-font-size); - padding-left: 0.5rem; - padding-right: 0.5rem; - background: var(--ifm-code-background); - border-radius: 0.4rem; - } - - ul, - ol { - font-size: inherit !important; - line-height: inherit; - margin: 0.5rem 0; - } - - li { - font-size: inherit !important; - line-height: inherit; - } - - p { - font-size: inherit; - line-height: inherit; - } -} - -// Fallback selector for contexts where CSS module class names don't apply (e.g., wallet documentation) -:global([id='centerContent'] .md-content) { - font-size: inherit; - line-height: inherit; - - a { - color: var(--ifm-link-color); - text-decoration: underline; - text-decoration-color: var(--ifm-link-color); - text-decoration-thickness: 0.1rem; - text-underline-offset: 0.4rem; - transition: color var(--ifm-transition-fast) var(--ifm-transition-timing-default); - font-weight: 500; - - &:hover { - color: var(--ifm-link-hover-color); - text-decoration: underline; - text-decoration-color: var(--ifm-link-hover-color); - text-decoration-thickness: 0.1rem; - text-underline-offset: 0.4rem; - } - } - - code { - font-family: var(--font-mm-sans-mono); - font-size: var(--parser-code-font-size); - padding-left: 0.5rem; - padding-right: 0.5rem; - background: var(--ifm-code-background); - border-radius: 0.4rem; - } - - ul, - ol { - font-size: inherit !important; - line-height: inherit; - margin: 0.5rem 0; - } - - li { - font-size: inherit !important; - line-height: inherit; - } - - p { - font-size: inherit; - line-height: inherit; - } -} - -// Additional high-specificity selectors to ensure lists match paragraph font size -:global(.type-paragraph-m ul), -:global(.type-paragraph-m ol), -:global(.type-paragraph-m li) { - font-size: inherit !important; - line-height: inherit; -} - -// Even higher specificity for cases where lists break out of paragraphs -:global(.description_Vomw ul), -:global(.description_Vomw ol), -:global(.description_Vomw li) { - font-size: inherit !important; - line-height: inherit; -} - -// Styles for extraContent to match the rest of the component -.extraContent { - font-size: inherit; - line-height: inherit; - color: var(--ifm-color-content); - - // Ensure paragraphs match - p { - font-size: inherit; - line-height: inherit; - margin: 0 0 0.5rem 0; - } - - // Ensure lists match with proper bullet styling - ul { - font-size: inherit !important; - line-height: inherit; - margin: 0.5rem 0 1rem 0; - padding-left: 1.5rem; - list-style-type: disc; - list-style-position: outside; - } - - ol { - font-size: inherit !important; - line-height: inherit; - margin: 0.5rem 0 1rem 0; - padding-left: 1.5rem; - list-style-type: decimal; - list-style-position: outside; - } - - li { - font-size: inherit !important; - line-height: inherit; - margin-bottom: 0.25rem; - padding-left: 0.25rem; - list-style: inherit; - } - - // Nested lists - ul ul, - ol ol, - ul ol, - ol ul { - margin-top: 0.25rem; - margin-bottom: 0.25rem; - } - - ul ul { - list-style-type: circle; - } - - ul ul ul { - list-style-type: square; - } - - // Ensure headings are proportional - h1, - h2, - h3, - h4, - h5, - h6 { - font-size: calc(var(--parser-font-size) * 1.14); - font-weight: 600; - margin-bottom: 0.5rem; - } - - // Ensure inline code blocks match, but don't interfere with CodeBlock components - code:not(.codeBlockLines_e6Vv):not([class*='codeBlock']) { - font-family: var(--font-mm-sans-mono); - font-size: var(--parser-code-font-size); - padding-left: 0.5rem; - padding-right: 0.5rem; - background: var(--ifm-code-background); - border-radius: 0.4rem; - } - - // Ensure CodeBlock components maintain their original styling - div[class*='codeBlockContainer'] { - margin: 1rem 0; - } - - pre[class*='codeBlock'] code { - background: none !important; - padding: 0 !important; - border-radius: 0 !important; - font-size: inherit !important; - } - - // Ensure links match - a { - color: var(--ifm-link-color); - text-decoration: underline; - text-decoration-color: var(--ifm-link-color); - text-decoration-thickness: 0.1rem; - text-underline-offset: 0.4rem; - transition: color var(--ifm-transition-fast) var(--ifm-transition-timing-default); - font-weight: 500; - - &:hover { - color: var(--ifm-link-hover-color); - text-decoration: underline; - text-decoration-color: var(--ifm-link-hover-color); - text-decoration-thickness: 0.1rem; - text-underline-offset: 0.4rem; - } - } - - // Ensure Docusaurus admonitions match the component font size - :global(.alert) { - font-size: inherit; - line-height: inherit; - - p { - font-size: inherit; - line-height: inherit; - } - } -} - -html[data-theme='dark'] { - .paramWrapper, - .borderWrapper, - .enumWrapper, - .enumItem, - .enumTitle, - .borderTopLine, - .borderBottomLine, - .paramItemWrapper:not(:first-child) { - border-color: var(--general-black-light); - } -} diff --git a/src/components/ParserOpenRPC/ErrorsBox/index.tsx b/src/components/ParserOpenRPC/ErrorsBox/index.tsx deleted file mode 100644 index a393fe0897f..00000000000 --- a/src/components/ParserOpenRPC/ErrorsBox/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react' -import { useColorMode } from '@docusaurus/theme-common' -import clsx from 'clsx' -import Heading from '@theme/Heading' -import { MDContent } from '@site/src/components/ParserOpenRPC/DetailsBox/MDContent' - -import styles from './styles.module.scss' - -interface ErrorItem { - code: number - message: string -} - -interface ErrorsBoxProps { - errors: ErrorItem[] -} - -export default function ErrorsBox({ errors }: ErrorsBoxProps) { - const { colorMode } = useColorMode() - if (errors.length === 0) return null - - return ( -
- - Errors - -
-
-
Code
-
Message
-
- {errors.map((err, i) => ( -
-
{err.code}
-
- -
-
- ))} -
-
- ) -} diff --git a/src/components/ParserOpenRPC/ErrorsBox/styles.module.scss b/src/components/ParserOpenRPC/ErrorsBox/styles.module.scss deleted file mode 100644 index 2986203a090..00000000000 --- a/src/components/ParserOpenRPC/ErrorsBox/styles.module.scss +++ /dev/null @@ -1,93 +0,0 @@ -.errWrapper { - border: 1px solid var(--general-gray-light); - overflow: hidden; -} - -.errRow { - display: flex; - border-bottom: 1px solid var(--general-gray-light); -} - -.errRowHeading { - display: flex; - border-bottom: 1px solid var(--general-gray-light); -} - -.errRowHeadingDarkView { - background-color: var(--general-black-mid); -} - -.errRowHeadingLightView { - background-color: var(--general-white-off); -} - -.heading2 { - font-size: calc(var(--parser-font-size) * 1.43); - line-height: 140%; -} - -.borderBottomLine { - border-bottom: 1px solid var(--general-gray-light); - - html[data-theme='dark'] & { - border-bottom: 1px solid var(--general-black-light); - } -} - -.errRow:last-child { - display: flex; - border-bottom: 0; -} - -.errColCode { - display: inline-flex; - align-items: center; - justify-content: center; - width: 8rem; - min-height: 4.8rem; - border-right: 1px solid var(--general-gray-light); - font-size: inherit; - line-height: inherit; -} - -.errColMsg { - display: inline-flex; - align-items: center; - width: calc(100% - 8rem); - padding: 1.2rem; - min-height: 4.8rem; - font-size: inherit; - line-height: inherit; -} - -// Consistent font styling for error message content -.errColMsg :global(.md-content) { - font-size: inherit; - line-height: inherit; - - * { - font-size: inherit; - line-height: inherit; - } - - p { - margin: 0; - } - - code { - font-size: var(--parser-code-font-size); - font-family: var(--ifm-font-family-monospace); - background-color: var(--ifm-code-background); - padding: 0.2rem 0.4rem; - border-radius: 0.2rem; - } -} - -html[data-theme='dark'] { - .errWrapper, - .errRow, - .errRowHeading, - .errColCode { - border-color: var(--general-black-light); - } -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/AddButton.tsx b/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/AddButton.tsx deleted file mode 100644 index aa48c2aa178..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/AddButton.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import clsx from 'clsx' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' - -export const AddButton = props => ( - -) diff --git a/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/RemoveButton.tsx b/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/RemoveButton.tsx deleted file mode 100644 index 11622ebf1c6..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/RemoveButton.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import clsx from 'clsx' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' - -export const RemoveButton = props => ( - -) diff --git a/src/components/ParserOpenRPC/InteractiveBox/fields/ConditionalField.tsx b/src/components/ParserOpenRPC/InteractiveBox/fields/ConditionalField.tsx deleted file mode 100644 index 91ba583dd96..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/fields/ConditionalField.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react' -import { FieldTemplateProps } from '@rjsf/utils' -import { BaseInputTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate' -import { SelectWidget } from '@site/src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' -import { ParserOpenRPCContext } from '@site/src/components/ParserOpenRPC' -import clsx from 'clsx' - -export const ConditionalField = (props: FieldTemplateProps) => { - const [isOpened, setIsOpened] = useState(false) - const [selectedTypeSchema, setSelectedTypeSchema] = useState(null) - const [isEditView, setIsEditView] = useState(false) - const { setIsDrawerContentFixed, setDrawerLabel, isComplexTypeView, setIsComplexTypeView } = - useContext(ParserOpenRPCContext) - // @ts-ignore - const { formData, schema, name, onChange } = props - const listItems = schema?.anyOf ? schema?.anyOf : schema?.oneOf - const checkForNullTypeSchema = type => type === 'null' - const showComplexTypeView = () => { - setDrawerLabel(name) - setIsDrawerContentFixed(true) - setIsEditView(true) - setIsComplexTypeView(true) - } - const onDropdownOptionClick = e => { - const selectedSchema = listItems.find( - // @ts-ignore - ({ title }) => title === e.target.dataset.value - ) - // @ts-ignore - const isNullTypeSchema = checkForNullTypeSchema(selectedSchema?.type) - if (isNullTypeSchema) { - onChange(null) - } else { - setSelectedTypeSchema( - // @ts-ignore - listItems.find(({ title }) => title === e.target.dataset.value) - ) - showComplexTypeView() - } - setIsOpened(false) - } - const selectWidgetProps = { - ...props, - schema: selectedTypeSchema, - label: name, - value: formData, - ...(selectedTypeSchema?.enum && { - options: { - enumOptions: selectedTypeSchema?.enum.map(item => ({ - label: item, - value: item, - })), - }, - }), - } - const baseInputProps = { - ...props, - schema: selectedTypeSchema, - } - - useEffect(() => { - if (!isComplexTypeView) { - setIsEditView(false) - setSelectedTypeSchema(null) - } - }, [isComplexTypeView]) - - return listItems?.length > 0 ? ( - <> -
-
- -
-
-
-
- {formData === undefined ? '' : String(formData)} -
- { - setIsOpened(!isOpened) - }}> - - {schema?.anyOf ? 'anyOf' : 'oneOf'} - - - -
    - {listItems?.map((listItem, index) => ( -
  • - {/* @ts-ignore */} - {listItem.title} -
  • - ))} -
-
-
-
-
- {isComplexTypeView && - isEditView && - selectedTypeSchema && - selectedTypeSchema.type !== 'null' ? ( -
- {selectedTypeSchema?.enum ? ( - // @ts-ignore - - ) : ( - // @ts-ignore - - )} -
- ) : null} - - ) : null -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/index.tsx b/src/components/ParserOpenRPC/InteractiveBox/index.tsx deleted file mode 100644 index 84e07e4de78..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/index.tsx +++ /dev/null @@ -1,376 +0,0 @@ -import React, { useContext, useEffect, useRef, useState } from 'react' -import Form from '@rjsf/core' -import clsx from 'clsx' -import { RJSFSchema, UiSchema, RegistryWidgetsType, RegistryFieldsType } from '@rjsf/utils' -import validator from '@rjsf/validator-ajv8' -import $RefParser from '@apidevtools/json-schema-ref-parser' -import { - MethodExample, - MethodParam, - SchemaComponents, -} from '@site/src/components/ParserOpenRPC/interfaces' -import styles from './styles.module.scss' -import global from '../global.module.scss' -import { BaseInputTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate' -import { ArrayFieldTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate' -import { ObjectFieldTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/ObjectFieldTemplate' -import { WrapIfAdditionalTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/WrapIfAdditionalTemplate' -import { ConditionalField } from '@site/src/components/ParserOpenRPC/InteractiveBox/fields/ConditionalField' -import { DropdownWidget } from '@site/src/components/ParserOpenRPC/InteractiveBox/widgets/DropdownWidget' -import { SelectWidget } from '@site/src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget' -import { Tooltip } from '@site/src/components/Tooltip' -import { useColorMode } from '@docusaurus/theme-common' -import { ParserOpenRPCContext } from '@site/src/components/ParserOpenRPC' -import { MetamaskProviderContext } from '@site/src/theme/Root' -import * as isPlainObject from 'lodash.isplainobject' -import * as camelCase from 'lodash.camelcase' -import { RemoveButton } from '@site/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/RemoveButton' -import { AddButton } from '@site/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/AddButton' -import ClearIcon from '@site/static/img/icons/clear-icon.svg' -import ResetIcon from '@site/static/img/icons/reset-icon.svg' -import SubmitIcon from '@site/static/img/icons/submit-icon.svg' - -interface InteractiveBoxProps { - params: MethodParam[] - components: SchemaComponents - examples: MethodExample[] - onParamChange: (data) => void - drawerLabel?: string | null - closeComplexTypeView?: () => void - isOpen?: boolean - onModalClose?: () => void -} - -type ObjectType = { [key: string]: any } -type KeyOrderType = { name: string } - -function sortObjectKeysByArray(obj: ObjectType, orderArray: KeyOrderType[]): ObjectType { - const result: ObjectType = {} - for (const { name } of orderArray) { - if (name in obj) { - result[name] = obj[name] - } - } - return result -} - -function removeEmptyStrings(obj) { - for (const key in obj) { - if (obj[key] === '') { - delete obj[key] - } - } - return obj -} - -function removeEmptyArrays(obj: any, params: any[]) { - const newObj = JSON.parse(JSON.stringify(obj)) - for (const key in newObj) { - const currentParam = params.find(item => item.name === key) - if (newObj.hasOwnProperty(key)) { - if (!Array.isArray(newObj[key]) && typeof newObj[key] === 'object') { - newObj[key] = removeEmptyStrings(newObj[key]) - } - if (currentParam && currentParam.required) { - return newObj - } - if (Array.isArray(newObj[key]) && newObj[key].length === 0) { - delete newObj[key] - } else if (newObj[key] !== null && typeof newObj[key] === 'object') { - newObj[key] = removeEmptyArrays(newObj[key], params) - } - } - } - return newObj -} - -export default function InteractiveBox({ - params, - components, - examples, - onParamChange, - drawerLabel, - closeComplexTypeView, - isOpen = false, - onModalClose, -}: InteractiveBoxProps) { - const [parsedSchema, setParsedSchema] = useState(null) - const [defaultFormData, setDefaultFormData] = useState({}) - const [currentFormData, setCurrentFormData] = useState({}) - const [isFormReseted, setIsFormReseted] = useState(false) - const [currentSchemaId, setCurrentSchemaId] = useState('') - const [objectPropertyBeforeEdit, setObjectPropertyBeforeEdit] = useState(null) - const [objectValueBeforeEdit, setObjectValueBeforeEdit] = useState(null) - const formRef = useRef(null) - const { colorMode } = useColorMode() - const { isComplexTypeView } = useContext(ParserOpenRPCContext) - const { metaMaskAccount } = useContext(MetamaskProviderContext) - const addWalletId = propName => ({ [propName]: metaMaskAccount }) - const getObjectWithAddress = value => { - const addressField = 'address' - const fromField = 'from' - if (Object.keys(value).includes(addressField)) { - return { - ...value, - ...addWalletId(addressField), - } - } - if (Object.keys(value).includes(fromField)) { - return { - ...value, - ...addWalletId(fromField), - } - } - return value - } - - const checkName = (name: string) => { - if (name === 'requestPermissionObject') return 'requestPermissionsObject' - return name.trim().split(/\s+/).length > 1 ? camelCase(name) : name - } - - useEffect(() => { - if (examples && examples.length > 0 && examples[0].params) { - const defaultValues = Object.fromEntries( - examples[0].params.map(({ name, value }) => { - if (metaMaskAccount) { - if (name === 'Address' || name === 'From') { - return [checkName(name), metaMaskAccount] - } - if (isPlainObject(value)) { - return [checkName(name), getObjectWithAddress(value)] - } - } - if (isPlainObject(value)) { - return [ - checkName(name), - Object.fromEntries( - Object.entries(value).map(([key, val]) => [ - key, - isPlainObject(val) && val?.description ? val.value : val, - ]) - ), - ] - } - return [checkName(name), value] - }) - ) - setDefaultFormData({ ...defaultValues }) - setCurrentFormData({ ...defaultValues }) - onParamChange({ ...defaultValues }) - } - }, [examples, metaMaskAccount]) - - const schema: RJSFSchema = { - components: { - schemas: components, - }, - type: 'object', - // @ts-ignore - properties: Object.fromEntries(params.map(({ name, schema }) => [name, schema])), - } - const uiSchema: UiSchema = { - 'ui:globalOptions': { - label: false, - }, - 'ui:widget': 'checkbox', - } - const templates = { - BaseInputTemplate, - ArrayFieldTemplate, - WrapIfAdditionalTemplate, - ObjectFieldTemplate, - FieldErrorTemplate: () => null, - ErrorListTemplate: () => null, - ButtonTemplates: { AddButton, RemoveButton }, - } - const widgets: RegistryWidgetsType = { - CheckboxWidget: DropdownWidget, - SelectWidget: SelectWidget, - } - const fields: RegistryFieldsType = { - // @ts-ignore - AnyOfField: ConditionalField, - // @ts-ignore - OneOfField: ConditionalField, - } - const handleResetForm = e => { - e.preventDefault() - setCurrentFormData({ ...defaultFormData }) - onParamChange({ ...defaultFormData }) - setIsFormReseted(true) - } - const handleClearForm = e => { - e.preventDefault() - if (formRef) { - formRef?.current?.reset() - } - } - const handleSubmitAndClose = (e: React.MouseEvent) => { - e.preventDefault() - onParamChange(currentFormData) - if (isComplexTypeView) { - closeComplexTypeView() - } else { - onModalClose?.() - } - } - const isLightTheme = colorMode === 'light' - - useEffect(() => { - const dereferenceSchema = async () => { - try { - if (schema) { - setParsedSchema((await $RefParser.dereference(schema)) as RJSFSchema) - } - } catch (error) { - console.error('Error of parsing schema:', error) - } - } - dereferenceSchema() - }, []) - - const onChangeHandler = data => { - const validData = removeEmptyArrays(data, params) - if (isOpen) { - setCurrentFormData(validData) - onParamChange(validData) - } - } - - const cloneAndSetNullIfExists = (obj, key) => { - if (typeof obj !== 'object' || obj === null) return obj - const newObj = Array.isArray(obj) ? [] : {} - for (const prop in obj) { - if (obj.hasOwnProperty(prop)) { - if (prop === key) { - newObj[prop] = [] - } else if (typeof obj[prop] === 'object' && obj[prop] !== null) { - newObj[prop] = cloneAndSetNullIfExists(obj[prop], key) - } else { - newObj[prop] = obj[prop] - } - } - } - return newObj - } - - const cloneObjectAndSetNullIfExists = (obj, key) => { - if (typeof obj !== 'object' || obj === null) { - return obj - } - let newObj = {} - if (Object.keys(obj).length >= 1) { - newObj = objectValueBeforeEdit - } else { - for (const prop in obj) { - if (obj.hasOwnProperty(prop)) { - if (Object.keys(obj).length === 0) { - newObj[prop] = {} - } else if (typeof obj[prop] === 'object' && obj[prop] !== null) { - newObj[objectPropertyBeforeEdit] = cloneObjectAndSetNullIfExists(obj[prop], key) - } else { - newObj[prop] = obj[prop] - } - } - } - } - return newObj - } - - const handleCancelClick = () => { - if (drawerLabel) { - const currentKey = Object.keys(currentFormData)[0] - if (objectPropertyBeforeEdit && currentKey) { - const upData = cloneObjectAndSetNullIfExists( - currentFormData[currentKey], - objectPropertyBeforeEdit - ) - setCurrentFormData({ [currentKey]: upData }) - setObjectPropertyBeforeEdit(null) - setObjectValueBeforeEdit(null) - } else { - const upData = cloneAndSetNullIfExists(currentFormData, drawerLabel) - setCurrentFormData(upData) - } - } - closeComplexTypeView() - } - - return parsedSchema ? ( - <> -
-
Parameter
-
Value
-
-
{ - const orderData = sortObjectKeysByArray(data.formData, params) - onChangeHandler(orderData) - }} - templates={templates} - uiSchema={uiSchema} - widgets={widgets} - ref={formRef} - fields={fields}> -
-
- - - - - - - - - -
- {isComplexTypeView ? ( -
- - -
- ) : null} -
-
- - ) : null -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss b/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss deleted file mode 100644 index aa4a562dc70..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss +++ /dev/null @@ -1,428 +0,0 @@ -.tableHeadingRow { - display: flex; - border-top: 1px solid var(--general-gray-light); - width: 100%; - - @include bp-max('tablet') { - width: 100vw; - } - - html[data-theme='dark'] & { - border-color: var(--general-black-light); - } -} -.tableHeadingColumn { - width: 50%; - padding: 1.1rem; - line-height: 1.5; - background-color: var(--general-white-off); - font-size: 1.5rem; - - &:first-child { - border-right: 1px solid var(--general-gray-light); - } - - html[data-theme='dark'] & { - background-color: var(--general-black); - - &:first-child { - border-color: var(--general-black-light); - } - } -} -.tableRow { - display: flex; - width: calc(100%); - border-bottom: 1px solid var(--general-gray-light); - margin-bottom: -1px; - html[data-theme='dark'] & { - border-color: var(--general-black-light); - } - - @include bp-max('tablet') { - width: 100vw; - } -} -.tableColumn { - width: 50%; - line-height: 2.4rem; -} -.tableColumnParam { - display: flex; - align-items: center; - height: 100%; - padding: 1.1rem; - line-height: 2.8rem; - position: relative; -} -.tableColumnParamFocused { - &::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 0.4rem; - height: 100%; - background-color: var(--consumer-purple); - - html[data-theme='dark'] & { - background-color: var(--consumer-green); - } - } -} -.tableColumnParamError { - &::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 0.4rem; - height: 100%; - background-color: var(--error); - } -} -.tableColumn:first-child { - border-right: 1px solid var(--general-gray-light); -} -.tableValueRow { - display: flex; - width: 100%; - height: 100%; - justify-content: space-between; - align-items: center; - position: relative; -} -.tableValueRowPadding { - padding: 1.2rem 7.2rem 1.2rem 1.6rem; -} -.tableColumnType { - display: flex; - align-items: center; - position: absolute; - height: 100%; - right: 0; - padding-right: 2.8rem; - padding-bottom: 0.2rem; - color: var(--general-gray-mid); - line-height: 2.4rem; - font-size: inherit; -} -.tableColumnTypeDropdown { - width: 100%; - justify-content: flex-end; -} -.tableColumnTypeDropdown:hover { - cursor: pointer; -} -.tableLabelIconError { - position: absolute; - left: 0.4rem; - width: 1.3rem; - height: 1.1rem; - background: url('/img/icons/error-icon.svg') no-repeat 50% 50%; -} -.tableColumnIcon { - position: absolute; - right: 1.3rem; - width: 1.1rem; - height: 1.1rem; -} -.tableColumnIcon:hover { - cursor: pointer; -} -.tableColumnIconRemove { - background: url('/img/icons/remove-icon.svg') no-repeat 50% 50%; -} -.chevronIcon { - background: url('/img/icons/chevron-icon.svg') no-repeat 50% 50%; - transition: 0.2s transform; -} -.chevronIconDown { - transform: rotate(180deg); -} -.chevronIconRight { - transform: rotate(90deg); -} -.chevronIcon:hover { - cursor: pointer; -} -.deleteIcon { - position: absolute; - transform: translateY(-50%); - right: 1.2rem; - width: 1.4rem; - height: 1.4rem; - margin-top: -1px; - cursor: pointer; - background: url('/img/icons/delete-icon.svg') no-repeat 50% 50%; -} -.deleteIconCentered { - top: 50%; -} -.dropdown { - display: flex; - align-items: center; -} -.dropdown:hover { - cursor: pointer; -} -.dropdownChevronIcon { - transition: 0.2s transform; -} -.dropdownChevronButtonUp { - transform: rotate(180deg); -} -.dropdownList { - position: absolute; - right: 1rem; - visibility: visible; - list-style: none; - padding: 0; - margin: 0; - top: 3.5rem; - overflow: hidden; - border: 1px solid var(--general-gray-light); - z-index: 2; -} -.dropdownListClosed { - visibility: hidden; -} -.dropdownList li + li { - margin-top: 0; -} -.dropdownItem { - width: 100%; - padding: 1rem; - background-color: var(--general-white); - color: var(--general-black); - white-space: nowrap; - - html[data-theme='dark'] & { - color: var(--general-white); - background-color: var(--general-gray-dark); - } -} -.dropdownItem:hover { - background-color: var(--general-white-off); - cursor: pointer; - - html[data-theme='dark'] & { - background-color: var(--general-black-light); - } -} -.dropdownItem:not(:last-child) { - border-bottom: 1px solid var(--general-gray-light); -} -.tableFooterRow { - position: fixed; - display: flex; - justify-content: space-between; - padding: 1.6rem; - width: 100%; - bottom: 0; -} -.tableFooterRowDark { - background-color: var(--general-black-light); -} -.tableFooterRowLight { - background-color: var(--general-gray-light); -} -.tableButton { - display: flex; - background: none; - border: none; -} -.tableButtonAddNewArray { - margin: 0.8rem 0 0.8rem 1.6rem; - padding: 0.8rem 0; - color: var(--general-black-light); - align-items: center; - - html[data-theme='dark'] & { - color: var(--general-white); - - img { - filter: invert(1); - } - } -} -.tableButtonAddArrayItemName { - margin-left: 0.8rem; - font-size: inherit; - font-weight: 500; -} -.tableComplexType { - position: absolute; - bottom: 6.5rem; - width: 100%; - height: 76.5%; - background-color: var(--general-white); - overflow-y: auto; - z-index: 1; - overflow-x: hidden; - - html[data-theme='dark'] & { - background-color: var(--general-gray-dark); - } -} -.tableComplexTypeItem { - position: relative; -} -.formControl { - width: 100%; - padding: 1.2rem 7.2rem 1.2rem 1.6rem; - font-size: inherit; - line-height: 2.4rem; - color: var(--general-black); - border-radius: 0; - border: 0.2rem solid transparent; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - - html[data-theme='dark'] & { - color: var(--general-white); - background-color: var(--general-gray-dark); - } -} -.formControl:focus { - outline: none; -} -.formControl[type='number'] { - border-color: transparent; -} -.formControl[type='number']::-webkit-inner-spin-button, -.formControl[type='number']::-webkit-outer-spin-button { - opacity: 0; -} -.formControlNumberUp { - top: 1.3rem; -} -.formControlNumberDown { - bottom: 1.5rem; -} -.formControlError { - border: 0.2rem solid var(--error); -} -.footerButtons { - display: flex; -} -.footerButtonsLeft { - display: flex; - gap: 1rem; - align-items: center; -} -.footerButtonLeft { - display: flex; - align-items: center; - justify-content: center; - width: 3.2rem; - height: 3.2rem; - border-radius: 0.4rem; - background: var(--general-gray-light); - transition: background-color var(--ifm-transition-fast) ease; - - &:hover { - background: var(--general-gray); - } - - [data-theme='dark'] & { - background: var(--general-gray-dark); - - &:hover { - background: var(--general-gray); - } - } -} -.footerButtonRight { - padding: 0.5rem 1.6rem !important; -} -.footerButtonRightOutline { - margin-right: 1.6rem; -} -.footerButtonIcon { - width: 100%; - border-radius: 0; -} -.arrayParentRow { - position: relative; - width: 100%; - height: 100%; - padding: 1.2rem 7.2rem 1.2rem 1.6rem; -} -.arrayColumnType { - position: absolute; - top: 0; - right: 0; - height: 100%; - padding-right: 2.8rem; - display: flex; - align-items: center; - color: var(--general-gray-light); - font-size: inherit; - cursor: pointer; -} -.arrayFormDataWrap { - display: block; - max-width: 39.5rem; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} -.addItemBtnWrap { - min-height: 5.2rem; - display: flex; - align-items: center; - padding: 1.2rem; - border-bottom: 1px solid var(--general-gray-light); -} -.addItemBtn { - display: flex; - align-items: center; - background: none; - padding: 0; - border: 0; - outline: none; - color: var(--general-gray-mid); - font-size: inherit; - line-height: 1; - cursor: pointer; -} -.addItemIcon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 2rem; - height: 2rem; - border-radius: 50%; - background-color: var(--general-gray-mid); - color: var(--general-black-mid); - font-size: 2rem; - line-height: 1; - margin-right: 1rem; -} -.arrayItemIcon { - position: absolute; - top: 50%; - left: 1.2rem; - transform: translateY(-50%); - font-size: 1.6rem; - line-height: 1; - font-weight: 400; -} -.arrayItemRowWrap { - position: relative; - padding-left: 4rem; - border-bottom: 1px dashed var(--general-gray-light); -} - -html[data-theme='dark'] { - .tableHeadingRow, - .tableRow, - .tableColumn:first-child, - .dropdownList, - .addItemBtnWrap, - .dropdownItem:not(:last-child) { - border-color: var(--general-black-light); - } -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate.tsx b/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate.tsx deleted file mode 100644 index b93158f6aa9..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react' -import { useCollapsible, Collapsible } from '@docusaurus/theme-common' -import { ArrayFieldTemplateProps } from '@rjsf/utils' -import { BaseInputTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' -import clsx from 'clsx' -import { ParserOpenRPCContext } from '@site/src/components/ParserOpenRPC' - -export const ArrayFieldTemplate = ({ - items, - canAdd, - onAddClick, - title, - schema, - formData, -}: ArrayFieldTemplateProps) => { - const [isComplexArrayEditView, setIsComplexArrayEditView] = useState(false) - const { setIsDrawerContentFixed, setDrawerLabel, isComplexTypeView, setIsComplexTypeView } = - useContext(ParserOpenRPCContext) - const { collapsed, toggleCollapsed } = useCollapsible({ initialState: true }) - // @ts-ignore - const itemsType = schema?.items?.type - const isSimpleArray = - itemsType === 'string' || - itemsType === 'boolean' || - itemsType === 'number' || - itemsType === 'integer' - const addComplexArray = () => { - if (formData?.length === 0) { - onAddClick() - } - setDrawerLabel(title) - setIsDrawerContentFixed(true) - setIsComplexArrayEditView(true) - setIsComplexTypeView(true) - } - const addSimpleArray = () => { - toggleCollapsed() - if (collapsed && formData?.length === 0) { - onAddClick() - } - } - - useEffect(() => { - if (!isComplexTypeView) { - setIsComplexArrayEditView(false) - } - }, [isComplexTypeView]) - - return ( -
-
-
- -
-
-
-
{JSON.stringify(formData, null, ' ')}
- - - {schema.type} - - - -
-
-
- {isComplexTypeView && isComplexArrayEditView && !isSimpleArray ? ( -
- {items.map(({ children, index, onDropIndexClick, hasRemove }) => ( -
- {hasRemove ? ( - - ) : null} - {children} -
- ))} - {canAdd ? ( - - ) : null} -
- ) : ( - - <> - {items.map((el, i) => { - const baseInputTemplateProps = { - ...el.children.props, - isArray: true, - value: formData[i] || null, - } - const { index, hasRemove, onDropIndexClick, schema } = el - const isNumber = schema.type === 'number' || schema.type === 'integer' - return ( -
- {i + 1} - - {hasRemove && ( - - )} -
- ) - })} - {canAdd && ( -
- -
- )} - -
- )} -
- ) -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate.tsx b/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate.tsx deleted file mode 100644 index 577ab2949dc..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react' -import { BaseInputTemplateProps } from '@rjsf/utils' -import clsx from 'clsx' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' -import { Tooltip } from '@site/src/components/Tooltip' -import debounce from 'lodash.debounce' -import { ParserOpenRPCContext } from '@site/src/components/ParserOpenRPC' - -interface ExtendedInputProps extends BaseInputTemplateProps { - isArray?: boolean -} - -export const BaseInputTemplate = ({ - schema, - id, - name, - value = '', - disabled, - onChange, - rawErrors, - hideError, - required, - formContext, - isArray, -}: ExtendedInputProps) => { - const isNumber = schema.type === 'number' || schema.type === 'integer' - const [isFocused, setIsFocused] = useState(false) - const [inputValue, setInputValue] = useState(isNumber ? 0 : '') - - const { isFormReseted, currentFormData } = formContext - const { isComplexTypeView } = useContext(ParserOpenRPCContext) - const hasErrors = rawErrors?.length > 0 && !hideError && value !== '' - const debouncedOnChange = useCallback( - debounce((e, isInputNumber = false) => { - onChange(isInputNumber ? e : e?.target?.value) - }, 300), - [] - ) - const onInputChange = e => { - const value = isNumber ? Number(+e?.target?.value || 0) : e?.target?.value - setInputValue(value) - isNumber ? debouncedOnChange(value, true) : debouncedOnChange(e) - } - const onInputNumberChange = value => { - setInputValue(value) - debouncedOnChange(value, true) - } - - useEffect(() => { - if (isComplexTypeView && Object.keys(currentFormData).includes(name)) { - setInputValue(currentFormData[name]) - } else { - setInputValue(value) - } - }, [value, isFormReseted, currentFormData]) - - return ( -
- {!isArray && ( -
- -
- )} -
- -
- {hasErrors && !isNumber ? : null} - { - if (e.key === 'Enter') { - e.preventDefault() - } - }} - onFocus={() => { - setIsFocused(true) - }} - onBlur={() => { - setIsFocused(false) - }} - /> - - {schema.type} - {isNumber ? ( - <> - { - onInputNumberChange(Number((+inputValue || 0) + 1)) - }} - /> - { - // @ts-ignore - inputValue >= 1 && onInputNumberChange(Number((+inputValue || 0) - 1)) - }} - /> - - ) : null} - -
-
-
-
- ) -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/templates/ObjectFieldTemplate.tsx b/src/components/ParserOpenRPC/InteractiveBox/templates/ObjectFieldTemplate.tsx deleted file mode 100644 index c81da5bb183..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/templates/ObjectFieldTemplate.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import React, { useContext, useState } from 'react' -import { canExpand } from '@rjsf/utils' -import { AddButton } from '@site/src/components/ParserOpenRPC/InteractiveBox/buttonTemplates/AddButton' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' -import clsx from 'clsx' -import { ParserOpenRPCContext } from '@site/src/components/ParserOpenRPC' -import { BaseInputTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate' - -export const ObjectFieldTemplate = props => { - const { formData, formContext, idSchema, onAddClick, properties, schema, uiSchema } = props - const [customFormData, setCustomFormData] = useState(formData) - const objectSchemaIds = [ - 'root_requestPermissionsObject', - 'root_revokePermissionObject', - 'eth_signTypedData_v4', - ] - const { isComplexTypeView, drawerLabel, setDrawerLabel, setIsComplexTypeView } = - useContext(ParserOpenRPCContext) - const { - currentSchemaId, - setCurrentSchemaId, - setObjectPropertyBeforeEdit, - setObjectValueBeforeEdit, - currentFormData, - setCurrentFormData, - onParamChange, - } = formContext - const addNewObject = (name, schemaId) => { - setIsComplexTypeView(true) - setDrawerLabel(name) - setCurrentSchemaId(schemaId) - setObjectPropertyBeforeEdit(name) - setObjectValueBeforeEdit(formData) - } - - if ( - isComplexTypeView && - idSchema?.$id === currentSchemaId && - currentSchemaId.includes('root_TypedData_') - ) { - const objectEntries = Object.entries(formData) - return ( - <> - {objectEntries.map(array => { - const name = array[0] - const value = array[1] - if (typeof value === 'string' || typeof value === 'number') { - const baseInputProps = { - ...props, - name, - value, - schema: { - type: typeof value, - }, - } - return ( - { - const newData = { - ...currentFormData, - TypedData: { - ...currentFormData.TypedData, - [`${drawerLabel}`]: { ...formData, [`${name}`]: val }, - }, - } - setCustomFormData({ ...formData, [`${name}`]: val }) - setCurrentFormData(newData) - onParamChange(newData) - }} - /> - ) - } - if (typeof value === 'object') { - return ( -
-
- -
-
-
-
- {formData - ? JSON.stringify( - Object.fromEntries( - Object.entries(formData).filter(([key]) => key.includes(name)) - ), - null, - ' ' - ) - : ''} -
- addNewObject(name, idSchema?.$id)}> - - {schema.type} - - - -
-
-
- ) - } - })} - - ) - } - - return ( - <> - {properties.map(({ content, name }, i) => { - if (content.props.schema.type === 'object') { - return ( -
- {!isComplexTypeView && idSchema?.$id !== 'root' ? ( -
-
- -
-
-
-
- {formData - ? JSON.stringify( - Object.fromEntries( - Object.entries(formData).filter(([key]) => key.includes(name)) - ), - null, - ' ' - ) - : ''} -
- addNewObject(name, content.props.idSchema?.$id)}> - - {schema.type} - - - -
-
-
- ) : null} - {content} -
- ) - } - return (!isComplexTypeView && content?.key !== 'EIP712Domain') || - !currentSchemaId.includes('root_TypedData_') - ? content - : null - })} - {((isComplexTypeView && idSchema?.$id === currentSchemaId) || - (!isComplexTypeView && objectSchemaIds.includes(idSchema?.$id))) && - canExpand(schema, uiSchema, formData) ? ( - - ) : null} - - ) -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/templates/WrapIfAdditionalTemplate.tsx b/src/components/ParserOpenRPC/InteractiveBox/templates/WrapIfAdditionalTemplate.tsx deleted file mode 100644 index 3663e0f8a2b..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/templates/WrapIfAdditionalTemplate.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useContext, useEffect } from 'react' -import { ADDITIONAL_PROPERTY_FLAG } from '@rjsf/utils' -import { BaseInputTemplate } from '@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate' -import { ParserOpenRPCContext } from '@site/src/components/ParserOpenRPC' -import * as isPlainObject from 'lodash.isplainobject' - -export const WrapIfAdditionalTemplate = props => { - const { - id, - classNames, - style, - disabled, - label, - onKeyChange, - onDropPropertyClick, - readonly, - schema, - children, - registry, - formContext, - formData, - } = props - const { templates } = registry - const { RemoveButton } = templates.ButtonTemplates - const additional = ADDITIONAL_PROPERTY_FLAG in schema - const { drawerLabel, isComplexTypeView, setIsComplexTypeView, setDrawerLabel } = - useContext(ParserOpenRPCContext) - const { currentSchemaId, setCurrentSchemaId } = formContext - const onRemoveButtonClick = () => { - if (isPlainObject(formData) && Object.keys(formData).length === 0) { - setIsComplexTypeView(false) - setDrawerLabel(null) - } - } - - if (!additional) { - return ( -
- {children} -
- ) - } - - useEffect(() => { - if (!id.includes('_newKey')) { - setCurrentSchemaId(id) - } - }, []) - - if (!id.includes(currentSchemaId)) { - return null - } - - return isComplexTypeView ? ( -
- {drawerLabel === label && ( -
-
- -
- {/* @ts-ignore */} - { - onKeyChange(target) - setDrawerLabel(target) - }} - schema={{ ...schema, ...{ type: 'string' } }} - formContext={formContext} - value={label} - /> -
- )} -
{children}
-
- ) : null -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/widgets/DropdownWidget.tsx b/src/components/ParserOpenRPC/InteractiveBox/widgets/DropdownWidget.tsx deleted file mode 100644 index 64bb2fcb28c..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/widgets/DropdownWidget.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { useState } from 'react' -import { WidgetProps } from '@rjsf/utils' -import clsx from 'clsx' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' - -export const DropdownWidget = ({ name, value, onChange, schema, options }: WidgetProps) => { - const [isOpened, setIsOpened] = useState(false) - - return ( -
-
- -
-
-
- {value === undefined ? '' : String(value)} - { - setIsOpened(!isOpened) - }}> - - {schema.type} - - - -
    - {options.enumOptions.map(({ value }, index) => ( -
  • { - onChange(value) - setIsOpened(false) - }}> - {String(value)} -
  • - ))} -
-
-
-
-
- ) -} diff --git a/src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget.tsx b/src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget.tsx deleted file mode 100644 index 0898a23a01e..00000000000 --- a/src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useState } from 'react' -import { WidgetProps } from '@rjsf/utils' -import clsx from 'clsx' -import styles from '@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.scss' - -export const SelectWidget = ({ value, onChange, schema, options, label }: WidgetProps) => { - const [isOpened, setIsOpened] = useState(false) - const emptyValue = - value === undefined || !options?.enumOptions.some(({ label }) => label === value) - - return ( -
-
- -
-
-
- {emptyValue ? '' : String(value)} - { - setIsOpened(!isOpened) - }}> - - {schema?.enum ? 'enum' : schema?.type} - - - -
    - {options?.enumOptions?.map(({ label, value }, index) => ( -
  • { - onChange(value) - setIsOpened(false) - }}> - {String(label)} -
  • - ))} -
-
-
-
-
- ) -} diff --git a/src/components/ParserOpenRPC/ModalDrawer/index.tsx b/src/components/ParserOpenRPC/ModalDrawer/index.tsx deleted file mode 100644 index df119c7bd42..00000000000 --- a/src/components/ParserOpenRPC/ModalDrawer/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react' -import clsx from 'clsx' -import { useColorMode } from '@docusaurus/theme-common' -import styles from './styles.module.scss' -import * as debounce from 'lodash.debounce' - -interface ModalDrawerProps { - title: string | React.ReactNode - isOpen: boolean - onClose: () => void - children: React.ReactNode - isContentFixed?: boolean - headerLabel?: string | null -} - -export const ModalDrawer = ({ - title, - isOpen, - onClose, - children, - isContentFixed = false, - headerLabel, -}: ModalDrawerProps) => { - const [showModal, setShowModal] = useState(isOpen) - const [leftOffset, setLeftOffset] = useState(360) - const [width, setWidth] = useState(972) - const { colorMode } = useColorMode() - const contentRef = useRef(null) - - useEffect(() => { - setShowModal(isOpen) - }, [isOpen]) - - useEffect(() => { - if (isContentFixed && contentRef?.current) { - contentRef?.current?.scrollTo(0, 0) - } - }, [isContentFixed]) - - useEffect(() => { - const setDrawerSizeAndPosition = () => { - const pageContent = document.getElementById('centerContent') - if (pageContent) { - const contentLeftOffset = pageContent.getBoundingClientRect().x - const contentComputedStyles = window.getComputedStyle(pageContent) - const contentLeftPadding = parseInt(contentComputedStyles.paddingLeft) - const contentRightPadding = parseInt(contentComputedStyles.paddingRight) - const contentWidth = parseInt(contentComputedStyles.width) - setLeftOffset(contentLeftOffset + Number(contentLeftPadding)) - setWidth(contentWidth - contentLeftPadding - contentRightPadding) - } - } - if (window) { - setDrawerSizeAndPosition() - const handleResize = debounce(setDrawerSizeAndPosition, 10) - window.addEventListener('resize', handleResize) - - return () => { - window.removeEventListener('resize', handleResize) - } - } - }, []) - - return ( -
-
-
- {title} - {headerLabel ? {headerLabel} : null} -
- -
-
- {children} -
-
- ) -} diff --git a/src/components/ParserOpenRPC/ModalDrawer/styles.module.scss b/src/components/ParserOpenRPC/ModalDrawer/styles.module.scss deleted file mode 100644 index 8f23ff9630c..00000000000 --- a/src/components/ParserOpenRPC/ModalDrawer/styles.module.scss +++ /dev/null @@ -1,134 +0,0 @@ -:root { - --param-item-bg: #292a36; - --modal-header-bg: #24272a; - --param-item-border-color: rgb(132 140 150 / 100%); -} - -:root[data-theme='light'] { - --param-item-bg: #f2f4f6; - --modal-header-bg: #fff; - --param-item-border-color: #bbc0c5; -} - -.modalContainer { - border: 1px solid var(--general-gray-light); - position: fixed; - z-index: 10; - top: 100%; - left: 0; - right: 1.5rem; - width: 100%; - min-height: 51.2rem; - transform: translate(0, 100%); - overflow: hidden; - background-color: var(--general-white); - transition: transform 0.4s ease; - - html[data-theme='dark'] & { - background-color: var(--general-gray-dark); - border-color: var(--general-black-light); - } -} - -.modalContainerOpen { - transition: transform 0.4s ease; - transform: translate(0, -100%); -} - -.modalHeader { - padding: 1.6rem 1.2rem; - display: flex; - align-items: center; - justify-content: space-between; - min-height: 5.6rem; - background-color: var(--general-white-off); - - html[data-theme='dark'] & { - background-color: var(--general-black-light); - } -} - -.modalHeaderLight { - background-color: var(--general-gray-light); -} - -.modalTitle { - font-size: 1.8rem; - line-height: 1; - font-weight: 500; - font-family: var(--ifm-font-family-base); -} - -.modalTitleContainer { - display: flex; - align-items: center; -} - -.modalHeaderIcon { - display: flex; - background: none; - border: none; - padding: 0; - margin-right: 0.8rem; -} - -.modalHeaderIconBack { - width: 1.6rem; - height: 1.6rem; -} - -.modalHeaderLabels { - display: flex; - align-items: center; - font-family: var(--ifm-heading-font-family); -} - -.modalHeaderLabel { - display: flex; - justify-content: center; - margin-left: 0.8rem; - padding: 0.2rem 0.8rem; - border: 1px solid var(--general-gray); - border-radius: 999px; - font-size: 1.2rem; - line-height: 1.8rem; -} - -.modalCloseBtn { - display: block; - padding: 0; - background: none; - font-size: 2.4rem; - line-height: 1; - border: 0; - cursor: pointer; -} - -.modalContent { - height: 39.4rem; -} - -.modalContentScrolled { - overflow-y: auto; - transition: none; -} - -.modalContentFixed { - overflow-y: hidden; -} - -@media (width <= 1760px) { - .modalContainer { - max-width: 100%; - margin-left: 0; - } -} - -@media (width <= 1200px) { - .modalContainer { - left: 0; - right: 0; - max-width: 100%; - margin-left: 0; - } -} diff --git a/src/components/ParserOpenRPC/ProjectsBox/index.tsx b/src/components/ParserOpenRPC/ProjectsBox/index.tsx deleted file mode 100644 index b99d823369a..00000000000 --- a/src/components/ParserOpenRPC/ProjectsBox/index.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react' -import ldClient from 'launchdarkly' -import { MetamaskProviderContext } from '@site/src/theme/Root' -import Select from 'react-dropdown-select' -import Button from '@site/src/components/Button' -import styles from './styles.module.scss' -import { WALLET_LINK_TYPE } from '@site/src/components/AuthLogin/AuthModal' - -const LOGIN_FF = 'mm-unified-login' - -const ProjectsBox = () => { - const { - projects, - metaMaskAccount, - walletLinked, - needsMfa, - metaMaskWalletIdConnectHandler, - walletAuthUrl, - setUserAPIKey, - } = useContext(MetamaskProviderContext) - const options = Object.keys(projects).map(v => ({ - value: v, - label: projects[v].name, - })) - const [currentProject, setCurrentProject] = useState([options[0]].filter(Boolean)) - const [ldReady, setLdReady] = useState(false) - const [loginEnabled, setLoginEnabled] = useState(false) - const [isWalletLinking, setIsWalletLinking] = useState(false) - - const metaMaskWalletConnectionHandler = () => { - setIsWalletLinking(true) - metaMaskWalletIdConnectHandler() - } - - useEffect(() => { - ldClient.waitUntilReady().then(() => { - setLoginEnabled(ldClient.variation(LOGIN_FF, false)) - setLdReady(true) - }) - const handleChange = current => { - setLoginEnabled(current) - } - ldClient.on(`change:${LOGIN_FF}`, handleChange) - return () => { - ldClient.off(`change:${LOGIN_FF}`, handleChange) - } - }, []) - - useEffect(() => { - if (!currentProject.length && options[0]) { - setCurrentProject([options[0]]) - setUserAPIKey(options[0].value) - } - }, [projects]) - - useEffect(() => { - if (options?.length > 0) { - setUserAPIKey(options[0].value) - } - }, [options.length]) - - useEffect(() => { - if (walletLinked) { - setIsWalletLinking(false) - } - }, [walletLinked]) - - return ( - ldReady && - loginEnabled && ( -
-
Infura API Key
- {metaMaskAccount && !!Object.keys(projects).length ? ( - { - setCurrentLang(value[0]) - }} - /> - )} - -
- - {exampleRequest} - -
-
0 && styles['cardFooter-btns'])}> - {params.length > 0 && ( -
-
- {exampleResponse && ( -
- - {defExampleResponse && response === undefined ? 'Example response' : 'Response'} - {defExampleResponse && response !== undefined && ( - - - - )} - -
- - {exampleResponse} - -
-
- )} - - ) -} diff --git a/src/components/ParserOpenRPC/RequestBox/styles.module.scss b/src/components/ParserOpenRPC/RequestBox/styles.module.scss deleted file mode 100644 index 4d760dc65be..00000000000 --- a/src/components/ParserOpenRPC/RequestBox/styles.module.scss +++ /dev/null @@ -1,66 +0,0 @@ -.cardWrapper { - margin-bottom: 2.4rem; - overflow: hidden; - width: 100%; - margin: 0 auto 2.4rem; - - @include bp-max(tablet) { - width: calc(100% - var(--page-padding-x) * 2); - } -} - -.cardHeader { - background-color: var(--general-black-light); - border-bottom: 1px solid var(--general-gray-dark); - padding: 1.2rem 2rem; - color: var(--general-white); - display: flex; - align-items: center; - justify-content: space-between; - min-height: 57px; -} - -.addIndent { - padding: 10px 70px 10px 16px; -} - -.codeWrapper pre { - max-height: 328px; - overflow-y: auto; -} - -.codeWrapper button { - opacity: 1 !important; - position: relative; - top: -47px; - right: 5px; -} - -.cardFooter { - display: flex; - justify-content: flex-end; - padding-top: 1.6rem; - padding-bottom: 1.6rem; - - &-btns { - justify-content: center; - gap: 1.6rem; - - @include bp-max(tablet) { - flex-direction: column; - gap: 1.2rem; - } - } -} - -.responseBlock { - max-height: 40rem; - overflow-y: auto; -} - -html[data-theme='dark'] { - .cardWrapper, - .cardHeader { - border-color: var(--general-gray-dark); - } -} diff --git a/src/components/ParserOpenRPC/global.module.scss b/src/components/ParserOpenRPC/global.module.scss deleted file mode 100644 index 5009ed6c624..00000000000 --- a/src/components/ParserOpenRPC/global.module.scss +++ /dev/null @@ -1,67 +0,0 @@ -.root { - --font-family-sans: 'Euclid Circular B', 'Roboto', sans-serif; -} - -.rowWrap { - // Font inheritance system for entire ParserOpenRPC component - --parser-font-size: 1.4rem; - --parser-line-height: 1.5; - --parser-code-font-size: 1.2rem; - - // Set base font styling - all child components inherit from here - font-size: var(--parser-font-size); - line-height: var(--parser-line-height); - - display: flex; - flex-wrap: wrap; - padding: 1.8rem 2.6rem 0 0; -} - -.stickyCol { - position: sticky; - top: 8.4rem; - left: 0; -} - -.colLeft { - position: relative; - width: calc(100% - 41rem); - border-right: 1px solid var(--general-gray-light); - overflow: hidden; -} - -.colContentWrap { - height: calc(100vh - 15rem); - padding: 0 2.5rem; - overflow-y: auto; -} - -.colRight { - width: 41rem; - padding-left: 4rem; -} - -@media (width <= 1200px) { - .colLeft { - width: 100%; - padding: 0; - border-right: 0; - } - .colRight { - width: 100%; - padding: 0; - } - .colContentWrap { - height: auto; - padding: var(--page-padding-x); - } - .rowWrap { - padding: 0; - } -} - -html[data-theme='dark'] { - .colLeft { - border-color: var(--general-black-light); - } -} diff --git a/src/components/ParserOpenRPC/index.tsx b/src/components/ParserOpenRPC/index.tsx deleted file mode 100644 index bdb13872034..00000000000 --- a/src/components/ParserOpenRPC/index.tsx +++ /dev/null @@ -1,384 +0,0 @@ -import React, { createContext, useContext, useMemo, useState, useEffect } from 'react' -import { usePluginData } from '@docusaurus/useGlobalData' -import { ResponseItem, NETWORK_NAMES } from '@site/src/plugins/plugin-json-rpc' -import DetailsBox from '@site/src/components/ParserOpenRPC/DetailsBox' -import InteractiveBox from '@site/src/components/ParserOpenRPC/InteractiveBox' -import RequestBox from '@site/src/components/ParserOpenRPC/RequestBox' -import ErrorsBox from '@site/src/components/ParserOpenRPC/ErrorsBox' -import { ModalDrawer } from '@site/src/components/ParserOpenRPC/ModalDrawer' -import global from './global.module.scss' -import modalDrawerStyles from './ModalDrawer/styles.module.scss' -import detailsBoxStyles from './DetailsBox/styles.module.scss' -import clsx from 'clsx' -import { useColorMode } from '@docusaurus/theme-common' -import { trackClickForSegment, trackInputChangeForSegment } from '@site/src/lib/segmentAnalytics' -import { AuthBox } from '@site/src/components/ParserOpenRPC/AuthBox' -import { MetamaskProviderContext } from '@site/src/theme/Root' -import ProjectsBox from '@site/src/components/ParserOpenRPC/ProjectsBox' -import { LINEA_REQUEST_URL } from '@site/src/lib/constants' -import useIsBrowser from '@docusaurus/useIsBrowser' -import { encrypt } from '@metamask/eth-sig-util' -import { hexlify } from 'ethers' - -// Import local specs -import localLineaSpec from '@site/src/specs/linea/openrpc.json' - -interface ParserProps { - network: NETWORK_NAMES - method?: string - extraContent?: JSX.Element | Record - src?: 'local' | 'remote' -} - -interface ParserOpenRPCContextProps { - drawerLabel?: string - setIsDrawerContentFixed?: (isFixed: boolean) => void - setDrawerLabel?: (label: string) => void - isComplexTypeView: boolean - setIsComplexTypeView: (isComplexTypeView: boolean) => void -} - -export const ParserOpenRPCContext = createContext(null) - -export default function ParserOpenRPC({ - network, - method, - extraContent, - src = 'remote', -}: ParserProps) { - const isBrowser = useIsBrowser() - if (!isBrowser) { - return null - } - if (!method || !network) return null - const [isModalOpen, setModalOpen] = useState(false) - const [reqResult, setReqResult] = useState(undefined) - const [paramsData, setParamsData] = useState([]) - const [isDrawerContentFixed, setIsDrawerContentFixed] = useState(false) - const [drawerLabel, setDrawerLabel] = useState(null) - const [isComplexTypeView, setIsComplexTypeView] = useState(false) - const { metaMaskAccount, metaMaskProvider, userAPIKey, userEncPublicKey, setUserEncPublicKey } = - useContext(MetamaskProviderContext) - const [defExampleResponse, setDefExampleResponse] = useState(undefined) - const [isLoading, setIsLoading] = useState(false) - const { colorMode } = useColorMode() - - // Helper function to render extraContent at specific positions - const renderExtraContent = (position: string) => { - if (React.isValidElement(extraContent)) { - // Backward compatibility - old syntax doesn't support after-errors - return null - } - // New syntax - render at specified position - return extraContent?.[position] || null - } - - const trackAnalyticsForRequest = response => { - trackClickForSegment({ - eventName: 'Request Sent', - clickType: 'Request Sent', - userExperience: 'B', - // @ts-ignore - ...(response?.code && { responseStatus: response.code }), - responseMsg: null, - timestamp: Date.now(), - }) - } - const openModal = () => { - setModalOpen(true) - trackClickForSegment({ - eventName: 'Customize Request', - clickType: 'Customize Request', - userExperience: 'B', - responseStatus: null, - responseMsg: null, - timestamp: Date.now(), - }) - } - const closeModal = () => setModalOpen(false) - - const { netData } = usePluginData('plugin-json-rpc') as { - netData?: ResponseItem[] - } - - // Use local data when src="local", otherwise use plugin data - const currentNetwork = - src === 'local' - ? { - name: network, - data: network === NETWORK_NAMES.linea ? localLineaSpec : null, - error: network !== NETWORK_NAMES.linea, // Error if local spec not available for this network - } - : netData?.find(net => net.name === network) - - if (!currentNetwork || currentNetwork.error) return null - - const currentMethodData = useMemo(() => { - const findReferencedItem = (items, refPath, componentType) => { - return ( - items - ?.map(item => { - if (item?.name || (item?.code && item?.message)) return item - if (item?.$ref) { - const ref = item.$ref.replace(refPath, '') - return currentNetwork.data.components[componentType][ref] - } - return null - }) - .filter(Boolean) || [] - ) - } - - const currentMethod = currentNetwork.data.methods?.find(met => met.name === method) - if (!currentMethod) return null - - const errors = findReferencedItem(currentMethod.errors, '#/components/errors/', 'errors') - const tags = findReferencedItem(currentMethod.tags, '#/components/tags/', 'tags') - - if (!!userEncPublicKey && method === 'eth_decrypt') { - const encDefValue = hexlify( - Buffer.from( - JSON.stringify( - encrypt({ - publicKey: userEncPublicKey, - data: 'Test message', - version: 'x25519-xsalsa20-poly1305', - }) - ) - ) - ) - currentMethod?.examples[0]?.params?.forEach(item => { - if (item.name === 'EncryptedMessage' && !!encDefValue) { - item.value = encDefValue - } - }) - } - - return { - description: currentMethod.description || null, - summary: currentMethod.summary || null, - params: currentMethod.params || [], - result: currentMethod.result || null, - components: currentNetwork.data.components || null, - examples: currentMethod?.examples, - paramStructure: currentMethod?.paramStructure || null, - errors, - tags, - servers: currentNetwork.data?.servers?.[0]?.url || null, - } - }, [currentNetwork, method, src]) - - if (currentMethodData === null) return null - - const isMetamaskNetwork = network === NETWORK_NAMES.metamask - - useEffect(() => { - const example = currentMethodData?.examples?.[0] - if (example?.result) { - if (example.id && example.jsonrpc) { - setDefExampleResponse({ - id: example.id, - jsonrpc: example.jsonrpc, - result: example.result.value, - }) - } else { - setDefExampleResponse(example.result.value) - } - } else { - setDefExampleResponse(undefined) - } - }, [currentMethodData]) - - const resetResponseHandle = () => { - setReqResult(undefined) - } - - const onParamsChangeHandle = data => { - trackInputChangeForSegment({ - eventName: 'Request Configuration Started', - userExperience: 'B', - timestamp: Date.now(), - }) - - if (typeof data !== 'object' || data === null || Object.keys(data).length === 0) { - setParamsData([]) - return - } - - if (typeof data === 'object' && currentMethodData.paramStructure === 'by-name') { - setParamsData({ ...data }) - return - } - - setParamsData(Object.values(data)) - } - - const handleMetaMaskRequest = async () => { - if (!metaMaskProvider) return - setIsLoading(true) - try { - const response = await metaMaskProvider.request({ - method, - params: paramsData, - }) - setReqResult(response) - trackAnalyticsForRequest(response) - if (method === 'eth_getEncryptionPublicKey' && response) { - setUserEncPublicKey(response as string) - } - } catch (e) { - setReqResult(e) - } finally { - setIsLoading(false) - } - } - - const INIT_URL = - currentMethodData.servers !== null ? currentMethodData.servers : `${LINEA_REQUEST_URL}/v3/` - - const handleServiceRequest = async () => { - const URL = `${INIT_URL}${userAPIKey}` - const params = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method, - params: paramsData, - id: 1, - }), - } - setIsLoading(true) - try { - const res = await fetch(URL, params) - if (res.ok) { - const response = await res.json() - setReqResult(response) - trackAnalyticsForRequest(response) - } else { - const errorText = await res.text() - const errorState = JSON.parse(errorText) - setReqResult(`Request failed. Status: ${res.status}. ${errorState}`) - } - } catch (e) { - setReqResult(`${e}`) - } finally { - setIsLoading(false) - } - } - - const onSubmitRequestHandle = async () => { - if (isMetamaskNetwork) { - await handleMetaMaskRequest() - } else { - await handleServiceRequest() - } - } - - const closeComplexTypeView = () => { - setIsComplexTypeView(false) - setIsDrawerContentFixed(false) - setDrawerLabel(null) - } - - const onModalClose = () => { - closeModal() - closeComplexTypeView() - } - - return ( - -
-
-
- - - {renderExtraContent('after-errors') && ( -
- {renderExtraContent('after-errors')} -
- )} -
- - - Editing Param - - ) : ( - 'Customize request' - ) - } - isOpen={isModalOpen} - onClose={onModalClose} - isContentFixed={isDrawerContentFixed} - headerLabel={drawerLabel ? drawerLabel : null}> - - -
-
-
- {!isMetamaskNetwork && } - {isMetamaskNetwork && !metaMaskAccount && } - -
-
-
-
- ) -} diff --git a/src/components/ParserOpenRPC/interfaces.ts b/src/components/ParserOpenRPC/interfaces.ts deleted file mode 100644 index 5e0321a0b30..00000000000 --- a/src/components/ParserOpenRPC/interfaces.ts +++ /dev/null @@ -1,56 +0,0 @@ -export interface SchemaPropertyType { - name?: string - $ref?: any - items?: any - title?: string - type: string - description?: string - required?: boolean - properties?: Record - enum?: string[] - default?: string - schema?: Schema -} - -export interface Schema { - summary: any - description: any - required: boolean - title: string - type: string - properties?: Record - items?: SchemaPropertyType - oneOf?: SchemaPropertyType[] - allOf?: SchemaPropertyType[] - anyOf?: SchemaPropertyType[] - $ref?: string -} - -export interface MethodParam { - name: string - description: string - schema: Schema - required?: boolean -} - -export interface SchemaComponents { - [key: string]: Schema -} - -export interface MethodExampleParam { - name: string - value: { - [key: string]: any - } -} - -export interface MethodExampleResult { - name: string - value: any -} - -export interface MethodExample { - name: string - params: MethodExampleParam[] - result: MethodExampleResult -} diff --git a/src/components/SidebarSectionDropdown/configs.ts b/src/components/SidebarSectionDropdown/configs.ts index 64f9f23d919..80dba45e6c4 100644 --- a/src/components/SidebarSectionDropdown/configs.ts +++ b/src/components/SidebarSectionDropdown/configs.ts @@ -23,29 +23,6 @@ export const SERVICES_DASHBOARD_CONFIG: SidebarSectionDropdownProps = { defaultSection: 'services', } -export const SDK_WALLET_CONFIG: SidebarSectionDropdownProps = { - sections: [ - { - key: 'sdk', - label: 'SDK', - title: 'MetaMask SDK', - description: 'Connect to MetaMask extension and mobile', - path: '/sdk/', - pathPattern: '/sdk', - }, - { - key: 'wallet', - label: 'Wallet API', - title: 'Wallet API', - description: 'Connect to MetaMask extension only', - path: '/wallet/', - pathPattern: '/wallet', - }, - ], - dropdownLabel: 'Product:', - defaultSection: 'sdk', -} - export const SNAPS_CONFIG: SidebarStaticTitleProps = { title: 'Snaps', pathPattern: '/snaps', diff --git a/src/components/SimplifiedApiReference/DetailsBox/index.tsx b/src/components/SimplifiedApiReference/DetailsBox/index.tsx new file mode 100644 index 00000000000..87f3faf816a --- /dev/null +++ b/src/components/SimplifiedApiReference/DetailsBox/index.tsx @@ -0,0 +1,128 @@ +import React from 'react' +import Heading from '@theme/Heading' +import ParamField from '../ParamField' +import styles from './styles.module.css' + +interface NestedParam { + name: string + type: string + required: boolean + description: string + children?: NestedParam[] +} + +interface Parameter { + name: string + type: string + required: boolean + description: string + children?: NestedParam[] +} + +interface Error { + code: number | string + message: string + description: string +} + +interface Returns { + type: string + description: string +} + +interface DetailsBoxProps { + method: string + description?: string + parameters: Parameter[] + returns?: Returns + errors: Error[] +} + +const DetailsBox: React.FC = ({ + method, + description, + parameters, + returns, + errors +}) => { + return ( + <> + {method} + + {description && ( +
+

{description}

+
+ )} + + + Parameters + + +
+ {parameters.length === 0 ? ( +
+ This method doesn't accept any parameters. +
+ ) : ( +
+ {parameters.map((param, index) => ( + + ))} +
+ )} +
+ + {returns && ( + <> + + Returns + +
+
+

{returns.description}

+
+
+ + )} + + {errors.length > 0 && ( + <> + + Errors + +
+ + + + + + + + + + {errors.map((error, index) => ( + + + + + + ))} + +
CodeMessageDescription
{error.code}{error.message}{error.description}
+
+ + )} + + ) +} + +export default DetailsBox diff --git a/src/components/SimplifiedApiReference/DetailsBox/styles.module.css b/src/components/SimplifiedApiReference/DetailsBox/styles.module.css new file mode 100644 index 00000000000..5808746399b --- /dev/null +++ b/src/components/SimplifiedApiReference/DetailsBox/styles.module.css @@ -0,0 +1,47 @@ +.sectionHeading { + padding-top: 1.5rem; + padding-bottom: 1rem; + margin-bottom: 0; +} + +.paramContainer { + margin-bottom: 1rem; +} + +.paramTable { + margin-top: 1rem !important; +} + +.noParams { + padding: 1rem 0; + color: var(--ifm-color-emphasis-700); + font-style: italic; +} + +.paramFields { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.returnsInfo { + padding-bottom: 1rem; +} + +.returnsInfo p { + margin: 0.5rem 0; +} + +.returnsInfo p:first-child { + margin-top: 0; +} + +.returnsInfo p:last-child { + margin-bottom: 0; +} + +html[data-theme='dark'] { + .paramTable th { + background: var(--ifm-color-emphasis-200); + } +} diff --git a/src/components/SimplifiedApiReference/Expandable/index.tsx b/src/components/SimplifiedApiReference/Expandable/index.tsx new file mode 100644 index 00000000000..66322682bc4 --- /dev/null +++ b/src/components/SimplifiedApiReference/Expandable/index.tsx @@ -0,0 +1,33 @@ +import React, { useState } from 'react' +import styles from './styles.module.css' + +interface ExpandableProps { + title: string + children: React.ReactNode +} + +const Expandable: React.FC = ({ title, children }) => { + const [isExpanded, setIsExpanded] = useState(false) + + return ( +
+ + {isExpanded && ( +
+ {children} +
+ )} +
+ ) +} + +export default Expandable diff --git a/src/components/SimplifiedApiReference/Expandable/styles.module.css b/src/components/SimplifiedApiReference/Expandable/styles.module.css new file mode 100644 index 00000000000..ea21925a93e --- /dev/null +++ b/src/components/SimplifiedApiReference/Expandable/styles.module.css @@ -0,0 +1,55 @@ +.expandable { + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 0.5rem; + margin: 0.5rem 0; + overflow: hidden; +} + +.expandableHeader { + width: 100%; + background: var(--ifm-color-emphasis-100); + border: none; + padding: 1rem; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + text-align: left; + font-size: 1.4rem; + font-weight: 600; + transition: background-color 0.2s ease; +} + +.expandableHeader:hover { + background: var(--ifm-color-emphasis-200); +} + +.expandableTitle { + color: var(--ifm-color-primary); +} + +.expandableIcon { + font-size: 0.8rem; + transition: transform 0.2s ease; + color: var(--ifm-color-emphasis-700); +} + +.expandableIcon.expanded { + transform: rotate(180deg); +} + +.expandableContent { + padding: 1rem; + background: var(--ifm-color-emphasis-50); + border-top: 1px solid var(--ifm-color-emphasis-200); +} + +html[data-theme='dark'] { + .expandableHeader { + background: var(--ifm-color-emphasis-200); + } + + .expandableHeader:hover { + background: var(--ifm-color-emphasis-300); + } +} diff --git a/src/components/SimplifiedApiReference/ParamField/index.tsx b/src/components/SimplifiedApiReference/ParamField/index.tsx new file mode 100644 index 00000000000..c760cca99cb --- /dev/null +++ b/src/components/SimplifiedApiReference/ParamField/index.tsx @@ -0,0 +1,69 @@ +import React from 'react' +import Expandable from '../Expandable' +import styles from './styles.module.css' + +interface NestedParam { + name: string + type: string + required: boolean + description: string + children?: NestedParam[] +} + +interface ParamFieldProps { + name: string + type: string + required: boolean + description: string + children?: NestedParam[] + expandableTitle?: string +} + +const ParamField: React.FC = ({ + name, + type, + required, + description, + children = [], + expandableTitle +}) => { + const hasChildren = children.length > 0 + + const renderNestedParams = (params: NestedParam[], title?: string) => { + if (params.length === 0) return null + + return ( + + {params.map((param, index) => ( + + ))} + + ) + } + + return ( +
+
+
+ {name} + ({type}) + {required && required} +
+
+
+ {description} +
+ {hasChildren && renderNestedParams(children, expandableTitle)} +
+ ) +} + +export default ParamField diff --git a/src/components/SimplifiedApiReference/ParamField/styles.module.css b/src/components/SimplifiedApiReference/ParamField/styles.module.css new file mode 100644 index 00000000000..98826bcfddb --- /dev/null +++ b/src/components/SimplifiedApiReference/ParamField/styles.module.css @@ -0,0 +1,46 @@ +.paramField { + margin-bottom: 1rem; + padding-top: 1rem; +} + +.paramHeader { + margin-bottom: 0.5rem; +} + +.paramName { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.paramName code { + font-family: var(--font-mm-sans-mono); + font-size: 1.3rem; +} + +.paramType { + color: var(--ifm-color-emphasis-700); + font-size: 1.4rem; + font-style: italic; +} + +.required { + background: var(--ifm-color-primary); + color: white; + padding: 0rem 0.3rem; + border-radius: 0.25rem; + font-size: 1.2rem; + font-weight: 500; +} + +.paramDescription { + font-size: 1.6rem; +} + +html[data-theme='dark'] { + .required { + color: black; + font-weight: 400; + } +} \ No newline at end of file diff --git a/src/components/SimplifiedApiReference/RequestBox/index.tsx b/src/components/SimplifiedApiReference/RequestBox/index.tsx new file mode 100644 index 00000000000..d059e57bdab --- /dev/null +++ b/src/components/SimplifiedApiReference/RequestBox/index.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import Heading from '@theme/Heading' +import CodeBlock from '@theme/CodeBlock' +import styles from './styles.module.css' + +interface RequestBoxProps { + method: string + exampleRequest: string + exampleResponse: string +} + +const RequestBox: React.FC = ({ + method, + exampleRequest, + exampleResponse +}) => { + return ( + <> +
+ + {exampleRequest} + +
+ +
+ + {exampleResponse} + +
+ + ) +} + +export default RequestBox diff --git a/src/components/SimplifiedApiReference/RequestBox/styles.module.css b/src/components/SimplifiedApiReference/RequestBox/styles.module.css new file mode 100644 index 00000000000..c676f9a40b1 --- /dev/null +++ b/src/components/SimplifiedApiReference/RequestBox/styles.module.css @@ -0,0 +1,4 @@ +.cardWrapper { + margin-bottom: 1.5rem; + overflow: hidden; +} diff --git a/src/components/SimplifiedApiReference/index.tsx b/src/components/SimplifiedApiReference/index.tsx new file mode 100644 index 00000000000..f3744618229 --- /dev/null +++ b/src/components/SimplifiedApiReference/index.tsx @@ -0,0 +1,72 @@ +import React from 'react' +import styles from './styles.module.css' +import DetailsBox from './DetailsBox' +import RequestBox from './RequestBox' + +interface NestedParam { + name: string + type: string + required: boolean + description: string + children?: NestedParam[] +} + +interface SimplifiedApiReferenceProps { + method: string + description?: string + parameters: Array<{ + name: string + type: string + required: boolean + description: string + children?: NestedParam[] + }> + returns?: { + type: string + description: string + } + errors?: Array<{ + code: number | string + message: string + description: string + }> + exampleRequest: string + exampleResponse: string +} + +const SimplifiedApiReference: React.FC = ({ + method, + description, + parameters, + returns, + errors = [], + exampleRequest, + exampleResponse +}) => { + return ( +
+
+
+ +
+
+
+
+ +
+
+
+ ) +} + +export default SimplifiedApiReference diff --git a/src/components/SimplifiedApiReference/styles.module.css b/src/components/SimplifiedApiReference/styles.module.css new file mode 100644 index 00000000000..6cfd35a71c2 --- /dev/null +++ b/src/components/SimplifiedApiReference/styles.module.css @@ -0,0 +1,54 @@ +.rowWrap { + display: flex; + flex-wrap: wrap; +} + +.stickyCol { + position: sticky; +} + +.colLeft { + position: relative; + width: calc(100% - 45rem); + border-right: 1px solid var(--ifm-toc-border-color); + overflow: hidden; +} + +.colContentWrap { + height: calc(100vh - 15rem); + padding-right: 2.5rem; + overflow-y: auto; +} + +.colRight { + width: 45rem; + padding-left: 1.5rem; +} + +@media (width <= 1300px) { + .colLeft { + width: 100%; + padding: 0; + border-right: 0; + } + + .colRight { + width: 100%; + padding: 0; + } + + .colContentWrap { + height: auto; + padding: 0; + } + + .rowWrap { + padding: 0; + } +} + +html[data-theme='dark'] { + .colLeft { + border-color: var(--ifm-toc-border-color); + } +} diff --git a/src/components/SubNavBar/configs.ts b/src/components/SubNavBar/configs.ts index e0532c19790..ada3f6ddc1e 100644 --- a/src/components/SubNavBar/configs.ts +++ b/src/components/SubNavBar/configs.ts @@ -47,8 +47,38 @@ export const EMBEDDED_WALLETS_SUBNAV_CONFIG: SubNavBarConfig = { ], } +export const SDK_SUBNAV_CONFIG: SubNavBarConfig = { + pathPattern: '/sdk', + sectionName: 'MM Connect', + links: [ + { + key: 'overview', + label: 'Overview', + path: '/sdk', + }, + { + key: 'multichain', + label: 'Multichain', + path: '/sdk/multichain', + }, + { + key: 'evm', + label: 'EVM', + path: '/sdk/evm', + }, + { + key: 'solana', + label: 'Solana', + path: '/sdk/solana', + }, + ], +} + // Array of all sub nav configs for easy iteration -export const ALL_SUBNAV_CONFIGS = [EMBEDDED_WALLETS_SUBNAV_CONFIG] +export const ALL_SUBNAV_CONFIGS = [ + EMBEDDED_WALLETS_SUBNAV_CONFIG, + SDK_SUBNAV_CONFIG, +] // Helper function to get config for current path export function getSubNavConfigForPath(pathname: string): SubNavBarConfig | null { diff --git a/src/config/mobileProductsMenu.ts b/src/config/mobileProductsMenu.ts index d5c15776522..3b77b8f46a0 100644 --- a/src/config/mobileProductsMenu.ts +++ b/src/config/mobileProductsMenu.ts @@ -12,12 +12,8 @@ export interface MobileProductItem { export const mobileProductsMenu: MobileProductItem[] = [ { - label: 'MetaMask SDK', - href: '/sdk/', - }, - { - label: 'Wallet API', - href: '/wallet/', + label: 'MM Connect', + href: '/sdk', }, { label: 'Embedded Wallets', diff --git a/src/pages/quickstart/NavigationOverlay/NavigationFlow.tsx b/src/pages/quickstart/NavigationOverlay/NavigationFlow.tsx index 32615271df4..56d25a3169a 100644 --- a/src/pages/quickstart/NavigationOverlay/NavigationFlow.tsx +++ b/src/pages/quickstart/NavigationOverlay/NavigationFlow.tsx @@ -27,7 +27,7 @@ const navigationOptions: NavigationOption[] = [ { id: 'mm-sdk', title: "I want to connect to users' MetaMask wallets", - description: 'MetaMask SDK', + description: 'MM Connect', product: METAMASK_SDK, }, { @@ -148,7 +148,7 @@ const NavigationFlow: React.FC = ({ onSelect }) => { Quick Links
- 📖 MetaMask SDK Docs + 📖 MM Connect Docs 💳 Embedded Wallets Docs diff --git a/src/pages/quickstart/builder/choices.ts b/src/pages/quickstart/builder/choices.ts index fed21beae75..b8e6f4fd5fe 100644 --- a/src/pages/quickstart/builder/choices.ts +++ b/src/pages/quickstart/builder/choices.ts @@ -6,7 +6,7 @@ export const METAMASK_SDK = 'METAMASK_SDK' // Product choices export const PRODUCTS: DisplayChoice[] = [ - { key: METAMASK_SDK, displayName: 'MetaMask SDK' }, + { key: METAMASK_SDK, displayName: 'MM Connect' }, { key: EMBEDDED_WALLETS, displayName: 'Embedded Wallets' }, ] diff --git a/src/pages/quickstart/builder/metamask-sdk/react/stepContent/initialization.mdx b/src/pages/quickstart/builder/metamask-sdk/react/stepContent/initialization.mdx index 007904313c5..7e24b459b12 100644 --- a/src/pages/quickstart/builder/metamask-sdk/react/stepContent/initialization.mdx +++ b/src/pages/quickstart/builder/metamask-sdk/react/stepContent/initialization.mdx @@ -1,6 +1,6 @@ -### Initialize MetaMask SDK +### Initialize MM Connect -Initialize MetaMask SDK with these options: +Initialize MM Connect with these options: - `dappMetadata` - Displays your dapp's name, URL, and icon URL during connection. - `infuraAPIKey` - Enables the read-only RPC and load‑balancing. diff --git a/src/pages/quickstart/builder/metamask-sdk/react/stepContent/installation.mdx b/src/pages/quickstart/builder/metamask-sdk/react/stepContent/installation.mdx index d6724f8405c..3ead8859cbc 100644 --- a/src/pages/quickstart/builder/metamask-sdk/react/stepContent/installation.mdx +++ b/src/pages/quickstart/builder/metamask-sdk/react/stepContent/installation.mdx @@ -1,6 +1,6 @@ -### Install MetaMask SDK +### Install MM Connect -Install MetaMask SDK in your React project. +Install MM Connect in your React project. ```bash npm2yarn npm install --save @metamask/sdk diff --git a/src/pages/quickstart/builder/metamask-sdk/react/stepContent/reactQuickStart.mdx b/src/pages/quickstart/builder/metamask-sdk/react/stepContent/reactQuickStart.mdx index a28c754a2da..5b9f99980d8 100644 --- a/src/pages/quickstart/builder/metamask-sdk/react/stepContent/reactQuickStart.mdx +++ b/src/pages/quickstart/builder/metamask-sdk/react/stepContent/reactQuickStart.mdx @@ -1,7 +1,7 @@ -### MetaMask SDK - React quickstart +### MM Connect - React quickstart This quickstart shows you how to connect a React application to the MetaMask wallet using -MetaMask SDK. +MM Connect. Clone the React quickstart application. diff --git a/src/pages/quickstart/builder/metamask-sdk/react/steps.ts b/src/pages/quickstart/builder/metamask-sdk/react/steps.ts index 106d3281016..960e7eda04b 100644 --- a/src/pages/quickstart/builder/metamask-sdk/react/steps.ts +++ b/src/pages/quickstart/builder/metamask-sdk/react/steps.ts @@ -12,7 +12,7 @@ export default function getSteps(steps, files, replacementAggregator) { pointer: replacementAggregator.highlightRange( qsFileLinks.MMSDK_REACT_APP_TSX, files[qsFileLinks.MMSDK_REACT_APP_TSX], - 'MetaMask SDK Import' + 'MM Connect Import' ), }, { @@ -20,7 +20,7 @@ export default function getSteps(steps, files, replacementAggregator) { pointer: replacementAggregator.highlightRange( qsFileLinks.MMSDK_REACT_PACKAGE_JSON, files[qsFileLinks.MMSDK_REACT_PACKAGE_JSON], - 'Install the MetaMask SDK' + 'Install MM Connect' ), }, { @@ -28,7 +28,7 @@ export default function getSteps(steps, files, replacementAggregator) { pointer: replacementAggregator.highlightRange( qsFileLinks.MMSDK_REACT_APP_TSX, files[qsFileLinks.MMSDK_REACT_APP_TSX], - 'Initialize the MetaMask SDK' + 'Initialize MM Connect' ), }, { diff --git a/src/pages/tutorials/create-wallet-ai-agent.md b/src/pages/tutorials/create-wallet-ai-agent.md index 4a3a673b7cd..ca87c3bdaea 100644 --- a/src/pages/tutorials/create-wallet-ai-agent.md +++ b/src/pages/tutorials/create-wallet-ai-agent.md @@ -1,8 +1,8 @@ --- -title: Create an AI agent using MetaMask SDK +title: Create an AI agent using MM Connect image: 'img/tutorials/tutorials-banners/create-wallet-ai-agent.png' -description: Create a wallet AI agent using MetaMask SDK and Vercel's AI SDK. -tags: [metamask sdk, AI agent, Vercel, Wagmi, Next.js, OpenAI] +description: Create a wallet AI agent using MM Connect and Vercel's AI SDK. +tags: [mm connect, AI agent, Vercel, Wagmi, Next.js, OpenAI] date: May 2, 2025 author: MetaMask Developer Relations --- @@ -11,7 +11,7 @@ import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; This tutorial walks you through creating an AI agent dapp that can display your wallet balance and initiate transactions from your wallet, on the Linea Sepolia network. -You will use a provided template, which sets up MetaMask SDK and [Vercel's AI SDK](https://sdk.vercel.ai/) with a [Next.js](https://nextjs.org/docs) and [Wagmi](https://wagmi.sh/) dapp. +You will use a provided template, which sets up MM Connect and [Vercel's AI SDK](https://sdk.vercel.ai/) with a [Next.js](https://nextjs.org/docs) and [Wagmi](https://wagmi.sh/) dapp. ## Prerequisites @@ -59,7 +59,7 @@ You will use a provided template, which sets up MetaMask SDK and [Vercel's AI SD ### 2. Create the dapp interface -In `app/page.tsx`, use the `useAccount`, `useConnect`, and `useDisconnect` hooks from Wagmi, along with the Wagmi [MetaMask SDK connector](https://wagmi.sh/react/api/connectors/metaMask) to create a button to connect and disconnect your MetaMask wallet. +In `app/page.tsx`, use the `useAccount`, `useConnect`, and `useDisconnect` hooks from Wagmi, along with the Wagmi [MetaMask connector](https://wagmi.sh/react/api/connectors/metaMask) to create a button to connect and disconnect your MetaMask wallet. Use the `Chat` component to display the AI agent chat interface. diff --git a/src/pages/tutorials/upgrade-eoa-to-smart-account.md b/src/pages/tutorials/upgrade-eoa-to-smart-account.md index f42fba26a3a..4a24292873b 100644 --- a/src/pages/tutorials/upgrade-eoa-to-smart-account.md +++ b/src/pages/tutorials/upgrade-eoa-to-smart-account.md @@ -1,13 +1,13 @@ --- title: Upgrade an EOA to a smart account -description: Upgrade a MetaMask EOA to a smart account using MetaMask SDK and Wagmi. +description: Upgrade a MetaMask EOA to a smart account using MM Connect and Wagmi. image: 'img/tutorials/tutorials-banners/upgrade-eoa-to-smart-account.png' -tags: [metamask sdk, wagmi, EOA, smart account, EIP-7702, EIP-5792] +tags: [mm connect, wagmi, EOA, smart account, EIP-7702, EIP-5792] date: Aug 22, 2025 author: MetaMask Developer Relations --- -This tutorial walks you through upgrading a MetaMask externally owned account (EOA) to a [MetaMask smart account](/smart-accounts-kit/concepts/smart-accounts) via [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702), and sending an [atomic batch transaction](/wallet/how-to/send-transactions/send-batch-transactions/#about-atomic-batch-transactions) via [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792). +This tutorial walks you through upgrading a MetaMask externally owned account (EOA) to a [MetaMask smart account](/smart-accounts-kit/concepts/smart-accounts) via [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702), and sending an [atomic batch transaction](/sdk/evm/connect/guides/javascript/send-transactions/batch-transactions) via [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792). You will use a provided template, which sets up MetaMask SDK with a [Next.js](https://nextjs.org/docs) and [Wagmi](https://wagmi.sh/) dapp. ## Prerequisites @@ -62,7 +62,7 @@ Add a `NEXT_PUBLIC_INFURA_API_KEY` environment variable, replacing ` ``` -In `src/providers/AppProvider.tsx`, configure the Wagmi [MetaMask SDK connector](https://wagmi.sh/react/api/connectors/metaMask) using your Infura API key: +In `src/providers/AppProvider.tsx`, configure the Wagmi [MetaMask connector](https://wagmi.sh/react/api/connectors/metaMask) using your Infura API key: ```tsx title="AppProvider.tsx" "use client"; @@ -88,7 +88,7 @@ import { metaMask } from "wagmi/connectors"; ### 3. Create a connect and disconnect button -In `src/app/page.tsx`, use the `useAccount`, `useConnect`, and `useDisconnect` hooks from Wagmi, along with the MetaMask SDK connector, to create a button to connect and disconnect your MetaMask wallet, and display the connection status: +In `src/app/page.tsx`, use the `useAccount`, `useConnect`, and `useDisconnect` hooks from Wagmi, along with the MetaMask connector, to create a button to connect and disconnect your MetaMask wallet, and display the connection status: ```tsx title="page.tsx" "use client"; @@ -199,7 +199,7 @@ When connected, the interface displays your connected wallet address: ### 4. Handle and send batch transactions -In `src/app/page.tsx`, use the [`useSendCalls`](https://wagmi.sh/react/api/hooks/useSendCalls) hook from Wagmi to handle and send [atomic batch transactions](/wallet/how-to/send-transactions/send-batch-transactions). +In `src/app/page.tsx`, use the [`useSendCalls`](https://wagmi.sh/react/api/hooks/useSendCalls) hook from Wagmi to handle and send [atomic batch transactions](/sdk/evm/connect/guides/javascript/send-transactions/batch-transactions). Also use React's `useState` hook to handle the transaction state. The following example sends 0.001 and 0.0001 ETH in a batch transaction. Replace `` with recipient addresses of your choice: diff --git a/src/plugins/plugin-json-rpc.ts b/src/plugins/plugin-json-rpc.ts index 10b2d8de420..97cee893dee 100644 --- a/src/plugins/plugin-json-rpc.ts +++ b/src/plugins/plugin-json-rpc.ts @@ -69,12 +69,12 @@ async function fetchMultipleData( } export const RPC_NETWORK_URL = 'https://sot-network-methods.vercel.app/specs' -export const MM_RPC_URL = 'https://metamask.github.io/api-specs/latest/openrpc.json' -export const MM_REF_PATH = 'wallet/reference/json-rpc-methods' +// export const MM_RPC_URL = 'https://metamask.github.io/api-specs/latest/openrpc.json' +// export const MM_REF_PATH = 'sdk/evm/connect/reference/json-rpc-methods' export enum NETWORK_NAMES { linea = 'linea', - metamask = 'metamask', +// metamask = 'metamask', } const requests = [ @@ -82,10 +82,10 @@ const requests = [ url: `${RPC_NETWORK_URL}/${NETWORK_NAMES.linea}`, name: NETWORK_NAMES.linea, }, - { - url: MM_RPC_URL, - name: NETWORK_NAMES.metamask, - }, +// { +// url: MM_RPC_URL, +// name: NETWORK_NAMES.metamask, +// }, ] export const prepareLinkItems = ( @@ -129,6 +129,7 @@ export default function useNetworksMethodPlugin() { async contentLoaded({ content, actions }) { const { addRoute, createData, setGlobalData } = actions setGlobalData({ netData: content }) + /* const dynamicRoutes = content.find(item => item.name === NETWORK_NAMES.metamask) if (dynamicRoutes) { const methodsData = await createData( @@ -147,6 +148,7 @@ export default function useNetworksMethodPlugin() { }) } } + */ }, configureWebpack() { return { diff --git a/src/theme/DocSidebar/Desktop/index.tsx b/src/theme/DocSidebar/Desktop/index.tsx index 5e59c5a13a5..6de40837ca1 100644 --- a/src/theme/DocSidebar/Desktop/index.tsx +++ b/src/theme/DocSidebar/Desktop/index.tsx @@ -11,7 +11,6 @@ import SidebarSectionDropdown, { } from '@site/src/components/SidebarSectionDropdown/SidebarSectionDropdown' import { SERVICES_DASHBOARD_CONFIG, - SDK_WALLET_CONFIG, SNAPS_CONFIG, SMART_ACCOUNTS_KIT_CONFIG, isPathInSections, @@ -40,7 +39,6 @@ function DocSidebarDesktop({ path, sidebar, onCollapse, isHidden }: Props) { location.pathname, SERVICES_DASHBOARD_CONFIG.sections ) - const isSDKOrWallet = isPathInSections(location.pathname, SDK_WALLET_CONFIG.sections) const isSnaps = location.pathname.startsWith('/snaps') let smartAccountsKitTitle = null @@ -75,19 +73,6 @@ function DocSidebarDesktop({ path, sidebar, onCollapse, isHidden }: Props) { console.error('Failed to render services dropdown:', e) } - let sdkDropdown = null - try { - if (isSDKOrWallet) { - sdkDropdown = ( -
- -
- ) - } - } catch (e) { - console.error('Failed to render SDK dropdown:', e) - } - let snapsTitle = null try { if (isSnaps) { @@ -112,7 +97,6 @@ function DocSidebarDesktop({ path, sidebar, onCollapse, isHidden }: Props) { {smartAccountsKitTitle} {versionDropdown} {servicesDropdown} - {sdkDropdown} {snapsTitle} {hideable && } diff --git a/src/theme/DocSidebar/Mobile/index.tsx b/src/theme/DocSidebar/Mobile/index.tsx index b64f6fb1182..d822629097d 100644 --- a/src/theme/DocSidebar/Mobile/index.tsx +++ b/src/theme/DocSidebar/Mobile/index.tsx @@ -14,7 +14,6 @@ import SidebarSectionDropdown, { } from '@site/src/components/SidebarSectionDropdown/SidebarSectionDropdown' import { SERVICES_DASHBOARD_CONFIG, - SDK_WALLET_CONFIG, SNAPS_CONFIG, SMART_ACCOUNTS_KIT_CONFIG, isPathInSections, @@ -31,7 +30,6 @@ const DocSidebarMobileSecondaryMenu: NavbarSecondaryMenuComponent = ({ si location.pathname, SERVICES_DASHBOARD_CONFIG.sections ) - const isSDKOrWallet = isPathInSections(location.pathname, SDK_WALLET_CONFIG.sections) const isSnaps = location.pathname.startsWith('/snaps') return ( @@ -51,11 +49,6 @@ const DocSidebarMobileSecondaryMenu: NavbarSecondaryMenuComponent = ({ si )} - {isSDKOrWallet && ( -
  • - -
  • - )} {isSnaps && (
  • diff --git a/src/utils/tutorials-map.tsx b/src/utils/tutorials-map.tsx index 899fcdeb764..494357368ca 100644 --- a/src/utils/tutorials-map.tsx +++ b/src/utils/tutorials-map.tsx @@ -1,7 +1,7 @@ export const tags = { web3Auth: 'web3auth', embeddedWallet: 'embedded wallets', - metamaskSdk: 'metamask sdk', + metamaskSdk: 'mm connect', smartAccountsKit: 'smart accounts kit', infura: 'infura', snaps: 'snaps', @@ -90,7 +90,7 @@ export const platformMap = [ export const productMap = [ { - label: 'Metamask SDK', + label: 'MM Connect', value: tags.metamaskSdk, }, { diff --git a/vercel.json b/vercel.json index bea96b6a74d..2cde2ff27b1 100644 --- a/vercel.json +++ b/vercel.json @@ -8,47 +8,51 @@ }, { "source": "/guide/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/guide/:path*/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/index/", - "destination": "/wallet/" + "destination": "/sdk/" + }, + { + "source": "/wallet/", + "destination": "/sdk/" }, { "source": "/wallet/category/get-started/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/get-started-building/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/guide/metamask-extension-provider/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/quickstart/:path*/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/discover-multiple-wallets/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/detect-wallet/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/detect-wallet/multiple-wallets/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/access-provider/", - "destination": "/wallet/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/connect/", @@ -56,15 +60,15 @@ }, { "source": "/sdk/quickstart/:path*/", - "destination": "/sdk/connect/:path*/" + "destination": "/sdk/" }, { "source": "/sdk/introduction/supported-platforms/", - "destination": "/sdk/reference/supported-platforms/" + "destination": "/sdk/about/" }, { "source": "/sdk/introduction/llm-prompt/", - "destination": "/sdk/reference/llm-prompt/" + "destination": "/sdk/" }, { "source": "/sdk/introduction/supported-networks/", @@ -96,7 +100,7 @@ }, { "source": "/wallet/connect/3rd-party-libraries/wagmi/", - "destination": "/sdk/connect/javascript-wagmi" + "destination": "/sdk/evm/connect/get-started/wagmi/" }, { "source": "/wallet/connect/3rd-party-libraries/web3-onboard/", @@ -136,67 +140,67 @@ }, { "source": "/wallet/category/how-to/", - "destination": "/wallet/how-to/connect-extension/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/", - "destination": "/wallet/how-to/connect-extension/" + "destination": "/sdk/" }, { "source": "/wallet/connect/", - "destination": "/wallet/how-to/connect-extension/" + "destination": "/sdk/" }, { "source": "/wallet/connect/wallet-api/", - "destination": "/wallet/how-to/connect-extension/" + "destination": "/sdk/" }, { "source": "/guide/accessing-accounts/", - "destination": "/wallet/how-to/access-accounts/" + "destination": "/sdk/evm/connect/guides/manage-user-accounts/" }, { "source": "/wallet/how-to/connect/access-accounts/", - "destination": "/wallet/how-to/access-accounts/" + "destination": "/sdk/evm/connect/guides/manage-user-accounts/" }, { "source": "/wallet/get-started/access-accounts/", - "destination": "/wallet/how-to/access-accounts/" + "destination": "/sdk/evm/connect/guides/manage-user-accounts/" }, { "source": "/wallet/how-to/manage-networks/", - "destination": "/wallet/how-to/manage-networks/detect-network/" + "destination": "/sdk/evm/connect/guides/manage-networks/" }, { "source": "/wallet/get-started/detect-network/", - "destination": "/wallet/how-to/manage-networks/detect-network/" + "destination": "/sdk/evm/connect/guides/manage-networks/" }, { "source": "/wallet/how-to/connect/detect-network/", - "destination": "/wallet/how-to/manage-networks/detect-network/" + "destination": "/sdk/evm/connect/guides/manage-networks/" }, { "source": "/wallet/how-to/detect-network/", - "destination": "/wallet/how-to/manage-networks/detect-network/" + "destination": "/sdk/evm/connect/guides/manage-networks/" }, { "source": "/wallet/how-to/add-network/", - "destination": "/wallet/how-to/manage-networks/add-network/" + "destination": "/sdk/evm/connect/guides/manage-networks/" }, { "source": "/guide/signing-data/", - "destination": "/wallet/how-to/sign-data/" + "destination": "/sdk/evm/connect/guides/sign-data/" }, { "source": "/wallet/how-to/sign-data/connect-and-sign/", - "destination": "/sdk/guides/advanced/connect-and-sign/" + "destination": "/sdk/evm/connect/guides/manage-user-accounts/" }, { "source": "/wallet/how-to/use-siwe/", - "destination": "/wallet/how-to/sign-data/siwe/" + "destination": "/sdk/evm/connect/guides/sign-data/siwe/" }, { "source": "/guide/sending-transactions/", - "destination": "/wallet/how-to/send-transactions/" + "destination": "/sdk/evm/connect/guides/send-transactions/" }, { "source": "/wallet/how-to/use-3rd-party-integrations/:path*/", @@ -208,35 +212,35 @@ }, { "source": "/wallet/how-to/batch-json-rpc-requests/", - "destination": "/sdk/guides/advanced/batch-requests/" + "destination": "/sdk/evm/connect/guides/batch-requests/" }, { "source": "/wallet/how-to/display/", - "destination": "/wallet/how-to/display/tokens/" + "destination": "/sdk/evm/connect/guides/display-tokens/" }, { "source": "/guide/registering-your-token/", - "destination": "/wallet/how-to/display/tokens/" + "destination": "/sdk/evm/connect/guides/display-tokens/" }, { "source": "/wallet/how-to/register-token/", - "destination": "/wallet/how-to/display/tokens/" + "destination": "/sdk/evm/connect/guides/display-tokens/" }, { "source": "/guide/registering-function-names/", - "destination": "/wallet/how-to/display/method-names/" + "destination": "/sdk/evm/connect/guides/best-practices/display/" }, { "source": "/wallet/how-to/register-method-names/", - "destination": "/wallet/how-to/display/method-names/" + "destination": "/sdk/evm/connect/guides/best-practices/display/" }, { "source": "/guide/defining-your-icon/", - "destination": "/wallet/how-to/display/icon/" + "destination": "/sdk/evm/connect/guides/best-practices/display/" }, { "source": "/wallet/how-to/set-icon/", - "destination": "/wallet/how-to/display/icon/" + "destination": "/sdk/evm/connect/guides/best-practices/display/" }, { "source": "/wallet/how-to/display/custom-modals/", @@ -244,11 +248,11 @@ }, { "source": "/wallet/how-to/request-permissions/", - "destination": "/wallet/how-to/manage-permissions/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/interact-with-smart-contracts/:path*/", - "destination": "/sdk/guides/interact-with-contracts/" + "destination": "/sdk/evm/connect/guides/interact-with-contracts/" }, { "source": "/wallet/how-to/use-unity-sdk/", @@ -260,51 +264,51 @@ }, { "source": "/guide/onboarding-library/", - "destination": "/wallet/how-to/onboard-users/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/use-onboarding-library/", - "destination": "/wallet/how-to/onboard-users/" + "destination": "/sdk/" }, { "source": "/wallet/get-started/run-development-network/", - "destination": "/wallet/how-to/run-devnet/" + "destination": "/sdk/evm/connect/guides/best-practices/run-devnet/" }, { "source": "/wallet/how-to/get-started-building/run-devnet/", - "destination": "/wallet/how-to/run-devnet/" + "destination": "/sdk/evm/connect/guides/best-practices/run-devnet/" }, { "source": "/wallet/how-to/get-started-building/secure-dapp/", - "destination": "/wallet/how-to/secure-dapp/" + "destination": "/sdk/" }, { "source": "/wallet/concepts/", - "destination": "/wallet/concepts/wallet-api/" + "destination": "/sdk/" }, { "source": "/wallet/category/concepts/", - "destination": "/wallet/concepts/wallet-api/" + "destination": "/sdk/" }, { "source": "/guide/provider-migration/", - "destination": "/wallet/concepts/wallet-api/" + "destination": "/sdk/" }, { "source": "/wallet/concepts/provider-api/", - "destination": "/wallet/concepts/wallet-api/" + "destination": "/sdk/" }, { "source": "/wallet/concepts/rpc-api/", - "destination": "/wallet/concepts/wallet-api/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/migrate-api/", - "destination": "/wallet/concepts/wallet-api/" + "destination": "/sdk/" }, { "source": "/wallet/concepts/apis/", - "destination": "/wallet/concepts/wallet-api/" + "destination": "/sdk/" }, { "source": "/wallet/concepts/sdk/:path*/", @@ -324,51 +328,51 @@ }, { "source": "/guide/initializing-dapps/", - "destination": "/wallet/concepts/smart-contracts/" + "destination": "/sdk/evm/connect/guides/interact-with-contracts/" }, { "source": "/wallet/how-to/interact-with-smart-contracts/", - "destination": "/wallet/concepts/smart-contracts/" + "destination": "/sdk/evm/connect/guides/interact-with-contracts/" }, { "source": "/wallet/tutorials/", - "destination": "/wallet/tutorials/react-dapp-local-state/" + "destination": "/sdk/" }, { "source": "/wallet/category/tutorials/", - "destination": "/wallet/tutorials/react-dapp-local-state/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/set-up-dev-environment/", - "destination": "/wallet/tutorials/javascript-dapp-simple/" + "destination": "/sdk/" }, { "source": "/wallet/get-started/set-up-dev-environment/", - "destination": "/wallet/tutorials/javascript-dapp-simple/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/get-started-building/set-up-dev-environment/", - "destination": "/wallet/tutorials/javascript-dapp-simple/" + "destination": "/sdk/" }, { "source": "/wallet/get-started/detect-metamask/", - "destination": "/wallet/tutorials/javascript-dapp-simple/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/detect-metamask/", - "destination": "/wallet/tutorials/javascript-dapp-simple/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/detect-wallet/metamask/", - "destination": "/wallet/tutorials/javascript-dapp-simple/" + "destination": "/sdk/" }, { "source": "/wallet/how-to/connect/detect-metamask/", - "destination": "/wallet/tutorials/javascript-dapp-simple/" + "destination": "/sdk/" }, { "source": "/wallet/tutorials/simple-react-dapp/", - "destination": "/wallet/tutorials/react-dapp-local-state/" + "destination": "/sdk/" }, { "source": "/wallet/tutorials/react-dapp-sdk-wagmi/", @@ -376,7 +380,11 @@ }, { "source": "/wallet/reference/sdk-js-options/", - "destination": "/sdk/reference/sdk-options/" + "destination": "/sdk/reference/options/" + }, + { + "source": "/sdk/reference/sdk-options/", + "destination": "/sdk/reference/options/" }, { "source": "/wallet/reference/sdk-unity-api/", @@ -392,51 +400,251 @@ }, { "source": "/guide/ethereum-provider/", - "destination": "/wallet/reference/provider-api/" + "destination": "/sdk/evm/connect/reference/provider-api/" }, { "source": "/wallet/reference/json-rpc-api/", - "destination": "/wallet/reference/json-rpc-methods/" + "destination": "/sdk/evm/connect/reference/json-rpc-api/" }, { "source": "/guide/rpc-api/", - "destination": "/wallet/reference/json-rpc-methods/" + "destination": "/sdk/evm/connect/reference/json-rpc-api/" }, { "source": "/wallet/reference/rpc-api/", - "destination": "/wallet/reference/json-rpc-methods/" + "destination": "/sdk/evm/connect/reference/json-rpc-api/" }, { "source": "/wallet/reference/(eth_.*)", - "destination": "/wallet/reference/json-rpc-methods/$1" + "destination": "/sdk/evm/connect/reference/json-rpc-api/$1" }, { "source": "/wallet/reference/(wallet_.*)", - "destination": "/wallet/reference/json-rpc-methods/$1" + "destination": "/sdk/evm/connect/reference/json-rpc-api/$1" }, { "source": "/wallet/reference/(personal_.*)", - "destination": "/wallet/reference/json-rpc-methods/$1" + "destination": "/sdk/evm/connect/reference/json-rpc-api/$1" }, { "source": "/wallet/reference/(web3_.*)", - "destination": "/wallet/reference/json-rpc-methods/$1" + "destination": "/sdk/evm/connect/reference/json-rpc-api/$1" }, { "source": "/wallet/reference/non-evm-apis/", - "destination": "/wallet/how-to/use-non-evm-networks/" + "destination": "/sdk/" }, { "source": "/sdk/guides/advanced/connect-and-sign/", - "destination": "/sdk/guides/authenticate-users/" + "destination": "/sdk/evm/connect/guides/manage-user-accounts/" }, { "source": "/sdk/guides/advanced/batch-requests/", - "destination": "/sdk/guides/batch-requests/" + "destination": "/sdk/evm/connect/guides/batch-requests/" }, { "source": "/sdk/guides/advanced/production-readiness/", - "destination": "/sdk/guides/production-readiness/" + "destination": "/sdk/evm/connect/guides/best-practices/production-readiness/" + }, + { + "source": "/wallet/reference/multichain-api/", + "destination": "/sdk/multichain/reference/api/" + }, + { + "source": "/sdk/connect/javascript-wagmi/", + "destination": "/sdk/evm/connect/get-started/wagmi/" + }, + { + "source": "/sdk/connect/javascript/", + "destination": "/sdk/evm/connect/get-started/javascript/" + }, + { + "source": "/sdk/connect/javascript-rainbowkit/", + "destination": "/sdk/evm/connect/get-started/rainbowkit/" + }, + { + "source": "/sdk/connect/react-native/", + "destination": "/sdk/evm/connect/get-started/react-native/" + }, + { + "source": "/sdk/guides/authenticate-users/", + "destination": "/sdk/evm/connect/guides/manage-user-accounts/" + }, + { + "source": "/sdk/guides/manage-networks/", + "destination": "/sdk/evm/connect/guides/manage-networks/" + }, + { + "source": "/sdk/guides/handle-transactions/", + "destination": "/sdk/evm/connect/guides/send-transactions/" + }, + { + "source": "/sdk/guides/interact-with-contracts/", + "destination": "/sdk/evm/connect/guides/interact-with-contracts/" + }, + { + "source": "/sdk/guides/use-deeplinks/", + "destination": "/sdk/evm/connect/guides/use-deeplinks/" + }, + { + "source": "/sdk/guides/batch-requests/", + "destination": "/sdk/evm/connect/guides/batch-requests/" + }, + { + "source": "/sdk/guides/production-readiness/", + "destination": "/sdk/evm/connect/guides/best-practices/production-readiness/" + }, + { + "source": "/wallet/how-to/connect-extension/", + "destination": "/sdk/evm/connect/guides/connect-extension/" + }, + { + "source": "/wallet/how-to/access-accounts/", + "destination": "/sdk/evm/connect/guides/manage-user-accounts/" + }, + { + "source": "/wallet/how-to/manage-networks/detect-network/", + "destination": "/sdk/evm/connect/guides/manage-networks/" + }, + { + "source": "/wallet/how-to/manage-networks/add-network/", + "destination": "/sdk/evm/connect/guides/manage-networks/" + }, + { + "source": "/wallet/how-to/manage-networks/use-multichain/", + "destination": "/sdk/evm/connect/guides/manage-networks/" + }, + { + "source": "/wallet/how-to/sign-data/", + "destination": "/sdk/evm/connect/guides/sign-data/" + }, + { + "source": "/wallet/how-to/sign-data/siwe/", + "destination": "/sdk/evm/connect/guides/sign-data/siwe/" + }, + { + "source": "/wallet/how-to/send-transactions/", + "destination": "/sdk/evm/connect/guides/send-transactions/" + }, + { + "source": "/wallet/how-to/send-transactions/send-batch-transactions/", + "destination": "/sdk/evm/connect/guides/send-transactions/batch-transactions/" + }, + { + "source": "/wallet/how-to/display/tokens/", + "destination": "/sdk/evm/connect/guides/display-tokens/" + }, + { + "source": "/wallet/how-to/display/method-names/", + "destination": "/sdk/evm/connect/guides/best-practices/display/" + }, + { + "source": "/wallet/how-to/display/icon/", + "destination": "/sdk/evm/connect/guides/best-practices/display/" + }, + { + "source": "/wallet/how-to/manage-permissions/", + "destination": "/sdk/" + }, + { + "source": "/wallet/how-to/use-non-evm-networks/", + "destination": "/sdk/" + }, + { + "source": "/wallet/how-to/use-non-evm-networks/solana/", + "destination": "/sdk/solana/" + }, + { + "source": "/wallet/how-to/use-non-evm-networks/starknet/", + "destination": "/sdk/" + }, + { + "source": "/wallet/how-to/onboard-users/", + "destination": "/sdk/" + }, + { + "source": "/wallet/how-to/run-devnet/", + "destination": "/sdk/evm/connect/guides/best-practices/run-devnet/" + }, + { + "source": "/wallet/how-to/secure-dapp/", + "destination": "/sdk/" + }, + { + "source": "/sdk/connect/javascript-dynamic/", + "destination": "/sdk/evm/connect/partners/dynamic/" + }, + { + "source": "/sdk/connect/javascript-web3auth/", + "destination": "/sdk/evm/connect/partners/web3auth/" + }, + { + "source": "/wallet/concepts/wallet-api/", + "destination": "/sdk/" + }, + { + "source": "/wallet/concepts/multichain-api/", + "destination": "/sdk/" + }, + { + "source": "/wallet/concepts/convenience-libraries/", + "destination": "/sdk/" + }, + { + "source": "/wallet/concepts/signing-methods/", + "destination": "/sdk/evm/connect/guides/sign-data/" + }, + { + "source": "/wallet/concepts/wallet-interoperability/", + "destination": "/sdk/" + }, + { + "source": "/wallet/concepts/smart-contracts/", + "destination": "/sdk/evm/connect/guides/interact-with-contracts/" + }, + { + "source": "/wallet/tutorials/react-dapp-local-state/", + "destination": "/sdk/" + }, + { + "source": "/wallet/tutorials/react-dapp-global-state/", + "destination": "/sdk/" + }, + { + "source": "/wallet/tutorials/javascript-simple-dapp/", + "destination": "/sdk/" + }, + { + "source": "/sdk/reference/llm-prompt/", + "destination": "/sdk/" + }, + { + "source": "/sdk/reference/supported-platforms/", + "destination": "/sdk/about/" + }, + { + "source": "/sdk/reference/sdk-methods/", + "destination": "/sdk/evm/connect/reference/methods/" + }, + { + "source": "/wallet/reference/non-evm-apis/starknet-snap-api/", + "destination": "/sdk/" + }, + { + "source": "/wallet/reference/provider-api/", + "destination": "/sdk/evm/connect/reference/provider-api/" + }, + { + "source": "/wallet/reference/multichain-api/", + "destination": "/sdk/multichain/connect/reference/api/" + }, + { + "source": "/wallet/reference/json-rpc-methods/", + "destination": "/sdk/evm/connect/reference/json-rpc-api/" + }, + { + "source": "/wallet/how-to/use-non-evm-networks/starknet/:path*/", + "destination": "/sdk/" }, { "source": "/guide/snaps/", diff --git a/wallet-sidebar.js b/wallet-sidebar.js deleted file mode 100644 index 2a1ea33cfaf..00000000000 --- a/wallet-sidebar.js +++ /dev/null @@ -1,287 +0,0 @@ -// @ts-check - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebar = { - walletSidebar: [ - { - type: "doc", - label: "Introduction", - id: "index", - }, - { - type: "category", - label: "How to", - collapsible: true, - collapsed: false, - items: [ - { - type: "doc", - label: "Connect to the extension", - id: "how-to/connect-extension" - }, - { - type: "doc", - label: "Access a user's accounts", - id: "how-to/access-accounts" - }, - { - type: "category", - label: "Manage networks", - collapsible: true, - collapsed: true, - items: [ - { - type: "doc", - label: "Detect a user's network", - id: "how-to/manage-networks/detect-network" - }, - { - type: "doc", - label: "Add a network", - id: "how-to/manage-networks/add-network" - }, - { - type: "doc", - label: "Interact with multiple networks simultaneously", - id: "how-to/manage-networks/use-multichain" - } - ] - }, - { - type: "category", - label: "Sign data", - collapsible: true, - collapsed: true, - link: { type: "doc", id: "how-to/sign-data/index" }, - items: [ - { - type: "doc", - label: "Sign in with Ethereum", - id: "how-to/sign-data/siwe" - } - ] - }, - { - type: "category", - label: "Send transactions", - collapsible: true, - collapsed: true, - link: {type: "doc", id: "how-to/send-transactions/index" }, - items: [ - { - type: "doc", - label: "Send batch transactions", - id: "how-to/send-transactions/send-batch-transactions" - } - ] - }, - { - type: "category", - label: "Display in MetaMask", - collapsible: true, - collapsed: true, - items: [ - { - type: "doc", - label: "Display tokens", - id: "how-to/display/tokens" - }, - { - type: "doc", - label: "Display a contract's method names", - id: "how-to/display/method-names" - }, - { - type: "doc", - label: "Display a dapp icon", - id: "how-to/display/icon" - } - ] - }, - { - type: "doc", - label: "Manage permissions", - id: "how-to/manage-permissions" - }, - { - type: "category", - label: "Use non-EVM networks", - collapsible: true, - collapsed: true, - link: { type: "doc", id: "how-to/use-non-evm-networks/index" }, - items: [ - { - type: "doc", - label: "Solana", - id: "how-to/use-non-evm-networks/solana" - }, - { - type: "category", - label: "Starknet", - link: { type: "doc", id: "how-to/use-non-evm-networks/starknet/index" }, - items: [ - { - type: "doc", - label: "Connect to Starknet", - id: "how-to/use-non-evm-networks/starknet/connect-to-starknet" - }, - { - type: "doc", - label: "Manage Starknet accounts", - id: "how-to/use-non-evm-networks/starknet/manage-starknet-accounts" - }, - { - type: "doc", - label: "Manage Starknet networks", - id: "how-to/use-non-evm-networks/starknet/manage-starknet-networks" - }, - { - type: "doc", - label: "Send Starknet transactions", - id: "how-to/use-non-evm-networks/starknet/send-starknet-transactions" - }, - { - type: "doc", - label: "Sign Starknet transactions", - id: "how-to/use-non-evm-networks/starknet/sign-starknet-data" - }, - { - type: "doc", - label: "Create a simple Starknet dapp", - id: "how-to/use-non-evm-networks/starknet/create-a-simple-starknet-dapp" - }, - { - type: "doc", - label: "Troubleshoot", - id: "how-to/use-non-evm-networks/starknet/troubleshoot" - }, - { - type: "doc", - label: "About get-starknet", - id: "how-to/use-non-evm-networks/starknet/about-get-starknet" - } - ] - } - ] - }, - { - type: "doc", - label: "Onboard users", - id: "how-to/onboard-users" - }, - { - type: "doc", - label: "Run a development network", - id: "how-to/run-devnet" - }, - { - type: "doc", - label: "Secure your dapp", - id: "how-to/secure-dapp" - } - ], - }, - { - type: "category", - label: "Concepts", - collapsible: true, - collapsed: true, - items: [ - { - type: "doc", - label: "About the Wallet API", - id: "concepts/wallet-api" - }, - { - type: "doc", - label: "About the Multichain API", - id: "concepts/multichain-api" - }, - { - type: "doc", - label: "Convenience libraries", - id: "concepts/convenience-libraries" - }, - { - type: "doc", - label: "Signing methods", - id: "concepts/signing-methods" - }, - { - type: "doc", - label: "Wallet interoperability", - id: "concepts/wallet-interoperability" - }, - { - type: "doc", - label: "Smart contracts", - id: "concepts/smart-contracts" - }, - ], - }, - { - type: "category", - label: "Tutorials", - collapsible: true, - collapsed: true, - items: [ - { - type: "doc", - label: "Create a React dapp with local state", - id: "tutorials/react-dapp-local-state", - }, - { - type: "doc", - label: "Create a React dapp with global state", - id: "tutorials/react-dapp-global-state", - }, - { - type: "doc", - label: "Create a simple dapp", - id: "tutorials/javascript-dapp-simple", - }, - ], - }, - { - type: "category", - label: "Reference", - collapsible: true, - collapsed: false, - items: [ - { - type: "category", - label: "Non-EVM APIs", - collapsible: true, - collapsed: true, - items: [ - { - type: "doc", - label: "Starknet Snap API", - id: "reference/non-evm-apis/starknet-snap-api", - }, - ], - }, - { - type: "doc", - label: "Ethereum provider API", - id: "reference/provider-api", - }, - { - type: "doc", - label: "Multichain API", - id: "reference/multichain-api", - }, - { - type: "category", - label: "JSON-RPC API", - collapsible: true, - collapsed: true, - link: { type: "doc", id: "reference/json-rpc-methods/index" }, - items: [{ type: "autogenerated", dirName: "reference/json-rpc-methods" }], - }, - ], - }, - ], -}; - -module.exports = sidebar; diff --git a/wallet/assets/add-network.png b/wallet/assets/add-network.png deleted file mode 100644 index 8db142eee96..00000000000 Binary files a/wallet/assets/add-network.png and /dev/null differ diff --git a/wallet/assets/contract-abi-converter-dialog.png b/wallet/assets/contract-abi-converter-dialog.png deleted file mode 100644 index 521b9001072..00000000000 Binary files a/wallet/assets/contract-abi-converter-dialog.png and /dev/null differ diff --git a/wallet/assets/custom-modal.gif b/wallet/assets/custom-modal.gif deleted file mode 100644 index e22361752d2..00000000000 Binary files a/wallet/assets/custom-modal.gif and /dev/null differ diff --git a/wallet/assets/react-tutorial-01-final.png b/wallet/assets/react-tutorial-01-final.png deleted file mode 100644 index 75bb508fb93..00000000000 Binary files a/wallet/assets/react-tutorial-01-final.png and /dev/null differ diff --git a/wallet/assets/react-tutorial-01-start.png b/wallet/assets/react-tutorial-01-start.png deleted file mode 100644 index 875737acefc..00000000000 Binary files a/wallet/assets/react-tutorial-01-start.png and /dev/null differ diff --git a/wallet/assets/request-permissions-2.png b/wallet/assets/request-permissions-2.png deleted file mode 100644 index 38b5c918027..00000000000 Binary files a/wallet/assets/request-permissions-2.png and /dev/null differ diff --git a/wallet/assets/request-permissions.png b/wallet/assets/request-permissions.png deleted file mode 100644 index e8e43d6132c..00000000000 Binary files a/wallet/assets/request-permissions.png and /dev/null differ diff --git a/wallet/assets/sdk-android-architecture.png b/wallet/assets/sdk-android-architecture.png deleted file mode 100644 index ac3a0c86764..00000000000 Binary files a/wallet/assets/sdk-android-architecture.png and /dev/null differ diff --git a/wallet/assets/sdk-android-communication.png b/wallet/assets/sdk-android-communication.png deleted file mode 100644 index 4ee909859cb..00000000000 Binary files a/wallet/assets/sdk-android-communication.png and /dev/null differ diff --git a/wallet/assets/sdk-clear-connections.png b/wallet/assets/sdk-clear-connections.png deleted file mode 100644 index 4fb204117ea..00000000000 Binary files a/wallet/assets/sdk-clear-connections.png and /dev/null differ diff --git a/wallet/assets/starknet-dapp-connected.png b/wallet/assets/starknet-dapp-connected.png deleted file mode 100644 index df5e1dfb60d..00000000000 Binary files a/wallet/assets/starknet-dapp-connected.png and /dev/null differ diff --git a/wallet/assets/starknet-metamask-connection.png b/wallet/assets/starknet-metamask-connection.png deleted file mode 100644 index f56d929fa3a..00000000000 Binary files a/wallet/assets/starknet-metamask-connection.png and /dev/null differ diff --git a/wallet/assets/starknet-token-update.png b/wallet/assets/starknet-token-update.png deleted file mode 100644 index 2bda4ac764b..00000000000 Binary files a/wallet/assets/starknet-token-update.png and /dev/null differ diff --git a/wallet/assets/starknet-tutorial-connected.png b/wallet/assets/starknet-tutorial-connected.png deleted file mode 100644 index c4ec0363aef..00000000000 Binary files a/wallet/assets/starknet-tutorial-connected.png and /dev/null differ diff --git a/wallet/assets/starknet-tutorial-modal.png b/wallet/assets/starknet-tutorial-modal.png deleted file mode 100644 index 1137384195a..00000000000 Binary files a/wallet/assets/starknet-tutorial-modal.png and /dev/null differ diff --git a/wallet/assets/starknet-tutorial-start-dapp.png b/wallet/assets/starknet-tutorial-start-dapp.png deleted file mode 100644 index fbfacbd0e4c..00000000000 Binary files a/wallet/assets/starknet-tutorial-start-dapp.png and /dev/null differ diff --git a/wallet/assets/starknet-tutorial-transfer-token.png b/wallet/assets/starknet-tutorial-transfer-token.png deleted file mode 100644 index c6fe6eedcb3..00000000000 Binary files a/wallet/assets/starknet-tutorial-transfer-token.png and /dev/null differ diff --git a/wallet/assets/starknet-wallet-modal.png b/wallet/assets/starknet-wallet-modal.png deleted file mode 100644 index 81db588e97c..00000000000 Binary files a/wallet/assets/starknet-wallet-modal.png and /dev/null differ diff --git a/wallet/assets/switch-network.png b/wallet/assets/switch-network.png deleted file mode 100644 index 6a6cd2bcbaa..00000000000 Binary files a/wallet/assets/switch-network.png and /dev/null differ diff --git a/wallet/assets/tutorials/beginner-tutorial/account.png b/wallet/assets/tutorials/beginner-tutorial/account.png deleted file mode 100644 index a6589971524..00000000000 Binary files a/wallet/assets/tutorials/beginner-tutorial/account.png and /dev/null differ diff --git a/wallet/assets/tutorials/beginner-tutorial/connect.png b/wallet/assets/tutorials/beginner-tutorial/connect.png deleted file mode 100644 index 7111896a909..00000000000 Binary files a/wallet/assets/tutorials/beginner-tutorial/connect.png and /dev/null differ diff --git a/wallet/assets/tutorials/beginner-tutorial/enable-button.png b/wallet/assets/tutorials/beginner-tutorial/enable-button.png deleted file mode 100644 index a48dc0d5a61..00000000000 Binary files a/wallet/assets/tutorials/beginner-tutorial/enable-button.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-01.png b/wallet/assets/tutorials/react-dapp/pt1-01.png deleted file mode 100644 index b669103db17..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-01.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-02.png b/wallet/assets/tutorials/react-dapp/pt1-02.png deleted file mode 100644 index 7b4352972ff..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-02.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-03.png b/wallet/assets/tutorials/react-dapp/pt1-03.png deleted file mode 100644 index c6a5dda6f41..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-03.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-04.png b/wallet/assets/tutorials/react-dapp/pt1-04.png deleted file mode 100644 index fa6f5f19e7e..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-04.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-05.png b/wallet/assets/tutorials/react-dapp/pt1-05.png deleted file mode 100644 index 2bb0644e42a..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-05.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-06.png b/wallet/assets/tutorials/react-dapp/pt1-06.png deleted file mode 100644 index e19fcc8f63f..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-06.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-07.png b/wallet/assets/tutorials/react-dapp/pt1-07.png deleted file mode 100644 index 2bc7f78e81b..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-07.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-08.png b/wallet/assets/tutorials/react-dapp/pt1-08.png deleted file mode 100644 index fde92ff5a47..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-08.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-09.png b/wallet/assets/tutorials/react-dapp/pt1-09.png deleted file mode 100644 index 44563214d49..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-09.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt1-10.png b/wallet/assets/tutorials/react-dapp/pt1-10.png deleted file mode 100644 index 2ba0fa979a2..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt1-10.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt2-01.png b/wallet/assets/tutorials/react-dapp/pt2-01.png deleted file mode 100644 index 7f03bfe49e2..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt2-01.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt2-02.png b/wallet/assets/tutorials/react-dapp/pt2-02.png deleted file mode 100644 index 2bbfcd49b81..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt2-02.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt2-03.png b/wallet/assets/tutorials/react-dapp/pt2-03.png deleted file mode 100644 index d475d4a263a..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt2-03.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt2-04.png b/wallet/assets/tutorials/react-dapp/pt2-04.png deleted file mode 100644 index 34b31327b7a..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt2-04.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/pt2-05.png b/wallet/assets/tutorials/react-dapp/pt2-05.png deleted file mode 100644 index e8551d0b2b1..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/pt2-05.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/react-tutorial-02-final-preview.png b/wallet/assets/tutorials/react-dapp/react-tutorial-02-final-preview.png deleted file mode 100644 index ed0f5c2c343..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/react-tutorial-02-final-preview.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/react-tutorial-02-selected-wallet.png b/wallet/assets/tutorials/react-dapp/react-tutorial-02-selected-wallet.png deleted file mode 100644 index c505559bb8b..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/react-tutorial-02-selected-wallet.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/react-tutorial-02-wallet-error.png b/wallet/assets/tutorials/react-dapp/react-tutorial-02-wallet-error.png deleted file mode 100644 index faef95049c4..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/react-tutorial-02-wallet-error.png and /dev/null differ diff --git a/wallet/assets/tutorials/react-dapp/react-tutorial-02-wallet-list.png b/wallet/assets/tutorials/react-dapp/react-tutorial-02-wallet-list.png deleted file mode 100644 index f88e4b88bfa..00000000000 Binary files a/wallet/assets/tutorials/react-dapp/react-tutorial-02-wallet-list.png and /dev/null differ diff --git a/wallet/assets/unity-empty-template.png b/wallet/assets/unity-empty-template.png deleted file mode 100644 index 6ed04976126..00000000000 Binary files a/wallet/assets/unity-empty-template.png and /dev/null differ diff --git a/wallet/assets/unity-example-template.png b/wallet/assets/unity-example-template.png deleted file mode 100644 index ef3da48013a..00000000000 Binary files a/wallet/assets/unity-example-template.png and /dev/null differ diff --git a/wallet/assets/unity-infura.png b/wallet/assets/unity-infura.png deleted file mode 100644 index ead13eaca7a..00000000000 Binary files a/wallet/assets/unity-infura.png and /dev/null differ diff --git a/wallet/assets/wagmi-errors.png b/wallet/assets/wagmi-errors.png deleted file mode 100644 index 5a8ae372fc4..00000000000 Binary files a/wallet/assets/wagmi-errors.png and /dev/null differ diff --git a/wallet/concepts/convenience-libraries.md b/wallet/concepts/convenience-libraries.md deleted file mode 100644 index de160faa209..00000000000 --- a/wallet/concepts/convenience-libraries.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: Learn about convenience libraries. ---- - -# Convenience libraries - -Various convenience libraries exist for dapp developers. -Some of them simplify the creation of specific user interface elements, some entirely manage the -user account onboarding, and others give you a variety of methods for interacting with smart -contracts, for a variety of API preferences (for example, promises, callbacks, and strong types). - -The [MetaMask Ethereum provider API](wallet-api.md#ethereum-provider-api) is lightweight, and wraps -[Ethereum JSON-RPC](wallet-api.md#json-rpc-api) formatted messages, which is why -some developers use a convenience library for interacting with the provider, such as -[Ethers](https://www.npmjs.com/package/ethers) or [web3.js](https://www.npmjs.com/package/web3). -You can refer to those tools' documentation to use them. - -You can use [MetaMask SDK](/sdk), which provides a reliable, secure, and -seamless connection from your dapp to MetaMask. -It onboards users smoothly from multiple dapp platforms using the MetaMask extension or -mobile app, and your dapp can call any [Wallet API](wallet-api.md) methods with the SDK installed. diff --git a/wallet/concepts/multichain-api.md b/wallet/concepts/multichain-api.md deleted file mode 100644 index ccbaf2af169..00000000000 --- a/wallet/concepts/multichain-api.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -description: Learn about the Multichain API. ---- - -# About the Multichain API - -:::tip Experimental -The Multichain API is an experimental feature. -::: - -The Multichain API is a scalable, generalized web3 wallet API that supports simultaneous -interactions across multiple blockchain networks and ecosystems. -When integrated with [MetaMask Snaps](/snaps), it enables developers to interact with both popular -and emerging networks. -Key benefits include: - -- **Elimination of chain switching** - The Multichain API allows dapps to interact with multiple networks without having to request chain switches. - This feature reduces development overhead involved with ensuring the correct network is targeted for a given dapp-proposed transaction. - -- **Extensibility** - The Multichain API can be integrated with - [interoperability Snaps](https://snaps.metamask.io/explore/), providing a standards-based interface - to connect wallets with dapps on non-EVM networks. - -- **Seamless multichain UX** - The Multichain API offers improvements over EIP-1193 and [wallet-standard](https://github.com/wallet-standard/wallet-standard) interfaces. - It allows dapps to create unified cross-ecosystem multichain wallet connection flows, trigger transactions across different networks more seamlessly, and clearly interpret chain-specific addresses. - -[**Get started using the Multichain API.**](../how-to/manage-networks/use-multichain.md) - -## Technical overview - -The Multichain API follows the [CAIP-25](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-25.md) -standard for dapps to interface with multichain wallets. -See [MIP-5](https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-5.md) and -[MIP-6](https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-6.md) for -detailed information about MetaMask's Multichain API implementation. - -The API includes methods for dapps to manage multichain connections: - -- [`wallet_createSession`](../reference/multichain-api.md#wallet_createsession) - Creates a multichain connection with a wallet, with specified properties and -authorization scopes. -Dapps can update the connection using the same method. -- [`wallet_invokeMethod`](../reference/multichain-api.md#wallet_invokemethod) - Calls a subset of the [Wallet JSON-RPC API methods](../reference/json-rpc-methods/index.md) on -a specified chain. -- [`wallet_getSession`](../reference/multichain-api.md#wallet_getsession) - Gets -the scopes and properties of the active connection. -- [`wallet_revokeSession`](../reference/multichain-api.md#wallet_revokesession) - Revokes the active connection. - -The API also includes events that wallets can send to dapps: - -- [`wallet_notify`](../reference/multichain-api.md#wallet_notify) - Notifies dapps of onchain events or state changes they previously subscribed to. -- [`wallet_sessionChanged`](../reference/multichain-api.md#wallet_sessionchanged) - Notifies dapps of changes to the multichain connection. - -See the [Multichain API reference](../reference/multichain-api.md) for full details. - -### Lifecycle diagram - -The following sequence diagram illustrates the multichain connection lifecycle. - -```mermaid -%%{ - init: { - 'sequence': { - 'actorMargin': 100, - 'width': 275 - } - } -}%% - -sequenceDiagram - participant Dapp - participant Wallet - participant WalletDataStore as Wallet data store - - opt Create connection - Dapp->>Wallet: wallet_createSession - Wallet->>WalletDataStore: Persist connection data - Wallet-->>Dapp: {"sessionScopes": {...}} - end - - opt Update connection - Dapp->>Wallet: wallet_createSession (update auth) - Wallet->>WalletDataStore: Update connection data - Wallet-->>Dapp: {"sessionScopes": {updatedScopes...}} - end - - opt Connection interrupted with wallet-side modification - Dapp-->>Wallet: Connection interrupted - Wallet->>WalletDataStore: User initiated connection change - Wallet-->>Dapp: wallet_sessionChanged (attempt fails) - Dapp-->>Wallet: Connection re-established - end - - opt Get connection - Dapp->>Wallet: wallet_getSession - Wallet-->>Dapp: {"sessionScopes": {...}} - end - - opt Revoke connection - Dapp->>Wallet: wallet_revokeSession - Wallet->>WalletDataStore: Update connection data - Wallet-->>Dapp: {"result": "true"} - end -``` - -## Backwards compatibility - -When using the Multichain API, your dapp can still interact with the existing -[Ethereum provider API](wallet-api.md#ethereum-provider-api). -However, the provider API is not optimized for multichain usage, and we recommend -[starting directly with the Multichain API](../how-to/manage-networks/use-multichain.md). -The Multichain API is backwards compatible mainly to support dapps that use third-party libraries -with dependencies on the legacy provider. - -:::note -A multichain connection will overwrite a connection with the legacy EIP-1193 provider, and vice versa. -::: - -## Get started - -Get started with the Multichain API: - -- Learn how to [use the Multichain API](../how-to/manage-networks/use-multichain.md). -- See the [Multichain API reference](../reference/multichain-api.md) for more details. diff --git a/wallet/concepts/signing-methods.md b/wallet/concepts/signing-methods.md deleted file mode 100644 index bc48b7c361a..00000000000 --- a/wallet/concepts/signing-methods.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -description: Learn about the RPC methods for signing transactions in MetaMask. ---- - -# Signing methods - -This page describes the signing RPC methods in MetaMask. -Learn how to [use the recommended signing methods](../how-to/sign-data/index.md). - -### `eth_signTypedData_v4` - -[`eth_signTypedData_v4`](/wallet/reference/json-rpc-methods/eth_signtypeddata_v4) -is: - -- Cheap to verify onchain. -- Human-readable. -- Protected against phishing signatures. - -If onchain verifiability cost is a high priority, -[use `eth_signTypedData_v4`](../how-to/sign-data/index.md#use-eth_signtypeddata_v4). - -### `personal_sign` - -[`personal_sign`](/wallet/reference/json-rpc-methods/personal_sign): - -- Displays human-readable text when UTF-8 encoded, making it a popular choice for site logins - (for example, [Sign-In with Ethereum](../how-to/sign-data/siwe.md)). -- Is protected against phishing signatures. - -The text prefix of `personal_sign` makes signatures expensive to verify onchain. -If onchain verifiability cost is not a priority, you can -[use `personal_sign`](../how-to/sign-data/index.md#use-personal_sign). - -:::note -MetaMask implements `personal_sign` similarly to the Go Ethereum client's updated `eth_sign` implementation. -MetaMask's `personal_sign` doesn't accept a password. -::: - -## Deprecated signing methods - -:::caution important -`eth_sign`, `eth_signTypedData_v1`, and `eth_signTypedData_v3` are deprecated. -Use `eth_signTypedData_v4` or `personal_sign`. -::: - -### `eth_sign` - -`eth_sign` allows signing an arbitrary hash, which means an attacker can use it to request users to -sign transactions or any other data. -Using `eth_sign` is a dangerous phishing risk. - -To enhance user security, MetaMask no longer supports using `eth_sign`. -Use [`eth_signTypedData_v4`](#eth_signtypeddata_v4) or [`personal_sign`](#personal_sign) instead. - -:::note -See [MIP-3](https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-3.md) for -more information about the discontinuation of `eth_sign`. -::: - -### `eth_signTypedData_v1` and `eth_signTypedData_v3` - -`eth_signTypedData` was introduced by [EIP-712](https://eips.ethereum.org/EIPS/eip-712). -The EIP-712 specification changed several times resulting in multiple versions -of `eth_signTypedData`. - -The earlier versions are: - -- `eth_signTypedData_v1` - The same as `eth_signTypedData`. - Read the - [introductory blog post to this method](https://medium.com/metamask/scaling-web3-with-signtypeddata-91d6efc8b290). -- `eth_signTypedData_v3` - A highly used version of the EIP-712 specification. - Read the - [introductory blog post to this method](https://medium.com/metamask/eip712-is-coming-what-to-expect-and-how-to-use-it-bb92fd1a7a26). - -The missing `v2` represents an intermediary design that the Cipher browser implemented. - -All early versions of this method lack later security improvements. -Use the latest version, [`eth_signTypedData_v4`](#eth_signtypeddata_v4). diff --git a/wallet/concepts/smart-contracts.md b/wallet/concepts/smart-contracts.md deleted file mode 100644 index 248bcc59876..00000000000 --- a/wallet/concepts/smart-contracts.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -description: Learn about interacting with smart contracts. ---- - -# Smart contracts - -This is a high-level overview of interacting with smart contracts. - -To interact with a smart contract, your dapp needs the following information: - -- [Contract network](#contract-network) -- [Contract address](#contract-address) -- [Contract ABI](#contract-abi) -- [Contract bytecode](#contract-bytecode) -- [Contract source code](#contract-source-code) - -## Contract network - -If you're not connected to the right network, you can't send transactions to your contract. -Many dapp developers deploy their contract to a testnet first, in order to avoid potentially -disastrous fees if something goes wrong during development and testing on Mainnet. - -Regardless of which network you deploy your final dapp on, your users must be able to access it. -Use the [`wallet_switchEthereumChain`](/wallet/reference/json-rpc-methods/wallet_switchethereumchain) and -[`wallet_addEthereumChain`](/wallet/reference/json-rpc-methods/wallet_addethereumchain) RPC methods to prompt -the user to add a chain that you suggest, and switch to it using a confirmation dialogue. - -## Contract address - -Every account in Ethereum has an address, whether it's an external key-pair account or a smart contract. -For any smart contract library to communicate with your contracts, a smart contract must know the exact address. - -Read about -[how to find a token contact address](https://metamask.zendesk.com/hc/en-us/articles/360059683451-How-to-view-or-add-custom-token-contract-address). - -## Contract ABI - -In Ethereum, the [ABI specification](https://solidity.readthedocs.io/en/develop/abi-spec.html) is a -way to encode the interface of a smart contract that's comprehensible to your user interface. -The ABI is an array of method-describing objects, and when you feed this and the address into a -contract-abstraction library, the ABI tells those libraries about what methods to provide, and -how to compose transactions to call those methods. - -Example libraries include the following: - -- [Ethers](https://www.npmjs.com/package/ethers) -- [web3.js](https://www.npmjs.com/package/web3) -- [ethjs](https://www.npmjs.com/package/ethjs) -- [Hardhat](https://hardhat.org/) - -## Contract bytecode - -If your dapp publishes a new pre-compiled smart contract, it might need to include some bytecode. -You don't know the contract address in advance; you must publish the contract, watch for the -transaction to be processed, and then extract the final contract's address from the completed transaction. - -If you publish a contract from bytecode, you still need an [ABI](#contract-abi) to interact with it. -The bytecode doesn't describe how to interact with the final contract. - -## Contract source code - -If your dapp allows users to edit smart contract source code and compile it, similar to -[Remix](https://remix.ethereum.org/), you can import a whole compiler. -You derive your bytecode and ABI from that source code, and eventually derive the contract's address -from the completed transaction, where that bytecode is published. diff --git a/wallet/concepts/wallet-api.md b/wallet/concepts/wallet-api.md deleted file mode 100644 index 5eef79e0df4..00000000000 --- a/wallet/concepts/wallet-api.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -description: Learn about the MetaMask Ethereum provider API and JSON-RPC API. ---- - -# About the Wallet API - -MetaMask's Wallet API consists of an [Ethereum provider API](#ethereum-provider-api), which wraps -a [JSON-RPC API](#json-rpc-api). - -:::tip API documentation -The API methods are documented in the following references: - -- [Ethereum provider API reference](../reference/provider-api.md) -- [JSON-RPC API reference](/wallet/reference/json-rpc-methods) - ::: - -## Ethereum provider API - -MetaMask injects a global JavaScript API into websites visited by its users using the -`window.ethereum` provider object. -This API is specified by [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193), and it allows dapps to -request users' EVM accounts, read data from blockchains the user is connected to, suggest -that the user sign messages and transactions, and more. - -:::info EIP-6963: Wallet interoperability -MetaMask supports [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963), which introduces an -alternative wallet detection mechanism to the `window.ethereum` injected provider. -This alternative mechanism enables dapps to support [wallet interoperability](wallet-interoperability.md) -by discovering multiple injected wallet providers in a user's browser. -We recommend [using this mechanism to connect to MetaMask](../how-to/connect-extension.md). - -You can access the provider API using the selected EIP-6963 provider object. -Throughout this documentation, we refer to the selected provider using `provider`. -::: - -The MetaMask Ethereum provider API contains the following: - -- [Properties](../reference/provider-api.md#properties) - The provider contains a property that - detects if a user has MetaMask installed. -- [Methods](../reference/provider-api.md#methods) - The provider contains methods that dapps can call. - The [`request()`](../reference/provider-api.md#request) - provider method wraps the [JSON-RPC API](#json-rpc-api); dapps can use this - provider method to call any RPC method. -- [Events](../reference/provider-api.md#events) - The provider emits events that dapps can listen to. - -View the [provider API reference](../reference/provider-api.md) for all the provider properties, -methods, and events. - -:::note Multichain API -MetaMask supports the [Multichain API](multichain-api.md) for interacting with multiple blockchain networks and ecosystems simultaneously. -While the Multichain API is backwards compatible with the provider API, the provider API is not optimized for multichain usage. -If you intend to support multichain interactions, we recommend -[starting directly with the Multichain API](../how-to/manage-networks/use-multichain.md). -::: - -## JSON-RPC API - -MetaMask uses the [`request()`](../reference/provider-api.md#request) -method of the [provider API](#ethereum-provider-api) to wrap a JSON-RPC API. -The JSON-RPC API contains standard Ethereum JSON-RPC API methods and MetaMask-specific methods. - -The RPC methods are documented in the interactive [JSON-RPC API reference](/wallet/reference/json-rpc-methods). - -:::note -All RPC method requests can return errors. -Make sure to handle errors for every call to -[`request()`](../reference/provider-api.md#request). -::: - -The RPC methods are divided into the following: - -- [Restricted methods](#restricted-methods) - Require user consent for actions that impact assets or data (for example, initiating a transaction). -- [Unrestricted methods](#unrestricted-methods) - Allow dapps to perform basic actions without permission (for example, retrieving a public address). - -### Restricted methods - -MetaMask implements permissions based on [EIP-2255](https://eips.ethereum.org/EIPS/eip-2255) to enhance security for when users interact with dapps. -This requires that dapps obtain user consent before accessing certain features. -Under the hood, permissions are plain, JSON-compatible objects, with fields that are mostly used -internally by MetaMask. - -Restricted methods are methods that cannot be called unless you have permission to do so using -[`wallet_requestPermissions`](/wallet/reference/json-rpc-methods/wallet_requestpermissions) or -[`wallet_requestSnaps`](/snaps/reference/wallet-api-for-snaps/#wallet_requestsnaps). - -The following methods are restricted: - -- [`eth_accounts`](/wallet/reference/json-rpc-methods/eth_accounts) - Gaining permission requires calling `wallet_requestPermissions`. - Granting permission for `eth_accounts` also grants permissions for the following methods: - - - [`eth_sendTransaction`](/wallet/reference/json-rpc-methods/eth_sendtransaction) - - [`personal_sign`](/wallet/reference/json-rpc-methods/personal_sign) - - [`eth_signTypedData_v4`](/wallet/reference/json-rpc-methods/eth_signtypeddata_v4) - :::caution important - To access accounts, we recommend using [`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts), - which automatically asks for permission to use `eth_accounts` by calling `wallet_requestPermissions` internally. - See [how to access a user's accounts](../how-to/access-accounts.md) for more information. - ::: - -- [`wallet_snap`](/snaps/reference/wallet-api-for-snaps/#wallet_snap) - Gaining permission requires - calling `wallet_requestSnap`. - -- [`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) - Gaining - permission requires calling `wallet_requestSnap`. - -### Unrestricted methods - -Unrestricted methods do not require requesting permission to call them, but they might require confirmation by the -user (for example, [`wallet_addEthereumChain`](/wallet/reference/json-rpc-methods/wallet_addethereumchain)). diff --git a/wallet/concepts/wallet-interoperability.md b/wallet/concepts/wallet-interoperability.md deleted file mode 100644 index 9497bc6f03b..00000000000 --- a/wallet/concepts/wallet-interoperability.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -description: Learn about wallet interoperability via EIP-6963. ---- - -# Wallet interoperability - -A web dapp can integrate with multiple installed browser wallets simultaneously by adding support for -[EIP-6963](https://eips.ethereum.org/EIPS/eip-6963), which introduces an alternative wallet detection -mechanism to the [`window.ethereum`](wallet-api.md#ethereum-provider-api) injected provider. -This mechanism is enabled by using the standardized interfaces defined by EIP-6963. - -:::info Why EIP-6963? -[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) standardizes the interface for wallet providers, -but it results in conflicts when users have multiple wallets installed, due to how the provider -object is injected. -This can cause issues with wallet discovery, user onboarding, and connecting. -The wallet discovery mechanism introduced by EIP-6963 solves these issues. -::: - -The following is a demo of the user experience of detecting multiple wallets, showing the data -provided from each installed wallet: - -

    - -

    - -You can [connect to MetaMask using EIP-6963](../how-to/connect-extension.md) and see the -[EIP-6963 Vite React + TypeScript demo](https://github.com/MetaMask/vite-react-ts-eip-6963/tree/main) -for more information. - -## EIP-6963 interfaces - -Wallets that support EIP-6963 implement and expose the following standardized interfaces. -When [connecting to MetaMask using EIP-6963](../how-to/connect-extension.md), it's important to review -and understand these interfaces. - -### Provider info - -The [`EIP6963ProviderInfo`](https://eips.ethereum.org/EIPS/eip-6963#provider-info) interface -represents the assets needed to display a wallet: - -- `uuid` - The wallet ID ([UUIDv4](https://www.rfc-editor.org/rfc/rfc4122)). -- `name` - A human-readable name of the wallet. -- `icon` - A [URI](https://www.rfc-editor.org/rfc/rfc3986) pointing to an icon of the wallet. -- `rdns` - The wallet's domain name. - -### Provider detail - -The [`EIP6963ProviderDetail`](https://eips.ethereum.org/EIPS/eip-6963#provider-detail) interface -represents additional metadata about the wallet: - -- `info` - The [`EIP6963ProviderInfo`](#provider-info). -- `provider` - The `EIP1193Provider` defined by [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193). - -### Announce and request events - -The [`EIP6963AnnounceProviderEvent`](https://eips.ethereum.org/EIPS/eip-6963#announce-and-request-events) -interface announces an event dispatched by the wallet: - -```typescript -interface EIP6963AnnounceProviderEvent extends CustomEvent { - type: "eip6963:announceProvider" - detail: EIP6963ProviderDetail -} -``` - -The [`EIP6963RequestProviderEvent`](https://eips.ethereum.org/EIPS/eip-6963#announce-and-request-events) -interface requests an event dispatched by a dapp: - -```typescript -interface EIP6963RequestProviderEvent extends Event { - type: "eip6963:requestProvider" -} -``` - -## Third-party library support - -The following third-party libraries support EIP-6963: - -- [Wagmi 2+](/sdk/connect/javascript-wagmi) -- [Reown AppKit](https://docs.reown.com/appkit/overview) -- [MIPD Store](https://github.com/wevm/mipd) -- [RainbowKit](https://www.rainbowkit.com) -- [Web3-Onboard](https://web3onboard.thirdweb.com) -- [ConnectKit](https://docs.family.co/connectkit) - -### MIPD Store - -The [MIPD Store](https://github.com/wevm/mipd) stores the wallet providers and enables you to -subscribe to the store and retrieve the providers. -Unlike [Wagmi](https://wagmi.sh) and [Web3-Onboard](https://web3onboard.thirdweb.com/), which are -libraries that provide components and connectors for multiple wallets and depend on MetaMask SDK for -integration, the MIPD Store is a utility library that makes it easier to work with EIP-6963 and -supports TypeScript types. - -## MetaMask SDK support - -:::note -MetaMask SDK does not support connecting to non-MetaMask wallets via EIP-6963. -If you intend to support discovery of other wallets, we recommend using a third-party library such as [Wagmi](/sdk/connect/javascript-wagmi). -::: - -[MetaMask SDK](/sdk) automatically checks for the presence of the MetaMask extension via EIP-6963. -This eliminates the need for manual configuration or detection methods, simplifying the initial -setup process for both developers and users. - -By adhering to the standards set by EIP-6963, the SDK unambiguously identifies and connects to -MetaMask, resolving potential conflicts that might arise with other wallet extensions, ensuring a -more stable and reliable interaction for users. - -The SDK is also integrated into third-party libraries, like [Wagmi](/sdk/connect/javascript-wagmi), which support EIP-6963. -The SDK on its own supports connecting _only_ to MetaMask via EIP-6963, so if you intend to support -discovery of other wallets, we recommend using third-party libraries. - -## Wallet support - -The EIP-6963 alternative discovery mechanism works for wallets that have implemented support for EIP-6963. -This includes MetaMask, Coinbase, Trust Wallet, OKX, and other major wallets. -See the [list of wallets that support EIP-6963](https://github.com/WalletConnect/EIP6963/blob/master/src/utils/constants.ts). - -## Backwards compatibility - -Dapps that do not support EIP-6963 can still -[detect MetaMask using the `window.ethereum` provider](../tutorials/javascript-dapp-simple.md). -However, we recommend adding support to improve the user experience for multiple installed wallets. -Read more about [EIP-6963 backwards compatibility](https://eips.ethereum.org/EIPS/eip-6963#backwards-compatibility). diff --git a/wallet/how-to/access-accounts.md b/wallet/how-to/access-accounts.md deleted file mode 100644 index aff91c27010..00000000000 --- a/wallet/how-to/access-accounts.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -description: Access a user's accounts and handle changed accounts. ---- - -# Access a user's accounts - -User accounts are used in a variety of contexts in Ethereum, including as identifiers and for -[signing transactions](sign-data/index.md). -To request a signature from a user or have a user approve a transaction, your dapp can -access the user's accounts using the -[`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts) RPC method. - -:::info note -`eth_requestAccounts` internally calls [`wallet_requestPermissions`](/wallet/reference/json-rpc-methods/wallet_requestpermissions) -to [request permission](manage-permissions.md) to call the restricted -[`eth_accounts`](/wallet/reference/json-rpc-methods/eth_accounts) method. -::: - -When accessing a user's accounts: - -- **Only** initiate a connection request in response to direct user action, such as - selecting a [connect button](#create-a-connect-button). -- **Always** disable the connect button while the connection request is pending. -- **Never** initiate a connection request on page load. - -:::note -You can also access users' accounts on some [non-EVM networks](/wallet/how-to/use-non-evm-networks). -::: - -## Create a connect button - -:::caution Important -This section describes how to create a single connect button. -When connecting to multiple wallets, use the [Connect to the MetaMask extension](connect-extension.md) guide to create -multiple connect buttons. -::: - -We recommend providing a button to allow users to connect MetaMask to your dapp. -Selecting this button should call `eth_requestAccounts` to access the user's accounts. - -For example, the following JavaScript code accesses the user's accounts when they select a connect -button: - -```javascript title="index.js" -// You should only attempt to request the user's account in response to user interaction, such as -// selecting a button. Otherwise, you pop-up spam the user like it's 1999. If you fail to retrieve -// the user's account, you should encourage the user to initiate the attempt. -const ethereumButton = document.querySelector(".enableEthereumButton") -const showAccount = document.querySelector(".showAccount") - -ethereumButton.addEventListener("click", () => { - getAccount() -}) - -// While awaiting the call to eth_requestAccounts, you should disable any buttons the user can -// select to initiate the request. MetaMask rejects any additional requests while the first is still -// pending. -async function getAccount() { - const accounts = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ method: "eth_requestAccounts" }) - .catch((err) => { - if (err.code === 4001) { - // EIP-1193 userRejectedRequest error. - // If this happens, the user rejected the connection request. - console.log("Please connect to MetaMask.") - } else { - console.error(err) - } - }) - const account = accounts[0] - showAccount.innerHTML = account -} -``` - -The following HTML code displays the button and the current account: - -```html title="index.html" - - -

    Account:

    -``` - -## Handle accounts - -Use the [`eth_accounts`](/wallet/reference/json-rpc-methods/eth_accounts) -RPC method to handle user accounts. -Listen to the [`accountsChanged`](../reference/provider-api.md#accountschanged) provider event to -be notified when the user changes accounts. - -The following code handles user accounts and detects when the user changes accounts: - -```javascript title="index.js" -let currentAccount = null -provider // Or window.ethereum if you don't support EIP-6963. - .request({ method: "eth_accounts" }) - .then(handleAccountsChanged) - .catch((err) => { - // Some unexpected error. - // For backwards compatibility reasons, if no accounts are available, eth_accounts returns an - // empty array. - console.error(err) - }) - -// Note that this event is emitted on page load. If the array of accounts is non-empty, you're -// already connected. -provider // Or window.ethereum if you don't support EIP-6963. - .on("accountsChanged", handleAccountsChanged) - -// eth_accounts always returns an array. -function handleAccountsChanged(accounts) { - if (accounts.length === 0) { - // MetaMask is locked or the user has not connected any accounts. - console.log("Please connect to MetaMask.") - } else if (accounts[0] !== currentAccount) { - // Reload your interface with accounts[0]. - currentAccount = accounts[0] - // Update the account displayed (see the HTML for the connect button) - showAccount.innerHTML = currentAccount - } -} -``` - -:::note -`eth_accounts` now returns the full list of accounts for which the user has permitted access to. -Previously, `eth_accounts` returned at most one account in the `accounts` array. -The first account in the array will always be considered the user's "selected" account. -::: - -## Disconnect a user's accounts - -Since `eth_requestAccounts` internally calls `wallet_requestPermissions` for permission to call -`eth_accounts`, you can use [`wallet_revokePermissions`](/wallet/reference/json-rpc-methods/wallet_revokepermissions) -to revoke this permission, revoking your dapp's access to the user's accounts. - -This is useful as a method for users to log out (or disconnect) from your dapp. -You can then use [`wallet_getPermissions`](/wallet/reference/json-rpc-methods/wallet_getpermissions) to determine -whether the user is connected or disconnected to your dapp. - -See [how to revoke permissions](manage-permissions.md#revoke-permissions-example) for an example. diff --git a/wallet/how-to/connect-extension.md b/wallet/how-to/connect-extension.md deleted file mode 100644 index 9e4b29f08c9..00000000000 --- a/wallet/how-to/connect-extension.md +++ /dev/null @@ -1,453 +0,0 @@ ---- -description: Connect to the MetaMask extension using the Wallet API and EIP-6963. -toc_max_heading_level: 4 -keywords: [extension, API] ---- - -# Connect to the MetaMask extension - -:::tip Building a cross-platform or mobile dapp? -For cross-platform development, mobile integration, or advanced features like QR codes and -deeplinking, connect to MetaMask using [**MetaMask SDK**](/sdk) instead. -::: - -You can connect your dapp to users' MetaMask wallets by detecting MetaMask in their browsers and -connecting to their accounts. -This page provides instructions for connecting to MetaMask using the wallet detection mechanism -introduced by [EIP-6963](../concepts/wallet-interoperability.md). -This approach allows you to detect multiple installed wallets and connect to them without conflicts. - -You can connect to the MetaMask browser extension [using third-party libraries](#connect-to-metamask-using-third-party-libraries) -or [directly using Vite](#connect-to-metamask-directly-using-vite). - -:::note -Using the Wallet API enables your dapp to work both with the MetaMask extension and in the in-app browser of the MetaMask mobile app. -However, we recommend using the [SDK](/sdk) for a more consistent mobile connection. -::: - -## Connect to MetaMask using third-party libraries - -You can connect to MetaMask using the following third-party libraries that support EIP-6963: - -- [Wagmi 2+](/sdk/connect/javascript-wagmi) -- [Reown AppKit](https://docs.reown.com/appkit/overview) -- [MIPD Store](https://github.com/wevm/mipd) -- [RainbowKit](https://rainbowkit.com/docs/introduction) -- [Web3-Onboard](https://web3onboard.thirdweb.com) -- [ConnectKit](https://docs.family.co/connectkit) - -## Connect to MetaMask directly using Vite - -To connect to MetaMask directly, we recommend implementing support for EIP-6963 using the -[Vite](https://vitejs.dev/) build tool with [vanilla TypeScript](#vanilla-typescript) or -[React TypeScript](#react-typescript). - -### Vanilla TypeScript - -Follow these steps for creating a vanilla TypeScript project to connect to MetaMask: - -#### 1. Create a project - -[Create a Vite project](https://v3.vitejs.dev/guide/#scaffolding-your-first-vite-project) using the -template for vanilla TypeScript: - -```bash -npm create vite@latest vanilla-ts-6963 -- --template vanilla-ts -``` - -#### 2. Set up the project - -In your Vite project, update `src/vite-env.d.ts` with the -[EIP-6963 interfaces](../concepts/wallet-interoperability.md#eip-6963-interfaces): - -```typescript title="vite-env.d.ts" -/// - -interface EIP6963ProviderInfo { - rdns: string - uuid: string - name: string - icon: string -} - -interface EIP6963ProviderDetail { - info: EIP6963ProviderInfo - provider: EIP1193Provider -} - -type EIP6963AnnounceProviderEvent = { - detail: { - info: EIP6963ProviderInfo - provider: Readonly - } -} - -interface EIP1193Provider { - isStatus?: boolean - host?: string - path?: string - sendAsync?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - send?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - request: (request: { - method: string - params?: Array - }) => Promise -} -``` - -:::note -In addition to the EIP-6963 interfaces, you need a `EIP1193Provider` interface (defined by -[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193)), which is the foundational structure for -Ethereum wallet providers, and represents the essential properties and methods for interacting with -MetaMask and other Ethereum wallets in JavaScript. -::: - -#### 3. Update `main.ts` - -Update `src/main.ts` with the following code: - -```typescript title="main.ts" -import "./style.css" -import { listProviders } from "./providers.ts" - -document.querySelector("#app")!.innerHTML = ` -
    -
    -
    -` - -listProviders(document.querySelector("#providerButtons")!) -``` - -The `querySelector` finds and returns the first HTML element that matches the CSS selector `app`, -and sets its `innerHTML`. -You need to include a basic HTML structure with an inner `div` to inject a list of buttons, each -representing a detected wallet provider. - -You'll create the `listProviders` function in the next step, and pass an argument which represents -the `div` element. - -#### 4. Connect to wallets - -Create a file `src/providers.ts` with the following code: - -```ts title="providers.ts" -declare global { - interface WindowEventMap { - "eip6963:announceProvider": CustomEvent - } -} - -// Connect to the selected provider using eth_requestAccounts. -const connectWithProvider = async ( - wallet: EIP6963AnnounceProviderEvent["detail"] -) => { - try { - await wallet.provider.request({ method: "eth_requestAccounts" }) - } catch (error) { - console.error("Failed to connect to provider:", error) - } -} - -// Display detected providers as connect buttons. -export function listProviders(element: HTMLDivElement) { - window.addEventListener( - "eip6963:announceProvider", - (event: EIP6963AnnounceProviderEvent) => { - const button = document.createElement("button") - - button.innerHTML = ` - ${event.detail.info.name} -
    ${event.detail.info.name}
    - ` - - // Call connectWithProvider when a user selects the button. - button.onclick = () => connectWithProvider(event.detail) - element.appendChild(button) - } - ) - - // Notify event listeners and other parts of the dapp that a provider is requested. - window.dispatchEvent(new Event("eip6963:requestProvider")) -} -``` - -The `connectWithProvider` function connects the user to the selected provider using -[`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts). -The `wallet` object is passed as an argument to the function, indicating the argument type. - -The `listProviders` function uses a simplified approach. -Instead of mapping and joining an entire block of HTML, it directly passes the `event.detail` object -to the `connectWithProvider` function when a provider is announced. - -#### 5. View the project - -Run the following command to view and test the Vite project in your browser: - -```bash -npm run dev -``` - -#### Example - -See the [vanilla TypeScript example](https://github.com/MetaMask/vite-vanilla-ts-eip-6963) for more information. -You can clone the repository and run the example locally using `npm i && npm run dev`. - -### React TypeScript - -Follow these steps for creating a React TypeScript project to connect to MetaMask: - -#### 1. Create a project - -[Create a Vite project](https://v3.vitejs.dev/guide/#scaffolding-your-first-vite-project) using the -template for React TypeScript: - -```bash -npm create vite@latest react-ts-6963 -- --template react-ts -``` - -#### 2. Set up the project - -In your Vite project, update `src/vite-env.d.ts` with the -[EIP-6963 interfaces](../concepts/wallet-interoperability.md#eip-6963-interfaces): - -```typescript title="vite-env.d.ts" -/// - -interface EIP6963ProviderInfo { - rdns: string - uuid: string - name: string - icon: string -} - -interface EIP6963ProviderDetail { - info: EIP6963ProviderInfo - provider: EIP1193Provider -} - -type EIP6963AnnounceProviderEvent = { - detail: { - info: EIP6963ProviderInfo - provider: Readonly - } -} - -interface EIP1193Provider { - isStatus?: boolean - host?: string - path?: string - sendAsync?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - send?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - request: (request: { - method: string - params?: Array - }) => Promise -} -``` - -:::note -In addition to the EIP-6963 interfaces, you need a `EIP1193Provider` interface (defined by -[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193)), which is the foundational structure for -Ethereum wallet providers, and represents the essential properties and methods for interacting with -MetaMask and other Ethereum wallets in JavaScript. -::: - -#### 3. Update `App.tsx` - -Update `src/App.tsx` with the following code: - -```ts title="App.tsx" -import "./App.css" -import { DiscoverWalletProviders } from "./components/WalletProviders" - -function App() { - return ( - - ) -} - -export default App -``` - -This code renders the `WalletProviders` component that you'll create in the next step, which -contains the logic for detecting and connecting to wallet providers. - -#### 4. Detect and connect to wallets - -Create a `src/components` directory and add a file `WalletProviders.tsx` with the following code: - -```ts title="WalletProviders.tsx" -import { useState } from "react" -import { useSyncProviders } from "../hooks/useSyncProviders" -import { formatAddress } from "../utils" - -export const DiscoverWalletProviders = () => { - const [selectedWallet, setSelectedWallet] = useState() - const [userAccount, setUserAccount] = useState("") - const providers = useSyncProviders() - - // Connect to the selected provider using eth_requestAccounts. - const handleConnect = async (providerWithInfo: EIP6963ProviderDetail) => { - const accounts: string[] | undefined = - await ( - providerWithInfo.provider - .request({ method: "eth_requestAccounts" }) - .catch(console.error) - ) as string[] | undefined; - - if (accounts?.[0]) { - setSelectedWallet(providerWithInfo) - setUserAccount(accounts?.[0]) - } - } - - // Display detected providers as connect buttons. - return ( - <> -

    Wallets Detected:

    -
    - { - providers.length > 0 ? providers?.map((provider: EIP6963ProviderDetail) => ( - - )) : -
    - No Announced Wallet Providers -
    - } -
    -
    -

    {userAccount ? "" : "No "}Wallet Selected

    - {userAccount && -
    -
    - {selectedWallet.info.name} -
    {selectedWallet.info.name}
    -
    ({formatAddress(userAccount)})
    -
    -
    - } - - ) -} -``` - -In this code: - -- `selectedWallet` is a state variable that holds the user's most recently selected wallet. -- `userAccount` is a state variable that holds the user's connected wallet's address. -- `useSyncProviders` is a custom hook that returns the providers array (wallets installed in the browser). - -The `handleConnect` function takes a `providerWithInfo`, which is an `EIP6963ProviderDetail` object. -That object is used to request the user's accounts from the provider using -[`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts). - -If the request succeeds, the `selectedWallet` and `userAccount` local state variables are set. - -Then, the component maps over the providers array and renders a button for each detected provider. - -Finally, if the `userAccount` state variable is not empty, the selected wallet icon, name, and -address are displayed. - -#### 5. Add React hooks - -Create a `src/hooks` directory and add a file `store.ts` with the following code: - -```ts title="hooks/store.ts" -declare global { - interface WindowEventMap { - "eip6963:announceProvider": CustomEvent - } -} - -// An array to store the detected wallet providers. -let providers: EIP6963ProviderDetail[] = [] - -export const store = { - value: () => providers, - subscribe: (callback: () => void) => { - function onAnnouncement(event: EIP6963AnnounceProviderEvent) { - if (providers.map((p) => p.info.uuid).includes(event.detail.info.uuid)) - return - providers = [...providers, event.detail] - callback() - } - - // Listen for eip6963:announceProvider and call onAnnouncement when the event is triggered. - window.addEventListener("eip6963:announceProvider", onAnnouncement) - - // Dispatch the event, which triggers the event listener in the MetaMask wallet. - window.dispatchEvent(new Event("eip6963:requestProvider")) - - // Return a function that removes the event listener. - return () => - window.removeEventListener("eip6963:announceProvider", onAnnouncement) - }, -} -``` - -Also, add a file `useSyncProviders.ts` with the following code to the `hooks` directory: - -```ts title="hooks/useSyncProviders.ts" -import { useSyncExternalStore } from "react" -import { store } from "./store" - -export const useSyncProviders = () => - useSyncExternalStore(store.subscribe, store.value, store.value) -``` - -This hook allows you to subscribe to MetaMask events, read updated values, and update components. -It uses the `store.value` and `store.subscribe` methods defined in the `store.ts` hook. - -#### 6. Create utility functions - -Create a `src/utils` directory and add a file `index.ts` with the following code: - -```ts title="index.ts" -export const formatBalance = (rawBalance: string) => { - const balance = (parseInt(rawBalance) / 1000000000000000000).toFixed(2) - return balance -} - -export const formatChainAsNum = (chainIdHex: string) => { - const chainIdNum = parseInt(chainIdHex) - return chainIdNum -} - -export const formatAddress = (addr: string) => { - const upperAfterLastTwo = addr.slice(0, 2) + addr.slice(2) - return `${upperAfterLastTwo.substring(0, 5)}...${upperAfterLastTwo.substring(39)}` -} -``` - -This is a good place to store utility functions that you might need to reuse throughout your dapp. -This example only uses the `formatAddress` function, but the others might be useful for other applications. - -#### Example - -See the [React TypeScript example](https://github.com/MetaMask/vite-react-ts-eip-6963) for more information. -You can clone the repository and run the example locally using `npm i && npm run dev`. - -### Next steps - -After connecting to MetaMask directly, you can: - -- [Detect, add, and switch networks](manage-networks/detect-network.md). -- [Send transactions](send-transactions/index.md). -- [Sign data](sign-data/index.md). -- [Display tokens, contract methods, and icons in MetaMask](display/tokens.md). diff --git a/wallet/how-to/display/icon.md b/wallet/how-to/display/icon.md deleted file mode 100644 index 9f840f324dd..00000000000 --- a/wallet/how-to/display/icon.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: Set an icon on MetaMask for your dapp. ---- - -# Display a dapp icon - -When your dapp makes a login request to a MetaMask user, MetaMask may render a modal that displays -your dapp icon. - -MetaMask retrieves this icon using the HTML selector ` link[rel="shortcut icon"]`, so you can -follow the [favicon standard](https://en.wikipedia.org/wiki/Favicon) to customize your dapp icon. -Make sure to have a `link` tag within your dapp's `head` with `rel = "shortcut icon"`, as in the -following example. -The tag's `href` attribute is used for assigning the dapp icon. - -```html - - - -``` diff --git a/wallet/how-to/display/method-names.md b/wallet/how-to/display/method-names.md deleted file mode 100644 index 406f0e642eb..00000000000 --- a/wallet/how-to/display/method-names.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Register a contract's method names with users. ---- - -# Display a contract's method names - -MetaMask uses the [Ethereum Signature Database](https://www.4byte.directory/) to display -method names on the confirmation screen. -For many common method names, such as token methods, this allows MetaMask to look up the method -names by their [method signature](https://solidity.readthedocs.io/en/v0.4.21/abi-spec.html). -However, sometimes you're using a method that isn't in that database, and MetaMask simply -displays **Contract Interaction** to the user. - -To register your contract's method names so they show in the MetaMask interface, -[submit each method's signature to the Ethereum Signature Database](https://www.4byte.directory/submit/). diff --git a/wallet/how-to/manage-networks/add-network.md b/wallet/how-to/manage-networks/add-network.md deleted file mode 100644 index 15283acf352..00000000000 --- a/wallet/how-to/manage-networks/add-network.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -description: Prompt a user to add or switch to an Ethereum network. ---- - -# Add a network - -In some cases, such as when [interacting with smart contracts](../../concepts/smart-contracts.md), -your dapp must connect a user to a new network in MetaMask. -Instead of the user [adding a new network manually](https://support.metamask.io/configure/networks/how-to-add-a-custom-network-rpc/#adding-a-network-manually), -which requires them to configure RPC URLs and chain IDs, your dapp can use the -[`wallet_addEthereumChain`](/wallet/reference/json-rpc-methods/wallet_addethereumchain) and -[`wallet_switchEthereumChain`](/wallet/reference/json-rpc-methods/wallet_switchethereumchain) RPC methods to prompt -the user to add a specific, pre-configured network to their MetaMask wallet. - -These methods are specified by [EIP-3085](https://eips.ethereum.org/EIPS/eip-3085) and -[EIP-3326](https://eips.ethereum.org/EIPS/eip-3326), and we recommend using them together. - -1. `wallet_addEthereumChain` creates a confirmation asking the user to add the specified network to MetaMask. -2. `wallet_switchEthereumChain` creates a confirmation asking the user to switch to the specified network. - -The confirmations look like the following: - -
    -
    - Add network confirmation -
    -
    - Switch network confirmation -
    -
    - -:::info Development and non-EVM networks -- To add a local development network such as [Hardhat](https://hardhat.org) to MetaMask, see [Run a development network](../run-devnet.md). -- To add a non-EVM network such as [Starknet](../use-non-evm-networks/starknet/index.md) to MetaMask, - see [Use non-EVM networks](/wallet/how-to/use-non-evm-networks). -::: - -## Example - -The following is an example of using `wallet_addEthereumChain` and `wallet_switchEthereumChain` to -prompt a user to add and switch to a new network: - -```javascript -try { - await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_switchEthereumChain", - params: [{ chainId: "0xf00" }], - }) -} catch (switchError) { - // This error code indicates that the chain has not been added to MetaMask. - if (switchError.code === 4902) { - try { - await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_addEthereumChain", - params: [ - { - chainId: "0xf00", - chainName: "...", - rpcUrls: ["https://..."] /* ... */, - }, - ], - }) - } catch (addError) { - // Handle "add" error. - } - } - // Handle other "switch" errors. -} -``` diff --git a/wallet/how-to/manage-networks/detect-network.md b/wallet/how-to/manage-networks/detect-network.md deleted file mode 100644 index 46c7d85c07e..00000000000 --- a/wallet/how-to/manage-networks/detect-network.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: Detect a user's network and network changes. ---- - -# Detect a user's network - -It's important to keep track of the user's network chain ID because all RPC requests are submitted -to the currently connected network. - -Use the [`eth_chainId`](/wallet/reference/json-rpc-methods/eth_chainid) -RPC method to detect the chain ID of the user's current network. -Listen to the [`chainChanged`](../../reference/provider-api.md#chainchanged) provider event to -detect when the user changes networks. - -For example, the following code detects a user's network and when the user changes networks: - -```javascript title="index.js" -const chainId = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ method: "eth_chainId" }) - -provider // Or window.ethereum if you don't support EIP-6963. - .on("chainChanged", handleChainChanged) - -function handleChainChanged(chainId) { - // We recommend reloading the page, unless you must do otherwise. - window.location.reload() -} -``` diff --git a/wallet/how-to/manage-networks/use-multichain.md b/wallet/how-to/manage-networks/use-multichain.md deleted file mode 100644 index 6e6c03197dd..00000000000 --- a/wallet/how-to/manage-networks/use-multichain.md +++ /dev/null @@ -1,233 +0,0 @@ ---- -description: Interact with multiple networks simultaneously using the Multichain API. -toc_max_heading_level: 4 ---- - -# Interact with multiple networks simultaneously - -:::tip Experimental -The Multichain API is an experimental feature. -::: - -You can use the Multichain API to interact with multiple blockchain networks in MetaMask simultaneously. -The API allows you to target specific chains as part of each method call, eliminating the need to -detect and switch networks before executing signatures and transactions. - -:::note See also -- [About the Multichain API](../../concepts/multichain-api.md) -- [Multichain API reference](../../reference/multichain-api.md) -::: - -## Prerequisites - -[Install MetaMask Flask.](/snaps/get-started/install-flask) - -## Steps - -### 1. Set up your project - -Establish a connection to MetaMask Flask and set up basic message handling using the -[`wallet_notify`](../../reference/multichain-api.md#wallet_notify) event: - -```javascript -// Initialize the connection to Flask. -const EXTENSION_ID = "ljfoeinjpaedjfecbmggjgodbgkmjkjk"; // Flask extension ID (Chrome) -const extensionPort = chrome.runtime.connect(EXTENSION_ID) - -// Set up a message listener for events. -extensionPort.onMessage.addListener((msg) => { - if (msg.data.method === "wallet_notify") { - console.log("wallet_notify:", msg.data.params) - return; - } - console.log(msg.data) -}) -``` - -### 2. Manage multichain connections - -To interact with multiple networks simultaneously, you'll create and manage -[CAIP-25](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-25.md) multichain connections -with MetaMask. - -#### 2.1. Check existing connections - -Before creating a new connection, check if one already exists using the -[`wallet_getSession`](../../reference/multichain-api.md#wallet_getsession) method. -For example: - -```javascript -extensionPort.postMessage({ - type: "caip-348", - data: { - jsonrpc: "2.0", - method: "wallet_getSession", - params: {} - } -}); -``` - -If the result returns an empty `sessionScopes` parameter, then a multichain connection is not active -and you must create a new connection. - -#### 2.2. Create a new connection - -Create a new connection using the [`wallet_createSession`](../../reference/multichain-api.md#wallet_createsession) method. -Specify which chains and methods your dapp needs access to, using the `optionalScopes` parameter. -For example: - -```javascript -extensionPort.postMessage({ - type: "caip-348", - data: { - jsonrpc: "2.0", - method: "wallet_createSession", - params: { - optionalScopes: { - "eip155:1": { // Ethereum Mainnet - methods: [ - "personal_sign", - "eth_blockNumber", - "eth_gasPrice", - "eth_getBalance", - "eth_getTransactionCount", - "eth_sendTransaction", - "eth_subscribe" - ], - notifications: ["eth_subscription"], - accounts: [] - }, - "eip155:59141": { // Linea Sepolia - methods: [ - "personal_sign", - "eth_blockNumber", - "eth_gasPrice", - "eth_getBalance", - "eth_getTransactionCount", - "eth_sendTransaction", - "eth_subscribe" - ], - notifications: ["eth_subscription"], - accounts: [] - } - } - } - } -}); -``` - -In `optionalScopes`: - -- Request access to networks that your dapp intends to interact with. - If a requested network is not configured by the MetaMask user, you might need to - [add the network](add-network.md). -- For each network, request access to [Wallet API methods](../../reference/json-rpc-methods/index.md) - that your dapp expects to call at any time. - The methods listed in the `sessionScope` of each Multichain API response indicate which wallet - capabilities your dapp can use during the connection. - -#### 2.3. Check for connection changes - -To ensure your dapp responds appropriately to changes in the multichain connection, such as network or -account updates, check for connection changes using the -[`wallet_sessionChanged`](../../reference/multichain-api.md#wallet_sessionchanged) event. -Based on the event data, you can determine whether your dapp needs to request additional permissions -using [`wallet_createSession`](../../reference/multichain-api.md#wallet_createsession). - -```javascript -extensionPort.onMessage.addListener((msg) => { - // Check for wallet_sessionChanged events. - if (msg.data.method === "wallet_sessionChanged") { - // Update permissions if required. - } -}); -``` - -### 3. Invoke Wallet API methods - -You can invoke a subset of the [Wallet JSON-RPC API methods](../../reference/json-rpc-methods/index.md) -on a specified chain using the [`wallet_invokeMethod`](../../reference/multichain-api.md#wallet_invokemethod) -Multichain API method. -The following are example Wallet API functionalities that are compatible with the Multichain API. - -#### 3.1. Sign in with Ethereum - -You can implement Sign-In with Ethereum (SIWE) by invoking -[`personal_sign`](/wallet/reference/json-rpc-methods/personal_sign). -For example: - -```javascript -// Specify an account that the signature will be requested for. -const address = "0xAddress"; -const message = `Sign-in request for ${address} at ${new Date().toLocaleString()}`; - -// Invoke the personal_sign Wallet API method. -const sign = await extensionPort.postMessage({ - type: "caip-348", - data: { - "jsonrpc": "2.0", - method: "wallet_invokeMethod", - params: { - scope: "eip155:1", - request: { - method: "personal_sign", - params: [message, address], - } - } - } -}) -``` - -#### 3.2. Check balances - -You can read gas token balances by invoking -[`eth_getBalance`](/wallet/reference/json-rpc-methods/personal_sign). -For example: - -```javascript -extensionPort.postMessage({ - type: "caip-348", - data: { - jsonrpc: "2.0", - method: "wallet_invokeMethod", - params: { - scope: "eip155:1", - request: { - method: "eth_getBalance", - params: ["0xAddress", "latest"], - } - } - } -}); -``` - -#### 3.3. Send transactions - -You can send transactions on a specific network, by invoking -[`eth_sendTransaction`](/wallet/reference/json-rpc-methods/eth_sendtransaction). -For example: - -```javascript -return extensionPort.postMessage({ - type: "caip-348", - data: { - jsonrpc: "2.0", - method: "wallet_invokeMethod", - params: { - // Specify a chain ID where the user has sufficient gas. - scope: "eip155:1", // Ethereum Mainnet - request: { - method: "eth_sendTransaction", - params: [{ - from: "0xFromAccount", - to: "0xToAccount", - value: "0x0", - gasLimit: "0x5028", - maxPriorityFeePerGas: "0x3b9aca00", - maxFeePerGas: "0x2540be400", - }] - } - } - } -}); -``` diff --git a/wallet/how-to/manage-permissions.md b/wallet/how-to/manage-permissions.md deleted file mode 100644 index b51ecb1c236..00000000000 --- a/wallet/how-to/manage-permissions.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -description: Request and revoke permissions to call restricted methods. ---- - -# Manage permissions - -To call a restricted RPC method, your dapp must request permission from the user using -the [`wallet_requestPermissions`](/wallet/reference/json-rpc-methods/wallet_requestpermissions) RPC method. -You can get the user's current permissions using [`wallet_getPermissions`](/wallet/reference/json-rpc-methods/wallet_getpermissions), -and revoke permissions previously granted to your dapp using -[`wallet_revokePermissions`](/wallet/reference/json-rpc-methods/wallet_revokepermissions). -These methods are specified by [EIP-2255](https://eips.ethereum.org/EIPS/eip-2255) and -[MIP-2](https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-2.md). - -`wallet_requestPermissions` creates a confirmation asking the user to connect to an account and -allow the dapp to call the requested method. -The confirmation screen describes the functions and data the requested method can access. -For example, something like the following confirmation displays when you request permission to call -the restricted method [`eth_accounts`](/wallet/reference/json-rpc-methods/eth_accounts): - -
    -
    - Request permissions confirmation 1 -
    -
    - Request permissions confirmation 2 -
    -
    - -:::info note -To access accounts, we recommend using [`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts), -which automatically asks for permission to use `eth_accounts` by calling `wallet_requestPermissions` -internally. -See [how to access a user's accounts](access-accounts.md) for more information. -Granting permission for `eth_accounts` also grants access to [`eth_sendTransaction`](/wallet/reference/json-rpc-methods/eth_sendtransaction), [`personal_sign`](/wallet/reference/json-rpc-methods/personal_sign), and [`eth_signTypedData_v4`](/wallet/reference/json-rpc-methods/eth_signtypeddata_v4). -::: - -## Request permissions example - -The following example uses `wallet_requestPermissions` to request permission from the user to call `eth_accounts`: - -```javascript -document.getElementById("requestPermissionsButton", requestPermissions) - -function requestPermissions() { - provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_requestPermissions", - params: [{ eth_accounts: {} }], - }) - .then((permissions) => { - const accountsPermission = permissions.find( - (permission) => permission.parentCapability === "eth_accounts" - ) - if (accountsPermission) { - console.log("eth_accounts permission successfully requested!") - } - }) - .catch((error) => { - if (error.code === 4001) { - // EIP-1193 userRejectedRequest error - console.log("Permissions needed to continue.") - } else { - console.error(error) - } - }) -} -``` - -## Revoke permissions example - -The following example uses `wallet_revokePermissions` to revoke the dapp's permission to call `eth_accounts`: - -```javascript -await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_revokePermissions", - params: [ - { - eth_accounts: {}, - }, - ], - }) -``` diff --git a/wallet/how-to/onboard-users.md b/wallet/how-to/onboard-users.md deleted file mode 100644 index 196e7d06bcf..00000000000 --- a/wallet/how-to/onboard-users.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -description: Simplify the MetaMask onboarding experience for your users. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Use the MetaMask onboarding library - -Sending users away from your dapp to install MetaMask presents challenges. -You must inform the user to return to your dapp and refresh their browser after the installation. -Your dapp detects the user's newly installed MetaMask extension only after that refresh. - -You can use MetaMask's [onboarding library](https://github.com/MetaMask/metamask-onboarding) to -improve and simplify the onboarding experience. -The library exposes an API to initiate the onboarding process. - -During the onboarding process, the library registers your dapp as the origin of the onboarding request. -MetaMask checks for this origin after the user completes the onboarding flow. -If it finds an origin, the final confirmation button of the MetaMask onboarding flow indicates that -the user will be redirected back to your dapp. - -:::tip -[MetaMask SDK](/sdk) incorporates the functionality of the MetaMask onboarding library. -You don't need to set up the onboarding library if you use the SDK. -::: - -## Steps - -1. Install [`@metamask/onboarding`](https://github.com/MetaMask/metamask-onboarding). -1. Import the library or include it in your page: - - ```javascript - // As an ES6 module - import MetaMaskOnboarding from "@metamask/onboarding" - // Or as an ES5 module - const MetaMaskOnboarding = require("@metamask/onboarding") - ``` - - Alternatively, you can include the prebuilt ES5 bundle that ships with the library: - - ```html - - ``` - -1. Create a new instance of the onboarding library: - - ```javascript - const onboarding = new MetaMaskOnboarding() - ``` - -1. Start the onboarding process in response to a user event (for example, a button click): - - ```javascript - onboarding.startOnboarding() - ``` - -## Example - -The following are example ways to use the onboarding library in various frameworks: - - - - -```jsx -import MetaMaskOnboarding from "@metamask/onboarding" -import React from "react" - -const ONBOARD_TEXT = "Click here to install MetaMask!" -const CONNECT_TEXT = "Connect" -const CONNECTED_TEXT = "Connected" - -export function OnboardingButton() { - const [buttonText, setButtonText] = React.useState(ONBOARD_TEXT) - const [isDisabled, setDisabled] = React.useState(false) - const [accounts, setAccounts] = React.useState([]) - const onboarding = React.useRef() - - React.useEffect(() => { - if (!onboarding.current) { - onboarding.current = new MetaMaskOnboarding() - } - }, []) - - React.useEffect(() => { - if (MetaMaskOnboarding.isMetaMaskInstalled()) { - if (accounts.length > 0) { - setButtonText(CONNECTED_TEXT) - setDisabled(true) - onboarding.current.stopOnboarding() - } else { - setButtonText(CONNECT_TEXT) - setDisabled(false) - } - } - }, [accounts]) - - React.useEffect(() => { - function handleNewAccounts(newAccounts) { - setAccounts(newAccounts) - } - if (MetaMaskOnboarding.isMetaMaskInstalled()) { - provider // Or window.ethereum if you don't support EIP-6963. - .request({ method: "eth_requestAccounts" }) - .then(handleNewAccounts) - provider // Or window.ethereum if you don't support EIP-6963. - .on("accountsChanged", handleNewAccounts) - return () => { - provider // Or window.ethereum if you don't support EIP-6963. - .removeListener("accountsChanged", handleNewAccounts) - } - } - }, []) - - const onClick = () => { - if (MetaMaskOnboarding.isMetaMaskInstalled()) { - provider // Or window.ethereum if you don't support EIP-6963. - .request({ method: "eth_requestAccounts" }) - .then((newAccounts) => setAccounts(newAccounts)) - } else { - onboarding.current.startOnboarding() - } - } - return ( - - ) -} -``` - - - - -The onboarding library ships with MetaMask's TypeScript types. -Modify the React example as follows to get type safety: - -```jsx --const onboarding = React.useRef(); -+const onboarding = React.useRef(); -``` - -This gives you editor autocomplete for the methods exposed by the library, and -helpful documentation: - -![Editor Highlighting](https://user-images.githubusercontent.com/4448075/85584481-ccc7ec00-b604-11ea-9b74-49c76ee0bf22.png) - - - - -```html - - - - MetaMask Onboarding Example - - - -

    Sample Dapp

    - - - - - -``` - -
    -
    diff --git a/wallet/how-to/secure-dapp.md b/wallet/how-to/secure-dapp.md deleted file mode 100644 index b3185fe181c..00000000000 --- a/wallet/how-to/secure-dapp.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -description: Secure your dapp using HTTPS and CSP. ---- - -# Secure your dapp - -We recommend implementing security controls, such as [HTTPS](#use-https) and -[Content Security Policy (CSP)](#use-content-security-policy), to improve the security of your dapp -and protect your users. - -:::caution -The following security advice isn't exhaustive. -::: - -## Use HTTPS - -HTTPS can protect your dapp against attackers who might try to eavesdrop or tamper with the communication -channel between your dapp and your users. -HTTPS encrypts data transmitted between the web server and the user's browser, making it -difficult for attackers to intercept or modify the data. - -To secure your dapp using HTTPS, obtain an SSL/TLS certificate from a trusted certificate authority (CA). -For example, [Let's Encrypt](https://letsencrypt.org/) offers free SSL/TLS certificates. - -Install the certificate on your web server. -If you're using a static website hosting service, it might have a default way to enable HTTPS on -your dapp. - -## Use Content Security Policy - -Content Security Policy (CSP) is a security feature that can protect your dapp against various -types of attacks, such as [cross-site scripting (XSS)](https://owasp.org/www-community/attacks/xss/) -and [clickjacking](https://owasp.org/www-community/attacks/Clickjacking). - -CSP defines a set of policies that the browser must follow when displaying the dapp. -See the full list of CSP directives that you can enable for your dapp in the -[MDN CSP reference documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy). - -### Use CSP with a server setup - -If your dapp uses a server setup, enable CSP by setting the `Content-Security-Policy` header in all -responses from your server. -For example, in Express.js, add the following middleware at the top of your server: - -```js -app.use((req, res, next) => { - res.setHeader( - "Content-Security-Policy", - "default-src 'self'; frame-ancestors 'none'" - ) - next() -}) -``` - -In a header, this looks like the following: - -``` -Content-Security-Policy: default-src 'self'; frame-ancestors 'none' -``` - -See [more examples](https://content-security-policy.com/examples/) of CSP in popular web frameworks -and languages. - -### Use CSP with a static site - -If your dapp uses a third-party hosting provider, and you can't set a custom -`Content-Security-Policy` header in the server responses, you can enable CSP by using the -[`` HTML tag](https://content-security-policy.com/examples/meta/). - -Add this tag to the `head` section of an HTML file to instruct the browser on which CSP directives -should be followed. -For example: - -```html - - - -``` - -### Configure your CSP - -CSP configuration is specific to each dapp. -We recommend starting with the following secure and restrictive CSP: - -```text -default-src 'self'; frame-ancestors 'none' -``` - -- `default-src 'self'` - By default, your dapp's code can't load or connect to content from outside - your domain. -- `frame-ancestors 'none'` - Your dapp can't be embedded within the webpage of another domain (to - prevent [clickjacking attacks](https://owasp.org/www-community/attacks/Clickjacking)). - -From here, you can make adjustments as needed by your dapp to support the content you want to load. -For example, if your dapp loads images hosted on [OpenSea](https://opensea.io/), you can enable this -by adding `img-src 'opensea.io'` to your CSP: - -```text -default-src: 'self'; frame-ancestors 'none'; img-src: 'opensea.io' -``` - -For more information and common use cases for CSP, see the -[MDN CSP documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). diff --git a/wallet/how-to/send-transactions/index.md b/wallet/how-to/send-transactions/index.md deleted file mode 100644 index a6db6d52aea..00000000000 --- a/wallet/how-to/send-transactions/index.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -description: Send transactions using `eth_sendTransaction`. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Send transactions - -You can send a transaction in MetaMask using the -[`eth_sendTransaction`](/wallet/reference/json-rpc-methods/eth_sendtransaction) -RPC method. - -:::note -To [send batch transactions](send-batch-transactions.md), use `wallet_sendCalls`. -::: - -For example, the following JavaScript gets the user's accounts and sends a transaction when they -select each button: - -```javascript title="index.js" -const ethereumButton = document.querySelector(".enableEthereumButton"); -const sendEthButton = document.querySelector(".sendEthButton"); - -let accounts = []; - -sendEthButton.addEventListener("click", () => { - provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "eth_sendTransaction", - params: [ - { - from: accounts[0], // The user's active address. - to: "0x0000000000000000000000000000000000000000", // Address of the recipient. Not used in contract creation transactions. - value: "0x0", // Value transferred, in wei. Only required to send ether to the recipient from the initiating external account. - gasLimit: "0x5028", // Customizable by the user during MetaMask confirmation. - maxPriorityFeePerGas: "0x3b9aca00", // Customizable by the user during MetaMask confirmation. - maxFeePerGas: "0x2540be400", // Customizable by the user during MetaMask confirmation. - }, - ], - }) - .then((txHash) => console.log(txHash)) - .catch((error) => console.error(error)); -}); - -ethereumButton.addEventListener("click", () => { - getAccount(); -}); - -async function getAccount() { - accounts = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ method: "eth_requestAccounts" }); -} -``` - -The following HTML displays the buttons: - -```html title="index.html" - - -``` - -## Transaction parameters - -The transaction parameters depend on the [transaction type](/services/concepts/transaction-types). -The following are examples of transaction objects for each type: - - - - -```js -{ - nonce: "0x0", // Number of transactions made by the sender before this one. - gasPrice: "0x09184e72a000", // Gas price, in wei, provided by the sender. - gasLimit: "0x2710", // Maximum gas provided by the sender. - to: "0x0000000000000000000000000000000000000000", // Address of the recipient. Not used in contract creation transactions. - value: "0x0", // Value transferred, in wei. - data: "0x7f7465737432000000000000000000000000000000000000000000000000000000600057", // Used for defining contract creation and interaction. - v: "0x1", // ECDSA recovery ID. - r: "0xa07fd6c16e169f0e54b394235b3a8201101bb9d0eba9c8ae52dbdf556a363388", // ECDSA signature r. - s: "0x36f5da9310b87fefbe9260c3c05ec6cbefc426f1ff3b3a41ea21b5533a787dfc", // ECDSA signature s. -} -``` - - - - -```js -{ - nonce: "0x0", // Number of transactions made by the sender before this one. - gasPrice: "0x09184e72a000", // Gas price, in wei, provided by the sender. - gasLimit: "0x2710", // Maximum gas provided by the sender. - to: "0x0000000000000000000000000000000000000000", // Address of the recipient. Not used in contract creation transactions. - value: "0x0", // Value transferred, in wei. - data: "0x7f7465737432000000000000000000000000000000000000000000000000000000600057", // Used for defining contract creation and interaction. - v: "0x1", // ECDSA recovery ID. - r: "0xa07fd6c16e169f0e54b394235b3a8201101bb9d0eba9c8ae52dbdf556a363388", // ECDSA signature r. - s: "0x36f5da9310b87fefbe9260c3c05ec6cbefc426f1ff3b3a41ea21b5533a787dfc", // ECDSA signature s. - chainId: "0x1", // Chain ID of the transaction. - accessList: [ // List of addresses and storage keys the transaction plans to access. - { - "address": "0xa02457e5dfd32bda5fc7e1f1b008aa5979568150", - "storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000081"] - } - ], - yParity: "0x1" // Parity of the y-value of a secp256k1 signature. -} -``` - - - - -```js -{ - nonce: "0x0", // Number of transactions made by the sender before this one. - gasLimit: "0x2710", // Maximum gas provided by the sender. - maxPriorityFeePerGas: "0x0", // Maximum fee, in wei, the sender is willing to pay per gas above the base fee. - maxFeePerGas: "0x6f4d3132b", // Maximum total fee (base fee + priority fee), in wei, the sender is willing to pay per gas. - to: "0x0000000000000000000000000000000000000000", // Address of the recipient. Not used in contract creation transactions. - value: "0x0", // Value transferred, in wei. - data: "0x7f7465737432000000000000000000000000000000000000000000000000000000600057", // Used for defining contract creation and interaction. - v: "0x1", // ECDSA recovery ID. - r: "0xa07fd6c16e169f0e54b394235b3a8201101bb9d0eba9c8ae52dbdf556a363388", // ECDSA signature r. - s: "0x36f5da9310b87fefbe9260c3c05ec6cbefc426f1ff3b3a41ea21b5533a787dfc", // ECDSA signature s. - chainId: "0x1", // Chain ID of the transaction. - accessList: [], // List of addresses and storage keys the transaction plans to access. - yParity: "0x1" // Parity of the y-value of a secp256k1 signature. -} -``` - - - - -### Nonce - -:::note -MetaMask ignores this field. -::: - -In Ethereum, every transaction has a nonce, so each transaction can only be processed by the -blockchain once. -To be a valid transaction, either: - -- The nonce must be `0`. -- A transaction with a nonce of the previous number, from the same account, must have been processed. - -This means that transactions are always processed in order for a given account. - -Nonces are easy to mess up, especially when a user is interacting with multiple applications with -pending transactions using the same account, potentially across multiple devices. -Because of this, MetaMask doesn't allow dapp developers to customize nonces. -Instead, MetaMask -[assists the user in managing their transaction queue themselves](https://support.metamask.io/manage-crypto/transactions/how-to-speed-up-or-cancel-a-pending-transaction/). - -### Gas price - -`gasPrice` is an optional parameter. -It is used in [legacy transactions](/services/concepts/transaction-types/#legacy-transactions) and specifies the gas price the sender is willing to pay for the transaction. -MetaMask automatically configures gas settings, but [users can also customize these settings](https://support.metamask.io/configure/transactions/how-to-customize-gas-settings/). - -### Gas limit - -`gasLimit` is an optional parameter. -It specifies the maximum amount of gas units the sender is willing to pay for the transaction. -MetaMask automatically sets this parameter, but [users can also customize their gas settings](https://support.metamask.io/configure/transactions/how-to-customize-gas-settings/). - -### Max priority fee per gas - -`maxPriorityFeePerGas` is an optional parameter. -It is used in [EIP-1559 transactions](/services/concepts/transaction-types/#eip-1559-transactions) and specifies the maximum fee the sender is willing to pay per gas above the base fee, in order to get their transaction prioritized. -MetaMask automatically sets this parameter, but [users can also customize their gas settings](https://support.metamask.io/configure/transactions/how-to-customize-gas-settings/). - -### Max fee per gas - -`maxFeePerGas` is an optional parameter. -It is used in [EIP-1559 transactions](/services/concepts/transaction-types/#eip-1559-transactions) and specifies the maximum total fee (base fee + priority fee) the sender is willing to pay per gas. -MetaMask automatically sets this parameter, but [users can also customize their gas settings](https://support.metamask.io/configure/transactions/how-to-customize-gas-settings/). - -### To - -The `to` parameter is a hex-encoded Ethereum address. -It's required for transactions with a recipient (all transactions except for contract creation). - -Contract creation occurs when there is no `to` value but there is a `data` value. - -### Value - -The `value` parameter is a hex-encoded value of the network's native currency to send. -On Mainnet, this is [ether](https://www.ethereum.org/eth), which is denominated in wei. - -These numbers are often far higher precision than native JavaScript numbers, and can cause -unpredictable behavior if not anticipated. -We recommend using [BN.js](https://github.com/indutny/bn.js/) when manipulating -values intended for Ethereum. - -### Data - -The `data` parameter is required for smart contract creation. - -This field is also used for specifying contract methods and their parameters. -See the [Solidity ABI spec](https://solidity.readthedocs.io/en/develop/abi-spec.html) for more -information on how the data is encoded. - -### Chain ID - -:::note -MetaMask ignores this field. -::: - -The chain ID is derived from the user's current selected network. -Use [`eth_chainId`](/wallet/reference/json-rpc-methods/eth_chainid) to get the user's chain ID. -If you need the network version, use [`net_version`](https://ethereum.org/en/developers/docs/apis/json-rpc/#net_version). - -In the future, MetaMask might allow connecting to multiple networks at the same time, at which point -this parameter will become important, so it might be useful to be in the habit of including it now. diff --git a/wallet/how-to/use-non-evm-networks/index.md b/wallet/how-to/use-non-evm-networks/index.md deleted file mode 100644 index 0c4ee034656..00000000000 --- a/wallet/how-to/use-non-evm-networks/index.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: Interact with users' accounts on non-EVM networks. ---- - -import CardList from "@site/src/components/CardList" - -# Use non-EVM networks - -Non-EVM networks are blockchain networks that are not compatible with the Ethereum Virtual Machine (EVM). -MetaMask provides different types of support for some non-EVM networks: - -- **Native support** - MetaMask implements a standard interface for [Solana](solana.md) dapps to natively connect to MetaMask. - Several third party libraries for Solana also detect and handle MetaMask by default. -- **Non-EVM Snaps** - MetaMask provides dedicated non-EVM [Snaps](https://metamask.io/snaps/) that dapps can use to interact with users' non-EVM accounts in MetaMask. - For example, you can connect to [Starknet](starknet/index.md) in this way. - -MetaMask supports the following non-EVM networks: - - - -:::info -See the [full list of available non-EVM Snaps](https://snaps.metamask.io/interoperability). -::: diff --git a/wallet/how-to/use-non-evm-networks/starknet/about-get-starknet.md b/wallet/how-to/use-non-evm-networks/starknet/about-get-starknet.md deleted file mode 100644 index a74e7a524af..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/about-get-starknet.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -description: Learn about how get-starknet interacts with MetaMask. ---- - -# About `get-starknet` - -[`get-starknet`](https://github.com/starknet-io/get-starknet) is a library that simplifies Starknet -network interactions. -It works with the [Starknet Snap](https://snaps.metamask.io/snap/npm/consensys/starknet-snap/) to -enable dapps to interact with users' Starknet accounts in MetaMask. - -When you integrate `get-starknet` into your dapp, it creates a [Starknet -Window Object (SWO)](https://github.com/starknet-io/get-starknet/blob/get-starknet-core%403.3.0/packages/core/src/StarknetWindowObject.ts), which acts as -the connection between the dapp and MetaMask, and manages Starknet interactions. -This allows users to send Starknet transactions, sign Starknet messages, and manage Starknet -accounts within MetaMask, and this functionality can be extended to multiple wallets in the Starknet -ecosystem. - -

    - -```mermaid -flowchart TD - A(Dapp) -->|get-starknet| B(Starknet Window Object) - A -->|wallet_invokeSnap| C(MetaMask / Starknet Snap) - B --> C - B --> D(Other Starknet wallets) - C --> E(Starknet network) - D --> E -```` - -

    - -## How `get-starknet` and MetaMask interact - -A dapp with `get-starknet` installed interacts with MetaMask as follows: - -1. The dapp uses `get-starknet` to request the user connect to MetaMask. - MetaMask automatically requests the user to add the Starknet Snap, if it's not already present. - -1. After the dapp is connected to MetaMask and the Starknet Snap, `get-starknet` receives a Starknet - Window Object (SWO), which represents the MetaMask wallet with Starknet functionality. - -1. The dapp uses `swo.account` to retrieve an [Account object](https://starknetjs.com/docs/API/#account) from the SWO. - This Account object enables the dapp to manage Starknet interactions within MetaMask. - -```mermaid -sequenceDiagram - participant user as End user - participant dapp as Dapp - participant get as get-starknet - participant mm as MetaMask - participant Snap as Starknet Snap - participant network as Starknet network - - dapp->>get: Initialize connection - get->>mm: Request connection - mm->>Snap: Activate - Snap-->>mm: Activated - get->>Snap: Request Starknet account address - Snap-->>mm: Recover account and return Starknet account address - mm-->>get: Return Starknet account address - get-->>dapp: Connection established with SWO return - - dapp->>get: Read blockchain data - get->>network: Query data - network-->>get: Return data - get-->>dapp: Processed data - - dapp->>get: Write transaction - get->>mm: Request write transaction - mm->>Snap: Write transaction - Snap-->>mm: Request confirmation to write transaction - mm-->>user: Request confirmation - user-->>mm: Confirm transaction - mm-->>Snap: Confirm transaction - - alt If the account has deployed - Snap-->>network: Deploying account transaction - end - Snap-->>network: Submit transaction - network-->>Snap: Transaction result - Snap-->>mm: Return transaction result - mm-->>get: Return transaction result - get-->>dapp: Return transaction result -``` - -The `get-starknet` library offers several features that improve how dapps interact with the Starknet -network through MetaMask: - -- The `Account` object uses a specified provider to access data from the Starknet network. -- For transactions, `get-starknet` prepares the data and sends it to MetaMask for signing through - the Starknet Snap. -- `get-starknet` enables the dapp to create contract instances connected to the `AccountInterface`, - allowing smart contract functions to be invoked, with MetaMask handling the signatures. -- `get-starknet` sets up listeners for account and network changes in MetaMask, so the dapp can - subscribe and update its state accordingly. -- `get-starknet` can request network changes through MetaMask, allowing users to switch between - Starknet networks, such as Mainnet and Sepolia testnet. -- `get-starknet` can also request MetaMask to display specific tokens, improving the user experience. diff --git a/wallet/how-to/use-non-evm-networks/starknet/connect-to-starknet.md b/wallet/how-to/use-non-evm-networks/starknet/connect-to-starknet.md deleted file mode 100644 index 1e209780349..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/connect-to-starknet.md +++ /dev/null @@ -1,469 +0,0 @@ ---- -description: Connect your dapp to Starknet in MetaMask. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Connect to Starknet - -Connect your dapp to Starknet in MetaMask by using the -[`get-starknet`](#connect-using-get-starknet) library or the -[`wallet_invokeSnap`](#connect-using-wallet_invokesnap) JSON-RPC method. - -:::warning Important - -We recommend using the `get-starknet` library for most use cases due to its ease of configuration -and multi-wallet support. -See [a comparison of the connection options](index.md). - -::: - -:::tip - -If you're new to Starknet, you can also follow the -[Create a simple Starknet dapp tutorial](create-a-simple-starknet-dapp.md). - -::: - -## Prerequisites - -- [MetaMask installed](https://metamask.io/download/) -- A text editor (for example, [VS Code](https://code.visualstudio.com/)) -- [Node](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) version 20.11 or later -- [Yarn](https://yarnpkg.com/) -- (Optional) A JavaScript or TypeScript React project set up - -:::note - -This connection guide uses [`get-starknet`](https://github.com/starknet-io/get-starknet) version `3.3.0` and `starknet.js` version `6.11.0`. - -::: - -## Connect using `get-starknet` - -### 1. Set up the project - -If you don't have an existing React project set up, you can use -[Create React App](https://create-react-app.dev/) to set up a new React project with TypeScript. -Create a new project named `get-starknet-dapp`: - - - - -```bash -yarn create react-app get-starknet-dapp -``` - - - - -```bash -npm create react-app get-starknet-dapp -``` - - - - -Change into the project directory: - -```bash -cd get-starknet-dapp -``` - -### 2. Add `get-starknet` and `starknet.js` - -Add [`get-starknet`](https://github.com/starknet-io/get-starknet) version `3.3.0` and `starknet.js` -version `6.11.0` to your project's dependencies: - - - - - ```bash - yarn add get-starknet@3.3.0 starknet@6.11.0 - ``` - - - - - - ```bash - npm install get-starknet@3.3.0 starknet@6.11.0 - ``` - - - - -### 3. Connect to the Snap - -Create a `src/components` directory, and add a new file named `WalletConnectButton.js` to the directory. -Add the following code to the file, which handles the connection to the Starknet Snap and displays a button -for users to initiate the wallet connection: - -```javascript title="WalletConnectButton.js" -import React, { useState } from "react"; -import { connect, disconnect } from "get-starknet"; -import { encode } from "starknet"; -function WalletConnectButton() { - const [walletAddress, setWalletAddress] = useState(""); - const [walletName, setWalletName] = useState(""); - const [wallet, setWallet] = useState(""); - const handleDisconnect = async () => { - await disconnect({clearLastWallet: true}); - setWallet(""); - setWalletAddress(""); - setWalletName("") - } - const handleConnect = async () => { - try{ - const getWallet = await connect({ modalMode: "alwaysAsk", modalTheme: "light" }); - await getWallet?.enable({ starknetVersion: "v5" }); - setWallet(getWallet); - const addr = encode.addHexPrefix(encode.removeHexPrefix(getWallet?.selectedAddress ?? "0x").padStart(64, "0")); - setWalletAddress(addr); - setWalletName(getWallet?.name || "") - } - catch(e){ - // Handle user rejection to install MetaMask / the Starknet Snap. - console.log(e) - } - }; - return ( -
    - {!walletAddress && ( - - )} - {walletAddress && ( -
    - -
    -

    Wallet Name: {walletName}

    -

    Wallet Address: {walletAddress}

    -
    -
    - )} -
    - ); -} -export default WalletConnectButton; -``` - -:::note - -This code automatically requests the user to add the Starknet Snap to MetaMask, if it's not already present. -Handle the error if the user rejects the connection request in the `try` / `catch` block of the -`handleConnect` function. - -::: - -Add a new file named `App.js` to the `src` directory, and add the following code to the file to use -the `WalletConnectButton` component: - -```js title="App.js" -import WalletConnectButton from "./components/WalletConnectButton.js" - -function App() { - return ( -
    -
    - -
    -
    - ); -} - -export default App; -``` - -### 4. Start the dapp - -Start the dapp and navigate to it in your browser. - - - - - ```bash - yarn start - ``` - - - - - - ```bash - npm start - ``` - - - - -A **Connect Wallet** button displays. -When a user selects it, `get-starknet` displays a modal that detects MetaMask and allows users to -choose which Starknet wallet to connect to. -When a user connects to MetaMask, `get-starknet` requests the user to connect to the Starknet Snap in MetaMask. - -
    -
    - Starknet wallet modal -
    -
    - Starknet MetaMask connection request -
    -
    - -After the user connects to Starknet, the dapp displays the user's connected wallet and wallet address: - -

    - Connected Starknet dapp -

    - -:::note - -An account can submit transactions only after it's deployed. -It does not deploy immediately upon creation. -Deployment happens during the first [transaction](send-starknet-transactions.md). - -::: - -## Connect using `wallet_invokeSnap` - -Alternatively, you can manage the Snap invocation manually. -Use the [`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) JSON-RPC -method to directly interact with the Starknet Snap. - -:::warning Important - -We recommend using [EIP-6963](../../../concepts/wallet-interoperability.md) for detecting MetaMask when using the `wallet_invokeSnap` approach. -This ensures you can connect to MetaMask and other installed wallets without conflict. - -::: - -:::note - -The [Starknet Snap companion dapp](https://snaps.consensys.io/starknet) serves as a reference implementation and example dapp. It demonstrates how to manually invoke the snap using `wallet_invokeSnap`, and presents potential use cases and UI integration. For more details, see the [source code of the companion dapp's UI](https://github.com/Consensys/starknet-snap/tree/main/packages/wallet-ui). - -::: - -### 1. Connect to the Snap - -Create a `src/utils` directory, and add a new file named `snapHelper.js` to the directory. -In `snapHelper.js`, add a `connect` function and a `callSnap` helper function as follows. -This file handles the interactions with the Starknet Snap: - -```javascript title="snapHelper.js" -const snapId = "npm:starknet-snap"; - -export async function connect() { - await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_requestSnaps", - params: { - [snapId]: {}, - }, - }); -} - -export async function callSnap(method, params) { - try { - const response = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_invokeSnap", - params: { - snapId, - request: { - method, - params, - }, - }, - }); - console.log(`${method} response:`, response); - return response; - } catch (err) { - console.error(`${method} error:`, err); - alert(`${method} error: ${err.message || err}`); - throw err; - } -} -``` - -:::note - -To connect to Starknet, the dapp user must add the Starknet Snap to MetaMask. - -::: - -### 2. Call a specific Snap method - -Use the `callSnap` function to call a specific Snap method. -The following example calls [`starkNet_createAccount`](../../../reference/non-evm-apis/starknet-snap-api.md#starknet_createaccount): - -```javascript -const deploy = false; // Set to true to deploy the actual account. -const addressIndex = 0; // Specify which address to derive. -const chainId = "0x534e5f5345504f4c4941"; // Chain ID of the network to use. - -const accountInfo = await callSnap("starkNet_createAccount", { addressIndex, deploy, chainId }); -``` - -:::note - -An account can submit transactions only after it's deployed. -It does not deploy immediately upon creation. -Deployment happens during the first [transaction](send-starknet-transactions.md). - -::: - -### Examples - -#### HTML and Vanilla JS - -The following is a full example of a simple HTML and Vanilla JavaScript dapp that connects to the -Starknet Snap using `wallet_invokeSnap`. - -It displays a button that, when selected: - -- Connects to Starknet in MetaMask. -- Creates a Starknet account. -- Displays the account address. - -```html - - - - - Connect Starknet Snap - - - -

    - - - -``` - -#### React - -The following is a full example of a simple React component that connects to the Starknet Snap using -`wallet_invokeSnap`. - -```javascript -import React, { useState } from "react"; -const ConnectWallet = () => { - const [accountInfo, setAccountInfo] = useState(''); - const connect = async (snapId) => { - try { - await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_requestSnaps", - params: { - [snapId]: {}, - }, - }); - } catch (err) { - console.error("Snap connection error:", err); - alert(`Error connecting to Snap: ${err.message || err}`); - } - }; - const callSnap = async (snapId, method, params) => { - try { - const response = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_invokeSnap", - params: { - snapId, - request: { - method, - params, - }, - }, - }); - return response; - } catch (err) { - console.error(`${method} problem happened:`, err); - alert(`${method} problem happened: ${err.message || err}`); - } - }; - const handleConnectClick = async () => { - try { - const snapId = "npm:@consensys/starknet-snap"; // Snap ID. - await connect(snapId); - const deploy = false; // Whether to deploy the actual account. - const addressIndex = 0; // The address to derive. - const chainId = "0x534e5f5345504f4c4941"; // Chain ID of the network to use. - const account = await callSnap(snapId, "starkNet_createAccount", { addressIndex, deploy, chainId }); - setAccountInfo(`Connected Starknet Account: ${account.address}`); - } catch (error) { - console.error("Error connecting to Starknet Snap:", error); - } - }; - return ( -
    - -

    {accountInfo}

    -
    - ); -}; -export default ConnectWallet; -``` - -:::note - -See how to [troubleshoot](troubleshoot.md) connection issues when configuring your dapp using -`wallet_invokeSnap`. - -::: - -## Next steps - -After connecting your dapp to Starknet in MetaMask, you can follow these next steps: - -- [Manage Starknet accounts](manage-starknet-accounts.md). -- [Manage Starknet networks](manage-starknet-networks.md). -- Explore the [Starknet Snap API reference](../../../reference/non-evm-apis/starknet-snap-api.md). diff --git a/wallet/how-to/use-non-evm-networks/starknet/create-a-simple-starknet-dapp.md b/wallet/how-to/use-non-evm-networks/starknet/create-a-simple-starknet-dapp.md deleted file mode 100644 index e4336f8e0d8..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/create-a-simple-starknet-dapp.md +++ /dev/null @@ -1,1629 +0,0 @@ ---- -description: Create a simple dapp using get-starknet and React TypeScript. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Create a simple Starknet dapp - -In this tutorial, you'll learn how to set up a React TypeScript dapp that uses the [`get-starknet`](https://github.com/starknet-io/get-starknet) library to connect to MetaMask and display the user's wallet address. -You'll also display the balance of an ERC-20 token and perform a token transfer. - -## Prerequisites - -- [MetaMask installed](https://metamask.io/download/) -- A text editor (for example, [VS Code](https://code.visualstudio.com/)) -- [Node](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) version 20.11 or later -- [Yarn](https://yarnpkg.com/) - -:::note - -This tutorial uses [`get-starknet`](https://github.com/starknet-io/get-starknet) version `3.3.0` and `starknet.js` version `6.11.0`. - -::: - -## 1. Set up the project - -Use [Create React App](https://create-react-app.dev/) to set up a new React project with TypeScript. -Create a new project named `get-starknet-tutorial`: - -```bash -yarn create react-app get-starknet-tutorial --template typescript -``` - -Change into the project directory: - -```bash -cd get-starknet-tutorial -``` - -Configure Yarn to use the `node-module` linker instead of its default linking strategy: - -```bash -yarn config set nodeLinker node-modules -``` - -## 2. Add `get-starknet` and `starknet.js` - -Add [`get-starknet`](https://github.com/starknet-io/get-starknet) version `3.3.0` and `starknet.js` -version `6.11.0` to your project's dependencies: - -```bash -yarn add get-starknet@3.3.0 starknet@6.11.0 -``` - -Your file structure should look similar to the following: - -```text -get-starknet-tutorial/ -├── public/ -│ ├── index.html -│ └── ... -├── src/ -│ ├── App.tsx -│ ├── index.tsx -│ ├── App.css -│ └── ... -└── ... -``` - -## 3. Configure the wallet connection - -### 3.1. Connect to MetaMask - -The `connect` function from `get-starknet` is the primary way to connect your dapp to a user's MetaMask wallet. -It opens a connection to MetaMask and returns an object containing important details about the wallet, including: - -- `name`: The name of the wallet. -- `icon`: The wallet's icon, which displays the wallet's logo. -- `account`: The account object of type `AccountInterface` from `starknet.js`, which contains the wallet's address and provides access to account-specific operations. - -To import the necessary functions and connect to a wallet, add the following code to `src/App.tsx`: - -```typescript title="App.tsx" -import { - type ConnectOptions, - type DisconnectOptions, - connect, - disconnect, -} from "get-starknet" -import { AccountInterface } from "starknet" -import { useState } from "react" - -function App() { - const [walletName, setWalletName] = useState("") - const [walletAddress, setWalletAddress] = useState("") - const [walletIcon, setWalletIcon] = useState("") - const [walletAccount, setWalletAccount] = useState(null) - - async function handleConnect(options?: ConnectOptions) { - const res = await connect(options) - setWalletName(res?.name || "") - setWalletAddress(res?.account?.address || "") - setWalletIcon(res?.icon || "") - setWalletAccount(res?.account) - } - - async function handleDisconnect(options?: DisconnectOptions) { - await disconnect(options) - setWalletName("") - setWalletAddress("") - setWalletAccount(null) - } -} -``` - -### 3.2. Display connection options - -The `connect` function accepts an optional `ConnectOptions` object. -This object can control the connection process, including: - -- `modalMode`: Determines how the connection modal behaves. The options are: - - `alwaysAsk`: Prompts the user every time a connection is initiated. - - `neverAsk`: Attempts to connect without showing the modal. -- `modalTheme`: Sets the visual theme of the connection modal. The options are `"dark"` and `"light"`. - -The `disconnect` function allows users to disconnect their wallet. -You can enable `clearLastWallet` to clear the last connected wallet information. - -In `App.tsx`, you can display connect and disconnect buttons with various options as follows: - -```typescript title="App.tsx" -function App() { - // ... - return ( -
    -

    get-starknet

    -
    - // Default connection: - - // Always show modal: - - // Never show modal: - - // Dark theme modal: - - // Light theme modal: - - // Default disconnect: - - // Disconnect and clear last wallet: - -
    -
    - ) -}; -``` - -### 3.3. Display wallet information - -Update `App.tsx` with the following code to display the name and icon of the connected wallet, and -the connected address. -This provides visual feedback to the user, confirming which wallet and account they are using. - -```typescript title="App.tsx" -function App() { - // ... - return ( -
    - // ... - {walletName && ( -
    -

    - Selected Wallet:
    {walletName}
    - Wallet icon -

    -
      -
    • Wallet address:
      {walletAddress}
    • -
    -
    - )} -
    - ) -}; -``` - -### 3.4. Full example - -The following is a full example of configuring the wallet connection. -It displays basic connect and disconnect buttons, and the connected wallet's information. - -```typescript title="App.tsx" -import "./App.css" -import { - type ConnectOptions, - type DisconnectOptions, - connect, - disconnect, -} from "get-starknet" -import { AccountInterface } from "starknet"; -import { useState } from "react" -function App() { - const [walletName, setWalletName] = useState("") - const [walletAddress, setWalletAddress] = useState("") - const [walletIcon, setWalletIcon] = useState("") - const [walletAccount, setWalletAccount] = useState(null) - async function handleConnect(options?: ConnectOptions) { - const res = await connect(options) - setWalletName(res?.name || "") - setWalletAddress(res?.account?.address || "") - setWalletIcon(res?.icon || "") - setWalletAccount(res?.account) - } - async function handleDisconnect(options?: DisconnectOptions) { - await disconnect(options) - setWalletName("") - setWalletAddress("") - setWalletAccount(null) - } - return ( -
    -

    get-starknet

    -
    - - -
    - {walletName && ( -
    -

    - Selected Wallet:
    {walletName}
    - Wallet icon -

    -
      -
    • Wallet address:
      {walletAddress}
    • -
    -
    - )} -
    - ) -}; - -export default App -``` - -### 3.5. Start the dapp - -Start the dapp and navigate to it in your browser. - -```bash -yarn start -``` - -You are directed to the default dapp display. - -

    - Starknet dapp start -

    - -When you select **Connect**, `get-starknet` displays a modal that detects MetaMask and allows you to -choose which Starknet wallet to connect to. -Follow the on-screen prompts to connect your MetaMask wallet to Starknet. - -

    - Starknet dapp select wallet -

    - -After you accept the terms in the prompts, your wallet is connected and its information is displayed. - -

    - Starknet dapp connected -

    - -## 4. Display the balance of and transfer an ERC-20 token - -Now that you have configured the wallet connection, you can display the balance of a specific ERC-20 -token, such as STRK, and perform a transfer using the `AccountInterface` instance. - -### 4.1. Obtain tokens and switch to testnet - -Use the [Starknet Snap companion dapp](https://snaps.consensys.io/starknet) to generate a Starknet address and switch to the -Starknet Sepolia testnet. - -Obtain testnet ETH (for gas) and at least 1 STRK token from the [Starknet faucet]( https://starknet-faucet.vercel.app/). - -### 4.2. Configure the TypeScript compiler - -In the `tsconfig.json` file in the root directory of your project, update the `compilerOptions` with -the `target` version set to `es2022`, and `jsx` set to `react-jsx`: - -```json title="tsconfig.json" -{ - "compilerOptions": { - "target": "es2022", - "jsx": "react-jsx", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true - } -} -``` - -### 4.3. Set up the contract - -Create a `src/components/` directory and add the following files to it: - -- `erc20Abi.json`: A JSON file containing the ERC-20 token contract's application binary interface (ABI). -- `TokenBalanceAndTransfer.tsx`: A React component file for handling token balance display and transfer operations. - -:::note ABI and contract address -The contract address for STRK (an ERC-20 token) on Sepolia testnet is `0x049D36570D4e46f48e99674bd3fcc84644DdD6b96F7C741B1562B82f9e004dC7`. -You can find the ABI of the ERC-20 contract on the **Code** tab in [Voyager](https://voyager.online/). -::: - -Add the following code to `TokenBalanceAndTransfer.tsx` to load the ABI from `erc20Abi.json`: - -```typescript title="TokenBalanceAndTransfer.tsx" -import { useEffect, useState } from "react"; -import { AccountInterface, Call, Contract } from "starknet"; -import erc20Abi from "./erc20Abi.json"; - -interface TokenBalanceAndTransferProps { - account: AccountInterface; - tokenAddress: string; -} - -export function TokenBalanceAndTransfer({ account, tokenAddress }: TokenBalanceAndTransferProps) { - const [balance, setBalance] = useState(null); -} -``` - -### 4.4. Fetch the token balance - -In the `TokenBalanceAndTransfer` function, add the following balance fetching logic. -Call the `balanceOf` method to fetch the balance of the connected account: - -```typescript title="TokenBalanceAndTransfer.tsx" -export function TokenBalanceAndTransfer({ account, tokenAddress }: TokenBalanceAndTransferProps) { - const [balance, setBalance] = useState(null); - - useEffect(() => { - async function fetchBalance() { - try { - if (account) { - const erc20 = new Contract(erc20Abi, tokenAddress, account); - const result = await erc20.balanceOf(account.address) as bigint; - const decimals = 18n; - const formattedBalance = result / 10n ** decimals; // Adjust the value for decimals using BigInt arithmetic. - setBalance(Number(formattedBalance)); // Convert the number for display. - } - } catch (error) { - console.error("Error fetching balance:", error); - } - } - - fetchBalance(); - }, [account, tokenAddress]); -} -``` - -### 4.5. Transfer tokens - -In the `TokenBalanceAndTransfer` function, add the following transfer logic. -Call the `transfer` method and execute the transaction using the `AccountInterface`. -Make sure to update `recipientAddress` with a Starknet address of your choice. - -```typescript title="TokenBalanceAndTransfer.tsx" -export function TokenBalanceAndTransfer({ account, tokenAddress }: TokenBalanceAndTransferProps) { - // ... - async function handleTransfer() { - try { - if (account) { - const erc20 = new Contract(erc20Abi, tokenAddress, account); - // Update this example recipient address. - const recipientAddress = "0x01aef74c082e1d6a0ec786696a12a0a5147e2dd8da11eae2d9e0f86e5fdb84b5"; - const amountToTransfer = 1n * 10n ** 18n; - - const transferCall: Call = erc20.populate("transfer", [recipientAddress, amountToTransfer]); - - // Execute the transfer. - const { transaction_hash: transferTxHash } = await account.execute([transferCall]); - - // Wait for the transaction to be accepted. - await account.waitForTransaction(transferTxHash); - - // Refresh the balance after the transfer. - const newBalance = await erc20.balanceOf(account.address) as bigint; - setBalance(Number(newBalance / 10n ** 18n)); - } - } catch (error) { - console.error("Error during transfer:", error); - } - } -} -``` - -### 4.6. Update `App.tsx` - -Call the `TokenBalanceAndTransfer` component in `App.tsx`. -Add the following to the header of `App.tsx` to import the component: - -```typescript title="App.tsx" -import { TokenBalanceAndTransfer } from "./components/TokenBalanceAndTransfer"; -``` - -After the displayed wallet information, add the `TokenBalanceAndTransfer` component with the contract address: - -```typescript title="App.tsx" -{walletAccount && - -} -``` - -:::note -The contract address for STRK (an ERC-20 token) on Sepolia testnet is `0x049D36570D4e46f48e99674bd3fcc84644DdD6b96F7C741B1562B82f9e004dC7`. -::: - -### 4.7. Full example - -The following is a full example of displaying the balance of an ERC-20 token and performing a transfer: - - - - -```typescript -import { useEffect, useState } from "react"; -import { AccountInterface, Call, Contract } from "starknet"; -import erc20Abi from "./erc20Abi.json"; - -interface TokenBalanceAndTransferProps { - account: AccountInterface; - tokenAddress: string; -} - -export function TokenBalanceAndTransfer({ account, tokenAddress }: TokenBalanceAndTransferProps) { - const [balance, setBalance] = useState(null); - - useEffect(() => { - async function fetchBalance() { - try { - if (account) { - const erc20 = new Contract(erc20Abi, tokenAddress, account); - const result = await erc20.balanceOf(account.address) as bigint; - - const decimals = 18n; - const formattedBalance = result / 10n ** decimals; // Adjust for decimals using BigInt arithmetic. - setBalance(Number(formattedBalance)); // Convert to a number for UI display. - } - } catch (error) { - console.error("Error fetching balance:", error); - } - } - - fetchBalance(); - }, [account, tokenAddress]); - - async function handleTransfer() { - try { - if (account) { - const erc20 = new Contract(erc20Abi, tokenAddress, account); - // Replace this example recipient address. - const recipientAddress = "0x01aef74c082e1d6a0ec786696a12a0a5147e2dd8da11eae2d9e0f86e5fdb84b5"; - const amountToTransfer = 1n * 10n ** 18n; // 1 token (in smallest units). - - // Populate transfer call. - const transferCall: Call = erc20.populate("transfer", [recipientAddress, amountToTransfer]); - - // Execute transfer. - const { transaction_hash: transferTxHash } = await account.execute([transferCall]); - - // Wait for the transaction to be accepted. - await account.waitForTransaction(transferTxHash); - - // Refresh balance after transfer. - const newBalance = await erc20.balanceOf(account.address) as bigint; - setBalance(Number(newBalance / 10n ** 18n)); // Adjust for decimals - } - } catch (error) { - console.error("Error during transfer:", error); - } - } - - return ( -
    -

    Token Balance: {balance !== null ? `${balance} STRK` : "Loading..."}

    - -
    - ); -} -``` - -
    - - -```json -[ - { - "type": "impl", - "name": "MintableToken", - "interface_name": "src::mintable_token_interface::IMintableToken" - }, - { - "type": "struct", - "name": "core::integer::u256", - "members": [ - { - "name": "low", - "type": "core::integer::u128" - }, - { - "name": "high", - "type": "core::integer::u128" - } - ] - }, - { - "type": "interface", - "name": "src::mintable_token_interface::IMintableToken", - "items": [ - { - "type": "function", - "name": "permissioned_mint", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "permissioned_burn", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "MintableTokenCamelImpl", - "interface_name": "src::mintable_token_interface::IMintableTokenCamel" - }, - { - "type": "interface", - "name": "src::mintable_token_interface::IMintableTokenCamel", - "items": [ - { - "type": "function", - "name": "permissionedMint", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "permissionedBurn", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "Replaceable", - "interface_name": "src::replaceability_interface::IReplaceable" - }, - { - "type": "struct", - "name": "core::array::Span::", - "members": [ - { - "name": "snapshot", - "type": "@core::array::Array::" - } - ] - }, - { - "type": "struct", - "name": "src::replaceability_interface::EICData", - "members": [ - { - "name": "eic_hash", - "type": "core::starknet::class_hash::ClassHash" - }, - { - "name": "eic_init_data", - "type": "core::array::Span::" - } - ] - }, - { - "type": "enum", - "name": "core::option::Option::", - "variants": [ - { - "name": "Some", - "type": "src::replaceability_interface::EICData" - }, - { - "name": "None", - "type": "()" - } - ] - }, - { - "type": "enum", - "name": "core::bool", - "variants": [ - { - "name": "False", - "type": "()" - }, - { - "name": "True", - "type": "()" - } - ] - }, - { - "type": "struct", - "name": "src::replaceability_interface::ImplementationData", - "members": [ - { - "name": "impl_hash", - "type": "core::starknet::class_hash::ClassHash" - }, - { - "name": "eic_data", - "type": "core::option::Option::" - }, - { - "name": "final", - "type": "core::bool" - } - ] - }, - { - "type": "interface", - "name": "src::replaceability_interface::IReplaceable", - "items": [ - { - "type": "function", - "name": "get_upgrade_delay", - "inputs": [], - "outputs": [ - { - "type": "core::integer::u64" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "get_impl_activation_time", - "inputs": [ - { - "name": "implementation_data", - "type": "src::replaceability_interface::ImplementationData" - } - ], - "outputs": [ - { - "type": "core::integer::u64" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "add_new_implementation", - "inputs": [ - { - "name": "implementation_data", - "type": "src::replaceability_interface::ImplementationData" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "remove_implementation", - "inputs": [ - { - "name": "implementation_data", - "type": "src::replaceability_interface::ImplementationData" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "replace_to", - "inputs": [ - { - "name": "implementation_data", - "type": "src::replaceability_interface::ImplementationData" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "AccessControlImplExternal", - "interface_name": "src::access_control_interface::IAccessControl" - }, - { - "type": "interface", - "name": "src::access_control_interface::IAccessControl", - "items": [ - { - "type": "function", - "name": "has_role", - "inputs": [ - { - "name": "role", - "type": "core::felt252" - }, - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "get_role_admin", - "inputs": [ - { - "name": "role", - "type": "core::felt252" - } - ], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - } - ] - }, - { - "type": "impl", - "name": "RolesImpl", - "interface_name": "src::roles_interface::IMinimalRoles" - }, - { - "type": "interface", - "name": "src::roles_interface::IMinimalRoles", - "items": [ - { - "type": "function", - "name": "is_governance_admin", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "is_upgrade_governor", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "register_governance_admin", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "remove_governance_admin", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "register_upgrade_governor", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "remove_upgrade_governor", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [], - "state_mutability": "external" - }, - { - "type": "function", - "name": "renounce", - "inputs": [ - { - "name": "role", - "type": "core::felt252" - } - ], - "outputs": [], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "ERC20Impl", - "interface_name": "openzeppelin::token::erc20::interface::IERC20" - }, - { - "type": "interface", - "name": "openzeppelin::token::erc20::interface::IERC20", - "items": [ - { - "type": "function", - "name": "name", - "inputs": [], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "symbol", - "inputs": [], - "outputs": [ - { - "type": "core::felt252" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "decimals", - "inputs": [], - "outputs": [ - { - "type": "core::integer::u8" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "total_supply", - "inputs": [], - "outputs": [ - { - "type": "core::integer::u256" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "balance_of", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::integer::u256" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "allowance", - "inputs": [ - { - "name": "owner", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "spender", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::integer::u256" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "transfer", - "inputs": [ - { - "name": "recipient", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "transfer_from", - "inputs": [ - { - "name": "sender", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "recipient", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "approve", - "inputs": [ - { - "name": "spender", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - } - ] - }, - { - "type": "impl", - "name": "ERC20CamelOnlyImpl", - "interface_name": "openzeppelin::token::erc20::interface::IERC20CamelOnly" - }, - { - "type": "interface", - "name": "openzeppelin::token::erc20::interface::IERC20CamelOnly", - "items": [ - { - "type": "function", - "name": "totalSupply", - "inputs": [], - "outputs": [ - { - "type": "core::integer::u256" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "balanceOf", - "inputs": [ - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress" - } - ], - "outputs": [ - { - "type": "core::integer::u256" - } - ], - "state_mutability": "view" - }, - { - "type": "function", - "name": "transferFrom", - "inputs": [ - { - "name": "sender", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "recipient", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "amount", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - } - ] - }, - { - "type": "constructor", - "name": "constructor", - "inputs": [ - { - "name": "name", - "type": "core::felt252" - }, - { - "name": "symbol", - "type": "core::felt252" - }, - { - "name": "decimals", - "type": "core::integer::u8" - }, - { - "name": "initial_supply", - "type": "core::integer::u256" - }, - { - "name": "recipient", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "permitted_minter", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "provisional_governance_admin", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "upgrade_delay", - "type": "core::integer::u64" - } - ] - }, - { - "type": "function", - "name": "increase_allowance", - "inputs": [ - { - "name": "spender", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "added_value", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "decrease_allowance", - "inputs": [ - { - "name": "spender", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "subtracted_value", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "increaseAllowance", - "inputs": [ - { - "name": "spender", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "addedValue", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - }, - { - "type": "function", - "name": "decreaseAllowance", - "inputs": [ - { - "name": "spender", - "type": "core::starknet::contract_address::ContractAddress" - }, - { - "name": "subtractedValue", - "type": "core::integer::u256" - } - ], - "outputs": [ - { - "type": "core::bool" - } - ], - "state_mutability": "external" - }, - { - "type": "event", - "name": "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer", - "kind": "struct", - "members": [ - { - "name": "from", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "to", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "value", - "type": "core::integer::u256", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "openzeppelin::token::erc20_v070::erc20::ERC20::Approval", - "kind": "struct", - "members": [ - { - "name": "owner", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "spender", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "value", - "type": "core::integer::u256", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::replaceability_interface::ImplementationAdded", - "kind": "struct", - "members": [ - { - "name": "implementation_data", - "type": "src::replaceability_interface::ImplementationData", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::replaceability_interface::ImplementationRemoved", - "kind": "struct", - "members": [ - { - "name": "implementation_data", - "type": "src::replaceability_interface::ImplementationData", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::replaceability_interface::ImplementationReplaced", - "kind": "struct", - "members": [ - { - "name": "implementation_data", - "type": "src::replaceability_interface::ImplementationData", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::replaceability_interface::ImplementationFinalized", - "kind": "struct", - "members": [ - { - "name": "impl_hash", - "type": "core::starknet::class_hash::ClassHash", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::access_control_interface::RoleGranted", - "kind": "struct", - "members": [ - { - "name": "role", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "sender", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::access_control_interface::RoleRevoked", - "kind": "struct", - "members": [ - { - "name": "role", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "account", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "sender", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::access_control_interface::RoleAdminChanged", - "kind": "struct", - "members": [ - { - "name": "role", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "previous_admin_role", - "type": "core::felt252", - "kind": "data" - }, - { - "name": "new_admin_role", - "type": "core::felt252", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::roles_interface::GovernanceAdminAdded", - "kind": "struct", - "members": [ - { - "name": "added_account", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "added_by", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::roles_interface::GovernanceAdminRemoved", - "kind": "struct", - "members": [ - { - "name": "removed_account", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "removed_by", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::roles_interface::UpgradeGovernorAdded", - "kind": "struct", - "members": [ - { - "name": "added_account", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "added_by", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "src::roles_interface::UpgradeGovernorRemoved", - "kind": "struct", - "members": [ - { - "name": "removed_account", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - }, - { - "name": "removed_by", - "type": "core::starknet::contract_address::ContractAddress", - "kind": "data" - } - ] - }, - { - "type": "event", - "name": "openzeppelin::token::erc20_v070::erc20::ERC20::Event", - "kind": "enum", - "variants": [ - { - "name": "Transfer", - "type": "openzeppelin::token::erc20_v070::erc20::ERC20::Transfer", - "kind": "nested" - }, - { - "name": "Approval", - "type": "openzeppelin::token::erc20_v070::erc20::ERC20::Approval", - "kind": "nested" - }, - { - "name": "ImplementationAdded", - "type": "src::replaceability_interface::ImplementationAdded", - "kind": "nested" - }, - { - "name": "ImplementationRemoved", - "type": "src::replaceability_interface::ImplementationRemoved", - "kind": "nested" - }, - { - "name": "ImplementationReplaced", - "type": "src::replaceability_interface::ImplementationReplaced", - "kind": "nested" - }, - { - "name": "ImplementationFinalized", - "type": "src::replaceability_interface::ImplementationFinalized", - "kind": "nested" - }, - { - "name": "RoleGranted", - "type": "src::access_control_interface::RoleGranted", - "kind": "nested" - }, - { - "name": "RoleRevoked", - "type": "src::access_control_interface::RoleRevoked", - "kind": "nested" - }, - { - "name": "RoleAdminChanged", - "type": "src::access_control_interface::RoleAdminChanged", - "kind": "nested" - }, - { - "name": "GovernanceAdminAdded", - "type": "src::roles_interface::GovernanceAdminAdded", - "kind": "nested" - }, - { - "name": "GovernanceAdminRemoved", - "type": "src::roles_interface::GovernanceAdminRemoved", - "kind": "nested" - }, - { - "name": "UpgradeGovernorAdded", - "type": "src::roles_interface::UpgradeGovernorAdded", - "kind": "nested" - }, - { - "name": "UpgradeGovernorRemoved", - "type": "src::roles_interface::UpgradeGovernorRemoved", - "kind": "nested" - } - ] - } -] -``` - - - - -```typescript -import "./App.css" -import { - type ConnectOptions, - type DisconnectOptions, - connect, - disconnect, -} from "get-starknet" -import { AccountInterface, wallet } from "starknet"; -import { useState } from "react" -import { TokenBalanceAndTransfer } from "./components/TokenBalanceAndTransfer"; - -function App() { - const [walletName, setWalletName] = useState("") - const [walletAddress, setWalletAddress] = useState("") - const [walletIcon, setWalletIcon] = useState("") - const [walletAccount, setWalletAccount] = useState(null) - - async function handleConnect(options?: ConnectOptions) { - const res = await connect(options) - setWalletName(res?.name || "") - setWalletAddress(res?.account?.address || "") - setWalletIcon(res?.icon || "") - setWalletAccount(res?.account as unknown as AccountInterface) - } - - async function handleDisconnect(options?: DisconnectOptions) { - await disconnect(options) - setWalletName("") - setWalletAddress("") - setWalletAccount(null) - } - - return ( -
    -

    get-starknet

    -
    - - -
    - {walletName && ( -
    -

    - Selected Wallet:
    {walletName}
    - Wallet icon -

    -
      -
    • Wallet address:
      {walletAddress}
    • -
    -
    - )} - {walletAccount && - - } -
    - ) -}; - -export default App -``` - -
    -
    - -### 4.8. Start the dapp - -Start the dapp and navigate to it in your browser. - -```bash -yarn start -``` - -After connecting to MetaMask, the dapp should display your STRK token balance: - -

    - Starknet transfer token -

    - -You can select **Transfer 1 STRK** to make a transfer to the recipient address specified in [Step 4.5](#45-transfer-tokens). - -## Next steps - -You've set up a simple React dapp that connects to MetaMask, displays an ERC-20 token balance, and performs token transfers. Creating a contract instance using `AccountInterface` allows you to interact with smart contracts, retrieve token balances, and execute transactions, enabling more advanced functionality in your dapp. - -You can follow these next steps: - -- [Manage Starknet accounts](manage-starknet-accounts.md). -- [Manage Starknet networks](manage-starknet-networks.md). -- Explore the [Starknet Snap API reference](../../../reference/non-evm-apis/starknet-snap-api.md). diff --git a/wallet/how-to/use-non-evm-networks/starknet/index.md b/wallet/how-to/use-non-evm-networks/starknet/index.md deleted file mode 100644 index a222d4b985b..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/index.md +++ /dev/null @@ -1,174 +0,0 @@ ---- -description: Interact with users' Starknet accounts in MetaMask. ---- - -# Starknet - -[Starknet](https://www.starknet.io/) is a non-EVM Layer 2 network. -You can interact with users' Starknet accounts in MetaMask by connecting to the -[Starknet Snap](https://snaps.metamask.io/snap/npm/consensys/starknet-snap/). -You can use the [`get-starknet`](https://github.com/starknet-io/get-starknet) library or the -[`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) JSON-RPC method from -your dapp to connect to the Starknet Snap. -Both options support similar functionalities, but offer different ways of interacting with users' -Starknet accounts. - -See [**Connect to Starknet**](connect-to-starknet.md) to get started. - -The following sections compare the two connection options. - -## `get-starknet` - -:::warning Important - -We recommend using the `get-starknet` library for most use cases due to its ease of configuration -and multi-wallet support. -Learn more [about how `get-starknet` interacts with MetaMask](about-get-starknet.md). - -::: - -The `get-starknet` library: - -- Provides a high-level API that abstracts complex operations. -- Standardizes error handling. -- Supports connecting to multiple Starknet wallets, not limited to MetaMask. -- Manages wallet connections and Starknet interactions. -- Provides results in more readable code. - -`get-starknet` provides the same functionalities as `wallet_invokeSnap` and integrates a Starknet -Window Object (SWO). -The SWO simplifies account management and signing, and enhances the experience of handling account -states and transactions. -A dapp uses the [Account object](https://starknetjs.com/docs/API/classes/Account) in the SWO to manage operations. - -## `wallet_invokeSnap` - -The `wallet_invokeSnap` method: - -- Requires precise method names and parameter structures. -- Handles both MetaMask-specific and Starknet-specific errors. -- Is designed for operating within the MetaMask framework. -- Manages lower-level Starknet interactions directly. -- Provides results in more detailed, lower-level code. - -`wallet_invokeSnap` manages direct interactions between the dapp and the Starknet Snap. -It facilitates network communication for account creation, transaction signing, fee estimation, and -other Starknet-related actions. - -## Supported functionalities - -The following section lists the core functionalities and API methods that each connection option supports: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Functionality`get-starknet``wallet_invokeSnap`
    Account managementDeploy an account`deployAccount` ↗`starkNet_createAccount`
    Recover an account address`getAddress` ↗`starkNet_recoverAccounts`
    Display a private keyn/a`starkNet_displayPrivateKey`
    Gas and feesEstimate the gas fee`estimateFeeBulk` ↗`starkNet_estimateFee`
    Estimate the account deploy fee`estimateAccountDeployFee` ↗`starkNet_estimateAccountDeployFee`
    Token managementAdd an ERC-20 token`watchAsset` ↗`starkNet_addErc20Token`
    Get the ERC-20 token balance`callContract` ↗`starkNet_getErc20TokenBalance`
    Signing and transactionsSign a message`signMessage` ↗`starkNet_signMessage`
    Sign a transaction`signTransaction` ↗`starkNet_signTransaction`
    Sign a declare transaction`signDeclareTransaction` ↗`starkNet_signDeclareTransaction`
    Verify a signed messagen/a`starkNet_verifySignedMessage`
    Execute a transaction`execute` ↗`starkNet_executeTxn`
    Declare a contract`declareContract` ↗`starkNet_declareContract`
    Get transactions`getTransaction` ↗`starkNet_getTransaction`
    Get the transaction status`getTransactionStatus` ↗`starkNet_getTransactionStatus`
    Network managementSwitch networks`switchNetwork` ↗`starkNet_switchNetwork`
    Get the current network`getChainId` ↗`starkNet_getCurrentNetwork`
    - -## Resources - -To get started, [connect your dapp to Starknet in MetaMask](connect-to-starknet.md). - -The following external resources provide additional information for learning about and interacting with Starknet: - -- [Official Starknet documentation](https://www.starknet.io/developers/) -- [Starknet companion dapp](https://snaps.consensys.io/starknet) -- [Voyager block explorer](https://voyager.online/) -- [Cairo language for Starknet](https://book.cairo-lang.org/) diff --git a/wallet/how-to/use-non-evm-networks/starknet/manage-starknet-accounts.md b/wallet/how-to/use-non-evm-networks/starknet/manage-starknet-accounts.md deleted file mode 100644 index d0c371d955a..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/manage-starknet-accounts.md +++ /dev/null @@ -1,431 +0,0 @@ ---- -description: Manage Starknet accounts in MetaMask. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Manage Starknet accounts - -You can manage Starknet accounts in MetaMask using the -[`get-starknet`](https://github.com/starknet-io/get-starknet) library or the -[`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) JSON-RPC method. - -:::note Notes - -- Account creation in Starknet is handled by the wallet provider. - As a dapp developer, you do not create accounts directly. - Instead, you can guide users to [create an account](connect-to-starknet.md) with MetaMask. -- Currently, the Starknet Snap doesn't support multiple Starknet accounts. - -::: - -## Prerequisites - -[Connect to Starknet](connect-to-starknet.md) from your dapp. - -## Display account information - -After a user connects to their Starknet account in MetaMask, you can display the account details. -The following example displays the account address: - - - - - ```javascript - const connectStarknetAccount = async () => { - const starknet = await connect(); - await starknet.enable(); // Prompts the user to connect their Starknet account using MetaMask. - return starknet; - }; - - const showAccountInfo = async () => { - const starknet = await connectStarknetAccount(); - - if (account) { - document.getElementById("accountAddress").innerText = `Account Address: ${starknet.selectedAddress}`; - } - }; - ``` - - - - - ```javascript - const showAccountInfo = async () => { - if (typeof provider !== "undefined" && provider.isMetaMask) { - try { - // Invoke the Starknet Snap to get account information. - const response = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_invokeSnap", - params: { - snapId: "npm:@starknet-snap/snap", - request: { - method: "starknet_recoverAccounts" - } - } - }); - - if (response && response.length > 0) { - const account = response[0]; // Get the first account. - document.getElementById("accountAddress").innerText = `Account Address: ${account.address}`; - } else { - document.getElementById("accountAddress").innerText = "No Starknet account found"; - } - } catch (error) { - console.error("Error fetching Starknet account:", error); - document.getElementById("accountAddress").innerText = "Error fetching account information"; - } - } else { - document.getElementById("accountAddress").innerText = "MetaMask not detected or Snaps not supported"; - } - }; - - // Call the function when needed. - showAccountInfo(); - ``` - - - - -## Retrieve the connected account - -You can retrieve and display a user's connected Starknet account. -The following example displays the connected account address if available, and displays buttons to -connect or disconnect the account. - - - - - ```javascript - import { useStarknet, useConnectors } from "@starknet-react/core"; - import { useState, useEffect } from "react"; - - function AccountDisplay() { - const { account } = useStarknet(); - const { available, connect, disconnect } = useConnectors(); - const [accountAddress, setAccountAddress] = useState(); - - useEffect(() => { - setAccountAddress(account?.address); - }, [account]); - - return ( -
    - {accountAddress ? ( -

    Connected Account: {accountAddress}

    - ) : ( -

    No account connected

    - )} - {available.map((connector) => ( - - ))} - {account && ( - - )} -
    - ); - } - ``` - -
    - - - ```javascript - import React, { useState, useEffect } from "react"; - - const STARKNET_SNAP_ID = "npm:@starknet-snap/snap"; - - function AccountDisplay() { - const [accountAddress, setAccountAddress] = useState(); - const [isConnected, setIsConnected] = useState(false); - const [error, setError] = useState(null); - - const connectToSnap = async () => { - if (typeof provider !== "undefined" && provider.isMetaMask) { - try { - // Request permission to access the Snap. - await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_requestSnaps", - params: { [STARKNET_SNAP_ID]: {} } - }); - setIsConnected(true); - fetchAccount(); - } catch (err) { - console.error("Error connecting to Starknet Snap:", err); - setError("Failed to connect to Starknet Snap"); - } - } else { - setError("MetaMask not detected or Snaps not supported"); - } - }; - - const disconnectFromSnap = async () => { - setAccountAddress(undefined); - setIsConnected(false); - }; - - const fetchAccount = async () => { - if (typeof provider !== "undefined" && provider.isMetaMask) { - try { - const response = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_invokeSnap", - params: { - snapId: STARKNET_SNAP_ID, - request: { - method: "starknet_recoverAccounts" - } - } - }); - - if (response && response.length > 0) { - setAccountAddress(response[0].address); - } else { - setError("No Starknet account found"); - } - } catch (err) { - console.error("Error fetching Starknet account:", err); - setError("Failed to fetch account information"); - } - } - }; - - useEffect(() => { - if (isConnected) { - fetchAccount(); - } - }, [isConnected]); - - return ( -
    - {accountAddress ? ( -

    Connected Account: {accountAddress}

    - ) : ( -

    No account connected

    - )} - {!isConnected ? ( - - ) : ( - - )} - {error &&

    {error}

    } -
    - ); - } - - export default AccountDisplay; - ``` - -
    -
    - - -## Manage account transactions - -You can manage a user's Starknet account transactions. -The following example invokes a specific function on a Starknet smart contract, handles wallet -connection and transaction submission, and logs the result or any errors: - - - - -```javascript -const invokeStarknetContract = async () => { - try { - const starknet = getStarknet(); - await starknet.enable(); // Make sure the wallet is enabled. - - const contractAddress = "0xYourContractAddress"; // Replace with your contract address. - const entrypoint = "function_name"; // The function you want to call. - const calldata = [/* your function arguments */]; // Replace with calldata. - - const result = await starknet.account.execute({ - contractAddress: contractAddress, - entrypoint: entrypoint, - calldata: calldata - }); - - console.log("Transaction result: ", result); - } catch (error) { - console.error("Error invoking contract:", error); - } -}; -``` - - - - -```javascript -const invokeStarknetContract = async () => { - if (typeof provider !== "undefined" && provider.isMetaMask) { - try { - const calls = [ - { - entrypoint: "transfer", // The function name to call on the contract. - calldata: [ - "0x1234567890abcdef1234567890abcdef12345678", // Recipient's address. - "1000000000000000000" // Amount to transfer in wei (1 token, assuming 18 decimals). - ] - } - ]; - - const result = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_executeTxn", - params: { - address: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", // The sender's address. - calls: calls, // The array of calls with entrypoint and calldata. - details: { - nonce: 1, // Optional nonce. - maxFee: "1000000000000000", // Maximum gas fee allowed. - }, - chainId: "0x534e5f5345504f4c4941" // Starknet Sepolia testnet chain ID. - } - } - } - }); - - console.log("Transaction result: ", result); - } catch (error) { - console.error("Error invoking contract:", error); - } - } else { - console.error("MetaMask not detected or Snaps not supported"); - } -}; -``` - - - - -## Handle account changes and disconnections - -You can handle account changes and disconnections. -Use the following component at the top level of your dapp to handle account changes globally: - - - - - ```javascript - import { getStarknet } from "get-starknet"; - import { useEffect, useState } from "react"; - - function AccountChangeHandler() { - const [account, setAccount] = useState(null); - - useEffect(() => { - const starknet = getStarknet(); - - const handleAccountsChanged = (accounts: string[]) => { - console.log("Accounts changed:", accounts); - setAccount(accounts[0] || null); - }; - - const handleDisconnect = () => { - console.log("Disconnected from wallet"); - setAccount(null); - }; - - if (starknet) { - starknet.on("accountsChanged", handleAccountsChanged); - starknet.on("networkChanged", handleDisconnect); - - // Initial account setup. - starknet.enable().then((accounts: string[]) => { - setAccount(accounts[0] || null); - }); - - return () => { - starknet.off("accountsChanged", handleAccountsChanged); - starknet.off("networkChanged", handleDisconnect); - }; - } - }, []); - - return ( -
    - {account ? ( -

    Connected Account: {account}

    - ) : ( -

    No account connected

    - )} -
    - ); - } - - export default AccountChangeHandler; - ``` -
    - - - ```javascript - import React, { useEffect, useState } from "react"; - - const STARKNET_SNAP_ID = "npm:@starknet-snap/snap"; - - function AccountChangeHandler() { - const [account, setAccount] = useState(null); - - const fetchAccount = async () => { - if (typeof provider !== "undefined" && provider.isMetaMask) { - try { - const response = await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_invokeSnap", - params: { - snapId: STARKNET_SNAP_ID, - request: { - method: "starknet_recoverAccounts" - } - } - }); - - if (response && response.length > 0) { - setAccount(response[0].address); - } else { - setAccount(null); - } - } catch (error) { - console.error("Error fetching Starknet account:", error); - setAccount(null); - } - } else { - console.error("MetaMask not detected or Snaps not supported"); - setAccount(null); - } - }; - - useEffect(() => { - fetchAccount(); - - // Retrieve account changes every 5 seconds. - const intervalId = setInterval(fetchAccount, 5000); - - return () => clearInterval(intervalId); - }, []); - - return ( -
    - {account ? ( -

    Connected Account: {account}

    - ) : ( -

    No account connected

    - )} -
    - ); - } - - export default AccountChangeHandler; - ``` - -
    -
    diff --git a/wallet/how-to/use-non-evm-networks/starknet/manage-starknet-networks.md b/wallet/how-to/use-non-evm-networks/starknet/manage-starknet-networks.md deleted file mode 100644 index 0262c9cb6b2..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/manage-starknet-networks.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -description: Manage Starknet networks in MetaMask. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Manage Starknet networks - -You can detect a user's Starknet network and prompt them to switch Starknet networks in MetaMask, -using the -[`get-starknet`](https://github.com/starknet-io/get-starknet) library or the -[`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) JSON-RPC method. - -## Prerequisites - -[Connect to Starknet](connect-to-starknet.md) from your dapp. - -## Detect a user's network - -Detect the Starknet network a user is currently connected to using the following: - - - - - ```javascript - const checkCurrentNetwork = (wallet) => { - try { - if(wallet?.isConnected !== true){ - throw("Wallet not connected"); - } - const currentNetwork = wallet?.chainId - console.log("Currently connected to:", currentNetwork); - return currentNetwork; - } catch (error) { - console.error("Error of detect current connected network:", error); - } - }; - ``` - - - - - ```javascript - const checkCurrentNetwork = async () => { - const provider = await getEip6963Provider(); // Or window.ethereum if you don't support EIP-6963. - if (provider) { - try { - const response = await provider.request({ - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_getCurrentNetwork" - } - } - }); - - console.log("Currently connected to:", response.name); - return response.chainId; // Returns the chain ID. - } catch (error) { - console.error("Error getting current Starknet network:", error); - throw error; - } - } else { - console.error("MetaMask not detected or Snaps not supported"); - throw new Error("MetaMask not detected or Snaps not supported"); - } - }; - - checkCurrentNetwork().then(chainId => { - console.log("Chain ID:", chainId); - }).catch(error => { - console.error("Error:", error); - }); - ``` - - - - -## Switch networks - -Starknet currently supports two public networks, Mainnet and Sepolia testnet. -Prompt users to switch between networks by setting the -[chain ID](../../../reference/non-evm-apis/starknet-snap-api.md#supported-networks) of the target network: - - - - - ```javascript - const switchChain = async (wallet, chainId) => { - try { - if(wallet?.isConnected !== true){ - throw("Wallet not connected"); - } - - await wallet?.request({ - type: "wallet_switchStarknetChain", - params: { chainId: chainId }, - }); - console.log(`Switched to chainId: ${chainId}`); - } catch (e) { - console.error("Failed to switch chain:", e); - } - }; - ``` - - - - - ```javascript - const switchStarknetNetwork = async (chainId) => { - const provider = await getEip6963Provider(); // Or window.ethereum if you don't support EIP-6963. - if (provider) { - try { - await provider.request({ - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_switchNetwork", - params: { chainId: chainId } - } - } - }); - console.log(`Switched to Starknet network with chainId: ${chainId}`); - } catch (error) { - console.error("Error switching Starknet network:", error); - throw error; - } - } else { - console.error("MetaMask not detected or Snaps not supported"); - throw new Error("MetaMask not detected or Snaps not supported"); - } - }; - ``` - - - \ No newline at end of file diff --git a/wallet/how-to/use-non-evm-networks/starknet/send-starknet-transactions.md b/wallet/how-to/use-non-evm-networks/starknet/send-starknet-transactions.md deleted file mode 100644 index 27d4633c1a0..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/send-starknet-transactions.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -description: Send Starknet transactions in MetaMask. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Send Starknet transactions - -You can send Starknet transactions using the -[`get-starknet`](https://github.com/starknet-io/get-starknet) library or the -[`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) JSON-RPC method. - -## Prerequisites - -[Connect to Starknet](connect-to-starknet.md) from your dapp. - -## Send a transaction - -Send a transaction using the -`starknet.account.execute()` -function (with `get-starknet`) or the -[`starknet_executeTxn`](../../../reference/non-evm-apis/starknet-snap-api.md#starknet_executetxn) -method (with `wallet_invokeSnap`): - - - - - ```javascript - const sendStarknetTransaction = async (wallet, contractAddress, entrypoint, calldata) => { - try { - if(wallet?.isConnected !== true){ - throw("Wallet not connected"); - } - - // Send the transaction. - const result = await wallet?.account?.execute({ - contractAddress: contractAddress, // The address of the contract. - entrypoint: entrypoint, // The function to call in the contract. - calldata: calldata // The parameters to pass to the function. - }); - console.log("Transaction successful:", result); - return result; - } catch (error) { - console.error("Error sending transaction:", error); - } - }; - ``` - - - - - ```javascript - const sendStarknetTransaction = async (address, chainId, contractAddress, entrypoint, calldata, maxFee = null) => { - const provider = await getEip6963Provider() // Or window.ethereum if you don't support EIP-6963. - if (!provider) { - throw new Error("MetaMask not detected or Snaps not supported"); - } - try { - const response = await provider.request({ - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_executeTxn", - params: { - address, // The address of the account. - chainId, // The chain ID of the request. - calls: { - contractAddress, // The address of the contract. - entrypoint, // The function to call in the contract. - calldata // Arguments to the contract. - }, - details: { - maxFee, // This is optional. It will re-estimate in the snap if not provided. - } - } - } - } - }); - console.log("Transaction sent:", response); - return response; - } catch (error) { - console.error("Error sending transaction:", error); - throw error; - } - }; - ``` - - - - -## Simplified example - -The following is a full, simplified example of connecting to a Starknet account and sending a transaction: - - - - -```javascript - import { connect } from "get-starknet"; - - const connectStarknetAccount = async () => { - const starknet = await connect(); - await starknet.enable(); // Prompts the user to connect their Starknet account using MetaMask. - return starknet; - }; - - const sendStarknetTransaction = async (contractAddress, entrypoint, calldata) => { - try { - const starknet = await connectStarknetAccount(); // Ensure the account is connected. - - // Send the transaction. - const result = await starknet.account.execute({ - contractAddress: contractAddress, - entrypoint: entrypoint, - calldata: calldata - }); - - console.log("Transaction successful:", result); - return result; - } catch (error) { - console.error("Error sending transaction:", error); - } - }; - - const contractAddress = "0xYourContractAddress"; - const entrypoint = "your_function_name"; - const calldata = [/* your function arguments */]; - - sendStarknetTransaction(contractAddress, entrypoint, calldata); - ``` - - - - - ```javascript - const connectStarknetAccount = async (provider) => { - try { - await provider // Or window.ethereum if you don't support EIP-6963. - .request({ - method: "wallet_requestSnaps", - params: { - "npm:@consensys/starknet-snap": {} - } - }); - console.log("Starknet Snap connected"); - } catch (error) { - console.error("Error connecting to Starknet Snap:", error); - throw error; - } - }; - - const sendStarknetTransaction = async (provider, address, calls, maxFee = null) => { - try { - await connectStarknetAccount(provider); - const requestParams = { - address, - calls, - details, - }; - if (maxFee) { - requestParams.details = { - maxFee - }; - } - const response = await provider.request({ // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_executeTxn", - params: requestParams - } - } - }); - console.log("Transaction sent:", response); - return response; - } - catch (error) { - console.error("Error sending transaction:", error); - throw error; - } - }; - - // Example usage. - const calls = [ - { - "entrypoint": "transfer", - "calldata": ["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "1000"], - "contractAddress": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4" - } - ]; - const address = "0xb60e8dd61c5d32be8058bb8eb970870f07233155"; - const maxFee = "1000000000000000"; // Optional. - - sendStarknetTransaction(address, calls, maxFee) - .then(result => console.log("Transaction result:", result)) - .catch(error => console.error("Transaction error:", error)); -} -``` - - - diff --git a/wallet/how-to/use-non-evm-networks/starknet/sign-starknet-data.md b/wallet/how-to/use-non-evm-networks/starknet/sign-starknet-data.md deleted file mode 100644 index e5b12685795..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/sign-starknet-data.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -description: Sign Starknet transactions in MetaMask. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Sign Starknet transactions - -You can sign Starknet transactions using the -[`get-starknet`](https://github.com/starknet-io/get-starknet) library or the -[`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) JSON-RPC method. - -## Prerequisites - -[Connect to Starknet](connect-to-starknet.md) from your dapp. - -## Sign a transaction - -Sign a Starknet transaction using the following: - - - - - ```typescript - const signStarknetTransaction = async (wallet, contractAddress, entrypoint, calldata) => { - try { - if(wallet?.isConnected !== true){ - throw("Wallet not connected"); - } - - // Sign the transaction. - const result = await wallet?.account?.signer.signTransaction({ - contractAddress: contractAddress, // The address of the contract. - entrypoint: entrypoint, // The function to call in the contract. - calldata: calldata // The parameters to pass to the function. - }); - console.log("Transaction signed successfully:", result); - return result; - } catch (error) { - console.error("Error signing transaction:", error); - } - }; - ``` - - - - ```typescript - const signStarknetTransactionWithSnap = async (contractAddress, entrypoint, calldata, chainId, address) => { - try { - const provider = await getEip6963Provider(); // Or window.ethereum if you don't support EIP-6963. - if (!provider) { - throw new Error("MetaMask not detected or Snaps not supported"); - } - // Connect to the Starknet Snap if it's not already connected. - await provider.request({ - method: "wallet_requestSnaps", - params: { - "npm:@consensys/starknet-snap": {} - } - }); - console.log("Starknet Snap connected"); - - // Use the wallet_invokeSnap method to sign the transaction. - const response = await provider.request({ - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_signTransaction", - params: { - address, // The address of the account. - chainId, // The chain ID of the request. - transactions: { - contractAddress, // The address of the contract. - entrypoint, // The function to call in the contract. - calldata // The parameters to pass to the function (as an array). - } - } - } - } - }); - - console.log("Transaction signed successfully:", response); - return response; - } catch (error) { - console.error("Error signing transaction:", error); - throw error; - } - }; - - // Example usage. - const contractAddress = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"; - const entrypoint = "transfer"; - const calldata = ["0xRecipientAddress", "1000"]; - - signStarknetTransactionWithSnap(contractAddress, entrypoint, calldata) - .then(result => console.log("Signed transaction result:", result)) - .catch(error => console.error("Transaction error:", error)); - ``` - - - \ No newline at end of file diff --git a/wallet/how-to/use-non-evm-networks/starknet/troubleshoot.md b/wallet/how-to/use-non-evm-networks/starknet/troubleshoot.md deleted file mode 100644 index ee2e5db8006..00000000000 --- a/wallet/how-to/use-non-evm-networks/starknet/troubleshoot.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -description: Troubleshoot common Starknet issues. -toc_max_heading_level: 4 ---- - -# Troubleshoot - -This guide addresses issues that might occur when connecting your dapp to the Starknet Snap in MetaMask. - -When using `get-starknet`, the library automatically handles detecting and connecting to MetaMask, -and adding the Starknet Snap. -If you're using `wallet_invokeSnap` directly, you might need to manage these processes manually. - -## MetaMask is not connected - -When using `wallet_invokeSnap`, use the following function to detect if MetaMask is installed: - -```typescript -async function detectMetamaskSupport(windowObject: Window & typeof globalThis): Promise { - const provider = await waitForMetaMaskProvider(windowObject, { retries: 3 }); - return provider; -} -``` - -This function uses the `waitForMetaMaskProvider` helper function, which -attempts to detect the MetaMask provider three times. - -In the event MetaMask is not installed, for example `isMetaMaskInstallRequired=true`, you can prompt -the user to install MetaMask using the following: - -```typescript -function checkAndPromptForMetaMask() { - const isMetaMaskInstalled = typeof provider !== "undefined" && provider.isMetaMask; - - if (!isMetaMaskInstalled) { - console.log("MetaMask is not installed. Prompting user to install."); - - // Update UI to inform the user. - const messageElement = document.getElementById("metamask-message") || document.createElement("div"); - messageElement.id = "metamask-message"; - messageElement.innerHTML = ` -

    MetaMask is required to use this dapp. Please install MetaMask to continue.

    - - `; - document.body.appendChild(messageElement); - - // Add click event to the install button. - document.getElementById("install-metamask").addEventListener("click", () => { - window.open("https://metamask.io/download.html", "_blank"); - }); - } else { - console.log("MetaMask is installed. Proceeding with this dapp."); - } -} - -// Call this function when your dapp initializes. -checkAndPromptForMetaMask(); -``` - -## Snap is not connected - -After connecting to MetaMask, verify that it supports Snaps: - -```typescript -const isSupportSnap = async (provider: any): Promise => { - try { - await provider.request({ - method: "wallet_getSnaps", - }); - return true; - } catch { - return false; - } -}; -``` - -If MetaMask is installed but the Snap is not added, use the following code to prompt the user to install the Snap: - -```typescript -async function installSnap(provider: MetaMaskProvider, snapId: string, snapVersion: string) { - try { - await provider.request({ - method: "wallet_requestSnaps", - params: { - [snapId]: { version: snapVersion }, - }, - }); - console.log("Snap installed successfully"); - } catch (error) { - console.error("Failed to install Snap:", error); - // Handle the error (for example, user rejected installation). - } -} -``` - - - \ No newline at end of file diff --git a/wallet/index.md b/wallet/index.md deleted file mode 100644 index e5d1a9a9126..00000000000 --- a/wallet/index.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -slug: / -title: Wallet API introduction -description: Introduction page for the Wallet API documentation. -keywords: [extension, api] ---- - -# Integrate your dapp with MetaMask using the Wallet API - -:::tip Building a cross-platform or mobile dapp? -For cross-platform development, mobile integration, or advanced features like QR codes and -deeplinking, see the [**MetaMask SDK** documentation](/sdk). -::: - -The Wallet API is MetaMask's core interface for web dapps to interact with the MetaMask browser extension. -With the Wallet API, you can: - -- Interact with users' EVM and non-EVM accounts. -- Send transactions and sign messages. -- Listen to account and network changes. - -The Wallet API uses standardized [JSON-RPC calls](reference/json-rpc-methods/index.md) to -interact with users' EVM accounts, and implements standard interfaces like -[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) for Ethereum provider methods and -[EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) for wallet detection. - -MetaMask also supports non-EVM networks, including native support for [Solana](how-to/use-non-evm-networks/solana.md). - -[Get started with the Wallet API.](how-to/connect-extension.md) - -## Wallet API vs. SDK - -The **Wallet API** is designed for web dapps that connect to the MetaMask browser extension. -If you're building a desktop web-only dapp, the Wallet API provides everything you need. - -[**MetaMask SDK**](/sdk) builds on top of the Wallet API, adding cross-platform support and features like mobile deeplinking and QR code connections. -We recommend the SDK if you want to build a cross-platform onchain dapp. - -:::note -Directly using the Wallet API also enables your dapp to work in the in-app browser of the MetaMask mobile app. -However, the [SDK](/sdk) offers a more consistent mobile connection. -::: diff --git a/wallet/reference/json-rpc-methods/index.md b/wallet/reference/json-rpc-methods/index.md deleted file mode 100644 index b95141a37ee..00000000000 --- a/wallet/reference/json-rpc-methods/index.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -sidebar_class_name: 'hidden' -description: See the MetaMask JSON-RPC API reference. ---- - -# JSON-RPC API - -This section provides an interactive reference for the JSON-RPC API of MetaMask's [Wallet API](../../concepts/wallet-api.md). The API builds on a set of standard Ethereum methods with MetaMask-specific enhancements, and is designed for seamless integration into dapps. - -View the JSON-RPC API methods by selecting a method in the left sidebar. You can test the methods directly in the page using the API playground, with pre-configured examples or custom parameters. You can also save URLs with custom parameters using your browser's bookmarks. - -Each method may have one or more of the following labels: - -- **MetaMask** - The functionalities of these methods are specific to MetaMask, and may or may not be supported by other wallets. -- **Restricted** - These methods are restricted and require requesting permission using [`wallet_requestPermissions`](/wallet/reference/json-rpc-methods/wallet_requestpermissions). -- **Mobile** - These methods are only available on the MetaMask mobile app. -- **Ethereum API** - These are standard Ethereum JSON-RPC API methods. See the [Ethereum wiki](https://ethereum.org/en/#json-rpc-methods) for more information about these methods. -- **Multichain API** - These methods are compatible with the [Multichain API](../multichain-api.md). -- **Experimental** - These methods are experimental and may be changed in the future. -- **Deprecated** - These methods are deprecated and may be removed in the future. diff --git a/wallet/reference/non-evm-apis/starknet-snap-api.md b/wallet/reference/non-evm-apis/starknet-snap-api.md deleted file mode 100644 index cf52bca3009..00000000000 --- a/wallet/reference/non-evm-apis/starknet-snap-api.md +++ /dev/null @@ -1,1115 +0,0 @@ ---- -description: See the Starknet Snap API reference. ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Starknet Snap API - -When connected to the [Starknet Snap](../../how-to/use-non-evm-networks/starknet/index.md), dapps -can use the Starknet Snap API to interact with users' Starknet accounts (for example, to send transactions). - -The examples on this page use the -[`wallet_invokeSnap`](/snaps/reference/wallet-api-for-snaps/#wallet_invokesnap) JSON-RPC method, -which supports all Starknet Snap API methods. - -:::note - -We recommend using [EIP-6963](../../concepts/wallet-interoperability.md#eip-6963-interfaces) for detecting the MetaMask wallet when using the `wallet_invokeSnap` approach. This ensures better interoperability and improved wallet integration. -::: - -:::note - -You can also communicate with the Starknet network using the -[Starknet API](/services/reference/starknet). - -::: - -## Supported networks - -Starknet currently supports two public networks. -Use these networks' chain IDs with the Starknet Snap API methods. - -| Network | Chain ID (Hexadecimal) | -|-------------------|--------------------------| -| Mainnet | `0x534e5f4d41494e` | -| Testnet (Sepolia) | `0x534e5f5345504f4c4941` | - -## API methods - -### `starkNet_createAccount` - -Deploys an account contract. - -#### Parameters - -- `addressIndex`: `integer` - (Optional) Specific address index of the derived key in - [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). -- `deploy`: `boolean` - (Optional) Indicate whether to include send the deploy transaction for the - account contract. - The default is `false`. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -The address of the account and the transaction hash if the account has been created. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_createAccount", - params: { - addressIndex: 1, - deploy: true, - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -{ - "transaction_hash": "0x05a56e2d52c817161883f50c441c3228cfe54d9f84b5b5b8b1c8b8e0e6f7e6d8", - "address": "0xb60e8dd61c5d32be8058bb8eb970870f07233155" -} -``` - - - - -### `starkNet_declareContract` - -Registers a contract's class on the Starknet blockchain without deploying it. - -#### Parameters - -- `senderAddress`: `string` - Address of the sender. -- `chainId`: `string` - (Optional) ID of the target Starknet network. The default is the Starknet Sepolia testnet. -- `contractPayload`: `object` - Transaction payload to be deployed. - - `contract`: `CompiledContract` | `string` - The compiled contract code (in Cairo). - - `classHash?`: `string` - (Optional) The computed class hash of compiled contract. - - `casm?`: `CompiledContract` | `string` - (Optional) - The compiled [casm](https://docs.starknet.io/architecture-and-concepts/smart-contracts/cairo-and-sierra/). - - `compiledClassHash?`: `string` - (Optional) The compiled class hash from casm. -- `invocationsDetails`: `object` - (Optional) Transaction details object containing: - - `nonce`: (Optional) Nonce for the transaction. - - `blockIdentifier`: (Optional) Block identifier for the transaction. - - `maxFee`: (Optional) Maximum gas fee allowed for the transaction. If not specified, the fee is automatically calculated. - - `tip`: (Optional) Additional fee to incentivize miners. - - `paymasterData`: (Optional) Paymaster-related data for the transaction. - - `accountDeploymentData`: (Optional) Data for account deployment. - - `nonceDataAvailabilityMode`: (Optional) Mode for nonce data availability. - - `feeDataAvailabilityMode`: (Optional) Mode for fee data availability. - - `version`: (Optional) The transaction version. - - `resourceBounds`: (Optional) The boundaries on resource consumption during the transaction. - - `skipValidate`: `boolean` - (Optional) Skip validation of the transaction. - -#### Returns - -The confirmation of sending a transaction on the Starknet contract, which contains: - -- `transaction_hash`: `string` - The transaction hash. -- `class_hash`: `string` - The computed class hash of compiled contract. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_declareContract", - params: { - senderAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - contractPayload: { - contract: { - // The compiled contract code - } - }, - chainId: "0x534e5f5345504f4c4941", - }, - }, - }, -}) - -``` - - - - -```json -{ - "transaction_hash": "0x05a56e2d52c817161883f50c441c3228cfe54d9f84b5b5b8b1c8b8e0e6f7e6d8", - "class_hash": "0xb60e8dd61c5d32be8058bb8eb970870f07233155" -} -``` - - - - -### `starkNet_displayPrivateKey` - -Extracts the private key from the deployed Starknet account and displays it in MetaMask. - -#### Parameters - -- `userAddress`: `string` - Address of the account contract. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -Always returns `null` for security reasons. -The private key is only shown in the MetaMask pop-up window. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_displayPrivateKey", - params: { - userAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -null -``` - - - - -### `starkNet_estimateAccountDeployFee` - -Gets the estimated gas fee for deploying an account contract. - -#### Parameters - -- `addressIndex`: `integer` - (Optional) Specific address index of the derived key in - [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -An object with the following properties (where values are originally `bigint` but converted to -base-10 `string` format using `.toString(10)`): - -- `suggestedMaxFee`: `string` - The maximum suggested fee for deploying the contract, in wei. -- `overallFee`: `string` - The overall fee for the deployment transaction, in wei. -- `gasConsumed`: `string` - The amount of gas consumed during the transaction. - The default is `0`. -- `gasPrice`: `string` - The gas price used for the transaction, in wei. - The default is `0`. -- `unit`: `string` - The unit of the fees and gas values, which is `wei`. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_estimateAccountDeployFee", - params: { - addressIndex: 0, - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -{ - "suggestedMaxFee": "1000000000000000", - "overallFee": "900000000000000", - "gasConsumed": "1000000", - "gasPrice": "1000000000", - "unit": "wei" -} -``` - - - - -### `starkNet_estimateFee` - -Gets the estimated gas fee for calling a method on any contract. - -#### Parameters - -- `address`: `string` - The account address from which the transaction is being made. -- `invocations`: `array` - The invocations to estimate the fee for. - Each invocation represents a contract call. -- `chainId`: `string` - The chain ID of the target Starknet network. - If not provided, the default is the Starknet Sepolia testnet. -- `details`: `object` - (Optional) The universal details - associated with the invocations, such as nonce and version. - -#### Returns - -A promise that resolves to an `EstimateFeeResponse` object, which contains the following properties: - -- `suggestedMaxFee`: `string` - The maximum suggested fee for the transaction, in wei. - This value is originally a `bigint` and is converted to a base-10 `string`. -- `overallFee`: `string` - The overall fee for the transaction, in wei. - This value is originally a `bigint` and is converted to a base-10 `string`. -- `gasConsumed`: `string`- The estimated amount of gas the transaction uses. -- `gasPrice`: `string` - The gas price per unit, represented as a string in wei. -- `unit`: `string` - The unit of the fees, typically `wei`. -- `includeDeploy`: `boolean` - Whether the transaction includes an account deployment step. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_estimateFee", - params: { - address: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - invocations: [ - { - entrypoint: "transfer", - calldata: [ - "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", - "1000000000000000000" - ] - } - ], - chainId: "0x534e5f5345504f4c4941", - details: { - nonce: 1, - maxFee: "2000000000000000" - } - }, - }, - }, -}); -``` - - - - -```json -{ - "suggestedMaxFee": "1000000000000000", - "overallFee": "900000000000000", - "gasConsumed": "1000000", - "gasPrice": "1000000000", - "unit": "wei", - "includeDeploy": false -} -``` - - - - -### `starkNet_executeTxn` - -Signs and executes a transaction. - -#### Parameters - -- `address`: `string` - The address of the sender. -- `calls`: An array of call objects to be executed. - Each call contains the target contract address, function name, and call data. -- `details`: `object` - (Optional) Transaction details as received by `starknet.js`, including: - - `nonce`: (Optional) Nonce for the transaction. - - `blockIdentifier`: (Optional) Block identifier for the transaction. - - `maxFee`: (Optional) Maximum gas fee allowed for the transaction. - If not specified, the fee is automatically calculated. - - `tip`: (Optional) Additional fee to incentivize miners. - - `paymasterData`: (Optional) Paymaster-related data for the transaction. - - `accountDeploymentData`: (Optional) Data for account deployment. - - `nonceDataAvailabilityMode`: (Optional) Mode for nonce data availability. - - `feeDataAvailabilityMode`: (Optional) Mode for fee data availability. - - `version`: (Optional) The transaction version. - - `resourceBounds`: (Optional) The boundaries on resource consumption during the transaction. - - `skipValidate`: `boolean` - (Optional) Skip validation of the transaction. -- `abis`: `any` - (Optional) Contract ABI for decoding the call data. -- `chainId`: `string` - The Starknet chain ID. Default is Starknet Sepolia testnet. - -#### Returns - -The hash of the transaction. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_executeTxn", - params: { - address: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", - calls: [ - { - entrypoint: "transfer", - calldata: [ - "0x1234567890abcdef1234567890abcdef12345678", - "1000000000000000000" - ] - } - ], - details: { - nonce: 1, - maxFee: "2000000000000000", - }, - chainId: "0x534e5f5345504f4c4941" - } - }, - }, -}) -``` - - - - -```json -{ - "transaction_hash": "0x05a56e2d52c817161883f50c441c3228cfe54d9f84b5b5b8b1c8b8e0e6f7e6d8" -} -``` - - - - -### `starkNet_extractPublicKey` - -Extracts the public key from a Starknet account address. - -#### Parameters - -- `userAddress`: `string` - Address of the account contract. -- `chainId`: `string` - (Optional) ID of the target Starknet network. The default network is the Starknet Sepolia testnet. - -#### Returns - -The public key associated with the specified account address (which may differ from the signer's public key). - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_extractPublicKey", - params: { - userAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -"0x04bfcab3b7ca7e8b3f3b62b2f7f77e9e4b68080bbf8f0f4a1c8f890864d2c7c1d3c45d8b2e3f5f1c27dfeea4c2f5733e90bfc7484e2a690aa9b8ac4559d2e6a8d7" -``` - - - - -### `starkNet_getCurrentNetwork` - -Retrieves the current Starknet Snap's network. - -#### Parameters - -None - -#### Returns - -A network object containing the name and chain ID of the network. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_getCurrentNetwork" - }, - }, -}) -``` - - - - -```json -{ - "name": "StarkNet Sepolia Testnet", - "chainId": "0x534e5f5345504f4c4941" -} -``` - - - - -### `starkNet_getErc20TokenBalance` - -Gets the user's current balance of an ERC-20 token. - -#### Parameters - -- `tokenAddress`: `string` - Address of the ERC-20 token contract. -- `userAddress`: `string` - Address of the user account. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -The latest and pending token balance, represented in hexadecimal. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_getErc20TokenBalance", - params: { - tokenAddress: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", - userAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -{ - "balancePending": "0x3e8", - "balanceLatest": "0x3e8", -} -``` - - - - -### `starkNet_addErc20Token` - -Add an ERC-20 token to the watch list. - -#### Parameters - -- `address`: `string` - Address of the sender. -- `chainId`: `string` - ID of the target Starknet network. -- `tokenAddress`: `string` - The contract address of the token. -- `tokenName`: `string` - The name of the token. -- `tokenSymbol`: `string` - The string symbol of the token. -- `tokenDecimals?`: `string` | `number` - The decimals of the token. - -#### Returns - -The token object that was added to the watch list. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_addErc20Token", - params: { - address: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - chainId: "0x534e5f5345504f4c4941", - tokenAddress: "0x1234567890123456789012345678901234567890", - tokenName: "Ether", - tokenSymbol: "ETH", - tokenDecimals: 18 - }, - }, - }, -}) -``` - - - - -```json -{ - "address": "0x1c4c12F3dfDAEa0D9a2b981A5a5DDE1a3c11dD54", - "symbol": "SNK", - "decimals": 18, - "logo": "https://starknet-logo.com/snk-logo.png" -} -``` - - - - -### `starkNet_getTransaction` - -Gets the transaction records from a sender address. - -#### Parameters - -- `senderAddress`: `string` - Address of the sender. -- `contractAddress`: `string` - (Optional) Address of the called contract. -- `pageSize`: `integer` - (Optional) Number of records to fetch per page from the Voyager API `/txn` endpoints. - Options are `10`, `25`, and `50`. - The default is `10`. -- `txnsInLastNumOfDays`: `integer` - (Optional) Number of days of transaction records to retrieve from Voyager. - The default is `5`. -- `withDeployTxn`: `boolean` - (Optional) Indicates whether to include the deployment transaction of the - sender's account contract. - The default is `false`. -- `onlyFromState`: `boolean` - (Optional) Indicates whether to only retrieve transaction records - that are stored in Snap state, such as those in `RECEIVED`, `PENDING`, `ACCEPTED_ON_L2`, or - `REJECTED` statuses. - The default is `false`. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -The list of the transaction records. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_getTransactions", - params: { - senderAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - contractAddress: "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", - pageSize: 25, - txnsInLastNumOfDays: 7, - withDeployTxn: true, - onlyFromState: false, - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -{ - "txnHash": "0x12f84d2b52a172c817161883f50c441c3e5f84d9f84b5b5b8b1c8b8e0e6f7e6f", - "txnType": "invoke", - "senderAddress": "0x1a2B3c4D5e6F7G8h9I0J1K2L3M4N5P6Q7R8S9T0U", - "contractAddress": "0x7AeD16c89125645FA675bD12C1f894Ae6458bcdF", - "contractFuncName": "transfer", - "contractCallData": [ - "0x3cAe7bD56f7b6A7De348F5dC856E1b123D45B3A5", - "1000000000000000000", - ], - "status": "ACCEPTED_ON_L2", - "failureReason": null, - "eventIds": [ - "0xabc123def4567890abc123def4567890" - ], - "timestamp": 1697070653, - "chainId": "0x534e5f5345504f4c4941" -} -``` - - - - -### `starkNet_getTransactionStatus` - -Gets the status of a transaction. - -#### Parameters - -- `transactionHash`: `string` - Hash of the target transaction. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -The [status](https://docs.starknet.io/architecture-and-concepts/network-architecture/transaction-life-cycle/) -of the transaction. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_getTransactionStatus", - params: { - transactionHash: "0x05a56e2d52c817161883f50c441c3228cfe54d9f84b5b5b8b1c8b8e0e6f7e6d8", - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -"ACCEPTED_ON_L2" -``` - - - - -### `starkNet_recoverAccounts` - -Recovers deployed user accounts from the seed phrase of MetaMask based on -[BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). - -#### Parameters - -- `startScanIndex`: `integer` - (Optional) Starting address index of the derived key in - [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). - The default is `0`. -- `maxScanned`: `integer` - (Optional) Maximum number of addresses to scan during the recovery process. - The default is `1`. -- `maxMissed`: `integer` - (Optional) Maximum number of uninitialized addresses hit during the - recovery process. - The default is `1`. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -The list of the scanned user accounts during the recovery process. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_recoverAccounts", - params: { - startScanIndex: 0, - maxScanned: 5, - maxMissed: 2, - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -[ - { - "address": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "addressIndex": 0, - "publicKey": "0x04bfcab3b7ca7e8b3f3b62b2f7f77e9e4b68080bbf8f0f4a1c8f890864d2c7c1d3c45d8b2e3f5f1c27dfeea4c2f5733e90bfc7484e2a690aa9b8ac4559d2e6a8d7", - "addressSalt": "0x789abc123456789abc123456789abc123456789abc123456789abc1234567890", - "deployTxnHash": "0x05a56e2d52c817161883f50c441c3228cfe54d9f84b5b5b8b1c8b8e0e6f7e6d8", - "derivationPath": "m/44'/9004'/0'/0/0", - "chainId": "0x534e5f5345504f4c4941" - }, - ... -] -``` - - - - -### `starkNet_signDeclareTransaction` - -Signs a [`DECLARE` transaction](https://docs.starknet.io/architecture-and-concepts/network-architecture/transactions/#declare-transaction) using the private key, returns the signature, and uses it to declare the contract class on Starknet. - -#### Parameters - -- `address`: `string` - Address of the sender. -- `chainId`: `string` - ID of the target Starknet network. -- `details`: `object` - The declare transaction details to be signed, including: - - `nonce`: (Optional) Nonce for the transaction. - - `blockIdentifier`: (Optional) Block identifier for the transaction. - - `maxFee`: (Optional) Maximum gas fee allowed for the transaction. If not specified, the fee is automatically calculated. - - `tip`: (Optional) Additional fee to incentivize miners. - - `paymasterData`: (Optional) Paymaster-related data for the transaction. - - `accountDeploymentData`: (Optional) Data for account deployment. - - `nonceDataAvailabilityMode`: (Optional) Mode for nonce data availability. - - `feeDataAvailabilityMode`: (Optional) Mode for fee data availability. - - `version`: (Optional) The transaction version. - - `resourceBounds`: (Optional) The boundaries on resource consumption during the transaction. - - `skipValidate`: `boolean` - (Optional) Skip validation of the transaction. - - `classHash`: The computed class hash of compiled contract - - `compiledClassHash`: `string` - (Optional) The compiled class hash from casm. - - `senderAddress`: `string` - Address of the sender. - - `chainId`: `string` - ID of the target Starknet network. - -#### Returns - -The signature of the transaction that declares a contract class. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_signDeclareTransaction", - params: { - senderAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - chainId: "0x534e5f5345504f4c4941", - details: { - version: "0x2", - chainId: "0x534e5f5345504f4c4941", - senderAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - classHash: "0x5f3614e8671257aff9ac38e929c74d65b02d460ae966cd826c9f04a7fa8e0d4" - } - }, - }, - }, -}) -``` - - - - -```js -{ - "signature": [ - "0x1b6efab56d39b2acdf5c631c5b1ec1e365e8a36576fa8e3e29ec53566b5782c6", - "0x43a9e7f9f9c9238b4d534c5a7621a9c5946f34e72604b539c7bb1ac0a7c2b95b" - ] -} -``` - - - - -### `starkNet_signMessage` - -Signs a typed data message. - -#### Parameters - -- `typedDataMessage`: `string` - JSON representation of the typed data to be signed. -- `signerAddress`: `string` - Address of the signer. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -The signed hash of typed data. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_signMessage", - params: { - typedDataMessage, - signerAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -"1234567890,9876543210" -``` - - - - -### `starkNet_signTransaction` - -Signs a transaction using the private key and returns the resulting signature. - -#### Parameters - -- `address`: `string` - Address of the sender. -- `chainId`: `string` - ID of the target Starknet network. -- `transactions`: `object` - An array of call objects to be executed. Each call contains the target contract address, function name, and call data. -- `transactionsDetail`: `object` - (Optional) The transaction details to be signed, including: - - `nonce`: (Optional) Nonce for the transaction. - - `blockIdentifier`: (Optional) Block identifier for the transaction. - - `maxFee`: (Optional) Maximum gas fee allowed for the transaction. If not specified, the fee is automatically calculated. - - `tip`: (Optional) Additional fee to incentivize miners. - - `paymasterData`: (Optional) Paymaster-related data for the transaction. - - `accountDeploymentData`: (Optional) Data for account deployment. - - `nonceDataAvailabilityMode`: (Optional) Mode for nonce data availability. - - `feeDataAvailabilityMode`: (Optional) Mode for fee data availability. - - `version`: (Optional) The transaction version. - - `resourceBounds`: (Optional) The boundaries on resource consumption during the transaction. - - `skipValidate`: `boolean` - (Optional) Skip validation of the transaction. - -#### Returns - -The signature of the transaction. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_signTransaction", - params: { - senderAddress: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - chainId: "0x534e5f5345504f4c4941", - transactions: [ - { - entrypoint: "transfer", - calldata: [ - "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", - "1000000000000000000" - ] - } - ] - } - }, - }, -}) -``` - - - - -```json -[ - "0x1b6efab56d39b2acdf5c631c5b1ec1e365e8a36576fa8e3e29ec53566b5782c6", - "0x43a9e7f9f9c9238b4d534c5a7621a9c5946f34e72604b539c7bb1ac0a7c2b95b" -] -``` - - - - -### `starkNet_switchNetwork` - -Switch the current Starknet Snap's network to a different Starknet network. - -#### Parameters - -`chainId`: `string` - ID of the target Starknet network. - -#### Returns - -`true` if the network switch was successful, `false` otherwise. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_switchNetwork", - "params": { - "chainId": "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -true -``` - - - - - -### `starkNet_verifySignedMessage` - -Verifies a signed typed data message. - -#### Parameters - -- `typedDataMessage`: `string` - JSON representation of the original typed data message to be verified. -- `address`: `string` - Address of the signer. -- `signature`: `string` - Signature of the typed data message. -- `chainId`: `string` - (Optional) ID of the target Starknet network. - The default is the Starknet Sepolia testnet. - -#### Returns - -`true` if the signature verification was successful, `false` otherwise. - -#### Example - - - - -```js -await provider.request({           // Or window.ethereum if you don't support EIP-6963. - method: "wallet_invokeSnap", - params: { - snapId: "npm:@consensys/starknet-snap", - request: { - method: "starkNet_verifySignedMessage", - params: { - typedDataMessage: { - "types": { - "StarkNetDomain": [ - { "name": "name", "type": "felt" }, - { "name": "version", "type": "felt" }, - { "name": "chainId", "type": "felt" }, - { "name": "verifyingContract", "type": "felt" } - ], - "Transaction": [ - { "name": "from", "type": "felt" }, - { "name": "to", "type": "felt" }, - { "name": "amount", "type": "felt" } - ] - }, - "primaryType": "Transaction", - "domain": { - "name": "StarkNet", - "version": "1", - "chainId": "0x534e5f5345504f4c4941", - "verifyingContract": "0x6789abcd1234ef567890abcdef1234567890abcd" - }, - "message": { - "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - "to": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", - "amount": "1000000000000000000" - } - }, - address: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", - signature: "0x1b2d3f4e5a6c7b8d9e0f1234567890ab,0x9876543210fedcba0123456789abcdef", - chainId: "0x534e5f5345504f4c4941" - }, - }, - }, -}) -``` - - - - -```json -true -``` - - - diff --git a/wallet/tutorials/javascript-dapp-simple.md b/wallet/tutorials/javascript-dapp-simple.md deleted file mode 100644 index b8fff49a6e2..00000000000 --- a/wallet/tutorials/javascript-dapp-simple.md +++ /dev/null @@ -1,333 +0,0 @@ ---- -description: Create a simple dapp to integrate with MetaMask. ---- - -# Create a simple dapp - -This tutorial walks you through creating a simple JavaScript dapp and integrating it with MetaMask. -It demonstrates the basics of connecting to MetaMask: detecting the MetaMask provider, detecting the user's network, and accessing the user's accounts. - -:::caution Learning tutorial -This tutorial is for educational purposes and connects to MetaMask using the legacy provider object, `window.ethereum`, for the sake of simplicity. -For deployment in a production environment, we recommend [connecting to MetaMask using EIP-6963](../how-to/connect-extension.md) instead. - -[EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) introduces an alternative wallet detection mechanism to the `window.ethereum` provider, and enables dapps to support [wallet interoperability](../concepts/wallet-interoperability.md). - -For a full end-to-end tutorial that can be used in production, see the -[Create a simple React dapp](../tutorials/react-dapp-local-state.md) tutorial. -::: - -## Prerequisites - -- [Node.js](https://nodejs.org/en/) version 20+ -- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) version 9+ -- A text editor of your choice, such as [VS Code](https://code.visualstudio.com/). -- [MetaMask](https://metamask.io/) installed in the browser of your choice on your development machine. - -## Steps - -### 1. Set up the project - -Create a new project using [Vite](https://vitejs.dev/guide/): - -```bash -npm create vite@latest simple-dapp -- --template vanilla -``` - -Change into your project directory: - -```bash -cd simple-dapp -``` - -Install the dependencies listed in the project's `package.json`: - -```bash -npm install -``` - -### 2. Create the dapp structure - -In your project directory, create a `main.js` file: - -```bash -touch main.js -``` - -In `main.js`, add the following: - -```js title="main.js" -import "./style.css" - -document.querySelector("#app").innerHTML = ` - -

    Account:

    ` -``` - -Update `index.html` to include the script: - -```html title="index.html" - - - - - - - Simple dapp - - -
    - - - -``` - -### 3. Detect MetaMask - -:::caution -The `@metamask/detect-provider` module is deprecated, and is only used here for educational purposes. -In production environments, we recommend [connecting to MetaMask using EIP-6963](../how-to/connect-extension.md). -::: - -Install the `@metamask/detect-provider` module in your project directory: - -```bash -npm i @metamask/detect-provider -``` - -Create a `src` directory and create a new file `detect.js`: - -```bash -mkdir src && touch src/detect.js -``` - -In a text editor, add the following code to `src/detect.js` to detect the MetaMask provider using `@metamask/detect-provider`: - -```js title="detect.js" -import detectEthereumProvider from "@metamask/detect-provider" - -async function setup() { - const provider = await detectEthereumProvider() - - if (provider && provider === window.ethereum) { - console.log("MetaMask is available!") - startApp(provider) // Initialize your dapp with MetaMask. - } else { - console.log("Please install MetaMask!") - } -} - -function startApp(provider) { - if (provider !== window.ethereum) { - console.error("Do you have multiple wallets installed?") - } -} - -window.addEventListener("load", setup) -``` - -### 4. Detect a user's network - -[Detect the user's network](../how-to/manage-networks/detect-network.md) to ensure all RPC requests -are submitted to the currently connected network. -Add the following code to `src/detect.js`, which uses the [`eth_chainId`](/wallet/reference/json-rpc-methods/eth_chainid) -RPC method to detect the chain ID of the user's current network, and listens to the -[`chainChanged`](/wallet/reference/provider-api/#chainchanged) provider event to detect when the -user changes networks: - -```js title="detect.js" -const chainId = await window.ethereum.request({ method: "eth_chainId" }) - -window.ethereum.on("chainChanged", handleChainChanged) - -function handleChainChanged(chainId) { - // We recommend reloading the page, unless you must do otherwise. - window.location.reload() -} -``` - -### 5. Access a user's accounts - -To interact with Ethereum on the user's behalf, such as sending transactions or requesting balances, -your dapp needs to [access the user's accounts](../how-to/access-accounts.md) by calling -[`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts). - -Add the following code to `src/detect.js`, which creates a button to allow users to connect to -MetaMask from your dapp. -Selecting the button activates the call to `eth_requestAccounts`, allowing you to access the user's accounts. - -```jsx title="detect.js" -// You should only attempt to request the user's account in response to user interaction, such as -// selecting a button. Otherwise, you risk spamming the user. If you fail to retrieve -// the user's account, you should encourage the user to initiate the attempt. -const ethereumButton = document.querySelector(".enableEthereumButton") -const showAccount = document.querySelector(".showAccount") - -ethereumButton.addEventListener("click", () => { - getAccount() -}) - -// While awaiting the call to eth_requestAccounts, you should disable any buttons the user can -// select to initiate the request. MetaMask rejects any additional requests while the first is still -// pending. -async function getAccount() { - const accounts = await window.ethereum - .request({ method: "eth_requestAccounts" }) - .catch((err) => { - if (err.code === 4001) { - // EIP-1193 userRejectedRequest error. - // If this happens, the user rejected the connection request. - console.log("Please connect to MetaMask.") - } else { - console.error(err) - } - }) - const account = accounts[0] - showAccount.innerHTML = account -} -``` - -Update `index.html` with the following HTML code, which displays the button and the current account: - -```html title="index.html" - - - - - - - Simple dapp - - - - - - -

    Account:

    - - -``` - -Save your changes and run the following command in your project directory to start a local -development server: - -```bash -npm run dev -``` - -Navigate to the local server URL to view the live dapp. -Something like the following displays: - -

    -Enable button -

    - -When you select the **Enable Ethereum** button, you are prompted to connect to MetaMask. - -![Connect and access dapp](../assets/tutorials/beginner-tutorial/connect.png) - -After connecting, your connected account displays: - -

    -View account -

    - -## Example - -The following code samples contain the full simple dapp JavaScript and HTML code that this tutorial walks through. -You can copy the following full examples to get started quickly. - -### JavaScript - -```jsx title="detect.js" -/*****************************************/ -/* Detect the MetaMask Ethereum provider */ -/*****************************************/ - -import detectEthereumProvider from "@metamask/detect-provider" - -async function setup() { - const provider = await detectEthereumProvider() - - if (provider && provider === window.ethereum) { - console.log("MetaMask is available!") - startApp(provider) - } else { - console.log("Please install MetaMask!") - } -} - -function startApp(provider) { - if (provider !== window.ethereum) { - console.error("Do you have multiple wallets installed?") - } -} - -window.addEventListener("load", setup) - -/**********************************************************/ -/* Handle chain (network) and chainChanged (per EIP-1193) */ -/**********************************************************/ - -const chainId = await window.ethereum.request({ method: "eth_chainId" }) - -window.ethereum.on("chainChanged", handleChainChanged) - -function handleChainChanged(chainId) { - window.location.reload() -} - -/*********************************************/ -/* Access the user's accounts (per EIP-1102) */ -/*********************************************/ - -const ethereumButton = document.querySelector(".enableEthereumButton") -const showAccount = document.querySelector(".showAccount") - -ethereumButton.addEventListener("click", () => { - getAccount() -}) - -async function getAccount() { - const accounts = await window.ethereum - .request({ method: "eth_requestAccounts" }) - .catch((err) => { - if (err.code === 4001) { - console.log("Please connect to MetaMask.") - } else { - console.error(err) - } - }) - const account = accounts[0] - showAccount.innerHTML = account -} -``` - -### HTML - -```html title="index.html" - - - - - - - Simple dapp - - - - - -

    Account:

    - - -``` - -## Next steps - -You've successfully created a simple dapp and connected it to MetaMask using JavaScript, Vite, and the `window.ethereum` provider. -With this setup, your dapp can interact with MetaMask and allow users to securely access accounts and send transactions on the Ethereum blockchain. - -As a next step, you can create a [React dapp with local state](react-dapp-local-state.md). -This follow-up tutorial walks you through integrating a simple React dapp with MetaMask using a -single JSX component for managing local state, and the Vite build tool with React and TypeScript to create the dapp. diff --git a/wallet/tutorials/react-dapp-global-state.md b/wallet/tutorials/react-dapp-global-state.md deleted file mode 100644 index 0f892ce61c5..00000000000 --- a/wallet/tutorials/react-dapp-global-state.md +++ /dev/null @@ -1,820 +0,0 @@ ---- -description: Create a multi-component React dapp with global state using EIP-6963. -toc_max_heading_level: 4 ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -# Create a React dapp with global state - -This tutorial walks you through integrating a React dapp with MetaMask. -The dapp has multiple components and requires managing the state globally, which can be helpful for -real-world use cases. -You'll use the [Vite](https://v3.vitejs.dev/guide) build tool with React and TypeScript to create -the dapp. - -The final state of the dapp will look like the following: - -![React dapp with global state](../assets/tutorials/react-dapp/react-tutorial-02-final-preview.png) - -In this tutorial, you'll put the state into a [React -Context](https://react.dev/reference/react/useContext) component, creating a [global -state](https://react.dev/learn/reusing-logic-with-custom-hooks#custom-hooks-sharing-logic-between-components) -that allows other components and UI elements to benefit from its data and functions. -You'll use `localStorage` to persist the selected wallet, ensuring the last connected wallet state -remains intact even after a page refresh. - -This tutorial addresses the edge case where a browser wallet might be disabled or uninstalled -between refreshes or visits to the dapp. -You'll add a disconnect function to reset the state, and use -[`wallet_revokePermissions`](/wallet/reference/json-rpc-methods/wallet_revokepermissions) to properly disconnect from MetaMask. - -:::info Project source code -You can view the [dapp source code on GitHub](https://github.com/MetaMask/vite-react-global-tutorial). -::: - -## Prerequisites - -- [Node.js](https://nodejs.org/) version 18+ -- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) version 9+ -- A text editor (for example, [VS Code](https://code.visualstudio.com/)) -- The [MetaMask extension](https://metamask.io/download) installed -- Basic knowledge of TypeScript, React, React Context, and React Hooks - -:::tip -We recommend following the [Create a React dapp with local state](react-dapp-local-state.md) -tutorial first, which introduces [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963). -The tutorial demonstrates how to iterate over all discovered providers, connect to the selected -wallet, and remember the selection within a single component. - -If you skip the tutorial, consider reviewing [wallet -interoperability](../concepts/wallet-interoperability.md) to understand how multiple injected wallet -providers work. -::: - -## Steps - -### 1. Set up the project - -This project introduces a new structure, independent of previous tutorials. -Instead of reusing code or states, this tutorial guides you through breaking down the -single-component structure into multiple components. - -Set up a new project using Vite, React, and TypeScript by running the following command: - -```bash -npm create vite@latest vite-react-global-state -- --template react-ts -``` - -Install the node module dependencies: - -```bash -cd vite-react-global-state && npm install -``` - -Launch the development server: - -```bash -npm run dev -``` - -This displays a `localhost` URL in your terminal, where you can view the dapp in your browser. - -:::note -If you use VS Code, you can run the command `code .` to open the project. -If the development server has stopped, you can run the command `npx vite` or `npm run dev` to -restart your project. -::: - -Open the project in your editor. -Create three directories, `src/components`, `src/hooks`, and `src/utils`, in the root of the project -using the following commands: - -```bash -mkdir src/components && mkdir src/hooks && mkdir src/utils -``` - -Create the following files in `src/components`, which will be used to create components for listing -installed wallets, displaying connected wallet information, and handling errors: - -- `WalletList.tsx` -- `WalletList.module.css` -- `SelectedWallet.tsx` -- `SelectedWallet.module.css` -- `WalletError.tsx` -- `WalletError.module.css` - -Create the following files in `src/hooks`: - -- `Eip6963Provider.tsx` -- `useEip6963Provider.tsx` - -Create the following file in `src/utils`: - -- `index.ts` - -#### 1.1. Style the components - -Add the following CSS code to `SelectedWallet.module.css`: - -```css title="SelectedWallet.module.css" -.selectedWallet { - display: flex; - flex-flow: row nowrap; - justify-content: flex-start; - - padding: 0.6em 1.2em; - margin-bottom: 0.5em; - - font-family: inherit; - font-size: 1em; - font-weight: 500; -} -.selectedWallet > img { - width: 2em; - height: 1.5em; - margin-right: 1em; -} - -.providers { - display: flex; - flex-flow: column wrap; - justify-content: center; - align-items: center; - align-content: center; - - padding: 0.6em 1.2em; -} -``` - -Add the following CSS code to `WalletError.module.css`: - -```css title="WalletError.module.css" -.walletError { - margin-top: 1em; - border-radius: 0.5em; - height: 36px; - padding: 16px; - color: #efefef; - background-color: transparent; - user-select: none; -} -``` - -Add the following CSS code to `WalletList.module.css`: - -```css title="WalletList.module.css" -.walletList { - display: flex; - flex-direction: column; - align-items: center; -} -``` - -Append the following code to the end of `src/index.css`: - -```css title="index.css" -/* Added CSS */ -:root { - text-align: left; -} - -hr { - margin-top: 2em; - height: 1px; -} - -button { - min-width: 12em; - display: flex; - flex-flow: row nowrap; - justify-content: flex-start; - - align-items: center; - border-radius: 0.5em; - margin-bottom: 0.5em; - border: 1px solid transparent; -} - -button > img { - width: 1.5em; - height: 1.5em; - margin-right: 1em; -} - -button:hover { - border-color: #75079d; -} - -button:first-child { - margin-top: 0.5em; -} -button:last-child { - margin-bottom: 0; -} -``` - -#### 1.2. Project structure - -You now have some basic global and component-level styling for your dapp. -The directory structure in the dapp's `/src` directory should look like the following: - -```text -├── src -│ ├── assets -│ ├── components -│ │ ├── SelectedWallet.module.css -│ │ ├── SelectedWallet.tsx -│ │ ├── WalletError.module.css -│ │ ├── WalletError.tsx -│ │ ├── WalletList.module.css -│ │ └── WalletList.tsx -│ ├── hooks -│ │ ├── WalletProvider.tsx -│ │ └── useWalletProvider.tsx -│ ├── utils -│ │ └── index.tsx -├── App.css -├── App.tsx -├── index.css -├── main.tsx -├── vite-env.d.ts -``` - -### 2. Import EIP-6963 interfaces - -The dapp will connect to MetaMask using the mechanism introduced by -[EIP-6963](https://eips.ethereum.org/EIPS/eip-6963). - -:::info Why EIP-6963? -[EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) introduces an alternative wallet detection -mechanism to the `window.ethereum` injected provider. -This alternative mechanism enables dapps to support -[wallet interoperability](../concepts/wallet-interoperability.md) by discovering multiple injected -wallet providers in a user's browser. -::: - -Update the Vite environment variable file, `src/vite-env.d.ts`, with the types and interfaces -needed for [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) and -[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193): - -```tsx title="vite-env.d.ts" -/// - -// Describes metadata related to a provider based on EIP-6963. -interface EIP6963ProviderInfo { - rdns: string - uuid: string - name: string - icon: string -} - -// Represents the structure of a provider based on EIP-1193. -interface EIP1193Provider { - isStatus?: boolean - host?: string - path?: string - sendAsync?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - send?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - request: (request: { - method: string - params?: Array - }) => Promise -} - -// Combines the provider's metadata with an actual provider object, creating a complete picture of a -// wallet provider at a glance. -interface EIP6963ProviderDetail { - info: EIP6963ProviderInfo - provider: EIP1193Provider -} - -// Represents the structure of an event dispatched by a wallet to announce its presence based on EIP-6963. -type EIP6963AnnounceProviderEvent = { - detail: { - info: EIP6963ProviderInfo - provider: Readonly - } -} - -// An error object with optional properties, commonly encountered when handling eth_requestAccounts errors. -interface WalletError { - code?: string - message?: string -} -``` - -### 3. Build the context provider - -In this step, you'll create the React Context component, which wraps the dapp and provides all -components access to the state and functions required to modify the state and manage connections to -discovered wallets. - -Add the following code to `src/hooks/WalletProvider.tsx` to import the context, define the -type alias, and define the context interface for the EIP-6963 provider: - -```tsx title="WalletProvider.tsx" -import { - PropsWithChildren, - createContext, - useCallback, - useEffect, - useState, -} from "react" - -// Type alias for a record where the keys are wallet identifiers and the values are account -// addresses or null. -type SelectedAccountByWallet = Record - -// Context interface for the EIP-6963 provider. -interface WalletProviderContext { - wallets: Record // A list of wallets. - selectedWallet: EIP6963ProviderDetail | null // The selected wallet. - selectedAccount: string | null // The selected account address. - errorMessage: string | null // An error message. - connectWallet: (walletUuid: string) => Promise // Function to connect wallets. - disconnectWallet: () => void // Function to disconnect wallets. - clearError: () => void -} -``` - -Add the following code to `src/hooks/WalletProvider.tsx` to extend the global `WindowEventMap` -interface with the custom `eip6963:announceProvider` event: - -```tsx title="WalletProvider.tsx" -declare global { - interface WindowEventMap { - "eip6963:announceProvider": CustomEvent - } -} -``` - -Explicitly declaring the custom `eip6963:announceProvider` event prevents type errors, enables -proper type checking, and supports autocompletion in TypeScript. - -Add the following code to `src/hooks/WalletProvider.tsx` to create the React Context for the -EIP-6963 provider with the defined interface `WalletProviderContext`, and define the -`WalletProvider` component: - -```tsx title="WalletProvider.tsx" showLineNumbers {6-12,14} -export const WalletProviderContext = createContext(null) - -// The WalletProvider component wraps all other components in the dapp, providing them with the -// necessary data and functions related to wallets. -export const WalletProvider: React.FC = ({ children }) => { - const [wallets, setWallets] = useState>({}) - const [selectedWalletRdns, setSelectedWalletRdns] = useState(null) - const [selectedAccountByWalletRdns, setSelectedAccountByWalletRdns] = useState({}) - - const [errorMessage, setErrorMessage] = useState("") - const clearError = () => setErrorMessage("") - const setError = (error: string) => setErrorMessage(error) - - useEffect(() => { - const savedSelectedWalletRdns = localStorage.getItem("selectedWalletRdns") - const savedSelectedAccountByWalletRdns = localStorage.getItem("selectedAccountByWalletRdns") - - if (savedSelectedAccountByWalletRdns) { - setSelectedAccountByWalletRdns(JSON.parse(savedSelectedAccountByWalletRdns)) - } - - function onAnnouncement(event: EIP6963AnnounceProviderEvent){ - setWallets(currentWallets => ({ - ...currentWallets, - [event.detail.info.rdns]: event.detail - })) - - if (savedSelectedWalletRdns && event.detail.info.rdns === savedSelectedWalletRdns) { - setSelectedWalletRdns(savedSelectedWalletRdns) - } - } - - window.addEventListener("eip6963:announceProvider", onAnnouncement) - window.dispatchEvent(new Event("eip6963:requestProvider")) - - return () => window.removeEventListener("eip6963:announceProvider", onAnnouncement) - }, []) -``` - -In this code sample, lines 6–12 are state definitions: - -- `wallets` - State to hold detected wallets. -- `selectedWalletRdns` - State to hold the Reverse Domain Name System (RDNS) of the selected wallet. -- `selectedAccountByWalletRdns` - State to hold accounts associated with each wallet. -- `errorMessage` - State to hold the error message when a wallet throws an error on connection. -- `clearError` - Function to clear the state in `errorMessage`. -- `setError` - Function to set the state in `errorMessage`. - -Line 14 is the `useEffect` hook and it handles the following: - -- Local storage retrieval - On mount, it retrieves the saved selected wallet and accounts from local storage. -- Event listener - It adds an event listener for the custom `eip6963:announceProvider` event. -- State update - When the provider announces itself, it updates the state. -- Provider request - It dispatches an event to request existing providers. -- Cleanup - It removes the event listener on unmount. - -Add the following code to `src/hooks/WalletProvider.tsx` to connect a wallet and update the component's state: - -```tsx title="WalletProvider.tsx" -const connectWallet = useCallback( - async (walletRdns: string) => { - try { - const wallet = wallets[walletRdns] - const accounts = (await wallet.provider.request({ - method: "eth_requestAccounts", - })) as string[] - - if (accounts?.[0]) { - setSelectedWalletRdns(wallet.info.rdns) - setSelectedAccountByWalletRdns((currentAccounts) => ({ - ...currentAccounts, - [wallet.info.rdns]: accounts[0], - })) - - localStorage.setItem("selectedWalletRdns", wallet.info.rdns) - localStorage.setItem( - "selectedAccountByWalletRdns", - JSON.stringify({ - ...selectedAccountByWalletRdns, - [wallet.info.rdns]: accounts[0], - }) - ) - } - } catch (error) { - console.error("Failed to connect to provider:", error) - const walletError: WalletError = error as WalletError - setError( - `Code: ${walletError.code} \nError Message: ${walletError.message}` - ) - } - }, - [wallets, selectedAccountByWalletRdns] -) -``` - -This code uses the `walletRdns` parameter to identify the wallet's RDNS for connecting. -It performs an asynchronous operation to request accounts from the wallet provider using the -[`eth_requestAccounts`](/wallet/reference/json-rpc-methods/eth_requestaccounts) RPC method. - -Add the following code to `src/hooks/WalletProvider.tsx` to disconnect from a wallet: - -```tsx title="WalletProvider.tsx" -const disconnectWallet = useCallback(async () => { - if (selectedWalletRdns) { - setSelectedAccountByWalletRdns((currentAccounts) => ({ - ...currentAccounts, - [selectedWalletRdns]: null, - })) - - const wallet = wallets[selectedWalletRdns] - setSelectedWalletRdns(null) - localStorage.removeItem("selectedWalletRdns") - - try { - await wallet.provider.request({ - method: "wallet_revokePermissions", - params: [{ eth_accounts: {} }], - }) - } catch (error) { - console.error("Failed to revoke permissions:", error) - } - } -}, [selectedWalletRdns, wallets]) -``` - -:::caution important -[`wallet_revokePermission`](/wallet/reference/json-rpc-methods/wallet_revokepermissions) is an experimental RPC -method that might only work with MetaMask. -Configuring the revocation in a try/catch block and separating it from the rest of the cleanup -ensures that if a wallet does not support this feature, the rest of the disconnect functionality -will still execute. -::: - -
    -Use of `useCallback` -
    -Both of the previous functions use `useCallback`. -It is used to memoize the `connectWallet` function, optimize performance, and prevent unnecessary re-renders. -It ensures the function instance remains consistent between renders if its dependencies are changed. - -For example, when using `disconnectWallet`, each time the `WalletProvider` component re-renders -without `useCallback`, a new instance of `disconnectWallet` is created. -This can cause unnecessary re-renders of child components that depend on this function. -By memoizing it with `useCallback`, React keeps the function instance consistent between renders, as -long as its dependencies (wallets and `selectedWalletRdns`) haven't changed, preventing unnecessary -re-renders of child components. - -Although `useCallback` is not necessary, it demonstrates best practices. -Predicting how a context provider will be used or how the dapp might change or scale is difficult. -Using `useCallback` can improve performance in some cases by reducing unnecessary re-renders. -
    -
    - -Add the following code to `src/hooks/WalletProvider.tsx` to bundle the state and functions using `contextValue`: - -```tsx title="WalletProvider.tsx" -const contextValue: WalletProviderContext = { - wallets, - selectedWallet: - selectedWalletRdns === null ? null : wallets[selectedWalletRdns], - selectedAccount: - selectedWalletRdns === null - ? null - : selectedAccountByWalletRdns[selectedWalletRdns], - errorMessage, - connectWallet, - disconnectWallet, - clearError, -} - -return ( - - {children} - -) -``` - -In the return statement, the `contextValue` object is constructed with all necessary state and -functions related to wallet management. -It is passed to the `WalletProviderContext.Provider`, making wallet-related data and functions -available to all descendant components. -The context provider wraps the children components, allowing them to access the context values. - -Add the following code to `src/hooks/useWalletProvider.tsx` to provide a custom hook that simplifies -the process of consuming the `WalletProviderContext`: - -```tsx title="useWalletProvider.tsx" -import { useContext } from "react" -import { WalletProviderContext } from "./WalletProvider" - -export const useWalletProvider = () => useContext(WalletProviderContext) -``` - -The benefit of this separate file exporting the hook is that components can directly call -`useWalletProvider()` instead of `useContext(WalletProviderContext)`, making the code cleaner and -more readable. - -### 4. Update the utility file - -Add the following code to `src/utils/index.ts`: - -```ts title="index.ts" -export const formatBalance = (rawBalance: string) => { - const balance = (parseInt(rawBalance) / 1000000000000000000).toFixed(2) - return balance -} - -export const formatChainAsNum = (chainIdHex: string) => { - const chainIdNum = parseInt(chainIdHex) - return chainIdNum -} - -export const formatAddress = (addr: string) => { - const upperAfterLastTwo = addr.slice(0, 2) + addr.slice(2) - return `${upperAfterLastTwo.substring(0, 5)}...${upperAfterLastTwo.substring(39)}` -} -``` - -Although `formatAddress` is the only function used, `formatBalance` and `formatChainAsNum` are -added as useful utility functions. -Explore [Viem formatters](https://viem.sh/docs/chains/formatters) or other libraries for additional -formatting options. - -### 5. Wrap components with the context provider - -With `WalletProvider.tsx` and `useWalletProvider.tsx`, the dapp can manage and access wallet-related -state and functionality across various components. -You can now wrap the entire dapp (the part that requires wallet connection and data) with a -`WalletProvider` component. - -Replace the code in `src/App.tsx` with the following: - -```tsx title="App.tsx" -import "./App.css" -import { WalletProvider } from "~/hooks/WalletProvider" -// import { WalletList } from "./components/WalletList" -// import { SelectedWallet } from "./components/SelectedWallet" -// import { WalletError } from "./components/WalletError" - -function App() { - return ( - - {/* - -
    - - - */} -
    - ) -} - -export default App -``` - -The child components are currently commented out, but as you create each of these components, you'll -uncomment the specific lines. - -### 6. Display detected wallets - -Add the following code to `src/components/WalletList.tsx` to display detected wallets: - -```tsx title="WalletList.tsx" -import { useWalletProvider } from "~/hooks/useWalletProvider" -import styles from "./WalletList.module.css" - -export const WalletList = () => { - const { wallets, connectWallet } = useWalletProvider() - return ( - <> -

    Wallets Detected:

    -
    - {Object.keys(wallets).length > 0 ? ( - Object.values(wallets).map((provider: EIP6963ProviderDetail) => ( - - )) - ) : ( -
    there are no Announced Providers
    - )} -
    - - ) -} -``` - -This component checks if there are any detected wallets. -If wallets are detected, it iterates over them and renders a button for each one. - -- `Object.keys(wallets)` returns an array of the wallet keys (`rdns` values). - It is used to check the length. -- `Object.values(wallets)` returns an array of the wallet objects. - It is used to map and render. -- `wallet.info.rdns` is used as the key to ensure that each wallet button is uniquely identified. - -Uncomment the `WalletList` component in `src/App.tsx` and run the dapp. -Something like the following displays: - -![View of WalletList component](../assets/tutorials/react-dapp/react-tutorial-02-wallet-list.png) - -### 7. Display wallet data - -Add the following code to `src/components/SelectedWallet.tsx` to display data for the selected wallet: - -```tsx title="SelectedWallet.tsx" showLineNumbers {11-22} -import { useWalletProvider } from "~/hooks/useWalletProvider" -import { formatAddress } from "~/utils" -import styles from "./SelectedWallet.module.css" - -export const SelectedWallet = () => { - const { selectedWallet, selectedAccount, disconnectWallet } = - useWalletProvider() - - return ( - <> -

    - {selectedAccount ? "" : "No "}Wallet Selected -

    - {selectedAccount && ( - <> -
    - {selectedWallet.info.name} -
    {selectedWallet.info.name}
    -
    ({formatAddress(selectedAccount)})
    -
    - uuid: {selectedWallet.info.uuid} -
    -
    - rdns: {selectedWallet.info.rdns} -
    -
    - - - )} - - ) -} -``` - -The code in lines 11-22 have conditional rendering, ensuring that the content inside is only -displayed if `selectedAccount` is true. This ensures that detailed information about the selected wallet is only displayed when an active -wallet is connected. - -You can display information about the wallet, and conditionally render anything related to the following: - -- Wallet address -- Wallet balance -- Chain ID or name -- Other components that first need a connected wallet to work - -Uncomment the `SelectedWallet` component in `src/App.tsx` and run the dapp. -When you connect to MetaMask, something like the following displays: - -![View of SelectedWallet component](../assets/tutorials/react-dapp/react-tutorial-02-selected-wallet.png) - -### 8. Display wallet connection errors - -Add the following code to `src/components/WalletError.tsx` to handle wallet connection errors: - -```tsx title="WalletError.tsx" -import { useWalletProvider } from "~/hooks/useWalletProvider" -import styles from "./WalletError.module.css" - -export const WalletError = () => { - const { errorMessage, clearError } = useWalletProvider() - const isError = !!errorMessage - - return ( -
    - {isError && ( -
    - Error: {errorMessage} -
    - )} -
    - ) -} -``` - -An error message renders only if `errorMessage` contains data. -After the error is selected, `errorMessage` resets to an empty string, which hides the content. - -This method demonstrates how to display specific content, such as a modal or notification, in -response to connection errors when connecting to a wallet. - -Uncomment the `WalletError` component in `src/App.tsx` and run the dapp. -Disconnect from MetaMask, reconnect, and reject or cancel the connection. -Something like the following displays: - -![View of WalletError component](../assets/tutorials/react-dapp/react-tutorial-02-wallet-error.png) - -### 9. Run the final state of the dapp - -Make sure all code in `App.tsx` is uncommented: - -```tsx title="App.tsx" -import "./App.css" -import { WalletProvider } from "~/hooks/WalletProvider" -import { WalletList } from "./components/WalletList" -import { SelectedWallet } from "./components/SelectedWallet" -import { WalletError } from "./components/WalletError" - -function App() { - return ( - - -
    - - -
    - ) -} - -export default App -``` - -Run the dapp to view the wallet list and select a wallet to connect to. -The final state of the dapp when connected to MetaMask looks like the following: - -![Final view of dapp](../assets/tutorials/react-dapp/react-tutorial-02-final-preview.png) - -### 10. Test the dapp features - -You can conduct user tests to evaluate the functionality and features demonstrated in this tutorial: - -1. Test the ability to connect and disconnect from multiple wallets installed in your browser. -2. After selecting a wallet, refresh the page and ensure that the selected wallet persists without - reverting to **No Wallet Selected**. -3. Select a wallet, disable it, refresh the page, then re-enable the wallet and refresh the page again. - Observe the behavior of the dapp. -4. When connecting to a wallet, cancel the connection or close the wallet prompt. - This action should trigger the `WalletError` component, which you can dismiss by selecting it. - -## Conclusion - -This tutorial guided you through applying EIP-6963 to connect to MetaMask. -This method also works with any wallet that [complies with -EIP-6963](https://github.com/WalletConnect/EIP6963/blob/master/src/utils/constants.ts) and supports -multi-injected provider discovery. - -In this tutorial, you addressed edge cases and created a context provider that facilitates data -sharing, manages functions for connecting and disconnecting from wallets, and handles errors. -You can view the [project source code on GitHub](https://github.com/MetaMask/vite-react-global-tutorial). diff --git a/wallet/tutorials/react-dapp-local-state.md b/wallet/tutorials/react-dapp-local-state.md deleted file mode 100644 index a104c8fca5f..00000000000 --- a/wallet/tutorials/react-dapp-local-state.md +++ /dev/null @@ -1,444 +0,0 @@ ---- -description: Create a single component React dapp with local state using EIP-6963. ---- - -# Create a React dapp with local state - -This tutorial walks you through integrating a simple React dapp with MetaMask. -The dapp has a single JSX component, which is used for managing local state. -You'll use the [Vite](https://v3.vitejs.dev/guide) build tool with React and TypeScript to create -the dapp. - -:::tip Why React? -React is familiar to most web developers and is standard in web3. -It makes it easy to work with state management, build components that use a one-way data flow, and -re-render those components upon state changes. -::: - -:::info Project source code -You can view the [dapp source code on GitHub](https://github.com/MetaMask/vite-react-local-tutorial). -::: - -## Prerequisites - -- [Node.js](https://nodejs.org/) version 18+ -- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) version 9+ -- A text editor (for example, [VS Code](https://code.visualstudio.com/)) -- The [MetaMask extension](https://metamask.io/download) installed -- Basic knowledge of TypeScript and React - -## Steps - -### 1. Set up the project - -Set up a new project using Vite, React, and TypeScript by running the following command: - -```bash -npm create vite@latest vite-react-local-state -- --template react-ts -``` - -Install the node module dependencies: - -```bash -cd vite-react-local-state && npm install -``` - -Launch the development server: - -```bash -npm run dev -``` - -This displays a `localhost` URL in your terminal, where you can view the dapp in your browser. - -:::note -If you use VS Code, you can run the command `code .` to open the project. -If the development server has stopped, you can run the command `npx vite` or `npm run dev` to -restart your project. -::: - -Open the project in your editor. -To start with a blank slate, replace the code in `src/App.tsx` with the following: - -```tsx title="App.tsx" -import "./App.css" - -const App = () => { - return ( -
    -

    Wallets Detected:

    -
    - ) -} - -export default App -``` - -### 2. Import EIP-6963 interfaces - -The dapp will connect to MetaMask using the mechanism introduced by -[EIP-6963](https://eips.ethereum.org/EIPS/eip-6963). - -:::info Why EIP-6963? -[EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) introduces an alternative wallet detection -mechanism to the `window.ethereum` injected provider. -This alternative mechanism enables dapps to support -[wallet interoperability](../concepts/wallet-interoperability.md) by discovering multiple injected -wallet providers in a user's browser. -::: - -Update the Vite environment variable file, `src/vite-env.d.ts`, with the types and interfaces -needed for [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) and -[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193): - -```tsx title="vite-env.d.ts" -/// - -// Describes metadata related to a provider based on EIP-6963. -interface EIP6963ProviderInfo { - walletId: string - uuid: string - name: string - icon: string -} - -// Represents the structure of a provider based on EIP-1193. -interface EIP1193Provider { - isStatus?: boolean - host?: string - path?: string - sendAsync?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - send?: ( - request: { method: string; params?: Array }, - callback: (error: Error | null, response: unknown) => void - ) => void - request: (request: { - method: string - params?: Array - }) => Promise -} - -// Combines the provider's metadata with an actual provider object, creating a complete picture of a -// wallet provider at a glance. -interface EIP6963ProviderDetail { - info: EIP6963ProviderInfo - provider: EIP1193Provider -} - -// Represents the structure of an event dispatched by a wallet to announce its presence based on EIP-6963. -type EIP6963AnnounceProviderEvent = { - detail: { - info: EIP6963ProviderInfo - provider: EIP1193Provider - } -} - -// An error object with optional properties, commonly encountered when handling eth_requestAccounts errors. -interface MMError { - code?: string - message?: string -} -``` - -### 3. Create store file - -Create a store file to manage the state of the detected wallet providers. -This file provides a centralized place to store and synchronize the detected wallet providers, -ensuring that your dapp always has access to the latest provider information. - -Create a `src/hooks` directory, and create a file `store.ts` in that directory with the following code: - -```ts title="store.ts" -// Extends WindowEventMap interface, including a custom event eip6963:announceProvider. -declare global { - interface WindowEventMap { - "eip6963:announceProvider": CustomEvent - } -} - -// Array that stores detected wallet providers and their details. -let providers: EIP6963ProviderDetail[] = [] - -// Object containing two methods. The store holds the state of detected Ethereum wallet providers. -// It's implemented as an external store, making it available for subscription and synchronization -// across the dapp. -export const store = { - // Returns the current state of providers. - value: () => providers, - // Subscribes to provider announcements and updates the store accordingly. - // Takes a callback function to be invoked on each store update, returning a function to - // unsubscribe from the event. - subscribe: (callback: () => void) => { - function onAnnouncement(event: EIP6963AnnounceProviderEvent) { - if (providers.map((p) => p.info.uuid).includes(event.detail.info.uuid)) - return - providers = [...providers, event.detail] - callback() - } - window.addEventListener("eip6963:announceProvider", onAnnouncement) - window.dispatchEvent(new Event("eip6963:requestProvider")) - - return () => - window.removeEventListener("eip6963:announceProvider", onAnnouncement) - }, -} -``` - -### 4. Sync provider state with React component - -With the store in place, create a custom hook that synchronizes the provider state with the React component. -Use the [`useSyncExternalStore`](https://react.dev/reference/react/useSyncExternalStore) React hook -to subscribe to changes in the provider store, and to ensure the component re-renders whenever -the store updates. - -Create a file `useSyncProviders.ts` in the `hooks` directory with the following code: - -```tsx title="useSyncProviders.ts" -import { useSyncExternalStore } from "react" -import { store } from "./store" - -export const useSyncProviders = () => - useSyncExternalStore(store.subscribe, store.value, store.value) -``` - -`useSyncExternalStore` takes three arguments: - -- A subscription function to listen for changes in the external store (`store.subscribe`). -- A function to get the current value of the store (`store.value`). -- An initial value for the store (`store.value`). - -:::note -As an alternative to `useSyncExternalStore`, you can use the `useState` React hook to manage the -provider state, and the `useEffect` React hook to subscribe to changes in the store. -When the React component mounts, you can subscribe to changes in the store, set the initial state -using the current value from the store, and return a cleanup function to unsubscribe from the store -when the component unmounts. -::: - -### 5. Create connect buttons - -Create an array of buttons that the user can select to connect to the EIP-6963 wallet providers that -you detect. - -Update `src/App.tsx` to the following: - -```tsx title="App.tsx" -import { useSyncProviders } from "./hooks/useSyncProviders" -import "./App.css" - -const App = () => { - const providers = useSyncProviders() - - const handleConnect = async (providerWithInfo: EIP6963ProviderDetail) => { - try { - const accounts = (await providerWithInfo.provider.request({ - method: "eth_requestAccounts", - })) as string[] - } catch (error) { - console.error(error) - } - } - - return ( -
    -

    Wallets Detected:

    -
    - {providers.length > 0 ? ( - providers?.map((provider: EIP6963ProviderDetail) => ( - - )) - ) : ( -
    No Announced Wallet Providers
    - )} -
    -
    - ) -} - -export default App -``` - -To style the buttons, update `src/App.css` to the following: - -```css title="App.css" -.App { - min-width: 100vw; - min-height: 100vh; - text-align: center; -} - -.providers { - display: flex; - flex-flow: column wrap; - justify-content: space-between; - align-items: center; - align-content: center; - gap: 1em; - - padding: 0.6em 1.2em; -} - -.providers button { - width: 12em; -} - -.providers button img { - width: 2em; -} -``` - -Run `npm run dev` to test the dapp. -Make sure you're signed in to MetaMask and that it's not currently connected to your dapp. -If you have multiple EIP-6963 wallets installed in your browser, something like the following should display: - -

    - -![View of Dapp - Wallets Detected](../assets/react-tutorial-01-start.png) - -

    - -### 6. Show connected wallet address - -Indicate when a wallet has been connected to by displaying the user's address on the page. - -Update everything above the `return` statement in `src/App.tsx` to the following, which -adds code to format and display user addresses, and handle errors: - -```tsx title="App.tsx" -import { useState } from "react" -import { useSyncProviders } from "./hooks/useSyncProviders" -import "./App.css" - -const App = () => { - const [selectedWallet, setSelectedWallet] = useState() - const [userAccount, setUserAccount] = useState("") - const providers = useSyncProviders() - - const [errorMessage, setErrorMessage] = useState("") - const clearError = () => setErrorMessage("") - const setError = (error: string) => setErrorMessage(error) - const isError = !!errorMessage - - // Display a readable user address. - const formatAddress = (addr: string) => { - const upperAfterLastTwo = addr.slice(0, 2) + addr.slice(2) - return `${upperAfterLastTwo.substring(0, 5)}...${upperAfterLastTwo.substring(39)}` - } - - const handleConnect = async (providerWithInfo: EIP6963ProviderDetail) => { - try { - const accounts = await providerWithInfo.provider.request({ - method: "eth_requestAccounts" - }) as string[] - - setSelectedWallet(providerWithInfo) - setUserAccount(accounts?.[0]) - } catch (error) { - console.error(error) - const mmError: MMError = error as MMError - setError(`Code: ${mmError.code} \nError Message: ${mmError.message}`) - } - } - ... -``` - -Below the `return` statement in `src/App.tsx`, update the `div` with the class of `.App` to the -following: - -```tsx title="App.tsx" - ... - return ( -
    -

    Wallets Detected:

    -
    - { - providers.length > 0 ? providers?.map((provider: EIP6963ProviderDetail) => ( - - )) : -
    - No Announced Wallet Providers -
    - } -
    -
    -

    {userAccount ? "" : "No"} Wallet Selected

    - {userAccount && -
    - {selectedWallet?.info.name} -
    {selectedWallet?.info.name}
    -
    ({formatAddress(userAccount)})
    -
    - } -
    - {isError && -
    - Error: {errorMessage} -
    - } -
    -
    - ) -``` - -Add the following CSS to `src/App.css` to style the error message: - -```css title="App.css" -.mmError { - height: 36px; - padding: 16px; - color: #efefef; - background-color: transparent; -} -``` - -Your dapp should look similar to the following: - -

    - -![Final View of Dapp](../assets/react-tutorial-01-final.png) - -

    - -## Troubleshoot - -#### Doesn't look right? - -This tutorial creates `className`s for each section's parent `div` in the JSX (HTML). -If your dapp does not look the same but functions properly, check the naming of your classes and -their corresponding CSS. - -#### Doesn't function properly? - -Try the following: - -- Check the code examples against your own. -- Place `console` statements in key areas such as `handleConnect` and `store`. -- Clone the - [GitHub repository containing the project source code](https://github.com/MetaMask/vite-react-local-tutorial) - and run it. - -If you find inconsistencies or erroneous code, feel free to create an issue on the repository. - -## Next steps - -This tutorial walked you through creating a single component dapp using Vite, detecting wallet -providers using EIP-6963, and managing the state in React locally. -You can view the [project source code on GitHub](https://github.com/MetaMask/vite-react-local-tutorial). - -As a next step, you can [create a React dapp with global state](react-dapp-global-state.md). -This follow-up tutorial walks you through adding multiple components that use a global state. -You'll use [React's Context API](https://react.dev/reference/react/useContext) to manage the state -globally and move away from using the `useSyncExternalStore`. -This is a more realistic (but also more complex) approach for building a real-world dapp.