😺

XRPLを使ってAMM機能が搭載されたDEXを開発してみた話!!

2024/04/26に公開
3

はじめに

皆さん、こんにちは!

今回はパブリックブロックチェーンの一つである XRPL をテーマにした記事になります!

2024 年現在 ハッカソンプラットフォーム Akindo と XRPL のチームがタッグ組んで WaveHack というグラントプログラムを実施中です!

https://lu.ma/wavehack-entry

https://app.akindo.io/wave-hacks/zKkLpoBQRTOeAz86J

グラントには 2 つの部門があるのですが、今回はそのうちの一つである

Creating technical JP content about XRPL

部門への応募も兼ねた記事になっています!!

XRPL を使ったプロダクトの開発か技術ブログを投稿するだけで参加できるので非エンジニアでもグラントを獲得できるチャンスがある非常に貴重な機会です!!

みなさんもぜひ参加してみてください!! ブロックチェーンプロトコルのことを調べる大変良い機会にもなると思います!!

詳細は下記サイトにて紹介されています!

https://twitter.com/illshin/status/1776052757436436632

https://prtimes.jp/main/html/rd/p/000000022.000058288.html

https://www.wavehack.global/jp

概要

本記事では、XRPL の目玉の一つでもあるネイティブな AMM 機能を利用して DEX を開発してみた時の話をまとめていきます!!

ただ、開発した記録だけを共有するのも面白くないので開発する時に参加にしたドキュメントだったりサンプルコードについても共有して行こうと思います!!

ちなみに開発したのは、2023 年末に開催された DeFi ハッカソンで開発したカーボンクレジット用の DEX 「DCCEX」 です!!

ファイナリストとしてデモピッチまでさせていただき最終的に 3 位に入賞したプロダクトになります!

GitHub

https://github.com/mashharuki/DCCEX

Live Demo

Devnet で動いています。

https://dccex.vercel.app/

Akindo のプロダクトページ

https://app.akindo.io/communities/27mOqg0ARtDLBrqj/products/DrWeMJ0qzfmj7WP6

それではいきましょう!!

XRPL とは

XRPL と聞いてまず皆さんが思い浮かべるのは リップル という単語ではないでしょうか??

ここで XRPL と XRP とリップルという用語について関係性を整理したいと思います。

勘違いされやすいのですが、 XRPL はパブリックブロックチェーンの名前XRP はそのブロックチェーン上で流通しているネイティブトークンの名前 になります!!

分かりやすいようにイーサリアムとの比較表を作りました!!

ブロックチェーン ネイティブトークン
イーサリアム ETH
XRPL XRP

XRPL はあまり知られていないのですが、実はイーサリアムよりも歴史が長いパブリックブロックチェーンになります!!!

2011 年から 2012 年にかけてビットコインの初期コントリビューターを含む 3 人の開発者によって開発されたレイヤー 1 ブロックチェーンなのです!

あれ、じゃあリップルは?? ということなんですが、リップルは XRPL とも XRP とも概念的に異なっているものになります!

リップル は、アメリカ合衆国カリフォルニア州サンフランシスコに本社を置く、 分散型台帳技術を利用した即時グロス決済システムを開発するフィンテック企業 の社名です!!

https://ja.wikipedia.org/wiki/リップル_(企業)

僕も説明聞くまで XRP = リップル だと思っていました・・・。

コンセンサスアルゴリズムも PoW や PoS とも異なる独自の方法(PoA)が採用されています。AMM 機能がプロトコルネイティブに利用できたり他のチェーンと比べて特徴があります!!

XRPL の概要には Q さんという XRPL の開発アンバサダーの方が作成してくれた資料がめちゃめちゃ分かりやすいのでこちらも参照することをお勧めします!!!

Q さん資料 はじめての XRP レジャー

https://speakerdeck.com/tequ/introduction-xrpl-for-ideathon

私が XRPL を調査した時に参考になったドキュメント

XRPL の概要を共有したところで、ハッカソンで実際に DEX を開発した時に役に立ったドキュメント群を紹介していきます!!

