I am following the CryptoZombies tutorial and having trouble getting one of the tests to pass. The test is as follows:
it("zombies should be able to attack another zombie", async () => {
let result;
result = await contractInstance.createRandomZombie(zombieNames[0], {from: alice});
const firstZombieId = result.logs[0].args.zombieId.toNumber();
result = await contractInstance.createRandomZombie(zombieNames[1], {from: bob});
const secondZombieId = result.logs[0].args.zombieId.toNumber();
await time.increase(time.duration.days(1));
await contractInstance.attack(firstZombieId, secondZombieId, {from: alice});
expect(result.receipt.status).to.equal(true);
})
essentially, create zombie1, create zombie2, fast forward one day, let zombie1 attack zombie2 (since there is a cooldown period between zombie creation and when it is allowed to attach) and finally assert that the smart contract was able to be executed.
The test fails with this blob of an unhelpful error message:
1) Contract: CryptoZombies
zombies should be able to attack another zombie:
Uncaught TypeError: callback is not a function
at /home/deepsports/.nvm/versions/node/v14.18.0/lib/node_modules/truffle/build/webpack:/packages/provider/wrapper.js:107:1
at XMLHttpRequest.request.onreadystatechange (/home/deepsports/.nvm/versions/node/v14.18.0/lib/node_modules/truffle/build/webpack:/node_modules/web3/node_modules/web3-providers-http/lib/index.js:98:1)
at XMLHttpRequestEventTarget.dispatchEvent (/home/deepsports/.nvm/versions/node/v14.18.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:1)
at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._setReadyState (/home/deepsports/.nvm/versions/node/v14.18.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:208:1)
at XMLHttpRequest.exports.modules.996763.XMLHttpRequest._onHttpResponseEnd (/home/deepsports/.nvm/versions/node/v14.18.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:318:1)
at IncomingMessage.<anonymous> (/home/deepsports/.nvm/versions/node/v14.18.0/lib/node_modules/truffle/build/webpack:/node_modules/xhr2-cookies/dist/xml-http-request.js:289:47)
at endReadableNT (internal/streams/readable.js:1334:12)
at processTicksAndRejections (internal/process/task_queues.js:82:21)
For background, I'm using:
The stacktrace is a bit hard to parse, since there's no lines in my actual code referenced. Through process of elimination, was able to confirm that it is this line of code that causes the failure:
await time.increase(time.duration.days(1));
which calls into this code (created as part of the tutorial):
async function increase(duration) {
//first, let's increase time
await web3.currentProvider.send({
jsonrpc: "2.0",
method: "evm_increaseTime",
params: [duration], // there are 86400 seconds in a day
id: new Date().getTime()
});
//next, let's mine a new block
web3.currentProvider.send({
jsonrpc: '2.0',
method: 'evm_mine',
params: [],
id: new Date().getTime()
})
}
It seems that CryptoZombies are emulating a deprecated version of web3
(I'm guessing also by their use of sendAsync
- which is in the CZ tutorial, not in your code), where this could have worked.
But, as documented in this GitHub issue, web3.currentProvider.send()
now expects a callback param, and isn't able to resolve using await
.
// no callback, fails
await web3.currentProvider.send({
jsonrpc: "2.0",
method: "evm_increaseTime",
params: [duration],
id: new Date().getTime()
});
Working solution:
async function increase(duration) {
return new Promise((resolve, reject) => {
web3.currentProvider.send({
jsonrpc: "2.0",
method: "evm_increaseTime",
params: [duration],
id: new Date().getTime()
}, (err, result) => {
// second call within the callback
web3.currentProvider.send({
jsonrpc: '2.0',
method: 'evm_mine',
params: [],
id: new Date().getTime()
}, (err, result) => {
// need to resolve the Promise in the second callback
resolve();
});
});
});
}
Note: Don't confuse this with other web3 send()
methods, such as the contract.methods.foo().send()
, that ARE able to resolve using await
.