-
Notifications
You must be signed in to change notification settings - Fork 43
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
Changes from all commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
4a22286
add script
gsteenkamp89 2a88f00
batch requests by chainId, refactor
gsteenkamp89 62d2a4a
wip
nicholaspai c4e0388
Merge branch 'master' into feat-compare-gas-costs
nicholaspai 566ca31
Update .gitignore
nicholaspai 69b0962
improve(API): Reduce stale-while-revalidate and gas price cache times
nicholaspai 0e94791
Separate native gas cost and op stack l1 gas cost calculation from to…
nicholaspai ccb8fa8
Use gas price cache for Linea as well
nicholaspai ea1585e
fix: only cron cache gas prices for non Linea chains
nicholaspai 5e4fd39
Update limits.ts
nicholaspai 296056f
Only pass in depositArgs for Linea
nicholaspai c1f6e03
add extra part to cache key
nicholaspai 5f7ab07
Use sdk for helper methods
nicholaspai d6dac13
Update limits.ts
nicholaspai 9a1157c
Merge branch 'cache-times' into feat-compare-gas-costs
nicholaspai b06f9cc
Fix gas-prices
nicholaspai 9e18813
Use utils in gas-prices.ts to read data from cache
nicholaspai bc5aec1
add gas costs to cron job
nicholaspai f42156f
cache gas prices before cost
nicholaspai be317b7
remove promise.all
nicholaspai bbfe3f7
Update _utils.ts
nicholaspai ea1d1a7
cache op stack l1 costs for op chains only
nicholaspai 129e1b8
Test only cache gas prices
nicholaspai 01981bd
debug
nicholaspai 39027b3
Fix cron job
nicholaspai a63bf04
Update cron-cache-gas-prices.ts
nicholaspai 2161690
fix promise nesting
nicholaspai a845217
Update cron-cache-gas-prices.ts
nicholaspai a93ccfd
Merge branch 'cache-times' into feat-compare-gas-costs
nicholaspai 2f4b20b
Merge branch 'master' into feat-compare-gas-costs
nicholaspai File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
|
||
# testing | ||
/coverage | ||
gas-price-comparison.csv | ||
|
||
# production | ||
/build | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import { BigNumber } from "ethers"; | ||
import { writeFileSync } from "fs"; | ||
import { getCachedLimits, buildSearchParams } from "../api/_utils"; | ||
import assert from "assert"; | ||
import dotenv from "dotenv"; | ||
|
||
dotenv.config({ | ||
path: [".env.local", ".env"], | ||
}); | ||
|
||
type Limits = Awaited<ReturnType<typeof getCachedLimits>>; | ||
|
||
type Route = { | ||
originChainId: number; | ||
originToken: string; | ||
destinationChainId: number; | ||
destinationToken: string; | ||
originTokenSymbol: string; | ||
destinationTokenSymbol: string; | ||
}; | ||
|
||
// 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 = process.env.TEST_URL_GAS_PRICE; | ||
|
||
assert( | ||
TEST_URL, | ||
'No Test URL defined. Please add "TEST_URL_GAS_PRICE" to .env file' | ||
); | ||
|
||
const routes = (await fetch(`${PROD_URL}/api/available-routes`).then((res) => | ||
res.json() | ||
)) as Array<Route>; | ||
|
||
const chainTokenMap = (() => { | ||
const chainTokenMap = new Map<number, Set<string>>(); | ||
routes.forEach((route) => { | ||
if (!chainTokenMap.has(route.destinationChainId)) { | ||
chainTokenMap.set( | ||
route.destinationChainId, | ||
new Set([route.destinationTokenSymbol]) | ||
); | ||
} else { | ||
const tokens = chainTokenMap.get(route.destinationChainId)!; | ||
chainTokenMap.set( | ||
route.destinationChainId, | ||
tokens.add(route.destinationTokenSymbol) | ||
); | ||
} | ||
}); | ||
return chainTokenMap; | ||
})(); | ||
|
||
console.log("Batching requests by chainId => ", chainTokenMap); | ||
console.log("Please wait..."); | ||
|
||
// gas prices are cached per-chain, so we want to batch requests by chainId for more accurate results | ||
const getLimitsByChainId = async ( | ||
baseUrl: string, | ||
chainId: number, | ||
tokenBatch: string[] | ||
) => { | ||
return Promise.all( | ||
tokenBatch.map(async (tokenSymbol) => { | ||
const route = routes.find( | ||
(r) => | ||
r.destinationChainId === chainId && | ||
r.destinationTokenSymbol === tokenSymbol | ||
); | ||
// shoudn't be possible | ||
if (!route) { | ||
throw new Error( | ||
`Route not found for chainId: ${chainId}, token: ${tokenSymbol}` | ||
); | ||
} | ||
|
||
const { | ||
originToken, | ||
destinationToken, | ||
originChainId, | ||
destinationChainId, | ||
} = route; | ||
|
||
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: tokenSymbol, | ||
}; | ||
}) | ||
); | ||
}; | ||
|
||
const fetchAllLimits = async (baseUrl: string) => { | ||
const allResults: Array<{ | ||
token: string; | ||
destinationChainId: number; | ||
gasFeeTotal: BigNumber; | ||
}> = []; | ||
|
||
for (const [chainId, tokens] of chainTokenMap.entries()) { | ||
const tokenBatch = Array.from(tokens); | ||
const batchResults = await getLimitsByChainId( | ||
baseUrl, | ||
chainId, | ||
tokenBatch | ||
); | ||
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(); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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/
directoryThere was a problem hiding this comment.
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