公式ドキュメント

英語だけでなく日本語も対応しています!!
これはありがたいですね!

https://xrpl.org/ja/docs

https://xrpl.org/resources/code-samples

下記サイトでは開発の際に役立つ便利なツールを紹介してくれています。

https://xrpl.org/resources/dev-tools/

https://ripple.com/

開発者向け公式ドキュメント

開発者向けにチュートリアルが用意されています!

参考になる実装ばかりです!!

https://xrpl.org/docs/tutorials/

ファウセットは下記サイトから入手できます。

https://xrpl.org/resources/dev-tools/xrp-faucets/

XRPL ではスマートコントラクトを実装しなくても色んな機能を利用することができます。
どのようなトランザクションを発火させることができるか下記ページで紹介されています!

https://xrpl.org/docs/concepts/transactions/

https://xrpl.org/docs/references/protocol/transactions/types/#transaction-types

https://xrpl.org/docs/references/protocol/transactions/transaction-results/tem-codes/#tem-codess

これ以外にも開発者向けのドキュメントはたくさんあるので必要に応じて調べて参照することをお勧めします。

XRPL 学習ポータル

React と SDK を使ったウォレットの開発方法などが学べます!!

SDK を使ってどんなアプリケーションが作れるのか試したいという方は下記サイトがお勧めです!

https://learn.xrpl.org/

https://learn.xrpl.org/course/build-with-react-js-and-xrpl/lesson/begin-coding-with-xrpl-and-react-js/

XRPL の GitHub

GitHub リポジトリももちろん参考になります!!

サンプルコードはここに格納されています!!

https://github.com/XRPLF/

https://github.com/XRPLF/xrpl-dev-portal/tree/master/content/_code-samples/

Q さんの技術ブログ集

日本人開発者であれば Q さんのブログ記事を参照することをお勧めします!!

サンプル実装とともに XRPL の魅力を丁寧に解説してくれている記事がたくさんあります!!

下手すると公式ドキュメントよりも分かりやすいかも!!
公式ドキュメント読んでも分からなかったら Q さんのブログを確認するのが良いと思います!

AMM 関連で詰まった時に質問したら丁寧にご回答していただけました!!

https://zenn.dev/tequ

https://zenn.dev/tequ/articles/xrpl-learning-flow

https://zenn.dev/tequ/articles/issue-xrpl-token

XRPL でトークンをスワップさせてみたいという場合には下記記事が参考になります!

https://zenn.dev/tequ/articles/xrpl-token-swap

その他、XRPL 関連のツール、ウォレットサービスの GitHub など

その他公式ドキュメントや学習ポータル以外にも参考になるリソースは山ほどあります!!

下記はその一例です!!

過去のハッカソン入賞者のプロダクトは実装の参考になりますし、ウォレットである XUMM や crossmark なども XRPL と連動する Dapp を開発するのであれば必見だと思います!!

Web3Auth とも連携できるみたいでサンプルコードが出ていたりします!

https://dorahacks.io/hackathon/xrpl-hackathon

https://xrp.cafe/

https://xumm.readme.io/

https://xumm.app/

https://apps.xumm.dev/

https://www.npmjs.com/package/xrpl

https://test.bithomp.com/nft-explorer

https://github.com/wojake/awesome-xrpl

UNCHAIN にも XRPL を利用した NFT アプリを開発することができる学習コンテンツが 1 つだけあります!

https://unchain-tech.github.io/UNCHAIN-projects-feature/XRPL-NFT-Maker/

https://github.com/XRPL-Labs/XummSDK-React-Demo/tree/main

https://codesandbox.io/examples/package/@nice-xrpl/react-xrpl

https://github.com/tequdev/xrpl-dex-sdk

https://web3auth.io/docs/sdk/helper-sdks/providers/xrpl/

https://github.com/Web3Auth/web3auth-pnp-examples/tree/main/web-modal-sdk/blockchain-connection-examples/xrpl-modal-example

https://docs.crossmark.io/docs

最後に私が XRPL を調査する上で諸々試したサンプルコードや実装を一つにまとめたリポジトリがあるので共有します!!

