PoP SDK - React

This is a library that allows you to easily integrate PoP into your React application.

Installation

npm install @anima-protocol/personhood-sdk-react
# or
yarn add @anima-protocol/personhood-sdk-react

Usage

Use the Personhood component

import '@anima-protocol/personhood-sdk-react/style.css'
import { Personhood } from '@anima-protocol/personhood-sdk-react'

const App = () => {
  const sign = async (message) => {
    // Sign the message
    return signature
  }

  const onFinish = ({ info, state }) => {
    // Do something when the user has finished the PoP flow
  }

  return (
    <Personhood
      onFinish={onFinish}
      sessionId="01234567-8901-2345-6789-012345678901"
      signCallback={sign}
      walletAddress="0x..."
    />
  )
}

The onFinish callback will be called when the user has finished the PoP flow or if the session is already validated. Its parameters follows the ones sent from the Personhood API session object.

Examples

Rainbow Kit

The PoP SDK integrates seamlessly with every wallets that implements the personal sign method. Here is an example of how to use it with Rainbow Kit.

// src/index.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import {
  connectorsForWallets,
  darkTheme,
  DisclaimerComponent,
  RainbowKitProvider,
} from '@rainbow-me/rainbowkit'
import { configureChains, createClient, WagmiConfig } from 'wagmi'
import {
  metaMaskWallet,
  ledgerWallet,
  braveWallet,
  walletConnectWallet,
  coinbaseWallet,
  injectedWallet,
} from '@rainbow-me/rainbowkit/wallets'
import { mainnet, polygon } from 'wagmi/chains'
import { publicProvider } from 'wagmi/providers/public'
import '@rainbow-me/rainbowkit/styles.css'

export const { chains, provider, webSocketProvider } = configureChains(
  [mainnet, polygon],
  [publicProvider()]
)

const connectors = connectorsForWallets([
  {
    groupName: 'Recommended',
    wallets: [
      metaMaskWallet({ chains }),
      ledgerWallet({ chains }),
      braveWallet({ chains }),
    ],
  },
  {
    groupName: 'Others',
    wallets: [
      walletConnectWallet({ chains }),
      coinbaseWallet({ appName: 'Anima dApp', chains }),
      injectedWallet({ chains }),
    ],
  },
])

export const wagmiClient = createClient({
  autoConnect: true,
  connectors,
  provider,
  webSocketProvider,
})

const Disclaimer: DisclaimerComponent = () => (
  <p>
    By connecting your wallet, you agree to the{' '}
    <a href="https://terms.anima.io" target="_blank" rel="noreferrer">
      Terms of Service
    </a>
  </p>
)

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <React.StrictMode>
    <WagmiConfig client={wagmiClient}>
      <RainbowKitProvider
        chains={chains}
        theme={darkTheme({ borderRadius: 'small' })}
        appInfo={{
          appName: 'Fairdrop dApp',
          disclaimer: Disclaimer,
        }}
      >
        <App />
      </RainbowKitProvider>
    </WagmiConfig>
  </React.StrictMode>
)
// src/App.tsx

import '@anima-protocol/personhood-sdk-react/style.css'
import { Personhood } from '@anima-protocol/personhood-sdk-react'
import { ConnectButton } from '@rainbow-me/rainbowkit'
import { useCallback } from 'react'
import { useAccount, useSignMessage } from 'wagmi'

const App = () => {
  const { signMessageAsync } = useSignMessage()
  const { address } = useAccount()

  const sign = useCallback(
    (payload: string) => signMessageAsync({ message: payload }),
    [signMessageAsync]
  )

  const shared = useCallback((e: { info: string }) => {
    console.log('shared', e.info)
  }, [])

  return (
    <div
      style={{
        flex: 1,
        display: 'flex',
        height: '100vh',
        backgroundColor: '#333',
        position: 'relative',
        alignItems: 'center',
        justifyContent: 'center',
      }}
    >
      <div style={{ position: 'absolute', right: '8px', top: '8px' }}>
        <ConnectButton />
      </div>
      <Personhood
        onFinish={shared}
        sessionId="01234567-8901-2345-6789-012345678901"
        signCallback={sign}
        walletAddress={address}
      />
    </div>
  )
}

export default App

Wagmi (standalone or with Web3modal)

// src/App.tsx

import '@anima-protocol/personhood-sdk-react/style.css'
import { Personhood } from '@anima-protocol/personhood-sdk-react'
import { useSignMessage, useAccount } from 'wagmi'

