183 lines
4.9 KiB
JavaScript
183 lines
4.9 KiB
JavaScript
/*global assert, web3 */
|
|
const bs58 = require('bs58')
|
|
|
|
// This has been tested with the real Ethereum network and Testrpc.
|
|
// Copied and edited from: https://gist.github.com/xavierlepretre/d5583222fde52ddfbc58b7cfa0d2d0a9
|
|
|
|
exports.assertReverts = (contractMethodCall, maxGasAvailable) => {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
resolve(contractMethodCall())
|
|
} catch (error) {
|
|
reject(error)
|
|
}
|
|
})
|
|
.then(tx => {
|
|
assert.equal(
|
|
tx.receipt.gasUsed,
|
|
maxGasAvailable,
|
|
'tx successful, the max gas available was not consumed',
|
|
)
|
|
})
|
|
.catch(error => {
|
|
if (
|
|
String(error).indexOf('invalid opcode') < 0 &&
|
|
String(error).indexOf('out of gas') < 0
|
|
) {
|
|
// Checks if the error is from TestRpc. If it is then ignore it.
|
|
// Otherwise relay/throw the error produced by the above assertion.
|
|
// Note that no error is thrown when using a real Ethereum network AND the assertion above is true.
|
|
throw error
|
|
}
|
|
})
|
|
}
|
|
|
|
exports.listenForEvent = event =>
|
|
new Promise((resolve, reject) => {
|
|
event({}, (error, response) => {
|
|
if (!error) {
|
|
resolve(response.args)
|
|
} else {
|
|
reject(error)
|
|
}
|
|
event.stopWatching()
|
|
})
|
|
})
|
|
|
|
exports.eventValues = (receipt, eventName) => {
|
|
if (receipt.events[eventName]) return receipt.events[eventName].returnValues
|
|
}
|
|
|
|
exports.addressToBytes32 = address => {
|
|
const stringed =
|
|
'0000000000000000000000000000000000000000000000000000000000000000' +
|
|
address.slice(2)
|
|
return `0x${ stringed.substring(stringed.length - 64, stringed.length)}`;
|
|
}
|
|
|
|
// OpenZeppelin's expectThrow helper -
|
|
// Source: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers/expectThrow.js
|
|
exports.expectThrow = async promise => {
|
|
try {
|
|
await promise
|
|
} catch (error) {
|
|
// TODO: Check jump destination to destinguish between a throw
|
|
// and an actual invalid jump.
|
|
const invalidOpcode = error.message.search('invalid opcode') >= 0
|
|
// TODO: When we contract A calls contract B, and B throws, instead
|
|
// of an 'invalid jump', we get an 'out of gas' error. How do
|
|
// we distinguish this from an actual out of gas event? (The
|
|
// testrpc log actually show an 'invalid jump' event.)
|
|
const outOfGas = error.message.search('out of gas') >= 0
|
|
const revert = error.message.search('revert') >= 0
|
|
assert(
|
|
invalidOpcode || outOfGas || revert,
|
|
`Expected throw, got '${ error }' instead`,
|
|
)
|
|
return
|
|
}
|
|
assert.fail('Expected throw not received')
|
|
}
|
|
|
|
exports.assertJump = error => {
|
|
assert(
|
|
error.message.search('VM Exception while processing transaction: revert') >
|
|
-1,
|
|
'Revert should happen',
|
|
)
|
|
}
|
|
|
|
function callbackToResolve(resolve, reject) {
|
|
return function(error, value) {
|
|
if (error) {
|
|
reject(error)
|
|
} else {
|
|
resolve(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.promisify = func => (...args) => {
|
|
return new Promise((resolve, reject) => {
|
|
const callback = (err, data) => (err ? reject(err) : resolve(data))
|
|
func.apply(this, [...args, callback])
|
|
})
|
|
}
|
|
|
|
exports.zeroAddress = '0x0000000000000000000000000000000000000000'
|
|
exports.zeroBytes32 =
|
|
'0x0000000000000000000000000000000000000000000000000000000000000000'
|
|
exports.timeUnits = {
|
|
seconds: 1,
|
|
minutes: 60,
|
|
hours: 60 * 60,
|
|
days: 24 * 60 * 60,
|
|
weeks: 7 * 24 * 60 * 60,
|
|
years: 365 * 24 * 60 * 60,
|
|
}
|
|
|
|
exports.ensureException = function(error) {
|
|
assert(isException(error), error.toString())
|
|
}
|
|
|
|
function isException(error) {
|
|
const strError = error.toString()
|
|
return (
|
|
strError.includes('invalid opcode') ||
|
|
strError.includes('invalid JUMP') ||
|
|
strError.includes('revert')
|
|
)
|
|
}
|
|
|
|
const evmMethod = (method, params = []) => {
|
|
return new Promise(function(resolve, reject) {
|
|
const sendMethod = web3.currentProvider.sendAsync
|
|
? web3.currentProvider.sendAsync.bind(web3.currentProvider)
|
|
: web3.currentProvider.send.bind(web3.currentProvider)
|
|
sendMethod(
|
|
{
|
|
jsonrpc: '2.0',
|
|
method,
|
|
params,
|
|
id: new Date().getSeconds(),
|
|
},
|
|
(error, res) => {
|
|
if (error) {
|
|
return reject(error)
|
|
}
|
|
resolve(res.result)
|
|
},
|
|
)
|
|
})
|
|
}
|
|
|
|
exports.evmSnapshot = async () => {
|
|
const result = await evmMethod('evm_snapshot')
|
|
return web3.utils.hexToNumber(result)
|
|
}
|
|
|
|
exports.evmRevert = id => {
|
|
const params = [id]
|
|
return evmMethod('evm_revert', params)
|
|
}
|
|
|
|
exports.increaseTime = async amount => {
|
|
await evmMethod('evm_increaseTime', [Number(amount)])
|
|
await evmMethod('evm_mine')
|
|
}
|
|
|
|
exports.getBytes32FromIpfsHash = ipfsListing => {
|
|
const decodedHash = bs58
|
|
.decode(ipfsListing)
|
|
.slice(2)
|
|
.toString('hex')
|
|
return `0x${decodedHash}`
|
|
}
|
|
|
|
exports.getIpfsHashFromBytes32 = bytes32Hex => {
|
|
const hashHex = `1220${bytes32Hex.slice(2)}`
|
|
const hashBytes = Buffer.from(hashHex, 'hex')
|
|
const hashStr = bs58.encode(hashBytes)
|
|
return hashStr
|
|
}
|