一部うまく動かない部分もあるのですが参考になる部分もあると思うのでよろしければご覧ください!!

https://github.com/mashharuki/XRPL-Repo/tree/main

DCCEX とは

DCCEX の概要

ではここからはハッカソンで開発した DCCEX について共有したいと思います。

DCCEX は一言で言うと カーボンクレジット特化の DEX です。

世界各国で発行されたカーボンクレジットを XRPL 上の DEX でやりとりできるようにしてしまおうというコンセプトで開発しました!!

XRPL のアイディアソンでたまたま一緒になった学生さんがアイディアを出してくれました!

近年カーボンクレジット、ReFi、クライメートテックへの注目が高まってきていて、カーボンクレジットの市場規模は 2028 年には 1 兆 6027 億米ドルに達すると予想されています。

想定されている大きな問題は、さまざまな規格の乱立によりクレジット取引のコストが大きくなることです。

民間企業、国際機関、政府、などさまざまな団体がそれぞれの地域の性質を反映させたカーボンクレジットの規格を作成し、規格の種類が非常に多くなる可能性は高いでしょう。

その際、現在の国際送金のように規格間で取引を行いたい際に多大なコストが発生することが考えられますよね。

この問題を、XRPL の特徴を活かしたカーボンクレジットのための DEX を作ることにより解決しようとしたプロダクトになります。

デモ動画は以下で確認することができます!!

https://youtu.be/yJkgKPayAIU

なぜ XRPL を採用したのか?

他のブロックチェーンではなく XRPL を選定した理由は以下の通りです。

特に最後の点なんかはイーサリアムなどで DEX を作った場合とは明確に違ってくる点だと思います。

最初にペアごとの LP が不要だったりする点も異なってくる点ですね!

以下の図はアイディアソンの際に作った時の比較図になります!

XRPL 上で Dapp を開発する時のポイント

実際に Dapp を開発していて感じましたが、XRPL は非常に多機能なレイヤー 1 ブロックチェーンになっているため搭載されている機能をいかに使いこなすかがプロダクト開発する上でのポイントだと思います。

XRPL にはいろんな機能を呼び出すために様々なトランザクションタイプが定義されています。

SDK を使って 各種トランザクションをどうやって実行するかを一つ一つ試しながら実装していくことがポイント だと思います!!

慣れるまでは検証用のスクリプトを開発して挙動を確認しながら開発を進めると良いと思います!!

そのため公式ドキュメントに書いてある仕様や Q さんの技術ブログの参考実装例、学習ポータルから参照できるサンプルコードが非常に参考になります!!

DCCEX の実装のポイント

技術スタック

Next.js をベースに開発していますが、特筆すべきスタックとしては以下の通りです。

xrpl は XRPL 用とやりとりするためのメソッドが用意されているライブラリです。

xumm は、今回採用したウォレットです。
※ イーサリアムでいう Metamask みたいなソフトウェアです。

XRPL 用の Dapp を開発するときに鍵になってくるのはxrplxummだと思います!!

ポイントとなるソースコード

XRPL とは直接関係ないのですが、今回フロントのソースコードは下記 GitHub リポジトリをベースに開発しています。

https://github.com/Siumauricio/nextui-dashboard-template

Next.js を使ってダッシュボード画面を作りたいという場合には参考になると思いますので共有いたします!!

XRPL 用とやりとりするためのステート変数やメソッドは全てXummProvider.tsxというファイルにまとめています。

GitHub のリンクは下記です。

https://github.com/mashharuki/DCCEX/blob/main/pkgs/frontend/src/context/XummProvider.tsx

ウォレットに接続してインスタンスを生成するのはloginメソッドになります。

// get env
const { XRP_API_KEY } = await getEnv();
// XUMM用のインスタンスを作成する。
const newXumm = new Xumm(XRP_API_KEY);