export default function Example(): JSX.Element {
  const { address } = useAccount()
  const { signMessageAsync } = useSignMessage()

  const sign = useCallback(
    (payload: string) => signMessageAsync({ message: payload }),
    [signMessageAsync]
  )

  const shared = useCallback((e: { info: string }) => {
    console.log('shared', e.info)
  }, [])

  return (
    <div>
      {address && (
        <Personhood
          onFinish={shared}
          sessionId="01234567-8901-2345-6789-012345678901"
          signCallback={sign}
          walletAddress={address}
        />
      )}
    </div>
  )
}

export default App;

Ether.js

// src/App.tsx

import '@anima-protocol/personhood-sdk-react/style.css'
import { Personhood } from '@anima-protocol/personhood-sdk-react'
import { ethers, JsonRpcSigner } from 'ethers'
import { useEffect, useState } from 'react'

export default function Example(): JSX.Element {
  const [signer, setSigner] = useState<JsonRpcSigner | null>(null)
  const [address, setAddress] = useState<string | null>(null)

  async function connectWallet() {
    let signer = null
    let provider = null

    // Connect to the MetaMask EIP-1193 object. This is a standard
    // protocol that allows Ethers access to make all read-only
    // requests through MetaMask.
    provider = new ethers.BrowserProvider(window.ethereum)

    // // It also provides an opportunity to request access to write
    // // operations, which will be performed by the private key
    // // that MetaMask manages for the user.
    signer = await provider.getSigner()
    setSigner(signer)
  }

  useEffect(() => {
    if (signer) {
      signer.getAddress().then((address: string) => {
        setAddress(address)
      })
    }
  }, [signer, setAddress])

  const sign = useCallback(
    (payload: string) => signer.signMessage(payload),
    [signer]
  )

  const shared = useCallback((e: { info: string }) => {
    console.log('shared', e.info)
  }, [])

  return (
    <div>
      <button onClick={connectWallet}>Connect Wallet</button>
      {address && signer && (
        <Personhood
          onFinish={shared}
          sessionId="01234567-8901-2345-6789-012345678901"
          signCallback={sign}
          walletAddress={address}
        />
      )}
    </div>
  )
}

Startknet Wallet (ArgentX or Braavos)

// src/App.tsx

import '@anima-protocol/personhood-sdk-react/style.css'
import { Personhood } from '@anima-protocol/personhood-sdk-react'
import { useState, useCallback } from 'react'

export default function Example(): JSX.Element {
  const [address, setAddress] = useState<string | null>(null);
  const [provider, setProvider] = useState<any>(null);

  async function connectWallet() {
    const provider = window.starknet;

    await provider.enable();

    setAddress(provider.selectedAddress);
    setProvider(provider);
  }

  const sign = useCallback(
    (payload: object) => provider.account.signMessage(payload),
    [provider]
  )

  const shared = useCallback((e: { info: string }) => {
    console.log('shared', e.info)
  }, [])

  return (
    <div>
      <button onClick={connectWallet}>Connect Wallet</button>
      {address && provider && (
        <Personhood
          sessionId="01234567-8901-2345-6789-012345678901"
          onFinish={shared}
          signCallback={sign}
          walletAddress={address}
          starknetChainId={provider.chainId}
          chainType="STARKNET"
        />
      )}
    </div>
  );
}

Cosmos Wallet (Keplr)

// src/App.tsx

import '@anima-protocol/personhood-sdk-react/style.css'
import { Personhood } from '@anima-protocol/personhood-sdk-react'
import { useState } from 'react'

export default function Example(): JSX.Element {
  const [keplr, setKeplr] = useState<any>((window as any).keplr);

  const shared = useCallback((e: { info: string }) => {
    console.log('shared', e.info)
  }, [])

  return (
    <div>
      <button onClick={connectWallet}>Connect Wallet</button>
      {address && provider && (
        <Personhood
          sessionId="01234567-8901-2345-6789-012345678901"
          onFinish={shared}
          cosmosConfig={keplr}
          walletAddress={address}
          chainType="COSMOS"
        />
      )}
    </div>
  );
}

Cosmos Wallet (Leap)

// src/App.tsx

import '@anima-protocol/personhood-sdk-react/style.css'
import { Personhood } from '@anima-protocol/personhood-sdk-react'
import { useState } from 'react'

export default function Example(): JSX.Element {
  const [leap, setLeap] = useState<any>((window as any).leap);

  const shared = useCallback((e: { info: string }) => {
    console.log('shared', e.info)
  }, [])

  return (
    <div>
      <button onClick={connectWallet}>Connect Wallet</button>
      {address && provider && (
        <Personhood
          sessionId="01234567-8901-2345-6789-012345678901"
          onFinish={shared}
          cosmosConfig={leap}
          walletAddress={address}
          chainType="COSMOS"
        />
      )}
    </div>
  );
}