0

I am attempting to modify the masterCounter variable within the timeKeyAdditionCheck function. Within the timeKeyAdditionCheck function, I successfully assign a value to masterCounter, but this change is not reflected within the scope of getEventsWithTime. When timeKeyAdditionCheck is complete, the value of masterCounter returns to null.

What changes do I need to make with timeKeyAdditionCheck function?

let masterCounter = null;
let userTracker = {}; 

let timeKeyAdditionCheck = ('hour') => {
    assert((range == 'minute' || range == 'hour'), "In calcArrayof... range value needs to equal 'minute' or 'hours'")
    
    if (masterCounter == null) {
        masterCounter = [{timerange: event.timestamp, totalusercount: 0, totalvalidatorcount: 0, totaletherdeposited: 0}]
    }
    if (event.timestamp > (masterCounter[masterCounter.length - 1] + 3599)) {
        
            let differenceInTime = event.timestamp - (masterCounter[masterCounter.length - 1] + 3599);
            let timeKeysNeeded = Math.ceil(differenceInTime / 3600);
        
            i = 0;
            while (i < timeKeysNeeded) {
                
                let newEntry = masterCounter[masterCounter.length - 1];
                newEntry.timerange = newEntry.timerange + 3600;
                masterCounter.push(newEntry);
                i++;
            }
        }
}
(async () => {
    let events = await getEventsWithTime(3085928,3089928);
    
    for (event of events) {
        timeKeyAdditionCheck('hour');
        checkNewUsers();
        addValidatorsAndEth();    
    }

    convertToCsv(masterCounter)
    console.log(masterCounter)
  })()

3 Answers3

1

Because masterCounter is not declared within timeKeyAdditionCheck (it's assigned there, but not declared there), masterCounter is implicitly declared as a Global variable. But, in getEventsWithTime, you do declare masterCounter so that declaration "hides" the Global one and is treated as a completely separate variable.

You need masterCounter to be declared in a higher scope than either of the two functions so that both can have access to it, or you could pass masterCounter to the getEventsWithTime function as an argument.

// By declaring the variable in a higher scope than either of the functions
// that need access to it, both can find it and use it.
let masterCounter = null // later turns into an array

let timeKeyAdditionCheck = (event, range, masterCounter) => {
    assert((range == 'minute' || range == 'hour'), "In calcArrayof... range value needs to equal 'minute' or 'hours'")
    
    if (masterCounter == null) {
        masterCounter = [{timerange: event.timestamp, totalusercount: 0, totalvalidatorcount: 0, totaletherdeposited: 0}]
    }
    if (event.timestamp > (masterCounter[masterCounter.length - 1] + 3599)) {
        
            let differenceInTime = event.timestamp - (masterCounter[masterCounter.length - 1] + 3599);
            let timeKeysNeeded = Math.ceil(differenceInTime / 3600);
        
            i = 0;
            while (i < timeKeysNeeded) {
                
                let newEntry = masterCounter[masterCounter.length - 1];
                newEntry.timerange = newEntry.timerange + 3600;
                masterCounter.push(newEntry);
                i++;
            }
        }
}

let getEventsWithTime = async (firstBlock, lastBlock, range) => {

    try {
        let userTracker = {};
        let events = await depositContract.getPastEvents('DepositEvent', {fromBlock: firstBlock, toBlock: lastBlock}) // 2845084 2846000
    
        for (event of events) {
            let results = await web3.eth.getBlock(event.blockNumber);
            event.timestamp = results.timestamp;

            timeKeyAdditionCheck(event, range, masterCounter);
            checkNewUsers(event, userTracker, masterCounter);
            addValidatorsAndEth(event, userTracker, masterCounter);

        }
        convertToCsv(masterCounter)
        console.log(masterCounter)
        
    
    } catch(error) {
        console.log(error);
    }
}

Also, see this post of mine, which explains the "scope chain" and illustrates this issue in more detail.

Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • Thanks for the response Scott. I made `masterCounter` a global variable, but I am still having the same problem. `timeKeyAdditionsCheck` does correctly recognize and modifies `masterCounter` but, when `timeKeyAdditionCheck` finishes executing, and we return to the next line of code in `getEventsWithTime`, the value returns to `null`. Any idea why that happens? – Julian Martinez Aug 22 '20 at 23:55
  • @JulianMartinez Can you clarify? Did you remove the `let masterCounter = null` line from `getEventsWithTime`, which you need to do because if all you did was declare `masterCounter` Globally, you'll still have a local `masterCounter` variable hiding the Global one. Also, because you've made `getEventsWithTime` an `async` function, you need to make sure that you don't attempt to use any variable that is affected by that function until after the async operation finishes. – Scott Marcus Aug 23 '20 at 00:46
  • I did remove `let masterCounter = null` line from `getEventsWithTime`. By "you need to make sure that you don't attempt to use any variable affected by that function until after the async operation finishes", do you mean I need to that I should remove `timeKeyAdditionsCheck`, `checkNewUsers`, `addValidatorsAndEth` from within the try block? – Julian Martinez Aug 23 '20 at 17:56
  • @JulianMartinez I only meant that you shouldn't try to access the result of an async function until it has resolved, which you do with `await`, but not knowing the entire flow of your code, I just wanted to point that out. – Scott Marcus Aug 23 '20 at 19:49
0

You should declare masterCounter in a scope which contains both functions that are going to use it.

For example, you could declare masterCounter immediately preceding when you declare either function (assuming they are in the same scope).

A sign that your declarations are incorrect is the check to see if masterCounter is null. For it to be shared outside of these functions, it can never be null when the function attempts to access or set it.

Ben
  • 101
  • 4
  • Considering that I declare `masterCounter` within `getEventsWithTime` and `timeKeyAdditionCheck` is called within `getEventsWithTime`, shouldn't both have access to `masterCounter`? – Julian Martinez Aug 22 '20 at 23:27
  • @JulianMartinez it's not about where you call a function, it's about the scope in which you declare it. – Ben Aug 23 '20 at 10:38
0

The reason I wasn't getting the expected output for masterCounter was because, in the timeKeyAdditionCheckfunction, I thought I was making a copy of the object in the masterCounter array, but I actually created a reference instead. Here is the moment in my code when I unintentionally created a reference instead of a copy:

let newEntry = masterCounter[masterCounter.length - 1];

When I thought I was adding a unique object to the array, I was instead adding the reference to that same object at the end of the array.

I fixed it using the following code:

while (i < timeKeysNeeded) {
                
   let lastObjectRef = masterCounter[masterCounter.length - 1];
   let newEntry = Object.assign({}, lastObjectRef)
   newEntry.timerange = newEntry.timerange + 60;
   masterCounter.push(newEntry);
   i++;
}

I used Object.assign() to create a copy of the last object in the array instead of creating another reference.