/*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 }