UNPKG

@symmetry-hq/liquidity-sdk

Version:

Exchange functionality using symmetry funds liquidity

205 lines (204 loc) 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.computeOutputAmount = exports.compute_amount_of_bought_token = exports.compute_value_of_sold_token = exports.usd_value_to_amount = exports.amount_to_usd_value = exports.mul_div = exports.WEIGHT_MULTIPLIER = exports.BPS_DIVIDER = void 0; exports.BPS_DIVIDER = 10000; exports.WEIGHT_MULTIPLIER = 10000; function mul_div(a, b, c) { if (c == 0) return 0; return Math.floor(a * b / c); } exports.mul_div = mul_div; function amount_to_usd_value(amount, decimals, price) { return mul_div(amount, price, 10 ** decimals); } exports.amount_to_usd_value = amount_to_usd_value; function usd_value_to_amount(worth, decimals, price) { return mul_div(worth, 10 ** decimals, price); } exports.usd_value_to_amount = usd_value_to_amount; function compute_value_of_sold_token(amount, token_settings, price, start_amount, target_amount, curve_data) { let current_amount = start_amount; let curve_offset = (start_amount > target_amount) ? (start_amount - target_amount) : 0; let current_output_value = 0; let amount_left = amount; let current_price = price.sell_price; let total_fees = 0; for (let step = 0; step <= 10; step++) { let step_amount = (step < 10) ? curve_data.amount[step] : amount_left; if (step < 10 && curve_data.price[step] < current_price) { if (token_settings.useCurveData == true) { current_price = curve_data.price[step]; } } if (step == 10) { curve_offset = 0; } if (step_amount <= curve_offset) { curve_offset -= curve_data.amount[step]; continue; } let amount_in_interval = step_amount - curve_offset; curve_offset = 0; if (amount_in_interval > amount_left) { amount_in_interval = amount_left; } ; let amount_before_tw = amount_in_interval; if (current_amount >= target_amount) { amount_before_tw = 0; } else if (current_amount + amount_in_interval >= target_amount) { amount_before_tw -= current_amount + amount_in_interval - target_amount; } let amount_after_tw = amount_in_interval - amount_before_tw; let value_before_tw = amount_to_usd_value(amount_before_tw, token_settings.decimals, current_price); let value_after_tw = amount_to_usd_value(amount_after_tw, token_settings.decimals, current_price); let fees = mul_div(value_before_tw, token_settings.tokenSwapFeeBeforeTwBps, exports.BPS_DIVIDER) + mul_div(value_after_tw, token_settings.tokenSwapFeeAfterTwBps, exports.BPS_DIVIDER); total_fees += fees; current_output_value += value_before_tw + value_after_tw - fees; amount_left -= amount_in_interval; current_amount += amount_in_interval; if (amount_left == 0) { break; } } ; let fee_bps_x100 = mul_div(total_fees, exports.BPS_DIVIDER * 100, total_fees + current_output_value); return current_output_value; } exports.compute_value_of_sold_token = compute_value_of_sold_token; function compute_amount_of_bought_token(value, token_settings, price, start_amount, target_amount, curve_data) { let current_amount = start_amount; let curve_offset = (start_amount < target_amount) ? (target_amount - start_amount) : 0; let current_output_amount = 0; let value_left = value; let current_price = price.buy_price; let total_fees = 0; for (let step = 0; step <= 10; step++) { let step_amount = (step < 10) ? curve_data.amount[step] : usd_value_to_amount(value_left * 2, token_settings.decimals, current_price); if (step < 10 && curve_data.price[step] > current_price) { if (token_settings.useCurveData == true) { current_price = curve_data.price[step]; } ; } if (step == 10) { curve_offset = 0; } if (step_amount <= curve_offset) { curve_offset -= curve_data.amount[step]; continue; } let amount_in_interval = step_amount - curve_offset; curve_offset = 0; let value_in_interval = amount_to_usd_value(amount_in_interval, token_settings.decimals, current_price); if (value_in_interval > value_left) { value_in_interval = value_left; amount_in_interval = usd_value_to_amount(value_in_interval, token_settings.decimals, current_price); } let value_before_tw = value_in_interval; if (current_amount <= target_amount) { value_before_tw = 0; } else if (current_amount <= target_amount + amount_in_interval) { value_before_tw -= amount_to_usd_value(target_amount + amount_in_interval - current_amount, token_settings.decimals, current_price); } let value_after_tw = value_in_interval - value_before_tw; let fees = mul_div(value_before_tw, token_settings.tokenSwapFeeBeforeTwBps, exports.BPS_DIVIDER) + mul_div(value_after_tw, token_settings.tokenSwapFeeAfterTwBps, exports.BPS_DIVIDER); let amount_bought = usd_value_to_amount(value_in_interval - fees, token_settings.decimals, current_price); current_output_amount += amount_bought; value_left -= value_in_interval; total_fees += fees; if (amount_bought > current_amount) { current_amount = 0; } else { current_amount -= amount_bought; } if (value_left == 0) { break; } } ; let fee_bps_x100 = mul_div(total_fees, exports.BPS_DIVIDER * 100, value); return current_output_amount; } exports.compute_amount_of_bought_token = compute_amount_of_bought_token; function computeOutputAmount(from_token_id, to_token_id, from_amount, fundState, oracle_prices, curve_data, token_list) { fundState.currentCompToken = fundState.currentCompToken.map(x => x.toNumber()); fundState.currentCompAmount = fundState.currentCompAmount.map(x => x.toNumber()); fundState.targetWeight = fundState.targetWeight.map(x => x.toNumber()); fundState.weightSum = fundState.weightSum.toNumber(); fundState.numOfTokens = fundState.numOfTokens.toNumber(); let from_token_settings = token_list.list[from_token_id]; let to_token_settings = token_list.list[to_token_id]; let from_token_index = fundState.currentCompToken.findIndex((x) => x == from_token_id); let to_token_index = fundState.currentCompToken.findIndex((x) => x == to_token_id); if (from_token_index == -1 || to_token_index == -1) return { toAmount: undefined, feePct: undefined }; let fund_worth = 0; for (let i = 0; i < fundState.numOfTokens; i++) { let token = fundState.currentCompToken[i]; let token_settings = token_list.list[token]; let token_price = oracle_prices[token]; fund_worth += amount_to_usd_value(fundState.currentCompAmount[i], token_settings.decimals, token_price.avg_price); } let from_token_price = oracle_prices[from_token_id]; let to_token_price = oracle_prices[to_token_id]; let from_token_target_amount = usd_value_to_amount(mul_div(fundState.targetWeight[from_token_index], fund_worth, fundState.weightSum), from_token_settings.decimals, from_token_price.avg_price); let to_token_target_amount = usd_value_to_amount(mul_div(fundState.targetWeight[to_token_index], fund_worth, fundState.weightSum), to_token_settings.decimals, to_token_price.avg_price); let value = compute_value_of_sold_token(from_amount, from_token_settings, from_token_price, fundState.currentCompAmount[from_token_index], from_token_target_amount, curve_data.sell[from_token_id]); let to_amount = compute_amount_of_bought_token(value, to_token_settings, to_token_price, fundState.currentCompAmount[to_token_index], to_token_target_amount, curve_data.buy[to_token_id]); let amount_without_fees = usd_value_to_amount(amount_to_usd_value(from_amount, from_token_settings.decimals, from_token_price.sell_price), to_token_settings.decimals, to_token_price.buy_price); let fair_amount = usd_value_to_amount(amount_to_usd_value(from_amount, from_token_settings.decimals, from_token_price.avg_price), to_token_settings.decimals, to_token_price.avg_price); if (amount_without_fees > fundState.currentCompAmount[to_token_index]) { amount_without_fees = fundState.currentCompAmount[to_token_index]; } if (to_amount > amount_without_fees) { to_amount = amount_without_fees; } let total_fees = amount_without_fees - to_amount; let symmetry_bps = token_list.list[0].additionalData[60]; let symmetry_fee = mul_div(total_fees, symmetry_bps, 100); let host_bps = token_list.list[0].additionalData[61]; let host_fee = mul_div(total_fees, host_bps, 100); let manager_bps = token_list.list[0].additionalData[62]; let manager_fee = mul_div(total_fees, manager_bps, 100); let fund_fee = total_fees - symmetry_fee - host_fee - manager_fee; let confidence_bps = mul_div(fair_amount - amount_without_fees, exports.BPS_DIVIDER * 100, fair_amount); let fee_bps = mul_div(amount_without_fees - to_amount, exports.BPS_DIVIDER * 100, fair_amount); let from_token_worth_before_swap = amount_to_usd_value(fundState.currentCompAmount[from_token_index], from_token_settings.decimals, from_token_price.avg_price); let to_token_worth_before_swap = amount_to_usd_value(fundState.currentCompAmount[to_token_index], to_token_settings.decimals, to_token_price.avg_price); fundState.currentCompAmount[to_token_index] -= amount_without_fees - fund_fee; fundState.currentCompAmount[from_token_index] += from_amount; let from_token_worth_after_swap = amount_to_usd_value(fundState.currentCompAmount[from_token_index], from_token_settings.decimals, from_token_price.avg_price); let to_token_worth_after_swap = amount_to_usd_value(fundState.currentCompAmount[to_token_index], to_token_settings.decimals, to_token_price.avg_price); let from_old_weight = mul_div(from_token_worth_before_swap, exports.WEIGHT_MULTIPLIER, fund_worth); let to_old_weight = mul_div(to_token_worth_before_swap, exports.WEIGHT_MULTIPLIER, fund_worth); fund_worth = fund_worth - from_token_worth_before_swap; fund_worth = fund_worth - to_token_worth_before_swap; fund_worth = fund_worth + from_token_worth_after_swap; fund_worth = fund_worth + to_token_worth_after_swap; let from_new_weight = mul_div(from_token_worth_after_swap, exports.WEIGHT_MULTIPLIER, fund_worth); let to_new_weight = mul_div(to_token_worth_after_swap, exports.WEIGHT_MULTIPLIER, fund_worth); let allowed_offset = fundState.rebalanceThreshold.toNumber() * fundState.lpOffsetThreshold.toNumber(); let allowed_from_target_weight = mul_div(fundState.targetWeight[from_token_index], exports.BPS_DIVIDER * exports.BPS_DIVIDER + allowed_offset, exports.BPS_DIVIDER * exports.BPS_DIVIDER); let allowed_to_target_weight = mul_div(fundState.targetWeight[to_token_index], exports.BPS_DIVIDER * exports.BPS_DIVIDER - allowed_offset, exports.BPS_DIVIDER * exports.BPS_DIVIDER); if (allowed_from_target_weight > exports.WEIGHT_MULTIPLIER) { allowed_from_target_weight = exports.WEIGHT_MULTIPLIER; } let removing_dust = (from_token_id == 0 && fundState.targetWeight[to_token_index] == 0); if (from_new_weight > allowed_from_target_weight && (!removing_dust)) return { toAmount: undefined, feePct: undefined }; if (to_new_weight < allowed_to_target_weight) return { toAmount: undefined, feePct: undefined }; return { toAmount: Math.floor(to_amount), feePct: fee_bps / 100, }; } exports.computeOutputAmount = computeOutputAmount;