diff --git a/api-sdk/assets/sdk-desktop-browser.gif b/api-sdk/assets/sdk-desktop-browser.gif new file mode 100644 index 00000000000..39c2b58f9e1 Binary files /dev/null and b/api-sdk/assets/sdk-desktop-browser.gif differ diff --git a/api-sdk/assets/sdk-mobile-browser.gif b/api-sdk/assets/sdk-mobile-browser.gif new file mode 100644 index 00000000000..d801aa2a800 Binary files /dev/null and b/api-sdk/assets/sdk-mobile-browser.gif differ diff --git a/api-sdk/assets/sdk-nodejs.gif b/api-sdk/assets/sdk-nodejs.gif new file mode 100644 index 00000000000..5059d52fa7c Binary files /dev/null and b/api-sdk/assets/sdk-nodejs.gif differ diff --git a/api-sdk/assets/sdk-react-native.gif b/api-sdk/assets/sdk-react-native.gif new file mode 100644 index 00000000000..84495e42f2c Binary files /dev/null and b/api-sdk/assets/sdk-react-native.gif differ diff --git a/api-sdk/concepts/convenience-libraries.md b/api-sdk/concepts/convenience-libraries.md index 1805c0e835a..c92bd0c9139 100644 --- a/api-sdk/concepts/convenience-libraries.md +++ b/api-sdk/concepts/convenience-libraries.md @@ -15,4 +15,7 @@ some developers use a convenience library for interacting with the provider, suc You can refer to those tools' documentation to interact with the provider. The provider API is all you need to create a full-featured web3 application, but if you need -higher-level abstractions than those provided by the API, we recommend using a convenience library. \ No newline at end of file +higher-level abstractions than those provided by the API, we recommend using a convenience library. + +## MetaMask SDK and WalletConnect + diff --git a/api-sdk/how-to/migrate-api.md b/api-sdk/how-to/migrate-api.md index a401b8d4976..385298b36fc 100644 --- a/api-sdk/how-to/migrate-api.md +++ b/api-sdk/how-to/migrate-api.md @@ -52,7 +52,7 @@ MetaMask made the following breaking changes to the `window.ethereum` API: :::caution Pages no longer reload on chain changes Since we removed `window.web3`, MetaMask no longer automatically reloads the page on chain/network changes. -Please see [Handling the removal of `ethereum.autoRefreshOnNetworkChange`](#handling-the-removal-of-ethereumautorefreshonnetworkchange) +Please see [Handling the removal of `ethereum.autoRefreshOnNetworkChange`](#handle-the-removal-of-ethereumautorefreshonnetworkchange) for details. ::: diff --git a/api-sdk/how-to/use-sdk/index.md b/api-sdk/how-to/use-sdk/index.md index 323757c001c..6ff65eb0afb 100644 --- a/api-sdk/how-to/use-sdk/index.md +++ b/api-sdk/how-to/use-sdk/index.md @@ -1,31 +1,87 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Use MetaMask SDK -[MetaMask SDK](../../concepts/sdk-connections.md) currently supports JavaScript-based dapps. -It enables these dapps to easily connect with a MetaMask wallet client. +MetaMask SDK currently supports all JavaScript-based apps and Unity gaming apps. +It enables these apps to easily connect with a MetaMask wallet client. -The following instructions work for dapps based on standard JavaScript, React, NodeJS, Electron, and +The following instructions work for apps based on standard JavaScript, React, Node.js, Electron, and other web frameworks. -MetaMask SDK also supports [React Native](react-native.md), [pure JavaScript](pure-js.md), and -[Unity gaming](unity.md) dapps. +You can also see instructions for [React Native](react-native.md), [pure JavaScript](pure-js.md), +and [Unity gaming](unity.md) apps. -:::tip -The MetaMask SDK instance returns the [`ethereum` web3 provider](reference/provider-api.md) that -developers are already used to, so existing dapps work out of the box with the SDK! +:::tip Coming soon +SDK support for Android native apps, iOS native apps, and Unreal Engine is coming soon. ::: :::note -SDK support for Android native apps, iOS native apps, and Unreal Engine is coming soon. +MetaMask SDK uses the [Ethereum provider](../../reference/provider-api.md) that developers are +already used to, so existing dapps work out of the box with the SDK. ::: +## How it works + + + + +If a user accesses your app on a desktop browser and doesn't have the MetaMask extension installed, +a popup appears that prompts the user to either install the MetaMask extension or connect to +MetaMask Mobile using a QR code. + +![SDK desktop browser example](../../assets/sdk-desktop-browser.gif) + +You can try the +[hosted test dapp with the SDK installed](https://c0f4f41c-2f55-4863-921b-sdk-docs.github.io/test-dapp-2/). +You can also see this +[React project example](https://github.com/MetaMask/examples/tree/main/metamask-with/metamask-sdk-create-react-app). + + + + +If a user accesses your app on a mobile browser, the SDK automatically deeplinks to MetaMask Mobile +(or if the user doesn't already have it, prompts them to install it). +Once the user accepts the connection, they're automatically redirected back to your web app. +This happens for all actions that need user approval. + +

+ +![SDK mobile browser example](../../assets/sdk-mobile-browser.gif) + +

+ +You can try the +[hosted test dapp with the SDK installed](https://c0f4f41c-2f55-4863-921b-sdk-docs.github.io/test-dapp-2/). +You can also see this +[React project example](https://github.com/MetaMask/examples/tree/main/metamask-with/metamask-sdk-create-react-app). + +
+ + +When a user accesses your Node.js app, the SDK renders a QR code on the console which users can scan +with their MetaMask Mobile app. + +

+ +![SDK Node.js example](../../assets/sdk-nodejs.gif) + +

+ +
+
+ ## Prerequisites -- MetaMask Mobile v5.8.1 or above +- An existing or [new project](../../get-started/set-up-dev-environment.md) set up +- [MetaMask Mobile](https://github.com/MetaMask/metamask-mobile) v5.8.1 or above - [Yarn](https://yarnpkg.com/getting-started/install) or [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) -## 1. Install the SDK +## Steps + +### 1. Install the SDK -Install the SDK using Yarn or npm: +In your project directory, install the SDK using Yarn or npm: ```bash yarn add @metamask/sdk @@ -33,13 +89,17 @@ or npm i @metamask/sdk ``` -## 2. Import the SDK +### 2. Import the SDK + +In your project script, add the following to import the SDK: ```javascript import MetaMaskSDK from '@metamask/sdk'; ``` -## 3. Instantiate the SDK +### 3. Instantiate the SDK + +Instantiate the SDK using any [options](../../reference/sdk-js-options.md): ```javascript const MMSDK = new MetaMaskSDK(options); @@ -47,14 +107,13 @@ const MMSDK = new MetaMaskSDK(options); const ethereum = MMSDK.getProvider(); // You can also access via window.ethereum ``` -See the [list of options](../../reference/sdk-js-options.md). +### 4. Use the SDK -## 4. Use the SDK +Use the SDK by calling any [provider API methods](../../reference/provider-api.md). +Always call [`eth_requestAccounts`](../../reference/rpc-api.md#eth_requestaccounts) using +[`ethereum.request()`](../../reference/provider-api.md#ethereumrequestargs) first, since it prompts +the installation or connection popup to appear. ```javascript ethereum.request({ method: 'eth_requestAccounts', params: [] }); ``` - -Always call `eth_requestAccounts` first, since it prompts the installation or connection popup to appear. - -See the [Ethereum provider API](../../reference/provider-api.md) for all methods. diff --git a/api-sdk/how-to/use-sdk/pure-js.md b/api-sdk/how-to/use-sdk/pure-js.md index c667da45c07..7875eea132c 100644 --- a/api-sdk/how-to/use-sdk/pure-js.md +++ b/api-sdk/how-to/use-sdk/pure-js.md @@ -4,8 +4,12 @@ title: Pure JavaScript # Use MetaMask SDK with pure JavaScript -If your project just uses pure JavaScript, you can import the SDK by putting a script on the head -section of your website: +You can import MetaMask SDK into your pure JavaScript app to enable your users to easily connect +with a MetaMask wallet client. +The SDK for pure JavaScript [works the same way](index.md#how-it-works) and has the +[same prerequisites](index.md#prerequisites) as for standard JavaScript and other web frameworks. + +To import, instantiate, and use the SDK, you can insert a script in the head section of your website: ```javascript @@ -26,3 +30,9 @@ section of your website: ... ``` + +You can configure the SDK using any [options](../../reference/sdk-js-options.md) and call any +[provider API methods](../../reference/provider-api.md). +Always call [`eth_requestAccounts`](../../reference/rpc-api.md#eth_requestaccounts) using +[`ethereum.request()`](../../reference/provider-api.md#ethereumrequestargs) first, since it prompts +the installation or connection popup to appear. diff --git a/api-sdk/how-to/use-sdk/react-native.md b/api-sdk/how-to/use-sdk/react-native.md index b1c35ebca8b..8c26278347b 100644 --- a/api-sdk/how-to/use-sdk/react-native.md +++ b/api-sdk/how-to/use-sdk/react-native.md @@ -4,14 +4,42 @@ title: React Native # Use MetaMask SDK with React Native -You can import MetaMask SDK into your React Native dapp to enable your users to easily connect +You can import MetaMask SDK into your React Native app to enable your users to easily connect with their MetaMask Mobile wallet. +## How it works + +When a user accesses your mobile React Native app, the SDK automatically deeplinks to MetaMask +Mobile (or if the user doesn't already have it, prompts them to install it). +Once the user accepts the connection, they're automatically redirected back to your app. +This happens for all actions that need user approval. + +

+ +![SDK React Native example](../../assets/sdk-react-native.gif) + +

+ +You can download the +[React Native example](https://c0f4f41c-2f55-4863-921b-sdk-docs.github.io/downloads/reactNativeApp_v0.1.0.zip). +Install the example using `yarn setup` and run it using `yarn ios` or `yarn android`. + +## Prerequisites + +- A React Native project set up +- [MetaMask Mobile](https://github.com/MetaMask/metamask-mobile) v5.8.1 or above +- [Yarn](https://yarnpkg.com/getting-started/install) or + [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) + ## Install the SDK -Use [rn-nodeify](https://github.com/tradle/rn-nodeify) to install the SDK. +:::tip Coming soon +A `metamask-react-native-sdk` package that simplifies the installation of the SDK for React Native +apps is coming soon. +::: -### 1. Install rn-nodeify +Use [`rn-nodeify`](https://github.com/tradle/rn-nodeify) to install the SDK. +In your project directory, install `rn-nodeify`: ```bash yarn add --dev rn-nodeify @@ -19,7 +47,7 @@ or npm i --dev rn-nodeify ``` -### 2. Install rn-nodeify libraries +Install the `rn-nodeify` libraries: ```bash yarn add react-native-crypto @@ -30,22 +58,23 @@ yarn add stream yarn add events ``` -### 3. Insert rn-nodeify post install script into `package.json` -> `"scripts"` +In your project's `package.json` file, insert the `rn-nodeify` command into the postinstall script: -```bash -"postinstall": "rn-nodeify --install 'crypto,process,stream,events' --hack" +```json title="package.json" +"scripts": { + ..., + "postinstall": "rn-nodeify --install 'crypto,process,stream,events' --hack" +} ``` -### 4. Import rn-nodeify shim.js - -rn-nodeify creates a shim.js file in your project root directory. -Import it in the root file of your application. +`rn-nodeify` creates a `shim.js` file in your project root directory. +Import it in the root file of your application: ```bash import './shim' ``` -### 5. Install react-native-background-timer +Install `react-native-background-timer`: ```bash yarn add react-native-background-timer @@ -53,23 +82,19 @@ yarn add react-native-background-timer cd ios && pod install && cd .. ``` -### 6. Install MetaMask SDK +Install MetaMask SDK: ```bash yarn add @metamask/sdk ``` -### 7. Run postinstall - -Run the postinstall script after everything is installed. +Run the postinstall script after everything is installed: ```bash yarn postinstall ``` -### 8. Install pods - -Finally, install the necessary pods that come with the libraries. +Finally, install the necessary pods that come with the libraries: ```bash cd ios && pod install && cd .. @@ -77,6 +102,8 @@ cd ios && pod install && cd .. ## Use the SDK +Import, instantiate, and use the SDK by adding something similar to the following to your project script: + ```javascript import MetaMaskSDK from '@metamask/sdk'; import { Linking } from 'react-native'; @@ -98,7 +125,13 @@ const ethereum = MMSDK.getProvider(); const accounts = await ethereum.request({ method: 'eth_requestAccounts' }); ``` -You can now use [EthersJS](https://docs.ethers.io/v5/getting-started/) with your React Native dapp: +You can configure the SDK using any [options](../../reference/sdk-js-options.md) and call any +[provider API methods](../../reference/provider-api.md). +Always call [`eth_requestAccounts`](../../reference/rpc-api.md#eth_requestaccounts) using +[`ethereum.request()`](../../reference/provider-api.md#ethereumrequestargs) first, since it prompts +the installation or connection popup to appear. + +You can use [EthersJS](https://docs.ethers.io/v5/getting-started/) with your React Native app: ```javascript const provider = new ethers.providers.Web3Provider(ethereum); @@ -112,14 +145,6 @@ const balanceInETH = ethers.utils.formatEther(balance); // '0.182826475815887608' ``` -:::note -The MetaMask team is creating a `metamask-react-native-sdk` package that will install all of this -automatically, making the installation much easier for React Native dapps. -::: - ## Examples View the [React Native dapp](https://recordit.co/FClppLgWzT) recording. - -View the [React Native example](https://c0f4f41c-2f55-4863-921b-sdk-docs.github.io/downloads/reactNativeApp_v0.1.0.zip). -Install the example using `yarn setup` and run it using `yarn ios` or `yarn android`. diff --git a/api-sdk/how-to/use-sdk/unity.md b/api-sdk/how-to/use-sdk/unity.md index 077b8b51528..21b37f66533 100644 --- a/api-sdk/how-to/use-sdk/unity.md +++ b/api-sdk/how-to/use-sdk/unity.md @@ -6,25 +6,31 @@ title: Unity gaming You can import MetaMask SDK into your Unity game to enable users to easily connect to their MetaMask Mobile wallet. -The supported platforms are macOS, Windows, Linux, IOS, Android, and WebGL. +The MetaMask Unity SDK supports macOS, Windows, Linux, IOS, Android, and WebGL. -The SDK renders a QR code in the UI via a dedicated prefab which players can scan with their +## How it works + +The SDK renders a QR code in the UI using a dedicated prefab which players can scan with their MetaMask Mobile app. It also supports deeplinking on mobile platforms. You can use all the [provider API methods](../../reference/provider-api.md) right from your game. ## Video tutorial -The following video tutorial explains how to install and use MetaMask SDK with Unity. +The following video tutorial explains how to install and use the MetaMask Unity SDK. + +

+ +

- +## Steps -## 1. Install the SDK for Unity +### 1. Install the SDK for Unity To install the module, first download the [Unity SDK Package](https://drive.google.com/u/0/uc?id=1ArTJvKIZXK5vkUOM3cgr0t0NspenWRU9&export=download). -Then, go to the Window menu > **Package Manager**. +Then, go to the menu > **Package Manager**. Select **My Assets**, **MetaMask Unity SDK**, and **Install**. You should see the MetaMask SDK package listed in the project packages and be able to interface with it and its examples in the scene. @@ -32,7 +38,7 @@ with it and its examples in the scene. You also need to install [TextMeshPro](https://docs.unity3d.com/Manual/com.unity.textmeshpro.html). If you don't have TextMeshPro installed, the Unity editor automatically prompts you to install it. -## 2. Initialize MetaMask +### 2. Initialize MetaMask The main class you interface with is called `MetaMaskWallet`. It handles the connection to the user's wallet, as well as processing the requests to it using a @@ -54,7 +60,7 @@ You first must initialize by doing one of the following: This initializes the wallet instance, making it accessible from `MetaMaskUnity.Instance.Wallet`. You can now make calls to the user's wallet using [provider API methods](../../reference/provider-api.md). -## 3. Connect to MetaMask +### 3. Connect to MetaMask Once the wallet is prepared and initialized, you can connect to MetaMask. Call the `Connect()` method on the wallet instance as follows: @@ -93,7 +99,7 @@ scene with its fields provided. The transport field is also required if you want to use it isolated from the canvas that is spawned by the transport, then it generates the QR code for you. -## 4. Use MetaMask +### 4. Use MetaMask Once the wallet is authorized, you can make requests to it. The wallet is authorized when the buttons become interactable or the `WalletAuthorized` event is fired: @@ -127,12 +133,10 @@ var request = new MetaMaskEthereumRequest await wallet.Request(request); ``` -## 5. Configure MetaMask +### 5. Configure MetaMask You can customize the default configuration or create your own configuration. -### Edit the default configuration - Edit the default configuration by doing one of the following: - Navigate to the **Window > MetaMask > Setup** menu item. @@ -140,8 +144,6 @@ Edit the default configuration by doing one of the following: Edit the fields and save the changes. -### Create a new configuration - Create a new configuration by right-clicking on the project window and navigating to **MetaMask > Config**. Name the new configuration and use it when initializing the `MetaMaskUnity` instance. diff --git a/api-sdk/reference/sdk-js-options.md b/api-sdk/reference/sdk-js-options.md index b2eeed11dcd..508e88d485b 100644 --- a/api-sdk/reference/sdk-js-options.md +++ b/api-sdk/reference/sdk-js-options.md @@ -1,7 +1,7 @@ -# MetaMask SDK JavaScript options +# MetaMask JavaScript SDK options -The JavaScript version of the MetaMask SDK takes several options. -For example: +The JavaScript version of [MetaMask SDK](../how-to/use-sdk/index.md) takes several options. +For example, you can specify options as follows: ```javascript const options = { @@ -14,25 +14,25 @@ const MMSDK = new MetaMaskSDK(options); The following table shows the full list of options: -| Option name | Type | Default value | Description | -| ------------------------------ | :--------------------------------------------: | :-----------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `dappMetadata` | `{name: "My Dapp", url: "https://mydapp.com"}` | `undefined` | Only required for non-web dapps (for example, React Native and Unity). | -| `injectProvider` | `boolean` | `true` | Doesn't inject in NodeJS or React Native since the window object is unavailable. | -| `forceInjectProvider` | `boolean` | `false` | Forces injection even if another provider is already present on the window object. | -| `forceDeleteProvider` | `boolean` | `false` | Forces deletion of a provider that exists on a window. | -| `checkInstallationImmediately` | `boolean` | `false` | The SDK checks if MetaMask is installed when a call to `eth_requestAccounts` is made. When `true`, it checks before any call is made. | -| `checkInstallationOnAllCalls` | `boolean` | `false` | Normally checked when a call to `eth_requestAccounts` is made. When `true`, it checks on all calls. | -| `shouldShimWeb3` | `boolean` | `true` | Set as `true` if `window.web3` should be shimmed for legacy compatibility purposes. See [more information](../how-to/migrate-api.md#replace-windowweb3) | -| `preferDesktop` | `boolean` | `false` | For a web dapp running on a desktop browser without a MetaMask extension, the SDK gives the option to connect with a MetaMask Mobile wallet via a QR code. When `true`, the SDK guides the user to install the MetaMask extension instead. | -| `openDeeplink` | `(deeplinkUrl: string) => void` | `undefined` | Platforms open deeplinks differently. For example, web: `window.open` versus React Native: `Linking.open`. This function retrieves the deeplink URL and allows developers to customize how it opens. | -| `getUniversalLink` | `() => string` | `undefined` | Get the universal link that is presented on the QR Code (web) and deeplinks (mobile). This makes it easier to enable users to connect with backend code. | -| `communicationLayerPreference` | `"socket" or "webrtc"` | `socket` | Defines the communication library that the dapp and MetaMask wallet use to communicate with each other. Waku or another similar decentralized communication layer solution coming soon. | -| `webRTCLib` | `WebRTC Lib` | `undefined` | Not installed on the SDK by default. Check the React Native section to see how to install it. | -| `WalletConnectInstance` | `WalletConnect Lib` | `undefined` | Another way of connecting a dapp to MetaMask. Not installed by default. Check [here](https://docs.walletconnect.com/) on how to install it. | -| `forceRestartWalletConnect` | `boolean` | `false` | Set `forceRestartWalletConnect` to `true` to kill the previous WalletConnect session and start another one. | -| `transports` | `['websocket', 'polling']` | `undefined` | Used to set the preference on socket.io transports to 'use'. Check the socket.io [documentation](https://socket.io/docs/v4/) for more details. | -| `timer` | `BackgroundTimer` | `undefined` | Used by React Native apps to keep the app alive while in the background using `react-native-background-timer` | -| `enableDebug` | `boolean` | `true` | Enables/disables the sending of debugging information to the `socket.io` server. The default is `true` for the beta version of the SDK. The default is `false` in production versions. | +| Option name | Type | Default value | Description | +| ------------------------------ | :--------------------------------------------: | :-----------: | -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `dappMetadata` | `{name: "My Dapp", url: "https://mydapp.com"}` | `undefined` | Only required for non-web apps (for example, React Native and Unity). | +| `injectProvider` | `boolean` | `true` | Doesn't inject in Node.js or React Native since the window object is unavailable. | +| `forceInjectProvider` | `boolean` | `false` | Forces injection even if another provider is already present on the window object. | +| `forceDeleteProvider` | `boolean` | `false` | Forces deletion of a provider that exists on a window. | +| `checkInstallationImmediately` | `boolean` | `false` | The SDK checks if MetaMask is installed when a call to `eth_requestAccounts` is made. When `true`, it checks before any call is made. | +| `checkInstallationOnAllCalls` | `boolean` | `false` | Normally checked when a call to `eth_requestAccounts` is made. When `true`, it checks on all calls. | +| `shouldShimWeb3` | `boolean` | `true` | Set as `true` if `window.web3` should be shimmed for [legacy compatibility purposes](../how-to/migrate-api.md#replace-windowweb3). | +| `preferDesktop` | `boolean` | `false` | For a web app running on a desktop browser without a MetaMask extension, the SDK gives the option to connect with a MetaMask Mobile wallet via a QR code. When `true`, the SDK guides the user to install the MetaMask extension instead. | +| `openDeeplink` | `(deeplinkUrl: string) => void` | `undefined` | Platforms open deeplinks differently. For example, web: `window.open` versus React Native: `Linking.open`. This function retrieves the deeplink URL and allows developers to customize how it opens. | +| `getUniversalLink` | `() => string` | `undefined` | Get the universal link that is presented on the QR Code (web) and deeplinks (mobile). This makes it easier to enable users to connect with backend code. | +| `communicationLayerPreference` | `"socket" or "webrtc"` | `socket` | Defines the communication library that the dapp and MetaMask wallet use to communicate with each other. Waku or another similar decentralized communication layer solution coming soon. | +| `webRTCLib` | `WebRTC Lib` | `undefined` | Not installed on the SDK by default. | +| `WalletConnectInstance` | `WalletConnect Lib` | `undefined` | Connect an app to MetaMask using [WalletConnect](https://docs.walletconnect.com/). Not installed by default. | +| `forceRestartWalletConnect` | `boolean` | `false` | Set `forceRestartWalletConnect` to `true` to kill the previous WalletConnect session and start another one. | +| `transports` | `['websocket', 'polling']` | `undefined` | Used to set the preference on [socket.io](https://socket.io/docs/v4/) transports to `use`. | +| `timer` | `BackgroundTimer` | `undefined` | Used by React Native apps to keep the app alive while using `react-native-background-timer` in the background | +| `enableDebug` | `boolean` | `true` | Enables/disables the sending of debugging information to the socket.io server. The default is `true` for the beta version of the SDK. The default is `false` in production versions. | :::tip If your project is a web app and `injectProvider` is `true`, then the `ethereum` object should be diff --git a/docusaurus.config.js b/docusaurus.config.js index e2b3be6c06d..3f796080c77 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -66,8 +66,14 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ + docs: { + sidebar: { + hideable: true, + }, + }, navbar: { - title: "MetaMask Docs", + title: "MetaMask docs", + hideOnScroll: true, logo: { alt: "My Site Logo", src: "img/metamask-fox.svg", @@ -89,7 +95,7 @@ const config = { footer: {}, prism: { theme: codeTheme, - additionalLanguages: [`csharp`], + additionalLanguages: ["csharp"], }, }), };