見出し画像

PancakeSwap「Frontend」のソースコードを見てみた

string greeting = "こんにちは、web3エンジニアのポコ太郎です。";

今回の記事では、PancakeSwapのGithubで公開されている「フロントエンド」のソースコードを見ていきます。

PancakeSwap(パンケーキスワップ)とは、BNB Chain上に構築されたDEX(Decentralized Exchanges/分散型取引所)の一つです。

01. ディレクトリ構成(apps/web/)


フロントエンドのソースコード pancake-frontend/apps/web にフォーカスをして見ていきたいと思います。

pancake-frontend/apps/web/
 ├ public/ ------------------- 静的ファイル置き場
 ├ src/ ---------------------- メインコード
 ├ .env.development ---------- 環境変数 (開発環境)
 ├ .env.example -------------- 環境変数 (テンプレ)
 ├ .env.production ----------- 環境変数 (本番環境)
 ├ .eslintrc ----------------- ESLint設定ファイル
 ├ .gitignore ---------------- バージョン管理設定ファイル
 ├ babel-test.config.json ---- Babel設定ファイル
 ├ jest.config.js ------------ Jest設定ファイル
 ├ jest.setup.js ------------- Jestセットアップファイル
 ├ next-env.d.ts ------------- Next.js設定ファイル
 ├ next.config.mjs ----------- Next.js設定ファイル
 ├ package.json -------------- パッケージ管理ファイル
 ├ sentry.client.config.js --- Sentry設定ファイル
 ├ sentry.properties --------- Sentry設定ファイル
 ├ sentry.server.config.js --- Sentry設定ファイル
 └ tsconfig.json ------------- TypeScript設定ファイル

ディレクトリは、「public」「src」の2種類となっており、非常なシンプルな構成です。

使われているフレームワークは、「Next.js (React, TypeScript)」です。

DAppsの多くはReactで実装してされているため、web3エンジニアを目指されている方は「React」の業務経験をつけていくことをオススメします。

日本では「Vueが得意です!」というエンジニアが多いですが、現状、Vueに対応したweb3ライブラリが非常に少ないです。
そのため、VueはDAppsとの親和性は低く、Reactの方が仕事に繋がりやすい技術となります。

02. ディレクトリ構成 (src/)


続きまして、メインコードが実装されている pancake-frontend/apps/web のディレクトリ構成を見ていきます。

pancake-frontend/apps/web/src/
 ├ __tests__/
 ├ components/ ---------- コンポーネント
 ├ config/ -------------- 設定
 ├ contexts/ ------------ Context (useContext, useMemo, Redux)
 ├ hooks/ --------------- Hook
 ├ pages/ --------------- Page
 ├ state/ --------------- State
 ├ style/ --------------- Style (CSS)
 ├ utils/ --------------- Utility
 ├ views/ --------------- View (Pageのロジック)
 ├ Providers.tsx -------- Provider (Wagmi, NextJSなど)
 ├ global.d.ts ---------- 型定義 (グローバル)
 ├ index.tsx ------------ インデックス
 ├ middleware.ts -------- Middleware
 ├ react-app-env.d.ts --- 型定義設定
 └ testUtils.tsx -------- Utility (テスト用)

ディレクトリ構成は、Next.jsに準拠しており、非常にシンプルな構成となっております。

03. Metamask接続機能の実装


ディレクトリ構成が確認できましたので、Metamask接続機能の実装を見ていきます。

Metamask接続をするためには「Connect Wallet」を押下します。

index

ボタンは以下に実装されています。
pancake-frontend/appfs/web/src/components/ConnectWalletButton.tsx

import { WalletModalV2 } from '@pancakeswap/ui-wallets'

中略

const handleActive = useActiveHandle()
const { login } = useAuth()
const {
  t,
  currentLanguage: { code },
} = useTranslation()
const { connectAsync } = useConnect()
const { chainId } = useActiveChainId()
const [open, setOpen] = useState(false)

