UNPKG

web3-utils

Version:

Collection of utility functions used in web3.js.

321 lines (318 loc) 12.1 kB
"use strict"; /* This file is part of web3.js. web3.js is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. web3.js is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with web3.js. If not, see <http://www.gnu.org/licenses/>. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.getStorageSlotNumForLongString = exports.soliditySha3Raw = exports.soliditySha3 = exports.encodePacked = exports.processSolidityEncodePackedArgs = exports.keccak256 = exports.keccak256Wrapper = exports.sha3Raw = exports.sha3 = void 0; const keccak_js_1 = require("ethereum-cryptography/keccak.js"); const utils_js_1 = require("ethereum-cryptography/utils.js"); const web3_errors_1 = require("web3-errors"); const web3_validator_1 = require("web3-validator"); const converters_js_1 = require("./converters.js"); const string_manipulation_js_1 = require("./string_manipulation.js"); const SHA3_EMPTY_BYTES = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; /** * computes the Keccak-256 hash of the input and returns a hexstring * @param data - the input to hash * @returns - the Keccak-256 hash of the input * * @example * ```ts * console.log(web3.utils.sha3('web3.js')); * > 0x63667efb1961039c9bb0d6ea7a5abdd223a3aca7daa5044ad894226e1f83919a * * console.log(web3.utils.sha3('')); * > undefined * ``` */ const sha3 = (data) => { let updatedData; if (typeof data === 'string') { if (data.startsWith('0x') && (0, web3_validator_1.isHexStrict)(data)) { updatedData = (0, converters_js_1.hexToBytes)(data); } else { updatedData = (0, utils_js_1.utf8ToBytes)(data); } } else { updatedData = data; } const hash = (0, converters_js_1.bytesToHex)((0, keccak_js_1.keccak256)(updatedData)); // EIP-1052 if hash is equal to c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, keccak was given empty data return hash === SHA3_EMPTY_BYTES ? undefined : hash; }; exports.sha3 = sha3; /** * Will calculate the sha3 of the input but does return the hash value instead of null if for example a empty string is passed. * @param data - the input to hash * @returns - the Keccak-256 hash of the input * * @example * ```ts * conosle.log(web3.utils.sha3Raw('web3.js')); * > 0x63667efb1961039c9bb0d6ea7a5abdd223a3aca7daa5044ad894226e1f83919a * * console.log(web3.utils.sha3Raw('')); * > 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 * ``` */ const sha3Raw = (data) => { const hash = (0, exports.sha3)(data); if ((0, web3_validator_1.isNullish)(hash)) { return SHA3_EMPTY_BYTES; } return hash; }; exports.sha3Raw = sha3Raw; /** * A wrapper for ethereum-cryptography/keccak256 to allow hashing a `string` and a `bigint` in addition to `UInt8Array` * @param data - the input to hash * @returns - the Keccak-256 hash of the input * * @example * ```ts * console.log(web3.utils.keccak256Wrapper('web3.js')); * > 0x63667efb1961039c9bb0d6ea7a5abdd223a3aca7daa5044ad894226e1f83919a * * console.log(web3.utils.keccak256Wrapper(1)); * > 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6 * * console.log(web3.utils.keccak256Wrapper(0xaf12fd)); * > 0x358640fd4719fa923525d74ab5ae80a594301aba5543e3492b052bf4598b794c * ``` */ const keccak256Wrapper = (data) => { let processedData; if (typeof data === 'bigint' || typeof data === 'number') { processedData = (0, utils_js_1.utf8ToBytes)(data.toString()); } else if (Array.isArray(data)) { processedData = new Uint8Array(data); } else if (typeof data === 'string' && !(0, web3_validator_1.isHexStrict)(data)) { processedData = (0, utils_js_1.utf8ToBytes)(data); } else { processedData = (0, converters_js_1.bytesToUint8Array)(data); } return (0, converters_js_1.bytesToHex)((0, keccak_js_1.keccak256)(processedData)); }; exports.keccak256Wrapper = keccak256Wrapper; exports.keccak256 = exports.keccak256Wrapper; /** * returns type and value * @param arg - the input to return the type and value * @returns - the type and value of the input */ const getType = (arg) => { if (Array.isArray(arg)) { throw new Error('Autodetection of array types is not supported.'); } let type; let value; // if type is given if (typeof arg === 'object' && ('t' in arg || 'type' in arg) && ('v' in arg || 'value' in arg)) { type = 't' in arg ? arg.t : arg.type; value = 'v' in arg ? arg.v : arg.value; // otherwise try to guess the type } else { type = (0, converters_js_1.toHex)(arg, true); value = (0, converters_js_1.toHex)(arg); if (!type.startsWith('int') && !type.startsWith('uint')) { type = 'bytes'; } } if ((type.startsWith('int') || type.startsWith('uint')) && typeof value === 'string' && !/^(-)?0x/i.test(value)) { value = (0, converters_js_1.toBigInt)(value); } return [type, value]; }; /** * returns the type with size if uint or int * @param name - the input to return the type with size * @returns - the type with size of the input */ const elementaryName = (name) => { if (name.startsWith('int[')) { return `int256${name.slice(3)}`; } if (name === 'int') { return 'int256'; } if (name.startsWith('uint[')) { return `uint256'${name.slice(4)}`; } if (name === 'uint') { return 'uint256'; } return name; }; /** * returns the size of the value of type 'byte' */ const parseTypeN = (value, typeLength) => { const typesize = /^(\d+).*$/.exec(value.slice(typeLength)); return typesize ? parseInt(typesize[1], 10) : 0; }; /** * returns the bit length of the value * @param value - the input to return the bit length * @returns - the bit length of the input */ const bitLength = (value) => { const updatedVal = value.toString(2); return updatedVal.length; }; /** * Pads the value based on size and type * returns a string of the padded value * @param type - the input to pad * @returns = the padded value */ const solidityPack = (type, val) => { const value = val.toString(); if (type === 'string') { if (typeof val === 'string') return (0, converters_js_1.utf8ToHex)(val); throw new web3_errors_1.InvalidStringError(val); } if (type === 'bool' || type === 'boolean') { if (typeof val === 'boolean') return val ? '01' : '00'; throw new web3_errors_1.InvalidBooleanError(val); } if (type === 'address') { if (!(0, web3_validator_1.isAddress)(value)) { throw new web3_errors_1.InvalidAddressError(value); } return value; } const name = elementaryName(type); if (type.startsWith('uint')) { const size = parseTypeN(name, 'uint'.length); if (size % 8 || size < 8 || size > 256) { throw new web3_errors_1.InvalidSizeError(value); } const num = (0, converters_js_1.toNumber)(value); if (bitLength(num) > size) { throw new web3_errors_1.InvalidLargeValueError(value); } if (num < BigInt(0)) { throw new web3_errors_1.InvalidUnsignedIntegerError(value); } return size ? (0, string_manipulation_js_1.leftPad)(num.toString(16), (size / 8) * 2) : num.toString(16); } if (type.startsWith('int')) { const size = parseTypeN(name, 'int'.length); if (size % 8 || size < 8 || size > 256) { throw new web3_errors_1.InvalidSizeError(type); } const num = (0, converters_js_1.toNumber)(value); if (bitLength(num) > size) { throw new web3_errors_1.InvalidLargeValueError(value); } if (num < BigInt(0)) { return (0, string_manipulation_js_1.toTwosComplement)(num.toString(), (size / 8) * 2); } return size ? (0, string_manipulation_js_1.leftPad)(num.toString(16), size / 4) : num.toString(16); } if (name === 'bytes') { if (value.replace(/^0x/i, '').length % 2 !== 0) { throw new web3_errors_1.InvalidBytesError(value); } return value; } if (type.startsWith('bytes')) { if (value.replace(/^0x/i, '').length % 2 !== 0) { throw new web3_errors_1.InvalidBytesError(value); } const size = parseTypeN(type, 'bytes'.length); if (!size || size < 1 || size > 64 || size < value.replace(/^0x/i, '').length / 2) { throw new web3_errors_1.InvalidBytesError(value); } return (0, string_manipulation_js_1.rightPad)(value, size * 2); } return ''; }; /** * returns a string of the tightly packed value given based on the type * @param arg - the input to return the tightly packed value * @returns - the tightly packed value */ const processSolidityEncodePackedArgs = (arg) => { const [type, val] = getType(arg); // array case if (Array.isArray(val)) { // go through each element of the array and use map function to create new hexarg list const hexArg = val.map((v) => solidityPack(type, v).replace('0x', '')); return hexArg.join(''); } const hexArg = solidityPack(type, val); return hexArg.replace('0x', ''); }; exports.processSolidityEncodePackedArgs = processSolidityEncodePackedArgs; /** * Encode packed arguments to a hexstring */ const encodePacked = (...values) => { const args = Array.prototype.slice.call(values); const hexArgs = args.map(exports.processSolidityEncodePackedArgs); return `0x${hexArgs.join('').toLowerCase()}`; }; exports.encodePacked = encodePacked; /** * Will tightly pack values given in the same way solidity would then hash. * returns a hash string, or null if input is empty * @param values - the input to return the tightly packed values * @returns - the keccack246 of the tightly packed values * * @example * ```ts * console.log([{ type: 'string', value: '31323334' }]); * console.log(web3.utils.soliditySha3({ type: "string", value: "31323334" })); * > 0xf15f8da2ad27e486d632dc37d24912f634398918d6f9913a0a0ff84e388be62b * ``` */ const soliditySha3 = (...values) => (0, exports.sha3)((0, exports.encodePacked)(...values)); exports.soliditySha3 = soliditySha3; /** * Will tightly pack values given in the same way solidity would then hash. * returns a hash string, if input is empty will return `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` * @param values - the input to return the tightly packed values * @returns - the keccack246 of the tightly packed values * * @example * ```ts * console.log(web3.utils.soliditySha3Raw({ type: "string", value: "helloworld" })) * > 0xfa26db7ca85ead399216e7c6316bc50ed24393c3122b582735e7f3b0f91b93f0 * ``` */ const soliditySha3Raw = (...values) => (0, exports.sha3Raw)((0, exports.encodePacked)(...values)); exports.soliditySha3Raw = soliditySha3Raw; /** * Get slot number for storage long string in contract. Basically for getStorage method * returns slotNumber where will data placed * @param mainSlotNumber - the slot number where will be stored hash of long string * @returns - the slot number where will be stored long string */ const getStorageSlotNumForLongString = (mainSlotNumber) => (0, exports.sha3)(`0x${(typeof mainSlotNumber === 'number' ? mainSlotNumber.toString() : mainSlotNumber).padStart(64, '0')}`); exports.getStorageSlotNumForLongString = getStorageSlotNumForLongString; //# sourceMappingURL=hash.js.map