# 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:

<VideoSnippet title="Video" src="ZkknZ2n8Cbw" />

## 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

::::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 add @openfort/ecosystem-js
```

:::

## 2. Creating your wallet SDK

This is how developers will make your wallet available in their application.

<div align="center">
  <img alt="fort-architecture-1" src="https://www.openfort.io/images/blog/fort_framework_step_1_a97cc606f2.svg" width="80%" height="80%" />
</div>

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.

<HoverCardLayout>
  <HoverCardLink description="Complete sample including auth, transactions and session keys" href="https://github.com/openfort-xyz/ecosystem-sample/tree/main/wallet-sdk" title="Wallet SDK Sample" subtitle="GitHub repository" icon={Code} color="#8B5CF6" />
</HoverCardLayout>

## 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 [users 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/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). 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/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.
:::

<div align="center">
  <img alt="fort-architecture-2" src="https://www.openfort.io/images/blog/fort_framework_step_2_5ff477cb98.svg" width="80%" height="80%" />
</div>

`@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@2.x viem@2.x @tanstack/react-query
```

```sh [yarn]
yarn add @openfort/ecosystem-js wagmi@2.x 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/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<string> {
  // 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: () => <Loading />,
  });
  return <Component {...args} />;
};

export default function Providers({children}: {children: React.ReactNode}) {
const nav = useNavigate()
return (
  <EcosystemProvider
    appName='Rapidfire ID'
    navigateTo={(appState) => {
      nav({
        pathname: appState?.to,
        search: appState?.search
      })
    }}
    theme='midnight'
    logoUrl='https://purple-magnificent-bat-958.mypinata.cloud/ipfs/QmfQrh2BiCzugFauYF9Weu9SFddsVh9qV82uw43cxH8UDV'
  >
    <WagmiProvider config={Wagmi.config}>
      <QueryClientProvider
        client={Query.client}
      >
        <OpenfortProvider
          // If you're using third party authentication with Openfort (i.e. your own auth or providers like Firebase), set thirdPartyAuthentication to true.
          thirdPartyAuthentication={false}
          ecosystemId={process.env.REACT_APP_OPENFORT_ECOSYSTEM_ID}
          onRedirectCallback={(appState) => {
            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/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}
        </OpenfortProvider>
      </QueryClientProvider>
    </WagmiProvider>
  </EcosystemProvider>
);
}

```

```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: () => <Loading />,
  });
  return <Component {...args} />;
};

function App() {
  return (
    <Routes>
      {/* Required endpoints for EIP-1193 methods */}
      <Route path='/sign/personal-sign' element={<ProtectedRoute component={PersonalSign} />} />
      <Route path='/sign/eth-sign-typed-data-v-4' element={<ProtectedRoute component={EthSignTypedDataV4} />} />
      <Route path='/sign/eth-send-transaction' element={<ProtectedRoute component={EthSendTransaction} />} />
      <Route path='/sign/wallet-grant-permissions' element={<ProtectedRoute component={WalletGrantPermissions} />} />
      <Route path='/sign/wallet-show-calls' element={<ProtectedRoute component={WalletShowCalls} />} />
      <Route path='/sign/wallet-send-calls' element={<ProtectedRoute component={WalletSendCalls} />} />
      <Route path='/sign/eth-request-accounts' element={<ProtectedRoute component={EthRequestAccounts} />} />
      <Route path='/settings' element={<ProtectedRoute component={Settings} />} />
      <Route path='/sign' element={<ProtectedRoute component={Loading} />} />
      <Route path='*' element={<UnsupportedMethod />} />
      
      {/* OpenfortProvider specific methods for EmbeddedSigner recovery (password based) and authentication */}
      <Route path='/authenticate' element={<LoginMethods />} />
      <Route path='/recover' element={<Recover />} />
    </Routes>
  );
}

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
    <Layout>
      <svg
        width="20"
        height="20"
        viewBox="0 0 20 20"
        className="animate-spin"
      >
        <circle 
          cx="25" 
          cy="25" 
          r="20" 
          fill="none" 
          stroke="#ffffff" 
          strokeWidth="5"
          strokeLinecap="round"
          strokeDasharray="80"
          strokeDashoffset="60"
        />
      </svg>
      
      {/* Adding custom styles */}
      <style
        dangerouslySetInnerHTML={{
          __html: `
            .animate-spin {
              animation: spin 1s linear infinite;
            }
            
            @keyframes spin {
              from {
                transform: rotate(0deg);
              }
              to {
                transform: rotate(360deg);
              }
            }
          `
        }}
      />
    </Layout>
  );
};

export default Loading;
```

```ts [lib/Wagmi.ts]
// Configure your wagmi instance with the chains you want to support: https://wagmi.sh/react/guides/chain-properties
import { http, createConfig } from 'wagmi'
import { baseSepolia, polygonAmoy, sepolia } from 'wagmi/chains'

export const config = createConfig({
  chains: [polygonAmoy, baseSepolia, sepolia],
  transports: {
    [polygonAmoy.id]: http(),
    [baseSepolia.id]: http(),
    [sepolia.id]: http(),
  },
})

declare module 'wagmi' {
  interface Register {
    config: typeof config
  }
}
```

```ts [lib/Query.ts]
import { QueryClient } from '@tanstack/react-query'

export const client = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 60 * 60 * 24, // 24 hours
      retry: 0,
    },
  },
})
```

:::

### Configure Supported Chains

As you can see above, its required that you configure [Wagmi](https://wagmi.sh) and the chains you plan on enabling for your wallet.

Note that, to enable transaction simulation through asset changes, the **Ecosystem SDK** internally requires the [`eth_simulateV1`](https://github.com/ethereum/execution-apis/pull/484) [JSON-RPC method](https://github.com/ethereum/execution-apis/pull/484), so you will need to provide an RPC endpoint that supports this method (or disable simulation through the EcosystemProvider using `disableTransactionSimulation`).

<HoverCardLayout>
  <HoverCardLink description="Sample wallet UI with all the necessary screens" href="https://github.com/openfort-xyz/ecosystem-sample/tree/main/wallet-ui/frontend" title="Wallet UI Sample" subtitle="GitHub repository" icon={Layout} color="#10B981" />
</HoverCardLayout>
