Ủa sao nói UniswapV3 vẫn là constant-product mà có thấy dòng nào trong code đảm bảo đâu?
2. Suy luận từ V2 sang V3
2.1. Nhắc lại một chút về V2
Ai cũng biết, công thức toán khi swap của Uniswap là constant-product, nghĩa là: . Giá trị k trước khi swap và sau khi swap phải được giữ nguyên, k chỉ thay đổi khi có hành động thêm hoặc rút thanh khoản.
Dễ hiểu hơn, ban đầu Pool đang có reserves là token0 và token1. Alice muốn swap một lượng token0 lấy một lượng token1. Thì và phải thoả mãn:
Ví dụ: ban đầu , . Alice muốn swap 20 token0 lấy token1 thì lượng token1 mà Alice nhận được là:
Và, thanh khoản của Pool được tính theo công thức:
Mắc đi hát karaoke quá mà ở quê không ai rủ đi.
Tại sao công thức tính L lại là ?
Suy luận một chút, tính chất đầu tiên của L cần phải được thoả mãn là: phải được giữ nguyên giá trị trước khi swap và sau khi swap. Nếu lấy công thức , tính chất đó sẽ không thể thoả mãn, mà ta đã có (hằng số), do đó L sẽ là một công thức gì đó liên quan đến , dạng .
Tính chất thứ hai mà cần phải được thoả mãn là tính tỷ lệ, nghĩa là khi một liquidity provider thêm thanh khoản vào Pool, lượng liquidity được ghi nhận cho provider đó phải có tỷ lệ tương xứng với liquidity của tổng Pool dưa trên số lượng token0 và token1 mà provider đó đã thêm vào.
Ví dụ, Pool đang có reserves là token0 và token1, có tổng liquidity là , Alice thêm token0 và token1 vào Pool, liquidity được ghi nhận cho Alice , phải thoả mãn:
hoặc
Nếu chúng ta sử dụng công thức , công thức trên không thể thoả mãn, hãy cho các con số vào để dễ hình dung hơn.
và
Rõ ràng là công thức không thể thoả mãn tính tỷ lệ.
Nếu thì sao? Hãy thử với các con số ở trên:
Như vậy, công thức thoả mãn tính tỷ lệ.
Oke, tiếp theo, tại sao lại hoặc giữa , . Hãy nhìn vào code một tí, hàm mint() của UniswapV2Pair.sol
Nếu là provider đầu tiên thêm thanh khoản thì sẽ được tính theo công thức , dòng 119-120.
Những provider tiếp theo chỉ cần tính theo công thức (dòng 122, 123) là sẽ tự động thoả mãn 2 tính chất ở trên. Điều này cho phép những provider này không cần thêm thành khoản đúng tỷ lệ , với mục đích có thể làm thay đổi price luôn trong lúc thêm thanh khoản, cho nên phải là .
2.2. Chuyển đổi từ V2 sang V3
Mặc dụ V2 ko quy ước về công thức giá, nhưng bây giờ chúng ta hãy quy ước rằng P được xem là giá của token0 trên token1, nghĩa là 1 token0 = P token1. Nghĩa là
Chúng ta có thể biểu diễn tương quan của như sau (lưu ý, hình này không đúng về mặt toán học, đừng cố chia tỷ lệ độ dài hay diện tích làm gì, nó chỉ minh hoạ ra cho dễ hiểu):
Chúng ta có thể thấy những điều sau với Pool V2:
- sẽ có khoảng giá trị (price-range) là , nhưng sẽ không bao giờ chạm đến 0
- càng tăng, nghĩa là lượng token0 trong pool càng nhiều thì càng giảm
- càng tăng, nghĩa là lượng token1 trong pool càng nhiều thì càng tăng
- Bên phải của , chỉ chứa toàn token0. Bên trái của chỉ chứa toàn token1
Ok, bây giờ hãy qua lại điểm khác nhau về tính năng của V2 và V3 là: thanh khoản khi một provider thêm vào V2, provider không có quyền chọn price-range, sẽ được hoà tan hoàn toàn vào price-range . Còn ở V3, provider có quyền chọn price-range cho lượng thanh khoản của mình, khi nào current-price đi vào price-range mà provider đã chọn thì lượng thanh khoản mà anh ấy đã thêm vào mới được phép mang ra sử dụng, và L của mỗi price-range có giá trị khác nhau.
Từ hình mình hoạ của V2, chúng ta có thể mở rộng ra minh hoạ cho V3 như sau:
Để ra được hình minh hoạ này thì mình tư duy như sau:
-
Lấy A,B,C,D,E,F,G,H là những tick mà index của chúng đều chia hết cho tick-spacing, chúng không cần phải cách đều nhau.
-
Đầu tiên gom cả PoolV2 thành 1 price-range nhất định, ở đây là [D,E). Trong price range này, sẽ có vừa có cả token0 và token1, giá hiện tại (current-price) đang là ở điểm D1 thuộc [D,E) và . Và nếu sau khi swap, price không vượt qua khỏi [, , thì lệnh swap đó mới được sử dụng token0 hoặc token1 trong thanh khoản
-
Bên trái của current-price vẫn sẽ toàn là token1, bên phải của current-price vẫn sẽ toàn là token0.
-
Và như mình đã nói ở phần trước, liquidity provider có thể thêm thanh khoản vào bất cứ price-range nào họ muốn. Do đó, L của các đoạn sẽ nhấp nhô (không bằng nhau). Khi họ thêm thanh khoản vào price-range lớn hơn current-price-range, họ chỉ được phép deposit token0. Khi họ thêm thanh khoản vào price-range nhỏ hơn current-price-range, họ chỉ được phép deposit token1.
Nhưng có một vấn đề ở đây là, với các đoạn [E,F), [F,G), [G,H) chỉ toàn token0 và các đoạn [A,B), [B,C), [C,D) chỉ toàn là token1 thì làm sao xác định được L của mỗi đoạn nếu dựa vào công thức thuần tuý , chẳng phải tất cả L sẽ là 0 sao?