การพูดคุยสั้นๆ เกี่ยวกับ Curve: โปรโตคอลการแลกเปลี่ยน Stablecoin ที่ทรงพลังที่สุด
การแนะนำ
การแนะนำ
StableSwap
ชื่อระดับแรก
StableSwap เป็นรูปแบบการทำธุรกรรมสกุลเงินที่มีเสถียรภาพซึ่งออกแบบและนำเสนอโดย Curve ในเอกสารไวท์เปเปอร์ ซึ่งสามารถให้การคลาดเคลื่อนของธุรกรรมที่ต่ำมากและสภาพคล่องไม่จำกัด
ชื่อเรื่องรอง

เส้นโค้งผู้ดูแลสภาพคล่องของฟังก์ชันคงที่ (CFMM) ของโมเดล StableSwap เป็นดังนี้:
แนวคิดการออกแบบขึ้นอยู่กับการหลอมรวมแบบจำลองการทำตลาดสองแบบของผลรวมคงที่และผลคูณคงที่ ซึ่งมีลักษณะของการคลาดเคลื่อนต่ำของผลรวมคงที่และสภาพคล่องไม่จำกัดของผลิตภัณฑ์คงที่
ชื่อเรื่องรอง

ได้รับมา

สมมติว่ามีทั้งหมด n เหรียญที่มีเสถียรภาพ มี:

การรวมสองสูตรข้างต้นเพื่อลดความซับซ้อนจะได้สูตร (1)
คุณสมบัติ
ชื่อเรื่องรอง

Basepool
คุณสมบัติ
basepool เป็นกลุ่มการแลกเปลี่ยนพื้นฐานที่เปิดตัวโดย Curve อันที่พบมากที่สุดคือ 3pool ซึ่งประกอบด้วยสามเหรียญที่มีเสถียรภาพ DAI, USDC และ USDT
สัญญา basepool ใช้โมเดลธุรกรรม StableSwap พื้นฐาน
ชื่อเรื่องรอง
@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
การคำนวณพารามิเตอร์
อัลกอริทึมเดียวกันนี้ยังใช้ในฟังก์ชัน _get_y เพื่อคำนวณค่า 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
ความลื่นไหล
ชื่อเรื่องรอง
@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,ความลื่นไหล
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,การเพิ่มสภาพคล่องของ Curve Pool นั้นแตกต่างจากสินทรัพย์สองรายการตรงที่ Uniswap จะต้องเพิ่มคู่การซื้อขาย และ Basepool สามารถเพิ่มได้เฉพาะสินทรัพย์บางอย่างในกลุ่มเท่านั้น
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# สถานะเริ่มต้น 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) #ฮุนย่า#ทฤษฎีD1หลังเสริมสภาพคล่อง
0: #hunya# ไม่ใช่ครั้งแรกที่เพิ่มสภาพคล่องเพื่อหักค่าธรรมเนียมการจัดการ
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 หลังหักค่าดำเนินการแล้ว

Metapool เป็นพูลใหม่ที่ได้มาจากการขยายตัวของเบสพูล ซึ่งใช้เพื่อยึดสกุลเงินที่เสถียรใหม่กับโทเค็น LP ของเบสพูล ผู้ให้บริการสภาพคล่องของพูลเบสพูลสามารถเพิ่มโทเค็น LP ของเบสพูลไปยังเมตาพูลเพื่อเพิ่มสภาพคล่องอีกครั้ง และรับรายได้ค่าธรรมเนียมการทำธุรกรรมเพิ่มเติม
ในสัญญา metapool สกุลเงินที่เสถียรใหม่และสกุลเงินที่เสถียรของแหล่งรวมพื้นฐานนั้นเชื่อมโยงกันโดยอ้อม ในขณะที่ให้ฟังก์ชันการแลกเปลี่ยนระหว่างเหรียญที่มีเสถียรภาพที่แตกต่างกัน มันยังแยกความเสี่ยงของสกุลเงินที่เสถียรใหม่ในระดับหนึ่ง
ชื่อเรื่องรอง
@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
...
การแลกเปลี่ยน Stablecoin
old_balances: uint256[N_COINS] = self.balances
xp: uint256[N_COINS] = self._xp_mem(rates[MAX_COIN], old_balances)
x: uint256 = 0
ฟังก์ชัน exchange_underlying ในสัญญา metapool ตระหนักถึงฟังก์ชันการแลกเปลี่ยนระหว่างเหรียญที่มีเสถียรภาพ
x = xp[i] + dx_w_fee * rates[i] / PRECISION
ถ้า base_i < 0 หรือ base_j < 0: #hunya# swap มีโทเค็นเมตาพูล
# 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,ถ้า base_i < 0: #hunya# โทเค็นอินพุตคือโทเค็นเมตาพูล
# 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 >อื่น: #hunya# โทเค็นอินพุตคือโทเค็นพูลฐาน
out_amount: uint256 = ERC20(output_coin).balanceOf(self)
Curve(base_pool).remove_liquidity_one_coin(dy, base_j,0) #hunya#ฐานพูลเสริมสภาพคล่อง
dy = ERC20(output_coin).balanceOf(self) - out_amount
assert dy >= _min_dy, "Too few coins in result"
= 0: #hunya# โทเค็นเอาต์พุตเป็นโทเค็นพูลพื้นฐาน
# 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
อื่น: #hunya# การแลกเปลี่ยนโทเค็นพูลฐานบริสุทธิ์ เรียกใช้ฟังก์ชันการแลกเปลี่ยนพูลฐานโดยตรง
สรุป
หากการแลกเปลี่ยนเกี่ยวข้องกับโทเค็นเบสพูลและโทเค็นพูลเมตาพูล กระบวนการขั้นกลางจะทำโดยการเพิ่มหรือลบสภาพคล่องผ่าน LP ของเบสพูล หากเป็นเพียงการแลกเปลี่ยนโทเค็นเบสพูล ฟังก์ชันการแลกเปลี่ยนของเบสพูลจะถูกเรียกใช้โดยตรงสำหรับ แลกเปลี่ยน.

