เมื่อเร็วๆ นี้ ฉันกำลังเขียนบทช่วยสอนเกี่ยวกับการพัฒนาการแลกเปลี่ยนแบบกระจายอำนาจ https://github.com/WTFAcademy/WTF-Dapp ฉันอ้างถึงการใช้โค้ดของ Uniswap V3 และได้เรียนรู้ประเด็นความรู้มากมาย ผู้เขียนเคยพัฒนาสัญญา NFT แบบง่ายๆ มาก่อน แต่นี่เป็นครั้งแรกที่ฉันพยายามพัฒนาสัญญา Defi ฉันเชื่อว่าเคล็ดลับเหล่านี้จะเป็นประโยชน์สำหรับผู้มาใหม่ที่ต้องการเรียนรู้การพัฒนาสัญญา
ผู้เชี่ยวชาญด้านการพัฒนาสัญญาสามารถไปที่ https://github.com/WTFAcademy/WTF-Dapp ได้โดยตรงเพื่อสนับสนุนโค้ดและสนับสนุน Web3~
ต่อไป เรามาดูกลเม็ดเล็กๆ น้อยๆ เหล่านี้กัน ซึ่งบางส่วนอาจเรียกได้ว่าเป็นกลเม็ดอัศจรรย์ก็ได้
มีวิธีที่จะทำให้ที่อยู่สัญญาของการปรับใช้สัญญาสามารถคาดการณ์ได้
เมื่อเราปรับใช้สัญญา เรามักจะได้รับที่อยู่แบบสุ่มที่ดูเหมือน เนื่องจากเกี่ยวข้องกับ nonce ที่อยู่ของสัญญาจึงคาดเดาได้ยาก แต่ใน Uniswap เรามีข้อกำหนดดังกล่าว: เราจำเป็นต้องอนุมานที่อยู่ของสัญญาผ่านคู่ธุรกรรมและข้อมูลที่เกี่ยวข้อง สิ่งนี้มีประโยชน์ในหลาย ๆ สถานการณ์ เช่น การกำหนดสิทธิ์ในการทำธุรกรรม หรือการได้รับที่อยู่ของพูล เป็นต้น
ใน Uniswap การสร้างสัญญาจะถูกสร้างขึ้นผ่านโค้ด เช่น pool = address(new Uniswap V3 Pool{salt: keccak 256(abi.encode(token 0, token 1, fee))}()); โดยเติม เกลือ เพื่อใช้ CREATE 2 ( https://github.com/AmazingAng/WTF-Solidity/blob/main/25_Create2/readme.md ) เพื่อสร้างสัญญา ข้อดีคือ ที่อยู่สัญญาที่สร้างขึ้น เป็นที่คาดเดาได้ และตรรกะของการสร้างที่อยู่คือ ที่อยู่ใหม่ = hash(0x FF, ที่อยู่ของผู้สร้าง, Salt, รหัสเริ่มต้น)
คุณสามารถดูบท https://github.com/WTFAcademy/WTF-Dapp/blob/main/P103_Factory/readme.md ของหลักสูตร WTF-DApp เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับส่วนนี้
ใช้ฟังก์ชันโทรกลับให้เกิดประโยชน์
ใน Solidity สัญญาสามารถเรียกหากันได้ มีสถานการณ์สมมติที่ A เรียก B ในวิธีการบางอย่าง และ B โทรกลับ A ในวิธีที่เรียก ซึ่งยังใช้ได้ดีในบางสถานการณ์
ใน Uniswap เมื่อคุณเรียกใช้วิธี swap ของสัญญา Uniswap V3 Pool เพื่อแลกเปลี่ยน มันจะโทรกลับ swapCallback การโทรกลับจะส่งผ่านใน โทเค็น ที่คำนวณได้จริงซึ่งจำเป็นสำหรับธุรกรรมนี้ โทเค็นที่จำเป็นสำหรับธุรกรรมจะถูกโอนไปยัง Uniswap V3 Pool แทนที่จะแยกวิธี swap ออกเป็นสองส่วนเพื่อให้ผู้โทรโทรได้ สิ่งนี้ทำให้มั่นใจได้ถึงความปลอดภัยของวิธี 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 วิธีการ swap ของ Uniswap V3 Pool ถูกห่อด้วย try catch และดำเนินการ:
ทำไมเป็นเช่นนี้? เนื่องจากเราจำเป็นต้องจำลองวิธีการ สลับ เพื่อประมาณโทเค็นที่จำเป็นสำหรับการทำธุรกรรม แต่เนื่องจากการแลกเปลี่ยนโทเค็นจะไม่เกิดขึ้นจริงในระหว่างการประมาณการ ข้อผิดพลาดจะถูกรายงาน ใน Uniswap จะแสดงข้อผิดพลาดพิเศษในฟังก์ชันการเรียกกลับของธุรกรรม จากนั้นจะจับข้อผิดพลาดและแยกวิเคราะห์ข้อมูลที่จำเป็นจากข้อความแสดงข้อผิดพลาด
มันดูค่อนข้างแฮ็ก แต่ก็ใช้งานได้จริงเช่นกัน ด้วยวิธีนี้ ไม่จำเป็นต้องแก้ไขวิธีการสลับเพื่อให้ตรงกับความต้องการของธุรกรรมโดยประมาณ และตรรกะก็ง่ายขึ้น ในหลักสูตรของเรา เรายังปรับใช้สัญญา https://github.com/WTFAcademy/WTF-Dapp/blob/main/demo-contract/contracts/wtfswap/SwapRouter.sol โดยอ้างอิงถึงตรรกะนี้
ใช้ตัวเลขจำนวนมากเพื่อแก้ปัญหาที่แม่นยำ
ในโค้ดของ Uniswap มีตรรกะในการคำนวณมากมาย เช่น การคำนวณโทเค็นที่แลกเปลี่ยนตามราคาปัจจุบันและสภาพคล่อง ในกระบวนการนี้ เราต้องหลีกเลี่ยงการสูญเสียความแม่นยำระหว่างการดำเนินการหาร ใน Uniswap กระบวนการคำนวณมักจะใช้การดำเนินการ << FixPoint 96.RESOLUTION ซึ่งแสดงถึงการเลื่อนไปทางซ้าย 96 บิต ซึ่งเทียบเท่ากับการคูณด้วย 2^96 ดำเนินการแบ่งหลังกะซ้ายเพื่อรับประกันความแม่นยำไม่ล้นในธุรกรรมปกติ (ปกติจะใช้ uint 256 ในการคำนวณซึ่งก็เพียงพอแล้ว)
รหัสมีดังนี้ (คำนวณจำนวนโทเค็นที่จำเป็นสำหรับการทำธุรกรรมผ่านราคาและสภาพคล่อง):
อย่างที่คุณเห็น อันดับแรกใน Uniswap ราคาจะถูกคูณด้วยรากที่สองด้วย 2^96 (ตรงกับ sqrtRatioAX 96 และ sqrtRatioBX 96 ในโค้ดด้านบน) จากนั้นสภาพคล่อง สภาพคล่อง จะเป็น เลื่อนไปทางซ้ายเพื่อคำนวณ ตัวเศษ 1 ในการคำนวณด้านล่าง 2^96 จะถูกลบออกในระหว่างขั้นตอนการคำนวณเพื่อให้ได้ผลลัพธ์สุดท้าย
แน่นอนว่าไม่ว่าจะเกิดอะไรขึ้น ในทางทฤษฎีก็ยังคงสูญเสียความแม่นยำ แต่ในกรณีนี้คือการสูญเสียหน่วยที่เล็กที่สุดซึ่งเป็นที่ยอมรับได้
สำหรับข้อมูลเพิ่มเติม คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับหลักสูตรนี้ ได้ที่ https://github.com/WTFAcademy/WTF-Dapp/blob/main/P106_PoolSwap/readme.md
คำนวณรายได้โดยใช้วิธีแบ่งปัน
ใน Uniswap เราจำเป็นต้องบันทึกรายได้ค่าธรรมเนียมของ LP (ผู้ให้บริการสภาพคล่อง) แน่นอนว่าเราไม่สามารถบันทึกค่าธรรมเนียมการจัดการของตนเองสำหรับแต่ละ LP ในทุกธุรกรรมได้ เนื่องจากจะทำให้ใช้ Gas จำนวนมาก แล้วจะจัดการกับมันอย่างไร?
ใน Uniswap เราจะเห็นว่าโครงสร้างต่อไปนี้ถูกกำหนดไว้ใน ตำแหน่ง:
ประกอบด้วย feeGrowthInside0LastX128 และ feeGrowthInside1LastX128 ซึ่งบันทึกค่าธรรมเนียมการจัดการที่แต่ละสภาพคล่องควรได้รับเมื่อมีการถอนค่าธรรมเนียมการจัดการครั้งล่าสุดสำหรับแต่ละตำแหน่ง
พูดง่ายๆ ฉันต้องบันทึกค่าธรรมเนียมการจัดการทั้งหมดและค่าธรรมเนียมการจัดการที่ควรจัดสรรให้กับสภาพคล่องแต่ละรายการเป็นจำนวนเท่าใด เพื่อว่าเมื่อ LP ถอนค่าธรรมเนียมการจัดการ เขาสามารถคำนวณจำนวนค่าธรรมเนียมการจัดการที่เขาสามารถถอนออกได้ตามสภาพคล่องใน มือ. เช่นเดียวกับหากคุณถือหุ้นของบริษัทใดบริษัทหนึ่ง เมื่อคุณต้องการถอนกำไรจากหุ้น คุณเพียงแค่ต้องทราบกำไรต่อหุ้นในอดีตของบริษัทและรายได้เมื่อคุณถอนกำไรครั้งล่าสุด
ก่อนหน้านี้เราอยู่ใน Ingenious Contract Design มาดูกันว่า stETH กระจายรายได้โดยอัตโนมัติในแต่ละวันอย่างไร ให้ ETH ของคุณมีส่วนร่วมในการจำนำเพื่อรับดอกเบี้ยที่มั่นคง บทความนี้ยังแนะนำวิธีการคำนวณรายได้ของ stETH ซึ่งคล้ายกัน
ไม่จำเป็นต้องได้รับข้อมูลทั้งหมดจากห่วงโซ่
พื้นที่จัดเก็บข้อมูลออนไลน์มีราคาค่อนข้างแพง ดังนั้นจึงไม่จำเป็นต้องอัพโหลดหรือรับข้อมูลทั้งหมดของเราจากเครือข่ายดังกล่าว ตัวอย่างเช่น อินเทอร์เฟซจำนวนมากที่เรียกโดยเว็บไซต์ส่วนหน้าของ Uniswap นั้นเป็นอินเทอร์เฟซ Web2 แบบดั้งเดิม
รายการกลุ่มธุรกรรม ข้อมูลกลุ่มธุรกรรม ฯลฯ สามารถจัดเก็บไว้ในฐานข้อมูลทั่วไป และบางส่วนอาจจำเป็นต้องซิงโครไนซ์จากห่วงโซ่เป็นประจำ แต่เราไม่จำเป็นต้องเรียกใช้อินเทอร์เฟซ PRC ที่ห่วงโซ่หรือบริการโหนดให้บริการจริง เวลาที่จะได้รับข้อมูลที่เกี่ยวข้อง
แน่นอนว่าซัพพลายเออร์บล็อคเชน PRC จำนวนมากในขณะนี้มีอินเทอร์เฟซขั้นสูง และคุณสามารถรับข้อมูลบางอย่างได้รวดเร็วและถูกกว่า นี่เป็นเหตุผลที่คล้ายกัน ตัวอย่างเช่น ZAN มีอินเทอร์เฟซที่คล้ายกับการรับ NFT ทั้งหมดภายใต้ผู้ใช้บางราย สามารถแคชข้อมูลนี้ได้อย่างชัดเจนเพื่อปรับปรุงประสิทธิภาพและประสิทธิภาพ คุณสามารถไปที่ https://zan.top/service/advance-api เพื่อรับข้อมูลเพิ่มเติม
แน่นอนว่าธุรกรรมสำคัญจะต้องดำเนินการบนเครือข่าย
เรียนรู้วิธีแยกสัญญา และเรียนรู้การใช้สัญญามาตรฐานที่มีอยู่ เช่น ERC 721
โปรเจ็กต์อาจมีสัญญาหลายฉบับที่ใช้งานจริง แม้ว่าจะมีสัญญาเดียวที่ใช้งานจริง โค้ดของเราก็สามารถแบ่งสัญญาออกเป็นหลายสัญญาเพื่อการบำรุงรักษาผ่านการสืบทอดได้
ตัวอย่างเช่น ใน Uniswap สัญญา https://github.com/Uniswap/v3-periphery/blob/main/contracts/NonfungiblePositionManager.sol จะสืบทอดสัญญาจำนวนมาก โดยมีโค้ดดังนี้:
และเมื่อคุณดูการดำเนินการตามสัญญา ERC 721 Permit คุณจะพบว่ามีการใช้สัญญา @openzeppelin/contracts/token/ERC 721/ERC 721.sol โดยตรง ซึ่งทำให้สะดวกในการจัดการตำแหน่งผ่าน ในทางกลับกัน สัญญามาตรฐานที่มีอยู่ยังสามารถใช้เพื่อปรับปรุงประสิทธิภาพการพัฒนาสัญญาได้อีกด้วย
ในหลักสูตรของเรา คุณสามารถเรียนรู้ 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 เพื่อทำการแลกเปลี่ยนเวอร์ชันง่ายๆ ทีละขั้นตอน ฉันเชื่อว่ามันจะมีประโยชน์ ถึงคุณ. ช่วยด้วย~
บทความนี้เขียนโดย Fisher (บัญชี X @yudao 1024 ) จากทีม ZAN (บัญชี X @zan_team )