253 lines
8.2 KiB
JavaScript
253 lines
8.2 KiB
JavaScript
const EC = require('elliptic').ec;
|
|
const ec = new EC('secp256k1');
|
|
const web3Utils = require('web3-utils');
|
|
|
|
// 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 ((error + "").indexOf("invalid opcode") < 0 && (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.eventAddress = (bytes32Address) => {
|
|
return web3Utils.toChecksumAddress("0x"+bytes32Address.substring(26))
|
|
}
|
|
|
|
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('revert') > -1, 'Revert should happen');
|
|
}
|
|
|
|
|
|
var callbackToResolve = function (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]);
|
|
});
|
|
}
|
|
|
|
|
|
// 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 ((error + "").indexOf("invalid opcode") < 0 && (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('revert') > -1, 'Revert should happen');
|
|
}
|
|
|
|
var callbackToResolve = function (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) {
|
|
let strError = error.toString();
|
|
return strError.includes('invalid opcode') || strError.includes('invalid JUMP') || strError.includes('revert');
|
|
}
|
|
|
|
exports.increaseTime = async (addSeconds) => {
|
|
const id = Date.now();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
web3.currentProvider.send({
|
|
jsonrpc: '2.0',
|
|
method: 'evm_increaseTime',
|
|
params: [addSeconds],
|
|
id,
|
|
}, (err1) => {
|
|
if (err1) return reject(err1);
|
|
|
|
web3.currentProvider.send({
|
|
jsonrpc: '2.0',
|
|
method: 'evm_mine',
|
|
id: id + 1,
|
|
}, (err2, res) => (err2 ? reject(err2) : resolve(res)));
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
exports.generateXY = pub => {
|
|
const stripped = pub.slice(2);
|
|
const key = ec.keyFromPublic(stripped, 'hex');
|
|
const pubPoint = key.getPublic();
|
|
const x = '0x' + pubPoint.getX().toString(16);
|
|
const y = '0x'+ pubPoint.getY().toString(16);
|
|
return { x, y };
|
|
}
|
|
|
|
exports.keyFromXY = (X, Y) => {
|
|
const x = Buffer.from(X.substring(2), 'hex');
|
|
const y = Buffer.from(Y.substring(2), 'hex');
|
|
const keys = ec.keyFromPublic({ x, y }, 'hex');
|
|
return `0x${keys.getPublic().encode('hex')}`;
|
|
}
|