try {
  globalContext.setLoading(true);

  // authorize
  await newXumm.authorize();
  const account = await newXumm.user.account;
  const userInfo = await newXumm.user;
  console.log("user info:", userInfo);
  console.log("account address:", account);

  setAddress(account);
  setXumm(newXumm);
  await getAccountInfo(account!);
} catch (err) {
  console.error("eer:", err);
} finally {
  globalContext.setLoading(false);
}

こんな感じでウォレットのアカウント情報を取得します。

一番の目玉はなんといっても Swap 機能ですね!

swapメソッドに実装されています。

/**
 * Swap
 */
const swap = async (
  token1Info: TokenInfo,
  token2Info: TokenInfo,
  token1Value: string,
  token2Value: string
) => {
  // path find
  var result;

  try {
    // Connect to the client
    await client.connect();
    globalContext.setLoading(true);

    if (token1Info.currency != null && token2Info.issuer != null) {
      result = await client.request({
        command: "path_find",
        subcommand: "create",
        source_account: address,
        source_amount: {
          currency: token2Info.currency,
          value: token2Value,
          issuer: token2Info.issuer,
        },
        destination_account: address,
        destination_amount: {
          currency: token1Info.currency,
          value: token1Value,
          issuer: token1Info.issuer,
        },
      });
    } else if (token2Info.issuer == null) {
      result = await client.request({
        command: "path_find",
        subcommand: "create",
        source_account: address,
        source_amount: {
          currency: "XRP",
        },
        destination_account: address,
        destination_amount: {
          currency: token1Info.currency,
          value: token1Value,
          issuer: token1Info.issuer,
        },
      });
    } else if (token1Info.issuer == null) {
      result = await client.request({
        command: "path_find",
        subcommand: "create",
        source_account: address,
        source_amount: {
          currency: "XRP",
        },
        destination_account: address,
        destination_amount: {
          currency: token2Info.currency,
          value: token2Value,
          issuer: token2Info.issuer,
        },
      });
    }

    console.log("path find:", result);

    // get env
    const { FAUCET_SEED } = await getEnv();
    // Create a wallet using the seed
    const wallet = await Wallet.fromSeed(FAUCET_SEED);

    // AccountSet Tx ------------------------------------------
    const {
      created: create2,
      resolve: resolve2,
      resolved: resolved2,
      websocket: websocket2,
    } = await xumm!.payload!.createAndSubscribe({
      TransactionType: "AccountSet",
      Account: address!,
      Domain: "6578616D706C656D617368686172756B692E636F6D",
      SetFlag: AccountSetAsfFlags.asfDefaultRipple,
      Flags:
        AccountSetTfFlags.tfDisallowXRP | AccountSetTfFlags.tfRequireDestTag,
    });

    console.log("AccountSet Payload URL:", create2.next.always);
    console.log("AccountSet Payload QR:", create2.refs.qr_png);

    websocket2.onmessage = (msg) => {
      const data = JSON.parse(msg.data.toString());
      // トランザクションへの署名が完了/拒否されたらresolve
      if (typeof data.signed === "boolean") {
        resolve2({
          signed: data.signed,
          txid: data.txid,
        });
      }
    };

    // resolveされるまで待機
    await resolved2;

    // Create trust line (token1 & token2) to user ----------------------------------------------
    if (token1Info.issuer != null) {
      const {
        created: create3,
        resolve: resolve3,
        resolved: resolved3,
        websocket: websocket3,
      } = await xumm!.payload!.createAndSubscribe({
        TransactionType: "TrustSet",
        Account: address!,
        Flags: TrustSetFlags.tfClearNoRipple,
        LimitAmount: {
          currency: token1Info.currency!,
          issuer: token1Info.issuer!,
          value: "100000000000000000000000000", // Large limit, arbitrarily chosen
        },
      });

      console.log("TrustSet Payload URL:", create3.next.always);
      console.log("TrustSet Payload QR:", create3.refs.qr_png);

      websocket3.onmessage = (msg) => {
        const data = JSON.parse(msg.data.toString());
        // トランザクションへの署名が完了/拒否されたらresolve
        if (typeof data.signed === "boolean") {
          resolve3({
            signed: data.signed,
            txid: data.txid,
          });
        }
      };

      // resolveされるまで待機
      await resolved3;
    }
    if (token2Info.issuer != null) {
      const {
        created: create3,
        resolve: resolve3,
        resolved: resolved3,
        websocket: websocket3,
      } = await xumm!.payload!.createAndSubscribe({
        TransactionType: "TrustSet",
        Account: address!,
        Flags: TrustSetFlags.tfClearNoRipple,
        LimitAmount: {
          currency: token2Info.currency!,
          issuer: token2Info.issuer!,
          value: "100000000000000000000000000", // Large limit, arbitrarily chosen
        },
      });

      console.log("TrustSet Payload URL:", create3.next.always);
      console.log("TrustSet Payload QR:", create3.refs.qr_png);

      websocket3.onmessage = (msg) => {
        const data = JSON.parse(msg.data.toString());
        // トランザクションへの署名が完了/拒否されたらresolve
        if (typeof data.signed === "boolean") {
          resolve3({
            signed: data.signed,
            txid: data.txid,
          });
        }
      };

      // resolveされるまで待機
      await resolved3;
    }

    // Swap用のトランザクションデータを作成する
    var swapTxData: any;

    if (token1Info.issuer != null && token2Info.issuer != null) {
      swapTxData = {
        TransactionType: "Payment",
        Account: address,
        Destination: address, // AMMの際は自分自身のアドレスを指定
        DestinationTag: 1,
        Amount: {
          currency: token1Info.currency, // ここで変換先トークンの種類を指定する。
          value: token1Value, // ここで変換先トークンの金額を指定する。
          issuer: token1Info.issuer!,
        },
        SendMax: {
          currency: token2Info.currency, // ここで変換先のトークンの種類を指定する。
          value: token2Value,
          issuer: token2Info.issuer!,
        },
        Paths: [
          [
            {
              account: token2Info.issuer!,
              type: "1",
            },
            {
              currency: token1Info.currency,
              issuer: token1Info.issuer!,
              type: "48",
            },
          ],
        ],
      };
    } else if (token2Info.issuer == null) {
      // XRP > その他のトークン
      swapTxData = {
        TransactionType: "Payment",
        Account: address,
        Destination: address, // AMMの際は自分自身のアドレスを指定
        DestinationTag: 1,
        Amount: {
          currency: token1Info.currency, // ここで変換先トークンの種類を指定する。
          value: token1Value, // ここで変換先トークンの金額を指定する。
          issuer: token1Info.issuer,
        },
        SendMax: xrpToDrops(token2Value),
        Paths: [
          [
            {
              currency: token1Info.currency,
              issuer: token1Info.issuer,
              type: 48,
            },
          ],
        ],
      };
    } else if (token1Info.issuer == null) {
      // その他のトークン > XRP
      swapTxData = {
        TransactionType: "Payment",
        Account: address,
        Destination: address, // AMMの際は自分自身のアドレスを指定
        DestinationTag: 1,
        Amount: xrpToDrops(token1Value),
        SendMax: {
          currency: token2Info.currency, // ここで変換先トークンの種類を指定する。
          value: token2Value, // ここで変換先トークンの金額を指定する。
          issuer: token2Info.issuer,
        },
        Paths: [
          [
            {
              currency: "XRP",
              type: 16,
            },
          ],
        ],
      };
    }

    // Swap用のトランザクションを実行
    const { created, resolve, resolved, websocket } =
      await xumm!.payload!.createAndSubscribe(swapTxData);

    console.log("Payload URL:", created.next.always);
    console.log("Payload QR:", created.refs.qr_png);

    websocket.onmessage = (msg) => {
      const data = JSON.parse(msg.data.toString());
      // トランザクションへの署名が完了/拒否されたらresolve
      if (typeof data.signed === "boolean") {
        resolve({
          signed: data.signed,
          txid: data.txid,
        });
      }
    };

    // resolveされるまで待機
    await resolved;

    // Check balances ------------------------------------------------------------
    console.log("Getting hot address balances...");
    // get hot address data
    const balances = await client.request({
      command: "account_lines",
      account: address,
      ledger_index: "validated",
    });
    console.log("address's balance Info:", balances.result);
  } catch (err) {
    console.error("error occuered while swaping:", err);
  } finally {
    globalContext.setLoading(false);
    await client.disconnect();
  }
};

