Thảo luận ngắn gọn về Curve: Giao thức trao đổi Stablecoin mạnh mẽ nhất
Giới thiệu
Giới thiệu
StableSwap
tiêu đề cấp đầu tiên
StableSwap là một mô hình giao dịch tiền tệ ổn định được Curve thiết kế và đề xuất trong sách trắng, có thể cung cấp mức trượt giao dịch cực thấp và thanh khoản không giới hạn.
tiêu đề phụ

Đường cong nhà tạo lập thị trường hàm hằng số (CFMM) của mô hình StableSwap như sau:
Khái niệm thiết kế dựa trên sự kết hợp của hai mô hình tạo thị trường tổng không đổi và sản phẩm không đổi, có đặc điểm là độ trượt thấp của tổng không đổi và tính thanh khoản không giới hạn của sản phẩm không đổi
tiêu đề phụ

lấy được

Giả sử rằng có tổng cộng n đồng tiền ổn định, có:

Kết hợp 2 công thức trên để đơn giản ta được công thức (1)
đặc trưng
tiêu đề phụ

Basepool
đặc trưng
basepool là nhóm trao đổi cơ bản do Curve đưa ra. Phổ biến nhất là 3pool, bao gồm ba đồng tiền ổn định DAI, USDC và USDT
Hợp đồng basepool triển khai mô hình giao dịch StableSwap cơ bản
tiêu đề phụ
@pure
@internal
def _get_D(_xp: uint256[N_COINS], _amp: uint256) -> uint256:
"""
D invariant calculation in non-overflowing integer operations
iteratively
A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))
Converging solution:
D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
"""
S: uint256 = 0
Dprev: uint256 = 0
for _x in _xp:
S += _x
if S == 0:
return 0
D: uint256 = S
Ann: uint256 = _amp * N_COINS
for _i in range(255):
D_P: uint256 = D
for _x in _xp:
D_P = D_P * D / (_x * N_COINS) # If division by 0, this will be borked: only withdrawal will work. And that is good
Dprev = D
D = (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) *D_P)
# Equality with the precision of 1
if D > Dprev:
if D - Dprev <= 1:
return D
else:
if Dprev - D <= 1:
return D
# convergence typically occurs in 4 rounds or less, this should be unreachable!
# if it does happen the pool is borked and LPs can withdraw via `remove_liquidity`
raise
Tính toán thông số
Thuật toán tương tự cũng được sử dụng trong hàm _get_y để tính giá trị y
@view
@internal
def _get_y(i: int128, j: int128, x: uint256, _xp: uint256[N_COINS]) -> uint256:
"""
Calculate x[j] if one makes x[i] = x
Done by solving quadratic equation iteratively.
x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
x_1**2 + b*x_1 = c
x_1 = (x_1**2 + c) / (2*x_1 + b)
"""
# x in the input is converted to the same price/precision
assert i != j # dev: same coin
assert j >= 0 # dev: j below zero
assert j < N_COINS # dev: j above N_COINS
# should be unreachable, but good for safety
assert i >= 0
assert i < N_COINS
A: uint256 = self._A()
D: uint256 = self._get_D(_xp, A)
Ann: uint256 = A * N_COINS
c: uint256 = D
S: uint256 = 0
_x: uint256 = 0
y_prev: uint256 = 0
for _i in range(N_COINS):
if _i == i:
_x = x
elif _i != j:
_x = _xp[_i]
else:
continue
S += _x
c = c * D / (_x * N_COINS)
c = c * D * A_PRECISION / (Ann * N_COINS)
b: uint256 = S + D * A_PRECISION / Ann # - D
y: uint256 = D
for _i in range(255):
y_prev = y
y = (y*y + c) / (2 * y + b - D)
# Equality with the precision of 1
if y > y_prev:
if y - y_prev <= 1:
return y
else:
if y_prev - y <= 1:
return y
raise
tính trôi chảy
tiêu đề phụ
@external
@nonreentrant('lock')
def add_liquidity(_amounts: uint256[N_COINS], _min_mint_amount: uint256) -> uint256:
"""
@notice Deposit coins into the pool
@param _amounts List of amounts of coins to deposit
@param _min_mint_amount Minimum amount of LP tokens to mint from the deposit
@return Amount of LP tokens received by depositing
"""
assert not self.is_killed # dev: is killed
amp: uint256 = self._A()
old_balances: uint256[N_COINS] = self.balances
# Initial invariant
D0: uint256 = self._get_D_mem(old_balances,tính trôi chảy
lp_token: address = self.lp_token
token_supply: uint256 = CurveToken(lp_token).totalSupply()
new_balances: uint256[N_COINS] = old_balances
for i in range(N_COINS):
if token_supply == 0:
assert _amounts[i] > 0 # dev: initial deposit requires all coins
# balances store amounts of c-tokens
new_balances[i] += _amounts[i]
# Invariant after change
D1: uint256 = self._get_D_mem(new_balances,Việc bổ sung tính thanh khoản của nhóm Curve khác với hai tài sản mà Uniswap phải thêm các cặp giao dịch và nhóm cơ sở chỉ có thể thêm một tài sản nhất định vào nhóm
assert D1 > D0
# We need to recalculate the invariant accounting for fees
# to calculate fair user's share
D2: uint256 = D1
fees: uint256[N_COINS] = empty(uint256[N_COINS])
mint_amount: uint256 = 0
if token_supply >amp) #hunya# trạng thái ban đầu D0
# Only account for fees if we are not the first to deposit
fee: uint256 = self.fee * N_COINS / (4 * (N_COINS - 1))
admin_fee: uint256 = self.admin_fee
for i in range(N_COINS):
ideal_balance: uint256 = D1 * old_balances[i] / D0
difference: uint256 = 0
new_balance: uint256 = new_balances[i]
if ideal_balance > new_balance:
difference = ideal_balance - new_balance
else:
difference = new_balance - ideal_balance
fees[i] = fee * difference / FEE_DENOMINATOR
self.balances[i] = new_balance - (fees[i] * admin_fee / FEE_DENOMINATOR)
new_balances[i] -= fees[i]
D2 = self._get_D_mem(new_balances,amp) #hunya# lý thuyết D1 sau khi thêm thanh khoản
0: #hunya# Đây không phải là lần đầu tiên thêm thanh khoản để trừ phí xử lý
else:
self.balances = new_balances
mint_amount = D1 # Take the dust if there was any
assert mint_amount >= _min_mint_amount, "Slippage screwed you"
# Take coins from the sender
for i in range(N_COINS):
if _amounts[i] > 0:
# "safeTransferFrom" which works for ERC20s which return bool or not
_response: Bytes[32] = raw_call(
self.coins[i],
concat(
method_id("transferFrom(address,address,uint256)"),
convert(msg.sender, bytes32),
convert(self, bytes32),
convert(_amounts[i], bytes32),
),
max_outsize=32,
)
if len(_response) > 0:
assert convert(_response, bool) # dev: failed transfer
# end "safeTransferFrom"
# Mint pool tokens
CurveToken(lp_token).mint(msg.sender, mint_amount)
log AddLiquidity(msg.sender, _amounts, fees, D1, token_supply + mint_amount)
return mint_amount
Metapool
amp) #hunya# D2 sau khi trừ phí xử lý

