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

script for comparing gas prices #1354

Merged
merged 30 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4a22286
add script
gsteenkamp89 Jan 6, 2025
2a88f00
batch requests by chainId, refactor
gsteenkamp89 Jan 7, 2025
62d2a4a
wip
nicholaspai Jan 10, 2025
c4e0388
Merge branch 'master' into feat-compare-gas-costs
nicholaspai Jan 10, 2025
566ca31
Update .gitignore
nicholaspai Jan 10, 2025
69b0962
improve(API): Reduce stale-while-revalidate and gas price cache times
nicholaspai Jan 10, 2025
0e94791
Separate native gas cost and op stack l1 gas cost calculation from to…
nicholaspai Jan 11, 2025
ccb8fa8
Use gas price cache for Linea as well
nicholaspai Jan 11, 2025
ea1585e
fix: only cron cache gas prices for non Linea chains
nicholaspai Jan 11, 2025
5e4fd39
Update limits.ts
nicholaspai Jan 11, 2025
296056f
Only pass in depositArgs for Linea
nicholaspai Jan 11, 2025
c1f6e03
add extra part to cache key
nicholaspai Jan 11, 2025
5f7ab07
Use sdk for helper methods
nicholaspai Jan 11, 2025
d6dac13
Update limits.ts
nicholaspai Jan 11, 2025
9a1157c
Merge branch 'cache-times' into feat-compare-gas-costs
nicholaspai Jan 11, 2025
b06f9cc
Fix gas-prices
nicholaspai Jan 11, 2025
9e18813
Use utils in gas-prices.ts to read data from cache
nicholaspai Jan 11, 2025
bc5aec1
add gas costs to cron job
nicholaspai Jan 11, 2025
f42156f
cache gas prices before cost
nicholaspai Jan 11, 2025
be317b7
remove promise.all
nicholaspai Jan 11, 2025
bbfe3f7
Update _utils.ts
nicholaspai Jan 11, 2025
ea1d1a7
cache op stack l1 costs for op chains only
nicholaspai Jan 11, 2025
129e1b8
Test only cache gas prices
nicholaspai Jan 11, 2025
01981bd
debug
nicholaspai Jan 11, 2025
39027b3
Fix cron job
nicholaspai Jan 11, 2025
a63bf04
Update cron-cache-gas-prices.ts
nicholaspai Jan 11, 2025
2161690
fix promise nesting
nicholaspai Jan 11, 2025
a845217
Update cron-cache-gas-prices.ts
nicholaspai Jan 11, 2025
a93ccfd
Merge branch 'cache-times' into feat-compare-gas-costs
nicholaspai Jan 11, 2025
2f4b20b
Merge branch 'master' into feat-compare-gas-costs
nicholaspai Jan 14, 2025
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@
"generate:routes:mainnet": "yarn generate:routes 1",
"generate:routes:testnet": "yarn generate:routes 11155111",
"generate:chain-config": "tsx scripts/generate-chain-config.ts",
"generate:ui-assets": "tsx scripts/generate-ui-assets.ts"
"generate:ui-assets": "tsx scripts/generate-ui-assets.ts",
"gas-prices": "tsx scripts/gas-prices.ts"
},
"lint-staged": {
"*.{jsx,tsx,js,ts}": [
Expand Down
204 changes: 204 additions & 0 deletions scripts/gas-prices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { BigNumber } from "ethers";
import { writeFileSync } from "fs";

type Limits = {
minDeposit: string;
maxDeposit: string;
maxDepositInstant: string;
maxDepositShortDelay: string;
recommendedDepositInstant: string;
relayerFeeDetails: {
relayFeeTotal: string;
relayFeePercent: string;
gasFeeTotal: string;
gasFeePercent: string;
capitalFeeTotal: string;
capitalFeePercent: string;
};
};
gsteenkamp89 marked this conversation as resolved.
Show resolved Hide resolved

type Route = {
originChainId: number;
originToken: string;
destinationChainId: number;
destinationToken: string;
originTokenSymbol: string;
destinationTokenSymbol: string;
};
Comment on lines +13 to +20
Copy link
Contributor

Choose a reason for hiding this comment

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

fwiw I think we have this typing elsewhere as well. If not we definitely reference it in the src/ directory

Copy link
Member

@nicholaspai nicholaspai Jan 10, 2025

Choose a reason for hiding this comment

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

yeah ideally we can re-use a type


function buildSearchParams(
params: Record<string, number | string | Array<number | string>>
): string {
const searchParams = new URLSearchParams();
for (const key in params) {
const value = params[key];
if (!value) continue;
if (Array.isArray(value)) {
value.forEach((val) => searchParams.append(key, String(val)));
} else {
searchParams.append(key, String(value));
}
}
return searchParams.toString();
}

const BATCH_SIZE = 5;

function chunkArray<T>(array: T[], size: number): T[][] {
const result: T[][] = [];
for (let i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size));
}
return result;
}
gsteenkamp89 marked this conversation as resolved.
Show resolved Hide resolved

