@okxweb3/coin-base
Version:
A base package for @ok/coin-*
634 lines • 19.9 kB
JavaScript
"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