契約書がこのように書かれるとは予想していませんでしたか?これが私が最近表現した感情です〜
最近、分散型取引所開発に関するチュートリアルを書いていましたhttps://github.com/WTFAcademy/WTF-Dapp で、Uniswap V3 のコード実装を参照し、多くの知識ポイントを学びました。筆者はこれまでに簡単なNFTコントラクトを開発したことがありますが、Defiコントラクトの開発に挑戦するのは今回が初めてなので、これからコントラクト開発を学びたい初心者にとっては役立つと思います。
契約開発の専門家は、https://github.com/WTFAcademy/WTF-Dappに直接アクセスしてコードを提供し、Web3 に貢献できます~
次に、奇跡とも言える小技をご紹介します。
コントラクト展開のコントラクトアドレスを予測可能にする方法があります。
コントラクトをデプロイすると、通常、一見ランダムなアドレスが取得されます。これは「ノンス」に関連しているため、コントラクトのアドレスを予測するのは困難です。しかし、Uniswap では、トランザクション ペアと関連情報を通じてコントラクトのアドレスを推定する必要があるという要件があります。これは、トランザクション権限の決定やプールのアドレスの取得など、多くの状況で役立ちます。
Uniswap では、コントラクトの作成は、「pool = address(new UniswapV3Pool{salt: keccak256(abi.encode(token0, token1,fee))}());」のようなコードを通じて作成されます。 CREATE2 ( https://github.com/AmazingAng/WTF-Solidity/blob/main/25_Create2/readme.md ) を使用してコントラクトを作成するために「salt」を追加すると、作成されたコントラクトのアドレスが予想通り、アドレス生成のロジックは「新しいアドレス = ハッシュ("0xFF", 作成者アドレス, ソルト, initcode)」です。
この部分の詳細については、WTF-DApp コースのhttps://github.com/WTFAcademy/WTF-Dapp/blob/main/P103_Factory/readme.mdの章を参照してください。
コールバック関数を上手に活用する
Solidity では、コントラクトは相互に呼び出しを行うことができます。 A が特定のメソッドで B を呼び出し、B が呼び出されたメソッドで A にコールバックするシナリオがあります。これも一部のシナリオではうまく機能します。
Uniswap では、取引するために「UniswapV3Pool」コントラクトの「swap」メソッドを呼び出すと、「swapCallback」がコールバックされ、呼び出し元はこのトランザクションに実際に必要な計算された「トークン」を渡します。コールバックの「トークン」はトランザクションに必要なトークンです。呼び出し元が呼び出すために「swap」メソッドを 2 つの部分に分割するのではなく、「UniswapV3Pool」に転送することで、「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で、「UniswapV3Pool」の「swap」メソッドがラップされていることがわかりました。 「try catch」して実行します。
これはなぜでしょうか?トランザクションに必要なトークンを見積もるために「スワップ」方法をシミュレートする必要があるためですが、見積中に実際にはトークンの交換が行われないため、エラーが報告されます。 Uniswap では、トランザクション コールバック関数で特別なエラーをスローし、そのエラーをキャプチャして、エラー メッセージから必要な情報を解析します。
かなりハックに見えますが、非常に実用的でもあります。この方法では、推定トランザクションのニーズを満たすためにスワップ方法を変更する必要がなく、ロジックがより簡単になります。私たちのコースでは、このロジックを参照して、コントラクトhttps://github.com/WTFAcademy/WTF-Dapp/blob/main/demo-contract/contracts/wtfswap/SwapRouter.solも実装しました。
大きな数値を使用して精度の問題を解決する
Uniswap のコードには、現在の価格と流動性に基づいて交換トークンを計算するなど、多くの計算ロジックがあり、このプロセスでは除算演算中に精度が失われることを避ける必要があります。 Uniswap では、計算プロセスで「<<FixedPoint96.RESOLUTION」という演算がよく使用されます。これは 96 ビットの左シフトを表し、「2^96」を乗算するのと同じです。通常のトランザクションではオーバーフローすることなく精度を保証できるように、左シフト後に除算演算を行ってください(通常は「uint256」で計算しますので十分です)。
コードは次のとおりです (価格と流動性を通じてトランザクションに必要なトークンの数を計算します)。
Uniswap では、まず価格に「2^96」の平方根を乗算し(上記コードの「sqrtRatioAX96」と「sqrtRatioBX96」に相当)、その後流動性「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 を取得するのと同様のインターフェイスを提供しており、この情報をキャッシュしてパフォーマンスと効率を向上させることができます。
もちろん、重要なトランザクションはチェーン上で実行される必要があります。
契約の分割方法を学び、ERC721 などの既存の標準契約の使用方法も学びます。
プロジェクトには、実際にデプロイされているコントラクトが 1 つしかない場合でも、コードは継承を通じてメンテナンスのためにコントラクトを複数のコントラクトに分割できます。
たとえば、Uniswap では、 https://github.com/Uniswap/v3-periphery/blob/main/contracts/NonfungiblePositionManager.solコントラクトは多くのコントラクトを継承します。コードは次のとおりです。
「ERC721Permit」コントラクトの実装を見ると、「@openzeppelin/contracts/token/ERC721/ERC721.sol」コントラクトを直接使用していることがわかります。これにより、NFT を介してポジションを管理するのが便利になります。一方では、既存の標準契約を使用して契約開発の効率を向上させることもできます。
私たちのコースでは、 https://github.com/WTFAcademy/WTF-Dapp/blob/main/P108_PositionManager/readme.mdを学習し、ポジションを管理するための簡単な ERC721 コントラクトの開発を試すことができます。
要約する
どれだけ多くの記事を読んでも、自分で開発を始めるほど現実的ではありません。分散型取引所の単純なバージョンを自分で実装しようとする過程で、Uniswap のコード実装をより深く理解できます。実際のプロジェクトについてさらに学び、ナレッジポイントを体験してください。
WTF-DApp コースは、ZAN の開発者コミュニティと WTF Academy 開発者コミュニティの学生が共同で完了するオープンソース コースです。 Web3 および Defi プロジェクトの開発にも興味がある場合は、実践的なコースhttps://github.com/WTFAcademy/WTF-Dappを参照して、段階的に簡単なバージョンの交換を完了することが役立つと思います。助かりました〜。
この記事は、ZAN チーム (X アカウント@zan_team ) の Fisher (X アカウント@yudao1024 ) によって書かれました。