const docLink = useMemo(() => getDocLink(code), [code])

const handleClick = () => {
  if (typeof __NEZHA_BRIDGE__ !== 'undefined') {
    handleActive()
  } else {
    setOpen(true)
  }
}

const wallets = useMemo(() => createWallets(chainId, connectAsync), [chainId, connectAsync])

return (
  <>
    <Button onClick={handleClick} {...props}>
      {children || <Trans>Connect Wallet</Trans>}
    </Button>
    <WalletModalV2
      docText={t('Learn How to Connect')}
      docLink={docLink}
      isOpen={open}
      wallets={wallets}
      login={login}
      onDismiss={() => setOpen(false)}
    />
  </>
)

ウォレット接続時のUIは「@pancakeswap/ui-wallets」のWalletModalV2コンポーネントが使われており、ウォレット接続処理は「useAuth」のlogin関数が使われています。

WalletModalV2を表示させるとこのような感じになります。
「Metamaskボタン」を押下すると、ウォレット接続処理が実行されます。

WalletModalV2

useAuthは、以下に実装されています。
pancake-frontend/appfs/web/src/hooks/useAuth.tsx

// ウォレット接続のライブラリには「wagmi」を使用
import {
  ConnectorNotFoundError,
  SwitchChainError,
  SwitchChainNotSupportedError,
  useConnect,
  useDisconnect,
  useNetwork,
} from 'wagmi'

中略

const useAuth = () => {
  const dispatch = useAppDispatch()
  const { connectAsync, connectors } = useConnect()
  const { chain } = useNetwork()
  const { disconnectAsync } = useDisconnect()
  const { chainId } = useActiveChainId()
  const [, setSessionChainId] = useSessionChainId()
  const { t } = useTranslation()

  // WalletModalV2のPropsに指定しているlogin
  const login = useCallback(
    async (connectorID: ConnectorNames) => {
      const findConnector = connectors.find((c) => c.id === connectorID)
      try {
        // connectAsyncで接続処理が実行されている
        const connected = await connectAsync({ connector: findConnector, chainId })
        if (!connected.chain.unsupported && connected.chain.id !== chainId) {
          replaceBrowserHistory('chain', CHAIN_QUERY_NAME[connected.chain.id])
          setSessionChainId(connected.chain.id)
        }
      } catch (error) {
        if (error instanceof ConnectorNotFoundError) {
          throw new WalletConnectorNotFoundError()
        }
        if (error instanceof SwitchChainNotSupportedError || error instanceof SwitchChainError) {
          throw new WalletSwitchChainError(t('Unable to switch network. Please try it on your wallet'))
        }
      }
      return undefined
    },
    [connectors, connectAsync, chainId, setSessionChainId, t],
  )
}

省略

PancakeSwapのMetamask接続処理には「wagmi」というライブラリが使われていることが分かりました。

04. wagmiとは


wagmi」とは、Metamaskなどのウォレット接続やEVM互換のコントラクトとやり取りなどができるReact Hooksのコレクションです。

長所

  • ウォレット、ENS、コントラクト、トランザクション、署名などを操作するための20以上のフックが準備されている

  • Metamask、WalletConnect、Coinbase Wallet、および、Injected用の組み込みウォレットのコネクタ

  • キャッシング、リクエストの重複排除、永続性の強化

  • マルチコールのサポート

  • TypeScript対応

  • ENS、Foundation、SushiSwapで使用されている

短所

  • 他のライブラリと比べると、対応しているウォレットが少ない

05. まとめ


Next.js (React, TypeScript) でDAppsを開発していきたいというエンジニアは、一度ローカル環境で動かしてみることをオススメします。
Thirdweb、Maralis、web3AuthなどSaaSを組み合わせることで、より効率よく開発ができるようになると思います。

最後までお読みいただき、ありがとうございました。

株式会社RuckPlusでは、web3開発に興味のあるエンジニアを探しております。
ご連絡をお待ちしております!


この記事が気に入ったらサポートをしてみませんか?