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

Test slippage calculation with real data #85

Merged
merged 3 commits into from
Aug 22, 2024
Merged
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
115 changes: 63 additions & 52 deletions apps/api/scripts/test-slippage.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,72 +13,72 @@ const PAIRS_TEST = {
Mainnet: [
{
pair: 'USDC-DAI',
quoteToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
baseToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
},
{
pair: 'DAI-ETH',
quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
baseToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
quoteToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
},
{
pair: 'DAI-WETH',
quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
baseToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
baseToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
quoteToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
{
pair: 'PEPE-DAI',
quoteToken: '0x6982508145454Ce325dDbE47a25d4ec3d2311933',
baseToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
baseToken: '0x6982508145454Ce325dDbE47a25d4ec3d2311933',
quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
},
{
pair: 'PEPE-WETH',
quoteToken: '0x6982508145454Ce325dDbE47a25d4ec3d2311933',
baseToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
baseToken: '0x6982508145454Ce325dDbE47a25d4ec3d2311933',
quoteToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
{
pair: 'COW-WETH',
quoteToken: '0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB',
baseToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
baseToken: '0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB',
quoteToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
},
],
'Gnosis Chain': [
{
pair: 'USDC-xDAI',
quoteToken: '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83',
baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
baseToken: '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83',
quoteToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
},
{
pair: 'sDAI-xDAI',
quoteToken: '0xaf204776c7245bF4147c2612BF6e5972Ee483701',
baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
baseToken: '0xaf204776c7245bF4147c2612BF6e5972Ee483701',
quoteToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
},
{
pair: 'sDAI-wxDAI',
quoteToken: '0xaf204776c7245bF4147c2612BF6e5972Ee483701',
baseToken: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d',
baseToken: '0xaf204776c7245bF4147c2612BF6e5972Ee483701',
quoteToken: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d',
},
{
pair: 'WETH-xDAI',
quoteToken: '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1',
baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
baseToken: '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1',
quoteToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
},
],
Arbitrum: [
{
pair: 'USDC-DAI',
quoteToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
baseToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
baseToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
quoteToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
},
{
pair: 'DAI-WETH',
quoteToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
baseToken: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
baseToken: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
quoteToken: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
},
{
pair: 'COW-WETH',
quoteToken: '0xcb8b5CD20BdCaea9a010aC1F8d835824F5C87A04',
baseToken: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
baseToken: '0xcb8b5CD20BdCaea9a010aC1F8d835824F5C87A04',
quoteToken: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
},
],
};
Expand All @@ -98,42 +98,53 @@ const fetchForMarket = async (chainId, quoteToken, baseToken, endpoint) => {
};

const fetchVolatilityDetails = async (pair, chainId, quoteToken, baseToken) => {
const result = await fetchForMarket(
chainId,
quoteToken,
baseToken,
'volatilityDetails'
);

fs.writeFileSync(
`${chainId}-${pair}__volatilityDetails.json`,
JSON.stringify(result, null, 2)
);
return fetchForMarket(chainId, quoteToken, baseToken, 'volatilityDetails');
};

const fetchSlippageTolerance = async (pair, chainId, quoteToken, baseToken) => {
const result = await fetchForMarket(
chainId,
quoteToken,
baseToken,
'slippageTolerance'
);
console.log(
`${pair}: ${result ? result.slippageBps : 'Error fetching data'}`
);
fs.writeFileSync(
`${chainId}-${pair}__slippageTolerance.json`,
JSON.stringify(result, null, 2)
);
return fetchForMarket(chainId, quoteToken, baseToken, 'slippageTolerance');
};

(async function () {
for (const chain in PAIRS_TEST) {
console.log(`\nFetching slippage tolerances for ${chain}:`);
const chainId = NetworkToChainId[chain];
for (const { pair, quoteToken, baseToken } of PAIRS_TEST[chain]) {
await fetchSlippageTolerance(pair, chainId, quoteToken, baseToken);
await fetchVolatilityDetails(pair, chainId, quoteToken, baseToken);
const slippage = await fetchSlippageTolerance(
pair,
chainId,
quoteToken,
baseToken
);
const volatilityDetails = await fetchVolatilityDetails(
pair,
chainId,
quoteToken,
baseToken
);

if (!slippage) {
console.error(`Error fetching slippage tolerance for ${pair}`);
process.exit(1);
}

console.log(`${pair}: ${slippage.slippageBps}`);

fs.writeFileSync(
`${chainId}-${pair}.json`,
JSON.stringify(
{
pair,
chainId,
baseToken,
quoteToken,
slippageBps: slippage ? slippage.slippageBps : null,
volatilityDetails,
},
null,
2
)
);
}
}
})();
21 changes: 18 additions & 3 deletions apps/api/src/app/plugins/cors.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import cors from "@fastify/cors";
import fp from "fastify-plugin";
import cors, { FastifyCorsOptions } from '@fastify/cors';
import fp from 'fastify-plugin';

export default fp(async (fastify, opts) => {
const options = {
const options: FastifyCorsOptions = {
...opts,
origin: false,
delegator: (req, callback) => {
const corsOptions = {
origin: false,
};

// do not include CORS headers for requests from localhost
const origin = req.headers.origin;
console.log('delegator', origin);
if (origin && /^http:\/\/localhost/.test(origin)) {
corsOptions.origin = true;
}

// callback expects two parameters: error and options
callback(null, corsOptions);
},
};
fastify.register(cors, options);
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ const POINTS_WITH_LOW_VOLATILITY = getPoints([
100.0001, 100.0002, 100.0003, 100.0004,
]); // 0.0001% each 5min

describe('SlippageServiceMain', () => {
/**
* Test specification for the SlippageService main implementation
*/
describe('SlippageServiceMain Specification', () => {
let slippageService: SlippageServiceMain;
let usdRepositoryMock: UsdRepository;

Expand Down
105 changes: 105 additions & 0 deletions libs/services/src/SlippageService/SlippageServiceMain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { SlippageServiceMain } from './SlippageServiceMain';
import fs from 'fs';
import path from 'path';
import {
ChainNames,
SupportedChainId,
toSupportedChainId,
} from '../../../shared/src';

const getUsdPrice = jest.fn();
const getUsdPrices = jest.fn();

/**
* Test the SlippageService main implementation using realistic test data.
* One difference between these tests and the ones in SlippageServiceMain.spec.ts is that in these tests we will use
* real data for prices and USD we received from Coingecko and we save into test data files.
*
* These will allow to refine the slippage calculation algorithm to make it work well with real data.
*/
describe('SlippageServiceMain: Real test data', () => {
let slippageService: SlippageServiceMain;

// Read all files in test-data folder
const testDataDir = path.join(__dirname, 'test-data');
const testDataFiles = fs.readdirSync(testDataDir);

for (const fileName of testDataFiles) {
const {
pair,
baseToken: baseTokenAddress,
quoteToken: quoteTokenAddress,
chainId: chainIdValue,
volatilityDetails,
slippageBps,
} = readTestFile(path.join(testDataDir, fileName));

const chainId = toSupportedChainId(chainIdValue);
const chainName = ChainNames[chainId];

// Uncomment to tests a single pair
// if (pair !== 'WETH-xDAI' || chainId !== SupportedChainId.GNOSIS_CHAIN)
// continue;

test(`Expect ${chainName} ${pair} slippage to be ${slippageBps} BPS`, async () => {
const {
quoteToken: quoteTokenVolatilityDetails,
baseToken: baseTokenVolatilityDetails,
} = volatilityDetails;

// GIVEN: USD price for the base and quote tokens
getUsdPrice.mockImplementation(async (_chainId, tokenAddress) => {
if (tokenAddress === baseTokenAddress) {
return baseTokenVolatilityDetails.usdPrice;
} else {
return quoteTokenVolatilityDetails.usdPrice;
}
});

// GIVEN: USD prices for the base and quote tokens
getUsdPrices.mockImplementation(async (_chainId, tokenAddress) => {
if (tokenAddress === baseTokenAddress) {
return baseTokenVolatilityDetails.prices;
} else {
return quoteTokenVolatilityDetails.prices;
}
});

// WHEN: Get the slippage
const slippage = await slippageService.getSlippageBps({
chainId,
baseTokenAddress,
quoteTokenAddress,
});

// THEN: The slippage should be as expected
expect(slippage).toBe(slippageBps);
});
}

beforeEach(() => {
slippageService = new SlippageServiceMain({
getUsdPrice,
getUsdPrices,
});
});
});

function readTestFile(filePath: string) {
const testContent = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
const volatilityDetails = testContent.volatilityDetails;

volatilityDetails.quoteToken.prices =
volatilityDetails.quoteToken.prices.map(fixDateForPrices);
volatilityDetails.baseToken.prices =
volatilityDetails.baseToken.prices.map(fixDateForPrices);

return testContent;
}

function fixDateForPrices(price: any) {
return {
...price,
date: new Date(price.date),
};
}
Loading
Loading