@sora-substrate/liquidity-proxy
Advanced tools
Comparing version 1.35.6 to 1.35.7
@@ -24,2 +24,6 @@ import { FPNumber } from '@sora-substrate/math'; | ||
static readonly MAX: FPNumber; | ||
/** Manimal significant balance */ | ||
static readonly MIN: FPNumber; | ||
/** Irreducible reserve percent = 1% */ | ||
static readonly IrreducibleReserve: FPNumber; | ||
/** ETH & DAI which are incentivized */ | ||
@@ -32,7 +36,11 @@ static readonly incentivizedCurrenciesNum: FPNumber; | ||
export declare enum Errors { | ||
PriceCalculationFailed = "An error occurred while calculating the price.", | ||
CalculationError = "Specified parameters lead to arithmetic error", | ||
CantExchange = "Liquidity source can't exchange assets with the given IDs on the given DEXId.", | ||
PoolIsEmpty = "The pool has empty liquidity.", | ||
InsufficientLiquidity = "None of the sources has enough reserves to execute a trade", | ||
InvalidOrderAmount = "Invalid Order Amount", | ||
InvalidFeeRatio = "Invalid fee ratio value.", | ||
NotEnoughLiquidityInOrderBook = "Not Enough Liquidity In OrderBook", | ||
NotEnoughReserves = "It's not enough reserves in the pool to perform the operation.", | ||
PoolIsEmpty = "The pool has empty liquidity.", | ||
PriceCalculationFailed = "An error occurred while calculating the price.", | ||
SyntheticDoesNotExist = "Synthetic asset does not exist.", | ||
@@ -42,4 +50,3 @@ SyntheticBaseBuySellLimitExceeded = "Input/output amount of synthetic base asset exceeds the limit", | ||
UnknownOrderBook = "Order book does not exist for this trading pair", | ||
NotEnoughLiquidityInOrderBook = "Not Enough Liquidity In OrderBook", | ||
InvalidOrderAmount = "Invalid Order Amount", | ||
UnsupportedQuotePath = "Attempt to quote via unsupported path, i.e. both output and input tokens are not XOR.", | ||
UnsupportedLiquiditySource = "Unsupported liquidity source" | ||
@@ -46,0 +53,0 @@ } |
@@ -21,2 +21,4 @@ "use strict"; | ||
Consts.MAX = new _math.FPNumber('170141183460469231731.687303715884105727'); | ||
Consts.MIN = _math.FPNumber.fromCodecValue(1); | ||
Consts.IrreducibleReserve = new _math.FPNumber(0.01); | ||
Consts.incentivizedCurrenciesNum = _math.FPNumber.TWO; | ||
@@ -28,7 +30,11 @@ Consts.initialPswapTbcRewardsAmount = new _math.FPNumber(2500000000); | ||
(function (Errors) { | ||
Errors["PriceCalculationFailed"] = "An error occurred while calculating the price."; | ||
Errors["CalculationError"] = "Specified parameters lead to arithmetic error"; | ||
Errors["CantExchange"] = "Liquidity source can't exchange assets with the given IDs on the given DEXId."; | ||
Errors["PoolIsEmpty"] = "The pool has empty liquidity."; | ||
Errors["InsufficientLiquidity"] = "None of the sources has enough reserves to execute a trade"; | ||
Errors["InvalidOrderAmount"] = "Invalid Order Amount"; | ||
Errors["InvalidFeeRatio"] = "Invalid fee ratio value."; | ||
Errors["NotEnoughLiquidityInOrderBook"] = "Not Enough Liquidity In OrderBook"; | ||
Errors["NotEnoughReserves"] = "It's not enough reserves in the pool to perform the operation."; | ||
Errors["PoolIsEmpty"] = "The pool has empty liquidity."; | ||
Errors["PriceCalculationFailed"] = "An error occurred while calculating the price."; | ||
Errors["SyntheticDoesNotExist"] = "Synthetic asset does not exist."; | ||
@@ -38,4 +44,3 @@ Errors["SyntheticBaseBuySellLimitExceeded"] = "Input/output amount of synthetic base asset exceeds the limit"; | ||
Errors["UnknownOrderBook"] = "Order book does not exist for this trading pair"; | ||
Errors["NotEnoughLiquidityInOrderBook"] = "Not Enough Liquidity In OrderBook"; | ||
Errors["InvalidOrderAmount"] = "Invalid Order Amount"; | ||
Errors["UnsupportedQuotePath"] = "Attempt to quote via unsupported path, i.e. both output and input tokens are not XOR."; | ||
Errors["UnsupportedLiquiditySource"] = "Unsupported liquidity source"; | ||
@@ -42,0 +47,0 @@ })(Errors || (exports.Errors = Errors = {})); |
{ | ||
"name": "@sora-substrate/liquidity-proxy", | ||
"version": "1.35.6", | ||
"version": "1.35.7", | ||
"license": "Apache-2.0", | ||
@@ -11,4 +11,4 @@ "main": "./build/index.js", | ||
"dependencies": { | ||
"@sora-substrate/math": "1.35.6" | ||
"@sora-substrate/math": "1.35.7" | ||
} | ||
} |
@@ -18,3 +18,3 @@ import { FPNumber } from '@sora-substrate/math'; | ||
addSource(source: LiquiditySourceTypes, discreteQuotation: DiscreteQuotation): void; | ||
aggregateSwapOutcome(amount: FPNumber): AggregatedSwapOutcome | null; | ||
aggregateSwapOutcome(amount: FPNumber): AggregatedSwapOutcome; | ||
findBestPriceCandidates(locked?: LiquiditySourceTypes[]): Array<LiquiditySourceTypes>; | ||
@@ -21,0 +21,0 @@ sumChunks(chunks: SwapChunk[]): SwapChunk; |
@@ -25,3 +25,3 @@ "use strict"; | ||
aggregateSwapOutcome(amount) { | ||
if (!this.liquidityQuotations.size) return null; | ||
if (!this.liquidityQuotations.size) throw new Error(_consts.Errors.InsufficientLiquidity); | ||
let remainingAmount = amount; | ||
@@ -34,3 +34,3 @@ const lockedSources = []; | ||
let source = candidates[0]; | ||
if (!source) return null; | ||
if (!source) throw new Error(_consts.Errors.InsufficientLiquidity); | ||
@@ -46,5 +46,5 @@ // if there are several candidates with the same best price, | ||
const discreteQuotation = this.liquidityQuotations.get(source); | ||
if (!discreteQuotation) return null; | ||
if (!discreteQuotation) throw new Error(_consts.Errors.InsufficientLiquidity); | ||
let chunk = discreteQuotation.chunks.shift(); | ||
if (!chunk) return null; | ||
if (!chunk) throw new Error(_consts.Errors.InsufficientLiquidity); | ||
let payback = _primitives.SwapChunk.zero(); | ||
@@ -78,3 +78,3 @@ const total = this.sumChunks((_selected$get = selected.get(source)) !== null && _selected$get !== void 0 ? _selected$get : []); | ||
const remainingSubResult = (0, _utils.checkedSub)(remainingAmount, remainingDelta); | ||
if (!remainingSubResult) return null; | ||
if (!remainingSubResult) throw new Error(_consts.Errors.CalculationError); | ||
remainingAmount = remainingSubResult; | ||
@@ -86,3 +86,3 @@ if (remainingAmount.isZero()) { | ||
const discreteQuotation = this.liquidityQuotations.get(source); | ||
if (!discreteQuotation) return null; | ||
if (!discreteQuotation) throw new Error(_consts.Errors.InsufficientLiquidity); | ||
const [aligned, remainder] = discreteQuotation.limits.alignChunk(total); | ||
@@ -110,3 +110,3 @@ if (!remainder.isZero()) { | ||
const value = (0, _utils.checkedSub)(remainderSide.amount, chunk.getAssociatedField(this.variant).amount); | ||
if (!value) return null; | ||
if (!value) throw new Error(_consts.Errors.CalculationError); | ||
remainderSide.amount = value; | ||
@@ -113,0 +113,0 @@ discreteQuotation.chunks.unshift(chunk); |
@@ -193,5 +193,2 @@ "use strict"; | ||
const result = aggregator.aggregateSwapOutcome(amount); | ||
if (!result) { | ||
throw new Error(_consts.Errors.UnavailableExchangePath); | ||
} | ||
const rewards = []; | ||
@@ -198,0 +195,0 @@ for (const { |
@@ -34,2 +34,20 @@ "use strict"; | ||
} | ||
// reduce amount if it exceeds reserves | ||
if ((0, _utils.isAssetAddress)(inputAsset, baseAssetId)) { | ||
const collateralSupply = (0, _utils.toFp)(payload.reserves.tbc[outputAsset]); | ||
if (collateralSupply.isZero()) return quotation; | ||
const [adjustedAmount, maxLimit] = (() => { | ||
if (!isDesiredInput) { | ||
// reduce by `IrreducibleReserve` percent, because (reserve - output) must be > 0 | ||
const maxValue = (0, _utils.saturatingSub)(collateralSupply, _consts.Consts.IrreducibleReserve.mul(collateralSupply)); | ||
const value = maxValue.min(amount); | ||
return [value, new _primitives.SideAmount(maxValue, _consts.SwapVariant.WithDesiredOutput)]; | ||
} else { | ||
return [amount, null]; | ||
} | ||
})(); | ||
quotation.limits.maxAmount = maxLimit; | ||
amount = adjustedAmount; | ||
} | ||
const samplesCount = recommendedSamplesCount < 1 ? 1 : recommendedSamplesCount; | ||
@@ -61,13 +79,2 @@ const step = (0, _utils.safeDivide)(amount, new _math.FPNumber(samplesCount)); | ||
} | ||
if ((0, _utils.isAssetAddress)(inputAsset, baseAssetId)) { | ||
const collateralSupply = (0, _utils.toFp)(payload.reserves.tbc[outputAsset]); | ||
if (isDesiredInput) { | ||
const mainPricePerReferenceUnit = sellFunction(inputAsset, outputAsset, _math.FPNumber.ZERO, payload); | ||
const collateralPricePerReferenceUnit = referencePrice(outputAsset, _consts.PriceVariant.Sell, payload); | ||
const mainSupply = (0, _utils.safeDivide)(collateralSupply.mul(collateralPricePerReferenceUnit), mainPricePerReferenceUnit); | ||
quotation.limits.maxAmount = new _primitives.SideAmount(mainSupply, _consts.SwapVariant.WithDesiredInput); | ||
} else { | ||
quotation.limits.maxAmount = new _primitives.SideAmount(collateralSupply, _consts.SwapVariant.WithDesiredOutput); | ||
} | ||
} | ||
return quotation; | ||
@@ -74,0 +81,0 @@ }; |
@@ -20,4 +20,24 @@ "use strict"; | ||
// decide_is_fee_from_destination | ||
exports.canExchange = canExchange; | ||
const decideIsFeeFromDestination = (baseAssetId, assetA, assetB) => { | ||
if ((0, _utils.isAssetAddress)(baseAssetId, assetA)) { | ||
return false; | ||
} else if ((0, _utils.isAssetAddress)(baseAssetId, assetB)) { | ||
return true; | ||
} else { | ||
throw new Error(_consts.Errors.UnavailableExchangePath); | ||
} | ||
}; | ||
// calc_max_output | ||
const calcMaxOutput = (getFeeFromDestination, reserveOutput, deduceFee) => { | ||
if (reserveOutput.isZero()) return _math.FPNumber.ZERO; | ||
const maxOutput = getFeeFromDestination && deduceFee ? reserveOutput.mul(_math.FPNumber.ONE.sub(_consts.Consts.XYK_FEE)) : reserveOutput; | ||
// // reduce by `IrreducibleReserve` percent, because (reserve - output) must be > 0 | ||
return (0, _utils.saturatingSub)(maxOutput, _consts.Consts.IrreducibleReserve.mul(maxOutput)); | ||
}; | ||
// step_quote | ||
exports.canExchange = canExchange; | ||
const stepQuote = (baseAssetId, _syntheticBaseAssetId, inputAsset, outputAsset, amount, isDesiredInput, payload, deduceFee, recommendedSamplesCount) => { | ||
@@ -35,4 +55,14 @@ const quotation = new _primitives.DiscreteQuotation(); | ||
if (_math.FPNumber.isLessThanOrEqualTo(reserveInput, _math.FPNumber.ZERO) || _math.FPNumber.isLessThanOrEqualTo(reserveOutput, _math.FPNumber.ZERO)) { | ||
throw new Error(_consts.Errors.PoolIsEmpty); | ||
return quotation; | ||
} | ||
const getFeeFromDestination = decideIsFeeFromDestination(baseAssetId, inputAsset, outputAsset); | ||
amount = (() => { | ||
if (isDesiredInput) { | ||
return amount; | ||
} else { | ||
const maxOutput = calcMaxOutput(getFeeFromDestination, reserveOutput, deduceFee); | ||
quotation.limits.maxAmount = new _primitives.SideAmount(maxOutput, _consts.SwapVariant.WithDesiredOutput); | ||
return maxOutput.min(amount); | ||
} | ||
})(); | ||
const commonStep = (0, _utils.safeDivide)(amount, new _math.FPNumber(samplesCount)); | ||
@@ -45,3 +75,3 @@ // volume & step | ||
volumes.push([volume, commonStep]); | ||
remaining = remaining.sub(commonStep); | ||
remaining = (0, _utils.saturatingSub)(remaining, commonStep); | ||
} | ||
@@ -56,5 +86,5 @@ volumes.push([amount, remaining]); | ||
fee | ||
} = calcOutputForExactInput(baseAssetId, inputAsset, outputAsset, reserveInput, reserveOutput, volume, deduceFee); | ||
const output = calculated.sub(subSum); | ||
const feeChunk = fee.sub(subFee); | ||
} = calcOutputForExactInput(getFeeFromDestination, inputAsset, outputAsset, reserveInput, reserveOutput, volume, deduceFee); | ||
const output = (0, _utils.saturatingSub)(calculated, subSum); | ||
const feeChunk = (0, _utils.saturatingSub)(fee, subFee); | ||
subSum = calculated; | ||
@@ -69,5 +99,5 @@ subFee = fee; | ||
fee | ||
} = calcInputForExactOutput(baseAssetId, inputAsset, outputAsset, reserveInput, reserveOutput, volume, deduceFee); | ||
const input = calculated.sub(subSum); | ||
const feeChunk = fee.sub(subFee); | ||
} = calcInputForExactOutput(getFeeFromDestination, inputAsset, outputAsset, reserveInput, reserveOutput, volume, deduceFee); | ||
const input = (0, _utils.saturatingSub)(calculated, subSum); | ||
const feeChunk = (0, _utils.saturatingSub)(fee, subFee); | ||
subSum = calculated; | ||
@@ -162,3 +192,3 @@ subFee = fee; | ||
} | ||
const fxwYout = yOut.add(_math.FPNumber.fromCodecValue(1)); // by 1 correction to overestimate required input | ||
const fxwYout = yOut.add(_consts.Consts.MIN); // by 1 correction to overestimate required input | ||
const nominator = x.mul(fxwYout); | ||
@@ -192,3 +222,3 @@ const denominator = y.sub(fxwYout); | ||
const xykQuoteD = (input, output, x, y, yOut, deduceFee) => { | ||
const fxwYout = yOut.add(_math.FPNumber.fromCodecValue(1)); // by 1 correction to overestimate required input | ||
const fxwYout = yOut.add(_consts.Consts.MIN); // by 1 correction to overestimate required input | ||
const yOutWithFee = deduceFee ? (0, _utils.safeDivide)(fxwYout, _math.FPNumber.ONE.sub(_consts.Consts.XYK_FEE)) : fxwYout; | ||
@@ -218,9 +248,17 @@ if (_math.FPNumber.isGreaterThanOrEqualTo(yOutWithFee, y)) { | ||
// calc_output_for_exact_input | ||
const calcOutputForExactInput = (baseAssetId, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) => { | ||
return (0, _utils.isAssetAddress)(inputAsset, baseAssetId) ? xykQuoteA(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) : xykQuoteB(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
const calcOutputForExactInput = (getFeeFromDestination, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) => { | ||
if (getFeeFromDestination) { | ||
return xykQuoteB(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} else { | ||
return xykQuoteA(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} | ||
}; | ||
// calc_input_for_exact_output | ||
const calcInputForExactOutput = (baseAssetId, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) => { | ||
return (0, _utils.isAssetAddress)(inputAsset, baseAssetId) ? xykQuoteC(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) : xykQuoteD(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
const calcInputForExactOutput = (getFeeFromDestination, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) => { | ||
if (getFeeFromDestination) { | ||
return xykQuoteD(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} else { | ||
return xykQuoteC(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} | ||
}; | ||
@@ -233,3 +271,4 @@ const quote = (baseAssetId, _syntheticBaseAssetId, inputAsset, outputAsset, amount, isDesiredInput, payload, deduceFee) => { | ||
const [inputReserves, outputReserves] = getXykReserves(inputAsset, outputAsset, payload, baseAssetId); | ||
return isDesiredInput ? calcOutputForExactInput(baseAssetId, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) : calcInputForExactOutput(baseAssetId, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
const getFeeFromDestination = decideIsFeeFromDestination(baseAssetId, inputAsset, outputAsset); | ||
return isDesiredInput ? calcOutputForExactInput(getFeeFromDestination, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) : calcInputForExactOutput(getFeeFromDestination, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} catch (error) { | ||
@@ -236,0 +275,0 @@ return (0, _utils.safeQuoteResult)(inputAsset, outputAsset, amount, _consts.LiquiditySourceTypes.XYKPool); |
@@ -35,2 +35,7 @@ "use strict"; | ||
// Get max amount for the limit | ||
const limit = (0, _utils.toFp)(payload.consts.xst.syntheticBaseBuySellLimit); | ||
if (limit.isZero()) return quotation; | ||
quotation.limits.maxAmount = (0, _utils.isAssetAddress)(inputAsset, syntheticBaseAssetId) ? new _primitives.SideAmount(limit, _consts.SwapVariant.WithDesiredInput) : new _primitives.SideAmount(limit, _consts.SwapVariant.WithDesiredOutput); | ||
// Get the price without checking the limit, because even if it exceeds the limit it will be rounded below. | ||
@@ -45,6 +50,2 @@ // It is necessary to use as much liquidity from the source as we can. | ||
// Get max amount for the limit | ||
const limit = (0, _utils.toFp)(payload.consts.xst.syntheticBaseBuySellLimit); | ||
quotation.limits.maxAmount = (0, _utils.isAssetAddress)(inputAsset, syntheticBaseAssetId) ? new _primitives.SideAmount(limit, _consts.SwapVariant.WithDesiredInput) : new _primitives.SideAmount(limit, _consts.SwapVariant.WithDesiredOutput); | ||
// If amount exceeds the limit, it is necessary to round the amount to the limit. | ||
@@ -51,0 +52,0 @@ if ((0, _utils.isAssetAddress)(inputAsset, syntheticBaseAssetId)) { |
{ | ||
"name": "@sora-substrate/liquidity-proxy", | ||
"version": "1.35.6", | ||
"version": "1.35.7", | ||
"license": "Apache-2.0", | ||
@@ -11,5 +11,5 @@ "main": "./build/index.js", | ||
"dependencies": { | ||
"@sora-substrate/math": "1.35.6" | ||
"@sora-substrate/math": "1.35.7" | ||
}, | ||
"gitHead": "b7b25bf27e079d3717505b38bcb3141a3249fa1b" | ||
"gitHead": "c3e9404856b29a6fd6967542c40110e98f4e4616" | ||
} |
@@ -26,2 +26,6 @@ import { FPNumber } from '@sora-substrate/math'; | ||
static readonly MAX = new FPNumber('170141183460469231731.687303715884105727'); | ||
/** Manimal significant balance */ | ||
static readonly MIN = FPNumber.fromCodecValue(1); | ||
/** Irreducible reserve percent = 1% */ | ||
static readonly IrreducibleReserve = new FPNumber(0.01); | ||
@@ -37,7 +41,11 @@ /** ETH & DAI which are incentivized */ | ||
export enum Errors { | ||
PriceCalculationFailed = 'An error occurred while calculating the price.', | ||
CalculationError = 'Specified parameters lead to arithmetic error', | ||
CantExchange = "Liquidity source can't exchange assets with the given IDs on the given DEXId.", | ||
PoolIsEmpty = 'The pool has empty liquidity.', | ||
InsufficientLiquidity = 'None of the sources has enough reserves to execute a trade', | ||
InvalidOrderAmount = 'Invalid Order Amount', | ||
InvalidFeeRatio = 'Invalid fee ratio value.', | ||
NotEnoughLiquidityInOrderBook = 'Not Enough Liquidity In OrderBook', | ||
NotEnoughReserves = "It's not enough reserves in the pool to perform the operation.", | ||
PoolIsEmpty = 'The pool has empty liquidity.', | ||
PriceCalculationFailed = 'An error occurred while calculating the price.', | ||
SyntheticDoesNotExist = 'Synthetic asset does not exist.', | ||
@@ -47,4 +55,3 @@ SyntheticBaseBuySellLimitExceeded = 'Input/output amount of synthetic base asset exceeds the limit', | ||
UnknownOrderBook = 'Order book does not exist for this trading pair', | ||
NotEnoughLiquidityInOrderBook = 'Not Enough Liquidity In OrderBook', | ||
InvalidOrderAmount = 'Invalid Order Amount', | ||
UnsupportedQuotePath = 'Attempt to quote via unsupported path, i.e. both output and input tokens are not XOR.', | ||
// own | ||
@@ -51,0 +58,0 @@ UnsupportedLiquiditySource = 'Unsupported liquidity source', |
import { FPNumber } from '@sora-substrate/math'; | ||
import { SwapVariant, LiquiditySourceTypes } from '../../consts'; | ||
import { SwapVariant, LiquiditySourceTypes, Errors } from '../../consts'; | ||
import { SwapChunk, DiscreteQuotation, SideAmount } from '../../common/primitives'; | ||
@@ -33,4 +33,4 @@ import { checkedSub } from '../../utils'; | ||
// Liquidity sources provide discretized liquidity curve by chunks and then Liquidity Aggregator selects the best chunks from different sources to gain the best swap amount. | ||
public aggregateSwapOutcome(amount: FPNumber): AggregatedSwapOutcome | null { | ||
if (!this.liquidityQuotations.size) return null; | ||
public aggregateSwapOutcome(amount: FPNumber): AggregatedSwapOutcome { | ||
if (!this.liquidityQuotations.size) throw new Error(Errors.InsufficientLiquidity); | ||
@@ -45,3 +45,3 @@ let remainingAmount = amount; | ||
let source = candidates[0]; | ||
if (!source) return null; | ||
if (!source) throw new Error(Errors.InsufficientLiquidity); | ||
@@ -58,5 +58,5 @@ // if there are several candidates with the same best price, | ||
const discreteQuotation = this.liquidityQuotations.get(source); | ||
if (!discreteQuotation) return null; | ||
if (!discreteQuotation) throw new Error(Errors.InsufficientLiquidity); | ||
let chunk = discreteQuotation.chunks.shift(); | ||
if (!chunk) return null; | ||
if (!chunk) throw new Error(Errors.InsufficientLiquidity); | ||
let payback = SwapChunk.zero(); | ||
@@ -99,3 +99,3 @@ | ||
const remainingSubResult = checkedSub(remainingAmount, remainingDelta); | ||
if (!remainingSubResult) return null; | ||
if (!remainingSubResult) throw new Error(Errors.CalculationError); | ||
remainingAmount = remainingSubResult; | ||
@@ -109,3 +109,3 @@ | ||
const discreteQuotation = this.liquidityQuotations.get(source); | ||
if (!discreteQuotation) return null; | ||
if (!discreteQuotation) throw new Error(Errors.InsufficientLiquidity); | ||
const [aligned, remainder] = discreteQuotation.limits.alignChunk(total); | ||
@@ -139,3 +139,3 @@ | ||
const value = checkedSub(remainderSide.amount, chunk.getAssociatedField(this.variant).amount); | ||
if (!value) return null; | ||
if (!value) throw new Error(Errors.CalculationError); | ||
remainderSide.amount = value; | ||
@@ -142,0 +142,0 @@ discreteQuotation.chunks.unshift(chunk); |
@@ -335,6 +335,2 @@ import { FPNumber } from '@sora-substrate/math'; | ||
if (!result) { | ||
throw new Error(Errors.UnavailableExchangePath); | ||
} | ||
const rewards = []; | ||
@@ -341,0 +337,0 @@ |
@@ -51,4 +51,26 @@ import { FPNumber } from '@sora-substrate/math'; | ||
// reduce amount if it exceeds reserves | ||
if (isAssetAddress(inputAsset, baseAssetId)) { | ||
const collateralSupply = toFp(payload.reserves.tbc[outputAsset]); | ||
if (collateralSupply.isZero()) return quotation; | ||
const [adjustedAmount, maxLimit] = (() => { | ||
if (!isDesiredInput) { | ||
// reduce by `IrreducibleReserve` percent, because (reserve - output) must be > 0 | ||
const maxValue = saturatingSub(collateralSupply, Consts.IrreducibleReserve.mul(collateralSupply)); | ||
const value = maxValue.min(amount); | ||
return [value, new SideAmount(maxValue, SwapVariant.WithDesiredOutput)]; | ||
} else { | ||
return [amount, null]; | ||
} | ||
})(); | ||
quotation.limits.maxAmount = maxLimit; | ||
amount = adjustedAmount; | ||
} | ||
const samplesCount = recommendedSamplesCount < 1 ? 1 : recommendedSamplesCount; | ||
const step = safeDivide(amount, new FPNumber(samplesCount)); | ||
@@ -87,16 +109,2 @@ const volumes = []; | ||
if (isAssetAddress(inputAsset, baseAssetId)) { | ||
const collateralSupply = toFp(payload.reserves.tbc[outputAsset]); | ||
if (isDesiredInput) { | ||
const mainPricePerReferenceUnit = sellFunction(inputAsset, outputAsset, FPNumber.ZERO, payload); | ||
const collateralPricePerReferenceUnit = referencePrice(outputAsset, PriceVariant.Sell, payload); | ||
const mainSupply = safeDivide(collateralSupply.mul(collateralPricePerReferenceUnit), mainPricePerReferenceUnit); | ||
quotation.limits.maxAmount = new SideAmount(mainSupply, SwapVariant.WithDesiredInput); | ||
} else { | ||
quotation.limits.maxAmount = new SideAmount(collateralSupply, SwapVariant.WithDesiredOutput); | ||
} | ||
} | ||
return quotation; | ||
@@ -103,0 +111,0 @@ }; |
import { FPNumber } from '@sora-substrate/math'; | ||
import { LiquiditySourceTypes, Consts, Errors } from '../consts'; | ||
import { safeDivide, toFp, isAssetAddress, safeQuoteResult } from '../utils'; | ||
import { SwapChunk, DiscreteQuotation } from '../common/primitives'; | ||
import { LiquiditySourceTypes, Consts, Errors, SwapVariant } from '../consts'; | ||
import { safeDivide, toFp, isAssetAddress, safeQuoteResult, saturatingSub } from '../utils'; | ||
import { SwapChunk, DiscreteQuotation, SideAmount } from '../common/primitives'; | ||
@@ -26,2 +26,24 @@ import type { QuotePayload, QuoteResult } from '../types'; | ||
// decide_is_fee_from_destination | ||
const decideIsFeeFromDestination = (baseAssetId: string, assetA: string, assetB: string) => { | ||
if (isAssetAddress(baseAssetId, assetA)) { | ||
return false; | ||
} else if (isAssetAddress(baseAssetId, assetB)) { | ||
return true; | ||
} else { | ||
throw new Error(Errors.UnavailableExchangePath); | ||
} | ||
}; | ||
// calc_max_output | ||
const calcMaxOutput = (getFeeFromDestination: boolean, reserveOutput: FPNumber, deduceFee: boolean) => { | ||
if (reserveOutput.isZero()) return FPNumber.ZERO; | ||
const maxOutput = | ||
getFeeFromDestination && deduceFee ? reserveOutput.mul(FPNumber.ONE.sub(Consts.XYK_FEE)) : reserveOutput; | ||
// // reduce by `IrreducibleReserve` percent, because (reserve - output) must be > 0 | ||
return saturatingSub(maxOutput, Consts.IrreducibleReserve.mul(maxOutput)); | ||
}; | ||
// step_quote | ||
@@ -55,5 +77,19 @@ export const stepQuote = ( | ||
) { | ||
throw new Error(Errors.PoolIsEmpty); | ||
return quotation; | ||
} | ||
const getFeeFromDestination = decideIsFeeFromDestination(baseAssetId, inputAsset, outputAsset); | ||
amount = (() => { | ||
if (isDesiredInput) { | ||
return amount; | ||
} else { | ||
const maxOutput = calcMaxOutput(getFeeFromDestination, reserveOutput, deduceFee); | ||
quotation.limits.maxAmount = new SideAmount(maxOutput, SwapVariant.WithDesiredOutput); | ||
return maxOutput.min(amount); | ||
} | ||
})(); | ||
const commonStep = safeDivide(amount, new FPNumber(samplesCount)); | ||
@@ -70,3 +106,3 @@ // volume & step | ||
remaining = remaining.sub(commonStep); | ||
remaining = saturatingSub(remaining, commonStep); | ||
} | ||
@@ -81,3 +117,3 @@ volumes.push([amount, remaining]); | ||
const { amount: calculated, fee } = calcOutputForExactInput( | ||
baseAssetId, | ||
getFeeFromDestination, | ||
inputAsset, | ||
@@ -91,4 +127,4 @@ outputAsset, | ||
const output = calculated.sub(subSum); | ||
const feeChunk = fee.sub(subFee); | ||
const output = saturatingSub(calculated, subSum); | ||
const feeChunk = saturatingSub(fee, subFee); | ||
subSum = calculated; | ||
@@ -101,3 +137,3 @@ subFee = fee; | ||
const { amount: calculated, fee } = calcInputForExactOutput( | ||
baseAssetId, | ||
getFeeFromDestination, | ||
inputAsset, | ||
@@ -111,4 +147,4 @@ outputAsset, | ||
const input = calculated.sub(subSum); | ||
const feeChunk = fee.sub(subFee); | ||
const input = saturatingSub(calculated, subSum); | ||
const feeChunk = saturatingSub(fee, subFee); | ||
subSum = calculated; | ||
@@ -236,3 +272,3 @@ subFee = fee; | ||
const fxwYout = yOut.add(FPNumber.fromCodecValue(1)); // by 1 correction to overestimate required input | ||
const fxwYout = yOut.add(Consts.MIN); // by 1 correction to overestimate required input | ||
const nominator = x.mul(fxwYout); | ||
@@ -276,3 +312,3 @@ const denominator = y.sub(fxwYout); | ||
): QuoteResult => { | ||
const fxwYout = yOut.add(FPNumber.fromCodecValue(1)); // by 1 correction to overestimate required input | ||
const fxwYout = yOut.add(Consts.MIN); // by 1 correction to overestimate required input | ||
const yOutWithFee = deduceFee ? safeDivide(fxwYout, FPNumber.ONE.sub(Consts.XYK_FEE)) : fxwYout; | ||
@@ -310,3 +346,3 @@ | ||
const calcOutputForExactInput = ( | ||
baseAssetId: string, | ||
getFeeFromDestination: boolean, | ||
inputAsset: string, | ||
@@ -319,5 +355,7 @@ outputAsset: string, | ||
) => { | ||
return isAssetAddress(inputAsset, baseAssetId) | ||
? xykQuoteA(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) | ||
: xykQuoteB(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
if (getFeeFromDestination) { | ||
return xykQuoteB(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} else { | ||
return xykQuoteA(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} | ||
}; | ||
@@ -327,3 +365,3 @@ | ||
const calcInputForExactOutput = ( | ||
baseAssetId: string, | ||
getFeeFromDestination: boolean, | ||
inputAsset: string, | ||
@@ -336,5 +374,7 @@ outputAsset: string, | ||
) => { | ||
return isAssetAddress(inputAsset, baseAssetId) | ||
? xykQuoteC(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) | ||
: xykQuoteD(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
if (getFeeFromDestination) { | ||
return xykQuoteD(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} else { | ||
return xykQuoteC(inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
} | ||
}; | ||
@@ -358,6 +398,23 @@ | ||
const [inputReserves, outputReserves] = getXykReserves(inputAsset, outputAsset, payload, baseAssetId); | ||
const getFeeFromDestination = decideIsFeeFromDestination(baseAssetId, inputAsset, outputAsset); | ||
return isDesiredInput | ||
? calcOutputForExactInput(baseAssetId, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee) | ||
: calcInputForExactOutput(baseAssetId, inputAsset, outputAsset, inputReserves, outputReserves, amount, deduceFee); | ||
? calcOutputForExactInput( | ||
getFeeFromDestination, | ||
inputAsset, | ||
outputAsset, | ||
inputReserves, | ||
outputReserves, | ||
amount, | ||
deduceFee | ||
) | ||
: calcInputForExactOutput( | ||
getFeeFromDestination, | ||
inputAsset, | ||
outputAsset, | ||
inputReserves, | ||
outputReserves, | ||
amount, | ||
deduceFee | ||
); | ||
} catch (error) { | ||
@@ -364,0 +421,0 @@ return safeQuoteResult(inputAsset, outputAsset, amount, LiquiditySourceTypes.XYKPool); |
@@ -53,2 +53,11 @@ import { FPNumber } from '@sora-substrate/math'; | ||
// Get max amount for the limit | ||
const limit = toFp(payload.consts.xst.syntheticBaseBuySellLimit); | ||
if (limit.isZero()) return quotation; | ||
quotation.limits.maxAmount = isAssetAddress(inputAsset, syntheticBaseAssetId) | ||
? new SideAmount(limit, SwapVariant.WithDesiredInput) | ||
: new SideAmount(limit, SwapVariant.WithDesiredOutput); | ||
// Get the price without checking the limit, because even if it exceeds the limit it will be rounded below. | ||
@@ -64,8 +73,2 @@ // It is necessary to use as much liquidity from the source as we can. | ||
// Get max amount for the limit | ||
const limit = toFp(payload.consts.xst.syntheticBaseBuySellLimit); | ||
quotation.limits.maxAmount = isAssetAddress(inputAsset, syntheticBaseAssetId) | ||
? new SideAmount(limit, SwapVariant.WithDesiredInput) | ||
: new SideAmount(limit, SwapVariant.WithDesiredOutput); | ||
// If amount exceeds the limit, it is necessary to round the amount to the limit. | ||
@@ -72,0 +75,0 @@ if (isAssetAddress(inputAsset, syntheticBaseAssetId)) { |
281788
6654
+ Added@sora-substrate/math@1.35.7(transitive)
- Removed@sora-substrate/math@1.35.6(transitive)
Updated@sora-substrate/math@1.35.7