Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FxPool rounding errors fixes #393

Merged
merged 57 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
bf44477
WIP fxPool rounding error
andreiashu May 25, 2023
37b6ef3
fixed regression in xaveFxPool.spec.ts
andreiashu May 26, 2023
e9f50e1
🍜 minor amends; removed obsolete bnum conversions; rearranged checks …
andreiashu May 29, 2023
c1f880b
🎐 wip
andreiashu May 31, 2023
1ab80f6
wip; broken branch for now
andreiashu May 31, 2023
a44635b
⛽ fixed 10 wei precision issue; still wip;
andreiashu Jun 2, 2023
f0b3516
breaking polygon test
0xAplki Jun 6, 2023
519d418
🍅 fix the subgraph values. Tests passing on Polygon
andreiashu Jun 8, 2023
29f16c1
added new values for subgraph return
0xAplki Jun 8, 2023
7b753bd
🏈 code formatting
andreiashu Jun 10, 2023
fdeb41b
🍿 minor changes: code reuse
andreiashu Jun 10, 2023
7a79166
🦄 added documentation
andreiashu Jun 10, 2023
c1f4c35
Removed obsolete documentation
andreiashu Jun 11, 2023
91a3076
⛑ switched back to `ALCHEMY_URL`; fixed one test in `xaveFxPool.spec.ts`
andreiashu Jun 12, 2023
b9d6c90
🎣 fixed several test cases; still wip
andreiashu Jun 14, 2023
6e35042
🎣 added tests for expectedSwapOutput values
andreiashu Jun 14, 2023
4e736d8
🎤 minor changes
andreiashu Jun 14, 2023
ba76227
⚓ added _inHigherPrecision function to workaround the SOR limitation …
andreiashu Jun 14, 2023
559e0bd
⛄ pull from upstream/develop
andreiashu Jun 14, 2023
820bf11
🏈 lint fixes
andreiashu Jun 14, 2023
adc7836
🍁 added xaveFxPool.math.spec.ts
andreiashu Jun 14, 2023
c33c58c
🌳 removed debug module
andreiashu Jun 14, 2023
e464e24
🌳 removed debug module
andreiashu Jun 14, 2023
19ad8be
🚗 removed FXPoolMath tests from integration
andreiashu Jun 14, 2023
49094af
🐙 removed code change from `src/pools/index.ts` (oops)
andreiashu Jun 14, 2023
09253c6
🍳 enabled FX pool type
andreiashu Jun 15, 2023
a965343
🍵 xavePool run tests against polygon fork; better organize test files;
andreiashu Jun 15, 2023
c578d17
🐟 fixed bug where OldBigNumber.config.DECIMAL_PLACES was left as 36 i…
andreiashu Jun 15, 2023
4d98aa8
💻 removed comment about innacuracy test since it is irrelevant now
andreiashu Jun 15, 2023
2576c4d
test cases + bug
0xAplki Jun 20, 2023
c2590b0
🦩 new fxpool test cases
0xAplki Jun 21, 2023
af3eb5c
🍎 removed rounding of values in xaveFxPool.spec.ts; the values for `e…
andreiashu Jun 22, 2023
61a9774
🐨 fixed bug whereby the way we calculated our derivative would have p…
andreiashu Jun 23, 2023
f62a047
🐨 lint fixes
andreiashu Jun 23, 2023
9c82d4b
⛰ removed obsolete comments
andreiashu Jun 23, 2023
60071d9
💾 do not use floats in calculations; found a place where we were roun…
andreiashu Jun 23, 2023
090466f
⛏ viewRawAmount and viewNumeraireAmount using BigInt internally
andreiashu Jun 26, 2023
5e537cc
🐼 calculateMicroFee is using BigNumber now
andreiashu Jun 26, 2023
2f8f87b
😮 oops I deleted some of the test cases by mistake. fixed now
andreiashu Jun 26, 2023
94b2616
🐬 calculateTrade uses BigNumber internally
andreiashu Jun 26, 2023
71ab58b
👽 alpha, beta, delta, epsilon, viewRawAmount, viewNumeraire using Big…
andreiashu Jun 26, 2023
ac20365
tokenOutLatestFXPrice and tokenInLatestFXPrice are BigNumber now
andreiashu Jun 27, 2023
a3d7557
⚽ amount -> amount_36 [wip]
andreiashu Jun 27, 2023
2673734
🎻 viewRawAmount amount -> amount_36
andreiashu Jun 27, 2023
83cc506
ParsedFxPoolData uses BigNumber
andreiashu Jun 27, 2023
b90f725
🖖 spotPriceBeforeSwap uses BigNumber amount
andreiashu Jun 27, 2023
4635ed9
🚣 params to param_64
andreiashu Jun 27, 2023
e5b7a8d
_derivativeSpotPriceAfterSwapTokenInForExactTokenOut and _derivativeS…
andreiashu Jun 27, 2023
3471d7d
🐺 viewRawAmount and spotPriceBeforeSwap return BigNumber
andreiashu Jun 27, 2023
edc0b95
🍳bnum(10).pow(36) -> OLD_ONE_36
andreiashu Jun 27, 2023
2350377
⛰ default to 8 for `fxOracleDecimals`
andreiashu Jun 27, 2023
1e0d545
🍜 removed redundant operations; thanks @brunoguerios
andreiashu Jun 28, 2023
2cf04c2
🏺 parseFixedCurveParam test
andreiashu Jun 28, 2023
1bbc298
🙅🏻‍♂️ removed the conditional that was previously used on other funct…
0xAplki Jun 29, 2023
3d347f5
🍳 removed the rounding down from spot price related functions;
andreiashu Jul 3, 2023
7191c7b
⛲ formatting
andreiashu Jul 3, 2023
6a15ae4
🍎 reverted the changes in `_derivativeSpotPriceAfterSwapExactTokenInF…
andreiashu Jul 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 129 additions & 39 deletions src/pools/xaveFxPool/fxPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { getAddress } from '@ethersproject/address';
import { BigNumber, formatFixed, parseFixed } from '@ethersproject/bignumber';
import { Zero } from '@ethersproject/constants';
import { BigNumber as OldBigNumber, ZERO, bnum } from '../../utils/bignumber';
import { isSameAddress } from '../../utils';