複数パターンの Swap をまとめて一つのメソッドで実装しているのでちょっと複雑になっていますが、基本的な流れは他のブロックチェーンと同じです。

トランザクションデータを作成して。署名&送信させるという流れです。

XRPL の場合はこのような実装になるわけです!!

これのメソッドを呼び出しているのが下記ファイルになります!!

https://github.com/mashharuki/DCCEX/blob/main/pkgs/frontend/src/components/home/selectTab/swap.tsx

XummProvider.tsxの方にロジックをほぼ全て実装したので画面コンポーネント側のファイルは大分スッキリしています!!

ライブラリが定義するデータ定義に合致する用にトランザクションデータを用意してあげればスマートコントラクトを開発しなくても DEX の AMM 機能が使用できてしまうわけですね!!

この点が非常に魅力的な点だと思います!!

Uniswap のように完成度の高い DEX のソースコードをフォークしてきてオリジナルの機能を実装するにしてもプロダクト毎に違いが生まれソースコード監査を受ける必要があります。

また、致命的な脆弱性が見つかった場合には該当するソースコードを使っているプロダクトは全てアップグレードする必要があります!!

XRPL ではそれをプロトコルレイヤーで吸収できるのでやはり利点がありますよ!

やはりこのようなセキュリティインシデント対応の観点から見ても AMM の機能がプロトコルネイティブに実装されているというのは他のブロックチェーンと比較しても優位性になる点だと考えています。

