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

BACK-1626: Integrate fx-protocol rUSD #734

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1,227 changes: 1,227 additions & 0 deletions src/abi/fx-protocol/FxUSD.json

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions src/dex/fx-protocol-rusd/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { DexParams } from './types';
import { DexConfigMap, AdapterMappings } from '../../types';
import { Network, SwapSide } from '../../constants';

export const FxProtocolConfig: DexConfigMap<DexParams> = {
FxProtocolRusd: {
[Network.MAINNET]: {
rUSDAddress: '0x65D72AA8DA931F047169112fcf34f52DbaAE7D18',
weETHAddress: '0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee',
ezETHAddress: '0xbf5495Efe5DB9ce00f80364C8B423567e58d2110',
},
},
};

export const Adapters: Record<number, AdapterMappings> = {
// This is an example to copy
[Network.MAINNET]: { [SwapSide.SELL]: [{ name: 'Adapter02', index: 0 }] },
};
93 changes: 93 additions & 0 deletions src/dex/fx-protocol-rusd/fx-protocol-e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* eslint-disable no-console */
import dotenv from 'dotenv';
dotenv.config();

import { testE2E } from '../../../tests/utils-e2e';
import {
Tokens,
Holders,
NativeTokenSymbols,
} from '../../../tests/constants-e2e';
import { Network, ContractMethod, SwapSide } from '../../constants';
import { StaticJsonRpcProvider } from '@ethersproject/providers';
import { generateConfig } from '../../config';

function testForNetwork(
network: Network,
dexKey: string,
tokenASymbol: string,
tokenBSymbol: string,
tokenAAmount: string,
tokenBAmount: string,
nativeTokenAmount: string,
) {
const provider = new StaticJsonRpcProvider(
generateConfig(network).privateHttpProvider,
network,
);
const tokens = Tokens[network];
const holders = Holders[network];

const sideToContractMethods = new Map([
[SwapSide.SELL, [ContractMethod.swapExactAmountIn]],
]);

describe(`${network}`, () => {
sideToContractMethods.forEach((contractMethods, side) =>
describe(`${side}`, () => {
contractMethods.forEach((contractMethod: ContractMethod) => {
describe(`${contractMethod}`, () => {
it(`${tokenASymbol} -> ${tokenBSymbol}`, async () => {
await testE2E(
tokens[tokenASymbol],
tokens[tokenBSymbol],
holders[tokenASymbol],
side === SwapSide.SELL ? tokenAAmount : tokenBAmount,
side,
dexKey,
contractMethod,
network,
provider,
);
});
});
});
}),
);
});
}

describe('FxProtocolRusd E2E', () => {
const dexKey = 'FxProtocolRusd';
const network = Network.MAINNET;

const tokenASymbol: string = 'weETH';
const tokenBSymbol: string = 'rUSD';

const tokenAAmount: string = '1000000000000000000';
const tokenBAmount: string = '1000000000000000000';
const nativeTokenAmount = '1000000000000000000';
describe('Mainnet weETH=>rUSD', () => {
testForNetwork(
network,
dexKey,
tokenASymbol,
tokenBSymbol,
tokenAAmount,
tokenBAmount,
nativeTokenAmount,
);
});

describe('Mainnet rUSD=>weETH', () => {
testForNetwork(
network,
dexKey,
tokenBSymbol,
tokenASymbol,
tokenBAmount,
tokenAAmount,
nativeTokenAmount,
);
});
});
219 changes: 219 additions & 0 deletions src/dex/fx-protocol-rusd/fx-protocol-integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/* eslint-disable no-console */
import dotenv from 'dotenv';
dotenv.config();

import { Interface, Result } from '@ethersproject/abi';
import { DummyDexHelper } from '../../dex-helper/index';
import { Network, SwapSide } from '../../constants';
import { BI_POWS } from '../../bigint-constants';
import { FxProtocolRusd } from './fx-protocol-rusd';
import {
checkPoolPrices,
checkPoolsLiquidity,
checkConstantPoolPrices,
} from '../../../tests/utils';
import { Tokens } from '../../../tests/constants-e2e';

function getReaderCalldata(
exchangeAddress: string,
readerIface: Interface,
amounts: bigint[],
funcName: string,
) {
return amounts.map(amount => ({
target: exchangeAddress,
callData: readerIface.encodeFunctionData(funcName, []),
}));
}

function decodeReaderResult(
results: Result,
readerIface: Interface,
funcName: string,
destTokenSymbol: string,
amounts: bigint[],
) {
return results.map((result, index) => {
const parsed = readerIface.decodeFunctionResult(funcName, result);
return BigInt(parsed[0]._hex) * BigInt(amounts[index + 1] / BI_POWS[18]);
});
}

