How can I use getReserves inside a forEach loop to call the reserves relative to each array object (the array objects are token addresses)? Details below:

I have an array of addresses being monitored inside a forEach loop like so:

const arbFor = process.env.ARB_FOR 
const arbAgainst = ["address1","address2","address3"]
let i = 0;


  const main = async () => {
    const { token0Contract, token1Contract, token0, token1 } = await getTokenAndContract(arbFor, arbAgainst[i])
    var uPair = await getPairContract(uFactory, token0.address, token1.address)
    uniPair = Object.freeze(uPair);
    var sPair = await getPairContract(sFactory, token0.address, token1.address)
    sushiPair = Object.freeze(sPair);
    // Do some stuff

  // Other async functions

  const determineProfitability = async (_routerPath, _token0Contract, _token0, _token1,  exchange, token0, token1) => {
    console.log(`Determining Profitability...\n`)

    let reserves, exchangeToBuy, exchangeToSell

    if (_routerPath[0]._address == uRouter._address) {
        reserves = await getReserves(sushiPair)
        exchangeToBuy = 'Uniswap'
        exchangeToSell = 'Sushiswap'
    } else {
        reserves = await getReserves(uniPair)
        exchangeToBuy = 'Sushiswap'
        exchangeToSell = 'Uniswap'

    console.log(`Reserves on ${_routerPath[1]._address}\n`)
    console.log(`Token 1: ${web3.utils.fromWei(reserves[0].toString(), 'ether')}`)
    console.log(`Token 0: ${web3.utils.fromWei(reserves[1].toString(), 'ether')}\n`)

As you can see, I've been attempting to use Object.freeze to work with a specific object for that iteration of the forEach loop. However, when I run this, it seems to only freeze one time and attempt to iterate over that same object each time. So clearly it doesn't freeze anew for each array object.

However, if I simply use uPair and sPair inside getReserves() I get the following error stack:

  const reserves = await _pairContract.methods.getReserves().call()

TypeError: Cannot read properties of undefined (reading 'methods')
    at getReserves (/Users/Me/trading_bot_V2/helpers/helpers.js:54:42)
    at calculatePrice (/Users/Me/trading_bot_V2/helpers/helpers.js:59:40)
    at checkPrice (/Users/Me/trading_bot_V2/bot.js:105:34)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Subscription.callback (/Users/Me/trading_bot_V2/bot.js:40:45)

Any thoughts?

**Update - I removed var from in front of uPair and sPair. Now they're behaving exactly like uniPair and sushiPair did. Which is to say they're only looping through a single reserves router path. And I think it's also worth noting that path doesn't change ever regardless of which swap I identify first (say USDT/WETH or DAI/WETH)

Edit - I realized it may be beneficial to see the getReserves() function, as it is used differently in this case. Here are the relevant functions:

async function getPairAddress(_V2Factory, _token0, _token1) {
    const pairAddress = await _V2Factory.methods.getPair(_token0, _token1).call()
    return pairAddress

async function getPairContract(_V2Factory, _token0, _token1) {
    const pairAddress = await getPairAddress(_V2Factory, _token0, _token1)
    const pairContract = new web3.eth.Contract(IUniswapV2Pair.abi, pairAddress)
    return pairContract

async function getReserves(_pairContract) {
    const reserves = await _pairContract.methods.getReserves().call()
    return [reserves.reserve0, reserves.reserve1]

***Update 2: I've JUST noticed that when I call console.log(`Reserves on ${_routerPath[1]._address}\n`) it's actually printing the router address for sushiswap or uniswap rather than pulling reserves. Might have something to do with it. Still open to suggestions.

2 Answers 2


I would have an array with the addresses of all pairs and would do something like this:

for(let i = 0; i < allPairs.length; i++){
  const pair = new ethers.Contract(allPairs[i], UniswapV2Pair.abi, provider)
const reserves = await pair.getReserves()

pairWithReserves.push({pair: allPair[i], reserve0: reserves.token0, reserve1: reserves.token1})

This would be enough. It's just a glimpse on how you can solve this easily.

BROTIP: If it's for arbitrage, you should be looking for kLast() insteed of getReserves()

  • Thanks so much! What's the benefit of kLast() over getReserves()? Jan 5, 2023 at 10:26
  • Also, any chance you could put this example in Web3js terms? I don't use ethers haha Jan 5, 2023 at 11:26
  • kLast returns the quociente of getReserves. Jan 5, 2023 at 19:19
  • I dont work with web3.js for years but the approach is Just create istance for the pair contract and call the function. Jan 5, 2023 at 19:20
  • Thank you so much for this! After a lot of trial and error, I realized I was asking the wrong question. But this should definitely help others! You should link your Medium article about kLast() here too Jan 11, 2023 at 11:17

The answer here ended up being that uPair (which I didn't tell you because I didn't know it was important here) is a global variable with no initial definition. It was defined in main() and I never actually called it in to checkPrice

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.