検証用のサンプルスクリプト

AMM 機能については検証用のスクリプトを用意しました!!

https://github.com/mashharuki/DCCEX/blob/main/pkgs/scripts/src/create-amm.ts

トランザクション作成して送信する部分は下記ファイルに実装しています。

https://github.com/mashharuki/DCCEX/blob/main/pkgs/scripts/src/lib/amm.ts

このスクリプトには、検証用のトークンの発行機能も実装されておりその場で発行されたトークン同士を交換することが可能になっています!!

スクリプトの動かし方ですが以下の通りです!

pnpm install

モジュールをインストールしたら下記コマンドで実行できます。

pnpm scripts run create-amm

少し時間がかかりますが、以下のような結果がコンソールに出力されるはずです!!

Requesting address from the faucet...
Funding an issuer address with the faucet...
Got issuer address rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n.
Issuer DefaultRipple enabled: https://devnet.xrpl.org/transactions/06C0703F31A726ED96A12D7F37602151CD8084C52AC8AAC32C42C2932A13D235
Trust line created: https://devnet.xrpl.org/transactions/83C1A53EE1AEBE8640640D5C26BBE94A8E48BF000A77FAA7D20A2CF20EEA131A
Tokens issued: https://devnet.xrpl.org/transactions/81FA97E5BB9DBB69F08E7F8018AF9737D0854E486114869250DC29EEAE74258E
Funding an issuer address with the faucet...
Got issuer address rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8.
Issuer DefaultRipple enabled: https://devnet.xrpl.org/transactions/39920495A5F4B1357D36E58EF727250ED020F1D9788DCB8D811A45CDFE270692
Trust line created: https://devnet.xrpl.org/transactions/882D1500AA194AA46D8D09399BE9E15696BF3393DAD2B0D7F35BA6D84ACD595A
Tokens issued: https://devnet.xrpl.org/transactions/43BE73E72EF31E06FA00538CFAFD5DD7E7EF286659A0ED215D3DDF86164D58BA
MSH offer placed: https://devnet.xrpl.org/transactions/4C986CC4CAC05F258BD6F83C153BB145232458C10D4664341658A44171175062
MSH offer placed: https://devnet.xrpl.org/transactions/0246023D715CC4B3AB5594909ADCB83ECC5CAEB068E1F3BA95F58EF4CCC3331F
No AMM exists yet for the pair
          FOO.rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8 /
          MSH.rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n
          (This is probably as expected.)
