# Passkey (WebAuthn) signer

:::info
If you want to check out a live sample app using Passkey signers, check out the [live demo with RapidSafe](https://rapidsafe.sample.openfort.io/).
:::

### Install the required dependencies

In this sample, we'll be using the [Safe smart account](https://safe.global), with [Safe7579 adapter](https://github.com/rhinestonewtf/safe7579) to add support for **Passkeys** and **session keys**.

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

```sh [pnpm]
pnpm add @openfort/ecosystem-js wagmi@2.x viem@2.x @tanstack/react-query
```

:::

* TypeScript is optional, but highly recommended.

### Implementation

:::info
Please refer to the complete code for the [Wallet UI](https://github.com/openfort-xyz/ecosystem-sample-safe-passkeys/tree/main/wallet-ui/frontend) if you want to see the full implementation of the Passkey signer.
:::

#### Create React App

:::code-group

```tsx [index.tsx]
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 } from '@openfort/ecosystem-js/react';

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='RapidSafe'
    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}
      >
        <RapidsafeProvider
          debugMode={true}
          onRedirectCallback={(appState) => {
            return nav(appState?.returnTo || window.location.pathname);
          }}
          >
          {children}
        </RapidsafeProvider>
      </QueryClientProvider>
    </WagmiProvider>
  </EcosystemProvider>
);
}
```

```tsx [App.tsx]
import { 
  WalletGrantPermissions,
  WalletSendCalls,
  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';
import EthRequestAccounts from './routes/sign/EthRequestAccounts';

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='/sign/settings' element={<ProtectedRoute component={Settings} />} />
      <Route path='/sign/' element={<ProtectedRoute component={Loading} />} />
      <Route path='*' element={<UnsupportedMethod />} />
    </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 { rapidsafe, RapidsafeParameters } from '../providers/RapidsafeProvider/Connector';
import { http, createConfig, createStorage } from 'wagmi'
import { baseSepolia } from 'wagmi/chains'

const RapidsafeConfig: RapidsafeParameters = {
  rpId: window.location.hostname
};
export const config = createConfig({
  chains: [baseSepolia],
  connectors: [
    rapidsafe(RapidsafeConfig),
  ],
  multiInjectedProviderDiscovery: false,
  storage: createStorage({ storage: localStorage }),
  transports: {
    [baseSepolia.id]: http()
  },
})
export type Chain = (typeof config.chains)[number]
export type ChainId = Chain['id']
export const getChainConfig = (chainId: ChainId) =>
  config.chains.find((c) => c.id === chainId)

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`).

## Sample

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