Metapool là một pool mới bắt nguồn từ việc mở rộng basepool, được sử dụng để neo đồng tiền ổn định mới vào mã thông báo LP của basepool. Nhà cung cấp thanh khoản của nhóm cơ sở có thể thêm mã thông báo LP của nhóm cơ sở vào metapool để thêm thanh khoản trở lại và kiếm thêm thu nhập từ phí giao dịch.
Trong hợp đồng metapool, đồng tiền ổn định mới và đồng tiền ổn định của nhóm cơ bản được kết hợp gián tiếp. Trong khi cung cấp chức năng trao đổi giữa các đồng tiền ổn định khác nhau, nó cũng phân tách rủi ro của đồng tiền ổn định mới ở một mức độ nhất định.
tiêu đề phụ
@external
@nonreentrant('lock')
def exchange_underlying(i: int128, j: int128, _dx: uint256, _min_dy: uint256) -> uint256:
....
# Use base_i or base_j if they are >= 0
base_i: int128 = i - MAX_COIN
base_j: int128 = j - MAX_COIN
meta_i: int128 = MAX_COIN
meta_j: int128 = MAX_COIN
if base_i < 0:
meta_i = i
if base_j < 0:
meta_j = j
dy: uint256 = 0
...
Trao đổi tiền ổn định
old_balances: uint256[N_COINS] = self.balances
xp: uint256[N_COINS] = self._xp_mem(rates[MAX_COIN], old_balances)
x: uint256 = 0
Hàm exchange_underlying trong hợp đồng metapool thực hiện chức năng trao đổi lẫn nhau giữa các đồng tiền ổn định
x = xp[i] + dx_w_fee * rates[i] / PRECISION
nếu base_i < 0 hoặc base_j < 0: #hunya# hoán đổi bao gồm mã thông báo nhóm meta
# i is from BasePool
# At first, get the amount of pool tokens
base_inputs: uint256[BASE_N_COINS] = empty(uint256[BASE_N_COINS])
base_inputs[base_i] = dx_w_fee
coin_i: address = self.coins[MAX_COIN]
# Deposit and measure delta
x = ERC20(coin_i).balanceOf(self)
Curve(base_pool).add_liquidity(base_inputs,nếu base_i < 0: #hunya# mã thông báo đầu vào là mã thông báo nhóm meta
# Need to convert pool token to "virtual" units using rates
# dx is also different now
dx_w_fee = ERC20(coin_i).balanceOf(self) - x
x = dx_w_fee * rates[MAX_COIN] / PRECISION
# Adding number of pool tokens
x += xp[MAX_COIN]
...
# Withdraw from the base pool if needed
if base_j >khác: #hunya# Mã thông báo đầu vào là mã thông báo nhóm cơ sở
out_amount: uint256 = ERC20(output_coin).balanceOf(self)
Curve(base_pool).remove_liquidity_one_coin(dy, base_j,0) #hunya# nhóm cơ sở thêm tính thanh khoản
dy = ERC20(output_coin).balanceOf(self) - out_amount
assert dy >= _min_dy, "Too few coins in result"
= 0: #hunya# mã thông báo đầu ra là mã thông báo nhóm cơ sở
# If both are from the base pool
dy = ERC20(output_coin).balanceOf(self)
Curve(base_pool).exchange(base_i, base_j, dx_w_fee, _min_dy)
dy = ERC20(output_coin).balanceOf(self) - dy
...
log TokenExchangeUnderlying(msg.sender, i, _dx, j, dy)
return dy
khác: #hunya# Trao đổi mã thông báo nhóm cơ sở thuần túy, gọi trực tiếp chức năng trao đổi nhóm cơ sở
tóm tắt
Nếu việc trao đổi liên quan đến mã thông báo basepool và mã thông báo nhóm metapool, quy trình trung gian sẽ được thực hiện bằng cách thêm hoặc xóa tính thanh khoản thông qua LP của nhóm cơ sở; nếu đó chỉ là việc trao đổi mã thông báo cơ sở, chức năng trao đổi của nhóm cơ sở sẽ được gọi trực tiếp cho trao đổi.

