sign.js 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. const utils = require('ethereumjs-util');
  2. const { soliditySha3 } = require('web3-utils');
  3. const REAL_SIGNATURE_SIZE = 2 * 65; // 65 bytes in hexadecimal string legnth
  4. const PADDED_SIGNATURE_SIZE = 2 * 96; // 96 bytes in hexadecimal string length
  5. const DUMMY_SIGNATURE = `0x${web3.padLeft('', REAL_SIGNATURE_SIZE)}`;
  6. /**
  7. * Hash and add same prefix to the hash that ganache use.
  8. * @param {string} message the plaintext/ascii/original message
  9. * @return {string} the hash of the message, prefixed, and then hashed again
  10. */
  11. function hashMessage (message) {
  12. const messageHex = Buffer.from(utils.sha3(message).toString('hex'), 'hex');
  13. const prefix = utils.toBuffer('\u0019Ethereum Signed Message:\n' + messageHex.length.toString());
  14. return utils.bufferToHex(utils.sha3(Buffer.concat([prefix, messageHex])));
  15. }
  16. // signs message in node (auto-applies prefix)
  17. // message must be in hex already! will not be autoconverted!
  18. const signMessage = (signer, message = '') => {
  19. return web3.eth.sign(signer, message);
  20. };
  21. // @TODO - remove this when we migrate to web3-1.0.0
  22. const transformToFullName = function (json) {
  23. if (json.name.indexOf('(') !== -1) {
  24. return json.name;
  25. }
  26. const typeName = json.inputs.map(function (i) { return i.type; }).join();
  27. return json.name + '(' + typeName + ')';
  28. };
  29. /**
  30. * Create a signer between a contract and a signer for a voucher of method, args, and redeemer
  31. * Note that `method` is the web3 method, not the truffle-contract method
  32. * Well truffle is terrible, but luckily (?) so is web3 < 1.0, so we get to make our own method id
  33. * fetcher because the method on the contract isn't actually the SolidityFunction object ಠ_ಠ
  34. * @param contract TruffleContract
  35. * @param signer address
  36. * @param redeemer address
  37. * @param methodName string
  38. * @param methodArgs any[]
  39. */
  40. const getBouncerSigner = (contract, signer) => (redeemer, methodName, methodArgs = []) => {
  41. const parts = [
  42. contract.address,
  43. redeemer,
  44. ];
  45. // if we have a method, add it to the parts that we're signing
  46. if (methodName) {
  47. if (methodArgs.length > 0) {
  48. parts.push(
  49. contract.contract[methodName].getData(...methodArgs.concat([DUMMY_SIGNATURE])).slice(
  50. 0,
  51. -1 * PADDED_SIGNATURE_SIZE
  52. )
  53. );
  54. } else {
  55. const abi = contract.abi.find(abi => abi.name === methodName);
  56. const name = transformToFullName(abi);
  57. const signature = web3.sha3(name).slice(0, 10);
  58. parts.push(signature);
  59. }
  60. }
  61. // ^ substr to remove `0x` because in solidity the address is a set of byes, not a string `0xabcd`
  62. const hashOfMessage = soliditySha3(...parts);
  63. return signMessage(signer, hashOfMessage);
  64. };
  65. module.exports = {
  66. hashMessage,
  67. signMessage,
  68. getBouncerSigner,
  69. };