Current AMMCreate transaction cost: 2 XRP
AMM created: https://devnet.xrpl.org/transactions/2440E92B1DA220B497DF0927310A761BF4FC769BC92341E1169E94513AF1A671
amm_info_result2: {
  id: 99,
  result: {
    amm: {
      account: 'rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB',
      amount: {
        currency: 'MSH',
        issuer: 'rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n',
        value: '15'
      },
      amount2: {
        currency: 'FOO',
        issuer: 'rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8',
        value: '100'
      },
      asset2_frozen: false,
      asset_frozen: false,
      auction_slot: {
        account: 'rzKW653XWjjZSe1kGWshBMehALvemQbsM',
        discounted_fee: 50,
        expiration: '2023-12-05T03:02:02+0000',
        price: {
          currency: '03451AB86CDD94767A698DD826DE28F078EE0C11',
          issuer: 'rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB',
          value: '0'
        },
        time_interval: 0
      },
      lp_token: {
        currency: '03451AB86CDD94767A698DD826DE28F078EE0C11',
        issuer: 'rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB',
        value: '38.72983346207417'
      },
      trading_fee: 500,
      vote_slots: [
        {
          account: 'rzKW653XWjjZSe1kGWshBMehALvemQbsM',
          trading_fee: 500,
          vote_weight: 100000
        }
      ]
    },
    ledger_hash: '188D13D54F592251B421F87E195C7B451431E762266DC48AC88A69F851D68AD0',
    ledger_index: 2182662,
    validated: true
  },
  type: 'response'
}
The AMM account rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB has 38.72983346207417 total
                LP tokens outstanding, and uses the currency code 03451AB86CDD94767A698DD826DE28F078EE0C11.
In its pool, the AMM holds 15 MSH.rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n
                  and 100 FOO.rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8
ammAddress: rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB

Requesting address from the faucet...
Funding an issuer address with the faucet...
Got issuer address rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n.
Issuer DefaultRipple enabled: https://devnet.xrpl.org/transactions/06C0703F31A726ED96A12D7F37602151CD8084C52AC8AAC32C42C2932A13D235
Trust line created: https://devnet.xrpl.org/transactions/83C1A53EE1AEBE8640640D5C26BBE94A8E48BF000A77FAA7D20A2CF20EEA131A
Tokens issued: https://devnet.xrpl.org/transactions/81FA97E5BB9DBB69F08E7F8018AF9737D0854E486114869250DC29EEAE74258E
Funding an issuer address with the faucet...
Got issuer address rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8.
Issuer DefaultRipple enabled: https://devnet.xrpl.org/transactions/39920495A5F4B1357D36E58EF727250ED020F1D9788DCB8D811A45CDFE270692
Trust line created: https://devnet.xrpl.org/transactions/882D1500AA194AA46D8D09399BE9E15696BF3393DAD2B0D7F35BA6D84ACD595A
Tokens issued: https://devnet.xrpl.org/transactions/43BE73E72EF31E06FA00538CFAFD5DD7E7EF286659A0ED215D3DDF86164D58BA
MSH offer placed: https://devnet.xrpl.org/transactions/4C986CC4CAC05F258BD6F83C153BB145232458C10D4664341658A44171175062
MSH offer placed: https://devnet.xrpl.org/transactions/0246023D715CC4B3AB5594909ADCB83ECC5CAEB068E1F3BA95F58EF4CCC3331F
No AMM exists yet for the pair
          FOO.rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8 /
          MSH.rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n
          (This is probably as expected.)