import { parseFixedCurveParam } from './parseFixedCurveParam';
import { isSameAddress, safeParseFixed } from '../../utils';
import { universalNormalizedLiquidity } from '../liquidity';
import {
PoolBase,
Expand All @@ -21,6 +23,7 @@ import {
_spotPriceAfterSwapExactTokenInForTokenOut,
_spotPriceAfterSwapTokenInForExactTokenOut,
_tokenInForExactTokenOut,
ONE_36,
} from './fxPoolMath';

type FxPoolToken = Pick<
Expand All @@ -29,13 +32,15 @@ type FxPoolToken = Pick<
>;

export type FxPoolPairData = PoolPairBase & {
alpha: BigNumber;
beta: BigNumber;
lambda: BigNumber;
delta: BigNumber;
epsilon: BigNumber;
tokenInLatestFXPrice: OldBigNumber;
tokenOutLatestFXPrice: OldBigNumber;
alpha: OldBigNumber;
beta: OldBigNumber;
lambda: OldBigNumber;
delta: OldBigNumber;
epsilon: OldBigNumber;
tokenInLatestFXPrice: BigNumber;
tokenInfxOracleDecimals: number;
tokenOutLatestFXPrice: BigNumber;
tokenOutfxOracleDecimals: number;
};

export class FxPool implements PoolBase<FxPoolPairData> {
Expand All @@ -46,11 +51,11 @@ export class FxPool implements PoolBase<FxPoolPairData> {
totalShares: BigNumber;
tokens: FxPoolToken[];
tokensList: string[];
alpha: BigNumber;
beta: BigNumber;
lambda: BigNumber;
delta: BigNumber;
epsilon: BigNumber;
alpha: OldBigNumber;
beta: OldBigNumber;
lambda: OldBigNumber;
delta: OldBigNumber;
epsilon: OldBigNumber;

static fromPool(pool: SubgraphPoolBase): FxPool {
if (
Expand Down Expand Up @@ -95,11 +100,11 @@ export class FxPool implements PoolBase<FxPoolPairData> {
this.totalShares = parseFixed(totalShares, 18);
this.tokens = tokens;
this.tokensList = tokensList;
this.alpha = parseFixed(alpha, 18);
this.beta = parseFixed(beta, 18);
this.lambda = parseFixed(lambda, 18);
this.delta = parseFixed(delta, 18);
this.epsilon = parseFixed(epsilon, 18);
this.alpha = parseFixedCurveParam(alpha);
this.beta = parseFixedCurveParam(beta);
this.lambda = parseFixedCurveParam(lambda);
this.delta = bnum(parseFixed(delta, 18).toString());
this.epsilon = parseFixedCurveParam(epsilon);
}
updateTotalShares: (newTotalShares: BigNumber) => void;
mainIndex?: number | undefined;
Expand Down Expand Up @@ -138,6 +143,13 @@ export class FxPool implements PoolBase<FxPoolPairData> {
if (!tO.token?.latestFXPrice || !tI.token?.latestFXPrice)
throw 'FX Pool Missing LatestFxPrice';

if (tO.token?.fxOracleDecimals == null) {
tO.token.fxOracleDecimals = 8;
}
if (tI.token?.fxOracleDecimals == null) {
tI.token.fxOracleDecimals = 8;
}

const poolPairData: FxPoolPairData = {
id: this.id,
address: this.address,
Expand All @@ -154,8 +166,16 @@ export class FxPool implements PoolBase<FxPoolPairData> {
lambda: this.lambda,
delta: this.delta,
epsilon: this.epsilon,
tokenInLatestFXPrice: bnum(tI.token.latestFXPrice), // decimals is formatted from subgraph in rate we get from the chainlink oracle
tokenOutLatestFXPrice: bnum(tO.token.latestFXPrice), // decimals is formatted from subgraph in rate we get from the chainlink oracle
tokenInLatestFXPrice: parseFixed(
tI.token.latestFXPrice,
tI.token.fxOracleDecimals
), // decimals is formatted from subgraph in rate we get from the chainlink oracle
tokenOutLatestFXPrice: parseFixed(
tO.token.latestFXPrice,
tO.token.fxOracleDecimals
), // decimals is formatted from subgraph in rate we get from the chainlink oracle
tokenInfxOracleDecimals: tI.token.fxOracleDecimals,
tokenOutfxOracleDecimals: tO.token.fxOracleDecimals,
};

return poolPairData;
Expand All @@ -181,34 +201,58 @@ export class FxPool implements PoolBase<FxPoolPairData> {
getLimitAmountSwap(
poolPairData: FxPoolPairData,
swapType: SwapTypes
): OldBigNumber {
return this._inHigherPrecision(
this._getLimitAmountSwap,
poolPairData,
swapType
);
}

_getLimitAmountSwap(
poolPairData: FxPoolPairData,
swapType: SwapTypes
): OldBigNumber {
try {
const parsedReserves = poolBalancesToNumeraire(poolPairData);

const alphaValue = Number(formatFixed(poolPairData.alpha, 18));
const alphaValue = safeParseFixed(
poolPairData.alpha.toString(),
18
);

const maxLimit = (1 + alphaValue) * parsedReserves._oGLiq * 0.5;
const maxLimit = alphaValue
.add(ONE_36)
.mul(parsedReserves._oGLiq_36)
.div(ONE_36)
.div(2);

if (swapType === SwapTypes.SwapExactIn) {
const maxLimitAmount =
maxLimit - parsedReserves.tokenInReservesInNumeraire;
const maxLimitAmount_36 = maxLimit.sub(
parsedReserves.tokenInReservesInNumeraire_36.toString()
);

return bnum(
viewRawAmount(
maxLimitAmount,
poolPairData.tokenInLatestFXPrice.toNumber()
maxLimitAmount_36,
poolPairData.decimalsIn,
poolPairData.tokenInLatestFXPrice,
poolPairData.tokenInfxOracleDecimals
).toString()
);
).div(bnum(10).pow(poolPairData.decimalsIn));
} else {
const maxLimitAmount =
maxLimit - parsedReserves.tokenOutReservesInNumeraire;
const maxLimitAmount_36 = maxLimit.sub(
parsedReserves.tokenOutReservesInNumeraire_36
);

return bnum(
viewRawAmount(
maxLimitAmount,
poolPairData.tokenOutLatestFXPrice.toNumber()
maxLimitAmount_36,
poolPairData.decimalsOut,
poolPairData.tokenOutLatestFXPrice,
poolPairData.tokenOutfxOracleDecimals
).toString()
);
).div(bnum(10).pow(poolPairData.decimalsOut));
}
} catch {
return ZERO;
Expand All @@ -233,8 +277,12 @@ export class FxPool implements PoolBase<FxPoolPairData> {
amount: OldBigNumber
): OldBigNumber {
try {
return _exactTokenInForTokenOut(amount, poolPairData);
} catch {
return this._inHigherPrecision(
_exactTokenInForTokenOut,
amount,
poolPairData
);
Comment on lines +280 to +284
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you're using fixed point math with 36 decimals on fxPoolMath, you should be able to remove this inHigherPrecision wrapper, right?
I'm just thinking of ways to reduce complexity whenever possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this one might have to wait until we have SOR converted to fixed-point math as well.

For example, in _derivativeSpotPriceAfterSwapTokenInForExactTokenOut, because we still have to use OldBigNumber in _spotPriceAfterSwapTokenInForExactTokenOut, that means that for both spotPriceBeforeSwap and _spotPriceAfterSwapTokenInForExactTokenOut we're doing .div(OLD_ONE_36).decimalPlaces(....). I believe this will be easier to fix once we have fixed-point math across SOR. Thoughts?

Screenshot 2023-06-28 at 11 26 00

} catch (e) {
return ZERO;
}
}
Expand All @@ -244,7 +292,11 @@ export class FxPool implements PoolBase<FxPoolPairData> {
amount: OldBigNumber
): OldBigNumber {
try {
return _tokenInForExactTokenOut(amount, poolPairData);
return this._inHigherPrecision(
_tokenInForExactTokenOut,
amount,
poolPairData
);
} catch {
return ZERO;
}
Expand All @@ -255,7 +307,8 @@ export class FxPool implements PoolBase<FxPoolPairData> {
amount: OldBigNumber
): OldBigNumber {
try {
return _spotPriceAfterSwapExactTokenInForTokenOut(
return this._inHigherPrecision(
_spotPriceAfterSwapExactTokenInForTokenOut,
poolPairData,
amount
);
Expand All @@ -269,7 +322,8 @@ export class FxPool implements PoolBase<FxPoolPairData> {
amount: OldBigNumber
): OldBigNumber {
try {
return _spotPriceAfterSwapTokenInForExactTokenOut(
return this._inHigherPrecision(
_spotPriceAfterSwapTokenInForExactTokenOut,
poolPairData,
amount
);
Expand All @@ -282,8 +336,10 @@ export class FxPool implements PoolBase<FxPoolPairData> {
poolPairData: FxPoolPairData,
amount: OldBigNumber
): OldBigNumber {
// `calculateTrade` will throw if the trade is beyond alpha region
try {
return _derivativeSpotPriceAfterSwapExactTokenInForTokenOut(
return this._inHigherPrecision(
_derivativeSpotPriceAfterSwapExactTokenInForTokenOut,
amount,
poolPairData
);
Expand All @@ -297,12 +353,46 @@ export class FxPool implements PoolBase<FxPoolPairData> {
amount: OldBigNumber
): OldBigNumber {
try {
return _derivativeSpotPriceAfterSwapTokenInForExactTokenOut(
return this._inHigherPrecision(
_derivativeSpotPriceAfterSwapTokenInForExactTokenOut,
amount,
poolPairData
);
} catch {
return ZERO;
}
}

/**
* Runs the given function with the BigNumber config set to 36 decimals.
* This is needed since in the Solidity code we use 64.64 fixed point numbers
* for the curve math operations (ABDKMath64x64.sol). This makes the SOR
* default of 18 decimals not enough.
*
* @param funcName
* @param args
* @returns
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
_inHigherPrecision(funcName: Function, ...args: any[]): OldBigNumber {
const prevDecimalPlaces = OldBigNumber.config({}).DECIMAL_PLACES;
OldBigNumber.config({
DECIMAL_PLACES: 36,
});

try {
const val = funcName.apply(this, args);
OldBigNumber.config({
DECIMAL_PLACES: prevDecimalPlaces,
});
return val;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
// restore the original BigNumber config even in case of an exception
OldBigNumber.config({
DECIMAL_PLACES: prevDecimalPlaces,
});
throw err;
}
}
}
Loading