async function checkOnChainPricing(
fxProtocol: FxProtocolRusd,
funcName: string,
blockNumber: number,
prices: bigint[],
amounts: bigint[],
destTokenSymbol: string,
) {
const exchangeAddress = '0x65D72AA8DA931F047169112fcf34f52DbaAE7D18';

// Normally you can get it from fxProtocol.Iface or from eventPool.
// It depends on your implementation
const readerIface = FxProtocolRusd.fxUSDIface;
const readerCallData = getReaderCalldata(
exchangeAddress,
readerIface,
amounts.slice(1),
funcName,
);
const readerResult = (
await fxProtocol.dexHelper.multiContract.methods
.aggregate(readerCallData)
.call({}, blockNumber)
).returnData;

const expectedPrices = [0n].concat(
decodeReaderResult(
readerResult,
readerIface,
funcName,
destTokenSymbol,
amounts,
),
);
expect(prices).toEqual(expectedPrices);
}

async function testPricingOnNetwork(
fxProtocol: FxProtocolRusd,
network: Network,
dexKey: string,
blockNumber: number,
srcTokenSymbol: string,
destTokenSymbol: string,
side: SwapSide,
amounts: bigint[],
funcNameToCheck: string,
) {
const networkTokens = Tokens[network];
const pools = await fxProtocol.getPoolIdentifiers(
networkTokens[srcTokenSymbol],
networkTokens[destTokenSymbol],
side,
blockNumber,
);
console.log(
`${srcTokenSymbol} <> ${destTokenSymbol} Pool Identifiers: `,
pools,
);

expect(pools.length).toBeGreaterThan(0);

const poolPrices = await fxProtocol.getPricesVolume(
networkTokens[srcTokenSymbol],
networkTokens[destTokenSymbol],
amounts,
side,
blockNumber,
pools,
);
console.log(
`${srcTokenSymbol} <> ${destTokenSymbol} Pool Prices: `,
poolPrices,
);
expect(poolPrices).not.toBeNull();

// Check if onchain pricing equals to calculated ones
await checkOnChainPricing(
fxProtocol,
funcNameToCheck,
blockNumber,
poolPrices![0].prices,
amounts,
destTokenSymbol,
);
}

describe('FxProtocolRusd', function () {
const dexKey = 'FxProtocolRusd';
let blockNumber: number;
let fxProtocol: FxProtocolRusd;

describe('Mainnet', () => {
const network = Network.MAINNET;
const dexHelper = new DummyDexHelper(network);

const tokens = Tokens[network];

// Don't forget to update relevant tokens in constant-e2e.ts
const srcTokenSymbol = 'weETH';
const destTokenSymbol = 'rUSD';

const srcTokenSymbol_rUSD = 'rUSD';
const destTokenSymbol_weETH = 'weETH';

const amountsForSell = [
0n,
1n * BI_POWS[18],
2n * BI_POWS[18],
3n * BI_POWS[18],
4n * BI_POWS[18],
5n * BI_POWS[18],
6n * BI_POWS[18],
7n * BI_POWS[18],
8n * BI_POWS[18],
9n * BI_POWS[18],
10n * BI_POWS[18],
];

beforeAll(async () => {
blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
fxProtocol = new FxProtocolRusd(network, dexKey, dexHelper);
// if (fxProtocol.initializePricing) {
// await fxProtocol.initializePricing(blockNumber);
// }
});

it('getPoolIdentifiers and getPricesVolume SELL', async function () {
await testPricingOnNetwork(
fxProtocol,
network,
dexKey,
blockNumber,
srcTokenSymbol,
destTokenSymbol,
SwapSide.SELL,
amountsForSell,
'nav',
);
});

it('getPoolIdentifiers and getPricesVolume SELL', async function () {
await testPricingOnNetwork(
fxProtocol,
network,
dexKey,
blockNumber,
srcTokenSymbol_rUSD,
destTokenSymbol_weETH,
SwapSide.SELL,
amountsForSell,
'nav',
);
});

it('getTopPoolsForToken', async function () {
// We have to check without calling initializePricing, because
// pool-tracker is not calling that function
const newFxProtocol = new FxProtocolRusd(network, dexKey, dexHelper);
// if (newFxProtocol.updatePoolState) {
// await newFxProtocol.updatePoolState();
// }
const poolLiquidity = await newFxProtocol.getTopPoolsForToken(
tokens[srcTokenSymbol].address,
10,
);
console.log(`${srcTokenSymbol} Top Pools:`, poolLiquidity);

// if (!newFxProtocol.hasConstantPriceLargeAmounts) {
checkPoolsLiquidity(
poolLiquidity,
Tokens[network][srcTokenSymbol].address,
dexKey,
);
// }
});
});
});
Loading
Loading