// use this script to compare gas prices for relayers between 2 environments
async function compareGasPrices() {
const PROD_URL = "https://app.across.to";
const TEST_URL = "https://app-frontend-v3-git-gas-markup-fee-uma.vercel.app";
gsteenkamp89 marked this conversation as resolved.
Show resolved Hide resolved

const routes = (await fetch(`${PROD_URL}/api/available-routes`).then((res) =>
res.json()
)) as Array<Route>;

// Extract all unique destination tokens
const uniqueDestinationTokens = Array.from(
new Set(routes.map((route) => route.destinationTokenSymbol))
);

// Break the tokens into batches of 5
const tokenBatches = chunkArray(uniqueDestinationTokens, BATCH_SIZE);
nicholaspai marked this conversation as resolved.
Show resolved Hide resolved

const getLimits = async (baseUrl: string, tokenBatch: string[]) => {
return Promise.all(
routes
.filter((route) => tokenBatch.includes(route.destinationTokenSymbol))
.map(
async ({
destinationChainId,
originToken,
destinationToken,
originChainId,
destinationTokenSymbol,
}) => {
const limits = (await fetch(
`${baseUrl}/api/limits?${buildSearchParams({
inputToken: originToken,
outputToken: destinationToken,
originChainId,
destinationChainId,
})}`
).then((res) => res.json())) as Limits;
return {
destinationChainId,
gasFeeTotal: BigNumber.from(limits.relayerFeeDetails.gasFeeTotal),
token: destinationTokenSymbol,
};
}
)
);
};

const fetchAllLimits = async (baseUrl: string) => {
const allResults: Array<{
token: string;
destinationChainId: number;
gasFeeTotal: BigNumber;
}> = [];

for (const batch of tokenBatches) {
const batchResults = await getLimits(baseUrl, batch);
allResults.push(...batchResults);
}

return allResults;
};

const [prodResults, testResults] = await Promise.all([
fetchAllLimits(PROD_URL),
fetchAllLimits(TEST_URL),
]);

const aggregateResults = (
results: Array<{
token: string;
destinationChainId: number;
gasFeeTotal: BigNumber;
}>
) => {
const map = new Map<string, BigNumber>();

results.forEach(({ token, destinationChainId, gasFeeTotal }) => {
const key = `${token}-${destinationChainId}`;
if (!map.has(key)) {
// Use only the first entry for each token/chainId combination
map.set(key, gasFeeTotal);
}
// Ignore subsequent duplicates
});

return map;
};

const prodResultsMap = aggregateResults(prodResults);
const testResultsMap = aggregateResults(testResults);

// Prepare a set of all unique keys from both production and test results
const allKeys = new Set<string>([
...prodResultsMap.keys(),
...testResultsMap.keys(),
]);

// Prepare comparison data without duplicates
const comparisonData = Array.from(allKeys).map((key) => {
const [token, destinationChainIdStr] = key.split("-");
const destinationChainId = Number(destinationChainIdStr);
const prodGasFee = prodResultsMap.get(key) || BigNumber.from(0);
const testGasFee = testResultsMap.get(key) || BigNumber.from(0);
const difference = testGasFee.sub(prodGasFee);
const percentageChange = prodGasFee.isZero()
? "N/A"
: `${difference.mul(100).div(prodGasFee).toNumber()}%`;

return {
Token: token,
ChainID: destinationChainId,
ProductionGasFee: prodGasFee.toString(),
TestGasFee: testGasFee.toString(),
Difference: difference.toString(),
PercentageChange:
percentageChange !== "N/A" ? `${percentageChange}` : "N/A",
};
});

// Generate CSV content
const generateCSV = (data: typeof comparisonData) => {
const headers = [
"Token",
"ChainID",
"ProductionGasFee",
"TestGasFee",
"Difference",
"PercentageChange",
];
const rows = data.map((row) =>
[
row.Token,
row.ChainID,
row.ProductionGasFee,
row.TestGasFee,
row.Difference,
row.PercentageChange,
].join(",")
);
return [headers.join(","), ...rows].join("\n");
};

const csvContent = generateCSV(comparisonData);

// Save the CSV content to a file
writeFileSync("gas-price-comparison.csv", csvContent);

console.log("CSV file has been saved to gas-price-comparison.csv");
}
compareGasPrices();
Loading