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

4.0.0-beta.9 #269

Merged
merged 11 commits into from
Jun 20, 2022
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@balancer-labs/sor",
"version": "4.0.0-beta.8",
"version": "4.0.0-beta.9",
"license": "GPL-3.0-only",
"main": "dist/index.js",
"module": "dist/index.esm.js",
Expand Down
75 changes: 59 additions & 16 deletions src/pools/metaStablePool/metaStablePool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
_calcOutGivenIn,
_calcInGivenOut,
} from '../stablePool/stableMathBigInt';
import { StablePoolPairData } from 'pools/stablePool/stablePool';
import { StablePoolPairData } from '../stablePool/stablePool';

type MetaStablePoolToken = Pick<
SubgraphToken,
Expand Down Expand Up @@ -198,22 +198,31 @@ export class MetaStablePool implements PoolBase {
if (amount.isZero()) return ZERO;
// All values should use 1e18 fixed point
// i.e. 1USDC => 1e18 not 1e6
const amountConvertedEvm = parseFixed(amount.dp(18).toString(), 18)

const amtWithFee = this.subtractSwapFeeAmount(
parseFixed(
amount.dp(poolPairData.decimalsIn).toString(),
poolPairData.decimalsIn
),
poolPairData.swapFee
);

const amountConverted = amtWithFee
.mul(poolPairData.tokenInPriceRate)
.div(ONE);

const returnEvm = _calcOutGivenIn(
const returnAmt = _calcOutGivenIn(
this.amp.toBigInt(),
poolPairData.allBalancesScaled.map((balance) =>
balance.toBigInt()
),
poolPairData.tokenIndexIn,
poolPairData.tokenIndexOut,
amountConvertedEvm.toBigInt(),
poolPairData.swapFee.toBigInt()
amountConverted.toBigInt(),
BigInt(0)
);

const returnEvmWithRate = BigNumber.from(returnEvm)
const returnEvmWithRate = BigNumber.from(returnAmt)
.mul(ONE)
.div(poolPairData.tokenOutPriceRate);

Expand All @@ -230,28 +239,50 @@ export class MetaStablePool implements PoolBase {
): OldBigNumber {
try {
if (amount.isZero()) return ZERO;
const decimalsIn = poolPairData.decimalsIn;
const decimalsOut = poolPairData.decimalsOut;

// All values should use 1e18 fixed point
// i.e. 1USDC => 1e18 not 1e6
const amountConvertedEvm = parseFixed(amount.dp(18).toString(), 18)
.mul(poolPairData.tokenOutPriceRate)
.div(ONE);
const scalingFactorIn =
poolPairData.tokenInPriceRate.toBigInt() *
BigInt(10 ** (18 - decimalsIn));

const scalingFactorOut =
poolPairData.tokenOutPriceRate.toBigInt() *
BigInt(10 ** (18 - decimalsOut));

// eslint-disable-next-line prettier/prettier
const amountBigInt = BigInt(
amount
.times(10 ** decimalsOut)
.dp(0)
.toString()
);
const amountConverted =
(amountBigInt * scalingFactorOut) / BigInt(10 ** 18);

const returnEvm = _calcInGivenOut(
const returnAmount = _calcInGivenOut(
this.amp.toBigInt(),
poolPairData.allBalancesScaled.map((balance) =>
balance.toBigInt()
),
poolPairData.tokenIndexIn,
poolPairData.tokenIndexOut,
amountConvertedEvm.toBigInt(),
poolPairData.swapFee.toBigInt()
amountConverted,
BigInt(0)
);

const returnEvmWithRate = BigNumber.from(returnEvm)
.mul(ONE)
.div(poolPairData.tokenInPriceRate);
const returnAmountConverted =
(returnAmount * BigInt(10 ** 18)) / scalingFactorIn;

return bnum(formatFixed(returnEvmWithRate, 18));
const returnAmtWithFee = this.addSwapFeeAmount(
BigNumber.from(returnAmountConverted),
poolPairData.swapFee
);
return bnum(returnAmtWithFee.toString()).div(
10 ** poolPairData.decimalsIn
);
} catch (err) {
console.error(`_evminGivenOut: ${err.message}`);
return ZERO;
Expand Down Expand Up @@ -315,4 +346,16 @@ export class MetaStablePool implements PoolBase {
.times(priceRateOut)
.times(priceRateOut);
}

subtractSwapFeeAmount(amount: BigNumber, swapFee: BigNumber): BigNumber {
// https://github.com/balancer-labs/balancer-v2-monorepo/blob/c18ff2686c61a8cbad72cdcfc65e9b11476fdbc3/pkg/pool-utils/contracts/BasePool.sol#L466
const feeAmount = amount.mul(swapFee).add(ONE.sub(1)).div(ONE);
return amount.sub(feeAmount);
}

addSwapFeeAmount(amount: BigNumber, swapFee: BigNumber): BigNumber {
// https://github.com/balancer-labs/balancer-v2-monorepo/blob/c18ff2686c61a8cbad72cdcfc65e9b11476fdbc3/pkg/pool-utils/contracts/BasePool.sol#L458
const feeAmount = ONE.sub(swapFee);
return amount.mul(ONE).add(feeAmount.sub(1)).div(feeAmount);
}
}
10 changes: 7 additions & 3 deletions src/pools/phantomStablePool/phantomStablePool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ export class PhantomStablePool implements PoolBase {
amount: OldBigNumber
): OldBigNumber {
try {
// This code assumes that decimalsIn and decimalsOut is 18

if (amount.isZero()) return ZERO;
// All values should use 1e18 fixed point
// i.e. 1USDC => 1e18 not 1e6
Expand Down Expand Up @@ -301,6 +303,8 @@ export class PhantomStablePool implements PoolBase {
amount: OldBigNumber
): OldBigNumber {
try {
// This code assumes that decimalsIn and decimalsOut is 18

if (amount.isZero()) return ZERO;
// All values should use 1e18 fixed point
// i.e. 1USDC => 1e18 not 1e6
Expand Down Expand Up @@ -331,7 +335,7 @@ export class PhantomStablePool implements PoolBase {
poolPairData.allBalancesScaled.map((b) => b.toBigInt()),
amountsOutBigInt,
poolPairData.virtualBptSupply.toBigInt(),
BigInt(0) // Fee is handled above
BigInt(0) // Fee is handled below
);
} else {
returnEvm = _calcInGivenOut(
Expand All @@ -340,7 +344,7 @@ export class PhantomStablePool implements PoolBase {
poolPairData.tokenIndexIn,
poolPairData.tokenIndexOut,
amountConvertedEvm.toBigInt(),
BigInt(0) // Fee is handled above
BigInt(0) // Fee is handled below
);
}
// In Phantom Pools every time there is a swap (token per token, bpt per token or token per bpt), we substract the fee from the amount in
Expand Down Expand Up @@ -488,7 +492,7 @@ export class PhantomStablePool implements PoolBase {

subtractSwapFeeAmount(amount: BigNumber, swapFee: BigNumber): BigNumber {
// https://github.com/balancer-labs/balancer-v2-monorepo/blob/c18ff2686c61a8cbad72cdcfc65e9b11476fdbc3/pkg/pool-utils/contracts/BasePool.sol#L466
const feeAmount = amount.mul(swapFee).div(ONE);
const feeAmount = amount.mul(swapFee).add(ONE.sub(1)).div(ONE);
return amount.sub(feeAmount);
}

Expand Down
50 changes: 39 additions & 11 deletions src/pools/stablePool/stablePool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,20 @@ export class StablePool implements PoolBase {
): OldBigNumber {
try {
if (amount.isZero()) return ZERO;

const amtWithFeeEvm = this.subtractSwapFeeAmount(
parseFixed(
amount.dp(poolPairData.decimalsIn).toString(),
poolPairData.decimalsIn
),
poolPairData.swapFee
);

// All values should use 1e18 fixed point
// i.e. 1USDC => 1e18 not 1e6
const amtScaled = parseFixed(amount.dp(18).toString(), 18);
const amtScaled = amtWithFeeEvm.mul(
10 ** (18 - poolPairData.decimalsIn)
);

const amt = _calcOutGivenIn(
this.amp.toBigInt(),
Expand All @@ -188,8 +199,9 @@ export class StablePool implements PoolBase {
poolPairData.tokenIndexIn,
poolPairData.tokenIndexOut,
amtScaled.toBigInt(),
poolPairData.swapFee.toBigInt()
BigInt(0)
);

// return normalised amount
// Using BigNumber.js decimalPlaces (dp), allows us to consider token decimal accuracy correctly,
// i.e. when using token with 2decimals 0.002 should be returned as 0
Expand All @@ -214,23 +226,27 @@ export class StablePool implements PoolBase {
// i.e. 1USDC => 1e18 not 1e6
const amtScaled = parseFixed(amount.dp(18).toString(), 18);

const amt = _calcInGivenOut(
let amt = _calcInGivenOut(
this.amp.toBigInt(),
poolPairData.allBalancesScaled.map((balance) =>
balance.toBigInt()
),
poolPairData.tokenIndexIn,
poolPairData.tokenIndexOut,
amtScaled.toBigInt(),
poolPairData.swapFee.toBigInt()
BigInt(0)
);
// return normalised amount
// Using BigNumber.js decimalPlaces (dp), allows us to consider token decimal accuracy correctly,
// i.e. when using token with 2decimals 0.002 should be returned as 0
// Uses ROUND_UP mode (0)
return scale(bnum(amt.toString()), -18).dp(
poolPairData.decimalsIn,
0

// this is downscaleUp
const scaleFactor = BigInt(10 ** (18 - poolPairData.decimalsIn));
amt = (amt + scaleFactor - BigInt(1)) / scaleFactor;

const amtWithFee = this.addSwapFeeAmount(
BigNumber.from(amt),
poolPairData.swapFee
);
return bnum(amtWithFee.toString()).div(
10 ** poolPairData.decimalsIn
);
} catch (err) {
console.error(`_evminGivenOut: ${err.message}`);
Expand Down Expand Up @@ -271,4 +287,16 @@ export class StablePool implements PoolBase {
poolPairData
);
}

subtractSwapFeeAmount(amount: BigNumber, swapFee: BigNumber): BigNumber {
// https://github.com/balancer-labs/balancer-v2-monorepo/blob/c18ff2686c61a8cbad72cdcfc65e9b11476fdbc3/pkg/pool-utils/contracts/BasePool.sol#L466
const feeAmount = amount.mul(swapFee).add(ONE.sub(1)).div(ONE);
return amount.sub(feeAmount);
}

addSwapFeeAmount(amount: BigNumber, swapFee: BigNumber): BigNumber {
// https://github.com/balancer-labs/balancer-v2-monorepo/blob/c18ff2686c61a8cbad72cdcfc65e9b11476fdbc3/pkg/pool-utils/contracts/BasePool.sol#L458
const feeAmount = ONE.sub(swapFee);
return amount.mul(ONE).add(feeAmount.sub(1)).div(feeAmount);
}
}
30 changes: 21 additions & 9 deletions test/metaStablePools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,15 +488,22 @@ describe(`Tests for MetaStable Pools.`, () => {
);
expect(swapInfoStable.tokenIn).to.deep.eq(swapInfo.tokenIn);
expect(swapInfoStable.tokenOut).to.deep.eq(swapInfo.tokenOut);
expect(swapInfoStable.returnAmount.toString()).eq(
swapInfo.returnAmount.mul(tokenInPriceRate).div(ONE).toString()
);
expect(swapInfoStable.returnAmountConsideringFees.toString()).eq(
swapInfo.returnAmountConsideringFees
.mul(tokenInPriceRate)
.div(ONE)
.toString()
);
expect(
almostEqual(
swapInfoStable.returnAmount,
swapInfo.returnAmount.mul(tokenInPriceRate).div(ONE)
),
'they are not almost equal'
).eq(true);
expect(
almostEqual(
swapInfoStable.returnAmountConsideringFees,
swapInfo.returnAmountConsideringFees
.mul(tokenInPriceRate)
.div(ONE)
),
'they are not almost equal'
).eq(true);
expect(swapInfoStable.swaps.length).eq(swapInfo.swaps.length);
swapInfoStable.swaps.forEach((swapStable, i) => {
expect(swapStable.poolId).eq(swapInfo.swaps[i].poolId);
Expand Down Expand Up @@ -750,3 +757,8 @@ describe(`Tests for MetaStable Pools.`, () => {
// });
});
});

function almostEqual(arg1: BigNumber, arg2: BigNumber): boolean {
const diff = arg1.sub(arg2).toBigInt();
return diff == BigInt(0) || diff == BigInt(1) || diff == BigInt(-1);
}
4 changes: 2 additions & 2 deletions test/stablePools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ describe(`Tests for Stable Pools.`, () => {
console.log(`Return amt:`);
console.log(swapInfo.returnAmount.toString());
// This value is hard coded as sanity check if things unexpectedly change. Taken from V2 test run (with extra fee logic added).
expect(swapInfo.returnAmount.toString()).eq('1000401');
expect(swapInfo.returnAmount.toString()).eq('1000402');
expect(swapInfo.swaps.length).eq(1);
expect(swapInfo.swaps[0].amount.toString()).eq(swapAmt.toString());
expect(swapInfo.swaps[0].poolId).eq(poolsFromFile.pools[0].id);
Expand Down Expand Up @@ -336,7 +336,7 @@ describe(`Tests for Stable Pools.`, () => {
);

// This value is hard coded as sanity check if things unexpectedly change. Taken from V2 test run (with extra fee logic added).
expect(swapInfo.returnAmount.toString()).eq('18089532');
expect(swapInfo.returnAmount.toString()).eq('18089534');
expect(swapInfo.swaps.length).eq(2);
expect(swapInfo.swaps[0].amount.toString()).eq(swapAmt.toString());
expect(swapInfo.swaps[0].poolId).eq(
Expand Down
11 changes: 8 additions & 3 deletions test/testScripts/swapExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ export const ADDRESSES = {
decimals: 18,
symbol: 'bbausdc',
},
bbadai: {
address: '0x804cdb9116a10bb78768d3252355a1b18067bf8f',
decimals: 18,
symbol: 'bb-a-dai',
},
waDAI: {
address: '0x02d60b84491589974263d922d9cc7a3152618ef6',
decimals: 18,
Expand Down Expand Up @@ -717,10 +722,10 @@ export async function simpleSwap() {
const networkId = Network.MAINNET;
// Pools source can be Subgraph URL or pools data set passed directly
// Update pools list with most recent onchain balances
const tokenIn = ADDRESSES[networkId].DAI;
const tokenIn = ADDRESSES[networkId].wSTETH;
const tokenOut = ADDRESSES[networkId].WETH;
const swapType = SwapTypes.SwapExactIn;
const swapAmount = parseFixed('1000000', 18);
const swapType = SwapTypes.SwapExactOut;
const swapAmount = parseFixed('10.912438109873074672', 18);
const executeTrade = true;

const provider = new JsonRpcProvider(PROVIDER_URLS[networkId]);
Expand Down