Kata pengantar

Dengan pesatnya perkembangan keuangan terdesentralisasi (DeFi), Uniswap, sebagai bursa terdesentralisasi terkemuka, telah menjadi yang terdepan dalam inovasi. Artikel ini akan memberikan analisis mendalam tentang mekanisme inti protokol Uniswap v3 dan penjelasan rinci tentang desain fungsionalnya, termasuk fungsi utama seperti likuiditas terpusat, tarif berganda, pertukaran token, dan pinjaman kilat, sekaligus memberikan poin audit yang relevan untuk auditor. (Catatan: Gambar dalam artikel ini dapat dilihat dalam resolusi tinggi di https://www.figma.com/board/QyIpAUR93MxZ4XZZf2QjDk/uniswap-v3.)

Analisis singkat arsitektur

Protokol Uniswap v3 terutama terdiri dari empat modul:

  • PositionManager: Antarmuka utama bagi pengguna untuk melakukan operasi likuiditas.

  • SwapRouter: Pintu masuk bagi pengguna untuk bertukar token. Pengguna dapat menyelesaikan operasi pertukaran token melalui modul ini.

  • Pool: Bertanggung jawab untuk mengimplementasikan transaksi token, manajemen likuiditas, mengumpulkan biaya transaksi, dan fungsi manajemen data Oracle. Diantaranya, mekanisme Tick membagi kisaran harga menjadi beberapa skala kecil.

  • Factory:用于创建和管理 Pool 合约。

Penyortiran proses

Buat pasangan token

Pengguna dapat melakukan ini melalui fungsi createAndInitializePoolIfNecessary. Pengguna harus memasukkan token0, token1, biaya penanganan (biaya) dan harga awal () dari pasangan token. Pertama, sistem akan memeriksa apakah pasangan token sudah ada melalui fungsi getPool. Jika belum dibuat, createPool akan dipanggil dan instruksi CREATE2 akan digunakan untuk menyebarkan pasangan perdagangan. Terakhir, fungsi inisialisasi digunakan untuk menyelesaikan inisialisasi harga, biaya penanganan, tick, oracle, dan parameter terkait lainnya.

Menyediakan likuiditas

Pengguna dapat membuat posisi likuiditas baru dan menghasilkan NFT yang sesuai melalui fungsi mint, atau menambahkan likuiditas ke posisi likuiditas NFT yang ada melalui fungsi peningkatanLikuiditas. Pertama, sistem akan memeriksa apakah transaksi dieksekusi dalam rentang waktu yang ditentukan, dan kemudian memanggil fungsi addLiquidity untuk menyelesaikan operasi tertentu. Dalam fungsi ini, alamat dan likuiditas kumpulan pertama kali dihitung, dan kemudian updatePosition dipanggil untuk memperbarui posisi pengguna, memodifikasi centang bawah, atas, dan total biaya penanganan yang terakumulasi. Selanjutnya, sistem menambahkan likuiditas melalui modifyposition, memastikan bahwa centang memenuhi kondisi batas atas dan bawah, mengembalikan jumlah token0 dan token1 (INT256) yang dihitung, dan mengirimkannya ke kumpulan. Terakhir, sistem memperbarui informasi Posisi terkait berdasarkan tokenId pengguna.

Hapus likuiditas

Pengguna dapat menghapus likuiditas melalui fungsi penurunanLikuiditas. Pertama, sistem memeriksa otoritas sertifikat LP dan validitas waktu transaksi. Dengan alasan untuk memastikan bahwa kumpulan memiliki likuiditas yang cukup, panggil fungsi pembakaran untuk menghapus likuiditas. Sistem kemudian akan memverifikasi apakah jumlah sebenarnya token yang dihapus memenuhi persyaratan minimum yang ditetapkan oleh pengguna, dan memperbarui informasi Posisi pengguna sesuai dengan itu.

menukar

Pengguna dapat menentukan jumlah token yang akan dibayarkan dan jumlah minimum token yang diharapkan diperoleh melalui fungsi ExactInput, atau menentukan jumlah maksimum token yang akan dibayarkan dan mengatur jumlah token yang diharapkan diperoleh melalui fungsi ExactOutput. Sistem pertama-tama menguraikan jalur (path), dan kemudian memanggil fungsi ExactInputInternal atau ExactOutputInternal secara berurutan untuk menyelesaikan setiap langkah operasi swap.

Dalam fungsi swap, sistem terlebih dahulu mengunci status tidak terkunci untuk mencegah transaksi lain mengganggu pembaruan variabel status. Setelah memasuki loop, sistem menemukan harga transaksi berikutnya melalui tick dan memanggil fungsi computeSwapStep untuk menghitung pertukaran pada setiap langkah hingga tokenIn atau tokenOut mencapai harapan pengguna. Pada saat yang sama, sistem akan memperbarui nilai terkait biaya, likuiditas, tick, dan harga. Jika centang berubah, data Oracle juga perlu diperbarui. Setelah menyelesaikan operasi ini, sistem membayar tokenOut kepada pengguna, dan pengguna membayar tokenIn melalui fungsi panggilan balik uniswapV3SwapCallback. Mekanisme ini dapat dianggap sebagai flash swap. Selanjutnya, sistem akan memeriksa apakah saldo kontrak cocok dan membuka kunci status tidak terkunci setelah konfirmasi.

Suatu transaksi berakhir dengan sukses ketika semua operasi swap di jalur telah selesai dan transaksi memenuhi harapan pengguna.

kilatan

Pengguna dapat melakukan operasi pinjaman flash melalui fungsi flash. Pertama, sistem akan menghitung biaya peminjaman, dan kemudian mengirimkan token yang dibutuhkan pengguna ke alamat peminjaman yang ditentukan.接下来,系统回调用户实现的 uniswapV3FlashCallback 函数,用户在此函数中完成还款操作。系统会在回调后检查合约余额的变化,确保其与用户借贷的数量相符,同时更新相应的手续费。除了 flash 函数,用户也可以通过 swap 操作实现类似的闪电贷功能,即在交易过程中先借入再偿还 token。

Poin audit

1. Periksa apakah refundETH dipanggil setelah operasi swap

Dalam fungsi ExactInput, pengguna perlu menentukan jumlah token yang harus dibayar dan jumlah minimum token yang diharapkan diperoleh. Sebelum memanggil uniswapV3SwapCallback, sistem akan menghitung ulang jumlah0 dan jumlah1 untuk memastikan bahwa pengguna dapat mengirimkan token secara akurat. Namun, saat menukar dengan ETH, pengguna perlu mengirimkan ETH bersamaan dengan transaksinya. Meskipun seluruh ETH tidak digunakan selama transaksi, fungsi tersebut tidak akan secara otomatis mengembalikan kelebihannya. Fungsi ExactInput hanya mengembalikan amountOut, sehingga pedagang tidak dapat mengetahui secara langsung berapa banyak ETH yang sebenarnya dikonsumsi oleh bursa ini.

Selain itu, siapa pun dapat memanggil fungsi refundETH untuk menarik ETH yang tidak digunakan dari kontrak. Oleh karena itu, disarankan untuk memeriksa apakah refundETH dipanggil setelah operasi pertukaran untuk mencegah pengguna meninggalkan ETH yang tidak digunakan dalam protokol, atau untuk menggunakan fungsi MultiCall untuk menyelesaikan beberapa panggilan fungsi dalam satu operasi.

2. Periksa apakah TWAP diterapkan untuk mendapatkan harga oracle

Saat menggunakan Uniswap sebagai sumber harga, mungkin terdapat risiko manipulasi harga jika protokol eksternal langsung mengakses Slot0 untuk mendapatkan sqrtPriceX96. Penyerang dapat memanipulasi status kumpulan likuiditas melalui swap dan metode lain untuk mendapatkan harga yang menguntungkan saat melakukan transaksi.

为了降低这种风险,建议开发者进一步实现时间加权平均价格(TWAP) 来获取价格,因为 TWAP 能有效减少短期内价格的剧烈波动影响,使操纵价格的难度增加。

3. Disarankan untuk mengizinkan pengguna mengatur sendiri parameter slippage

当其他协议使用 Uniswap v3 进行 swap 操作时,建议开发者根据业务场景设置滑点保护,并允许用户自行调整参数,以防止遭受三明治攻击。在此 swap 函数中,第四个参数 sqrtPriceLimitX96 用于指定用户愿意执行交换的最低或最高价格。这一参数可有效防止在交易过程中价格出现极端波动,从而降低用户因滑点过大而产生的损失。

4. Direkomendasikan untuk memperkenalkan mekanisme daftar putih kumpulan likuiditas

Di Uniswap v3, pasangan token ERC20 yang sama mungkin ada di beberapa kumpulan likuiditas (Pools) secara bersamaan berdasarkan biaya yang berbeda. Biasanya, beberapa kolam likuiditas memiliki sebagian besar likuiditas, sementara kolam lain mungkin memiliki volume total terkunci (TVL) atau bahkan belum dibuat. Kumpulan TVL yang lebih rendah ini lebih cenderung menjadi target manipulasi harga.

Oleh karena itu, ketika pihak proyek memilih untuk menggunakan data kumpulan likuiditas, mereka harus menghindari penggunaan LP sebagai sumber data. Untuk memastikan keandalan data, disarankan untuk memperkenalkan mekanisme daftar putih untuk menyaring kumpulan dengan likuiditas yang cukup dan kesulitan dalam manipulasi. Mekanisme ini secara signifikan mengurangi risiko, memastikan keamanan dan keakuratan data referensi harga, sambil mencegah potensi kerugian akibat manipulasi kumpulan dengan TVL yang terlalu rendah.

5. 检查是否在 TickMath.sol、FullMath.sol 和 Position.sol 中使用 unchecked

TickMath、FullMath 和 Position 等模块在 Uniswap v3 中用于执行复杂的数学计算,这些计算依赖于 Solidity 中的溢出处理机制。在早期的 Solidity 版本(<0.8.0)中,整数溢出和下溢行为默认不抛出异常,因此代码可以基于这种假设进行正常运行。 Namun, mulai Solidity versi 0.8.0, overflow dan underflow secara otomatis memunculkan pengecualian, yang memengaruhi eksekusi kode yang ada.为确保这些模块在 Solidity 0.8.0 及更高版本中正常运行,开发者需要在特定函数中使用 unchecked 代码块,手动禁用溢出检查。 Ini memulihkan perilaku dari versi sebelumnya dan memastikan eksekusi yang efisien dari operasi yang sensitif terhadap overflow.

Dukungan resmi dan penyesuaian telah dilakukan untuk Solidity 0.8.0 dan lebih tinggi. Untuk detailnya, silakan lihat pembaruan ini (https://github.com/Uniswap/v3-core/commit/6562c52e8f75f0c10f9deaf44861847585fc8129). Perubahan ini memastikan bahwa TickMath, FullMath, dan modul terkait lainnya akan terus berjalan dengan benar pada versi kompiler yang baru.

6. Periksa apakah metode pengkodean dan penguraian jalur sama

Dalam fungsi ExactInput dan ExactOutput Uniswap v3, pengguna perlu memasukkan parameter jalur, yang harus dikodekan dan didekodekan sesuai dengan format tetap, yaitu tokenA-fee-tokenB, untuk operasi pertukaran token langkah demi langkah.这个路径结构明确指定了每一跳交易中涉及的两个代币以及它们之间的手续费级别。 Jika protokol eksternal memilih metode decoding jalur yang berbeda saat menggunakan fungsi pertukaran token Uniswap v3, hal ini dapat mengakibatkan format jalur yang tidak konsisten dengan format jalur yang diharapkan Uniswap. Dalam kasus ini, protokol mungkin tidak dapat menyelesaikan jalur dengan benar sehingga gagal melakukan operasi pertukaran token yang diinginkan.

因此,建议开发者在集成 Uniswap v3 的代币交换功能时,确保外部协议严格遵循 Uniswap 的路径编码规则。为防止出现路径解码错误,外部协议应在调用 exactInput 和 exactOutput 时,仔细检查 path 参数的格式,以避免交易失败或获得意外的结果。

7. Periksa apakah urutan token mempengaruhi logika proyek

在 Uniswap 中,token0 是排序顺序较低的代币,用作基础代币(base token),而 token1 是排序顺序较高的代币,用作报价代币(quote token)。 UNISWAP mengurutkan alamat dua token secara leksikografis untuk memastikan bahwa urutan pasangan token selalu konsisten di kolam renang.

Namun, karena alamat kontrak dari token yang sama pada jaringan blockchain yang berbeda mungkin berbeda, terutama untuk kontrak yang digunakan lintas rantai, urutan token dapat berubah. Perubahan ini akan menyebabkan peran token0 dan token1 terbalik, sehingga mempengaruhi kinerja harga. Misalnya, pada beberapa rantai, token tertentu mungkin berupa token0, namun pada rantai lain, token tersebut mungkin diurutkan sebagai token1, sehingga menghasilkan hubungan yang berbeda antara token dasar dan token yang dikutip, yang pada akhirnya memengaruhi harga yang ditampilkan. Oleh karena itu, disarankan agar pengembang memeriksa apakah urutan token akan mempengaruhi logika proyek. Terutama di lingkungan lintas rantai, pastikan untuk mempertimbangkan masalah harga yang mungkin disebabkan oleh urutan token untuk menghindari dampak buruk pada kinerja harga dan logika transaksi.

Meringkaskan

Item pemeriksaan dasar di atas didasarkan pada versi Uniswap v3 saat ini dan digunakan oleh auditor untuk memeriksa proyek yang berinteraksi dengan Uniswap v3. Pelaksanaan proyek yang berbeda memiliki karakteristiknya masing-masing, sehingga auditor perlu memiliki pemahaman mendalam tentang perjanjian dan melakukan pemeriksaan ketat berdasarkan situasi sebenarnya. Untuk proyek yang sedang dikembangkan, tim keamanan SlowMist merekomendasikan agar pengembang mempertimbangkan pemeriksaan ini dengan cermat selama proses pengembangan untuk memastikan keamanan dan keandalan protokol.

Penulis |. Kakak

Penyunting |