I have a simple contract that functions as a ratekeeper - it is constructed with a capacity and time_frame (in blocks) parameters and makes sure we never add more than $capacity payments in any given time frame. The function addPayment() registers the timestamp of the payment and adds one to the load, if the we didn't go over the capacity. It will return false otherwise.

pragma solidity >=0.5.8 <0.6.0;

    contract RateKeeper {
        uint256 public capacity;
        uint256 public timeframe;

        uint256 public load; // Number of transactions in record so far

        constructor (uint256 _capacity, uint256 _timeframe) public {

            capacity = _capacity;
            timeframe = _timeframe;
            load = 0;

        function addPayment() public onlyOwner returns (bool added) {
            // removeOldEntries();
            if (load >= capacity){
                return false;
            return true;

When I test the function in solidity it behaves as expected, however in JavaScript the load doesn't increase with function calls. Why is that?

In the following JavaScript test the two last assertions fail:

contract("JS RateKeeper test", async accounts => {
  it("Constructor, addPayment(). This test doesn't pass", async function () {

    let instance = await RateKeeper.new(1, 299);
    let inst_capacity = await instance.capacity.call();
    assert.equal(inst_capacity, 1);
    let load = await instance.load.call();
    assert.equal(load, 0, "load is 0 as no payments were initiated so far");
    let payment_success = await instance.addPayment.call();
    assert(payment_success, "Load is 0, capacity is 1 - payment should succeed");
    let load2 = await instance.load.call();
    assert.equal(load2, 1, "load should be 1 after a single call to addPayment()");
    payment_success = await instance.addPayment.call();
    assert(!payment_success, "We are at full capacity, payment should fail");

Meanwhile, the solidity test goes smoothly

  function testAddPayment() public{
    RateKeeper keeper = new RateKeeper(2,299);
    Assert.equal(keeper.load(), 0, "We added no payments");
    Assert.isTrue(keeper.addPayment(), "Contract has (0/2) payments");
    Assert.equal(keeper.load(), 1, "We added 1 payment");
    Assert.isTrue(keeper.addPayment(), "Contract has (1/2) payments");
    Assert.equal(keeper.load(), 2, "We added 2 payment");
    Assert.isFalse(keeper.addPayment(), "Contract has (2/2) payments");
    Assert.equal(keeper.load(), 2, "We added 2 payment");
    Assert.isFalse(keeper.addPayment(), "Contract has (2/2) payments");
    Assert.equal(keeper.load(), 2, "We added 2 payment");

1 Answer 1


This is due to the behavior of call(). Call is for read-only, e.g. any state alterations done in the function are not persistent. If you change the instances of instance.addPayment.call() to instance.addPayment(), it will persist the changes.

There is another complication of course, that you are using the return value of addPayment, which doesn't really work for persistent changes. You would have to revert on false or add log statements to see what's happening externally.

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.