คำนำ
ด้วยการพัฒนาอย่างรวดเร็วของการเงินแบบกระจายอำนาจ (DeFi) Uniswap ในฐานะการแลกเปลี่ยนแบบกระจายอำนาจชั้นนำ จึงอยู่ในระดับแนวหน้าของนวัตกรรม บทความนี้จะให้การวิเคราะห์เชิงลึกเกี่ยวกับกลไกหลักของโปรโตคอล Uniswap v3 และคำอธิบายโดยละเอียดเกี่ยวกับการออกแบบฟังก์ชัน รวมถึงฟังก์ชันหลักๆ เช่น สภาพคล่องแบบรวมศูนย์ อัตราหลายอัตรา การแลกเปลี่ยนโทเค็น และสินเชื่อแฟลช ในขณะเดียวกันก็จัดให้มีจุดตรวจสอบที่เกี่ยวข้องสำหรับ ผู้ตรวจสอบบัญชี (หมายเหตุ: สามารถดูภาพในบทความนี้แบบความคมชัดสูงได้ที่ https://www.figma.com/board/QyIpAUR93MxZ4XZZf2QjDk/uniswap-v3)
การวิเคราะห์ทางสถาปัตยกรรม
โปรโตคอล Uniswap v3 ส่วนใหญ่ประกอบด้วยสี่โมดูล:
- PositionManager: อินเทอร์เฟซหลักสำหรับผู้ใช้ในการดำเนินการเกี่ยวกับสภาพคล่อง ซึ่งผู้ใช้สามารถสร้างกลุ่มโทเค็น จัดเตรียม/ลบสภาพคล่อง และใช้ ERC721 เป็นข้อมูลรับรองสำหรับผู้ให้บริการสภาพคล่อง (LP)
- SwapRouter: ทางเข้าสำหรับผู้ใช้ในการแลกเปลี่ยนโทเค็น ผู้ใช้สามารถดำเนินการแลกเปลี่ยนโทเค็นให้เสร็จสิ้นผ่านโมดูลนี้
- กลุ่ม: รับผิดชอบในการใช้ธุรกรรมโทเค็น การจัดการสภาพคล่อง การเก็บค่าธรรมเนียมธุรกรรม และฟังก์ชันการจัดการข้อมูลของ Oracle กลไก Tick แบ่งช่วงราคาออกเป็นหลายระดับที่ดี
- โรงงาน: ใช้เพื่อสร้างและจัดการสัญญาพูล
การเรียงลำดับกระบวนการ
สร้างคู่โทเค็น
ผู้ใช้สามารถทำได้โดยใช้ฟังก์ชัน createAndInitializePoolIfNecessary ผู้ใช้จำเป็นต้องส่งโทเค็น 0, โทเค็น 1, ค่าธรรมเนียมการจัดการ (ค่าธรรมเนียม) และราคาเริ่มต้น () ของคู่โทเค็น ขั้นแรก ระบบจะตรวจสอบว่าคู่โทเค็นมีอยู่แล้วผ่านฟังก์ชัน getPool หรือไม่ หากไม่ได้ถูกสร้างขึ้น ระบบจะเรียกใช้ createPool และคำสั่ง CREATE2 จะถูกนำมาใช้เพื่อปรับใช้คู่การซื้อขาย สุดท้ายนี้ ฟังก์ชันการเริ่มต้นจะใช้เพื่อเริ่มต้นราคา ค่าธรรมเนียมการจัดการ เห็บ ออราเคิล และพารามิเตอร์อื่นๆ ที่เกี่ยวข้องให้เสร็จสมบูรณ์
ให้สภาพคล่อง
ให้สภาพคล่อง
ผู้ใช้สามารถสร้างสถานะสภาพคล่องใหม่และสร้าง NFT ที่สอดคล้องกันผ่านฟังก์ชัน mint หรือเพิ่มสภาพคล่องให้กับสถานะสภาพคล่อง NFT ที่มีอยู่ผ่านฟังก์ชันเพิ่มสภาพคล่อง ขั้นแรก ระบบจะตรวจสอบว่าธุรกรรมถูกดำเนินการภายในช่วงเวลาที่ระบุหรือไม่ จากนั้นเรียกใช้ฟังก์ชัน addLiquidity เพื่อดำเนินการเฉพาะให้เสร็จสมบูรณ์ ในฟังก์ชันนี้ ที่อยู่และสภาพคล่องของพูลจะถูกคำนวณก่อน จากนั้นจึงเรียก _updatePosition เพื่ออัปเดตตำแหน่งของผู้ใช้ แก้ไขขีดล่าง ขีดบน และค่าธรรมเนียมการจัดการสะสมทั้งหมด ต่อจากนั้น ระบบจะเพิ่มสภาพคล่องผ่าน _modifyPosition ตรวจสอบให้แน่ใจว่าเครื่องหมายถูกตรงตามเงื่อนไขขอบเขตบนและล่าง ส่งคืนหมายเลขที่คำนวณได้ของ token0 และ token1 (int256) และส่งไปที่พูล สุดท้าย ระบบจะอัปเดตข้อมูลตำแหน่งที่เกี่ยวข้องตาม tokenId ของผู้ใช้
ถอนสภาพคล่อง
ผู้ใช้สามารถลบสภาพคล่องผ่านฟังก์ชันลดสภาพคล่อง ขั้นแรก ระบบจะตรวจสอบสิทธิ์ของใบรับรอง LP และความถูกต้องของเวลาของธุรกรรม ภายใต้หลักฐานเพื่อให้แน่ใจว่าพูลมีสภาพคล่องเพียงพอ ให้เรียกใช้ฟังก์ชันเบิร์นเพื่อลบสภาพคล่อง ต่อจากนั้น ระบบจะตรวจสอบว่าจำนวนโทเค็นจริงที่ถูกลบออกตรงตามข้อกำหนดขั้นต่ำที่กำหนดโดยผู้ใช้หรือไม่ และอัปเดตข้อมูลตำแหน่งของผู้ใช้ตามนั้น
แลกเปลี่ยน
ผู้ใช้สามารถระบุจำนวนโทเค็นที่ต้องชำระและจำนวนโทเค็นขั้นต่ำที่คาดว่าจะได้รับผ่านฟังก์ชัน exactInput หรือระบุจำนวนโทเค็นสูงสุดที่จะจ่ายและกำหนดจำนวนโทเค็นที่คาดว่าจะได้รับผ่านฟังก์ชัน exactOutput ขั้นแรกระบบจะแยกวิเคราะห์เส้นทาง (เส้นทาง) จากนั้นเรียกใช้ฟังก์ชันactInputInternalหรือexactOutputInternalตามลำดับเพื่อทำให้แต่ละขั้นตอนของการดำเนินการสลับเสร็จสมบูรณ์
ในฟังก์ชันสลับ ระบบจะล็อกสถานะปลดล็อกก่อน เพื่อป้องกันไม่ให้ธุรกรรมอื่นรบกวนการอัปเดตตัวแปรสถานะ หลังจากเข้าสู่ลูป ระบบจะค้นหาราคาธุรกรรมถัดไปผ่านเครื่องหมายถูก และเรียกใช้ฟังก์ชัน computeSwapStep เพื่อคำนวณการแลกเปลี่ยนในแต่ละขั้นตอนจนกว่า tokenIn หรือ tokenOut จะถึงความคาดหวังของผู้ใช้ ในเวลาเดียวกันระบบจะอัพเดตค่าที่เกี่ยวข้องของค่าธรรมเนียม สภาพคล่อง เห็บ และราคา หากเครื่องหมายถูกเปลี่ยนแปลง ข้อมูล Oracle ก็จำเป็นต้องได้รับการอัปเดตด้วย หลังจากเสร็จสิ้นการดำเนินการเหล่านี้ ระบบจะจ่าย tokenOut ให้กับผู้ใช้ และผู้ใช้จะจ่าย tokenIn ผ่านฟังก์ชัน callback uniswapV3SwapCallback จากนั้นระบบจะตรวจสอบว่ายอดสัญญาตรงกันและปลดล็อคสถานะปลดล็อคหลังจากการยืนยัน
ธุรกรรมสิ้นสุดลงได้สำเร็จเมื่อการดำเนินการสลับทั้งหมดในเส้นทางเสร็จสมบูรณ์และธุรกรรมตรงตามความคาดหวังของผู้ใช้
แฟลช
ผู้ใช้สามารถดำเนินการยืมแฟลชผ่านฟังก์ชันแฟลชได้ ขั้นแรก ระบบจะคำนวณค่าธรรมเนียมการยืม จากนั้นจึงส่งโทเค็นที่ผู้ใช้ต้องการไปยังที่อยู่การให้ยืมที่กำหนด ถัดไป ระบบจะเรียกกลับฟังก์ชัน uniswapV3FlashCallback ที่ผู้ใช้ใช้งาน และผู้ใช้ดำเนินการชำระคืนให้เสร็จสิ้นในฟังก์ชันนี้ ระบบจะตรวจสอบการเปลี่ยนแปลงยอดคงเหลือสัญญาหลังจากการเรียกกลับเพื่อให้แน่ใจว่าสอดคล้องกับจำนวนเงินกู้ของผู้ใช้ และอัปเดตค่าธรรมเนียมการจัดการที่เกี่ยวข้อง นอกเหนือจากฟังก์ชันแฟลชแล้ว ผู้ใช้ยังสามารถใช้ฟังก์ชันแฟลชยืมที่คล้ายกันผ่านการดำเนินการสลับ กล่าวคือ ยืมก่อนแล้วจึงชำระคืนโทเค็นในระหว่างกระบวนการทำธุรกรรม
จุดตรวจสอบ
จุดตรวจสอบ
1. ตรวจสอบว่ามีการเรียกการคืนเงิน ETH หลังจากการดำเนินการสลับหรือไม่
ในฟังก์ชัน exactInput ผู้ใช้จำเป็นต้องระบุจำนวนโทเค็นที่จะจ่ายและจำนวนโทเค็นขั้นต่ำที่คาดว่าจะได้รับ ก่อนที่จะเรียก uniswapV3SwapCallback ระบบจะคำนวณ amount0 และ amount1 ใหม่เพื่อให้แน่ใจว่าผู้ใช้สามารถส่งโทเค็นได้อย่างถูกต้อง อย่างไรก็ตาม เมื่อทำการแลกเปลี่ยนกับ ETH ผู้ใช้จะต้องส่ง ETH ไปพร้อมกับธุรกรรม แม้ว่าจะไม่ได้ใช้ ETH ทั้งหมดในระหว่างการทำธุรกรรม ฟังก์ชันจะไม่ส่งคืนส่วนที่เกินโดยอัตโนมัติ ฟังก์ชันactInputจะส่งคืนเฉพาะ amountOut เท่านั้น ดังนั้นเทรดเดอร์จึงไม่สามารถทราบได้โดยตรงว่าการแลกเปลี่ยนนี้ใช้ ETH เป็นจำนวนเท่าใด
นอกจากนี้ ทุกคนสามารถเรียกใช้ฟังก์ชันการคืนเงิน ETH เพื่อถอน ETH ที่ไม่ได้ใช้ออกจากสัญญาได้ ดังนั้นจึงขอแนะนำให้ตรวจสอบว่ามีการเรียกการคืนเงิน ETH หลังจากการดำเนินการสลับเพื่อป้องกันไม่ให้ผู้ใช้ทิ้ง ETH ที่ไม่ได้ใช้ไว้ในโปรโตคอล หรือใช้ฟังก์ชัน MultiCall เพื่อทำการเรียกใช้ฟังก์ชันหลายรายการในการดำเนินการครั้งเดียว
2. ตรวจสอบว่ามีการใช้ TWAP เพื่อให้ได้ราคา oracle หรือไม่
เมื่อใช้ Uniswap เป็นแหล่งที่มาของราคา อาจมีความเสี่ยงของการปั่นราคาเมื่อโปรโตคอลภายนอกเข้าถึง Slot0 โดยตรงเพื่อรับ sqrtPriceX96 ผู้โจมตีสามารถจัดการสถานะของกลุ่มสภาพคล่องผ่านการแลกเปลี่ยนและวิธีการอื่น ๆ เพื่อให้ได้ราคาที่ดีเมื่อดำเนินธุรกรรม
เพื่อลดความเสี่ยงนี้ ขอแนะนำให้นักพัฒนาใช้ราคาเฉลี่ยถ่วงเวลา (TWAP) เพิ่มเติมเพื่อรับราคา เนื่องจาก TWAP สามารถลดผลกระทบจากความผันผวนของราคาอย่างรุนแรงในระยะสั้นได้อย่างมีประสิทธิภาพ ทำให้ยากต่อการควบคุมราคา
3. แนะนำให้ผู้ใช้ตั้งค่าพารามิเตอร์การเลื่อนไหลได้ด้วยตัวเอง
เมื่อโปรโตคอลอื่นใช้ Uniswap v3 สำหรับการดำเนินการสลับ ขอแนะนำให้นักพัฒนาตั้งค่าการป้องกัน Slippage ตามสถานการณ์ทางธุรกิจ และอนุญาตให้ผู้ใช้ปรับพารามิเตอร์ด้วยตนเองเพื่อป้องกันการโจมตีแบบแซนวิช ในฟังก์ชันสลับนี้ พารามิเตอร์ตัวที่สี่ sqrtPriceLimitX96 ใช้เพื่อระบุราคาต่ำสุดหรือสูงสุดที่ผู้ใช้ยินดีดำเนินการสลับ พารามิเตอร์นี้สามารถป้องกันความผันผวนของราคาที่รุนแรงในระหว่างกระบวนการทำธุรกรรมได้อย่างมีประสิทธิภาพ ซึ่งช่วยลดความสูญเสียของผู้ใช้เนื่องจากความคลาดเคลื่อนที่มากเกินไป
4. ขอแนะนำให้แนะนำกลไกไวท์ลิสต์ของพูลสภาพคล่อง
ใน Uniswap v3 โทเค็น ERC20 คู่เดียวกันอาจมีอยู่ในกลุ่มสภาพคล่อง (Pool) หลายแห่งพร้อมกันโดยขึ้นอยู่กับค่าธรรมเนียมที่แตกต่างกัน โดยทั่วไปแล้ว กลุ่มสภาพคล่องบางแห่งจะครอบครองสภาพคล่องส่วนใหญ่ ในขณะที่กลุ่มอื่นๆ อาจมี Total Volume Locked (TVL) น้อยมากหรือยังไม่ได้สร้างด้วยซ้ำ กลุ่ม TVL ที่ต่ำกว่าเหล่านี้มีแนวโน้มที่จะเป็นเป้าหมายสำหรับการควบคุมราคา
ดังนั้น เมื่อฝ่ายโครงการเลือกใช้ข้อมูลกลุ่มสภาพคล่อง พวกเขาควรหลีกเลี่ยงการใช้ LP เป็นแหล่งข้อมูลเพียงอย่างเดียว เพื่อให้มั่นใจในความน่าเชื่อถือของข้อมูล ขอแนะนำให้แนะนำกลไกไวท์ลิสต์เพื่อคัดกรองพูลที่มีสภาพคล่องเพียงพอและมีความยากในการจัดการ กลไกนี้สามารถลดความเสี่ยงได้อย่างมาก ทำให้มั่นใจในความปลอดภัยและความถูกต้องของข้อมูลอ้างอิงราคา ในขณะเดียวกันก็ป้องกันการสูญเสียที่อาจเกิดขึ้นจากการจัดการพูลที่มี TVL ต่ำเกินไป
5. ตรวจสอบว่ามีการใช้เครื่องหมายที่ไม่ได้เลือกใน TickMath.sol, FullMath.sol และ Position.sol หรือไม่
โมดูลต่างๆ เช่น TickMath, FullMath และ Position ใช้ใน Uniswap v3 เพื่อทำการคำนวณทางคณิตศาสตร์ที่ซับซ้อน ซึ่งอาศัยกลไกการจัดการโอเวอร์โฟลว์ใน Solidity ในเวอร์ชันก่อนหน้าของ Solidity (<0.8.0) จำนวนเต็มล้นและพฤติกรรมอันเดอร์โฟลว์ไม่ได้เกิดข้อยกเว้นตามค่าเริ่มต้น ดังนั้นโค้ดจึงสามารถทำงานได้ตามปกติตามสมมติฐานนี้ อย่างไรก็ตาม เริ่มต้นจาก Solidity เวอร์ชัน 0.8.0 การโอเวอร์โฟลว์และอันเดอร์โฟลว์จะส่งข้อยกเว้นโดยอัตโนมัติ ซึ่งจะส่งผลต่อการทำงานของโค้ดที่มีอยู่ เพื่อให้แน่ใจว่าโมดูลเหล่านี้ทำงานได้อย่างถูกต้องใน Solidity 0.8.0 และสูงกว่า นักพัฒนาจำเป็นต้องปิดใช้งานการตรวจสอบโอเวอร์โฟลว์ด้วยตนเองโดยใช้บล็อคโค้ดที่ไม่ได้ตรวจสอบในฟังก์ชันเฉพาะ วิธีนี้จะคืนค่าลักษณะการทำงานจากเวอร์ชันก่อนหน้าและรับประกันการดำเนินการที่ละเอียดอ่อนต่อโอเวอร์โฟลว์อย่างมีประสิทธิภาพ
มีการสนับสนุนและการปรับเปลี่ยนอย่างเป็นทางการสำหรับ Solidity 0.8.0 และสูงกว่า สำหรับรายละเอียด โปรดดูการอัปเดตนี้ (https://github.com/Uniswap/v3-core/commit/6562c52e8f75f0c10f9deaf44861847585fc8129) การเปลี่ยนแปลงนี้ช่วยให้แน่ใจว่า TickMath, FullMath และโมดูลที่เกี่ยวข้องอื่นๆ จะยังคงทำงานได้อย่างถูกต้องภายใต้คอมไพเลอร์เวอร์ชันใหม่
6. ตรวจสอบว่าวิธีการเข้ารหัสและถอดรหัสเส้นทางเหมือนกันหรือไม่
ในฟังก์ชันactInputและactOutputของ Uniswap v3 ผู้ใช้จำเป็นต้องป้อนพารามิเตอร์ path ซึ่งจะต้องเข้ารหัสและถอดรหัสตามรูปแบบคงที่ ซึ่งก็คือ tokenA-fee-tokenB สำหรับการดำเนินการแลกเปลี่ยนโทเค็นทีละขั้นตอน โครงสร้างเส้นทางนี้ระบุโทเค็นทั้งสองที่เกี่ยวข้องในการกระโดดแต่ละครั้งของธุรกรรมและระดับค่าธรรมเนียมระหว่างโทเค็นอย่างชัดเจน หากโปรโตคอลภายนอกเลือกวิธีการถอดรหัสเส้นทางอื่นเมื่อใช้ฟังก์ชันการแลกเปลี่ยนโทเค็นของ Uniswap v3 อาจส่งผลให้รูปแบบเส้นทางไม่สอดคล้องกับรูปแบบเส้นทางที่คาดหวังของ Uniswap ในกรณีนี้ โปรโตคอลอาจไม่สามารถแก้ไขเส้นทางได้อย่างถูกต้อง และทำให้ไม่สามารถดำเนินการแลกเปลี่ยนโทเค็นที่ต้องการได้สำเร็จ
ดังนั้น ขอแนะนำให้นักพัฒนาตรวจสอบให้แน่ใจว่าโปรโตคอลภายนอกปฏิบัติตามกฎการเข้ารหัสเส้นทางของ Uniswap อย่างเคร่งครัดเมื่อรวมฟังก์ชันการแลกเปลี่ยนโทเค็นของ Uniswap v3 เพื่อป้องกันข้อผิดพลาดในการถอดรหัสเส้นทาง โปรโตคอลภายนอกควรตรวจสอบรูปแบบของพารามิเตอร์เส้นทางอย่างระมัดระวังเมื่อเรียกค่าที่แน่นอนและค่าที่แน่นอนเพื่อหลีกเลี่ยงความล้มเหลวของธุรกรรมหรือผลลัพธ์ที่ไม่คาดคิด
ดังนั้น ขอแนะนำให้นักพัฒนาตรวจสอบให้แน่ใจว่าโปรโตคอลภายนอกปฏิบัติตามกฎการเข้ารหัสเส้นทางของ Uniswap อย่างเคร่งครัดเมื่อรวมฟังก์ชันการแลกเปลี่ยนโทเค็นของ Uniswap v3 เพื่อป้องกันข้อผิดพลาดในการถอดรหัสเส้นทาง โปรโตคอลภายนอกควรตรวจสอบรูปแบบของพารามิเตอร์เส้นทางอย่างระมัดระวังเมื่อเรียกค่าที่แน่นอนและค่าที่แน่นอนเพื่อหลีกเลี่ยงความล้มเหลวของธุรกรรมหรือผลลัพธ์ที่ไม่คาดคิด
7. ตรวจสอบว่าลำดับโทเค็นส่งผลต่อตรรกะของโครงการหรือไม่
ใน Uniswap นั้น token0 เป็นโทเค็นที่มีลำดับต่ำกว่าและใช้เป็นโทเค็นฐาน ในขณะที่ token1 เป็นโทเค็นที่มีลำดับสูงกว่าและใช้เป็นโทเค็นเครื่องหมายคำพูด Uniswap จัดเรียงที่อยู่ของโทเค็นทั้งสองตามพจนานุกรมเพื่อให้แน่ใจว่าลำดับของคู่โทเค็นจะสอดคล้องกันเสมอในกลุ่ม
อย่างไรก็ตาม เนื่องจากที่อยู่สัญญาของโทเค็นเดียวกันบนเครือข่ายบล็อกเชนที่แตกต่างกันอาจแตกต่างกัน โดยเฉพาะอย่างยิ่งสำหรับสัญญาที่ใช้งานข้ามเชน ลำดับการจัดเรียงของโทเค็นจึงอาจเปลี่ยนแปลงได้ การเปลี่ยนแปลงนี้จะทำให้บทบาทของ token0 และ token1 ถูกย้อนกลับ ซึ่งส่งผลต่อประสิทธิภาพของราคา ตัวอย่างเช่น ในบางเชน โทเค็นเฉพาะอาจเป็น token0 แต่บนเชนอื่นๆ อาจถูกเรียงลำดับเป็น token1 ส่งผลให้เกิดความสัมพันธ์ที่แตกต่างกันระหว่างโทเค็นฐานและโทเค็นที่เสนอราคา ซึ่งส่งผลต่อราคาที่แสดงในที่สุด ดังนั้น ขอแนะนำให้นักพัฒนาตรวจสอบว่าลำดับของโทเค็นจะส่งผลกระทบต่อตรรกะของโครงการหรือไม่ โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมแบบ cross-chain โปรดพิจารณาปัญหาด้านราคาที่อาจเกิดจากลำดับของโทเค็นเพื่อหลีกเลี่ยงผลกระทบด้านลบต่อประสิทธิภาพของราคาและ ตรรกะการทำธุรกรรม
สรุป
รายการตรวจสอบพื้นฐานข้างต้นอิงตามเวอร์ชันปัจจุบันของ Uniswap v3 และผู้ตรวจสอบใช้เพื่อตรวจสอบโปรเจ็กต์ที่มีการโต้ตอบกับ Uniswap v3 การดำเนินโครงการต่างๆ มีลักษณะเฉพาะของตัวเอง ดังนั้นผู้ตรวจสอบจำเป็นต้องมีความเข้าใจเชิงลึกเกี่ยวกับข้อตกลงและดำเนินการตรวจสอบอย่างเข้มงวดตามสถานการณ์จริง สำหรับโครงการที่อยู่ระหว่างการพัฒนา ทีมรักษาความปลอดภัย SlowMist แนะนำให้นักพัฒนาพิจารณาการตรวจสอบเหล่านี้อย่างรอบคอบในระหว่างกระบวนการพัฒนา เพื่อให้มั่นใจในความปลอดภัยและความน่าเชื่อถือของโปรโตคอล
ผู้เขียน |. ซิสซี่
บรรณาธิการ |. ลิซ
ความคิดเห็นทั้งหมด