最近、分散型取引所開発に関するチュートリアルを書いていましたhttps://github.com/WTFAcademy/WTF-Dapp で、Uniswap V3 のコード実装を参照し、多くの知識ポイントを学びました。筆者はこれまでに簡単なNFTコントラクトを開発したことがありますが、Defiコントラクトの開発に挑戦するのは今回が初めてなので、これからコントラクト開発を学びたい初心者にとっては役立つと思います。
契約開発の専門家は、https://github.com/WTFAcademy/WTF-Dappに直接アクセスしてコードを提供し、Web3 に貢献できます~
次に、奇跡とも言える小技をご紹介します。
コントラクト展開のコントラクトアドレスを予測可能にする方法があります。
コントラクトをデプロイすると、通常、一見ランダムなアドレスが取得されます。これは「ノンス」に関連しているため、コントラクトのアドレスを予測するのは困難です。しかし、Uniswap では、トランザクション ペアと関連情報を通じてコントラクトのアドレスを推定する必要があるという要件があります。これは、トランザクション権限の決定やプールのアドレスの取得など、多くの状況で役立ちます。
Uniswap では、コントラクトの作成は、「pool = address(new Uniswap V3 Pool{salt: keccak 256(abi.encode(token 0, token 1, feed))}());」のようなコードを通じて作成されます。 CREATE 2 ( https://github.com/AmazingAng/WTF-Solidity/blob/main/25_Create2/readme.md ) を使用してコントラクトを作成するために「salt」を追加することで、作成されたコントラクトのアドレスが予測可能になるという利点があります。 , アドレス生成のロジックは「新しいアドレス = hash(0x FF, 作成者アドレス, ソルト, initcode)」です。
この部分の詳細については、WTF-DApp コースのhttps://github.com/WTFAcademy/WTF-Dapp/blob/main/P103_Factory/readme.mdの章を確認してください。
コールバック関数を上手に活用する
Solidity では、コントラクトは相互に呼び出しを行うことができます。 A が特定のメソッドで B を呼び出し、B が呼び出されたメソッドで A にコールバックするシナリオがあります。これも一部のシナリオではうまく機能します。
Uniswap では、取引するために「Uniswap V3 プール」コントラクトの「swap」メソッドを呼び出すと、コールバックはこのトランザクションに実際に必要な計算された「トークン」を渡します。トランザクションに必要なトークンは、呼び出し元が呼び出すために「swap」メソッドを 2 つの部分に分割するのではなく、「Uniswap V3 プール」に転送されます。これにより、「swap」メソッドのセキュリティが確保され、ロジック全体が完全に実行されることが保証されます。セキュリティを確保するために面倒な変数ログを作成する必要はありません。
コードスニペットは次のとおりです。
コースの取引部分について詳しくは、 https://github.com/WTFAcademy/WTF-Dapp/blob/main/P106_PoolSwap/readme.md をご覧ください。
例外を使用して情報を伝達し、try catch を使用してトランザクションを推定します。
Uniswap のコードを参照すると、そのコントラクトhttps://github.com/Uniswap/v3-periphery/blob/main/contracts/lens/Quoter.solで、「Uniswap V3 Pool」の「swap」がメソッドは「try catch」でラップされて実行されます。
これはなぜでしょうか?トランザクションに必要なトークンを見積もるために「スワップ」方法をシミュレートする必要がありますが、見積中に実際にはトークンの交換が行われないため、エラーが報告されます。 Uniswap では、トランザクション コールバック関数で特別なエラーをスローし、そのエラーをキャプチャして、エラー メッセージから必要な情報を解析します。
かなりハックに見えますが、非常に実用的でもあります。この方法では、推定トランザクションのニーズを満たすためにスワップ方法を変更する必要がなく、ロジックがより簡単になります。私たちのコースでは、このロジックを参照して、コントラクトhttps://github.com/WTFAcademy/WTF-Dapp/blob/main/demo-contract/contracts/wtfswap/SwapRouter.solも実装しました。
大きな数値を使用して精度の問題を解決する
Uniswap のコードには、現在の価格と流動性に基づいて交換トークンを計算するなど、多くの計算ロジックがあり、このプロセスでは除算演算中に精度が失われることを避ける必要があります。 Uniswap では、計算プロセスで「<<FixedPoint 96.RESOLUTION」という演算がよく使用されます。これは 96 ビットの左シフトを表し、「2^96」を乗算するのと同じです。通常のトランザクションではオーバーフローすることなく精度を保証できるように、左シフト後に除算演算を行ってください(通常は「uint 256」で計算しますので十分です)。
コードは次のとおりです (価格と流動性を通じてトランザクションに必要なトークンの数を計算します)。
ご覧のとおり、Uniswap では、まず価格に「2^96」の平方根を乗算し (上記コードの「sqrtRatioAX 96」と「sqrtRatioBX 96」に相当)、流動性「liquidity」は次のようになります。左にシフトして「分子」1」を計算します。以下の計算では、計算プロセス中に「2^96」が減算されて最終結果が得られます。
もちろん、どちらにせよ、理論上は精度が失われることになりますが、この場合は最小単位の損失なので、許容範囲です。
詳細については、 https://github.com/WTFAcademy/WTF-Dapp/blob/main/P106_PoolSwap/readme.md でこのコースの詳細をご覧ください。
株式法による収入の計算
UniswapではLP(流動性プロバイダー)の手数料収入を記録する必要があります。当然のことながら、大量のガスを消費するため、すべてのトランザクションで各 LP に独自の手数料を記録することはできません。では、どうやって対処すればいいのでしょうか?
Uniswap では、「Position」に次の構造が定義されていることがわかります。
これには、「feeGrowthInside0LastX128」および「feeGrowthInside1LastX128」が含まれており、各ポジションの手数料が最後に引き出されたときに各流動性が受け取る必要がある手数料を記録します。
簡単に言うと、手数料の合計と、各流動性に割り当てる手数料を記録するだけで済みます。これにより、LP が手数料を引き出すときに、流動性に基づいていくらの手数料を引き出すことができるかを計算できます。手。特定の企業の株式を保有している場合と同様に、株式収益を引き出したいときは、その企業の過去の 1 株あたりの収益と、最後に引き出したときの収益だけを知る必要があります。
以前は、 「独創的な契約設計、stETH が毎日どのように収入を自動的に分配するかを見てみましょう?」でした。安定した金利を得るためにETHをプレッジに参加させましょう。同様のstETHの収入の計算方法もこの記事で紹介しました。
すべての情報をチェーンから取得する必要はない
オンチェーン ストレージは比較的高価であるため、すべての情報をチェーンにアップロードしたり、チェーンから取得したりする必要はありません。たとえば、Uniswap フロントエンド Web サイトによって呼び出されるインターフェイスの多くは、従来の Web2 インターフェイスです。
トランザクション プールのリスト、トランザクション プール情報などは通常のデータベースに保存でき、一部はチェーンから定期的に同期する必要がありますが、実際にはチェーンまたはノード サービスによって提供される PRC インターフェイスを呼び出す必要はありません。関連データを取得する時間。
もちろん、多くのブロックチェーン PRC サプライヤーは現在、いくつかの高度なインターフェイスを提供しており、より速く、より安価な方法で一部のデータを取得できます。これも同様の理由です。たとえば、ZAN は、特定のユーザーのすべての NFT を取得するのと同様のインターフェイスを提供しており、この情報をキャッシュしてパフォーマンスと効率を向上させることができます。
もちろん、重要なトランザクションはチェーン上で実行される必要があります。
契約の分割方法を学び、ERC 721 などの既存の標準契約の使用方法も学びます。
プロジェクトには、実際にデプロイされているコントラクトが 1 つしかない場合でも、コードは継承を通じてメンテナンスのためにコントラクトを複数のコントラクトに分割できます。
たとえば、Uniswap では、 https://github.com/Uniswap/v3-periphery/blob/main/contracts/NonfungiblePositionManager.solコントラクトは多くのコントラクトを継承します。コードは次のとおりです。
そして、「ERC 721 Permit」コントラクトの実装を見ると、「@openzeppelin/contracts/token/ERC 721/ERC 721.sol」コントラクトを直接使用していることがわかります。これにより、ポジションを管理するのが便利になります。一方、NFT は、既存の標準契約を使用して契約開発の効率を向上させることもできます。
私たちのコースでは、 https://github.com/WTFAcademy/WTF-Dapp/blob/main/ P 108 _PositionManager /readme.mdを学習し、ポジションを管理するための簡単な ERC 721 コントラクトの開発を試すことができます。
要約する
どれだけ多くの記事を読んでも、自分で開発を始めるほど現実的ではありません。分散型取引所の単純なバージョンを自分で実装しようとする過程で、Uniswap のコード実装をより深く理解できます。実際のプロジェクトについてさらに学び、ナレッジポイントを体験してください。
WTF-DApp コースは、ZAN の開発者コミュニティと WTF Academy 開発者コミュニティの学生が共同で完了するオープンソース コースです。 Web3 および Defi プロジェクトの開発にも興味がある場合は、実践的なコースhttps://github.com/WTFAcademy/WTF-Dappを参照して、段階的に簡単なバージョンの交換を完了することが役立つと思います。助かりました〜。
この記事は、ZAN チーム (X アカウント@zan_team ) の Fisher (X アカウント@yudao 1024 ) によって書かれました。