@findeth/abi
Advanced tools
Comparing version 0.5.2 to 0.6.0
@@ -6,4 +6,3 @@ "use strict"; | ||
}); | ||
exports.iterate = iterate; | ||
exports.unpack = exports.pack = exports.getParser = exports.decodeArray = exports.encodeArray = exports.getType = exports.isArray = void 0; | ||
exports.unpack = exports.pack = exports.getParser = exports.decodeArray = exports.encodeArray = exports.getType = exports.isTuple = exports.isArray = void 0; | ||
@@ -25,2 +24,3 @@ var _utils = require("../utils"); | ||
const ARRAY_REGEX = /^(.*)\[]$/; | ||
const TUPLE_REGEX = /^\((.*)\)$/; | ||
@@ -33,4 +33,14 @@ const isArray = type => { | ||
const isTuple = type => { | ||
return TUPLE_REGEX.test(type); | ||
}; | ||
exports.isTuple = isTuple; | ||
const getType = type => { | ||
return type.match(ARRAY_REGEX)[1]; | ||
if (!isArray(type) && isTuple(type)) { | ||
return type.slice(1, -1).split(',').map(type => type.trim()); | ||
} | ||
return [type.match(ARRAY_REGEX)[1]]; | ||
}; | ||
@@ -41,3 +51,3 @@ | ||
const encodeArray = (buffer, values, type) => { | ||
if (!isArray(type)) { | ||
if (!isArray(type) && !isTuple(type)) { | ||
throw new Error('Invalid type: type is not array'); | ||
@@ -47,5 +57,10 @@ } | ||
const actualType = getType(type); | ||
if (isTuple(type)) { | ||
return pack(buffer, values, actualType); | ||
} | ||
const length = (0, _utils.toBuffer)(values.length); | ||
const arrayBuffer = (0, _utils.concat)([buffer, length]); | ||
return pack(arrayBuffer, values, new Array(values.length).fill(actualType)); | ||
return pack(arrayBuffer, values, new Array(values.length).fill(actualType[0])); | ||
}; | ||
@@ -56,3 +71,3 @@ | ||
const decodeArray = (value, buffer, type) => { | ||
if (!isArray(type)) { | ||
if (!isArray(type) && !isTuple(type)) { | ||
throw new Error('Invalid type: type is not array'); | ||
@@ -63,6 +78,11 @@ } | ||
const pointer = Number((0, _utils.toNumber)(value)); | ||
if (isTuple(type)) { | ||
return unpack(value, actualType); | ||
} | ||
const length = Number((0, _utils.toNumber)(buffer.subarray(pointer, pointer + 32))); | ||
const arrayPointer = pointer + 32; | ||
const arrayBuffer = buffer.subarray(arrayPointer); | ||
return unpack(arrayBuffer, new Array(length).fill(actualType)); | ||
return unpack(arrayBuffer, new Array(length).fill(actualType[0])); | ||
}; | ||
@@ -82,2 +102,7 @@ | ||
}, | ||
tuple: { | ||
dynamic: false, | ||
encode: encodeArray, | ||
decode: decodeArray | ||
}, | ||
bool: { | ||
@@ -127,2 +152,6 @@ dynamic: false, | ||
if (isTuple(type)) { | ||
return parsers.tuple; | ||
} | ||
throw new Error(`type "${type}" is not supported`); | ||
@@ -180,19 +209,6 @@ }; | ||
function* iterate(buffer, chunkSize) { | ||
for (let i = 0; i < buffer.length; i += chunkSize) { | ||
yield buffer.slice(i, i + chunkSize); | ||
} | ||
return buffer; | ||
} | ||
const unpack = (buffer, types) => { | ||
const iterator = iterate(buffer, 32); | ||
let pointer = 0; | ||
return types.map(type => { | ||
const { | ||
value, | ||
done | ||
} = iterator.next(); | ||
if (done) { | ||
if (pointer >= buffer.length) { | ||
throw new Error('input data has an invalid length'); | ||
@@ -202,2 +218,13 @@ } | ||
const parser = getParser(type); | ||
if (isTuple(type)) { | ||
const types = getType(type); | ||
const tupleLength = types.length * 32; | ||
const value = buffer.subarray(pointer, pointer + tupleLength); | ||
pointer += tupleLength; | ||
return parser.decode(value, buffer, type); | ||
} | ||
const value = buffer.subarray(pointer, pointer + 32); | ||
pointer += 32; | ||
return parser.decode(value, buffer, type); | ||
@@ -204,0 +231,0 @@ }); |
@@ -9,10 +9,18 @@ import { concat, toBuffer, toNumber } from '../utils'; | ||
const ARRAY_REGEX = /^(.*)\[]$/; | ||
const TUPLE_REGEX = /^\((.*)\)$/; | ||
export const isArray = type => { | ||
return ARRAY_REGEX.test(type); | ||
}; | ||
export const isTuple = type => { | ||
return TUPLE_REGEX.test(type); | ||
}; | ||
export const getType = type => { | ||
return type.match(ARRAY_REGEX)[1]; | ||
if (!isArray(type) && isTuple(type)) { | ||
return type.slice(1, -1).split(',').map(type => type.trim()); | ||
} | ||
return [type.match(ARRAY_REGEX)[1]]; | ||
}; | ||
export const encodeArray = (buffer, values, type) => { | ||
if (!isArray(type)) { | ||
if (!isArray(type) && !isTuple(type)) { | ||
throw new Error('Invalid type: type is not array'); | ||
@@ -22,8 +30,13 @@ } | ||
const actualType = getType(type); | ||
if (isTuple(type)) { | ||
return pack(buffer, values, actualType); | ||
} | ||
const length = toBuffer(values.length); | ||
const arrayBuffer = concat([buffer, length]); | ||
return pack(arrayBuffer, values, new Array(values.length).fill(actualType)); | ||
return pack(arrayBuffer, values, new Array(values.length).fill(actualType[0])); | ||
}; | ||
export const decodeArray = (value, buffer, type) => { | ||
if (!isArray(type)) { | ||
if (!isArray(type) && !isTuple(type)) { | ||
throw new Error('Invalid type: type is not array'); | ||
@@ -34,6 +47,11 @@ } | ||
const pointer = Number(toNumber(value)); | ||
if (isTuple(type)) { | ||
return unpack(value, actualType); | ||
} | ||
const length = Number(toNumber(buffer.subarray(pointer, pointer + 32))); | ||
const arrayPointer = pointer + 32; | ||
const arrayBuffer = buffer.subarray(arrayPointer); | ||
return unpack(arrayBuffer, new Array(length).fill(actualType)); | ||
return unpack(arrayBuffer, new Array(length).fill(actualType[0])); | ||
}; | ||
@@ -51,2 +69,7 @@ const parsers = { | ||
}, | ||
tuple: { | ||
dynamic: false, | ||
encode: encodeArray, | ||
decode: decodeArray | ||
}, | ||
bool: { | ||
@@ -95,2 +118,6 @@ dynamic: false, | ||
if (isTuple(type)) { | ||
return parsers.tuple; | ||
} | ||
throw new Error(`type "${type}" is not supported`); | ||
@@ -142,18 +169,6 @@ }; | ||
}; | ||
export function* iterate(buffer, chunkSize) { | ||
for (let i = 0; i < buffer.length; i += chunkSize) { | ||
yield buffer.slice(i, i + chunkSize); | ||
} | ||
return buffer; | ||
} | ||
export const unpack = (buffer, types) => { | ||
const iterator = iterate(buffer, 32); | ||
let pointer = 0; | ||
return types.map(type => { | ||
const { | ||
value, | ||
done | ||
} = iterator.next(); | ||
if (done) { | ||
if (pointer >= buffer.length) { | ||
throw new Error('input data has an invalid length'); | ||
@@ -163,2 +178,13 @@ } | ||
const parser = getParser(type); | ||
if (isTuple(type)) { | ||
const types = getType(type); | ||
const tupleLength = types.length * 32; | ||
const value = buffer.subarray(pointer, pointer + tupleLength); | ||
pointer += tupleLength; | ||
return parser.decode(value, buffer, type); | ||
} | ||
const value = buffer.subarray(pointer, pointer + 32); | ||
pointer += 32; | ||
return parser.decode(value, buffer, type); | ||
@@ -165,0 +191,0 @@ }); |
{ | ||
"name": "@findeth/abi", | ||
"version": "0.5.2", | ||
"version": "0.6.0", | ||
"description": "A tiny Solidity ABI encoder and decoder", | ||
@@ -52,3 +52,4 @@ "author": "Maarten Zuidhoorn <maarten@zuidhoorn.com>", | ||
"format": "prettier --write --ignore-path .gitignore '**/*.{ts,tsx,js,json,yml}'", | ||
"prepare": "yarn run build" | ||
"prepare": "husky install", | ||
"prepack": "yarn run build" | ||
}, | ||
@@ -76,3 +77,3 @@ "devDependencies": { | ||
"eslint-plugin-jest": "^24.1.0", | ||
"husky": "^4.2.5", | ||
"husky": "^6.0.0", | ||
"jest": "^26.0.1", | ||
@@ -96,7 +97,2 @@ "lint-staged": "^10.2.9", | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"publishConfig": { | ||
@@ -103,0 +99,0 @@ "access": "public" |
@@ -20,2 +20,8 @@ import { decode, encode } from './abi'; | ||
it('encodes tuple values', () => { | ||
expect(toHex(encode(['uint256', '(uint256, uint256)', 'uint256'], [123n, [456n, 789n], 123n]))).toBe( | ||
'000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c80000000000000000000000000000000000000000000000000000000000000315000000000000000000000000000000000000000000000000000000000000007b' | ||
); | ||
}); | ||
it('encodes nested values', () => { | ||
@@ -82,2 +88,9 @@ expect( | ||
it('decodes tuple values', () => { | ||
const buffer = fromHex( | ||
'000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c80000000000000000000000000000000000000000000000000000000000000315000000000000000000000000000000000000000000000000000000000000007b' | ||
); | ||
expect(decode(['uint256', '(uint256, uint256)', 'uint256'], buffer)).toStrictEqual([123n, [456n, 789n], 123n]); | ||
}); | ||
it('decodes bytes values', () => { | ||
@@ -84,0 +97,0 @@ const buffer = fromHex( |
@@ -21,6 +21,12 @@ import { fromHex, toHex } from '../utils'; | ||
it('returns the type of an array', () => { | ||
expect(getType('string[]')).toBe('string'); | ||
expect(getType('uint256[]')).toBe('uint256'); | ||
expect(getType('string[][]')).toBe('string[]'); | ||
expect(getType('string[]')).toStrictEqual(['string']); | ||
expect(getType('uint256[]')).toStrictEqual(['uint256']); | ||
expect(getType('string[][]')).toStrictEqual(['string[]']); | ||
}); | ||
it('returns the type of a tuple', () => { | ||
expect(getType('(foo, bar)')).toStrictEqual(['foo', 'bar']); | ||
expect(getType('(foo,bar)')).toStrictEqual(['foo', 'bar']); | ||
expect(getType('(foo,bar)[]')).toStrictEqual(['(foo,bar)']); | ||
}); | ||
}); | ||
@@ -38,2 +44,8 @@ | ||
it('encodes a tuple', () => { | ||
expect(toHex(encodeArray(new Uint8Array(0), ['foo', 'bar'], '(string, string)'))).toBe( | ||
'000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003666f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036261720000000000000000000000000000000000000000000000000000000000' | ||
); | ||
}); | ||
it('throws if a type is not an array type', () => { | ||
@@ -54,2 +66,10 @@ expect(() => encodeArray(new Uint8Array(0), ['foo', 'bar', 'baz'], 'string')).toThrow(); | ||
it('decodes a tuple', () => { | ||
const value = fromHex( | ||
'000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003666f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036261720000000000000000000000000000000000000000000000000000000000' | ||
); | ||
expect(decodeArray(value, value, '(string, string)')).toStrictEqual(['foo', 'bar']); | ||
}); | ||
it('throws if a type is not an array type', () => { | ||
@@ -56,0 +76,0 @@ const buffer = fromHex( |
@@ -11,2 +11,3 @@ import { DecodeFunction, EncodeFunction } from '../types'; | ||
const ARRAY_REGEX = /^(.*)\[]$/; | ||
const TUPLE_REGEX = /^\((.*)\)$/; | ||
@@ -23,4 +24,8 @@ /** | ||
export const isTuple = (type: string): boolean => { | ||
return TUPLE_REGEX.test(type); | ||
}; | ||
/** | ||
* Get the "inner" type for an array type. E.g. `getType("uint256[]")` -> uint256. | ||
* Get the "inner" type for an array or tuple type. E.g. `getType("uint256[]")` -> ["uint256"]. | ||
* | ||
@@ -30,4 +35,11 @@ * @param {string} type | ||
*/ | ||
export const getType = (type: string): string => { | ||
return type.match(ARRAY_REGEX)![1]; | ||
export const getType = (type: string): string[] => { | ||
if (!isArray(type) && isTuple(type)) { | ||
return type | ||
.slice(1, -1) | ||
.split(',') | ||
.map((type) => type.trim()); | ||
} | ||
return [type.match(ARRAY_REGEX)![1]]; | ||
}; | ||
@@ -40,3 +52,3 @@ | ||
): Uint8Array => { | ||
if (!isArray(type)) { | ||
if (!isArray(type) && !isTuple(type)) { | ||
throw new Error('Invalid type: type is not array'); | ||
@@ -46,7 +58,11 @@ } | ||
const actualType = getType(type); | ||
if (isTuple(type)) { | ||
return pack(buffer, values, actualType); | ||
} | ||
const length = toBuffer(values.length); | ||
const arrayBuffer = concat([buffer, length]); | ||
return pack(arrayBuffer, values, new Array(values.length).fill(actualType)); | ||
return pack(arrayBuffer, values, new Array(values.length).fill(actualType[0])); | ||
}; | ||
@@ -59,3 +75,3 @@ | ||
): unknown[] => { | ||
if (!isArray(type)) { | ||
if (!isArray(type) && !isTuple(type)) { | ||
throw new Error('Invalid type: type is not array'); | ||
@@ -66,2 +82,7 @@ } | ||
const pointer = Number(toNumber(value)); | ||
if (isTuple(type)) { | ||
return unpack(value, actualType); | ||
} | ||
const length = Number(toNumber(buffer.subarray(pointer, pointer + 32))); | ||
@@ -72,3 +93,3 @@ | ||
return unpack(arrayBuffer, new Array(length).fill(actualType)); | ||
return unpack(arrayBuffer, new Array(length).fill(actualType[0])); | ||
}; | ||
@@ -90,2 +111,7 @@ | ||
}, | ||
tuple: { | ||
dynamic: false, | ||
encode: encodeArray, | ||
decode: decodeArray | ||
}, | ||
bool: { | ||
@@ -148,2 +174,7 @@ dynamic: false, | ||
// (type) | ||
if (isTuple(type)) { | ||
return parsers.tuple; | ||
} | ||
throw new Error(`type "${type}" is not supported`); | ||
@@ -220,23 +251,6 @@ }; | ||
/** | ||
* Iterate over a `Buffer` with provided `chunkSize`. | ||
* | ||
* @param {Buffer} buffer | ||
* @param {number} chunkSize | ||
* @return {Generator<Buffer, Buffer, void>} | ||
*/ | ||
export function* iterate(buffer: Uint8Array, chunkSize: number): Generator<Uint8Array, Uint8Array, void> { | ||
for (let i = 0; i < buffer.length; i += chunkSize) { | ||
yield buffer.slice(i, i + chunkSize); | ||
} | ||
return buffer; | ||
} | ||
export const unpack = (buffer: Uint8Array, types: string[]): unknown[] => { | ||
const iterator = iterate(buffer, 32); | ||
let pointer = 0; | ||
return types.map((type) => { | ||
const { value, done } = iterator.next(); | ||
if (done) { | ||
if (pointer >= buffer.length) { | ||
throw new Error('input data has an invalid length'); | ||
@@ -246,4 +260,17 @@ } | ||
const parser = getParser(type); | ||
if (isTuple(type)) { | ||
const types = getType(type); | ||
const tupleLength = types.length * 32; | ||
const value = buffer.subarray(pointer, pointer + tupleLength); | ||
pointer += tupleLength; | ||
return parser.decode(value, buffer, type); | ||
} | ||
const value = buffer.subarray(pointer, pointer + 32); | ||
pointer += 32; | ||
return parser.decode(value, buffer, type); | ||
}); | ||
}; |
@@ -9,4 +9,5 @@ import { DecodeFunction, EncodeFunction } from '../types'; | ||
export declare const isArray: (type: string) => boolean; | ||
export declare const isTuple: (type: string) => boolean; | ||
/** | ||
* Get the "inner" type for an array type. E.g. `getType("uint256[]")` -> uint256. | ||
* Get the "inner" type for an array or tuple type. E.g. `getType("uint256[]")` -> ["uint256"]. | ||
* | ||
@@ -16,3 +17,3 @@ * @param {string} type | ||
*/ | ||
export declare const getType: (type: string) => string; | ||
export declare const getType: (type: string) => string[]; | ||
export declare const encodeArray: EncodeFunction<unknown[]>; | ||
@@ -34,2 +35,7 @@ export declare const decodeArray: DecodeFunction<unknown[]>; | ||
}; | ||
tuple: { | ||
dynamic: boolean; | ||
encode: EncodeFunction<unknown[]>; | ||
decode: DecodeFunction<unknown[]>; | ||
}; | ||
bool: { | ||
@@ -103,11 +109,3 @@ dynamic: boolean; | ||
export declare const pack: (buffer: Uint8Array, values: unknown[], types: string[]) => Uint8Array; | ||
/** | ||
* Iterate over a `Buffer` with provided `chunkSize`. | ||
* | ||
* @param {Buffer} buffer | ||
* @param {number} chunkSize | ||
* @return {Generator<Buffer, Buffer, void>} | ||
*/ | ||
export declare function iterate(buffer: Uint8Array, chunkSize: number): Generator<Uint8Array, Uint8Array, void>; | ||
export declare const unpack: (buffer: Uint8Array, types: string[]) => unknown[]; | ||
export {}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
182741
2478