# Openfort Documentation > Comprehensive tutorials on smart accounts, API integration, and wallet management. import { HomePage } from "vocs/components";
Make wallets our problem. Not yours Openfort is an open source alternative to wallet infrastructure solutions.
Supercharge your project with authentication, user management and payments.
Get started GitHub ## Entity Addresses A list of contract addresses deployed by Openfort. ### Paymaster | Chains | Address | | ------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Mainnet Address** | [0x51032c841E823BB487C33c9f1773B18C8d0E5209](https://contractscan.xyz/contract/0x51032c841E823BB487C33c9f1773B18C8d0E5209) | | **Testnet Address** | [0x3210deD2e50363eF645C4eA15339Bec8Ac88e0F5](https://contractscan.xyz/contract/0x3210deD2e50363eF645C4eA15339Bec8Ac88e0F5) | ### Factories #### UpgradeableAccountFactory V6 | Chains | Address | | ------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Mainnet Address** | [0x2fcb244d49f712c38109ba0bfc21433a4dd4452d](https://contractscan.xyz/contract/0x2fcb244d49f712c38109ba0bfc21433a4dd4452d) | #### UpgradeableAccountFactory V5 | Chains | Address | | ------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Mainnet Address** | [0x5a2ed3e47798123ae30477424731de2ae47cc158](https://contractscan.xyz/contract/0x5a2ed3e47798123ae30477424731de2ae47cc158) | | **Testnet Address** | [0xcb71e008b9062bb7abd558816f8135ef2cab576f](https://contractscan.xyz/contract/0xcb71e008b9062bb7abd558816f8135ef2cab576f) | ### Implementation #### UpgradeableAccountImplementation V5 | Chains | Address | | ------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Mainnet Address** | [0x6e4a235c5f72a1054abFeb24c7eE6b48AcDe90ab](https://contractscan.xyz/contract/0x6e4a235c5f72a1054abFeb24c7eE6b48AcDe90ab) | | **Testnet Address** | [0xc8ebbdd2da25906b96c374c9b1757b1421e9f45a](https://contractscan.xyz/contract/0xc8ebbdd2da25906b96c374c9b1757b1421e9f45a) | #### ERC6651AccountImplementation V1 | Chains | Address | | ------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Mainnet Address** | [0x528fe3cf9a857127eceb6df26ac7535839bfaef8](https://contractscan.xyz/contract/0x528fe3cf9a857127eceb6df26ac7535839bfaef8) | | **Testnet Address** | [0x528fe3cf9a857127eceb6df26ac7535839bfaef8](https://contractscan.xyz/contract/0x528fe3cf9a857127eceb6df26ac7535839bfaef8) | ### Forward contract | Chains | Address | | ------------------- | ------------------------------------------ | | **Mainnet Address** | 0xc82BbE41f2cF04e3a8efA18F7032BDD7f6d98a81 | | **Testnet Address** | 0xc82BbE41f2cF04e3a8efA18F7032BDD7f6d98a81 | | **Beam** | 0xd04f98c88ce1054c90022ee34d566b9237a1203c | ## API Keys ### Livemode and testing Every account is divided into two universes: one for *testnet*, and one for *mainnet*. All requests exist in one of those two universes, and objects in one universe cannot be manipulated by objects in the other. In **test mode**, transactions can only go to **testnet networks**. ### API keys You'll need to authenticate your requests to access any of the endpoints in the Openfort API. API keys are used to authenticate these requests. #### Project secret and publishable keys All accounts have a total of four API keys by default—two for test mode and two for live mode: 1. **Test project secret key:** Use this key to authenticate requests on your server when in test mode. By default, you can use this key to perform any API request without restriction. 2. **Test project publishable key:** Use this key for testing purposes in your web or mobile app's client-side code. 3. **Live project secret key:** Use this key to authenticate requests on your server when in live mode. By default, you can use this key to perform any API request without restriction. 4. **Live project publishable key:** Use this key, when you're ready to launch your app, in your web or mobile app's client-side code. #### Shield secret and publishable keys All accounts have a total of three API keys by default for Shield functionality: 1. **Shield secret key:** Use this key to store the recovery share of your users on server. 2. **Shield publishable key:** Use this key, when you're ready to launch your app, in your web or mobile app's client-side code. 3. **Shield encryption share key:** Only used whenever you're using the [automatic recovery](/docs/products/embedded-wallet/javascript/signer/recovery) to encrypt the recovery share. | Type | Value | When to use | | ----------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Secret | `sk_test_ypc...YZp` | **On the server-side**: Must be secret and stored securely in your web or mobile app's server-side code (such as in an environment variable or credential management system) to call Openfort APIs. Don't expose this key on a website or embed it in a mobile application. | | Publishable | `pk_test_ZXb...aWg` | **On the client-side**: Can be publicly-accessible in your web or mobile app's client-side code (such as `openfort-js`). | ### Reveal an API secret key in your dashboard Openfort APIs use your secret key to authenticate requests from your server. To find your API secret key for test mode: 1. Open the [API keys](https://dashboard.openfort.io/developers/configuration/api-keys) page. 2. Under `API keys`, in the `Secret key` row, click `Reveal test key` and save the value. ### Regenerate API keys :::warning Regenerating an API key will invalidate the current key and generate a new one. This action cannot be undone. If you have set up webhooks or registered a Shield project with the key, you will need to update them with the new key. ::: Openfort supports the ability to regenerate, delete and create API keys. You can do this at any time in the [API keys](https://dashboard.openfort.io/developers/configuration/api-keys) section of the dashboard. * **Delete and regenerate API keys**: ![generate-delete-api-keys](https://blog-cms.openfort.io/uploads/regenerate_delete_api_keys_f6b94994b0.png) * **Generate new API keys**: You can create multiple API keys when you're planning to generate a new one to avoid disruption to your integration. ![generate-api-keys](https://blog-cms.openfort.io/uploads/generate_api_keys_5807b195fa.png) ### Limit API keys interaction by IP Openfort supports limiting the IPs that can interact with Openfort services using specific API keys. To enable this, navigate to the [API keys](https://dashboard.openfort.io/developers/configuration/api-keys) section of the dashboard and press the three dots next to the **secret key**. A new page will appear with an option `Whitelist IPs` like so: ![whitelist-ips](https://blog-cms.openfort.io/uploads/regenerate_delete_api_keys_f6b94994b0.png) You can then add multiple IPs per single secret key. If you try to make a request from an unauthorized IP, you will receive a `Forbidden` error like so: ```json { "error": { "type": "invalid_request_error", "message": "Access is limited for this address" } } ``` ## Supported Blockchains You can find a list of the chains that Openfort currently supports below. Openfort is compatible with all EVM chains.
Don't see a chain you're interested in below? Get in touch, we'll make it available within 12 hours.
| Blockchains | Testnet | Mainnet | | ------------------- | ----------- | --------- | | **Ancient8** | 28122024 | 888888888 | | **Arbitrum Nova** | | 42170 | | **Arbitrum One** | 421614 | 42161 | | **Avalanche** | 43113 | 43114 | | **B3** | 1993 | 8333 | | **Base** | 84532 | 8453 | | **Beam** | 13337 | 4337 | | **Berachain** | | 80094 | | **BNB** | 97 | 56 | | **Degen Chain** | | 666666666 | | **DOS Chain** | 3939 | 7979 | | **Gnosis** | 10200 | 100 | | **Immutable zkEVM** | 13473 | | | **Kroma** | 2358 | 255 | | **Linea** | 59140 | 59144 | | **Mantle** | 5001 | 5000 | | **Monad** | 10143 | | | **Soneium** | 1946 | | | **OpBNB** | 5611 | 204 | | **Optimism** | 420 | 10 | | **Saakuru** | 247253 | 7225878 | | **Sophon** | 531050104 | 50104 | | **Polygon POS** | 80002 | 137 | | **Xai Network** | 37714555429 | 660279 | | **Zora** | 999999999 | 7777777 | | **ZKsync** | 300 | 324 | ## Configure allowed domains To secure use of your client-side Openfort publishable key, we strongly recommend setting allowed domains for any application in production. This is a security best practice that prevents arbitrary applications from reusing your openfortpublishable key in their own site. You should always restrict allowed domains for any production application. This step is not necessary for the publishable key you use in staging, development, or local environments. To configure allowed domains for your app, go to the Openfort dashboard and select your production app from the dropdown in the left sidebar. Then, navigate to the Configuration > Security. Under Allowed Origins, list the domains that will use your production publishable key. ### Allowed domains format When listing allowed domains, please follow these guidelines: * The protocol **`https` is required**. * **Trailing paths** (e.g. `/path`) are **not supported**. * **Wildcards (`*`) are only supported as a subdomain**, e.g. `*.domain.com`.\ Wildcards like `*.com` or partial wildcards such as `*-something.domain.com` are **not supported**. * **Localhost** (`http://localhost:port`) is supported, **but the port number must be specified**.\ While supported, **we do not recommend listing `localhost`** as an allowed domain for production apps. If you must add it temporarily (e.g. for development), **remember to remove it afterward**. * Many hosting providers treat `https://www.example.com` and `https://example.com` as equivalent.\ If that's true for your app, **add both versions (with and without `www`)** as allowed origins in the dashboard. Setting allowed domains restricts client-side access to your Openfort publishable key only. Openfort's REST API endpoints that you would query from your backend are gated by your app secret, which should never be exposed on a user's client. ### Supporting preview URLs Many hosting providers (e.g. Vercel) support preview deployment URLs to make it easy to test changes, like: #### Matches the pattern \*.netlify.app Which anyone with a free Netlify account can deploy to `deploy-preview-id--yoursitename.netlify.app`. For security reasons, we do not allow whitelisting domains with a generic pattern that are commonly used for these preview deployments, such as: * https\://*.netlify.app / https\://*.vercel.app * https\://*-projectname.netlify.app / https\://*-projectname.vercel.app * Any project can deploy to a domain that matches https\://*.netlify.app, https\://*.vercel.app, or similar. If you were to whitelist this domain for your production publishable key, any actor could set up any arbitrary deployment with your hosting provider and can use your production publishable key within their site. If you'd like to secure your Openfort publishable key on preview deployment URLs, please check if your hosting provider allows you to map preview deployments to a stable subdomain that only you control, like: #### Matches the pattern \_.yoursitename.netlify.app Which only members of your Netlify account (or hosting provider) can deploy to deploy-preview-42.yoursitename.netlify.app This allows you to list [https://\_.yoursitename.netlify.app](https://_.yoursitename.netlify.app) under allowed domains, which arbitrary actors cannot deploy to. See instructions to set this up with Vercel or Netlify. Allowed domains are primarily recommended for production applications. If your preview deployments use a development Openfort publishable key, feel free to leave Allowed Origins empty to support use of your publishable key in previews without the setup above. App clients and allowed domains Within an app client, you can override Allowed origins on your app while still sharing the same user base. To add a client, go to the Configuration > App settings page > Clients tab, and find the Add app client button. Create a client and add Allowed origins. Allowed OAuth redirect URLs Similar to allowed domains, you can configure allowed OAuth redirect URLs to restrict where users can be redirected after they log in with an external OAuth provider. This is a security best practice that prevents users from being redirected to malicious sites with their authentication token. To configure allowed OAuth redirect URLs, navigate to Configuration > App settings > Advanced on the dashboard. Add the OAuth providers are allowed to redirect to after authentication. > The URL must be an exact match for the redirect URL; query params and trailing slashes will error. > The URL must be at a domain listed in allowed domains. > The protocol (https) is required. > Wildcards (\*) are not supported. > If no URLs are listed, users can be redirected to any URL. import { VideoSnippet } from "@/components/VideoSnippet.tsx"; ## Backend wallets Backend wallets serve as an internal account for games and developers to manage assets and flows. The accounts are EOAs (Externally Owned Account).
dev-account-dashboard
There are [several use cases](/docs/products/server/wallet) for this: * **Treasury Account**: Transferring assets on demand. * **Minting Account**: Minting assets on demand. * **Escrow Account**: Hold assets or tokens in an escrow between users and transferring them afterwards. ### Requisites Developer account pay for gas using either: * **Support [ERC-2771](https://eips.ethereum.org/EIPS/eip-2771) transactions**: The assets you want to escrow need to support [ERC-2771](https://eips.ethereum.org/EIPS/eip-2771) transactions (i.e. gasless). * **Funding a backend wallet**: You need to fund your developer account with the chain's native token. ### Create a backend wallet Head to the [backend wallet](https://dashboard.openfort.io/developers/wallets) page in your dashboard settings and click on `Add account`. By default, the backend wallets created with Openfort are custodial. #### Dashboard
addDevAccount
#### API ##### Node.js ```ts server.ts // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.openfort.io/developers/configuration/api-keys const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const settings = await openfort.settings.createDeveloperAccount({ name: 'Minting Account', }) ``` ##### curl ```bash command-line curl https://api.openfort.io/v1/settings/developer_accounts \ -u "$YOUR_SECRET_KEY:" \ -d name= "Minting Account" ``` #### Verify wallet ownership If you're looking to **sponsor gas with your native tokens** for smart accounts, it's important to verify your deposited funds with the paymaster. Head to [backend wallets](https://dashboard.openfort.io/accounts) in your dashboard and click on `Add account`. 1. Click on "Advanced Options" to start the process. 2. Sign the explorer message. 3. Add the wallet address used to deposit native tokens. 4. Add the signed message.
noncustodial-dev-account
#### Optional: Custom forwarder contract By default you can use the supported forwarder contracts from Openfort if you need. Alternatively, you can also use your forward contract in order to sponsor the transaction of your backend wallet. Go to your [Gas policy](https://dashboard.openfort.io/policies) section and create a policy by adding the address of your `Forwarder contract address`. ## Ecosystem dashboard configuration :::note Creating your own global wallet is an **enterprise feature**. To enable it, please [contact us](https://t.me/joalavedra). ::: Use the **customization** page of the dashboard to configure your ecosystem's brand settings, including name, logo, accent color, and legal policies.
whitelabel_dashboard
### Customization options | Customization | Description | | ------------- | ----------------------------------------------------------------- | | Domain | Choose where to host your dashboard | | Name | Ecosystem name as you'd like to present it to users | | Logo | Ecosystem logo | | Brand color | Accent color for your ecosystem | | Legal | Used to set your own terms & conditions, and your privacy policy. | | SDK links | Client SDKs | | Example links | Template examples using the ecosystem SDKs | ## Using a third-party auth provider Openfort's signer solution enables the user onboarding by integrating with established backend solutions and authentication providers that support JWT-based authentication. This strategy provides a trusted, scalable, and versatile framework that supports an extensive range of authentication options. ### Third-party auth platforms With this approach, Openfort is in charge of creating **embedded signers** on the and follow the necessary security steps to make sure the smart wallet created remains non custodial. * **[PlayFab](/docs/configuration/external-auth/playfab)** * **[Firebase](/docs/configuration/external-auth/firebase)** * **[Accelbyte](/docs/configuration/external-auth/accelbyte)** * **[Lootlocker](/docs/configuration/external-auth/lootlocker)** * **[Supabase](/docs/configuration/external-auth/supabase)** ### Custom Auth Methods We offer two options to setup embedded signers with custom auth, one that is based on the OIDC ([Open ID Connect](https://openid.net/developers/how-connect-works/)) standard, and a generic option that lets you bring your own auth server. * **[Custom OIDC Token](/docs/configuration/custom-auth/oidc-token)** * **[Custom Auth Token](/docs/configuration/custom-auth/auth-token)** ## FAQs #### Can players use ERC-20 tokens to pay for gas fees? Yes, you can sponsor fully or partially with the network token or ERC-20. #### Do users need to fund the newly created accounts? You don't need to. With Openfort you can use policies to sponsor gas fees on behalf of your users. #### How do I pay for the sponsored gas fees? Openfort handles all the gas payments for you when using policies. While everything is free on `Testnets`, on `Mainnets` you'll need to top up your account. #### What smart contracts can I interact with? Yes, you're free to use any smart contracts you wish. You will need add contracts using the assets page in your dashboard. #### Is Openfort ERC-4337 compatible? Yes, Openfort is compatible with Account Abstraction (ERC-4337) among other standards and follows best practices and implementations across other ethereum standards. #### What blockchains do you support? Checkout the comprehensive list of [supported chains](/docs/configuration/chains). #### Can users have the same smart account address on all EVM chains? Yes, users can have the same address across all EVM chains because the addresses are deterministic. Each chain will have separate smart account. #### Has Openfort been audited? The Smart Account implementation has been [audited by CertiK](https://drive.google.com/drive/u/0/folders/1aoPgJD_oz1qagWflnO91ASlQo-2upjRL) and the embedded wallet is [audited by Cure+53](https://drive.google.com/drive/u/0/folders/1aoPgJD_oz1qagWflnO91ASlQo-2upjRL). #### Do you provide the ability to communicate with players through notifications? Openfort can notify you of transactions succeeded or reverted with [webhooks](/docs/configuration/webhooks). You can use that to send push notifications. #### What's Openfort's business model? At Openfort we work with any size business to connect your backend and product to the blockchain. Our business model adapts to your needs based on volume as well as the revenue and growth model your business has. #### How can I activate "Live Mode"? Whenever you want to go live with your product, you need to make sure to complete the details on your billing settings. This is necessary on order to top up your Account Funds. #### What options are available for branding and white labeling? Openfort offers headless smart accounts which means that you can customize your own UI and experience. You can decide to use any popular framework or completely integrate it within you game (zero popups). ## Pay Gas Fees with ERC20 Tokens Gas fees can be a hurdle for many users in blockchain applications. This guide will help you configure gasless transactions where the gas is paid using an ERC20 token. Choose between dynamic or fixed-rate payment strategies. ### Charge an ERC20 with Dynamic Price ::::steps ##### Step 1: Select the Contract You Want to Interact With * Add your collectible's smart contract. * Define the contract function you want to use (e.g., `mint`). **API** ```bash [Terminal] curl https://api.openfort.io/v1/contracts \ -u "$YOUR_SECRET_KEY:" \ -d 'name=NFT Contract' \ -d 'chainId=80002' \ -d 'address=contract_address' ``` **Dashboard** ![Add Contract](https://blog-cms.openfort.io/uploads/Add_contract_7634c46ab8.png) ##### Step 2: Set Up a Dynamic ERC20 Policy * Create a new policy and select the strategy `charge dynamic amount of ERC20`. * Select the ERC20 token contract and exchange rate. * Link the imported contract and the function you want to sponsor. :::note If you want to update the dynamic rate programmatically, contact us. ::: **API** ```bash [Terminal] curl https://api.openfort.io/v1/policies \ -u "$YOUR_SECRET_KEY:" \ -d 'name=My Policy' \ -d 'chainId=80002' \ -d 'strategy[sponsorSchema]=charge_custom_tokens' \ -d 'strategy[tokenContract]=con_...' \ -d 'strategy[tokenContractAmount]=1' ``` ```bash [Terminal] curl https://api.openfort.io/v1/policies/:id/policy_rules \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d type="contract_functions" \ -d functionName="All functions" \ -d contract="con_..." ``` **Dashboard** ![Dynamic Pricing Policy](https://blog-cms.openfort.io/uploads/gas_payment_erc20_f380972463.png) ##### Step 3: Create a Gasless Transaction * Add `chainId`. * Add the contract. * Add the policy. * Add the function you want to interact with. :::note You can make a transaction without a registered user or account deployed at the time of interaction. Once you make the transaction, a `playerId` and `accountId` will be created. ::: ```bash [Terminal] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d 'chainId=80002' \ -d 'optimistic=true' \ -d 'policy=policy_id' \ -d 'interactions[0][contract]=contract_address' \ -d 'interactions[0][functionName]=mint' \ -d 'interactions[0][functionArgs][0]=sender_address_or_id' ``` ##### Step 4: (Optional) Add the Account or Player * Add an account or player to specify the user. ```bash [Terminal] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d 'chainId=80002' \ -d 'policy=policy_id' \ -d 'account=account_id' \ -d 'optimistic=true' \ -d 'interactions[0][contract]=contract_address' \ -d 'interactions[0][functionName]=mint' \ -d 'interactions[0][functionArgs][0]=sender_address_or_id' ``` :::: ### Charge an ERC20 with Fixed Price ::::steps ##### Step 1: Select the Contract You Want to Interact With * Add your collectible's smart contract. * Define the contract function you want to use (e.g., `mint`). **API** ```bash [Terminal] curl https://api.openfort.io/v1/contracts \ -u "$YOUR_SECRET_KEY:" \ -d 'name=NFT Contract' \ -d 'chainId=80002' \ -d 'address=contract_address' ``` **Dashboard** ![Add Contract](https://blog-cms.openfort.io/uploads/Add_contract_7634c46ab8.png) ##### Step 2: Set Up a Fixed ERC20 Policy * Create a new policy and select the strategy `charge fixed amount of ERC20`. * Specify the ERC20 token contract and the fixed amount to charge. * Link the imported contract and the function you want to sponsor. **API** ```bash [Terminal] curl https://api.openfort.io/v1/policies \ -u "$YOUR_SECRET_KEY:" \ -d 'name=Fixed Policy' \ -d 'chainId=80002' \ -d 'strategy[sponsorSchema]=charge_fixed_rate' \ -d 'strategy[tokenContract]=contract_address' \ -d 'strategy[tokenContractAmount]=1' ``` **Dashboard** ![Fixed Pricing Policy](https://blog-cms.openfort.io/uploads/fixed_gas_policy_example.png) ##### Step 3: Create a Gasless Transaction * Add `chainId`. * Add the contract. * Add the policy. * Add the function you want to interact with. :::note You can make a transaction without a registered user or account deployed at the time of interaction. Once you make the transaction, a `playerId` and `accountId` will be created. ::: ```bash [Terminal] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d 'chainId=80002' \ -d 'optimistic=true' \ -d 'policy=pol_...' \ -d 'interactions[0][contract]=con_...' \ -d 'interactions[0][functionName]=mint' \ -d 'interactions[0][functionArgs][0]=sender address or Id' ``` ##### Step 4: (Optional) Add the Account or Player You're Using * Add account or add player ```bash [Terminal] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d 'chainId=80002' \ -d 'policy=pol_...' \ -d 'account=acc...' or 'player=pla...' \ -d 'optimistic=true' \ -d 'interactions[0][contract]=con_...' \ -d 'interactions[0][functionName]=mint' \ -d 'interactions[0][functionArgs][0]=sender address or Id' ``` :::: import { VideoSnippet } from "@/components/VideoSnippet.tsx"; ## Gas sponsorship Whether you're building a game, marketplace, or any web3 application, these policies give you the tools to create seamless user experiences by controlling how transaction fees are handled. ### What's a Gas Manager? A gas manager is at the heart of how Openfort handles transaction fees. Think of it as your control center for managing how and when your application will sponsor user's gas fees. Start by visiting the [Gas Policy tab](https://dashboard.openfort.io/policies) in your dashboard and clicking `Add Policy`. From there, you'll be able to configure how you want to handle transaction fees. ### Sponsoring gas fees When it comes to sponsoring gas fees, you have two main payment methods available: #### Paying with credit card The simplest way to get started is by adding [balance credit](https://dashboard.openfort.io/settings/project/billing) to your account. When you choose this method, gas costs are automatically deducted from your balance as transactions occur. This is particularly useful when you're ready to go live with your project, as it's required for `livemode` operations. #### Paying with native tokens For more advanced use cases, you can use network native tokens (like ETH on Base). This method involves depositing tokens to Openfort's Paymaster contract. Here's how to set it up: Check the entity addresses page to find the paymaster's address in your network. :::note Tokens deposited in this contract can always be withdrawn by the owner, and you can control its balance at any time by checking its balance. ::: :::steps ##### Deposit your tokens using the `depositFor` function
deposit native tokens
##### Register your EOA wallet Go to the [backend wallets](https://dashboard.openfort.io/accounts) page to register your EOA wallet. ##### Sign and validate your signature Click on advanced configuration (see the video below). ##### Configure your policy Select "Sponsor gas with your tokens" when editing or creating a policy.
deposit native tokens
::: :::warning When using a strategy that supports payment in ERC-20 tokens (i.e. `charge_custom_tokens` or `fixed_rate`), the backend wallet will receive the tokens users pay for gas fees. ::: ### Optional: Using external paymasters For those needing custom solutions, Openfort supports integration with external paymasters. This feature is particularly useful when you have specific requirements for gas sponsorship that go beyond the standard options. Note that when using external paymasters, you'll need to use the `pay_for_user` strategy. :::info When using an external paymaster, the only supported `strategy` is `pay_for_user`. ::: You can set up an external paymaster either through the dashboard or via the API. #### Using the dashboard
Using external paymasters
#### Using the API ```bash # Create the paymaster object curl https://api.openfort.io/v1/paymasters/ \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d address=80002 \ -d url="YOUR_PAYMASTER_URL" # Link it to your policy curl https://api.openfort.io/v1/policies/:id \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d paymaster=pay_... ``` With these fundamentals in place, you're ready to start managing gas fees for your users. The next section will dive deeper into the different types of policies and rules you can create. import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; import { BadgeDollarSignIcon, BotIcon, CogIcon, FlameIcon, KeyIcon, KeyRoundIcon, LinkIcon, Users2Icon, UsersIcon, } from "lucide-react"; ## Configuration The Openfort configuration settings determine how your application interacts with our services. Here are the main configuration options available for you to jump right in: ## Events ### Why use events? When building Openfort integrations, you might want your applications to receive events as they occur in your Openfort accounts, so that your backend systems can execute actions accordingly. You can configure notifications via the API to be notified about events that happen in your Openfort account or on-chain. By default, Openfort will send a notification to the specified subscriptions every 24 hours. ### Create an event Notification objects are the core of the event system. They define the name of the event and encapsulate both the trigger and subscriptions. ##### curl ```bash command-line curl https://api.openfort.io/v1/notifications/ \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d "name=Low balance" ``` ##### Node.js ```ts server.ts const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const notifications = await openfort.notifications.create({ name: "Low balance" }) ``` ##### .NET ```csharp Program.cs using Openfort.SDK; using Openfort.SDK.Model; const openfort = new OpenfortClient(YOUR_SECRET_KEY); const notifications = await openfort.Notifications.Create( new CreateNotificationRequest( name: "Low balance" ) ); ``` {/* Note: You can also configure events directly from your **dashboard**. */} ### Create a trigger Triggers define the condition that will trigger the notification. There are 3 available triggers: * **[Project balance trigger](https://dashboard.openfort.io/settings/project/notifications)**: Define a threshold balance of credits in your project. This is useful to control you can continue to sponsor gas fees for your users. * **[Contract balance trigger](https://dashboard.openfort.io/developers/events)**: Check for the returned parameter of a contract call and compare it to a threshold. This is useful to control the deposited amount in a paymaster contract. * **[Backend wallet balance trigger](https://dashboard.openfort.io/developers/events)**: Check for the balance of a backend wallet and compare it to a threshold. This is useful when you're using a backend wallet itself is paying for the gas fees of the transactions it puts onchain. There can be more than one notification trigger per event. ##### curl ```bash command-line curl https://api.openfort.io/v1/notification_triggers \ -u "$YOUR_SECRET_KEY:" \ -d 'notification=not_e0b84653-1741-4a3d-9e91-2b0fd2942f60' \ -d 'type=project_balance_trigger' \ -d 'threshold=1000000000' ``` ##### Node.js ```ts server.ts const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const notificationtriggers = await openfort.notificationTriggers.create({ notification: "not_e0b84653-1741-4a3d-9e91-2b0fd2942f60", type: "project_balance_trigger", threshold: "1000000000" }) ``` ##### .NET ```csharp Program.cs using Openfort.SDK; using Openfort.SDK.Model; const openfort = new OpenfortClient(YOUR_SECRET_KEY); const notificationtriggers = await openfort.NotificationTriggers.Create( new CreateNotificationTriggerRequest( notification: "not_e0b84653-1741-4a3d-9e91-2b0fd2942f60", type: "project_balance_trigger", threshold: "1000000000" ) ); ``` ### Create a subscription Subscriptions define the method and target of the event. There are 2 available subscription methods: * **Email**: Send an email to the specified target. * **Webhook**: Send a POST request to the specified target. To learn more about receiving webhooks, check out the [webhooks guide](/docs/configuration/webhooks) and the types `notification.developer_account.balance`, `notification.contract.balance` or `notification.project.balance`. There can be more than one notification subscription per event. #### curl ```bash command-line curl https://api.openfort.io/v1/notification_subscriptions \ -u "$YOUR_SECRET_KEY:" \ -d 'notification=not_e0b84653-1741-4a3d-9e91-2b0fd2942f60' \ -d 'method=Email' \ -d 'target=jaume@openfort.io' ``` #### Node.js ```ts server.ts const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const notificationsubscriptions = await openfort.notificationSubscriptions.create({ notification: "not_e0b84653-1741-4a3d-9e91-2b0fd2942f60", method: "Email", target: "jaume@openfort.io" }) ``` #### .NET ```csharp Program.cs using Openfort.SDK; using Openfort.SDK.Model; const openfort = new OpenfortClient(YOUR_SECRET_KEY); const notificationsubscriptions = await openfort.NotificationSubscriptions.Create( new CreateNotificationSubscriptionRequest( notification: "not_e0b84653-1741-4a3d-9e91-2b0fd2942f60", method: "Email", target: "jaume@openfort.io" ) ); ``` ## How billing works Our goal at Openfort is to provide a *predictable* billing system that grows with your project. ### How billing is organized The Openfort Platform has "projects". A user can be a member of multiple projects. For example: * `User 1` * `Project 1` (Smart Accounts, Auth, Payments) * `Project 2` (Smart Accounts, Auth, Payments) * `User 2` * `Project 3` (Smart Accounts, Auth, Payments) ### Billing system #### Project-based Billing Each project has an individual subscription, a plan and addons. For example, `Project 1` could be on the Pro plan and `Project 2` could be on the Free plan. ![Credit Balance](https://blog-cms.openfort.io/uploads/project_billing_4f33e2d7a9.png) #### Credit balance (Prepaid) Each project has a credit balance. The credit balance is used to pay for the project's gas sponsorship. Given that each proct supports all chains, the credit balance is shared across all chains. You can monitor the gas report spending in by visiting the policy page. ![Policy Gas Reports](https://blog-cms.openfort.io/uploads/policy_report_de8f42695f.png) #### FAQ
Do you only support the prepaid option? No, we also support the postpaid option. Please contact us for more information at [joan@openfort.xyx](mailto\:joan@openfort.xyx).
What happens if my balance reaches 0? Gas sponsorship will be disabled for your project. You can top up your balance to enable it again. If you want more flexible billing options, please contact us at [joan@openfort.io](mailto\:joan@openfort.io), where we can enable a buffer for your project.
## Security & Trust at Openfort At Openfort, safeguarding the data and digital resources of your users is our utmost concern. We understand the crucial role we play in supporting our customers' applications and deeply value the trust placed in us. Our system's design and infrastructure have been rigorously evaluated through multiple security assessments, audits, and penetration tests. Recognizing security as an ever-evolving challenge, we continually subject our systems to these evaluations to identify and remedy emerging vulnerabilities. The process of embedding, maintaining, and testing security measures within your application is a significant endeavor. We dedicate ourselves to implementing industry-leading practices to protect your users' data and digital assets. Highlighted below are key practices we employ: ### Authentication methods Openfort facilitates a variety of authentication techniques to confirm the identities of your users, including: * Email verification using **password-based verification**. * **Social account** verification (Google, Apple, Twitter, Discord) through OAuth2.0. * Ethereum wallet ownership verification using **Sign In With Ethereum (SIWE)**. * Custom authentication methods to match your specific needs (Farcaster, etc.) ### Token Issuance & Session Management After authentication, Openfort issues an **app Access Token (JWT):** * Signed per application; 1-hour expiry for rapid revocation. ### Data Encryption & Backup * **Encryption at Rest:** All databases encrypted using AES-256. * **Regular Backups:** Full daily snapshots; transaction log backups every 5 minutes; 7-day retention. ### Network & API Security * **TLS Everywhere:** TLS 1.2+ with HTTPS enforcement for all traffic. * **API Secrets:** Each application has a unique API secret for server-to-server communication. * **Rate Limiting & WAF:** Protect against brute-force and automated attacks. *** ### Wallet Configurations Openfort offers three wallet types to meet diverse use cases: 1. **Embedded Wallet** 2. **Global wallet** 3. **Backend Wallet** ### 1. Embedded Wallet [Documentation → Embedded Wallet Integration](/docs/products/embedded-wallet/javascript/signer/recovery) Users retain full control of their private key, which is generated and used entirely in a secure, isolated environment in the application's domain. #### Architecture & Workflow 1. **Key Generation:** * 128-bit entropy seed generated in an isolated iframe, converted to BIP-39 mnemonic, and used to derive the keypair. 2. **Private Key Handling:** * Private key remains in memory within the iframe and is never persisted to any database. 3. **Shamir Secret Sharing (SSS):** * Splits the private key into three shares: * **Device Share:** Stored in iframe's local storage. * **Auth Share:** Encrypted by Openfort; fetched via SDK on login. * **Recovery Share:** Encrypted with user password or Shield service.
Non-custodial-key-generation
### 2. Global wallet [Documentation → Global wallet Setup](/docs/configuration/advanced/iframe) A wallet used across multiple applications within the same ecosystem. Functionally identical to the embedded non-custodial wallet, but scoped for interoperability. * **Hosting Domain:** Iframe served from the ecosystem's subdomain. * **Use Cases:** Shared user identity and assets across games, dApps, or partner applications.
Ecosystem signer
### 3. Backend Wallet [Documentation → Server-Side Wallets](/docs/products/server/wallet) Backend wallets managed by Openfort's Key Management Service (KMS) for automated and internal transactions. #### Architecture & Workflow * **Key Generation & Storage:** Google Cloud KMS generates and stores private keys securely. * **Usage:** Automated game transactions, escrow, contract deployments, and gasless ERC-2771 compatible actions.
Custodial-key-generation
### Open source Key Management & Recovery :::info Recovery is facilitated through a service known as **Shield**, which manages the recovery share. It's important to note that the authentication share is retained within Openfort. **Deployment Options:** You can either use the [self-hosted](/docs/configuration/advanced/shield) or the [managed](/docs/products/embedded-wallet/javascript/signer/recovery) Shield service. **Managed Shield service considerations** When Openfort manages Shield on your behalf, the primary security objective is to prevent Openfort from having control over both the authentication and recovery shares simultaneously. This precaution is essential to avoid the possibility of reconstructing the private key. To accomplish this, when registering your project with shield, you will get an extra parameter called `encryption_share`. This parameter is one of the 2 shares of a splitted encryption key (that uses the same Shamir's Secret Sharing algorithm) that is used to encrypt the recovery share. This way, Openfort can't decrypt the recovery share without the encryption share. ::: This setup ensures that the full private key is reconstructed only in memory and never persists, requiring at least two of the three shares for reconstitution.
player-sign-flow
using-private-key
key-regeneration
* **Reconstruction:** Any two shares restore the private key in memory only. * **Off-chain Recovery:** Combine Auth + Recovery or Device + Recovery.
wallet-recovery
* **On-chain Social Recovery:** Use Guardians and smart-contract-based recovery for self-custody accounts.
Custodial-key-generation
### Smart Contract Accounts Programmable wallets at the smart contract layer; custodian status depends on the underlying wallet type (non-custodial, cross-app, or custodial). ### Third-Party Reviews & Audits * **Open-Source Libraries:** Cryptographic libraries under [GitHub](https://github.com/openfort-xyz/shamir-secret-sharing-go). * **Security Audits:** Comprehensive audits for SSS, smart contracts, and paymasters (latest reports available upon request). *** ### Reporting Vulnerabilities If you discover a security issue, please contact our security team at **[security@openfort.io](mailto\:security@openfort.io)**. We welcome responsible disclosure and reward impactful findings. ## Social Login OAuth is commonly used for things like logging in to a social media account from a third-party app. It is a secure and convenient way to authenticate users and share information between applications. ### Provider Token You can use the provider token and provider refresh token returned to make API calls to the OAuth provider. For example, you can use the Google provider token to access Google APIs on behalf of your user. ## Gas Policy Strategies & Rules ### Gas Policy Strategies Gas policies offer three distinct strategies for handling transaction fees: #### 1. Sponsored Transactions Developers cover all gas fees, removing the need for users to hold native tokens. ##### Dashboard ![PolicyERC20Payment](https://blog-cms.openfort.io/uploads/charge_fix_gas_a59ea440fb.png) ##### API ```bash curl https://api.openfort.io/v1/policies/ \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d chainId=80002 \ -d name="Sponsored Policy" \ -d "strategy[sponsorSchema]=pay_for_user" ``` #### 2. Dynamic ERC20 Payment Users pay gas fees using ERC20 tokens at a dynamic exchange rate. ##### Dashboard ![PolicyERC20Payment](https://blog-cms.openfort.io/uploads/gas_payment_erc20_f380972463.png) ##### API ```bash curl https://api.openfort.io/v1/policies/ \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d chainId=43113 \ -d name="Dynamic ERC20" \ -d "strategy[sponsorSchema]=charge_custom_tokens" \ -d "strategy[tokenContract]=con_..." \ -d "strategy[tokenContractAmount]=1" ``` #### 3. Fixed ERC20 Payment Users pay a fixed amount of ERC20 tokens per transaction. ##### Dashboard ![PolicyERC20Payment](https://blog-cms.openfort.io/uploads/charge_fix_gas_a59ea440fb.png) ##### API ```bash curl https://api.openfort.io/v1/policies/ \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d chainId=80002 \ -d name="Fixed ERC20" \ -d "strategy[sponsorSchema]=fixed_rate" \ -d "strategy[tokenContract]=con_..." \ -d "strategy[tokenContractAmount]=10000" ``` ### Policy Rules While strategies determine how gas fees are paid, rules define when and how your gas policy applies. Every policy needs at least one rule to function, and you can choose from three types: | Rule Model | Description | | ------------------------ | ------------------------------------------------------------------- | | **`contract_functions`** | Sponsor interactions with smart contracts. | | **`account_functions`** | Sponsor interactions with the smart account. | | **`limit_rules`** | Limit the amount of gas or number of transactions over an interval. | #### Contract functions These rules let you specify which smart contract interactions your policy covers. You might want to sponsor all interactions with your game contracts but not with external marketplaces, for instance. :::note Wildcard policy
Wildcard policies allow you to sponsor any transaction for any contract without the need to add them in the policy. Enable "Catch-all sponsorship". ::: ![Wildcard policy](https://blog-cms.openfort.io/uploads/wildcard_policy_ba98390abd.png) To create a policy rule, you need first to add a contract to Openfort: ##### Add a contract :::info If the contract is not verified on the block explorer, you will need to enter the ABI manually. If the contract you want to interact with is a proxy contract, you will need to enter the ABI of the implementation contract. ::: ##### API Add a contract to Openfort to an API request: ```bash curl https://api.openfort.io/v1/contracts \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d name="SimpleNFT" \ -d address="0x416c...354D" \ -d chainId="80002" ``` ##### Dashboard Add a new contract by clicking the `Add contract` button in the [Asset contracts](https://dashboard.openfort.io/contracts) section, then enter: * The name of the contract (it can be any name you want; the name is only for identification purposes) * The network (`chainId`) where the smart contract is located. * The address of the contract. * The Application Binary Interface (ABI) of the contract (if not verified in the block explorer of that network). ![DashboardAddContract](https://blog-cms.openfort.io/uploads/adding_contract_dashboard_09dce2f83c.png) Once you've selected your contract, you can then choose what function you wish to enable sponsorship for. You can select `All functions` instead to allow sponsoring all functions in that specific contract. ##### Dashboard ![policyRules](https://blog-cms.openfort.io/uploads/Screenshot_2023_05_29_at_15_53_15_40188a75a5.png) ##### API ```bash curl https://api.openfort.io/v1/policy_rules \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d type="contract_functions" \ -d functionName="mint" \ -d "contract=con_..." ``` #### Account functions These rules cover account-related operations, such as transferring ownership of accounts, managing session keys, or deploying smart accounts. They're essential for maintaining smooth account management operations within your application. ##### Dashboard ![Register Session key](https://blog-cms.openfort.io/uploads/Screenshot_2023_05_31_at_10_02_51_1cd0c933ba.png) ##### API ```bash curl https://api.openfort.io/v1/policy_rules \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d type="account_functions" ``` #### Limit rule Limit rules help you control usage of your gas policy. You can set limits based on: | Rule Type | Description | Example Use Case | | ------------------- | ---------------------------- | ---------------------- | | Gas per Interval | Total gas limit in timeframe | 1000 WEI/minute | | Gas per Transaction | Gas limit per transaction | 100 WEI/transaction | | Count per Interval | Transaction count limit | 10 transactions/minute | ##### Dashboard ![Register Session key](https://blog-cms.openfort.io/uploads/Screenshot_2023_09_09_at_23_56_02_04f898c850.png) ##### API ```bash curl https://api.openfort.io/v1/policy_rules \ -H "Authorization: Bearer $YOUR_SECRET_KEY" \ -d type="rate_limit" \ -d functionName="gas_per_transaction" \ -d gasLimit="1000000" ``` ## Manage teams Openfort provides granular **access controls** to manage permissions across your organizations. For each project, a user can have one of the following roles: * Owner * Administrator * Member A default project is created when you first sign in and you'll be assigned the **Owner** role. Each member can access everything under the project. Create a separate project if you need to restrict access to certain parts. ### Manage team members To invite others to collaborate, visit your project's team settings in the [Dashboard](https://dashboard.openfort.com/settings/project/members) to send an invite link to another user's email. The invite expires after 24 hours. :::note If you're creating an ecosystem, head to the [ecosystem guide](#TODO). ::: #### Permissions across roles \[#permission-across-roles] The table below shows the corresponding permissions for each available role you can assign a team member in the Dashboard. | Permissions | Owner | Administrator | Member | | ----------------------- | ----- | ------------- | ------ | | **Members** | | | | | Add an Administrator | ✓ | ✓ | | | Remove an Administrator | ✓ | ✓ | | | Add a Member | ✓ | ✓ | | | Remove a Member | ✓ | ✓ | | | Revoke an invite | ✓ | ✓ | | | Resend an invite | ✓ | ✓ | | | Accept an invite | ✓ | ✓ | ✓ | | **Billing** | | | | | Read invoices | ✓ | ✓ | | | Read billing email | ✓ | ✓ | | | Read billing address | ✓ | ✓ | | | Update billing address | ✓ | ✓ | | | Read payment methods | ✓ | ✓ | | | Update payment methods | ✓ | ✓ | | | **Projects** | | | | | Create a project | ✓ | ✓ | ✓ | ### Organization Overview The default organization structure at Openfort are split in different projects. Each project has their own API Keys, players, assets and billing configuration beign completely independent one another.
organization-architecture
## Manage Users Use the **Users** page of the dashboard to manage all registered players in your Openfort application. ### Player table The **player table** displays all players that have been registered within Openfort for your selected application. This table only shows players who have been created through Openfort's systems, whether via direct authentication or through your application's integration. You can see important information including: * Provider used for authentication * API ID (unique identifier) * Creation date * Last sign-in timestamp The **player table** is paginated to display 10 players at a time. You can navigate through the list using the "Previous" and "Next" buttons at the bottom. The current view range is displayed (e.g., "Viewing 1 to 10 of 838 results"). #### Player details Clicking on a player's API ID opens the **player details** page, where you can see comprehensive information about the selected player, including: * Details * API ID * Description * Creation timestamp * Metadata * External User ID * Accounts * Chain (e.g., Polygon) * Wallet address * Status (Active/Inactive) * Creation date * Transactions View and manage player transactions, including asset transfers and NFT minting * Sessions Monitor and manage player authentication sessions for frictionless interactions ### Authentication The drawer displays session data such as when the user first logged into your app, when they were last seen in your app. Please note that the "Last seen" field is a rough approximation on the order of an hour from when the user was precisely last active in your app. If you're using a third-party authentication provider instead of Openfort's authentication solution, this section will remain empty, and you'll manage your authentication through your chosen provider's interface. #### Deleting users From the user drawer, you can delete a user if necessary. This is an irreversible and destructive action; if the user logs into your app again, they will have a new user's ID (`playerID`), will have to relink any formerly linked accounts, and will get a new embedded wallet address. **Please take extreme care when deleting users.** For security of user assets, Openfort does not delete the embedded wallet, and instead "soft deletes" it by disassociating it from the deleted user. If the user still has access to their login method and their wallet password, if they have set one, their wallet can be recovered after deletion. ## Webhooks Openfort uses webhooks to push real-time notifications to you about your transactions. All webhooks use HTTPS and deliver a JSON payload that can be used by your application. You can use webhook feeds to do things like: * Granting users a game item when a transaction is confirmed. * Store all transaction events in your own database for custom reporting/retention ### Using Openfort Node SDK Use the [Openfort SDK's](https://www.npmjs.com/package/@openfort/openfort-node) `constructWebhookEvent` method to verify an incoming webhook. Pass in the request body and the signature header. As an example, you can verify a webhook using the code below: :::code-group ```ts [Node.js] app.post( '/webhook', express.raw({ type: 'application/json' }), async (req: Request, _res: Response) => { const openfort = new Openfort('OPENFORT_SECRET_KEY') try { const event = await openfort.constructWebhookEvent( req.body.toString(), req.headers['openfort-signature'] ) switch (event.type) { case "transaction_intent.succeeded": console.log(`TransactionIntent ID: ${event.data.id}`) break case "transaction_intent.failed": console.log(`TransactionIntent ID: ${event.data.id}`) break default: console.log(`Unhandled event type ${event.type}`); } } catch (e) { console.error((e as Error).message) } } ) ``` ```csharp [.NET] [HttpPost("webhook")] public async Task Webhook() { var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); WebHookEvent openfortEvent; try { openfortEvent = client.ConstructWebhookEvent( json, Request.Headers["openfort-signature"] ); // Handle the webhook event var type = openfortEvent.Type; var transactionIntent = openfortEvent.Data; Console.WriteLine($"Webhook notification: {openfortEvent} found"); } catch (Exception e) { Console.WriteLine($"Something failed {e}"); return BadRequest(); } var transactionIntent = openfortEvent.Data; Console.WriteLine($"TransactionIntent ID: {transactionIntent.Id}"); return Ok(); } ``` ```json [Webhook Object] { "data": { "id": "tin_c502d628-5bb3-42f2-b8f5-62ba4d71df3a", "createdAt": 1689869074, "object": "transactionIntent", "etc":"..." }, "type": "transaction_intent.succeeded", "date": 1689869074 } ``` ::: ### Webhook object The webhook object contains the following fields as shown in the JSON tab above. Where the `type` will be one of the following: * `transaction_intent.succeeded`: The transaction intent has arrived on-chain and is confirmed. * `transaction_intent.failed`: The transaction intent has arrived on-chain and is reverted. * `transaction_intent.cancelled`: The transaction intent parameters were not met. * `transaction_intent.broadcast`: The transaction intent was broadcasted. * `balance. project`: The project balance. * `balance.contract`: The contract balance. * `balance.dev_account`: The balance of your backend wallet. The `data` will be a [transaction intent object](https://www.openfort.io/docs/reference/api/get-a-transaction-intent-object). #### Register your development webhook endpoint Register your publicly accessible HTTPS URL in the Openfort [dashboard](https://dashboard.openfort.io/webhooks). Then decide the type of webhook you want to receive. :::tip You can create a tunnel to your localhost server using a tool like [ngrok](https://ngrok.com/download). For example: [https://8733-191-204-177-89.sa.ngrok.io/webhooks](https://8733-191-204-177-89.sa.ngrok.io/webhooks) :::
transaction_intent-sign
#### Test that your webhook endpoint is working properly :::tip Your endpoint must return a 2xx (status code 200-299) response for the webhook to be marked as delivered. Any other statuses (including 3xx) are considered failed deliveries. ::: Send a few test transactions to check that your webhook endpoint is receiving the events. You can specify the number of block confirmations you want to wait before getting notified of a transaction making it on chain. The default is 0 (i.e. as soon as the transaction arrives on chain). To do so, you need to include the `confirmationBlocks` body parameter when [creating the transaction intent](https://www.openfort.io/docs/reference/api/create-a-transaction-intent-object).
transaction_intent-sign
### Comparison Here are non-detailed reasons why you may want to use **Openfort** over other wallet infrastructure providers and approaches. #### **vs Other Wallet SDKs** * **Framework agnostic** – Works with any stack, from web to mobile SDKs including Vanilla JS for maxi,um customization. * **Unified experience** – Embedded wallets, account abstraction, and necessary infrastructure in one SDK. No need for 4 different vendors. * **Full control** – Customize UI, flows, and permissions exactly how you want. We love headless. #### **vs Self-Hosted Wallet Solutions** * **No separate infrastructure** – Runs in your app, no need to maintain a dedicated wallet backend. * **Zero ops overhead** – No additional servers or security audits to manage. * **Complete feature set** – AA wallets, MPC options, and in-app UX ready out-of-the-box. #### **vs Managed Wallet Services** * **Own your data** – Your users stay in your ecosystem; no vendor lock-in. * **Transparent pricing** – No hidden per-user or transaction-based fees that scale unpredictably. * **Open source** – Inspect and contribute; avoid black-box dependencies. #### **vs Building Your Own Wallet Stack** * **Security handled** – Battle-tested flows for wallet creation, key management, and transaction signing. * **Save months of work** – Focus on your core product instead of low-level wallet infra. * **Extensible design** – Add custom flows without reinventing the wheel. import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; ## Backend Wallets Openfort provides powerful backend wallet APIs that allow developers to provision and manage wallets directly from their server-side applications. These backend wallets offer a robust set of features for creating, controlling, and automating blockchain interactions at scale. ### Features import { Server, Shuffle, Wallet, RefreshCcw, ShieldCheck, Package, Globe } from "lucide-react"; ## Pricing | Product | Description | Pricing | Value Proposition | | :------------------- | :---------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Embedded Wallets** | Seamless, invisible wallets for your users. Integrate onboarding, authentication, and account abstraction in one stack. | [See pricing](https://www.openfort.io/pricing) | **Faster integration, lower costs:** Open source and vertically integrated, so you don’t need 3–4 separate vendors. Easy to self-host or migrate anytime—no vendor lock-in. | | **Global Wallets** | Launch your own global, cross-app wallet (like Abstract Wallet or Coinbase Smart Wallet). Modular and flexible. | [See pricing](https://www.openfort.io/pricing) | **Own your wallet ecosystem:** Build a branded wallet that works across apps. Full modularity: bring your own auth (Privy, Dynamic, WebAuthn) and any smart account (4337, 7702, Biconomy). | | **Rapidfire** | Open source, free-to-use authentication and wallet connector UI. Competes with Porto, but with more auth options. | Free & open source | **Onboard users instantly:** Plug-and-play UI for wallet connection with OAuth, email, and WebAuthn. Fully open source. | import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; ## Global Wallets (Ecosystem SDK) Openfort's global wallets provide a comprehensive solution for creating interoperable wallets across multiple games and applications within a unified ecosystem. These wallets offer advanced features that enhance user experience and simplify asset management for both players and developers. :::tip Want to start building right away? Check out the [demo](https://rapidsafe.sample.openfort.io/) [Get started with our documentation](/docs/products/cross-app-wallet) ::: ### Features import { Wallet, LayoutDashboard, Palette, Network, LogIn, Coins, CreditCard } from "lucide-react"; import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; ## Embedded Wallets (Headless Wallets) Openfort's embedded wallets provide a seamless way to integrate wallet functionality into your application without any user interface. These embedded wallets offer a range of features to enhance user experience and security, allowing for onchain interactions. :::tip Want to start building right away? Check out the [demo](https://create-next-app.openfort.io/). [Get started with our documentation](/docs/products/embedded-wallet/). ::: ### Features import { KeyRound, ShieldCheck, Zap, LogIn, Wallet, Settings } from "lucide-react"; {/* Export Keys */} {/* Smart Wallet */} {/* Session Keys */} {/* Sign Messages */} {/* Security Shield */} {/* State Management */} {/* Authentication Suite */} import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; import { Wallet, Layers, PackageOpen, Zap, Puzzle } from "lucide-react"; ## Welcome to Openfort Openfort builds secure wallet infrastructure and user authentication to enable better financial products. We provide embedded wallet solutions and global wallet capabilities that allow applications to seamlessly integrate digital asset functionality for users, businesses, and automated systems. ### Why Openfort? *Authentication and wallet management have traditionally been complex and fragmented. Many solutions require developers to stitch together multiple services or write extensive custom code to support secure onboarding, embedded wallets, and global wallet experiences. Openfort is designed to solve these challenges by providing a unified, developer-friendly, and open-source platform for authentication and wallet infrastructure—making it easy to integrate secure, seamless digital asset functionality into any application.* ### Chose the right product for import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; ## RapidFire Wallet Rapidfire is the branded wallet from Openfort. It's an opensource implementation that you can integrate for free to have all the wallet capabilities. :::tip Want to start building right away? Check out the [demo](https://rapidfire.sample.openfort.io/) [Get started with our documentation](/docs/products/cross-app-wallet/usage/web-app-wagmi) ::: ### Features import { Mail, KeyRound, Droplet, Lock, CreditCard, Globe, Palette } from "lucide-react"; {/* Email, social & passkeys */} {/* Authentication with SIWE */} {/* Natively gasless */} {/* Permission */} {/* Payment flows */} {/* No app or extension needed */} {/* Customizable theme */} import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; import { Puzzle } from "lucide-react"; ## Openfort SDKs \[The recommended way to interact with the Openfort API is by using one of our official SDKs] ### Client-side SDKs Openfort client-side helper libraries (also known as client-side SDKs) reduce the amount of work required to handle signers and session keys. #### With Auth UI #### Without Auth UI (Headless) ### Server-side SDKs and API Openfort server-side helper libraries (also known as server-side SDKs) reduce the amount of work required to use Openfort's REST APIs, starting with reducing the boilerplate code you have to write. import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; import { Lock, Puzzle } from "lucide-react"; ## Openfort samples \[Browse the official Openfort repositories for SDKs, smart contracts, and integrations to accelerate your development.] Explore the library of sample projects using Openfort. :::tip Find even more samples in our [GitHub repository](https://github.com/openfort-xyz/samples#readme). ::: ### Embedded Wallet #### With Auth UI #### Without Auth UI (Headless) ### Global Wallet (Ecosystem) #### Server-side ## Postman API Suite Interested in trying out Openfort's APIs via Postman? We've got you covered! Postman offers a seamless experience for testing APIs, and we've created specialized collections just for you. We recommend using our quality assurance (QA) testing use cases, and importing our [Postman collection](https://api.postman.com/collections/26730089-47206a29-8371-4fe2-82ce-6cb893f4bfa7?access_key=PMAT-01HZSY9PS64FCGCKXSB9FG7J0K) to aid you in the testing process. ### Getting Started * **Fork:** Clone the collection, keeping a link to the original. * **View**: Test the API instantly without importing to Postman. * **Import**: Clone the collection without the original link. ### Configuration To use the collection, navigate to the collection you just imported and click Variables. Copy your testmode Stripe secret key from the Openfort Dashboard, and paste it into the Initial Value field. After you complete this step, you're ready to begin making requests. #### Authorization Input your API key in the "Authorization" tab. For advanced testing, store the API key as a Postman variable. ## Iframe Service The iframe service is a crucial element in ensuring the security and privacy of a user's private keys. The isolated iframe operates as a secure environment, managing the private key and executing wallet operations. This setup is pivotal because it keeps the private key confined to an in-memory state within the iframe, never storing it in a database or allowing it to persist beyond the session. This isolation also means the private key is protected from potential exposure or theft, as it's inaccessible to both the app's code and the Openfort SDK. :::tip[Recommendation] Self hosting and writing your own iframe communication code is a complex task. We recommend using the [@openfort/openfort-js](https://www.npmjs.com/package/@openfort/openfort-js) to interact with the iframe. The SDK provides a simple and secure way to communicate with the iframe service. ::: This guide provides a step-by-step approach to downloading and running the openfort/iframe Docker image, which is hosted on Docker Hub. By following these instructions, you will be able to set up and host the image on your local machine or server environment. ### Hosting the iframe ::::steps #### Prerequisites Before you begin, make sure you have the following installed: * **Docker**: Ensure you have Docker installed on your machine. If not, download and install Docker from the [official website](https://docs.docker.com/engine/install/). #### Step 1: Pulling the Docker Image To start, you'll need to pull the openfort/iframe image from Docker Hub. Open a terminal and run the following command: :::code-group ```bash [Terminal] docker pull openfort/iframe ``` ```bash [Specific Version] docker pull openfort/iframe:tagname ``` ::: This command downloads the latest version of the openfort/iframe image to your local machine. If you need a specific version, you can tag it like this: `openfort/iframe:tagname`. You can also find the Docker image on [Docker Hub](https://hub.docker.com/r/openfort/iframe). #### Step 2: Running the Docker Image Once the image is pulled, you can run it as a container. To do this, execute the following command in your terminal: ```bash [Terminal] docker run -d -p 8080:80 openfort/iframe ``` Here, `-d` runs the container in detached mode (in the background), and `-p 8080:80` maps port 80 of the container to port 8080 on your host machine. Adjust the port settings as needed for your environment. #### Step 3: Accessing the Application After the container starts, you can access the application by opening a web browser and navigating to `http://localhost:8080`. You should see the Openfort iframe service running and ready for use. :::: ### Interacting with the iframe The iframe service provides a set of APIs that allow you to interact with the embedded wallet. These APIs enable you to create, sign, and broadcast transactions, as well as manage accounts and keys. #### Communicating with the iframe We have created a client-side library to help you interact with the iframe service. You can find the library on [GitHub](https://github.com/openfort-xyz/iframe-client). :::tip[iframe Client SDK] A client library to communicate with the Openfort iframe service. This library provides a simple and secure way to interact with the iframe service for key generation and signing transactions. ::: ## Shield Service :::info[Developer Guide] This guide is for developers that want to self-host Shield (automatic recovery) for their users. If you are a user looking to use Shield and to understand how it works, please refer to the [user guide](/docs/products/embedded-wallet/javascript/signer/recovery). ::: In automatic recovery, Openfort uses Shield to encrypt and store the **recovery share** of the embedded wallet on behalf of the user. :::warning[Security Consideration] In the case you do not require a passcode from your users, you are trusting whoever hosts Shield's infrastructure as well as setting the user's authentication token as the sole root of trust for their wallet. We generally recommend you prompt users to set a recovery passcode upfront, especially as assets in a wallet grow. ::: ### Self-Hosted Shield You can self-host the Shield service to maintain full control over your users' recovery data: :::code-group ```bash [GitHub Repository] # Clone the Shield repository git clone https://github.com/openfort-xyz/shield ``` ```bash [Installation] # Install dependencies npm install ``` ```bash [Configuration] # Configure your Shield instance cp .env.example .env # Edit .env with your settings ``` ::: :::tip[Repository] **Shield** - Self-hostable Shield service for automatic wallet recovery * **Repository**: [github.com/openfort-xyz/shield](https://github.com/openfort-xyz/shield) * **Description**: Complete Shield service implementation for self-hosting ::: ### Communicating with Shield We have created a client-side library to help you interact with the Shield service: :::code-group ```bash [Installation] npm install @openfort/shield-js ``` ```typescript [Usage] import { ShieldClient } from '@openfort/shield-js' const shieldClient = new ShieldClient({ baseUrl: 'https://your-shield-instance.com' }) ``` ::: :::tip[Client SDK] **Shield Client SDK** - Official client library for Shield communication * **Repository**: [github.com/openfort-xyz/shield-js](https://github.com/openfort-xyz/shield-js) * **Description**: A client library to communicate with the Openfort Shield service ::: ### Next Steps * [User Guide](/docs/products/embedded-wallet/javascript/signer/recovery) - Learn how Shield works for end users * [Shield Repository](https://github.com/openfort-xyz/shield) - Self-host the Shield service * [Shield Client SDK](https://github.com/openfort-xyz/shield-js) - Integrate with Shield programmatically ## Using AI-powered IDEs with our docs We've created `.txt` files that you can easily copy and paste into your large language model (LLM) of choice to give more context on how all of our Openfort APIs works and significantly improve the accuracy of your AI-generated code. ### Use Openfort docs llms-full.txt file Copy paste the following into an LLM or AI Agents like Cursor or Replit to help them easily understand the correct syntax of the Openfort SDKs 2. Navigate to **Settings > Features > Docs** 3. Select "Add new doc: and paste the following IRL: ``` https://www.openfort.io/docs/llms-full.txt ``` 3. Use `@docs -> Openfort` ro reference Openfort's docs in your code. #### Relevant files * **[Directory structure (llms.txt)](https://www.openfort.io/docs/llms.txt)** * **[Full documentation (llms-full.txt)](https://www.openfort.io/docs/llms-full.txt)** ## Custom Generic Auth Token Generic authentication serves as an alternative for those utilizing their own authentication server. This method accommodates various authentication types not currently supported, such as `GitHub`, or bespoke systems. Essential steps and requirements for generic authentication through an endpoint include: * Post-login, generate a public identifier to recognize the user. * Relay this identifier to the private key to initiate wallet creation. * An endpoint you provide will be contacted to confirm the user's identity, upon which we'll create a wallet if the information is valid. You'll need to supply an endpoint for identity verification. Additional headers for request authentication can be passed and will accompany every verification request to your endpoint. ### Authenticating Users with Generic Authentication #### Configure your server Within the server that handles authentication requests, you'll need to implement an endpoint responsible for verifying the user's identity. This endpoint should accept a `POST` request with a JSON body containing the `payload` field, which corresponds to the user's public identifier. ```json { "payload": "public_identifier" // you can put any data you want here (as long as it's a string) } ``` After returning a JSON response, the SDK will create a wallet for the user if the response is valid. The response should contain the following fields: ```json { "userId": "unique_user_id", // A unique identifier for the user, used for wallet identification if no email is provided "email": "user_email" // optional } ``` :::warning Openfort uses the `userId` field as the user identifier for **`externalUserId`**. Ensure that, across your `test` and `live` environments, different users cannot get the same `userId` value. This is crucial for maintaining unique user identification. ::: #### Set up your provider To set up your Custom Authentication with Openfort, visit your [dashboard provider settings](https://dashboard.openfort.io/players/auth/providers).
custom auth
## Custom OIDC compatible Auth OIDC authentication setup is a viable choice when leveraging an external authentication provider such as `Auth0`, `Cognito`, etc., that offers JWK publication for token authenticity verification. An OIDC authentication framework employs a public-private key pair, utilizing the private key to sign authentication tokens. The public key is made accessible via a public URL in JWKS format, typically found at `https://{domain}.com/.well-known/jwks.json`. When a user logs in, an idToken, a JWT, is produced and signed with the private key, following OIDC specifications for token field requirements. This JWT is then used within the private key to create a user wallet. The verification of the JWT against the public key confirms its authenticity, allowing wallet generation based on the subject (user identifier) within the idToken. Input Requirements: * JWKS File URL (public key): Validates the token's authentic signature. * idToken's `aud` value: Confirms that the intended recipient of the token is correct. :::danger From the JWT token, Openfort extracts the `sub` field, which is used as the user identifier for **`externalUserId`**. Ensure that, across your `test` and `live` environments, different users cannot get the same `sub` value. This is crucial for maintaining unique user identification. ::: ### Authenticating Users with OIDC-Compatible Authentication #### Set up your provider To set up your OIDC Authentication with Openfort, visit your [dashboard provider settings](https://dashboard.openfort.io/players/auth/providers).
oidc auth
## AccelByte Auth AccelByte offers robust backend services for building and operating online games, featuring scalable cloud infrastructure, matchmaking, and cross-platform player accounts. Their solutions focus on enhancing multiplayer experiences while providing developers with the flexibility to customize game services. ### Set up your provider To set up Accelbyte to authenticate players with Openfort, visit your [dashboard provider settings](https://dashboard.openfort.io/players/auth/providers).
accelbyte auth
## Firebase Auth Firebase is a development platform from Google that provides a variety of tools and services to help developers build, improve, and grow their apps, with features such as databases, analytics, messaging, and crash reporting. It emphasizes easy integration and real-time updates, enabling developers to create rich, collaborative experiences. ### Prerequisites Head to your Project Settings in the Firebase Console and grab your Firebase Project ID. ### Set up your provider To set up Firebase to authenticate players with Openfort, visit your [dashboard provider settings](https://dashboard.openfort.io/players/auth/providers).
firebase auth
## LootLocker Auth LootLocker offers robust backend services for building and operating online games, featuring scalable cloud infrastructure, matchmaking, and cross-platform player accounts. Their solutions focus on enhancing multiplayer experiences while providing developers with the flexibility to customize game services. ### Set up your provider To set up LootLocker to authenticate players with Openfort, visit your [dashboard provider settings](https://dashboard.openfort.io/players/auth/providers).
lootlocker auth
## PlayFab Auth PlayFab provides a comprehensive suite of live game management services, including server hosting, data analytics, and liveOps utilities to streamline game development and monetization. It is designed to empower developers with the tools needed to engage players and drive revenue, all while minimizing overhead and time to market. ### Prerequisites To set up your PlayFab authentication with Openfort, you'll need to get the `Project ID`. 1. Visit the [PlayFab developer dashboard](https://developer.playfab.com/), select your title, and navigate to ***Settings wheel --> Title settings***:
playfab title settings
2. In the ***API Features*** section, copy your ***Title ID***:
get playfab title id
## Supabase Auth Supabase is an open-source alternative to Firebase. It provides a variety of tools and services to help with Postgres database, Authentication, instant APIs, Realtime, Functions, Storage and Vector embeddings. ### Prerequisites Head to your Project Settings in the Supabase Console and grab your Project URL and anon API Key. ### Set up your provider To set up Supabase to authenticate players with Openfort, visit your [dashboard provider settings](https://dashboard.openfort.io/players/auth/providers).
supabase auth
## Custom SMTP \[How to work with passwords in Openfort Auth] At present, you can trial the Openfort platform by sending up to **3** emails per hour via the built-in service. The default email service as a whole is offered on a best effort basis: we will do our best to maintain it and will review usage of the service on a regular basis to see if the email service should be continued. As you progress toward production, you may find yourself wanting for a custom SMTP service in order to increase your limits. A custom SMTP server will allow you to set your own cap on the number of emails sent per hour. Beyond rate limits, an SMTP server might also help with: * Deliverability and Reputation Management * Scalability * Analytics and Tracking * Compliance and Anti Spam measures ### How to set up SMTP Head over to [Settings Page](https://dashboard.openfort.io/settings/configuration/auth) and hit "Enable Custom SMTP" under the SMTP Provider section. Fill in fields below with the relevant details obtained from your custom SMTP provider:
authenticated players
#### SMTP providers You can use Openfort Auth with any major SMTP provider of your choosing. Some SMTP providers you could consider using are: * [Twilio SendGrid](https://docs.sendgrid.com/for-developers/sending-email/integrating-with-the-smtp-api) * [AWS SES](https://docs.aws.amazon.com/ses/latest/dg/send-email-smtp.html) ### Email templates You can customize the email messages used for the authentication flows. You can edit the following email templates: * Confirm signup * Reset Password
email templates
### Terminology The templating system provides the following variables for use: | Name | Description | | -------------------- | ------------------------------------------------------------------------ | | `{{ .state }}` | Contains a 6-digit One-Time-Password (OTP). | | `{{ .email }}` | The user's email address. | | `{{ .redirectUrl }}` | Contains the redirect URL to confirm the email address to a new account. | ### Editing email templates Edit your email templates on the [Email Templates](https://dashboard.openfort.io/settings/configuration/templates) page. Below is an example for a verification of a sign up: ``` Subject: Confirm Reauthentication Body:

Confirm reauthentication

Enter the code: {{ .state }}

``` ## Password-based Auth :::note Email authentication with global wallets You only need to enable this toggle if you're developing an global wallets.
email setup
::: ### Password security A password is more secure if it is harder to guess or brute-force. In theory, a password is harder to guess if it is longer. It is also harder to guess if it uses a larger set of characters (for example, digits, lowercase and uppercase letters, and symbols). This table shows the *minimum* number of guesses that need to be tried to access a user's account: | Required characters | Length | Guesses | | -------------------------------------------- | ------ | ----------------- | | Digits only | 8 | \~ 227 | | Digits and letters | 8 | \~ 241 | | Digits, lower and uppercase letters | 8 | \~ 248 | | Digits, lower and uppercase letters, symbols | 8 | \~ 252 | In reality though, passwords are not always generated at random. They often contain variations of names, words, dates, and common phrases. Malicious actors can use these properties to guess a password in fewer attempts. There are hundreds of millions (and growing!) known passwords out there. Malicious actors can use these lists of leaked passwords to automate login attempts (known as credential stuffing) and steal or access sensitive user data. #### Password strength and leaked password protection To help protect your users, Openfort Auth sets strength constrains of the passwords used on your project. * Set a large minimum password length. Anything less than 8 characters is not recommended. * Set the required characters that must appear at least once in a user's password. Use the strongest option of requiring digits, lowercase and uppercase letters, and symbols. * Prevent the use of leaked passwords. Openfort Auth uses the open-source [HaveIBeenPwned.org Pwned Passwords API](https://haveibeenpwned.com/Passwords) to reject passwords that have been leaked and are known by malicious actors. ## Login with Apple Openfort Auth supports using [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) on the web and in native apps for iOS, macOS, watchOS or tvOS. ### Overview To support Sign in with Apple, you need to configure the [Apple provider in the Openfort dashboard](https://dashboard.openfort.io/players/auth/providers) for your project. There are three general ways to use Sign in with Apple, depending on the application you're trying to build: * Sign in on the web or in web-based apps * Using an OAuth flow initiated by Openfort Auth using the [Sign in with Apple REST API](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api). * Sign in natively inside iOS, macOS, watchOS or tvOS apps using [Apple's Authentication Services](https://developer.apple.com/documentation/authenticationservices) In some cases you're able to use the OAuth flow within web-based native apps such as with [React Native](https://reactnative.dev), [Expo](https://expo.dev) or other similar frameworks. It is best practice to use native Sign in with Apple capabilities on those platforms instead. When developing with Expo, you can test Sign in with Apple via the Expo Go app, in all other cases you will need to obtain an [Apple Developer](https://developer.apple.com) account to enable the capability. ### Using the OAuth flow for web Sign in with Apple's OAuth flow is designed for web or browser based sign in methods. It can be used on web-based apps as well as websites, though some users can benefit by using Sign in with Apple JS directly. Behind the scenes, Openfort Auth uses the [REST APIs](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api) provided by Apple. #### Configuration You will require the following information: 1. Your Apple Developer account's **Team ID**, which is an alphanumeric string of 10 characters that uniquely identifies the developer of the app. It's often accessible in the upper right-side menu on the Apple Developer Console. 2. Register email sources for *Sign in with Apple for Email Communication* which can be found in the [Services](https://developer.apple.com/account/resources/services/list) section of the Apple Developer Console. 3. An **App ID** which uniquely identifies the app you are building. You can create a new App ID from the [Identifiers](https://developer.apple.com/account/resources/identifiers/list/bundleId) section in the Apple Developer Console (use the filter menu in the upper right side to see all App IDs). These usually are a reverse domain name string, for example `com.example.app`. Make sure you configure Sign in with Apple once you create an App ID in the Capabilities list. At this time Openfort Auth does not support Server-to-Server notification endpoints, so you should leave that setting blank. (In the past App IDs were referred to as *bundle IDs.*) 4. A **Services ID** which uniquely identifies the web services provided by the app you registered in the previous step. You can create a new Services ID from the [Identifiers](https://developer.apple.com/account/resources/identifiers/list/serviceId) section in the Apple Developer Console (use the filter menu in the upper right side to see all Services IDs). These usually are a reverse domain name string, for example `com.example.app.web`. 5. Configure Website URLs for the newly created **Services ID**. The web domain you should use is the domain your Openfort project is hosted on. The redirect URL is `https://api.openfort.io/iam/v1/oauth/callback/apple`. 6. Create a signing **Key** in the [Keys](https://developer.apple.com/account/resources/authkeys/list) section of the Apple Developer Console. You can use this key to generate a secret key using the tool below, which is added to your Openfort project's Auth configuration. Make sure you safely store the `AuthKey_XXXXXXXXXX.p8` file. If you ever lose access to it, or make it public accidentally, revoke it from the Apple Developer Console and create a new one immediately. You will have to generate a new secret key using this file every 6 months, so make sure you schedule a recurring meeting in your calendar! 7. Finally, add the information you configured above to the [Apple provider configuration in the Openfort dashboard](https://dashboard.openfort.io/players/auth/providers).
Use this tool to generate a new Apple client secret. No keys leave your browser! Be aware that this tool does not currently work in Safari, so use Firefox or a Chrome-based browser instead.
> **Apple Client Secret Generator** > > To generate your Apple client secret, you'll need: > > * Your Apple Developer Team ID > * Your Services ID (Client ID) > * Your private key file (.p8) downloaded from Apple Developer Console > > You can use online JWT generators or create the secret programmatically using the Apple Developer documentation. The secret must be regenerated every 6 months. > > For detailed instructions, refer to [Apple's Sign in with Apple REST API documentation](https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens). ### Using native sign in with Apple in Expo When working with Expo, you can use the [Expo AppleAuthentication](https://docs.expo.dev/versions/latest/sdk/apple-authentication/) library to obtain an ID token that you can pass to openfort-js [`authenticateThirdParty` method](#TODO).
When testing with Expo Go, the Expo App ID `host.exp.Exponent` will be used. Make sure to add this to the "Client IDs" list in your [Openfort dashboard Apple provider configuration](https://dashboard.openfort.io/players/auth/providers)! Make sure you set up **Apple Native** under third party authentication providers.
1. Have an **App ID** which uniquely identifies the app you are building. You can create a new App ID from the [Identifiers](https://developer.apple.com/account/resources/identifiers/list/bundleId) section in the Apple Developer Console (use the filter menu in the upper right side to see all App IDs). These usually are a reverse domain name string, for example `com.example.app`. Make sure you configure Sign in with Apple for the App ID you created or already have, in the Capabilities list. At this time Openfort Auth does not support Server-to-Server notification endpoints, so you should leave that setting blank. (In the past App IDs were referred to as *bundle IDs.*) 2. Register all of the App IDs that will be using your Openfort project in the [Apple provider configuration in the Openfort dashboard](https://dashboard.openfort.io/players/auth/providers) under *Client IDs*.
If you're building a native app only, you do not need to configure the OAuth settings.
## Discord Login To enable Discord Auth for your project, you need to set up a Discord OAuth application and add the application credentials in the Openfort Dashboard. ### Overview Setting up Discord logins for your application consists of 3 parts: * Create and configure a Discord Project and App on the [Discord Developer Dashboard](https://discord.com/developers). * Add your Discord API Key and API Secret Key to your [Openfort Project](https://dashboard.openfort.io/players/auth/providers). * Add the login code to your [Openfort JS Client App](https://github.com/openfort-xyz/openfort-js). #### Configuration **Access your Discord account** * Go to [discord.com](https://discord.com/). * Click on `Login` at the top right to log in. * Once logged in, go to [discord.com/developers](https://discord.com/developers). * callback URL: `https://api.openfort.io/iam/v1/oauth/callback/discord` **Create a Discord application** * Click on `New Application` at the top right. * Enter the name of your application and click `Create`. * Click on `OAuth2` under `Settings` in the left side panel. * Click `Add Redirect` under `Redirects`. * Type or paste your `callback URL` into the `Redirects` box. * Click `Save Changes` at the bottom. * Copy your `Client ID` and `Client Secret` under `Client information`. **Add your Discord credentials into your Supabase project**
Discord Auth
## Epic Games Login To enable Epic Games Auth for your project, you need to set up a Epic Games OAuth application and add the application credentials in the Openfort Dashboard. ### Overview Setting up Epic Games login for your application consists of 3 parts: * Create and configure a Epic Project and App on the [Epic Developer Dashboard](https://dev.epicgames.com/portal/en-US/). * Add your Epic API Key and API Secret Key to your [Openfort Project](https://dashboard.openfort.io/players/auth/providers). * Add the login code to your [Openfort JS Client App](https://github.com/openfort-xyz/openfort-js).
Epic Auth
#### Configuration 1. Create an organization on Epic Games portal and create a new project. 2. Click on `Epic Account Services` and add a new applications: * Verify your application website and privacy policy URL * Save the API Key (client\_id) and API Secret Key (client\_secret) for later use. 3. Set up Epic Games in Openfort: * Go to the [Openfort dashboard](https://dashboard.openfort.io/players/auth/providers). * Click on Epic Enabled to turn it ON. * Enter the Epic Client ID and Epic Client Secret. ## Facebook Login To enable Facebook Auth for your project, you need to set up a Facebook OAuth application and add the application credentials to your Openfort Dashboard. ### Overview Setting up Facebook logins for your application consists of 3 parts: * Create and configure a Facebook Application on the [Facebook Developers Site](https://developers.facebook.com/). * Add your Facebook keys to your [Openfort Project](https://dashboard.openfort.io/players/auth/providers). * Add the login code to your [Openfort JS Client App](https://github.com/openfort-xyz/openfort-js). #### Configuration * Go to [developers.facebook.com](https://developers.facebook.com). * Click on `Log In` at the top right to log in. **Create a Facebook app** * Click on `My Apps` at the top right. * Click `Create App` near the top right. * Select your app type and click `Continue`. * Fill in your app information, then click `Create App`. * This should bring you to the screen: `Add Products to Your App`. (Alternatively you can click on `Add Product` in the left sidebar to get to this screen.) **Set up Facebook login for your Facebook app** From the `Add Products to your App` screen: * Click `Setup` under `Facebook Login` * Skip the Quickstart screen, instead, in the left sidebar, click `Settings` under `Facebook Login` * Enter your callback URI (`https://api.openfort.io/iam/v1/oauth/callback/facebook`) under `Valid OAuth Redirect URIs` on the `Facebook Login Settings` page * Enter this in the `Valid OAuth Redirect URIs` box * Click `Save Changes` at the bottom right Be aware that you have to set the right use case permissions to enable Third party applications to read the email address. To do so: Under `Build Your App`, click on `Use Cases` screen. From there, do the following steps: * Click the Edit button in `Authentication and Account Creation` on the right side. This action will lead to the other page. * `public_profile` is set by default, so make sure it and `email` have status of **Ready for testing** in the redirected page. * If not, click the **Add** button in email on right side. **Copy your Facebook app ID and secret** * Click `Settings / Basic` in the left sidebar * Copy your App ID from the top of the `Basic Settings` page * Under `App Secret` click `Show` then copy your secret * Make sure all required fields are completed on this screen. **Enter your Facebook app ID and secret into your Supabase project**
Facebook Auth
## Google Login Openfort Auth supports Sign in with Google on the web, native Android applications and Chrome extensions. ### Overview Setting up Twitter logins for your application consists of 3 parts: * Create and configure a Google Project and App on the [Google Cloud Platform](https://console.cloud.google.com/home/dashboard). * Add your Google API Key and API Secret Key to your [Openfort Project](https://dashboard.openfort.io/players/auth/providers). * Add the login code to your [Openfort JS Client App](https://github.com/openfort-xyz/openfort-js). #### Web Configuration ##### Google pre-built configuration 1. Go to the [API Credentials page](https://console.cloud.google.com/apis/credentials). 2. Click `Create credentials` and choose `OAuth Client ID`. 3. For application type, choose `Web application`. 4. Under **Authorized redirect URLs**, enter the callback URL from the [Openfort dashboard](https://dashboard.openfort.io/players/auth/providers). Expand the Google Auth Provider section to display it. 5. When you finish configuring your credentials, you will be shown your client ID and secret. Add these to the Google Auth Provider section of the Openfort Dashboard.
Google Auth
#### Expo React Native Configuration 1. Configure OAuth credentials for your Google Cloud project in the [Credentials](https://console.cloud.google.com/apis/credentials) page of the console. When creating a new OAuth client ID, choose *Android* or *iOS* depending on the mobile operating system your app is built for. * For Android, use the instructions on screen to provide the SHA-1 certificate fingerprint used to sign your Android app. * You will have a different set of SHA-1 certificate fingerprint for testing locally and going to production. Make sure to add both to the Google Cloud Console. and add all of the Client IDs to Openfort dashboard. * For iOS, use the instructions on screen to provide the app Bundle ID, and App Store ID and Team ID if the app is already published on the Apple App Store. 2. Configure the [OAuth Consent Screen](https://console.cloud.google.com/apis/credentials/consent). This information is shown to the user when giving consent to your app. In particular, make sure you have set up links to your app's privacy policy and terms of service. 3. Finally, add the client ID from step 1 in the [Google provider on the Openfort Dashboard](https://dashboard.openfort.io/players/auth/providers), under *Client IDs*. Note that you do not have to configure the OAuth flow in the Openfort Dashboard in order to use native sign in. ## LINE Login Integrate LINE Login into your web app (website) to make it easier for people to create an account and log in. ### Overview Setting up LINE login for your application consists of 3 parts: * Create and configure a LINE Project and App on the [LINE Dashboard](https://developers.line.biz/console/). * Add your LINE `Channel ID` and `Channel Secret` to your [Openfort Project](https://dashboard.openfort.io/players/auth/providers). * Add the login code to your [Openfort JS Client App](https://github.com/openfort-xyz/openfort-js).
LINE Auth
## X (Twitter) Login To enable Twitter Auth for your project, you need to set up a Twitter OAuth application and add the application credentials in the Openfort Dashboard. ### Overview Setting up Twitter logins for your application consists of 3 parts: * Create and configure a Twitter Project and App on the [Twitter Developer Dashboard](https://developer.twitter.com/en/portal/dashboard). * Add your Twitter API Key and API Secret Key to your [Openfort Project](https://dashboard.openfort.io/players/auth/providers). * Add the login code to your [Openfort JS Client App](https://github.com/openfort-xyz/openfort-js).
Twitter Auth
#### Configuration 1. Create Project and App: * Click "+ Create Project", enter project name, and select use case. * Enter a project description and app name. * Copy and save your API Key (client\_id) and API Secret Key (client\_secret). 2. Set Up App Settings: * Click on "App settings". * Go to "User authentication settings" and click "Set up". 3. Configure App Permissions: * Turn ON "Request email from users". * Select "Web App" as the Type of App. * Enter Callback URL, Website URL, Terms of service URL, and Privacy policy URL. 4. Set up X in Openfort: * Go to the [Openfort dashboard](https://dashboard.openfort.io/players/auth/providers). * Click on Twitter Enabled to turn it ON. * Enter the Twitter Client ID and Twitter Client Secret. import { VideoSnippet } from "@/components/VideoSnippet.tsx"; import { RapidfireDemo } from "@/components/Rapidfire/RapidfireDemo.tsx"; import { WalletInfo } from "@/components/Rapidfire/WalletInfo.tsx"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink.tsx"; ## Global Wallet (Ecosystem SDK) Openfort **Global wallets** integrate onboarding, authentication, payments, and recovery with no app or extension required. Projects use the [Ecosystem SDK](https://www.npmjs.com/package/@openfort/ecosystem-js), a TypeScript library, to bootstrap the global wallet. :::note Global wallets are whitelabel and allow you to own the entire developer experience. **In short, you own the *npm package* the developer installs.** ::: We have created a *sample* global wallet called **Rapidfire ID** to showcase the Ecosystem SDK to create a global wallet. To interact with it, developers install its SDK: NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). Check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample). Try signing into your **Rapidfire** account: #### Features | Feature | Status | | --------------------------------- | ------ | | Authentication | ✅ | | Third-party auth/signer support | ✅ | | Smart wallet infrastructure | ✅ | | Mobile Wallet Protocol compatible | ✅ | | Themes and customization | ✅ | ### Get started Check out these popular guides to get started. ### Using on any wallet provider This will internally inject a Wallet instance into your Application via [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963). Modern Wallet Connection libraries will automatically detect the injected instance. Supported wallet connection libraries include: * [Wagmi](https://wagmi.sh/) * [RainbowKit](https://rainbowkit.com/) * [ConnectKit](https://docs.family.co/connectkit) Learn more on how to get started in the [examples](/docs/products/cross-app-wallet/usage/web-app-wagmi) guide. ## Quickstart - 7702 Accounts :::warning This repository is under active development.\ Contracts are **unaudited**, and the codebase may have **breaking changes** without notice. ::: :::note We believe smart accounts should provide an excellent experience throughout a user's journey: * **Effortless Onboarding**: Use WebAuthn and Passkeys with no deployment transaction required * **Flexible Authentication**: Multiple authentication methods including EOA and WebAuthn/Passkeys * **Fine-grained Access Control**: Session keys with customizable permissions and spending limits * **Secure Transactions**: Built-in security features including whitelisting, function filtering, and time-based controls * **Seamless Experience**: Full compatibility with ERC-4337 account abstraction standard * **Gas Sponsorship**: Allow applications to pay for user transactions through session keys * **No Vendor Lock-in**: Built on EIP-7702 and ERC-4337 standards for maximum interoperability ::: **🟢 Live Demo**: [https://7702.openfort.io](https://7702.openfort.io) [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) is an upgrade (Pectra) Ethereum enhancement behaving both as a smart account and as a regular EOA, effectively creating a "hybrid" account that enables embedding EVM code into an Externally Owned Account (EOA). Our [video blog series](https://www.youtube.com/watch?v=94VhTY-1ZsI) has more details for all of you. Account Abstraction has many features that EOA holders can now access due to this. Account Abstraction offers sponsored gas fees as well as multiple operations batched in a single transaction plus session keys in addition to chain abstraction support with similar conveniences. This guide walks through a basic demo that leverages ERC-4337 along with EIP-7702 so that an EOA can directly send a gasless user operation. This [video blog](https://www.youtube.com/watch?v=bE7YUrThS5k) gives information regarding the architecture of the `openfort 7702 account`. We'll demonstrate how to build a complete smart account system that: * Creates secure passkeys using WebAuthn * Initializes smart accounts with non-extractable keys * Signs and executes UserOperations seamlessly * Implements session keys for enhanced user experience ### Installation :::code-group ```sh [npm] npm install viem @noble/secp256k1 ox permissionless ``` ```sh [yarn] yarn add viem @noble/secp256k1 ox permissionless ``` ::: :::steps #### Prepares an EIP-7702 Authorization for signing *Prepares an EIP-7702 Authorization for signing. This Action will fill the required fields of the Authorization object if they are not provided (e.g. nonce and chainId).* **Contract:** [0xad7d8ce7b7A24f80AB107c571840851178F29650](https://sepolia.etherscan.io/address/0xad7d8ce7b7A24f80AB107c571840851178F29650) ##### `Self-execution` ```ts import { sepolia } from 'viem/chains'; import { privateKeyToAccount } from 'viem/accounts' const SEPOLIA_RPC = ''; const walletClient = createWalletClient({ account, chain: sepolia, transport: http(SEPOLIA_RPC), }); const authorization = await walletClient.prepareAuthorization({ account: privateKeyToAccount('0x...'), contractAddress: '0xad7d8ce7b7A24f80AB107c571840851178F29650', executor: 'self', }); ``` * Type: 'self' | undefined * Source: [VIEM](https://viem.sh/docs/eip7702/prepareAuthorization) ##### `Ephemeral EOA` > **What is An Ephemeral EOA:**\ > *Ephemeral key is a temporary cryptographic key created for a single use and then deleted. Unlike permanent keys, ephemeral keys are discarded after each session, making them more secure since compromised keys can't be reused.* ```ts import { createClient } from "viem"; import { sepolia } from 'viem/chains'; import { prepareAuthorization } from "viem/actions"; import { generatePrivateKey, privateKeyToAccount, signAuthorization } from "viem/accounts"; const privateKey = generatePrivateKey(); const signer = privateKeyToAccount(privateKey); const client = createClient({ sepolia, pollingInterval: 1_000, transport: http() }); const authorization = await prepareAuthorization(client, { account: signer.address, contractAddress: '0xad7d8ce7b7A24f80AB107c571840851178F29650' }); ``` #### Initialize the account with a WebAuthn owner, Session Key and guardian ```ts function initialize( Key calldata _key, KeyReg calldata _keyData, Key calldata _sessionKey, KeyReg calldata _sessionKeyData, bytes memory _signature, bytes32 _initialGuardian ) external initializer {} ``` * Source: [OPF7702](https://github.com/openfort-xyz/openfort-7702-account/blob/3c3eecfc80d34295a9e72c749bbb9ff4b0018b48/src/core/OPF7702Recoverable.sol#L122) ##### `Creating Your First Passkey` The adventure begins with creating a WebAuthn credential that will serve for your smart account as the primary authentication method. Your device ties to this credential as well as biometric authentication and a PIN protects that same device. ```ts import { toHex } from "viem"; import { Bytes, WebAuthnP256 } from "ox"; // Create a WebAuthn credential using the account address as the user ID const credential = await WebAuthnP256.createCredential({ authenticatorSelection: { requireResidentKey: false, residentKey: "preferred", // Store credential on device when possible userVerification: "required", // Require biometric/PIN verification }, user: { id: Bytes.from(account.address), name: `${account.address.slice(0, 6)}...${account.address.slice(-4)}`, }, }); // Store the credential ID for future authentication const credentialId = credential.id; const x = toHex(credential.publicKey.x, { size: 32 }); const y = toHex(credential.publicKey.y, { size: 32 }); ``` * A unique key pair that is bound to their device is then created when this process prompts authentication by the user through Face ID, Touch ID, Windows Hello, etc. The private key remains within secure hardware. Smart account initialization returns back the public key. ##### `CallData` With the WebAuthn credential now created, a smart account recognizing this passkey as its primary signer can be initialized: * Prepare Key Data ```ts import { zeroAddress, toHex, keccak256, encodeFunctionData } from "viem"; export enum KeyType { EOA = 0, WEBAUTHN = 1, P256 = 2, P256_NON_EXTRACTABLE = 3, } const key = { pubKey: { x: toHex(x), y: toHex(y), }, eoaAddress: zeroAddress, keyType: KeyType.WEBAUTHN, } as const; ``` * Prepare Token Spend ```ts const spendTokenInfo = { token: zeroAddress, limit: 0n, } as const; ``` * Prepare Key Registration Data ```ts const keyReg = { validUntil: 0n, validAfter: 0n, limit: 0n, whitelisting: false, contractAddress: zeroAddress, spendTokenInfo: spendTokenInfo, allowedSelectors: ['0xdeedbeff'], ethLimit: 0n } as const; const initialGuardian = keccak256('address'); ``` * Prepare Session Key Registration Data (Optional) ```ts const sessionKey = { pubKey: { x: toHex(x), // if not register sessionKey add bytes32(0) y: toHex(y), // if not register sessionKey add bytes32(0) }, eoaAddress: zeroAddress, // if not register sessionKey add zeroAddress keyType: KeyType.P256_NON_EXTRACTABLE, } as const; const spendTokenInfo = { token: usdcAddress, limit: spending_limits, } as const; const sessionKeyReg = { validUntil: unixTime, validAfter: unixTime, limit: limits, whitelisting: true, contractAddress: address, spendTokenInfo: spendTokenInfo, allowedSelectors: [approve_selector, transfer_selector, swap_selector], ethLimit: eth_limits } as const; ``` * Call Data (If Sponsored use with `userOperation`) ```ts // Prepare the initialization call data const callData = encodeFunctionData({ abi: accountABI, functionName: "initialize", args: [ key, keyReg, sessionKey, sessionKeyReg, SIGNATURE_EIP712, // sign with EOA/EPHEMERAL EOA `function getDigestToInit` initialGuardian ] }); // Create the user operation with EIP-7702 authorization if EPHEMERAL EOA const userOperation = await walletClient.prepareUserOperation({ callData, authorization: signedAuthorization, // EIP-7702 authorization signature }); ``` * An EOA can temporarily act as a smart contract because EIP-7702 authorization exists so it enables the initialization process. After execution of this UserOperation, your smart account is ready for WebAuthn-based transactions. #### Register Session Key P256 ```js function registerKey(Key calldata _key, KeyReg calldata _keyData) public {} ``` * Source: [OPF7702](https://github.com/openfort-xyz/openfort-7702-account/blob/3c3eecfc80d34295a9e72c749bbb9ff4b0018b48/src/core/KeysManager.sol#L71) ##### `Creating a Session Key` * Session keys are additionally non-extractable P256 keys that exist. They are created through usage of the WebCrypto API and are stored in a local way: ```ts import { WebCryptoP256 } from "ox"; // Generate a new P256 key pair for the session const keyPair = await WebCryptoP256.createKeyPair(); const publicKey = await WebCryptoP256.getPublicKey({ publicKey: keyPair.publicKey }); // Store the key pair securely (e.g., in IndexedDB) await storeSessionKey(keyPair); ``` * WebAuthn is great for security, but nobody wants to scan their fingerprint for every little action. Session keys fix that: they're short-lived "spare keys" with limited powers, so you can breeze through everyday tasks without constant biometric check-ins. ##### `Registering the Session Key` The session key must be registered with the smart account because it defines its permissions along with validity: * Prepare Key Data ```ts const key = { pubKey: { x: toHex(publicKey.x), // The P256 public key x coordinate y: toHex(publicKey.y), // The P256 public key y coordinate }, eoaAddress: zeroAddress, keyType: KeyType.P256_NON_EXTRACTABLE, } as const; const spendTokenInfo = { token: TOKEN_ADDRESS, limit: SPEND_LIMIT, } as const; ``` * Prepare Key Registration Data ```ts const keyReg = { validUntil: VALID_UNTIL, validAfter: VALID_AFTER, limit: ACTIONS_LIMIT, whitelisting: true, contractAddress: CONTRACT_ADDRESS, spendTokenInfo: spendTokenInfo, allowedSelectors: [ALLOW_SELECTORS], ethLimit: ETH_SPEND_LIMIT } as const; ``` * Call Data (If Sponsored use with `userOperation`) ```ts const callData = encodeFunctionData({ abi: accountABI, functionName: 'registerSessionKey', args: [ key, keyReg ] }); // This registration requires WebAuthn approval const userOperation = await walletClient.prepareUserOperation({ callData, signature: webAuthnStubSignature, }); // Sign with WebAuthn (one-time approval for session key) const webauthnData = await WebAuthnP256.sign({ challenge: getUserOperationHash(userOperation), credentialId, rpId: window.location.hostname, userVerification: "required", }); ``` #### Executing Transactions with WebAuthn Let's show the time when we mint some ERC-20 tokens. First, a UserOperation is prepared by us with a placeholder for its signature. Then, the actual WebAuthn signature acts in its place: * `Preparing the Transaction` ```ts // Encode the mint function call const data = encodeFunctionData({ abi: erc20ABI, functionName: "mint", args: [ walletClient.account.address, parseEther("10"), // Mint 10 tokens ], }); // Prepare UserOperation with stub signature const userOperation = await walletClient.prepareUserOperation({ calls: [ { to: erc20Address, data, }, ], signature: webAuthnStubSignature, // Placeholder signature }); ``` The actual signature is now generated using the WebAuthn credential: ```ts // Get the UserOperation hash that needs to be signed const userOperationHash = getUserOperationHash(userOperation); // Sign with WebAuthn - this triggers biometric authentication const webauthnData = await WebAuthnP256.sign({ challenge: userOperationHash, credentialId, // The credential we created earlier rpId: window.location.hostname, // Relying party identifier userVerification: "required", // Require user verification }); // Replace stub signature with the actual WebAuthn signature userOperation.signature = encodeWebAuthnSignature(webauthnData); // Send to bundler for execution await bundlerClient.sendUserOperation(userOperation); ``` * The user experiences a familiar biometric prompt like Face ID, fingerprint, etc., so once they authenticate, someone signs then submits the transaction. #### Using Session Keys for Transactions * After setup, your session keys can authorize transactions on their own—no fingerprint or face scan needed: ##### `Prepare CallData` * Call Data (If Sponsored use with `userOperation`) ```ts import { WebCryptoP256 } from "ox"; // Prepare the transaction as before const userOperation = await walletClient.prepareUserOperation({ calls: [ { to: erc20Address, data: encodeFunctionData({ abi: erc20ABI, functionName: "mint", args: [walletClient.account.address, parseEther("10")], }), }, ], signature: P256Signature, }); // Sign with the session key (no user interaction required) const { r, s } = await WebCryptoP256.sign({ privateKey: sessionKey.privateKey, payload: getUserOperationHash(userOperation), }); // Format and attach the signature userOperation.signature = encodeP256Signature({ r, s }); // Submit the transaction await bundlerClient.sendUserOperation(userOperation); ``` ::: ### Conclusion WebAuthn with P256 signatures marks a large advance with regard to Web3 security, as well as user experience. Smart accounts that are more secure and even more user-friendly than customary wallets, can be built through leveraging familiar authentication methods and also eliminating private key exposure. Adapting to different security contexts, a system results from combining WebAuthn for high-security operations and session keys for routine transactions requiring strong authentication for sensitive actions enabling frictionless interactions for everyday use. import { MultiOptionDisplay } from '@/components/MCP/MultiOptionDisplay'; import { AddToCursor } from '@/components/MCP/AddToCursor'; import { CopyPrompt } from '@/components/MCP/CopyPrompt'; import Prompt1 from '@/components/MCP/prompt1.mdx?raw'; import Rules from '@/components/MCP/rules.mdx?raw'; ## AI Tooling Quickstart Get started using Openfort's AI tooling to build your app faster than ever. #### Steps 1. Install Openfort's MCP server. 2. Add rules for the LLMs. 3. Create a new project. 4. Debug common issues. 5. Discover all the capabilities. ### 1. Install Openfort's MCP server This will allow your AI Assistant to interact with Openfort's tools on your behalf to create projects and manage them. Ensure you have the following prerequisites: * `Node.js` - Installation guide [here](https://nodejs.org/en/download) * An `Openfort account` - Create one [here](https://dashboard.openfort.io/) Now, add it to your code editor. Based on your preferred tool, follow the instructions below: To integrate our MCP Server with [Cursor](https://docs.cursor.com/context/mcp) you can either: ##### One-click installation ##### Edit the `~/.cursor/mcp.json` You can look it up on your system or find it under the `Tools & Integrations` tab in your `Cursor Settings`. Fill it with the following content: ```json { "mcpServers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ] } } } ``` Then you should see the Openfort MCP server listed on your `Tools & Integrations` tab without the need to restart. The authentication will trigger automatically. For integration with [Windsurf](https://docs.windsurf.com/windsurf/cascade/mcp#custom-mcp-server-sse), replace the contents of the `~/.codeium/windsurf/mcp_config.json` file with the following. It can be located at: `Windsurf Settings > Cascade > Plugins (MCP Servers) > View Raw Config` :::code-group ```json [MacOS/Linux] { "mcpServers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ], "disabled": false } } } ``` ```json [Windows] { "mcpServers": { "openfort-mcp": { "command": "cmd", "args": [ "/c", "npx", "mcp-remote", "https://mcp.openfort.io/sse" ], "disabled": false } } } ``` ::: To integrate an MCP Server into [VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for use with GitHub Copilot, you should edit the `.vscode/mcp.json` file or run the `MCP: Open User Configuration` command which opens the file to add the following content: ```json { "servers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ] } } } ``` To add our MCP Server to [Claude Desktop](https://modelcontextprotocol.io/quickstart/user), click on `Edit Config` in the `Developer` tab under `Settings` to automatically create a file at: * macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` * Windows: `%APPDATA%\Claude\claude_desktop_config.json` Once created, fill it with the following content: ```json { "mcpServers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ] } } } ``` You will need to restart Claude Desktop after modifying the configuration file. Also, if you freshly installed Node.js you may need to reboot your computer too. ### 2. Add rules for the LLMs LLMs tend to forget about the availability of tools and can make some pathological mistakes. Therefore, it's a good idea to include rules to remind them about this. Here's how to set them up. Add these rules to the configuration at `Rules & Memories > User Rules > Add Rule`. For project-specific rules, you can add them at `.cursor/rules/openfort.md` Add these rules to the root folder of your project at `.windsurf/rules/openfort.md`. For global rules, you can add them at: `Windsurf Settings > Cascade > Customizations > Cascade Rules > + Global` Copy these rules and add them to any AI assistant you use. ### 3. Create a new project Now create a new project in your editor and type up a prompt to the LLM to scaffold it for you. When needed, the LLM will automatically call the [available tools](https://www.openfort.io/docs/configuration/ai-tooling/mcp-server/tools) on Openfort's MCP Server, enhancing your developer experience. Here's an example prompt you can try: ### 4. Debug Common Issues Here are some common issues you might encounter and how to resolve them. :::details[General Errors] After the agent finishes creating a project, it may still throw errors, even with extended context. This is especially common for complex prompts or large applications. > Don’t expect the AI to flawlessly generate entire applications in a single prompt without any issues. To resolve these errors, fix them manually or ask the AI for help. Iteration is normal, review the output, make corrections, and continue prompting as needed. ::: *** :::details[Loop When Creating a Policy] Occasionally, the AI agent may get stuck in a loop while creating a policy. The policy is successfully created, but the agent repeatedly attempts to update it with the same values. > The cause is unknown, and the effect is harmless. To fix this, simply cancel the generation and prompt the agent to continue with the next step. ::: *** :::details[npm Error: `Missing script: "dev"`] If the AI agent fails to start the project using `npm run dev`, it’s often because it created the project in a subfolder and didn’t change into that directory before running the command. > Manually navigate to the subfolder and run the project again. ::: *** :::details[No Permission to Edit the `.env` File] When the AI agent fails to edit or create a `.env` file with your project keys, it’s usually due to insufficient file permissions. > In Cursor, add a `.cursorignore` file with `!.env` to explicitly allow the AI to edit the `.env` file. > > For other editors or environments, follow an equivalent approach to ensure the file is not ignored. ::: ### 5. Discover all the capabilities For more information on the available tools and how to use them, check out the [MCP Server documentation](https://www.openfort.io/docs/configuration/ai-tooling/mcp-server/tools). import { OpenfortKitButton } from "@/components/Openfortkit/OpenfortKitButton"; import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { CogIcon } from "lucide-react"; ## Embedded Wallet {/* */} ### Choose Your Platform ### What is an Embedded Wallet? Embedded wallets provide a seamless blockchain experience by abstracting away the complexity of wallet management. Users can interact with your application without needing to understand private keys, seed phrases, or blockchain concepts. #### Key Features * **Seamless Authentication**: Users sign in with familiar methods like email, social logins, or third-party auth providers * **No Seed Phrases**: Secure key management without burdening users with recovery phrases * **Cross-Platform**: Consistent wallet experience across web, mobile, and game engines * **Smart Wallet Support**: Built-in support for account abstraction and gasless transactions * **Recovery Options**: Multiple recovery methods to ensure users never lose access ## User Session (JWT/Authorization) ### Getting a user's access token from a request When your app frontend sends a request to your server, you should include the current user's access token in the Authorization header of the request. This allows your backend to securely identify the requesting user and gate API routes based on their authentication status, their user ID, and more. :::info This guide assumes you have already configured your frontend to including users' access tokens in requests to your server. If this is not the case, please begin with the [frontend authorization guide](/docs/products/embedded-wallet/javascript/auth/user-sessions). ::: When your server receives a request, the location of the user's access token depends on whether your app uses local storage (the default) or cookies to manage user sessions: * If using **local storage** to store a user's session, the access token will be passed in the Authorization header of the request. * If using **cookies** to store a user's session, the access token will be passed in the openfort-token cookie on the request. For example, in [NextJS](https://nextjs.org/), you might extract the auth token from a `NextApiRequest` as follows: :::code-group ```ts [Using Local Storage] const accessToken = req.headers.authorization.replace('Bearer ', ''); ``` ```ts [Using Cookies] const accessToken = req.cookies['openfort-token']; ``` ::: ### Verifying the user's access token Once you've obtained the user's access token from a request, you should verify the token against Openfort's verification key for your app to confirm that the token was issued by Openfort and the user referenced by the player Id in the token is truly authenticated. The access token is a standard ES256 JWT and the verification key is a standard Ed25519 public key. You can verify the access token against the public key using the official supported libraries library or using a third-party library for managing tokens. #### When using Openfort auth :::code-group ```ts [Node.js] import Openfort from "@openfort/openfort-node"; const openfort = new Openfort(process.env.OPENFORT_SK); const authSession = openfort.iam.verifyAuthToken("USER_AUTH_TOKEN"); ``` ```csharp [.NET] using Openfort.SDK.Model; class Program { static async Task Main() { var client = new Openfort.SDK.OpenfortClient("sk_test_..."); var authSession = await client.Iam.VerifyAuthToken("USER_AUTH_TOKEN"); } } ``` ::: #### When using a third-party auth When using a third-party auth provider, you can either verify the token using the provider's SDK or use Openfort's SDK to verify the token. ```ts [Node.js] import Openfort from "@openfort/openfort-node"; const openfort = new Openfort(process.env.OPENFORT_SK); const authSession = openfort.iam.verifyOAuthToken({ provider: 'firebase', // one of "google" | "twitter" | "facebook" | "discord" | "epic_games" | "accelbyte" | "firebase" | "lootlocker" | "playfab" | "supabase" | "custom" | "oidc"; token: "USER_AUTH_TOKEN", tokenType: 'idToken', // either "idToken" | "customToken" }); ``` import { HoverCardLink } from "@/components/HoverCard/HoverCardLink"; import { HoverCardLayout } from "@/components/HoverCard/HoverCardLayout"; import { Database, DatabaseZap, ShieldCheck, Wallet2 } from "lucide-react"; ## Using Openfort from your server Openfort's client-side SDKs, like [`@openfort/openfort-js`](https://www.npmjs.com/package/@openfort/openfort-js), allow you to securely authenticate your users, connect their wallets, and create embedded wallets for them in your application frontend. In addition to these SDKs, you can also use **Openfort from your server** to secure and manage your application. See the guides below for some common flows. ### Get started ### SDKs and API reference ## Provider migration \[Migrate users from one Auth provider to another using Openfort's migration tool] This tool allows you to link users from one authentication provider to another, ensuring a smooth transition without losing any user data. In this guide we will be creating a migration from **OIDC provider** with google oauth flow to **openfort Google provider**. This is useful if you want to migrate users from a custom OIDC provider to Openfort's Google provider. ### Creating a migration 1. Enable both your source and destination providers in the Openfort dashboard. You can do this in the [Providers](https://dashboard.openfort.io/players/auth/providers) section of your Openfort dashboard. 2. Start by going to the [Migration page](https://dashboard.openfort.io/players/auth/migrations) in your Openfort dashboard and create a new one. 3. Select the source destination, and add the matching criteria for the users you want to migrate.
migration-1
4. Do the same for the destination provider. 5. Click on **Create migration**. 6. Start the migration by clicking on the **Start migration** button, next to "Ready to start".
migration-2
7. That is it! Now when a user logs in with **openfort Google provider**, they will be automatically linked to their OIDC account. When all users are migrated, you can mark the migration as complete by clicking on the **Mark as complete** button. ## Pregenerating an embedded wallet You can pregenerate non-custodial wallet associated with a given account, like an email address or social login, without requiring the user to login. You can even send assets to the wallet before the user logs in to your app for the first time. Once the user associated with the account logs in, they will be able to access the pregenerated wallet and any assets sent to them. To pregenerate wallets, use one of the supported backend SDKs: A user can easily claim their pregenerated wallet simply by logging into your app with one of its linked accounts. :::code-group ```ts [Node.js] // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.openfort.io/developers/configuration/api-keys const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const notifications = await openfort.iam.createAuthPlayer( { thirdPartyUserId: "user id", thirdPartyProvider: 'provider name', // Id of the provider of the user preGenerateEmbeddedAccount: true chainId: 4337, metadata: { name: "Jaume" } }, { shieldAuthProvider: '', // ShieldAuthProvider.OPENFORT or ShieldAuthProvider.CUSTOM apiKey: '', // Shield publishable key apiSecret: '', // Shield secret key encryptionPart: '' // Shield encryption share }) ``` ```csharp [.NET] using Openfort.SDK; using Openfort.SDK.Model; const openfort = new OpenfortClient(YOUR_SECRET_KEY); const notifications = await openfort.Iam.CreateAuthPlayer( new CreateAuthPlayerRequest( ThirdPartyUserId = "user id", ThirdPartyProvider = 'provider name', // Id of the provider of the user PreGenerateEmbeddedAccount = true, ChainId = 4337, ), new PreGenerateEmbeddedAccountsConfiguration( ShieldAuthProvider = ShieldAuthProvider.OPENFORT, // ShieldAuthProvider.OPENFORT or ShieldAuthProvider.CUSTOM ApiKey = '', // Shield publishable ApiSecret = '', // Shield secret key EncryptionPart = '' // Shield encryption share ) ); ``` ::: In the body of the request: * `thirdPartyUserId`: the user's account id. * `thirdPartyProvider`: the provider used: custom, supabase, oidc, etc. * `preGenerateEmbeddedAccount`: boolean with the value **true**. A successful response will include the new user object along with their user's ID (`playerID`), as shown in the Response tab above. ```json [Response] { "id": "pla_ff54b031-a878-4ca2-9cf5-ae190f921e9b", "object": "player", "createdAt": 1691658234, "linkedAccounts": [ { "provider": "email", "email": "jaume@openfort.io", "disabled": false, "updatedAt": 1691658234 } ] } ``` ## Server Environment Setup This guide will help you set up your server environment to work with Openfort, including getting your API keys and configuring the necessary SDKs for backend operations. ### Get your API keys To start, go to the [Openfort Dashboard](https://dashboard.openfort.io) and select your desired project from the dropdown at the top. Then, navigate to the Developers page > API Keys tab for your app. Then grab your secret key. :::warning Your app secret is a sensitive value that gives you permission to manage Openfort from your server. Do not expose it outside of your backend server. ::: Once you've retrieved your API keys, you can begin interacting with Openfort's API in one of two ways: using the server-side SDKs, or querying Openfort's REST API directly. ### Server SDKs These libraries include helpful utilities around verifying access tokens issued by Openfort and interacting with Openfort's API to query and import users, create wallets, verify webhooks, and more. You can visit the [SDK reference documentation](https://openfort.apidocumentation.com) to learn more about the available SDKs and how to use them in your server-side application. :::code-group ```ts [Node.js] const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); ``` ```csharp [.NET] using Openfort.SDK; using Openfort.SDK.Model; const openfort = new OpenfortClient(YOUR_SECRET_KEY); ``` ::: ### Using the REST API You can visit the [API reference documentation](https://openfort.apidocumentation.com) to learn more. For example, in a JavaScript fetch request, your headers should look like: ```js headers: { 'Authorization': `Bearer sk_test_...`, } ``` import { MultiOptionDisplay } from '@/components/MCP/MultiOptionDisplay'; import { CopyPrompt } from '@/components/MCP/CopyPrompt'; import Prompt1 from '@/components/MCP/prompt1.mdx?raw'; import Rules from '@/components/MCP/rules.mdx?raw'; ## Prompts & Examples Once everything is set up, you can start using your AI assistant as usual. It will now have access to the tools provided by Openfort's MCP Server and will call them when needed. * It can create projects without the need for you to set them up in the dashboard. * It can automatically retrieve keys from your project and store them safely. * It can set up policies, contracts, and create users and accounts. * It can fetch real-time data from your project. * It can scaffold your project faster than ever. * It can get context from the latest version of Openfort's documentation. If you need further inspiration, here are some considerations and use cases. ### Rules LLMs tend to forget about the availability of tools and can make some pathological mistakes. Therefore, it's a good idea to include rules to remind them about this. Here's how to set them up. Add these rules to the configuration at `Rules & Memories > User Rules > Add Rule`. For project-specific rules, you can add them at `.cursor/rules/openfort.md` Add these rules to the root folder of your project at `.windsurf/rules/openfort.md`. For global rules, you can add them at: `Windsurf Settings > Cascade > Customizations > Cascade Rules > + Global` Copy these rules and add them to any AI assistant you use. ### Prompts Here are some example prompts to guide your AI assistant in creating projects with Openfort’s features. Copy and paste them directly into the chat to try them out. ### Common Issues :::details[General Errors] After the agent finishes creating a project, it may still throw errors, even with extended context. This is especially common for complex prompts or large applications. > Don’t expect the AI to flawlessly generate entire applications in a single prompt without any issues. To resolve these errors, fix them manually or ask the AI for help. Iteration is normal, review the output, make corrections, and continue prompting as needed. ::: *** :::details[Loop When Creating a Policy] Occasionally, the AI agent may get stuck in a loop while creating a policy. The policy is successfully created, but the agent repeatedly attempts to update it with the same values. > The cause is unknown, and the effect is harmless. To fix this, simply cancel the generation and prompt the agent to continue with the next step. ::: *** :::details[npm Error: `Missing script: "dev"`] If the AI agent fails to start the project using `npm run dev`, it’s often because it created the project in a subfolder and didn’t change into that directory before running the command. > Manually navigate to the subfolder and run the project again. ::: *** :::details[No Permission to Edit the `.env` File] When the AI agent fails to edit or create a `.env` file with your project keys, it’s usually due to insufficient file permissions. > In Cursor, add a `.cursorignore` file with `!.env` to explicitly allow the AI to edit the `.env` file. > > For other editors or environments, follow an equivalent approach to ensure the file is not ignored. ::: import { MultiOptionDisplay } from "@/components/MCP/MultiOptionDisplay"; import { AddToCursor } from "@/components/MCP/AddToCursor"; ## MCP Server Openfort’s MCP Server is a plug-and-play solution that enhances AI assistants by enabling them to create projects, manage configurations, and query data automatically when building applications on Openfort's infrastructure. ### What's an MCP Server? AI agents in AI-powered IDEs excel at reasoning and generating high-quality answers. However, they usually stall when it comes to building real-world applications. Their limitations arise from being isolated from real-time data and actionable tools. **Model Context Protocol** (MCP) provides a universal and open [standard](https://modelcontextprotocol.io/introduction) for connecting AI systems to live data sources, callable functions, and richer context. In a client-server architecture, the AI agent (MCP Client) in your code editor connects to an MCP Server. This connection enables real-time context awareness and seamless integration with external tools (set up by the server provider), allowing the language model to invoke them as needed. ### Quickstart Openfort's MCP Server can be easily plugged into your code editor. The only requirement is to have a working Openfort account; you can create one [here](https://dashboard.openfort.io/). You will also need [Node.js](https://nodejs.org/en) installed on your computer. To verify you have Node, open the command line on your computer and run: ```bash node --version ``` If Node.js is not installed, you can follow the [installation guide](https://nodejs.org/en/download). To integrate our MCP Server with [Cursor](https://docs.cursor.com/context/mcp) you can either: ##### One-click installation ##### Edit the `~/.cursor/mcp.json` You can look it up on your system or find it under the `Tools & Integrations` tab in your `Cursor Settings`. Fill it with the following content: ```json { "mcpServers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ] } } } ``` Then you should see the Openfort MCP server listed on your `Tools & Integrations` tab without the need to restart. The authentication will trigger automatically. For integration with [Windsurf](https://docs.windsurf.com/windsurf/cascade/mcp#custom-mcp-server-sse), replace the contents of the `~/.codeium/windsurf/mcp_config.json` file with the following. It can be located at: `Windsurf Settings > Cascade > Plugins (MCP Servers) > View Raw Config` :::code-group ```json [MacOS/Linux] { "mcpServers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ], "disabled": false } } } ``` ```json [Windows] { "mcpServers": { "openfort-mcp": { "command": "cmd", "args": [ "/c", "npx", "mcp-remote", "https://mcp.openfort.io/sse" ], "disabled": false } } } ``` ::: To integrate an MCP Server into [VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for use with GitHub Copilot, you should edit the `.vscode/mcp.json` file or run the `MCP: Open User Configuration` command which opens the file to add the following content: ```json { "servers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ] } } } ``` To add our MCP Server to [Claude Desktop](https://modelcontextprotocol.io/quickstart/user), click on `Edit Config` in the `Developer` tab under `Settings` to automatically create a file at: * macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` * Windows: `%APPDATA%\Claude\claude_desktop_config.json` Once created, fill it with the following content: ```json { "mcpServers": { "openfort-mcp": { "command": "npx", "args": [ "mcp-remote", "https://mcp.openfort.io/sse" ] } } } ``` You will need to restart Claude Desktop after modifying the configuration file. Also, if you freshly installed Node.js you may need to reboot your computer too. ### Important note Once it's been set up, you will be asked to authenticate with your Openfort account. :::warning By authenticating, you grant the LLM permission to act on your behalf within Openfort's infrastructure. By default, you will be prompted to approve or deny each tool execution. ::: From here, the LLM will automatically invoke the [available tools](https://www.openfort.io/docs/configuration/ai-tooling/mcp-server/tools) from Openfort's MCP Server on demand, helping your developer experience. Tool updates will sync automatically. ## Available Tools Our MCP Server provides three main tool categories to enhance your coding experience: 1. Context – Fetch real‑time documentation and code snippets to scaffold your project. 2. Management – Create and initialize new projects effortlessly. 3. Project – Control all the details within a specific project. ### Context **Documentation** * `search-documentation`: Searches a given phrase in the latest live version of [Openfort's documentation](https://www.openfort.io/docs). **Initialization** * `create-openfortkit-app`: Provides a guide to the LLM on how to create a React + Vite app using [Openfort Kit](https://www.openfort.io/docs/products/embedded-wallet/react/kit). ### Management **Projects** * `list-projects`: List all your projects. * `get-project`: Get details of a specific project. * `create-project`: Create a new project. * `select-project`: Sets a project as active. Subsequent project-specific actions will apply to the selected project. * `show-selected-project`: Show currently selected project by the Agent. * `deselect-project`: Deselect the project. **Keys** * `get-publishable-keys`: Get public project keys. * `get-secret-keys`: Get secret project keys. * `get-shield-publishable-key`: Get shield public key. * `get-shield-secret-key`: Get shield private key. * `create-publishable-key`: Create new public key. * `create-secret-key`: Create new secret key. * `create-shield-keys`: Create public, private, and encryption shield keys. :::danger Do not expose the encryption share returned by the `create-shield-keys` to the client side of your application. The MCP explicitly states to the LLM that the encryption share should never be exposed but it's not deterministic. ::: ### Project **Policies** * `list-policies`: Retrieves a list of all policies associated with the selected project. * `create-policy`: Creates a new policy for your project. * `get-policy`: Fetches details of a specific policy. * `update-policy`: Updates an existing policy with new information. * `delete-policy`: Removes a specified policy from your project. * `disable-policy`: Disables a policy without deleting it. * `enable-policy`: Re-enables a previously disabled policy. * `list-policy-rules`: Lists all rules associated with a specific policy. * `create-policy-rule`: Adds a new rule to a specified policy. * `update-policy-rule`: Modifies an existing rule within a policy. * `delete-policy-rule`: Deletes a specific rule from a policy. **Contracts** * `create-contract`: Creates a new contract for the selected project. * `get-contract`: Retrieves details of a specific contract. * `list-contracts`: Lists all contracts associated with the selected project. * `update-contract`: Updates the details of an existing contract. * `delete-contract`: Removes a specified contract from your project. **Users** * `create-user`: Creates a new user in the project. * `get-user`: Get detailed information about a specific user in the project. * `list-users`: Get the users in the project. * `update-user`: Update an existing user's information. * `delete-user`: Delete a user from the project. **Accounts** * `create-account`: Creates a new blockchain account for the provided player. If no player is provided, a new one will be created. * `get-account`: Get detailed information about a specific account in the project. * `list-accounts`: Get the accounts in the project with optional filtering. **Transactions** * `list-transactions`: Lists all transactions associated with the selected project. * `get-transaction`: Retrieves details of a specific transaction. * `simulate-transaction`: Simulates a transaction to check validity and expected fee. import { ProviderRequest } from "@/components/Rapidfire/ProviderRequest.tsx"; ## eth\_accounts ### Request ```ts type Request = { method: "eth_accounts"; }; ``` ### Response Array of connected Account addresses. ```ts type Response = `0x${string}`[]; ``` ### Example `const accounts = ${x};`} /> :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: ```ts import RapidfireID from "@rapidfire/id"; const rapidfire = new RapidfireID(); const provider = rapidfire.getEthereumProvider(); const accounts = await provider.request({ method: "eth_accounts" }); // [!code focus] ``` import { ProviderRequest } from "@/components/Rapidfire/ProviderRequest.tsx"; ## eth\_requestAccounts Requests access to Account addresses. :::info The `eth_requestAccounts` methods effectively "connects" an Application to a Wallet. ::: ### Request ```ts type Request = { method: "eth_requestAccounts"; }; ``` ### Response Array of connected Account addresses. ```ts type Response = `0x${string}`[]; ``` ### Example `const accounts = ${x};`} /> :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: ```ts import RapidfireID from "@rapidfire/id"; const rapidfire = new RapidfireID(); const provider = rapidfire.getEthereumProvider(); const accounts = await provider.request({ method: "eth_requestAccounts" });// [!code focus] ``` import { ProviderRequest } from '@/components/Rapidfire/ProviderRequest' import { encodeFunctionData, parseAbi, parseEther } from 'viem' ## eth\_sendTransaction Instructs the Wallet to broadcast a transaction to the network. :::warning This method is deprecated and exists for compatibility. Please [use `wallet_sendCalls` instead](/docs/products/cross-app-wallet/rpc/wallet_sendCalls). ::: ### Request ```ts type Request = { method: 'eth_sendTransaction', params: [{ /** Target chain ID. Defaults to the connected chain. */ chainId?: `0x${string}`, /** Calldata to send with the transaction. */ data?: `0x${string}`, /** Address of the sender. */ from: `0x${string}`, /** Address of the recipient. */ to: `0x${string}`, /** Value to transfer. Defaults to 0. */ value?: `0x${string}`, }] } ``` ### Response Transaction hash. ```ts type Response = `0x${string}` ``` ### Example :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: #### Mint ERC20 Tokens The example below demonstrates minting 100 EXP. { const exp = { address: '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c', abi: parseAbi([ 'function mint(address, uint256)', ]), } const [account] = await provider.request({ method: 'eth_accounts', }) const hash = await provider.request({ method: 'eth_sendTransaction', params: [{ from: account, // @ts-expect-error to: exp.address, data: encodeFunctionData({ abi: exp.abi, functionName: 'mint', args: [account, parseEther('100')], }), }], }) return hash }} transformResultCode={(x) => `const txHash = \n ${x};`} /> ```ts import RapidfireID from '@rapidfire/id' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() import { encodeFunctionData, parseAbi, parseEther } from 'viem' const [account] = await provider.request({ method: 'eth_accounts', }) const hash = await provider.request({ method: 'eth_sendTransaction', params: [{ from: account, to: '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c', data: encodeFunctionData({ abi: parseAbi([ 'function mint(address, uint256)', ]), functionName: 'mint', args: [account, parseEther('100')], }), }], }) ``` #### Send ETH ```ts import RapidfireID from '@rapidfire/id' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() const hash = await provider.request({// [!code focus] method: 'eth_sendTransaction',// [!code focus] params: [{// [!code focus] from: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',// [!code focus] to: '0xcafebabecafebabecafebabecafebabecafebabe',// [!code focus] value: '0x1',// [!code focus] }],// [!code focus] })// [!code focus] ``` import { ProviderRequest } from "@/components/Rapidfire/ProviderRequest"; ## eth\_signTypedData\_v4 Signs [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data. ### Request ```ts type Request = { method: "eth_signTypedData_v4"; params: [ /** Address of the signer. */ address: `0x${string}`, /** Serialized typed data to sign. */ data: string ]; }; ``` ### Response Signature. ```ts type Response = `0x${string}`; ``` ### Example {/* { const hash = await provider.request({ method: 'eth_signTypedData_v4', params: [ "0x3b7252d007059ffc82d16d022da3cbf9992d2f70", { "types": { "EIP712Domain": [ { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, { "name": "chainId", "type": "uint256" }, { "name": "verifyingContract", "type": "address" } ], "Person": [ { "name": "name", "type": "string" }, { "name": "wallet", "type": "address" } ] }, "primaryType": "Person", "domain": { "name": "Example App", "version": "1", "chainId": 1, "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" }, "message": { "name": "Alice", "wallet": "0x3b7252d007059ffc82d16d022da3cbf9992d2f70" } } ], }) return hash }} transformResultCode={(x) => `const txHash = ${x};`} /> */} :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: ```ts import RapidfireID from "@rapidfire/id"; const rapidfire = new RapidfireID(); const provider = rapidfire.getEthereumProvider(); const signature = await provider.request({// [!code focus] method: "eth_signTypedData_v4",// [!code focus] params: ["0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "..."],// [!code focus] });// [!code focus] ``` ## RPC Methods Overview ### Methods | Method | Description | Standard | | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | [`eth_accounts`](/docs/products/cross-app-wallet/rpc/eth_accounts) | Returns an array of all **connected** Account addresses. | [EIP-1474](https://eips.ethereum.org/EIPS/eip-1474) | | [`eth_requestAccounts`](/docs/products/cross-app-wallet/rpc/eth_requestAccounts) | Requests access to Account addresses. | | | [`eth_sendTransaction`](/docs/products/cross-app-wallet/rpc/eth_sendTransaction) | Broadcasts a transaction to the network. | [EIP-1474](https://eips.ethereum.org/EIPS/eip-1474) | | [`eth_signTypedData_v4`](/docs/products/cross-app-wallet/rpc/eth_signTypedData_v4) | Signs [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data. | [EIP-712](https://eips.ethereum.org/EIPS/eip-712) | | [`personal_sign`](/docs/products/cross-app-wallet/rpc/personal_sign) | Signs an [EIP-191](https://eips.ethereum.org/EIPS/eip-191) personal message. | [EIP-191](https://eips.ethereum.org/EIPS/eip-191) | | [`wallet_getCapabilities`](/docs/products/cross-app-wallet/rpc/wallet_getCapabilities) | Gets supported capabilities of global wallet. | [ERC-5792](https://eips.ethereum.org/EIPS/eip-5792) | | [`wallet_getCallsStatus`](/docs/products/cross-app-wallet/rpc/wallet_getCallsStatus) | Gets the status of a call bundle. | [ERC-5792](https://eips.ethereum.org/EIPS/eip-5792) | | [`wallet_sendCalls`](/docs/products/cross-app-wallet/rpc/wallet_sendCalls) | Broadcast a bundle of calls to the network. | [ERC-5792](https://eips.ethereum.org/EIPS/eip-5792) | | [`wallet_grantPermissions`](/docs/products/cross-app-wallet/rpc/wallet_grantPermissions) | Grants permissions for an Application to perform actions on behalf of the account. | [ERC-7715](https://github.com/ethereum/ERCs/blob/23fa3603c6181849f61d219f75e8a16d6624ac60/ERCS/erc-7715.md) | | [`wallet_revokePermissions`](/docs/products/cross-app-wallet/rpc/wallet_revokePermissions) | Revokes a permission. | [ERC-7715](https://github.com/ethereum/ERCs/blob/23fa3603c6181849f61d219f75e8a16d6624ac60/ERCS/erc-7715.md) | import { ProviderRequest } from '@/components/Rapidfire/ProviderRequest'; ## personal\_sign Signs an [EIP-191](https://eips.ethereum.org/EIPS/eip-191) personal message. ### Request ```ts type Request = { method: 'personal_sign', params: [ /** Message to sign. */ message: string, /** Address of the signer. */ address: `0x${string}`, ], } ``` ### Response Signature. ```ts type Response = `0x${string}` ``` ### Example :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: { const [account] = await provider.request({ method: 'eth_accounts', }) const hash = await provider.request({ method: 'personal_sign', params: [ '0x68656c6c6f20776f726c64', // "hello world" in hex account, ], }) return hash }} /> ```ts import RapidfireID from '@rapidfire/id' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() const [account] = await provider.request({ method: 'eth_accounts', }) const hash = await provider.request({ // [!code focus] method: 'personal_sign', // [!code focus] params: [ // [!code focus] '0x68656c6c6f20776f726c64', // "hello world" in hex [!code focus] account, // [!code focus] ], // [!code focus] })// [!code focus] ``` import { ProviderRequest } from '@/components/Rapidfire/ProviderRequest.tsx'; import { encodeFunctionData, parseAbi, parseEther } from 'viem'; ## wallet\_getCallsStatus Gets the status of a call bundle sent via [`wallet_sendCalls`](/docs/products/cross-app-wallet/rpc/wallet_sendCalls). ### Request ```ts type Request = { method: 'wallet_getCallsStatus', params: [ /** ID of the call bundle. */ id: `0x${string}` ] } ``` ### Response ```ts type Response = { /** Chain ID the calls were submitted to. */ chainId: `0x${string}`, /** ID of the call bundle. */ id: `0x${string}`, /** Transaction receipts of the calls. */ receipts: { /** Block hash. */ blockHash: `0x${string}`, /** Block number. */ blockNumber: `0x${string}`, /** Gas used by the transaction. */ gasUsed: `0x${string}`, /** Logs of the call. */ logs: { /** Address of the contract that emitted the log. */ address: `0x${string}`, /** Data of the log. */ data: `0x${string}`, /** Topics of the log. */ topics: `0x${string}`[], }[], /** Status. */ status: `0x${string}`, /** Transaction hash. */ transactionHash: `0x${string}`, }[], /** Status code. See "Status Codes" below. */ status: number } ``` ### Example :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: { const exp = { address: '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c', abi: parseAbi([ 'function mint(address, uint256)', ]), } const [account] = await provider.request({ method: 'eth_accounts', }) const mintCall = { from: account, // @ts-expect-error to: exp.address, data: encodeFunctionData({ abi: exp.abi, functionName: 'mint', args: [account, parseEther('100')], }), } const hash = await provider.request({ method: 'wallet_sendCalls', params: [{ calls: [mintCall,mintCall] }], }) const status = await provider.request({ method: 'wallet_getCallsStatus', params: [hash], }) return status }} transformResultCode={(x) => `const status = ${x};`} /> ```ts import RapidfireID from '@rapidfire/id' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() const status = await provider.request({ method: 'wallet_getCallsStatus', params: ['0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef'], }) ``` import { ProviderRequest } from '@/components/Rapidfire/ProviderRequest.tsx'; ## wallet\_getCapabilities Gets supported capabilities of the global wallet. ### Request ```ts type Request = { method: 'wallet_getCapabilities', } ``` ### Response ```ts type Response = { capabilities: { atomicBatch: { supported: true } createAccount: { supported: true }, permissions: { supported: true } } } ``` ### Example :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: `const capabilities = ${x};`} /> ```ts import RapidfireID from '@rapidfire/id' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() const capabilities = await provider.request({ method: 'wallet_getCapabilities', }) ``` import { ProviderRequest } from '@/components/Rapidfire/ProviderRequest.tsx'; ## wallet\_grantPermissions Grants permissions for an Application to perform actions on behalf of the account. Based on [ERC-7715](https://github.com/ethereum/ERCs/blob/23fa3603c6181849f61d219f75e8a16d6624ac60/ERCS/erc-7715.md). ### Request ```ts type Request = { method: 'wallet_grantPermissions', params: [{ /** * Address of the account to grant permissions on. * Defaults to the current account. */ address?: `0x${string}` /** Chain ID to grant permissions on. */ chainId?: `0x${string}` /** Expiry of the permissions. */ expiry: number /** Key to grant permissions to. Defaults to a wallet-managed key. */ key?: { /** * Public key. * Accepts an address for `address` & `secp256k1` types. */ publicKey?: `0x${string}`, /** Key type. */ type?: 'address' | 'p256' | 'secp256k1' | 'webauthn-p256', } /** Permissions to grant. */ permissions: { /** Call permissions. */ calls: { /** Function signature or 4-byte signature. */ signature?: string /** Authorized target address. */ to?: `0x${string}` }[], }, }] } ``` ### Response ```ts type Response = { address: `0x${string}`, chainId: `0x${string}`, expiry: number, id: `0x${string}`, key: { publicKey: `0x${string}`, type: 'address' | 'p256' | 'secp256k1' | 'webauthn-p256', }, permissions: { calls: { signature?: string, to?: `0x${string}`, }[], }, } ``` ### Example :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: The example below demonstrates granting permissions for an Application to perform `transfer` calls on the EXP ERC20 contract, with a spending limit of up to `50 EXP` per day. :::info Once permissions have been granted, they will be automatically applied to any calls made by the Application via [`wallet_sendCalls`](/docs/products/cross-app-wallet/rpc/wallet_sendCalls) or [`eth_sendTransaction`](/docs/products/cross-app-wallet/rpc/eth_sendTransaction). ::: {/* { const token = '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c' const permissions = await provider.request({ method: 'wallet_grantPermissions', params: [{ expiry: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 1 week permissions: [{ calls: [{ signature: 'transfer(address,uint256)', to: token }], }], }], }) return permissions }} transformResultCode={(x) => `const status = ${x};`} /> */} ```ts import RapidfireID from '@rapidfire/id' import { parseEther, toHex } from 'viem' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() const token = '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c' const permissions = await provider.request({ method: 'wallet_grantPermissions', params: [{ expiry: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 1 week permissions: { calls: [{ signature: 'transfer(address,uint256)', to: token }], }, }], }) ``` ## wallet\_revokePermissions Revokes a permission. ### Request ```ts type Request = { method: 'wallet_revokePermissions', params: [{ /** Address of the account to revoke a permission on. */ address?: `0x${string}` /** ID of the permission to revoke. */ id: `0x${string}` }] } ``` ### Example :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: ```ts import RapidfireID from '@rapidfire/id' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() await provider.request({ method: 'experimental_revokePermissions', params: [{ id: '0x...' }], }) ``` import { ProviderRequest } from '@/components/Rapidfire/ProviderRequest.tsx'; import { encodeFunctionData, parseAbi, parseEther } from 'viem'; ## wallet\_sendCalls Requests for the Wallet to broadcast a bundle of calls to the network. ### Request ```ts type Request = { method: 'wallet_sendCalls', params: [{ /** Calls to prepare. */ calls: { /** Recipient. */ to: `0x${string}`; /** Calldata. */ data?: `0x${string}`; /** Value to transfer. */ value?: `0x${string}`; }[]; /** * Chain ID to send the calls to. * If not provided, the current chain will be used. */ chainId?: `0x${string}`; /** * Address of the account to send the calls from. * If not provided, the Account will be filled by the Wallet. */ from?: `0x${string}`; /** Capabilities. */ capabilities?: { permissions?: { /** ID of the permission to use. */ id: `0x${string}`; }; }; }] } ``` ### Response ```ts type Response = { /** ID of the bundle. */ id: string; } ``` ### Example :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: #### Mint ERC20 Tokens The example below demonstrates minting 100 EXP twice. { const exp = { address: '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c', abi: parseAbi([ 'function mint(address, uint256)', ]), } const [account] = await provider.request({ method: 'eth_accounts', }) const mintCall = { from: account, // @ts-expect-error to: exp.address, data: encodeFunctionData({ abi: exp.abi, functionName: 'mint', args: [account, parseEther('100')], }), } const hash = await provider.request({ method: 'wallet_sendCalls', params: [{ calls: [mintCall,mintCall] }], }) return hash }} transformResultCode={(x) => `const id = ${x};`} /> ```ts import RapidfireID from '@rapidfire/id' import { encodeFunctionData, parseAbi, parseEther } from 'viem' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() const [account] = await provider.request({ method: 'eth_accounts', }) const hash = await provider.request({ method: 'wallet_sendCalls', params: [{ calls: [{ to: '0x706aa5c8e5cc2c67da21ee220718f6f6b154e75c', data: encodeFunctionData({ abi: parseAbi([ 'function mint(address, uint256)', ]), functionName: 'mint', args: [account, parseEther('100')], }), }], }], }) ``` #### Send a Transaction ```ts import RapidfireID from '@rapidfire/id' const rapidfire = new RapidfireID() const provider = rapidfire.getEthereumProvider() const response = await provider.request({ method: 'wallet_sendCalls', params: [{ calls: [{ to: '0xcafebabecafebabecafebabecafebabecafebabe', value: '0x12345678', }], }] }) ``` ## FAQs #### Can Openfort support my ecosystem's login system? Yes, Openfort's architecture is built to allow thousands of new gaming ecosystems flourish. You ecosystem can be added as an OAuth provider allowing your community to be recognized. #### What if I can't find my authentication provider in the documentation? No problem, let us know which provider you want to use and we'll get right into it. #### Can players use their existing wallets alongside Openfort Auth? Users can link existing wallets to their account. This allows you to store and share these wallet addresses with games via the SDK for read-only use. #### What level of support and Service Level Agreements (SLAs) are provided? At Openfort, we understand the significance of maintaining a high standard of service. Our Service Level Agreements (SLAs) reflect our commitment to providing a reliable, efficient, and safe environment for your operations. ### Security and Contingency Planning #### If Openfort were to shut down with a one-month notice, would there be scope to change the signer on the Smart Contract Wallet and use it in connection with a different provider that manages the private keys differently? **TLDR:** With enough time, transitioning is fairly simple. You’d need to invoke the `transferOwnership` function for users to accept the new signer. **Detailed Answer:** Yes, if Openfort shuts down with a one-month notice, it's possible to change the signer. Since Openfort wallets are non-custodial, users have control over their private keys. The key migration process would involve using the "recovery share" and "device share" from Shamir's Secret Sharing (SSS) to reconstruct the private key, allowing users to accept a new signer through the `transferOwnership` function. The new provider would need to support compatible key management systems. #### If Openfort were shut down with zero notice, would there be any scope to do a migration? Would that rely on a self-hosted Shield for the recovery share and the device share being intact? **TLDR:** If there is no self-hosted option, users should rely on on-chain social recovery. **Detailed Answer:** Migration is still possible, but it depends on the self-hosted Shield for the recovery share and the availability of the device share. If a self-hosted Shield is not in place, users can utilize on-chain social recovery. As long as the device share and recovery share are intact, users can reconstruct their private key and migrate to a new provider. On-chain social recovery can also help recover the wallet if the device share is lost. #### If Openfort's API were compromised, what is the risk there? Can that risk be mitigated? What’s the risk of the auth share on the private key being exposed? **TLDR:** Both Openfort's server and Shield are encrypted. Even if an attacker obtains the auth share, they would need a secret to decrypt it. **Detailed Answer:** If Openfort's API were compromised, the risk is limited because both the auth share and Shield service are encrypted. Even if an attacker gains access to the auth share, they would still need to decrypt it using a secret. Furthermore, since the private key is split using Shamir's Secret Sharing, the auth share alone is insufficient to reconstruct the full key without the device or recovery share. ## Why create a Global Wallet? Embedded wallets typically require each application to manage its own wallet setup.
Diagram showing separate embedded wallets within isolated apps
This can lead to several challenges: * **Fragmented authentication across apps** When each app manages its own authentication, you end up with duplicated logic, inconsistent session handling, and a messy user experience. OAuth2 providers struggle to maintain continuity across domains, making it difficult to track a user's identity and wallet access holistically. * **Liquidity fragmentation kills network effects** If every app generates and manages its own wallets, user assets are scattered across isolated accounts. This makes it harder to build shared economies, pooled inventories, or unified user profiles—preventing the kind of liquidity and composability that ecosystems need to thrive. * **No interoperability across apps** Different wallet implementations across apps lead to incompatibilities in how users onboard, transact, or authorize actions. Without a shared interface or contract standard, there's no guarantee that features like cross-app identity, shared balances, or seamless UX will work consistently. ### How does Openfort Global Wallets solve this?
Global wallet architecture that centralizes wallet management
Switching to an ecosystem approach addresses the challenges of embedded wallets: * **Unified authentication:** A single sign-on mechanism minimizes friction and simplifies session management across apps. * **Monitoring and user management:** Manage all user permissions from a single dashboard. This empowers administrators to monitor activities and enforce security policies consistently. * **Enhanced developer experience:** One integration point means your team can focus on building features rather than managing multiple wallet configurations. * **Consistent user experience:** Provide a seamless and intuitive wallet experience regardless of which app the user interacts with. ### Ecosystem SDK: Deep Dive The Openfort Ecosystem SDK is a modular system, with each package playing a specific role in enabling secure, global wallet experiences. #### `@openfort/ecosystem/client` * Announces a provider using both [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) and [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193), making it compatible with popular Ethereum tooling (e.g., wagmi, viem). * Initiates a signer (via the `SCWSigner` class), which is responsible for transaction signing and secure communication. * All communication is based on window messaging (using `postMessage`), either between an iframe and the main page or between a popup and its parent. * The provider's `request` method wraps JSON-RPC requests and forwards them through the communicator, which handles the secure postMessage exchange. * The secure channel is established with the wallet UI (where the user interacts), following the [Mobile Wallet Protocol (MWP)](https://github.com/mobile-wallet-protocol). * The `SCWKeyManager` manages the secure key exchange and communication. #### `@openfort/ecosystem/core` * Acts as the **bridge** between the client SDK (in the app) and the wallet UI (the user-facing component). * Installed in the wallet UI, it handles the other end of the communication with the client SDK via MWP. * Exposes helper functions for reading incoming requests and sending responses. * Stores session data (key pairs for Diffie-Hellman exchange) per app, based on the origin, in local storage. This allows multiple secure channels for different apps. * If storage is cleared, a new handshake and key exchange are required. * Because it's MWP-compatible, you can use platform-specific libraries (e.g., Unity) for wallet UIs. #### `@openfort/ecosystem/react` * Provides React-specific helpers and a set of prebuilt components and a styling library (inspired by ConnectKit) to bootstrap wallet UIs quickly. * Contains React wrappers for invoking `@openfort/core` methods. * Used in the sample wallet UI at [id.sample.openfort.io](https://id.sample.openfort.io), which demonstrates how to build a wallet front-end using these prebuilt components. * The sample uses the Openfort Embedded Signer for key management, but other implementations (e.g., Safe, on-chain passkey) are possible. #### `@openfort/ecosystem-js` * Re-exports everything from the above packages, providing a single entry point for all SDK functionality. ### Architecture
Architectural diagram of Ecosystem SDK
### Core Components * **Client SDK (`@openfort/ecosystem/client`):**\ The "head chef" of the wallet system, handling wallet creation, UI pop-ups, and transaction management. EIP-1193 compatible. * **Communication module (`@openfort/ecosystem/core`):**\ The bridge between the app and the wallet UI, handling secure communication and session management via MWP. * **Framework prefabs (`@openfort/ecosystem/react`):**\ Prebuilt React components and styling helpers to accelerate wallet UI development. * **Ecosystem entrypoint (`@openfort/ecosystem-js`):**\ A unified package that re-exports all SDK modules for convenience. ## Batch transactions Smart Wallet enables you to send multiple onchain calls in a single transaction, improving UX by reducing multi-step interactions to a single click. A common example is combining an ERC-20 `approve` with a swap operation. You can submit batch transactions using the new `wallet_sendCalls` [RPC](https://eip5792.xyz/reference/sendCalls) method. :::steps #### Check wallet compatibility for atomic batching First, verify if your app supports atomic batching (This step is crucial if your app supports multiple wallet types). Use the `useCapabilities` hook from Wagmi experimental features and implement fallback functionality for unsupported wallets. Note: The `useWriteContracts` and `useCapabilities` hooks rely on new wallet RPC and may not be supported in all wallets. ```typescript app.tsx import { useCapabilities } from 'wagmi/experimental' function App() { const { data: capabilities } = useCapabilities() // Returns capability object per chain: // { // 84532: { // atomicBatch: { // supported: true, // }, // } // } // Check if atomic batching is supported in the chain we want to interact with const isAtomicBatchSupported = capabilities?.[84532]?.atomicBatch?.supported return (
{isAtomicBatchSupported ? ( ) : ( )}
) } ``` #### Set up contract interactions Import required hooks: `useAccount` and `useWriteContracts` from Wagmi, define your smart contract ABI, initialize the `useWriteContracts` hook for batch transactions, and create a transaction handling function that can process multiple contract calls. Important: Make sure your contract ABIs are correctly typed for better development experience. ```typescript app.tsx import { useAccount } from 'wagmi' import { useWriteContracts } from 'wagmi/experimental' // Define your contract ABI const abi = [ { stateMutability: 'nonpayable', type: 'function', inputs: [{ name: 'to', type: 'address' }], name: 'safeMint', outputs: [], } ] as const function BatchTransactionComponent() { const account = useAccount() const { writeContracts } = useWriteContracts() const handleBatchTransaction = () => { writeContracts({ contracts: [ { address: "0x119Ea671030FBf79AB93b436D2E20af6ea469a19", abi, functionName: "safeMint", args: [account.address], }, // Add more contract interactions as needed { address: "0x119Ea671030FBf79AB93b436D2E20af6ea469a19", abi, functionName: "safeMint", args: [account.address], } ], }) } return ( ) } ``` #### Implement transaction status monitoring Add the `useCallsStatus` hook to track transaction status, configure polling interval for status updates, display transaction status to users, and handle transaction completion and errors. Pro tip: The polling interval can be adjusted based on your needs, but 1 second is a good default. ```typescript app.tsx import { useCallsStatus } from 'wagmi/experimental' function BatchTransactionComponent() { const { data: id, writeContracts } = useWriteContracts() const { data: callsStatus } = useCallsStatus({ id: id as string, query: { enabled: !!id, // Poll every second until confirmed refetchInterval: (data) => data.state.data?.status === "CONFIRMED" ? false : 1000, }, }) // Example response structure: // { // status: 'CONFIRMED', // receipts: [ // { // logs: [{ // address: '0x...', // topics: ['0x...'], // data: '0x...' // }], // status: 'success', // blockHash: '0x...', // blockNumber: 122414523n, // gasUsed: 390000n, // transactionHash: '0x...' // } // ] // } return (
{callsStatus && (

Status: {callsStatus.status}

{callsStatus.status === 'CONFIRMED' && (

Transaction confirmed! Hash: {callsStatus.receipts[0].transactionHash}

)}
)}
) } ``` ::: ## Build a "Create Wallet" button For the best onboarding experience, we recommend adding a highly visible 'Create' or 'Create Wallet' button to your app's homepage. Adding this button streamlines the onboarding experience for new users and gets them ready to use your app in a few seconds. Here is an example of how the button looks:
create-wallet-button
### Implementation with Wagmi
To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID** using the Ecosystem SDK. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet.
Using Wagmi offers a balanced approach that combines flexibility with developer convenience. By integrating the global wallet with Wagmi, you get access to a comprehensive set of React hooks that handle common wallet operations, state management, and chain interactions. This approach requires both `@rapidfire/id` and `Wagmi` dependencies, along with React Query, but provides a more structured development experience. While you'll still need to build your own UI components, Wagmi's hooks significantly reduce the complexity of handling wallet connections, transactions, and chain switching.
:::steps #### Configure Wagmi First, set up your Wagmi configuration with the required chain and connector settings. ```ts wagmi.ts import { http, createConfig } from 'wagmi'; import { polygonAmoy } from 'wagmi/chains'; import { injected } from 'wagmi/connectors'; export const config = createConfig({ chains: [polygonAmoy], connectors: [ injected(), ], transports: { [polygonAmoy.id]: http(), }, }); declare module 'wagmi' { interface Register { config: typeof config; } } ``` #### Implement the Create Wallet Button (Wagmi) Create the main button component using Wagmi's useConnect hook. This uses the WalletLogo component defined in the previous step. ```tsx [CreateWalletButton.tsx] import React, { useCallback } from 'react'; import { useConnect } from 'wagmi'; import { WalletLogo } from './WalletLogo'; const buttonStyles = { background: 'linear-gradient(135deg, #2D3436 0%, #000000 100%)', border: '1px solid rgba(255, 255, 255, 0.1)', boxSizing: 'border-box' as const, display: 'flex', alignItems: 'center', gap: '12px', width: 'auto', minWidth: '200px', fontFamily: 'Inter, system-ui, sans-serif', fontWeight: '600', fontSize: '16px', color: '#FFFFFF', padding: '12px 20px', borderRadius: '12px', cursor: 'pointer', transition: 'all 0.2s ease-in-out', boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', ':hover': { transform: 'translateY(-1px)', boxShadow: '0 6px 8px -1px rgba(0, 0, 0, 0.1), 0 4px 6px -1px rgba(0, 0, 0, 0.06)', }, }; export function CreateWalletButton() { const { connectors, connect, data } = useConnect(); const createWallet = useCallback(() => { const injectedConnector = connectors.find( // Here you'd write your own connector ID as per the one you configured in the wallet SDK (connector) => connector.id === 'com.rapidfire.id' ); if (injectedConnector) { connect({ connector: injectedConnector }); } }, [connectors, connect]); return ( ); } ``` :::
**Notes:** For more detail, view the [`useConnect` documentation](https://wagmi.sh/react/api/hooks/useConnect). Upon successful connection, account information can be accessed via [data](https://wagmi.sh/react/api/hooks/useConnect#data) returned from `useConnect`, or via [`useAccount`](https://wagmi.sh/react/api/hooks/useAccount).
### Implementation with EIP-1193 Provider
The EIP-1193 Provider approach gives you the most granular control over your global wallet integration by directly using the SDK's Ethereum provider. This method requires only the `@rapidfire/id` package and allows you to build everything from scratch. You'll handle all wallet interactions through the standard `EIP-1193` provider interface, giving you complete flexibility in implementing wallet creation, connection flows, and transaction handling. While this approach requires more custom development work, it's ideal for applications that need specialized wallet interactions or want to minimize dependencies.
:::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: ::::steps #### Initialize the SDK, Provider, and Components Create a new SDK instance with your application configuration, set up the Ethereum provider, and define reusable components. :::tip Make sure to set up your `NEXT_PUBLIC_POLICY_ID` in your environment variables. ::: :::code-group ```ts [sdk.ts] import EcosystemWallet from '@rapidfire/id'; // Initialize the SDK with required parameters export const sdk = new EcosystemWallet({ appChainIds: [80002], appLogoUrl: 'https://a.rgbimg.com/users/b/ba/barunpatro/600/mf6B5Gq.jpg', appName: 'Example App', }); ``` ```ts [provider.ts] import { sdk } from './sdk'; // Configure the provider with chain and policy export const provider = sdk.getEthereumProvider({ policy: process.env.NEXT_PUBLIC_POLICY_ID }); ``` ```tsx [WalletLogo.tsx] import React from 'react'; const defaultContainerStyles = { paddingTop: 2, display: 'flex', alignItems: 'center', }; export function WalletLogo({ size = 24, containerStyles = defaultContainerStyles, }) { return (
); } ``` ::: #### Implement the Create Wallet Button Create the button component that uses the configured provider. ```tsx [CreateWalletButton.tsx] import React, { useCallback } from 'react'; import { WalletLogo } from './WalletLogo'; import { provider } from './provider'; const buttonStyles = { background: 'linear-gradient(135deg, #2D3436 0%, #000000 100%)', border: '1px solid rgba(255, 255, 255, 0.1)', boxSizing: 'border-box' as const, display: 'flex', alignItems: 'center', gap: '12px', width: 'auto', minWidth: '200px', fontFamily: 'Inter, system-ui, sans-serif', fontWeight: '600', fontSize: '16px', color: '#FFFFFF', padding: '12px 20px', borderRadius: '12px', cursor: 'pointer', transition: 'all 0.2s ease-in-out', boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', ':hover': { transform: 'translateY(-1px)', boxShadow: '0 6px 8px -1px rgba(0, 0, 0, 0.1), 0 4px 6px -1px rgba(0, 0, 0, 0.06)', }, }; export function CreateWalletButton({ handleSuccess, handleError }) { const createWallet = useCallback(async () => { try { const [address] = await provider.request({ method: 'eth_requestAccounts', }); handleSuccess(address); } catch (error) { handleError(error); } }, [handleSuccess, handleError]); return ( ); } ``` :::: ## Sponsor transactions One of the biggest UX enhancements unlocked by smart accounts is the ability for app developers to sponsor their users' transactions. If your ecosystem has a gas policy enabled, you can start sponsorship your user's transactions by using the `policy` in your [dashboard](https://dashboard.openfort.io/policies). :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: ### Use a policy when injecting your provider Sponsor all transactions that will be made with the injected provider. :::code-group ```tsx [app.tsx] import { sdk } from './sdk' function App() { useEffect(() => { sdk.getEthereumProvider({policy: 'pol_...'}); }, []); return (
) } ``` ```ts [sdk.ts] import EcosystemWallet from '@rapidfire/id' // Initialize the SDK with required parameters export const sdk = new EcosystemWallet({ appChainIds: [80002], appLogoUrl: 'https://a.rgbimg.com/users/b/ba/barunpatro/600/mf6B5Gq.jpg', appName: 'Example App', }); ``` ::: ### Updating the policy Updating your policy is useful if you change chain and need to specify a different policy ID. :::code-group ```tsx [app.tsx] import { sdk } from './sdk' function App() { useEffect(() => { sdk.setPolicy({policy: 'pol_...'}); }, []); return (
) } ``` ```ts [sdk.ts] import EcosystemWallet from '@rapidfire/id' // Initialize the SDK with required parameters export const sdk = new EcosystemWallet({ appChainIds: [80002], appLogoUrl: 'https://a.rgbimg.com/users/b/ba/barunpatro/600/mf6B5Gq.jpg', appName: 'Example App', }); ``` ::: *** You can also configure your own app level policy. Check the details in the [dashboard section](/docs/configuration/gas-sponsorship). * Using an [**external paymaster** setup.](/docs/configuration/gas-sponsorship#optional-using-external-paymasters) * Learn how to [use **erc20 tokens**](/docs/configuration/gas-erc20) to pay for gas fees. ## Integrating with wallet connectors Using a **wallet library** provides the most streamlined path to implementing a wallet UI, offering a complete solution with pre-built UI components and a polished user experience out of the box. This approach builds on top of Wagmi and includes additional UI components like the `ConnectButton`, ready-to-use themes, and built-in transaction interfaces. While it requires the most dependencies (`@rapidfire/id`, `Wagmi`, `React Query`, and `RainbowKit` -for example), it significantly reduces development time by providing a complete wallet interface solution.
To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet.
### Getting an EIP-1193 provider All of Ecosystem SDK wallets can export a standard [EIP-1193 provider](https://eips.ethereum.org/EIPS/eip-1193) object. This allows your app to request signatures and transactions from the wallet, using familiar JSON-RPC requests like `personal_sign` or `eth_sendTransaction`.
[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193), also known as the Ethereum JavaScript API, is a standardized interface for how applications can request information, signatures, and transactions from a connected wallet.
To get a wallet's EIP-1193 provider, use the `getEthereumProvider` method: ```tsx main.tsx import { ecosystemWalletInstance } from "./rapidfireConfig" // This example assumes you have already checked that Openfort 'embeddedState' is // `ready` and the user is `authenticated` const provider = await ecosystemWalletInstance.getEthereumProvider(); ``` ```ts rapidfireConfig.ts import EcosystemWallet from '@rapidfire/id'; export const ecosystemWalletInstance = new EcosystemWallet({ appChainIds: [80002], appLogoUrl: 'https://a.rgbimg.com/users/b/ba/barunpatro/600/mf6B5Gq.jpg', appName: 'Example App', }); ``` When requesting signatures and transactions from the wallet, you can either choose to interface with the **EIP-1193** provider directly, or to pass it to a library like `wagmi` or `viem`.
If you want to check out the sample using the `@rapidfire/id` directly, you can find it in the [Rapidfire SDK repository](https://github.com/openfort-xyz/ecosystem-sample/tree/main/usage-examples/wagmi-nextjs). You will see it includes examples using [ethers](https://docs.ethers.org/v5/), [connectKit](https://docs.family.co/connectkit), [rainbowKit](https://www.rainbowkit.com/) and [wagmi](https://wagmi.sh/).
### Implementation with RainbowKit
ecosystem-rainbow-kit
#### 1. Install dependencies Install the `@rapidfire/id` SDK and its peer dependencies: ```bash npm i @rapidfire/id @rainbow-me/rainbowkit @tanstack/react-query ``` #### 2. Create the connector ```ts config.ts import EcosystemWallet from '@rapidfire/id'; export const identityInstance = new EcosystemWallet({ appChainIds: [80002], appLogoUrl: 'https://a.rgbimg.com/users/b/ba/barunpatro/600/mf6B5Gq.jpg', appName: 'Example App', }); ``` #### 3. Wrap app with providers At the highest level of your applications, wrap the component with the wagmi, QueryClient, and RainbowKit providers. Pass the configuration you created in step 2 to the wagmi provider. All of **global wallets** can export a standard [EIP-1193 provider](https://eips.ethereum.org/EIPS/eip-1193) object. This allows your app to request signatures and transactions from the wallet, using familiar JSON-RPC requests like `personal_sign` or `eth_sendTransaction`.
[EIP-1193](https://eips.ethereum.org/EIPS/eip-1193), also known as the Ethereum JavaScript API, is a standardized interface for how applications can request information, signatures, and transactions from a connected wallet.
To get a wallet's EIP-1193 provider, use the `getEthereumProvider` method: ```tsx _app.tsx import {QueryClient, QueryClientProvider} from '@tanstack/react-query'; import {WagmiProvider} from 'wagmi'; import {polygonAmoy} from 'wagmi/chains'; import {useEffect} from 'react'; import {config} from './wagmiConfig'; import {identityInstance} from './config'; const queryClient = new QueryClient(); export default function App() { useEffect(() => { if (!identityInstance) return; identityInstance.getEthereumProvider(); }, []); return ( ); } ``` ```ts wagmiConfig.ts import {http, createConfig} from 'wagmi'; import {polygonAmoy} from 'wagmi/chains'; import {injected} from 'wagmi/connectors'; export const config = createConfig({ chains: [polygonAmoy], connectors: [injected()], ssr: true, transports: { [polygonAmoy.id]: http(), }, }); ``` ```ts config.ts import EcosystemWallet from '@rapidfire/id'; export const identityInstance = new EcosystemWallet({ appChainIds: [80002], appLogoUrl: 'https://a.rgbimg.com/users/b/ba/barunpatro/600/mf6B5Gq.jpg', appName: 'Example App', }); ``` #### 4. Use the `ConnectButton` Import the `ConnectButton` and use to prompt users to connect to their provider **global wallet**. ```tsx import {ConnectButton} from '@rainbow-me/rainbowkit'; function Page() { return (

My app

...
); } ``` Thats it! You can now use any wagmi hook in your application to interact with the connected wallet. When users connect and transact with their wallet, Openfort will open a pop-up for users to authorize any actions. ## Create a React Native App :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: This guide will walk you through adding support for any **global wallet** into a React Native app by integrating the [Mobile Wallet Protocol Client](https://www.npmjs.com/package/@mobile-wallet-protocol/client).
If you need a template or scaffold to start with, you can check out the [Global Wallet Expo Example](https://github.com/openfort-xyz/ecosystem-wallet-expo-example).
### Prerequisites #### Install peer dependencies The Mobile Wallet Protocol Client library requires the [Expo WebBrowser](https://docs.expo.dev/versions/latest/sdk/webbrowser/) and [Async Storage](https://react-native-async-storage.github.io/async-storage/docs/install) packages to be installed. Follow the instructions on the respective pages for any additional setup. ```zsh [npm] npm i expo expo-web-browser @react-native-async-storage/async-storage ``` ```zsh [yarn] yarn add expo expo-web-browser @react-native-async-storage/async-storage ``` #### Polyfills Mobile Wallet Protocol Client requires `crypto.randomUUID`, `crypto.getRandomValues`, and `URL` to be polyfilled globally since they are not available in the React Native environment. Below is an example of how to polyfill these functions in your app using the [expo-crypto](https://docs.expo.dev/versions/latest/sdk/crypto/) and [expo-standard-web-crypto](https://github.com/expo/expo/tree/master/packages/expo-standard-web-crypto/) packages. ```zsh [npm] npm i expo-crypto expo-standard-web-crypto react-native-url-polyfill ``` ```zsh [yarn] yarn add expo-crypto expo-standard-web-crypto react-native-url-polyfill ``` ```js [polyfills.js] import "react-native-url-polyfill/auto"; import { polyfillWebCrypto } from "expo-standard-web-crypto"; import { randomUUID } from "expo-crypto"; polyfillWebCrypto(); crypto.randomUUID = randomUUID; ``` ```tsx [App.tsx] import "./polyfills"; // import before @mobile-wallet-protocol/client /// ... ``` ### Setup :::steps #### Install Mobile Wallet Protocol Client Add the latest version of Mobile Wallet Protocol Client to your project. ```zsh [npm] npm i @mobile-wallet-protocol/client@latest ``` ```zsh [yarn] yarn add @mobile-wallet-protocol/client@latest ``` ### Usage Mobile Wallet Protocol Client provides 2 interfaces for mobile app to interact with the Global Wallet, an EIP-1193 compliant provider interface and a wagmi connector.
If your app is using wallet aggregator, go straight to **Option 2: Wagmi Connector** for 1-line integration. :)
#### Option 1: EIP-1193 Provider Create a new `EIP1193Provider` instance, which is EIP-1193 compliant. ```tsx [App.tsx] import { EIP1193Provider } from "@mobile-wallet-protocol/client"; // Step 1. Initialize provider with your dapp's metadata and target wallet const metadata = { name: "My App Name", customScheme: "myapp://", // only custom scheme (e.g. `myapp://`) is supported in v1.0.0 chainIds: [8453], logoUrl: "https://example.com/logo.png", }; const provider = new EIP1193Provider({ metadata, wallet: { type: 'web', name: "Rapid fire wallet", // The scheme should be the target wallet's URL scheme: 'https://id.sample.openfort.io', iconUrl: 'https://purple-magnificent-bat-958.mypinata.cloud/ipfs/QmfQrh2BiCzugFauYF9Weu9SFddsVh9qV82uw43cxH8UDV', }, }); // ... // 2. Use the provider const addresses = await provider.request({ method: "eth_requestAccounts" }); const signedData = await provider.request({ method: "personal_sign", params: ["0x48656c6c6f20776f726c6421", addresses[0]], }); ``` #### Option 2: Wagmi Connector Add the latest verion of Mobile Wallet Protocol wagmi-connectors to your project. ```zsh [npm] npm i @mobile-wallet-protocol/wagmi-connectors@latest ``` ```zsh [yarn] yarn add @mobile-wallet-protocol/wagmi-connectors@latest ``` Simply import the `createConnectorFromWallet` function and pass in the wallet you want to use to wagmi config. ```ts [config.ts] import { createConnectorFromWallet, Wallets, } from "@mobile-wallet-protocol/wagmi-connectors"; const metadata = { name: "My App Name", customScheme: "myapp://", // only custom scheme (e.g. `myapp://`) is supported in v1.0.0 chainIds: [8453], logoUrl: "https://example.com/logo.png", }; export const config = createConfig({ chains: [base], connectors: [ createConnectorFromWallet({ metadata, wallet: { type: 'web', name: "Rapid fire wallet", scheme: 'https://id.sample.openfort.io', iconUrl: 'https://purple-magnificent-bat-958.mypinata.cloud/ipfs/QmfQrh2BiCzugFauYF9Weu9SFddsVh9qV82uw43cxH8UDV', }, }), ], transports: { [base.id]: http(), }, }); ``` Then you can use wagmi's react interface to interact with the Smart Wallet. ```tsx [App.tsx] import { useConnect } from "wagmi"; // ... const { connect, connectors } = useConnect(); return ( {signature && (