Current AMMCreate transaction cost: 2 XRP
AMM created: https://devnet.xrpl.org/transactions/2440E92B1DA220B497DF0927310A761BF4FC769BC92341E1169E94513AF1A671
amm_info_result2: {
  id: 99,
  result: {
    amm: {
      account: 'rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB',
      amount: {
        currency: 'MSH',
        issuer: 'rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n',
        value: '15'
      },
      amount2: {
        currency: 'FOO',
        issuer: 'rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8',
        value: '100'
      },
      asset2_frozen: false,
      asset_frozen: false,
      auction_slot: {
        account: 'rzKW653XWjjZSe1kGWshBMehALvemQbsM',
        discounted_fee: 50,
        expiration: '2023-12-05T03:02:02+0000',
        price: {
          currency: '03451AB86CDD94767A698DD826DE28F078EE0C11',
          issuer: 'rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB',
          value: '0'
        },
        time_interval: 0
      },
      lp_token: {
        currency: '03451AB86CDD94767A698DD826DE28F078EE0C11',
        issuer: 'rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB',
        value: '38.72983346207417'
      },
      trading_fee: 500,
      vote_slots: [
        {
          account: 'rzKW653XWjjZSe1kGWshBMehALvemQbsM',
          trading_fee: 500,
          vote_weight: 100000
        }
      ]
    },
    ledger_hash: '188D13D54F592251B421F87E195C7B451431E762266DC48AC88A69F851D68AD0',
    ledger_index: 2182662,
    validated: true
  },
  type: 'response'
}
The AMM account rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB has 38.72983346207417 total
                LP tokens outstanding, and uses the currency code 03451AB86CDD94767A698DD826DE28F078EE0C11.
In its pool, the AMM holds 15 MSH.rfY8Yf51Hm2gqrpLn2EB7PMMKSi4GSxi6n
                  and 100 FOO.rfCCuGCStks2tabt5ThAx5ZY5Aw7fbK1Q8
ammAddress: rfCBEVwnwbjWzNaWLPuYgo8osKCtrKRiZB

過去に実行した処理のトランザクション内容については下記の通り、エクスプローラーで確認することが可能です!!

このコードを動かして実行してもらうだけでも XRPL 上で AMM がどのように処理されるのかイメージを具体化できると思います!!

最後に

いかがでしたでしょうか?

ここまで聞くとイーサリアムとは全然違うアプローチを取っていることがわかったと思います!!

スマートコントラクト開発用のプログラミング言語の知識を習得しなくても TypeScript や Python 用の SDK を利用することで、ブロックチェーンアプリケーションをすぐに開発できる点は非常に魅力的だと思います!!

独自のスマートコントラクトロジックを組み込みづらいというデメリットも確かにあるのですが、エンタープライズでの商用利用などを考えるとセキュリティの脆弱性が発生した時もプロトコルレイヤーでほとんど吸収できるのはシステム障害やインシデントが発生した時に対応しやすいはずです。

元々国際送金を意識して設計されている点もあることから商用利用しやすいブロックチェーンの一つなのではないかと考えています。

どこから手をつけて良いかわからないという方でもまずは手を動かしてみて簡単でも良いのでサンプルコードを実装してみるところから始めてみることをお勧めします!!

まだ公式ドキュメントにないサンプルコードを実装してみたものをプルリクするだけで承認される可能性もあります!!

下記は、僕が AMM に関する一連の機能を一括で検証できるサンプルコードを作って提出した時のプルリクです!!

承認されて公式ドキュメントからも参照できるようになっています!!

https://github.com/XRPLF/xrpl-dev-portal/pull/2307

公式ドキュメントに載せてもらいました!
※ TypeScript で実装したサンプルコードは初めてだったみたいで TypeScript のロゴもこの時初めて掲載したみたいです。

https://xrpl.org/resources/code-samples

GitHub 上では下記で確認ができます!!

https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/create-amm

こういったところからでも貢献できますので皆さんもぜひトライしてみてください!!

長くなりましたがここまでになります!!

ありがとうございました!!

GitHubで編集を提案

Discussion

momotaro151amomotaro151a

本日、記事を拝見しました。
素晴らしい内容ですね。
現在、私はXRPLのVolumeBotを作成していますが、経験が浅いため、少々困っています。
もし可能であれば、参考になる資料を共有していただけないでしょうか?
何卒よろしくお願いいたします!

HarukiHaruki

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

https://zenn.dev/tequ

個人的には Qさんの記事がすごく参考になりました!!!!
おすすめです!!