🍏

UniSwapの様なDEXアプリを作成する【その1】

2022/05/01に公開

今回は、世界的にも有名になりDeFiを盛り上げたきっかけを作ったUniSwapの様なDEXアプリの作成を目指して、1回目のブログを投稿します。

まずは、手始めにクライアント側から!

目次

  1. 概要
  2. 作成したアプリについて
  3. 最後に

概要

2020年、2021年は、新型コロナの感染拡大で世界が大きく変わりましたが、ブロックチェーンの世界も大きく変わりました。
DeFiやNFTが話題となり、特にNFTについてはアーティスト達にも受け入れられ始めたことから一般人にも広く認知され始めました。

今回は、そのDeFiの中でもかなり有名なUniSwapにフォーカスを当てながら、DEXアプリの作成を目指していきたいと思います。
公式ドキュメントを読みながら進めたところ、クライアント側についてはとりあえず動くものができたので一回目の成果物として共有したいと思います。

今回使用したソースコードは下記の通りです。

https://github.com/mashharuki/SwapExample

クライアントアプリのイメージは下記の通りです。

アプリの操作性などの話に移る前に、DEXやAMM形式などについて簡単に用語の整理をさせていただきたいと思います。

DEXとは

日本語だと分散型金融と呼ばれることが多いです。これは、ある企業や組織が管理するのではなく、スマートコントラクトによって自動で暗号資産やトークンの交換を実現する交換所のことを指す言葉となります。
DEXとは正反対の意味として使われるのが、CEXでコインチェック社やBitFlyer社、ディーカレット社などが挙げられますよね。これらの交換所は、管理者となる存在がありそこを介して暗号資産などを交換することになります。

AMM形式とは

日本語では「自動マーケットメイカー」とも呼ばれており、一定の既定ルールに従い、自動でのマーケットメイクを可能にするシステムを指します。
AMM形式が出るまでは、DEXは別の仕組みで構築されていることが多かったようですが、流動性が生まれにくかったりして今はあまり聞かれなくなっている印象です。

※ 取引額の上位のほとんどが UniSwapやSushiSwapなどのAMM形式を導入したDEXで占められているので、 自分もつい最近まではDEX=AMMだと思っていました・・・

今日は、言葉の簡単な概要だけになりますが、CoinPostなどの方がより詳しく解説していただいているので下記の様なページが参考になるはずです。

https://coinpost.jp/?p=304625

作成したアプリについて

UniSwapの公式ドキュメントを内、SDKを導入するセクションを参考にアプリを作成しました。
画面のデザイン等は、今まで作成したアプリから素材を抽出して組み合わせています。(ほとんどMUIコンポーネントの組み合わせです。)

まずは、公式ドキュメントに従い、Getting Started with the Swap Widgetに従い、ディレクトリや必要なパッケージを用意!

また、事前にINFURAなどのサイトでRPCエンドポイントを用意しておく必要があるので、まだアカウントを作っていないとは作成してエンドポイント用の情報を用意してしまいましょう!!

https://infura.io/

Reactの雛形を生成する npx create-react-appコマンドを打ち込んでそこにこのSwapWidgetコンポーネントをチュートリアルの通りに組み込んであげればすぐにSwapが可能なアプリが出来上がりました!(物凄くお手軽です!)
ただ、公式ドキュメントの通りにやってもエラーになったので一部修正が必要みたいですね。。

私の場合は、App.jsが下記の様になりました!

import { SwapWidget } from '@uniswap/widgets';
import React, { useEffect, useState } from "react";
import { providers, ethers } from 'ethers';
import './App.css';
import '@uniswap/widgets/fonts.css'
import detectEthereumProvider from "@metamask/detect-provider";
// material-ui関連をインポートする。
import AppBar  from '@mui/material/AppBar';
import Toolbar  from '@mui/material/Toolbar';
import Typography  from '@mui/material/Typography';
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
import Button from '@mui/material/Button';
import { styled } from "@mui/material/styles";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";


// Swap用のテーマ
const theme = {
  primary: '#001D82',
  secondary: '#6677C1',
  interactive: '#005BAE',
  container: '#ABD6FE',
  module: '#FFF7FB',
  accent: '#FF7BC2',
  outline: '#ABD6FE',
  dialog: '#FFF',
  fontFamily: 'Arvo',
  borderRadius: 1,
}

// StyledPaperコンポーネント
const StyledPaper = styled(Paper)(({ theme }) => ({
  ...theme.typography.body2,
  padding: theme.spacing(2),
  maxWidth: 360
}));

/**
 * Appコンポーネント
 */
const App = () => {
  // RPCのエンドポイントを設定する。
  const jsonRpcEndpoint = process.env.REACT_APP_API_ENDPOINT;
  const jsonRpcProvider = new providers.JsonRpcProvider(jsonRpcEndpoint);
  // create provider
  const provider = new ethers.providers.Web3Provider(jsonRpcProvider);
  // ステート変数
  const [account, setAccount] = useState({
    address: '',
    provider: provider,
  })

  // メタマスクに接続するための関数
  async function connectWallet() {
    const ethereumProvider = await detectEthereumProvider();

    if (ethereumProvider) {
      const accounts = await window.ethereum.request({
        method: 'eth_requestAccounts',
      })

      const address = accounts[0];
      setAccount({
        address: address,
        provider: ethereumProvider
      })
    }
  }

  return (
    <div>
      <AppBar position="static" color="inherit">
        <Toolbar className="toolbar">
          <Typography variant="h6" color="inherit" sx={{ flexGrow: 1 }}>
            <strong>Swap DApp</strong>
          </Typography>
          <Typography variant="h6" color="inherit">
            <Button onClick={connectWallet}>
              <AccountBalanceWalletIcon/>
            </Button>
          </Typography>
        </Toolbar>
      </AppBar>
      <Box sx={{ flexGrow: 1, overflow: "hidden", px: 3, mt: 10}}>
        <StyledPaper sx={{my: 1, mx: "auto", p: 0, borderRadius: 4}}>
          <div className="Uniswap">
            <SwapWidget provider={account.provider} jsonRpcEndpoint={jsonRpcEndpoint} theme={theme} />
          </div>
        </StyledPaper>
      </Box>
    </div>
  );
}

export default App;

次に操作についてですが、今回は、Rinkeby上でSwapを実践してみました!

① 交換したトークン同士を選ぶ(レート情報を自動で取得してくれる!!)

今回は、 ETHからUNIにして見たいと思います。
問題なければ Review swapというボタンが押せる様になるので押します!

② Confrim swapというボタンを押す様に求められるので押します!

③ MetaMaskで確認が求められるので確認を押す!

④ しばらくするとSwapが完了するのでこれで終わり!

MetaMaskで確認したところちゃんと増えていました!

第1回目はここまでになります。

最後に

とりあえず第1段階として、自分のReactアプリのテンプレにSwapWidgetを導入するところは、簡単にいけました。
ただ肝心のスマートコントラクトの実装はこれからなのでまだ壁がありそうです。
こちらも公式ドキュメントを読みながら開発していますが、なぜかエラーが起きてコンパイルできない。。。

目標は、開発したスマートコントラクトをローカルブロックチェーンにデプロイしてオリジナルのトークン同士でSwapできる様にするところまでやりたいと考えています。

ここまで読んでいただきありがとうございました!

参考にしたサイト

今回アプリを作成するにあたり大変参考になったものばかりです。
ありがとうございました!!

https://docs.uniswap.org/sdk/widgets/swap-widget#web3-provider

https://medium.com/geekculture/how-to-add-the-uniswap-swap-widget-to-your-react-dapp-9be353e2bd46

Discussion