Signature: {signature}

)} {error &&
Error: {error.message}
}
) } ``` ### Typed Data Signing For more complex signatures, you can use typed data signing (EIP-712) with wagmi's `useSignTypedData` hook: ```tsx import { useSignTypedData } from 'wagmi' function SignTypedData() { const { signTypedData, data: signature, isPending, error } = useSignTypedData() const types = { Mail: [ {name: 'from', type: 'Person'}, {name: 'to', type: 'Person'}, {name: 'content', type: 'string'}, ], Person: [ {name: 'name', type: 'string'}, {name: 'wallet', type: 'address'}, ], } const handleTypedMessage = () => { signTypedData({ domain: { chainId: 1, // Replace with your chain ID name: 'Example DApp', verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', version: '1', }, types, message: { from: { name: 'Alice', wallet: '0x2111111111111111111111111111111111111111' }, to: { name: 'Bob', wallet: '0x3111111111111111111111111111111111111111' }, content: 'Hello!', }, primaryType: 'Mail', }) } return (
{signature && (

Signature: {signature}

)} {error &&
Error: {error.message}
}
) } ``` Both signing methods work seamlessly with Smart Contract Wallets that implement EIP-1271, allowing for a consistent signing experience across EOA and Smart Contract Wallets. ## Unity Android :::note To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: This guide will walk you through adding support for any **global wallet** into a Unity app by integrating the [Mobile Wallet Protocol](https://mobilewalletprotocol.github.io/wallet-mobile-sdk/). On Android, we use [Chrome Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs/) (if available) to seamlessly connect gamers to your Openfort wallet from within the game. ### Requirements :::info **UNITY VERSIONS BELOW 2021.3** To check if Chrome Custom Tabs are available, older Unity versions may require a specific Gradle plugin version. For example, on Unity 2019.4, you must upgrade from 3.4.\* to 3.4.3 (see Android's blog post): 1. In Unity go to Build Settings -> Player Settings -> Android -> Publishing Settings -> Enable Custom Base Gradle Template under the Build section 2. Open the newly generated Assets/Plugins/Android/baseProjectTemplate.grade file 3. Update classpath `com.android.tools.build:gradle:3.4.0` to classpath `com.android.tools.build:gradle:3.4.3` ::: \##Setup 1. In Unity go to **Build Settings** -> **Player Settings** -> **Android** -> **Publishing Settings** -> Enable **Custom Main Manifest** and **Custom Main Gradle Template** under the **Build** section 2. Open the newly generated `Assets/Plugins/Android/AndroidManifest.xml` file. Add the following code inside the `` element: ```xml ``` :::warning Make sure that `android:scheme` **mygame** is the same as `Custom Scheme` (**mygame://** in this case) ::: 3. Open the newly generated `Assets/Plugins/Android/mainTemplate.gradle` file. Add the following code inside `dependencies` block: ```gradle implementation('androidx.browser:browser:1.5.0') ``` :::info For this version of the Chrome Custom Tabs to work, the compileSdkVersion must be at least 33. This is usually the same value as the targetSdkVersion, which you can set in **Build Settings** -> **Player Settings** -> **Android** -> **Other Settings** -> **Target API Level**. ::: **Proguard** If you enable **Minify** in your project settings, you will need to add a custom Proguard file to your project. 1. In Unity go to **Build Settings** -> **Player Settings** -> **Android** -> **Publishing Settings** -> Enable **Custom Proguard File** under the **Build** section 2. Open the newly generated Assets/Plugins/Android/proguard-user.txt file. Add the following code inside the `` element ```txt -dontwarn com.openfort.** -keep class com.openfort.** { *; } -keep interface com.openfort.** { *; } -dontwarn androidx.** -keep class androidx.** { *; } -keep interface androidx.** { *; } ``` ## Unity WebGL :::info To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: This guide will walk you through adding support for any **global wallet** into a Unity app by integrating the [Mobile Wallet Protocol](https://mobilewalletprotocol.github.io/wallet-mobile-sdk/). When doing `Build And Run` from your Unity **Build Settings** with the target platform `WebGL` you will notice that no popups are allowed. This is due to the `Cross-Origin-Opener-Policy` header being set to `same-origin-allow-popups` by default. This is a security feature to prevent popups from different origins. There are two main things to consider when running your Unity WebGL build with global wallet support: 1. For any global wallet to work, you need to host the build or run it locally with `Cross-Origin-Opener-Policy` set to `same-origin-allow-popups`. 2. Compression Format: Set to Disabled (Player Settings > Publishing Settings) for final builds. Here's an example setup for testing your global wallet locally using express server. Please note that in this example, the build output is in the `build-output` directory. Once your build is done, follow these steps: :::steps #### 1. Create a Project Folder and Setup Files Create a new folder for your server project and navigate to it: ```bash mkdir unity-webgl-server cd unity-webgl-server ``` Create a new subfolder called `build-output` where you'll place your Unity WebGL build files: ```bash mkdir build-output ``` Copy all the files from your Unity WebGL build into the `build-output` folder. This should include files like: * `index.html` * `.unityweb` files * Other assets and build files #### 2. Initialize Your Node.js Project Initialize a new Node.js project and install Express: ```bash npm init -y npm install express ``` Create a new file called `server.js` in the root folder with the following content: ```js server.js const express = require("express"); const path = require("path"); const app = express(); const port = 8000; app.use((req, res, next) => { res.header( "Cross-Origin-Opener-Policy", "same-origin-allow-popups", ); next(); }); app.use(express.static(path.join(__dirname, "build-output"))); app.listen(port, () => console.log(`Server running on http://localhost:${port}`), ); ``` ```json package.json { "name": "express-webgl", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.21.2" } } ``` #### 3. Run Your Local Server Start your Express server by running: ```bash node server.js ``` You should see the message: `Server running on http://localhost:8000` Open your browser and navigate to `http://localhost:8000` to see your Unity WebGL build running with global wallet support enabled. ::: ### Troubleshooting If you encounter any issues: 1. Make sure your `build-output` folder contains all the necessary Unity WebGL build files 2. Confirm that your server is properly setting the `Cross-Origin-Opener-Policy` header 3. Check browser console for any errors 4. Ensure your global wallet is properly configured to work with the Mobile Wallet Protocol ## Create a game in Unity :::note[Caution] To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: This guide will walk you through adding support for any **global wallet** into a Unity app by integrating the [Mobile Wallet Protocol](https://mobilewalletprotocol.github.io/wallet-mobile-sdk/). :::note[TARGET PLATFORM VS UNITY EDITOR PLATFORM] We have added compilation flags to the Unity SDK to ensure that specific Unity editors can only build certain platform targets. Please note that the table below indicates which editor you can use to build a platform target, but it does not determine whether you can run the SDK in that editor. For example, the SDK allows you to build iOS games using a macOS Unity Editor, but you cannot use the Windows Unity Editor. | Target Platform | Windows Unity Editor | macOS Unity Editor | | -------------------------------------------------------------- | :------------------: | :----------------: | | Windows | ✅ | ❌ | | macOS | ❌ | ✅ | | [Android](/docs/products/cross-app-wallet/usage/unity-android) | ✅ | ✅ | | iOS | ❌ | ✅ | | [Web](/docs/products/cross-app-wallet/usage/unity-webgl) | ✅ | ✅ | ::: :::tip If you need a template or scaffold to start with, you can check out the [Global Wallet Unity Example](https://github.com/openfort-xyz/mobile-wallet-protocol-unity-client/tree/main/Project). ::: ### Setup #### Install Mobile Wallet Protocol Client There are two ways to install the SDK: ##### UPM 1. Open the Package Manager 2. Click the add + button and select "Add package from git URL..." Enter [https://github.com/openfort-xyz/mobile-wallet-protocol-unity-client.git?path=com.openfort.mobile-wallet-protocol](https://github.com/openfort-xyz/mobile-wallet-protocol-unity-client.git?path=com.openfort.mobile-wallet-protocol) and click 'Add' ##### manifest.json 1. Open your project's Packages/manifest.json file 2. Add "com.openfort.mobile-wallet-protocol": "[https://github.com/openfort-xyz/mobile-wallet-protocol-unity-client.git?path=com.openfort.mobile-wallet-protocol](https://github.com/openfort-xyz/mobile-wallet-protocol-unity-client.git?path=com.openfort.mobile-wallet-protocol)" in the dependencies block ### Usage Mobile Wallet Protocol Client provides an interface for Unity to interact with the Global Wallet, an EIP-1193 compliant provider interface. The EIP-1193 provider implements a method called `request` that accepts an object with the following fields: * **`method`**: the name of a JSON-RPC request to send to the wallet * **`params`**: any parameters to include with the request #### Methods | Method | Function Name | | ------------------------ | ---------------------- | | eth\_requestAccounts | EthRequestAccounts | | eth\_sendTransaction | EthSendTransaction | | personal\_sign | PersonalSign | | eth\_signTypedData\_v4 | EthSignTypedDataV4 | | wallet\_sendCalls | WalletSendCalls | | wallet\_showCallsStatus | WalletShowCallsStatus | | wallet\_grantPermissions | WalletGrantPermissions | | wallet\_getCapabilities | WalletGetCapabilities | #### EIP-1193 Provider Create a new `EIP1193Provider` instance, which is EIP-1193 compliant. ```cs ClientController.cs using UnityEngine; using MobileWalletProtocol; public class ClientController : MonoBehaviour { [SerializeField] MWPClientOptions m_Options = new MWPClientOptions() { Metadata = new AppMetadata() { Name = "Smart Wallet", CustomScheme = "exp://", ChainIds = new string[] { "0xaa36a7" } }, Wallet = Wallet.CreateWebWallet( name: "Rapid fire wallet", // The scheme should be the target wallet's URL scheme: "https://id.sample.openfort.io#policy=pol_a909d815-9b6c-40b2-9f6c-e93505281137", iconUrl: "https://purple-magnificent-bat-958.mypinata.cloud/ipfs/QmfQrh2BiCzugFauYF9Weu9SFddsVh9qV82uw43cxH8UDV" ) }; MWPClient m_Client; string m_Account; void Awake() { m_Client = new MWPClient(m_Options); } public async void RequestAccounts() { var result = await m_Client.EthRequestAccounts(); if (result.IsSuccess) { var accounts = result.Value; m_Account = accounts[0]; foreach (var account in accounts) { Debug.Log("Account: " + account); } } else { Debug.LogError("Error: " + result.Error); } } public void Disconnect() { m_Client.Reset(); } } ``` ### Gas Sponsorship If you want to sponsor transactions, you need to add `#policy=POLICY_ID` to the wallet scheme. ``` #policy=POLICY_ID ``` ### Troubleshooting 1. If no network appears in your wallet transaction request, make sure you: * Have the correct chain ID in the metadata * That chain is supported by your global wallet. ## Wagmi template to create a web app The following is a quick and easy implementation for getting started with [Wagmi](https://wagmi.sh/) template and the global wallet of choice. :::warning To make these instructions concrete, we have created a sample global wallet called **Rapidfire ID**. To interact with it, you can find its SDK in the NPM package directory: [@rapidfire/id](https://www.npmjs.com/package/@rapidfire/id). You can check out the GitHub [repository for Rapidfire Wallet](https://github.com/openfort-xyz/ecosystem-sample) to learn how to create your own wallet. ::: This guide will walk you through integrating the global wallet Rapidfire ID. :::tip If you need a template or scaffold to start with, you can check out the [Rapidfire Wagmi Example](https://github.com/openfort-xyz/ecosystem-sample/tree/main/usage-examples/wagmi-nextjs). ::: ::::steps #### Create a new Wagmi project Run the project creation command using your preferred package manager and follow the CLI prompts to set up your project. ```bash terminal npm create wagmi ``` #### Install dependencies and start development server Install all required dependencies, start the development server. Your app should now be running on localhost. ```bash terminal npm install @rapidfire/id npm install npm run dev ``` #### Configure Wagmi with Smart Wallet Update the Wagmi configuration file, set up `polygonAmoy` as the primary chain, and configure wallet connector with injected wallet preference. ```ts wagmi.ts import { http, createConfig } from 'wagmi'; import { polygonAmoy } from 'wagmi/chains'; import { injected } from 'wagmi/connectors'; export const config = createConfig({ chains: [polygonAmoy], connectors: [ injected(), ], transports: { [polygonAmoy.id]: http(), }, }); declare module 'wagmi' { interface Register { config: typeof config; } } ``` #### Wrap App in Context Provider Wrap your app in the `WagmiProvider` React Context Provider and pass the **config** you created earlier to the **value** property. :::code-group ```tsx [app.tsx] import { WagmiProvider } from 'wagmi' import { config } from './config' function App() { useEffect(() => { config.getEthereumProvider(); }, []); return ( {/** ... */} ) } ``` ```ts [config.ts] import EcosystemWallet from '@rapidfire/id' // Initialize the SDK with required parameters export const config = new EcosystemWallet({ appChainIds: [84532], appLogoUrl: 'https://a.rgbimg.com/users/b/ba/barunpatro/600/mf6B5Gq.jpg', appName: 'Example App', }); ``` ::: #### Keep building You can find everything you need here: [https://wagmi.sh/react/api/hooks](https://wagmi.sh/react/api/hooks) :::: ## Authentication At a high-level, onboarding users onchain breaks down into three core questions: who is taking the onchain action (the user), through what means are they able to control this action (the signer) and where is the affected state (the account). **With Openfort, your app can authenticate users across web2 and web3 accounts, including:** * **Email**, via password recovery * **Wallet**: via Sign In With Ethereum ([SIWE](https://eips.ethereum.org/EIPS/eip-4361)) standard * **Web2 social accounts**: via [OAuth2.0 Protocol](https://oauth.net/2/) (Google, Facebook, Twitter, Discord & more.) These methods can be configured as either login or [link](/docs/products/embedded-wallet/javascript/auth/user-management) options for users. Once authenticated, Openfort creates a common player object that includes the user's ID (`playerID`) and all linked accounts, treating all users equally regardless of their authentication method. The Openfort Auth can be plugged into any front-end/user interface you already have. #### Guides ##### [Authenticating with email](/docs/products/embedded-wallet/javascript/auth/password) Learn how to authenticate users with email. ##### [Non-custodial signer](/docs/products/embedded-wallet/javascript/signer/recovery) Create a non-custodial signer for your users. ##### [Session keys](/docs/products/embedded-wallet/javascript/smart-wallet/advanced/session-keys) Create session keys with smart wallets #### Templates ##### [Auth with NextJS](https://github.com/openfort-xyz/openfort-js/tree/main/examples/apps/auth-sample) Openfort comlete sample solution for NextJS. ##### [Auth with Web3Auth](https://github.com/openfort-xyz/samples/tree/main/web3auth-nextjs) Integration with an external signer like Web3Auth. ##### [Auth with Firebase](hhttps://github.com/openfort-xyz/embedded-wallet-firebase-auth-sample-nextjs) Integration with thirdparty auth proividers like Firebase. ## FAQs #### What is an embedded wallet? An embedded wallet is a in-app wallet designed to onboard both web2 and web3 users simultaneously. Nexus includes several core components: * Authentication: A single-sign on authentication experience with familiar social logins * Wallet: An embedded wallet accessible across all devices and games * Account: The asset layer, powering users onchain to manage their assets #### How is an embedded wallet different to existing web3 wallets? Unlike browser extension wallets like MetaMask, which can be challenging for users to set up, or "embedded wallet" that are limited to a single application, Openfort Identity offers a balance of user-friendliness and versatility. Openfort Identity allows users to use the same wallet across all their games and marketplaces, rather than having one wallet per game. #### How does an embedded wallet's key management work? Openfort uses a smart contract wallet system on EVM chains. Transactions are processed only if they're signed by either the player (embedded wallet) or the game (session key). * Private key: Using an SSS approach to split the key in three shards only to be reconstructed on the player's client side. * Session key: Temporary key used created by the player authorizing a third party to make transactions under a scooped permission set. #### What platforms does embedded wallet support? | Functionality | Web | Unity | Unreal | PlayStation | Xbox | | :------------: | :-: | :---: | :----: | :---------: | :--: | | Authentication | Yes | Yes | Yes | No | No | | Transactions | Yes | Yes | Yes | No | No | #### What types of providers does authentication support? Openfort supports familiar social logins and passwordless email sign-in. You can also import a custom auth token or a OIDC compatible token. #### Can users export their wallet private keys? Yes, you can use the endpoints provide to [export the private key](/docs/products/embedded-wallet/javascript/signer/export-key). #### How does wallet recovery work with Openfort? With Openfort you can recover the account if you forget your email/social login as long as you have a majority of 2/3 key shards. In the event of not having the majority of shards, we've implemented a [social recovery](/docs/products/embedded-wallet/javascript/smart-wallet/advanced/social-recovery) where the private key can be reconstructed. #### Can a user change their associated email address for a pre-generated wallet? No, a user cannot change their email address once they have created a pre-generated wallet. If they need to change their email address, they will need to create a new wallet. #### How will embedded wallets integrate with existing authentication or account systems? **Option 1: Full Authentication & Signer.** If you're starting from scratch, a very common way to use Nexus is as your game's primary identity and wallet (as games like Rogue Nation do). **Option 2: Existing Authentication & Signer.** However, many games will already have an account system that they will be reluctant to migrate away from. Easy solution, integrate your authentication with Nexus while using the signer solution. **Option 3: Existing Authentication & Existing Signer.** You might be already using a wallet provider and want to upgrade to smart accounts. #### What level of support and Service Level Agreements (SLAs) are provided? At Openfort, we understand the significance of maintaining a high standard of service. Our Service Level Agreements (SLAs) reflect our commitment to providing a reliable, efficient, and safe environment for your operations. ### Security and Contingency Planning #### If Openfort were to shut down with a one-month notice, would there be scope to change the signer on the Smart Contract Wallet and use it in connection with a different provider that manages the private keys differently? **TLDR:** With enough time, transitioning is fairly simple. You'd need to invoke the `transferOwnership` function for users to accept the new signer. **Detailed Answer:** Yes, if Openfort shuts down with a one-month notice, it's possible to change the signer. Since Openfort wallets are non-custodial, users have control over their private keys. The key migration process would involve using the "recovery share" and "device share" from Shamir's Secret Sharing (SSS) to reconstruct the private key, allowing users to accept a new signer through the `transferOwnership` function. The new provider would need to support compatible key management systems. #### If Openfort were shut down with zero notice, would there be any scope to do a migration? Would that rely on a self-hosted Shield for the recovery share and the device share being intact? **TLDR:** If there is no self-hosted option, users should rely on on-chain social recovery. **Detailed Answer:** Migration is still possible, but it depends on the self-hosted Shield for the recovery share and the availability of the device share. If a self-hosted Shield is not in place, users can utilize on-chain social recovery. As long as the device share and recovery share are intact, users can reconstruct their private key and migrate to a new provider. On-chain social recovery can also help recover the wallet if the device share is lost. #### If Openfort's API were compromised, what is the risk there? Can that risk be mitigated? What's the risk of the auth share on the private key being exposed? **TLDR:** Both Openfort's server and Shield are encrypted. Even if an attacker obtains the auth share, they would need a secret to decrypt it. **Detailed Answer:** If Openfort's API were compromised, the risk is limited because both the auth share and Shield service are encrypted. Even if an attacker gains access to the auth share, they would still need to decrypt it using a secret. Furthermore, since the private key is split using Shamir's Secret Sharing, the auth share alone is insufficient to reconstruct the full key without the device or recovery share. ## Overview Javascript The [Openfort Javascript SDK](https://github.com/openfort-xyz/openfort-js) is a Javascript library client for Openfort that allows you to add secure authentication, non-custodial embedded wallets, and powerful UX infrastructure into your game. ### Features | Feature | Status | | ----------------------------------------- | ------ | | Sign in with email | ✅ | | Sign in with OAuth | ✅ | | Sign in with guest mode | ✅ | | Sign in with Third-Party Auth | ✅ | | Embedded wallet creation | ✅ | | Embedded wallet recovery | ✅ | | Embedded wallet signatures & transactions | ✅ | | Native smart wallets | ✅ | | Gas sponsorship | ✅ | | Session keys | ✅ | :::info This section is **completely headless (no UI)**, if you're looking to add authentication UI and wallet connectors please head to [Openfort Kit](/docs/products/embedded-wallet/react/kit). ::: ### Get started Check out these popular guides to get started. #### [Setting up the SDK](/docs/products/embedded-wallet/javascript/quickstart) Setting up a project #### [Smart wallets](/docs/products/embedded-wallet/javascript/smart-wallet) Using smart wallets #### [Embedded wallet](/docs/products/embedded-wallet/javascript/signer/recovery) Create an embedded wallet ### Templates Check out these popular samples to get started. #### [Auth Sample](https://github.com/openfort-xyz/openfort-js/tree/main/examples/apps/auth-sample) Use the next-app starter sample #### [Solana sample](https://github.com/openfort-xyz/openfort-js/tree/main/examples/apps/solana-sample) Create a Solana wallet #### [Vue sample](https://github.com/openfort-xyz/openfort-js/tree/main/examples/apps/wallet-libraries/vite-wagmi) Wagmi + Openfort + Vue ## Quickstart **The [Openfort JS SDK](https://www.npmjs.com/package/@openfort/openfort-js) is a low level library that allows you to build custom login flows, request signatures and more.**
**Want to start building right away?** If you're looking to quickly build with auth UI elements into your app, check out our [React](/docs/products/embedded-wallet/react/kit) guides.
You will get out of the box support for: 1. Multiple authentication methods 2. Bare-metal API access to customize your own UI 3. Non-custodial embedded wallet 4. Account abstraction compatible and infrastructure Check out the [starter repos](/docs/overview/repositories) for templates for integrating Openfort with various libaries, frameworks and use cases. ::::steps ### Install the Openfort SDK Install the latest version of the [Openfort JS SDK](https://www.npmjs.com/package/@openfort/openfort-js) using your package manager of choice: :::note **Prerequisites** In order to integrate Openfort, you project must: * a **Node v20** (Active LTS version) or **higher**. * a [minimum TypeScript version of 5](https://github.com/microsoft/TypeScript/releases/tag/v5.0.2) ::: :::code-group ```sh [npm] npm install @openfort/openfort-js@latest ``` ```sh [yarn] yarn add @openfort/openfort-js@latest ``` ```sh [pnpm] pnpm install @openfort/openfort-js@latest ``` ::: ### Set your auth providers Navigate to the **auth providers** page on the [Openfort dashboard](https://dashboard.openfort.io) by selecting your project and then clicking Auth providers Methods in the side bar in the [players page](https://dashboard.openfort.io/players). Select the account types you'd like users to be able to login with. For more information on how to enable social logins, check out the [dashboard docs](/docs/configuration/social-login). ### Get your [API keys](/docs/configuration/api-keys) In the [API keys](https://dashboard.openfort.io/developers/configuration/api-keys) section, you'll find: * **Publishable Key**: Safe to expose in client-side environment * **Secret Key**: Must be kept secure and used only server-side To generate non-custodial wallets: 1. Scroll to the Shield section and click **Create Shield keys** 2. **Store the encryption share** safely when it appears (you'll only see it once) 3. You'll receive: * **Shield Publishable Key**: Safe for client-side use * **Shield Secret Key**: Keep secure, server-side only ### Import Openfort into your app In your project, import the Openfort and configure it. Set the publishable keys fields to the publishable key you got from the Dashboard in step 3. Concretely, the Openfort SDK needs to be instantiated only once. ```ts server.ts import Openfort from "@openfort/openfort-js"; const openfort = new Openfort({ baseConfiguration: { publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY", }, shieldConfiguration: { shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY", } }); ``` ### You're good to go! Once you've configured your app, you can now use `openfort` throughout to access the Openfort SDK. Check out our [starter repo](https://github.com/openfort-xyz/openfort-js/tree/main/examples/apps/auth-sample) to see what a simple end-to-end integration looks like, or read on to learn how you can use Openfort to: * [Log your users in](/docs/products/embedded-wallet/javascript/auth). * [Request signatures](/docs/products/embedded-wallet/javascript/smart-wallet). :::: ## JavaScript SDK Troubleshooting ### Vite Build Errors #### Q: I'm getting warnings like "Module "buffer" has been externalized for browser compatibility" when building with Vite. How do I fix this? A: This issue is caused by missing Node polyfills in your bundled web application. To resolve it: 1. Install `vite-plugin-node-polyfills`: ```bash npm i --save-dev vite-plugin-node-polyfills # or yarn add --dev vite-plugin-node-polyfills ``` 2. Update your `vite.config.js` or `vite.config.ts`: ```javascript import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { nodePolyfills } from 'vite-plugin-node-polyfills' export default defineConfig({ plugins: [ react(), nodePolyfills() ], }) ``` 3. Restart your development server. #### Q: I'm getting a "Cannot read properties of undefined (reading 'from')" error in hdkey-without-crypto.js. How do I fix this? A: This error is related to the `ethereum-cryptography` package. To fix it: 1. Install the `buffer` package: ```bash npm install buffer # or yarn add buffer ``` 2. Update your `vite.config.ts`: ```javascript import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { nodePolyfills } from 'vite-plugin-node-polyfills' export default defineConfig({ plugins: [ react(), nodePolyfills({ globals: { Buffer: false } }) ], define: { global: {}, }, }) ``` 3. Restart your Vite dev server. #### Q: I'm getting a "require is not defined" error when using `vite preview`. How do I fix this? A: Add the following to your `vite.config.ts` file: ```javascript export default defineConfig({ // ... your existing config build: { commonjsOptions: { transformMixedEsModules: true } }, }) ``` Then rebuild your project before running `vite preview`. ### Webpack Build Errors #### Q: I'm getting "Module not found" errors for Node.js core modules when using create-react-app. How do I fix this? A: This is due to Webpack 5 not including Node.js polyfills by default. To fix it: 1. Install required packages: ```bash npm install --save-dev react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer process # or yarn add --dev react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer process ``` 2. Create a `config-overrides.js` file in your project root: ```javascript const webpack = require('webpack'); module.exports = function override(config) { const fallback = config.resolve.fallback || {}; Object.assign(fallback, { crypto: require.resolve('crypto-browserify'), stream: require.resolve('stream-browserify'), assert: require.resolve('assert'), http: require.resolve('stream-http'), https: require.resolve('https-browserify'), os: require.resolve('os-browserify'), url: require.resolve('url'), }); config.resolve.fallback = fallback; config.plugins = (config.plugins || []).concat([ new webpack.ProvidePlugin({ process: 'process/browser', Buffer: ['buffer', 'Buffer'], }), ]); config.module.rules.push({ test: /\.m?js/, resolve: { fullySpecified: false, }, }); return config; }; ``` 3. Update your `package.json` scripts: ```json "scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-scripts eject" } ``` #### Q: How do I deal with 'failed to parse source map' warnings? A: You can disable these warnings by adding `GENERATE_SOURCEMAP=false` to your start script in `package.json`: ```json "scripts": { "start": "GENERATE_SOURCEMAP=false react-app-rewired start", // ... }, ``` ### Next.js Build Errors #### Q: I'm getting a "Named export 'curves' not found" error with the elliptic package in Next.js 13/14. How do I fix this? A: Add the following to your `next.config.js`: ```javascript const nextConfig = { // ... other next config experimental: { esmExternals: false, }, }; ``` #### Q: I'm having issues importing SDK modules in Next.js 13 with the App Router. What should I do? A: Use the SDK Code Splitting method for imports: ```typescript import Openfort from '@openfort/openfort-js'; ``` #### Q: I'm getting a "Cannot read properties of undefined (reading 'fromMasterSeed')" error in Next.js 12. How do I fix this? A: Add the following to your `next.config.js`: ```javascript const nextConfig = { // ... other next config experimental: { esmExternals: false, }, }; ``` #### Q: I'm getting a "Could not detect network" error on Next.js 14 API endpoint with JsonRpcProvider. How do I fix this? A: You have three options: 1. Downgrade to Next.js 13 2. Upgrade to Ethers v6 3. Use the solution provided [in this GitHub issue discussion](https://github.com/ethers-io/ethers.js/issues/3952#issuecomment-1698913553) ## Using the JS SDKs There are **two** steps to configure Openfort's embedded wallets in your application: * Configure the embedded wallet configuration. * Wait for the signer to be ready. ### Embedded wallet configuration The Openfort SDKs provide a way to configure a non-custodial embedded wallet that can be used to sign transactions and interact with the blockchain. The **`configureEmbeddedSigner`** is the method responsible for setting up and creating the non-custodial wallet. :::code-group ```ts [config.ts] import { ShieldAuthType, RecoveryMethod } from "@openfort/openfort-js"; import openfort from "./openfortConfig"; // This example assumes that the user is `authenticated` async function configure(password: string, auth_token: string) { const chainId = 80002; const shieldAuthentication = { auth: ShieldAuthType.OPENFORT, token: auth_token, }; const recoveryParams = { password: password, recoveryMethod: RecoveryMethod.PASSWORD, }; await openfort.embeddedWallet.configure({ chainId, shieldAuthentication, recoveryParams, }); } ``` ```ts [openfortConfig.ts] import Openfort from "@openfort/openfort-js"; const openfort = new Openfort({ baseConfiguration: { publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY", }, shieldConfiguration: { shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY", shieldEncryptionKey: "YOUR_SHIELD_ENCRYPTION_SHARE", }, }); export default openfort; ``` ::: The `configureEmbeddedSigner` will include a `chainID` parameter, which is the identifier of the blockchain network you want to interact with and create an embedded wallet there. You can read the list of supported chains [here](/docs/configuration/chains). Let's go over the rest of the `shieldAuth` parameters of the `configureEmbeddedSigner` method: | Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | auth | Defines how the `token` parameter is going to be verified. Can be either **openfort** or **custom**, defining if the token is going to be verified by Openfort or by another backend. | | token | The `access_token` or `id_token` that will be used to verify that the user is logged in in your application. | :::tip **Automatic embedded wallet creation**: to offer a frictionless onboarding, enable automatic embedded wallet creation. Learn more about it [how to enable automatic recovery](/docs/products/embedded-wallet/javascript/signer/recovery). ::: ### Waiting for ready When calling `configureEmbeddedSigner`, the SDK will go through a series of states before it is ready to be used. These states are represented by the enum: | State | Description | | ------------------------------------------ | ---------------------------------------------------------------------- | | **0** - NONE | The initial state of the SDK. | | **1** - UNAUTHENTICATED | Before the user is authenticated. | | **2** - EMBEDDED\_SIGINER\_NOT\_CONFIGURED | Before calling the `configureEmbeddedSigner`. | | **3** - CREATING\_ACCOUNT | If no account exists for the current chainID, when it will be created. | | **4** - READY | The embedded wallet is ready to be used | **As a consequence, it's important to wait until the `embeddedState` has finished initializing before you use the embedded wallet**, to ensure that the state you consume is accurate and not stale. To determine whether the Openfort SDK has initialized the embedded wallet, you can call the method `getEmbeddedState` and check if the state is `READY`: :::code-group ```ts [main.ts] import openfort from "./openfortConfig"; const state = openfort.embeddedWallet.getEmbeddedState(); ``` ```ts [openfortConfig.ts] import Openfort from "@openfort/openfort-js"; const openfort = new Openfort({ baseConfiguration: { publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY", }, shieldConfiguration: { shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY", }, }); export default openfort; ``` ::: ## Understanding Wallets ### Embedded wallet A **private key is a signer**—a cryptographic entity capable of signing messages and transactions on behalf of a user. When this private key is created and managed within an app or game, it becomes what we call an **embedded wallet**. At Openfort, embedded wallets are self-custodial by design. These wallets are seamlessly integrated into your application and don't require an external wallet client like a browser extension or mobile app. This makes them ideal for users who don't already have a wallet or prefer not to connect an external one. You can choose exactly when to provision an embedded wallets for your user and can interact with it through Openfort's signature and transaction APIs, or through libraries like `wagmi`, `viem`, or any other tooling that supports the EIP-1193 provider interface. ### Wallet types: EOAs and Smart Accounts In the context of blockchains, there are two main types of wallets: * **Externally Owned Accounts (EOAs)**: These are the standard accounts controlled directly by a private key. EOAs are simple and efficient, but limited in terms of programmability. * **Smart Accounts (aka Smart Wallets)**: These are onchain smart contracts that implement advanced logic for how assets are managed and transactions are authorized. Every embedded wallet created by Openfort is paired with a **smart wallet**, offering users a modern, programmable onchain experience from day one. ### Smart wallets and ERC-4337 Smart wallets provisioned through Openfort are [ERC-4337-compatible](https://www.erc4337.io/) smart contract accounts. This means they support **account abstraction**—a standard that enables: * **Gas sponsorship** via paymasters * **Transaction batching** and session signing * **Permission delegation** and modular wallet logic ERC-4337 introduces a new infrastructure layer—**entry points, bundlers, and paymasters**—which abstracts complex wallet logic from the base protocol, allowing for highly customizable, user-friendly wallets. With Openfort, you don't need to manage any of this infrastructure manually. We handle signer creation, smart wallet deployment, and integration with the ERC-4337 stack automatically. ### EOAs + ERC-7702: Upgrading the basic wallet Thanks to the new [ERC-7702](https://eips.ethereum.org/EIPS/eip-7702) standard, EOAs can now be **temporarily upgraded into smart EOAs**, enabling many of the same benefits as smart wallets—including: * Gasless transactions * Batching * Custom validation logic The key difference is that with ERC-7702, the EOA remains in control and no smart contract is permanently deployed. However, **key rotation is not supported** under 7702, unlike fully programmable smart wallets. ### JavaScript Implementation In JavaScript applications, you can interact with embedded wallets through Openfort's signature and transaction APIs, or through popular web3 libraries that support the EIP-1193 provider interface. ```typescript import { Openfort } from '@openfort/openfort-js' const openfort = new Openfort({ baseConfiguration: { publishableKey: "YOUR_PUBLISHABLE_KEY" }, shieldConfiguration: { shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY" } }) // Get EIP-1193 provider for web3 library integration const provider = await openfort.embeddedWallet.getEthereumProvider() ``` ### Browser-Specific Features JavaScript embedded wallets in web browsers provide several unique capabilities: #### EIP-1193 Provider Interface Direct access to the standard Ethereum provider interface for seamless integration with existing web3 tooling. ```typescript // Use with any EIP-1193 compatible library const accounts = await provider.request({ method: 'eth_accounts' }) const chainId = await provider.request({ method: 'eth_chainId' }) ``` #### Web3 Library Integration Seamless integration with popular web3 libraries: * **Wagmi**: React hooks for Ethereum * **Viem**: TypeScript interface for Ethereum * **Ethers**: Complete Ethereum library ```typescript // Example with Viem import { createWalletClient, custom } from 'viem' const walletClient = createWalletClient({ transport: custom(provider) }) ``` ## React SDK We are excited to announce the **React SDK** for Openfort Embedded Wallet, which allows you to easily integrate Openfort's wallet solutions into your React applications. This SDK provides a simple and efficient way to manage user authentication, wallet interactions, and blockchain transactions. ### 🚧 Work in progress 🚧 Stay tuned for updates as we continue to enhance the SDK with new features and improvements. As for now, you can use the [JavaScript SDK](/docs/products/embedded-wallet/javascript) to integrate Openfort's wallet solutions into your React applications. The JavaScript SDK is fully compatible with React and provides all the necessary functionalities to manage user authentication, wallet interactions, and blockchain transactions. ## Build with React Native ::::steps ### Install Required Dependencies Install the latest version of the [Openfort React Native SDK](https://www.npmjs.com/package/@openfort/react-native) and its required dependencies: ```sh [terminal] # Install Openfort React Native SDK yarn add @openfort/react-native @openfort/openfort-js # Install required dependencies yarn add buffer react-native-crypto react-native-get-random-values react-native-randombytes stream-browserify react-native-mmkv ``` ### Configure Metro Create or update your `metro.config.js` to include the necessary Node.js module shims: ```javascript // metro.config.js const { getDefaultConfig } = require('expo/metro-config'); module.exports = (() => { const config = getDefaultConfig(__dirname); // Add shims for Node.js modules config.resolver.extraNodeModules = { crypto: require.resolve('react-native-crypto'), stream: require.resolve('stream-browserify'), buffer: require.resolve('buffer'), }; return config; })(); ``` ### Set up auth providers Navigate to the **auth providers** page on the [Openfort dashboard](https://dashboard.openfort.io) by selecting your project and clicking Auth Providers Methods in the side bar in the [players page](https://dashboard.openfort.io/players). Select the account types you'd like users to be able to login with. ### Get your Openfort keys From the [Openfort Dashboard](https://dashboard.openfort.io), select your desired app and navigate to the [developers page](https://dashboard.openfort.io/developers/configuration/api-keys). You'll need: * **Publishable Key**: Safe to expose in client-side environment * **Secret Key**: Must be kept secure and used only server-side * **Shield Keys**: Required for non-custodial wallets * Create Shield keys in the Shield section * Securely store the encryption share shown in the one-time popup * You'll get both a Publishable and Secret Shield key ### Configure your app #### Import Polyfills First, import the Openfort React Native polyfills at the top of your app: ```typescript import "@openfort/react-native/polyfills"; ``` #### Set up the WebView Add the Safe area provider and hooks to your app's root layout: ```typescript // app/_layout.tsx import { Redirect, Slot } from "expo-router"; import React from "react"; import { ConsoleProvider } from "../hooks/useConsole"; import { OpenfortProvider } from "../hooks/useOpenfort"; import { SafeAreaProvider } from "react-native-safe-area-context"; export default function RootLayout() { return ( ); } ``` #### Initialize Openfort Configure the Openfort SDK with your keys: ```typescript import { Openfort } from "@openfort/react-native"; const openfort = new Openfort({ baseConfiguration: { publishableKey: "YOUR_OPENFORT_PUBLISHABLE_KEY", }, shieldConfiguration: { shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY", } }); ``` :::note[Important] Because we use `mmkv` storage, expo-go will not work. Use `expo run:ios` or `expo run:android` to run your app. ::: :::: ### Next Steps Now that you've configured your app, you can use `openfort` throughout to access the Openfort SDK. Check out our [starter repo](https://github.com/openfort-xyz/react-native-auth-sample) to see a complete integration. ## Overview Unity The [Openfort Unity SDK](https://github.com/openfort-xyz/openfort-csharp-unity) is a Unity library client for Openfort that allows you to add secure authentication, non-custodial embedded wallets, and powerful UX infrastructure into your game. ### Features | Feature | Status | | ----------------------------------------- | ------ | | Sign in with email | ✅ | | Sign in with OAuth | ✅ | | Sign in with guest mode | ✅ | | Sign in with Third-Party Auth | ✅ | | Embedded wallet creation | ✅ | | Embedded wallet recovery | ✅ | | Embedded wallet signatures & transactions | ✅ | | Native smart wallets | ✅ | | Gas sponsorship | ✅ | | Session keys | ✅ | ### Supported platforms * Windows (64-bit) * macOS (minimum version 12.5) * Android (minimum version 5.1) * iOS (minimum version 15.2)
**SUPPORTED UNITY VERSIONS** * Unity 2021.3 or newer for Windows, macOS, Android and iOS * Unity 2019.4 or newer for macOS, Android, and iOS. Windows isn't supported on Unity versions from 2019.4 up through 2021.2.
**TARGET PLATFORM VS UNITY EDITOR PLATFORM** We have added compilation flags to the Unity SDK to ensure that specific Unity editors can only build certain platform targets. Please note that the table below indicates which editor you can use to build a platform target, but it does not determine whether you can run the SDK in that editor. For example, the SDK allows you to build iOS games using a macOS Unity Editor, but you cannot use the Windows Unity Editor. | Target Platform | Windows Unity Editor | macOS Unity Editor | | --------------- | :------------------: | :----------------: | | Windows | ✅ | ❌ | | macOS | ❌ | ✅ | | Android | ✅ | ✅ | | iOS | ❌ | ✅ | | Web | ✅ | ✅ |
### Get started Check out these popular guides to get started. #### [Setting up the SDK](/docs/products/embedded-wallet/unity/quickstart) Setting up and importing a project #### [WebGL](/docs/products/embedded-wallet/unity/webgl) Using embedded wallets with WebGL #### [Embedded wallet](/docs/products/embedded-wallet/unity/signer/recovery) Create an embedded wallet ### Templates Check out these popular samples to get started. #### [Unity Sample](https://github.com/openfort-xyz/openfort-csharp-unity/tree/main/sample) Check our latest Unity sample ## Unity Quickstart ### Prerequisites The Unity SDK requires: * [UniTask](https://github.com/Cysharp/UniTask) package (version 2.3.3) * Installation of git-lfs from [git-lfs.github.com](https://git-lfs.github.com/) ::::steps ### 1. Install the Openfort SDK There are two ways to install the SDK: #### UPM Since .dll files are stored on Git Large File Storage, you must download and install git-lfs from [here](https://git-lfs.github.com/). 1. Open the Package Manager 2. Click the add + button and select "Add package from git URL..." Enter [https://github.com/openfort-xyz/openfort-csharp-unity.git?path=/src/Packages/OpenfortSDK](https://github.com/openfort-xyz/openfort-csharp-unity.git?path=/src/Packages/OpenfortSDK) and click 'Add' #### manifest.json Since .dll files are stored on Git Large File Storage, you must download and install git-lfs from [here](https://git-lfs.github.com/). 1. Open your project's Packages/manifest.json file 2. Add "com.openfort.sdk": "[https://github.com/openfort-xyz/openfort-csharp-unity.git?path=/src/Packages/OpenfortSDK](https://github.com/openfort-xyz/openfort-csharp-unity.git?path=/src/Packages/OpenfortSDK)" in the dependencies block ### 2. Set your auth providers 1. Navigate to the **auth providers** page on the [Openfort dashboard](https://dashboard.openfort.io) 2. Click Auth providers Methods in the side bar in the [players page](https://dashboard.openfort.io/players) 3. Configure the methods you want users to be able to login with ### 3. Get your [API keys](/docs/configuration/api-keys) In the [API keys](https://dashboard.openfort.io/developers/configuration/api-keys) section, you'll find: * **Publishable Key**: Safe to expose in client-side environment * **Secret Key**: Must be kept secure and used only server-side To generate non-custodial wallets: 1. Scroll to the Shield section and click **Create Shield keys** 2. **Store the encryption share** safely when it appears (you'll only see it once) 3. You'll receive: * **Shield Publishable Key**: Safe for client-side use * **Shield Secret Key**: Keep secure, server-side only ### 4. Initialize Openfort in your Unity project Create a new script to manage the Openfort integration: ```csharp using Openfort.OpenfortSDK; using Openfort.OpenfortSDK.Model; public class OpenfortManager: MonoBehaviour { private OpenfortSDK openfort; const string PublishableKey = "YOUR_OPENFORT_PUBLISHABLE_KEY"; const string ShieldApiKey = "YOUR_SHIELD_PUBLISHABLE_KEY"; const string ShieldEncryptionShare = "YOUR_SHIELD_ENC_SHARE"; private async void Start() { openfort = await OpenfortSDK.Init(PublishableKey, ShieldApiKey, ShieldEncryptionShare); } } ``` :::tip If you're developing for WebGL, check out the additional setup steps in the Unity WebGL documentation. ::: ### 5. You're ready to build! With Openfort configured in your Unity project, you can now: * [Implement user authentication](/docs/products/embedded-wallet/unity/auth/email) * [Handle signatures](/docs/products/embedded-wallet/unity/signer/sign-messages) For a complete example of Openfort integration in Unity, check out our [sample projects](https://github.com/openfort-xyz/openfort-csharp-unity/tree/main/sample). :::: ## Unity SDK Troubleshooting ### Platform and Compatibility #### Q: Can I test the SDK using the Unity Editor for Android and iOS? A: Yes, you can test the SDK using the Unity Editor for Android and iOS on both Mac and Windows. However, be aware that: * Native Android and iOS WebViews cannot run in the editor. * The macOS WebView is used for the Mac Unity Editor. * The Windows WebView is used for the Windows Unity Editor. For the most accurate testing, we recommend using an actual device or emulator. #### Q: Do you support IL2CPP for Windows? A: Currently, we do not support IL2CPP on the Windows platform. #### Q: I'm getting "Webview is not supported on this platform." How do I fix this? A: This error suggests that the WebView is not properly set up for your target platform. Ensure that you've correctly installed and configured the WebView plugin for your specific platform. If the issue persists, check our documentation for platform-specific setup instructions. ### Installation and Setup #### Q: I'm getting "The type or namespace name 'Shared' does not exist in the namespace 'VoltstroStudios.UnityWebBrowser' (are you missing an assembly reference?)". How do I fix this? A: This error often occurs when large files like `.dll` are not properly downloaded. To resolve this: 1. Download and install git-lfs from [https://git-lfs.com/](https://git-lfs.com/) 2. Clone the repository again or pull the latest changes. If the issue persists, ensure that all project dependencies are correctly installed and that your Unity version is compatible with the SDK. ### Authentication and Login #### Q: Can I log in using a WebView instead of opening the browser? A: The SDK primarily uses the system browser for authentication due to security considerations. However, if you have specific requirements for using a WebView, please contact our support team to discuss potential alternatives. #### Q: Why is the in-app browser used for login on mobile and not a WebView? A: The in-app browser is used for enhanced security, especially for single sign-on (SSO) purposes: * It runs on a separate process from the hosting game, preventing the game from accessing or modifying its content. * It's more resistant to malicious code injection. * WebViews, in contrast, can be more easily controlled by the hosting game, making them potentially less secure for authentication processes. #### Q: On iOS, when using AuthenticateWithOAuth login function, I get an alert asking: "\[My Game] Wants to Use 'openfort.sdk' to Sign in". Can I modify or remove this alert? A: This alert is system-generated by iOS when using `ASWebAuthenticationSession` for secure authentication. Unfortunately, it cannot be removed or modified as it's triggered by the operating system. You can find more information about this in the [Apple Developer Documentation](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession). ### Functionality #### Q: Can I use the Unity SDK for crafting (burn and mint) assets? A: Yes, the SDK supports various blockchain operations, including minting and burning assets. For specific implementation details, please refer to our documentation on asset management or contact our support team for guidance. ### Troubleshooting #### Q: I'm getting "TimeoutException: Exceed Timeout:00:01:00". What does this mean? A: This exception occurs when a function call takes longer than the default timeout of one minute to return a response. To resolve this: 1. Check the bottom of the stack trace or logs to identify the specific function causing the timeout. 2. If needed, you can customize the timeout duration using the `SetCallTimeout` function. 3. Ensure your network connection is stable and that you're not experiencing unusually high latency. ## WebGL Setup Openfort's Unity SDK leverages an iframe to secure the key material for a user's embedded wallet. Given the use of an iframe, we recommend testing builds with Openfort's Unity SDK in the browser, or on a non-WebGL platform in the Unity editor.
Watch this [demo](https://github.com/openfort-xyz/sample-unity-webgl-embedded-signer) of setting up the Openfort SDK in a Unity Project!
To configure settings for your WebGL build to work with Openfort, go to your Project Settings in the Unity editor. Next, select Player and navigate to WebGL. Set the following values: * **In Resolution and Presentation**, select `openfort`.
unity-webgl-template
* In **Other Settings/Optimization, managed stripping level** to **minimal**
unity-stripping
## Backend wallets Backend wallets are externally-owned accounts that your application controls to manage on-chain operations. They act as trusted intermediaries that can handle various automated tasks and workflows in your application. :::tip As a developer, you can also use smart wallet as a backend wallet (some benefits include the batching of transactions), please refer to our guide on how to create [smart wallets](/docs/products/embedded-wallet/javascript/smart-wallet) and use as backend wallets. ::: ### Why use backend wallets? Backend wallets enable your application to: * Automate onchain operations and signatures * Manage transactions programmatically * Handle user interactions on behalf of the user * Control asset or token distribution and inventory ## Using backend wallets ### Create wallet Backend wallets can be created through our dashboard or API. You can either create a new wallet or add an existing one. #### Using the Dashboard Navigate to the [Openfort dashboard](https://dashboard.openfort.io) and create a new developer account. #### Using the API You can create or add backend wallets through our API: :::code-group ```ts [Node] const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const settings = await openfort.settings.createDeveloperAccount() ``` ```bash [curl] curl https://api.openfort.io/v1/settings/developer_accounts \ -u "$YOUR_SECRET_KEY:" \ -d address="0x..." \ -d signature="0x..." \ -d name="My Backend Wallet" ``` ::: ### Transactions To execute a transaction from your backend wallet: :::code-group ```ts [Node] const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const transactionintents = await openfort.transactionIntents.create({ account: 'dac_...', // Your backend wallet ID chainId: 80002, policy: 'pol_...', // Optional: Policy ID for gas sponsorship optimistic: true, interactions: { contract: 'con_....', functionName: 'transfer', functionArgs: ['recipient_address', 'amount'] } }) ``` ```bash [curl] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d chainId=80002 \ -d 'account=dac_...' \ -d optimistic=true \ -d 'interactions[0][contract]=con_....' \ -d 'interactions[0][functionName]=transfer' \ -d 'interactions[0][functionArgs][0]=recipient_address' \ -d 'interactions[0][functionArgs][1]=amount' ``` ::: :::info For gas sponsorship, make sure to set up appropriate policies with account\_functions rules. Learn more about creating policy sponsors in the Openfort dashboard. ::: #### Checking transaction status You can monitor the status of transactions through our webhook system or by querying the API: :::code-group ```ts [Node] const transactionIntent = await openfort.transactionIntents.retrieve('tin_...'); console.log(transactionIntent.status); ``` ```bash [curl] curl https://api.openfort.io/v1/transaction_intents/tin_... \ -u "$YOUR_SECRET_KEY:" ``` ::: ### Common Use Cases #### Minting NFTs with a backend wallet In this example, we'll use a backend wallet to mint an NFT to a player's account: :::code-group ```ts [Node] const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const transactionintents = await openfort.transactionIntents.create({ account: 'dac_...', // Your backend wallet ID chainId: 80002, policy: 'pol_...', // Policy for gas sponsorship optimistic: true, interactions: { contract: 'con_....', functionName: 'mint', functionArgs: ['pla_...'] // Player ID receiving the NFT } }) ``` ```bash [curl] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d chainId=80002 \ -d 'account=dac_...' \ -d optimistic=true \ -d 'interactions[0][contract]=con_....' \ -d 'interactions[0][functionName]=mint' \ -d 'interactions[0][functionArgs][0]=pla_...' ``` ::: #### Transferring Assets Here's how to transfer assets from your backend wallet to another account: :::code-group ```ts [Node] const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const transactionintents = await openfort.transactionIntents.create({ account: 'dac_...', // Your backend wallet ID chainId: 80002, policy: 'pol_...', // Policy for gas sponsorship optimistic: true, interactions: { contract: 'con_....', functionName: 'transfer', functionArgs: ['pla_...', '1000000000000000000'] // Player ID and amount (1 token) } }) ``` ```bash [curl] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d chainId=80002 \ -d 'account=dac_...' \ -d optimistic=true \ -d 'interactions[0][contract]=con_....' \ -d 'interactions[0][functionName]=transfer' \ -d 'interactions[0][functionArgs][0]=pla_...' \ -d 'interactions[0][functionArgs][1]=1000000000000000000' ``` ::: #### Implementing Escrow System This example shows how to implement an escrow system using a backend wallet: 1. First, check the player's NFT inventory: :::code-group ```ts [Node] const Openfort = require('@openfort/openfort-node').default; const openfort = new Openfort(YOUR_SECRET_KEY); const inventory = await openfort.inventories.getPlayerNftInventory({ player: 'pla_...', contract: 'con_...', }) ``` ```bash [curl] curl https://api.openfort.io/v1/accounts/acc_.../inventory/nft \ -u "$YOUR_SECRET_KEY:" \ -d 'contract=con_...' ``` ::: 2. Then, transfer the asset to the escrow wallet: :::code-group ```ts [Node] const transactionintents = await openfort.transactionIntents.create({ chainId: 80002, optimistic: false, player: 'pla_...', // Player ID policy: 'pol_...', // Policy for gas sponsorship interactions: { contract: 'con_...', functionName: "transferFrom", functionArgs: ['pla_...', 'dac_...', '10'], // From player to escrow wallet, token ID 10 } }); ``` ```bash [curl] curl https://api.openfort.io/v1/transaction_intents \ -u "$YOUR_SECRET_KEY:" \ -d chainId=80002 \ -d optimistic=false \ -d 'player=pla_...' \ -d 'policy=pol_...' \ -d 'interactions[0][contract]=con_...' \ -d 'interactions[0][functionName]=transferFrom' \ -d 'interactions[0][functionArgs][0]=pla_...' \ -d 'interactions[0][functionArgs][1]=dac_...' \ -d 'interactions[0][functionArgs][2]=10' ``` ::: :::info To keep your application in sync with blockchain events, Openfort provides [webhooks](/docs/configuration/webhooks) for various events including transaction status changes and asset transfers. ::: ## Branding - Wallet SDK
ecosystem-rainbow-kit
When instantiating the **Client SDK**, you can pass in the `appearance` object to customize the appearance of the wallet. For wallet discovery, it will use [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) to embedded your branding. The `appearance` object has the following properties: * **icon**: a data url schema, compliant with [RFC-2397](https://www.rfc-editor.org/rfc/rfc2397). * **logo**: a [URI](https://www.rfc-editor.org/rfc/rfc3986) pointing to an image. The image SHOULD be a square with 96x96px minimum resolution. * **name**: a human-readable local alias of the Wallet Provider to be displayed to the user on the DApp. (e.g. `Example Wallet Extension` or `Awesome Example Wallet`) * **reverseDomainNameSystem**: the Wallet MUST supply the `rdns` property which is intended to be a domain name from the Domain Name System in reverse syntax ordering such as `com.example.subdomain`. It’s up to the Wallet to determine the domain name they wish to use, but it’s generally expected the identifier will remain the same throughout the development of the Wallet. It’s also worth noting that similar to a user agent string in browsers, there are times where the supplied value could be unknown, invalid, incorrect, or attempt to imitate a different Wallet. Therefore, the DApp SHOULD be able to handle these failure cases with minimal degradation to the functionality of the DApp. :::note Getting the ecosystem id - When creating your wallet UI you need to add the ecosystem ID. It can be found in your dashboard at the settings section. ::: Therefore, when creating your wallet SDK, you can pass in the `appearance` object to customize the appearance of the wallet. ```ts index.ts // Set your ecosystem id. Remember to switch to your live secret key in production. // See your ecosystem id here: https://dashboard.openfort.io/settings/project/overview import { AppMetadata, Client } from '@openfort/ecosystem-js/client' class EcosystemWallet extends Client { constructor(appMetadata?: AppMetadata) { super({ // Optional App Info appMetadata: appMetadata, baseConfig: { // URL where the UI wallet is hosted ecosystemWalletDomain: 'https://wallet.ecosystem.com', windowStrategy: 'iframe', // or 'popup'. Rendering strategy the wallet UI. }, appearance: { icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFwF52RAAAACXBIWXMAAAsTAAALEwEAmpwYAAABpElEQVQ4jZ2Sv0vDUBTFf4f9Cg', logo: 'https://www.example.com/logo.png', name: 'Global Wallet', reverseDomainNameSystem: 'com.example.ecosystem.wallet' } }); // Use a Proxy to allow for new method additions return new Proxy(this, { get: (target, prop) => { if (prop in target) { const value = target[prop as keyof EcosystemWallet]; return typeof value === 'function' ? value.bind(target) : value; } return undefined; } }); } setPolicy(options?: { policy?: string; }): void { return super.setPolicy(options); } } export default EcosystemWallet; ``` You can check all the available components in the [Client SDK reference](#TODO). ## Ecosystem dashboard configuration :::note Creating your own global wallet is an **enterprise feature**. To enable it, please [contact us](https://t.me/joalavedra) ::: Use the **customization** page of the dashboard to configure your ecosystem's brand settings, including name, logo, accent color, and legal policies.
whitelabel_dashboard
### Customization options | Customization | Description | | ------------- | ----------------------------------------------------------------- | | Domain | Choose where to host your dashboard | | Name | Ecosystem name as you'd like to present it to users | | Logo | Ecosystem logo | | Brand color | Accent color for your ecosystem | | Legal | Used to set your own terms & conditions, and your privacy policy. | | SDK links | Client SDKs | | Example links | Template examples using the ecosystem SDKs | import { VideoSnippet } from "@/components/VideoSnippet.tsx"; ## Quickstart your global wallet \[Build and launch your custom global wallet] :::tip If you're looking to use an existing global wallet SDK, please refer to [use a global wallet](/docs/products/cross-app-wallet/usage/web-app-wagmi). ::: The [**Ecosystem SDK**](https://www.npmjs.com/package/@openfort/ecosystem-js) is the easiest way to create your global wallet. It comes with a [code-splitting environment](https://developer.mozilla.org/en-US/docs/Glossary/Code_splitting), all the necessary tools to make **your wallet SDK** a reality. It is the easier way to create your own global wallet. The Ecosystem SDK has **two main parts**: 1. `@openfort/ecosystem-js/client`: The **Client SDK** that you will use to create your wallet SDK. 2. `@openfort/ecosystem-js/core`: The **Core SDK** that you will use to create your wallet UI. Fort also includes a React specific package `@openfort/ecosystem-js/react` that you can use to get started faster. When you finish this quickstart, this is what you will have: ### Requirements Your project will need some specific configuration to enable code splitting: * [Typescript version 5.0.2](https://github.com/microsoft/TypeScript/releases/tag/v5.0.2) or higher. * TS Config needs to have `compilerOptions.moduleResolution` set to `bundler`. * Your code editor and project should have the same version of Typescript. We'll be needed 2 main projects to create your **Ecosystem SDK**: * **Wallet SDK**: This is the SDK that developers will use to integrate your wallet into their applications. * **Wallet UI**: This is the UI that users will interact with when using your wallet. ::::steps ### 1. Install the ecosystem SDK Under your **`wallet SDK folder`**, install the latest version of [Ecosystem SDK](https://www.npmjs.com/package/@openfort/ecosystem-js) using your package manager of choice: :::info You will need to install `@openfort/ecosystem-js` at both your wallet SDK and wallet UI projects. ::: :::code-group ```sh [npm] npm install @openfort/ecosystem-js ``` ```sh [yarn] yarn add @openfort/ecosystem-js ``` ```sh [pnpm] pnpm install @openfort/ecosystem-js ``` ::: ### 2. Creating your wallet SDK This is how developers will make your wallet available in their application.
fort-architecture-1
The easiest way to get started is to create a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) object in order to map the already provided functionality of the **Client SDK**. You can nevertheless change or add new methods to fit your needs. :::code-group ```tsx [main.ts] import { AppMetadata, Client } from "@openfort/ecosystem-js/client"; class EcosystemWallet extends Client { // optional constructor arguments developers can pass to the wallet for UI customization (e.g. logo, name, etc.) https://openfort.io/reference/ecosystem-js/interfaces/client.AppMetadata.html constructor(appMetadata?: AppMetadata) { super({ baseConfig: { // URL where the wallet UI is hosted: ecosystemWalletDomain: 'https://id.sample.openfort.io', windowStrategy: 'iframe', // or 'popup'. Rendering strategy the wallet UI. }, appMetadata, // Optional App Info appearance: { icon: 'data:image/....', // a data url schema, compliant with RFC-2397. logo: 'https://purple-magnificent-bat-958.mypinata.cloud/ipfs/QmfQrh2BiCzugFauYF9Weu9SFddsVh9qV82uw43cxH8UDV', // a URI pointing to an image. The image SHOULD be a square with 96x96px minimum resolution. name: 'Ecosystem Wallet Name', // human-readable local alias of the Wallet Provider reverseDomainNameSystem: 'com.openfort.ecosystem.wallet', // domain name from the Domain Name System in reverse syntax } }); return new Proxy(this, { get: (target, prop) => { if (prop in target) { const value = target[prop as keyof EcosystemWallet]; return typeof value === 'function' ? value.bind(target) : value; } return undefined; } }); } } export default EcosystemWallet; ``` ```json [package.json] { "name": "@ecosystem/wallet", "version": "0.0.1", "main": "dist/index.js", "module": "dist/index.mjs", "types": "dist/index.d.ts", "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.js", "types": "./dist/index.d.ts" } }, "scripts": { "build": "tsup src/index.ts --format cjs,esm --dts", "prepublishOnly": "npm run build" }, "keywords": [], "author": "Openfort (https://www.openfort.io)", "devDependencies": { "@types/node": "^20.4.5", "tsup": "^7.1.0", "typescript": "^5.6.2" }, "dependencies": { "@openfort/ecosystem-js": "0.1.8" }, "files": [ "dist" ] } ``` ```json [tsconfig.json] { "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "lib": ["ES2022", "DOM"], "declaration": true, "declarationMap": true, "sourceMap": true, "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.spec.ts"] } ``` ::: You're all set! By running the `build` script you'll get a `dist` folder with the compiled code. You can now publish your package to [npm](https://www.npmjs.com/) and share it with the world. You can check all the available client methods in the [Client SDK reference](#TODO). #### [Wallet SDK Sample](https://github.com/openfort-xyz/ecosystem-sample/tree/main/wallet-sdk) Complete sample including auth, transactions and session keys. ### 3. Choose your signer and account system The Ecosystem SDK is agnostic to the key management solution you choose to use. Openfort also offers an embedded wallet solution that you can use to create non-custodial wallets, referred to as `OpenfortProvider` in the Ecosystem SDK. To learn more about the available signer solutions, check out the [choose your signer](/docs/products/cross-app-wallet/setup/signers) guide. In this guide, we'll be using the `OpenfortProvider` as the key management and account system. You can use any other wallet SDK you prefer. #### 3.1. Setting up Openfort signer Navigate to the **auth providers** page on the [Openfort dashboard](https://dashboard.openfort.io) by selecting your project and then clicking Auth providers Methods in the side bar in the [players page](https://dashboard.openfort.io/players). Select the account types you'd like users to be able to login with. For more information on how to enable social logins, check out the [dashboard docs](/docs/configuration/social-login). From the [Openfort Dashboard](https://dashboard.openfort.io) for select your desired app, navigate to the [developers page](https://dashboard.openfort.io/developers/configuration/api-keys) in the top bar. On the de tab, find the API keys section. Get your Openfort API keys, you will need it in the next step. You will find two keys: * **Publishable Key**: This value can be safely exposed in a client-side environment. * **Secret Key**: This value should never be exposed in a client-side environment. It should be kept secure and used only in a server-side environment. Learn more on how to use it in the [server-side guide](/docs/products/server/wallet). You can [further secure it](/docs/configuration/api-keys) for production applications. To generate non custodial wallets, you will need to create a Shield instance. At the [API keys page](https://dashboard.openfort.io/developers/configuration/api-keys), scroll down to the Shield section and click on the **Create Shield keys** button. A **one time pop-up** will appear with a variable called **encryption share**. Its very important that you store it safely. You will not be able to see it again. Then, in your page, you will see two Shield keys: * **Publishable Key**: This value can be safely exposed in a client-side environment. * **Secret Key**: This value should never be exposed in a client-side environment. ### 4. Creating your wallet UI The **wallet UI** is how information is shown to wallet users. Under your **`wallet UI folder`**, install the latest version of [Ecosystem SDK](https://www.npmjs.com/package/@openfort/ecosystem-js) using your package manager of choice. Remember to comply with the requirements to benefit from code splitting. :::info To make things more concrete, we'll be using `OpenfortProvider` as the key management and account system. You can use any other wallet SDK you prefer. :::
fort-architecture-2
`@openfort/ecosystem-js` SDK comes with a set of pre-built **React** components that you can use to create your wallet UI. To learn how to customize it further, head to the [UI screens guide](/docs/products/cross-app-wallet/setup/react/wallet-ui). In your project, import the **EcosystemProvider** component and wrap your app with it. Concretely, the `EcosystemProvider` must wrap any component or page that will use the Ecosystem SDK in your react app. It is generally recommended to render it as close to the root of your application as possible. For example, in a [NextJS](https://nextjs.org/) or [Create React App](https://create-react-app.dev/) project, you may wrap your components like so: #### Install the required dependencies :::code-group ```sh [npm] npm install @openfort/ecosystem-js wagmi viem@2.x @tanstack/react-query ``` ```sh [yarn] yarn add @openfort/ecosystem-js wagmi viem@2.x @tanstack/react-query ``` ::: * TypeScript is optional, but highly recommended. #### Implementation :::info Getting the ecosystem id: When creating your **wallet UI** you need to add the *ecosystem ID*. It can be found in your dashboard at the [settings section](https://dashboard.openfort.io/settings/project/overview). ::: ##### Create React App :::code-group ```tsx [main.tsx] // Set your publishable key, shield publishable key and ecosystem id. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.openfort.io/developers/configuration/api-keys // See your ecosystem ID here: https://dashboard.openfort.io/settings/project/overview import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import { BrowserRouter, useNavigate } from 'react-router-dom'; import { WagmiProvider } from 'wagmi'; import { QueryClientProvider } from '@tanstack/react-query'; import * as Wagmi from './lib/Wagmi'; import * as Query from './lib/Query' import { EcosystemProvider, OpenfortProvider, RecoveryMethod } from '@openfort/ecosystem-js/react'; async function getShieldSession(accessToken:string):Promise { // When using AUTOMATIC embedded wallet recovery, an encryption session is required. // Sample encryption session generation backend: https://github.com/openfort-xyz/ecosystem-sample/tree/main/wallet-ui/backend const response = await fetch(`${process.env.REACT_APP_BACKEND_URL}/api/protected-create-encryption-session`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` } }); if (!response.ok) { throw new Error('Failed to fetch shield session'); } const data = await response.json(); return data.session; } const ProtectedRoute = ({ component, ...args }: any) => { const Component = withAuthenticationRequired(component, { onRedirecting: () => , }); return ; }; export default function Providers({children}: {children: React.ReactNode}) { const nav = useNavigate() return ( { nav({ pathname: appState?.to, search: appState?.search }) }} theme='midnight' logoUrl='https://purple-magnificent-bat-958.mypinata.cloud/ipfs/QmfQrh2BiCzugFauYF9Weu9SFddsVh9qV82uw43cxH8UDV' > { return nav(appState?.returnTo || window.location.pathname); }} overrides={{}} publishableKey={process.env.REACT_APP_OPENFORT_PUBLIC_KEY} // To choose your recovery method, set the recoveryMethod to either 'AUTOMATIC' or 'PASSWORD' // Learn more about configure embedded signers: https://openfort.io/docs/products/embedded-wallet/react/kit/wallets/ embeddedSignerConfiguration={ { shieldPublishableKey: process.env.REACT_APP_SHIELD_PUBLIC_KEY, recoveryMethod: RecoveryMethod.AUTOMATIC, // If you're using AUTOMATIC recovery, you need to provide an encryption session. // If you're only using PASSWORD recovery, you can remove this function. getEncryptionSessionFn(getAccessToken) { return getShieldSession(getAccessToken); } } } > {children} ); } ``` ```tsx [App.tsx] import { WalletGrantPermissions, WalletSendCalls, EthRequestAccounts, EthSendTransaction, EthSignTypedDataV4, PersonalSign, WalletShowCalls, withAuthenticationRequired, Settings, UnsupportedMethod, LoginMethods, Recover } from '@openfort/ecosystem-js/react'; import { Route, Routes } from 'react-router-dom'; import Loading from './Loading'; const ProtectedRoute = ({ component, ...args }: any) => { const Component = withAuthenticationRequired(component, { onRedirecting: () => , }); return ; }; function App() { return ( {/* Required endpoints for EIP-1193 methods */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> {/* OpenfortProvider specific methods for EmbeddedSigner recovery (password based) and authentication */} } /> } /> ); } export default App; ``` ```tsx [Loading.tsx] import { Layout } from '@openfort/ecosystem-js/react'; import React from 'react'; const Loading: React.FC = () => { return ( // Not how its wrapped in the Layout component, to ensure the styles are applied {/* Adding custom styles */}