Obtaining Pool Addresses
To retrieve a Uniswap V2 pool address (e.g., FTM/WETH on Ethereum Mainnet), use the factory contract's getPair function with the token contract addresses. Below is a JavaScript implementation using Ethers.js:
const { ethers } = require("hardhat");
const factoryAddress = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"; // Uniswap factory address
const weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const ftm = "0x4e15361fd6b4bb609fa63c81a2be19d873717870";
const factoryABI = [
"function getPair(address tokenA, address tokenB) external view returns (address pair)"
];
async function main() {
const factoryContract = new ethers.Contract(factoryAddress, factoryABI, ethers.provider);
const ftm_weth = await factoryContract.getPair(ftm, weth);
console.log("FTM/WETH Pool Address: %s", ftm_weth);
}
main().catch(console.error);Example Output: FTM/WETH Pool Address: 0x1ffC57cAda109985aD896a69FbCEBD565dB4290e
👉 Track live pool metrics on DexScreener
Calculating Token Prices
Method 1: Using Reserves
After obtaining the pool address, query reserves with getReserves() to calculate the price ratio:
const v2PairABI = ["function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)"];
async function getPrice() {
const ftmWethPairContract = new ethers.Contract(ftm_weth_address, v2PairABI, ethers.provider);
const [reserve0, reserve1] = await ftmWethPairContract.getReserves();
const price = new Big(reserve1).div(new Big(reserve0)).toString();
console.log("FTM/WETH Price:", price);
}Key Notes:
- Uses
Big.jsfor precise decimal calculations - Reserve order depends on token sorting in the pair
Example Output: FTM/WETH Price: 0.00027843670259855536
Method 2: Using Router Quotes
Alternative method with price impact consideration:
const routerAddress = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D";
const routerABI = ["function getAmountsIn(uint amountOut, address[] memory path) external view returns (uint[] memory amounts)"];
async function getRouterPrice() {
const routerContract = new ethers.Contract(routerAddress, routerABI, ethers.provider);
const amountOut = ethers.parseUnits('1', 18);
const price = await routerContract.getAmountsIn(amountOut, [weth, ftm]);
console.log("FTM/WETH Price (Router):", ethers.formatUnits(price[0], 18));
}Key Differences:
- Accounts for trading fees (0.3%)
- Returns slightly higher prices due to slippage protection
👉 Compare liquidity across DEXs
FAQ
Q: Why are prices from reserves and router different?
A: Router quotes include swap fees, while reserve ratios reflect raw pool balances.
Q: How do I handle tokens with different decimals?
A: Always normalize amounts using ethers.parseUnits() with the correct decimal places.
Q: Can I retrieve historical prices?
A: Yes, by querying reserves at specific block heights using archival nodes.
Q: What's the gas cost for these queries?
A: All methods shown are view functions—no gas fees required.
Optimization Tips
- Batch Requests: Fetch multiple pairs in single calls using Multicall contracts
- Caching: Store frequently-accessed pool addresses locally
- Error Handling: Verify contract existence before queries with
eth_getCode
For advanced analytics, consider subgraph queries or third-party APIs like 👉 OKX DEX aggregator.