UNPKG

@okxweb3/coin-base

Version:
634 lines 19.9 kB
"use strict"; const utils = require('./util'); const base = require('../base'); var BN = require('bn.js/lib/bn'); const { isHexString, stripHexPrefix, padToEven } = require('./internal'); const { intToBuffer, addHexPrefix, bufferToHex } = require('./bytes'); var ABI = function () { }; function elementaryName(name) { if (name.startsWith('int[')) { return 'int256' + name.slice(3); } else if (name === 'int') { return 'int256'; } else if (name.startsWith('uint[')) { return 'uint256' + name.slice(4); } else if (name === 'uint') { return 'uint256'; } else if (name.startsWith('fixed[')) { return 'fixed128x128' + name.slice(5); } else if (name === 'fixed') { return 'fixed128x128'; } else if (name.startsWith('ufixed[')) { return 'ufixed128x128' + name.slice(6); } else if (name === 'ufixed') { return 'ufixed128x128'; } return name; } function toBuffer(v) { if (v === null || v === undefined) { return Buffer.allocUnsafe(0); } if (Buffer.isBuffer(v)) { return Buffer.from(v); } if (Array.isArray(v) || v instanceof Uint8Array) { return Buffer.from(v); } if (typeof v === 'string') { if (!isHexString(v)) { throw new Error(`Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}`); } return Buffer.from(padToEven(stripHexPrefix(v)), 'hex'); } if (typeof v === 'number') { return intToBuffer(v); } if (typeof v === 'bigint') { if (v < BigInt(0)) { throw new Error(`Cannot convert negative bigint to buffer. Given: ${v}`); } let n = v.toString(16); if (n.length % 2) n = '0' + n; return Buffer.from(n, 'hex'); } if (v.toArray) { return Buffer.from(v.toArray()); } if (v.toBuffer) { return Buffer.from(v.toBuffer()); } throw new Error('invalid type'); } function normalize(input) { if (!input) { return undefined; } if (typeof input === 'number') { if (input < 0) { return '0x'; } const buffer = toBuffer(input); input = bufferToHex(buffer); } if (typeof input !== 'string') { let msg = 'eth-sig-util.normalize() requires hex string or integer input.'; msg += ` received ${typeof input}: ${input}`; throw new Error(msg); } return addHexPrefix(input.toLowerCase()); } ABI.eventID = function (name, types) { var sig = name + '(' + types.map(elementaryName).join(',') + ')'; return base.keccak256(Buffer.from(sig)); }; ABI.methodID = function (name, types) { return ABI.eventID(name, types).slice(0, 4); }; function parseTypeN(type) { return parseInt(/^\D+(\d+)$/.exec(type)[1], 10); } function parseTypeNxM(type) { var tmp = /^\D+(\d+)x(\d+)$/.exec(type); return [parseInt(tmp[1], 10), parseInt(tmp[2], 10)]; } function parseTypeArray(type) { var tmp = type.match(/(.*)\[(.*?)\]$/); if (tmp) { return tmp[2] === '' ? 'dynamic' : parseInt(tmp[2], 10); } return null; } function parseNumber(arg) { var type = typeof arg; if (type === 'string') { if (base.isHexPrefixed(arg)) { return new BN(base.stripHexPrefix(arg), 16); } else { if (arg.startsWith('0b') || arg.startsWith('0B')) { return new BN(arg.slice(2), 2); } else if (arg.startsWith('0o') || arg.startsWith('0O')) { return new BN(arg.slice(2), 8); } return new BN(arg, 10); } } else if (type === 'number') { return new BN(arg); } else if (arg.toArray) { return arg; } else { throw new Error('Argument is not a number'); } } function parseSignature(sig) { var tmp = /^(\w+)\((.*)\)$/.exec(sig); if (tmp.length !== 3) { throw new Error('Invalid method signature'); } var args = /^(.+)\):\((.+)$/.exec(tmp[2]); if (args !== null && args.length === 3) { return { method: tmp[1], args: args[1].split(','), retargs: args[2].split(','), }; } else { var params = tmp[2].split(','); if (params.length === 1 && params[0] === '') { params = []; } return { method: tmp[1], args: params, }; } } function encodeSingle(type, arg) { var size, num, ret, i; if (type === 'address') { return encodeSingle('uint160', parseNumber(arg)); } else if (type === 'bool') { return encodeSingle('uint8', arg ? 1 : 0); } else if (type === 'string') { return encodeSingle('bytes', Buffer.from(arg, 'utf8')); } else if (isArray(type)) { if (typeof arg.length === 'undefined') { throw new Error('Not an array?'); } size = parseTypeArray(type); if (size !== 'dynamic' && size !== 0 && arg.length > size) { throw new Error('Elements exceed array size: ' + size); } ret = []; type = type.slice(0, type.lastIndexOf('[')); if (typeof arg === 'string') { arg = JSON.parse(arg); } for (i in arg) { ret.push(encodeSingle(type, arg[i])); } if (size === 'dynamic') { var length = encodeSingle('uint256', arg.length); ret.unshift(length); } return Buffer.concat(ret); } else if (type === 'bytes') { arg = Buffer.from(arg); ret = Buffer.concat([encodeSingle('uint256', arg.length), arg]); if (arg.length % 32 !== 0) { ret = Buffer.concat([ret, utils.zeros(32 - (arg.length % 32))]); } return ret; } else if (type.startsWith('bytes')) { size = parseTypeN(type); if (size < 1 || size > 32) { throw new Error('Invalid bytes<N> width: ' + size); } return utils.setLengthRight(arg, 32); } else if (type.startsWith('uint')) { size = parseTypeN(type); if (size % 8 || size < 8 || size > 256) { throw new Error('Invalid uint<N> width: ' + size); } num = parseNumber(arg); if (num.bitLength() > size) { throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength()); } if (num < 0) { throw new Error('Supplied uint is negative'); } return num.toArrayLike(Buffer, 'be', 32); } else if (type.startsWith('int')) { size = parseTypeN(type); if (size % 8 || size < 8 || size > 256) { throw new Error('Invalid int<N> width: ' + size); } num = parseNumber(arg); if (num.bitLength() > size) { throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength()); } return num.toTwos(256).toArrayLike(Buffer, 'be', 32); } else if (type.startsWith('ufixed')) { size = parseTypeNxM(type); num = parseNumber(arg); if (num < 0) { throw new Error('Supplied ufixed is negative'); } return encodeSingle('uint256', num.mul(new BN(2).pow(new BN(size[1])))); } else if (type.startsWith('fixed')) { size = parseTypeNxM(type); return encodeSingle('int256', parseNumber(arg).mul(new BN(2).pow(new BN(size[1])))); } throw new Error('Unsupported or invalid type: ' + type); } function decodeSingle(parsedType, data, offset) { if (typeof parsedType === 'string') { parsedType = parseType(parsedType); } var size, num, ret, i; if (parsedType.name === 'address') { return decodeSingle(parsedType.rawType, data, offset) .toArrayLike(Buffer, 'be', 20) .toString('hex'); } else if (parsedType.name === 'bool') { return (decodeSingle(parsedType.rawType, data, offset).toString() === new BN(1).toString()); } else if (parsedType.name === 'string') { var bytes = decodeSingle(parsedType.rawType, data, offset); return Buffer.from(bytes, 'utf8').toString(); } else if (parsedType.isArray) { ret = []; size = parsedType.size; if (parsedType.size === 'dynamic') { offset = decodeSingle('uint256', data, offset).toNumber(); size = decodeSingle('uint256', data, offset).toNumber(); offset = offset + 32; } for (i = 0; i < size; i++) { var decoded = decodeSingle(parsedType.subArray, data, offset); ret.push(decoded); offset += parsedType.subArray.memoryUsage; } return ret; } else if (parsedType.name === 'bytes') { offset = decodeSingle('uint256', data, offset).toNumber(); size = decodeSingle('uint256', data, offset).toNumber(); return data.slice(offset + 32, offset + 32 + size); } else if (parsedType.name.startsWith('bytes')) { return data.slice(offset, offset + parsedType.size); } else if (parsedType.name.startsWith('uint')) { num = new BN(data.slice(offset, offset + 32), 16, 'be'); if (num.bitLength() > parsedType.size) { throw new Error('Decoded int exceeds width: ' + parsedType.size + ' vs ' + num.bitLength()); } return num; } else if (parsedType.name.startsWith('int')) { num = new BN(data.slice(offset, offset + 32), 16, 'be').fromTwos(256); if (num.bitLength() > parsedType.size) { throw new Error('Decoded uint exceeds width: ' + parsedType.size + ' vs ' + num.bitLength()); } return num; } else if (parsedType.name.startsWith('ufixed')) { size = new BN(2).pow(new BN(parsedType.size[1])); num = decodeSingle('uint256', data, offset); if (!num.mod(size).isZero()) { throw new Error('Decimals not supported yet'); } return num.div(size); } else if (parsedType.name.startsWith('fixed')) { size = new BN(2).pow(new BN(parsedType.size[1])); num = decodeSingle('int256', data, offset); if (!num.mod(size).isZero()) { throw new Error('Decimals not supported yet'); } return num.div(size); } throw new Error('Unsupported or invalid type: ' + parsedType.name); } function parseType(type) { var size; var ret; if (isArray(type)) { size = parseTypeArray(type); var subArray = type.slice(0, type.lastIndexOf('[')); subArray = parseType(subArray); ret = { isArray: true, name: type, size: size, memoryUsage: size === 'dynamic' ? 32 : subArray.memoryUsage * size, subArray: subArray, }; return ret; } else { var rawType; switch (type) { case 'address': rawType = 'uint160'; break; case 'bool': rawType = 'uint8'; break; case 'string': rawType = 'bytes'; break; } ret = { rawType: rawType, name: type, memoryUsage: 32, }; if ((type.startsWith('bytes') && type !== 'bytes') || type.startsWith('uint') || type.startsWith('int')) { ret.size = parseTypeN(type); } else if (type.startsWith('ufixed') || type.startsWith('fixed')) { ret.size = parseTypeNxM(type); } if (type.startsWith('bytes') && type !== 'bytes' && (ret.size < 1 || ret.size > 32)) { throw new Error('Invalid bytes<N> width: ' + ret.size); } if ((type.startsWith('uint') || type.startsWith('int')) && (ret.size % 8 || ret.size < 8 || ret.size > 256)) { throw new Error('Invalid int/uint<N> width: ' + ret.size); } return ret; } } function isDynamic(type) { return (type === 'string' || type === 'bytes' || parseTypeArray(type) === 'dynamic'); } function isArray(type) { return type.lastIndexOf(']') === type.length - 1; } ABI.rawEncode = function (types, values) { var output = []; var data = []; var headLength = 0; types.forEach(function (type) { if (isArray(type)) { var size = parseTypeArray(type); if (size !== 'dynamic') { headLength += 32 * size; } else { headLength += 32; } } else { headLength += 32; } }); for (var i = 0; i < types.length; i++) { var type = elementaryName(types[i]); var value = values[i]; var cur = encodeSingle(type, value); if (isDynamic(type)) { output.push(encodeSingle('uint256', headLength)); data.push(cur); headLength += cur.length; } else { output.push(cur); } } return Buffer.concat(output.concat(data)); }; ABI.rawDecode = function (types, data) { var ret = []; data = Buffer.from(data); var offset = 0; for (var i = 0; i < types.length; i++) { var type = elementaryName(types[i]); var parsed = parseType(type); var decoded = decodeSingle(parsed, data, offset); offset += parsed.memoryUsage; ret.push(decoded); } return ret; }; ABI.simpleEncode = function (method) { var args = Array.prototype.slice.call(arguments).slice(1); var sig = parseSignature(method); if (args.length !== sig.args.length) { throw new Error('Argument count mismatch'); } return Buffer.concat([ ABI.methodID(sig.method, sig.args), ABI.rawEncode(sig.args, args), ]); }; ABI.simpleDecode = function (method, data) { var sig = parseSignature(method); if (!sig.retargs) { throw new Error('No return values in method'); } return ABI.rawDecode(sig.retargs, data); }; function stringify(type, value) { if (type.startsWith('address') || type.startsWith('bytes')) { return '0x' + value.toString('hex'); } else { return value.toString(); } } ABI.stringify = function (types, values) { var ret = []; for (var i in types) { var type = types[i]; var value = values[i]; if (/^[^\[]+\[.*\]$/.test(type)) { value = value .map(function (item) { return stringify(type, item); }) .join(', '); } else { value = stringify(type, value); } ret.push(value); } return ret; }; ABI.solidityHexValue = function (type, value, bitsize) { var size, num; if (isArray(type)) { var subType = type.replace(/\[.*?\]/, ''); if (!isArray(subType)) { var arraySize = parseTypeArray(type); if (arraySize !== 'dynamic' && arraySize !== 0 && value.length > arraySize) { throw new Error('Elements exceed array size: ' + arraySize); } } var arrayValues = value.map(function (v) { return ABI.solidityHexValue(subType, v, 256); }); return Buffer.concat(arrayValues); } else if (type === 'bytes') { return value; } else if (type === 'string') { return Buffer.from(value, 'utf8'); } else if (type === 'bool') { bitsize = bitsize || 8; var padding = Array(bitsize / 4).join('0'); return Buffer.from(value ? padding + '1' : padding + '0', 'hex'); } else if (type === 'address') { var bytesize = 20; if (bitsize) { bytesize = bitsize / 8; } return utils.setLengthLeft(toBuffer(value), bytesize); } else if (type.startsWith('bytes')) { size = parseTypeN(type); if (size < 1 || size > 32) { throw new Error('Invalid bytes<N> width: ' + size); } if (typeof value === 'number') { value = normalize(value); } return utils.setLengthRight(toBuffer(value), size); } else if (type.startsWith('uint')) { size = parseTypeN(type); if (size % 8 || size < 8 || size > 256) { throw new Error('Invalid uint<N> width: ' + size); } num = parseNumber(value); if (num.bitLength() > size) { throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength()); } bitsize = bitsize || size; return num.toArrayLike(Buffer, 'be', bitsize / 8); } else if (type.startsWith('int')) { size = parseTypeN(type); if (size % 8 || size < 8 || size > 256) { throw new Error('Invalid int<N> width: ' + size); } num = parseNumber(value); if (num.bitLength() > size) { throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength()); } bitsize = bitsize || size; return num.toTwos(size).toArrayLike(Buffer, 'be', bitsize / 8); } else { throw new Error('Unsupported or invalid type: ' + type); } }; ABI.solidityPack = function (types, values) { if (types.length !== values.length) { throw new Error('Number of types are not matching the values'); } var ret = []; for (var i = 0; i < types.length; i++) { var type = elementaryName(types[i]); var value = values[i]; ret.push(ABI.solidityHexValue(type, value, null)); } return Buffer.concat(ret); }; ABI.soliditySHA3 = function (types, values) { return base.keccak256(ABI.solidityPack(types, values)); }; ABI.soliditySHA256 = function (types, values) { return base.sha256(ABI.solidityPack(types, values)); }; ABI.solidityRIPEMD160 = function (types, values) { return base.ripemd160(ABI.solidityPack(types, values), true); }; function isNumeric(c) { return c >= '0' && c <= '9'; } ABI.fromSerpent = function (sig) { var ret = []; for (var i = 0; i < sig.length; i++) { var type = sig[i]; if (type === 's') { ret.push('bytes'); } else if (type === 'b') { var tmp = 'bytes'; var j = i + 1; while (j < sig.length && isNumeric(sig[j])) { tmp += sig[j] - '0'; j++; } i = j - 1; ret.push(tmp); } else if (type === 'i') { ret.push('int256'); } else if (type === 'a') { ret.push('int256[]'); } else { throw new Error('Unsupported or invalid type: ' + type); } } return ret; }; ABI.toSerpent = function (types) { var ret = []; for (var i = 0; i < types.length; i++) { var type = types[i]; if (type === 'bytes') { ret.push('s'); } else if (type.startsWith('bytes')) { ret.push('b' + parseTypeN(type)); } else if (type === 'int256') { ret.push('i'); } else if (type === 'int256[]') { ret.push('a'); } else { throw new Error('Unsupported or invalid type: ' + type); } } return ret.join(''); }; module.exports = ABI; //# sourceMappingURL=abi.js.map