TLDR;
Similar to this issue here , use .call
after the function name. Then it works out!
const newLimit = await contract.setAllowance.call(kid1, value); // the .call() is the fix
Edit: (adding more details in the line of @GoodVibration's comments)
setAllowance
is a non-constant call, which means it modifies contract state in some way. constant
was used in older solidity code, which in modern versions are indicated via view
(only reads state) or pure
(no read from, or write to contract state).
When we invoke such a non-constant method, we will only get the transaction response as the return value, irrespective of what return value we might have coded in (in this case, uint256
). Thus, we have two options:
- use
.call
: this sort of "evaluates" the code inside the method, but not cause a transaction to happen, and thus no state change will be persisted. A test case to help explain things better:
it("will set allowance limits to non-zero within the scope of the method", async () => {
try {
const value = web3.utils.toWei("0.2");
// https://ethereum.stackexchange.com/a/90342/22522
const newLimit = await contract.setAllowance.call(accounts[1], value);
assert.equal(value, newLimit); // we get a return value
} catch (err) {
console.log("error:\n", err);
assert(false);
}
});
it("proves that above call does not persist state change", async () => {
const allowance = await contract.getAllowance(kid1);
assert.equal(allowance, 0); // allowance did not change at all!!
});
- Invoke the method, and use Events for testing: when we write
await contract.method(... args)
, that method
is actually invoked, transaction goes through mining, and events are fired. While this is great, it will always return a Tx object as response, and not the return type you coded. As such you need to assert on the events that gets fired. something like this:
// smart contract
event AllowanceChange(address user, uint256 oldLimit, string delta, uint256 newLimit);
...
function setAllowance(address user, uint256 amount) external onlyOwner nonReentrant returns (uint256) {
allowance[user] = amount;
emit AllowanceChange(user, amount, "set", allowance[user]);
return allowance[user];
}
// Truffle JS Test
const value = web3.utils.toWei("0.2");
const result = await contract.setAllowance(accounts[1], value);
truffleAssert.eventEmitted(
result,
"AllowanceLimitChange",
(ev) => ev.newLimit.toString() === value,
"Allowance Change event should be triggered"
);