@external @nonreentrant('lock') def add_liquidity(_amounts: uint256[N_COINS], _min_mint_amount: uint256) -> uint256: assert not self.is_killed // Проверяем, что смарт-контракт не уничтожен amp: uint256 = self._A() old_balances: uint256[N_COINS] = self.balances // Высчитываем значение инварианты D0: uint256 = self._get_D_mem(old_balances, amp) 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 // Обязательно в первый раз вложить все токены пула для инициализации его инварианты # balances store amounts of c-tokens new_balances[i] += _amounts[i] // Пересчитываем инварианту с новыми балансами пула D1: uint256 = self._get_D_mem(new_balances, amp) // После добавления ликвидности, новое значение инварианты должно быть больше assert D1 > D0 // Теперь нужно пересчитать инварианту D с учетом комиссии D2: uint256 = D1 fees: uint256[N_COINS] = empty(uint256[N_COINS]) mint_amount: uint256 = 0 // Если это не первое добавление ликвидности в пул if token_supply > 0: 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) // Рассчитываем количество lp токена mint_amount = token_supply * (D2 - D0) / D0 // Если это первое добавление ликвидности else: // Сохраняем значения балансов self.balances = new_balances // Количество lp токена будет равняться значению инварианты mint_amount = D1 assert mint_amount >= _min_mint_amount, "Slippage screwed you" // Аналог вызова safeTransferFrom для того, чтобы переместить активы от пользователя в пул ликвидности for i in range(N_COINS): if _amounts[i] > 0: _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" // Взамен ликвидности выдаем lp токен CurveToken(lp_token).mint(msg.sender, mint_amount) log AddLiquidity(msg.sender, _amounts, fees, D1, token_supply + mint_amount) return mint_amount Для защиты от проскальзывания def exchange(i: int128, j: int128, _dx: uint256, _min_dy: uint256) -> uint256: assert not self.is_killed // Проверяем, что контракт не уничтожен old_balances: uint256[N_COINS] = self.balances // Получаем список балансов по активам приведенным к единой точности xp: uint256[N_COINS] = self._xp_mem(old_balances) rates: uint256[N_COINS] = RATES // Рассчитываем количество актива в пуле после того, как пользователь добавит этот актив x: uint256 = xp[i] + _dx * rates[i] / PRECISION // Рассчитываем количество актива в пуле после того, как пользователь заберет его y: uint256 = self._get_y(i, j, x, xp) // Количество получаемого актива пользователем dy: uint256 = xp[j] - y - 1 // Для меня тут магия с округлением // Рассчитываем комиссию в выходном активе dy_fee: uint256 = dy * self.fee / FEE_DENOMINATOR // Рассчитываем реальное значение суммы получаемого пользователем актив с учетом точности актива и за вычетом комиссии. Это классический путь, но если у протокола будет достаточно veCRV, то можно проголосовать за свой пул и пул начнет раздавать CRV, что сделает его привлекательным для сторонних поставщиков ликвидности, так как они будут получать CRV и крутить его в Curve для получения дополнительного вознаграждения.
Author: pnaydanovgoo
Published at: 2025-05-14 04:37:15
Still want to read the full version? Full article