@findeth/abi
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -11,2 +11,6 @@ "use strict"; | ||
const encodeAddress = (buffer, value) => { | ||
if (value.length !== 42) { | ||
throw new Error('Invalid address length'); | ||
} | ||
const addressBuffer = Buffer.alloc(32); | ||
@@ -21,3 +25,3 @@ addressBuffer.write(value.substring(2), 12, 'hex'); | ||
const addressBuffer = value.subarray(-20); | ||
return `0x${addressBuffer.toString('hex')}`; | ||
return `0x${(0, _buffer.toHex)(addressBuffer)}`; | ||
}; | ||
@@ -24,0 +28,0 @@ |
@@ -13,4 +13,10 @@ "use strict"; | ||
var _bytes = require("./bytes"); | ||
var _fixedBytes = require("./fixed-bytes"); | ||
var _number = require("./number"); | ||
var _string = require("./string"); | ||
const ARRAY_REGEX = /^(.*)\[]$/; | ||
@@ -48,2 +54,6 @@ | ||
const parsers = { | ||
address: { | ||
encode: _address.encodeAddress, | ||
decode: _address.decodeAddress | ||
}, | ||
array: { | ||
@@ -54,9 +64,19 @@ dynamic: true, | ||
}, | ||
address: { | ||
encode: _address.encodeAddress, | ||
decode: _address.decodeAddress | ||
bytes: { | ||
dynamic: true, | ||
encode: _bytes.encodeBytes, | ||
decode: _bytes.decodeBytes | ||
}, | ||
fixedBytes: { | ||
encode: _fixedBytes.encodeFixedBytes, | ||
decode: _fixedBytes.decodeFixedBytes | ||
}, | ||
number: { | ||
encode: _number.encodeNumber, | ||
decode: _number.decodeNumber | ||
}, | ||
string: { | ||
dynamic: true, | ||
encode: _string.encodeString, | ||
decode: _string.decodeString | ||
} | ||
@@ -70,3 +90,7 @@ }; | ||
if ((0, _number.isNumber)(type)) { | ||
if ((0, _fixedBytes.isFixedBytes)(type)) { | ||
return parsers.fixedBytes; | ||
} | ||
if ((0, _number.isNumber)(type) || type === 'bool') { | ||
return parsers.number; | ||
@@ -121,8 +145,8 @@ } | ||
}, { | ||
staticBuffer: Buffer.alloc(0), | ||
dynamicBuffer: Buffer.alloc(0), | ||
staticBuffer: new Uint8Array(0), | ||
dynamicBuffer: new Uint8Array(0), | ||
updateFunctions: [] | ||
}); | ||
const updatedStaticBuffer = packedUpdateFunctions.reduce((target, update) => update(target), packedStaticBuffer); | ||
return Buffer.concat([buffer, updatedStaticBuffer, packedDynamicBuffer]); | ||
return new Uint8Array([...buffer, ...updatedStaticBuffer, ...packedDynamicBuffer]); | ||
}; | ||
@@ -129,0 +153,0 @@ |
@@ -42,3 +42,3 @@ "use strict"; | ||
const maxValue = 2n ** bits - 1n; | ||
return value >= 0 && value <= maxValue; | ||
return value >= 0n && value <= maxValue; | ||
}; | ||
@@ -48,4 +48,14 @@ | ||
const asNumber = value => { | ||
if (typeof value === 'bigint') { | ||
return value; | ||
} | ||
return BigInt(value); | ||
}; | ||
const encodeNumber = (buffer, value, type) => { | ||
if (!inRange(value, type)) { | ||
const numberValue = asNumber(value); | ||
if (!inRange(numberValue, type)) { | ||
throw new Error(`Cannot encode number: value is out of range for type ${type}`); | ||
@@ -55,6 +65,6 @@ } | ||
if (isSigned(type)) { | ||
return (0, _buffer.concat)(buffer, (0, _twosComplement.toTwosComplement)(value, 32)); | ||
return (0, _buffer.concat)(buffer, (0, _twosComplement.toTwosComplement)(numberValue, 32)); | ||
} | ||
return (0, _buffer.concat)(buffer, (0, _buffer.toBuffer)(value)); | ||
return (0, _buffer.concat)(buffer, (0, _buffer.toBuffer)(numberValue)); | ||
}; | ||
@@ -61,0 +71,0 @@ |
@@ -6,7 +6,7 @@ "use strict"; | ||
}); | ||
exports.toNumber = exports.toBuffer = exports.concat = void 0; | ||
exports.toHex = exports.toNumber = exports.toString = exports.toBuffer = exports.addPadding = exports.concat = void 0; | ||
const BUFFER_WIDTH = 32; | ||
const concat = (target, value, position) => { | ||
return Buffer.concat([target.subarray(0, position !== null && position !== void 0 ? position : target.length), value, target.subarray(position !== null && position !== void 0 ? position : target.length)]); | ||
return new Uint8Array([...target.subarray(0, position !== null && position !== void 0 ? position : target.length), ...value, ...target.subarray(position !== null && position !== void 0 ? position : target.length)]); | ||
}; | ||
@@ -16,3 +16,19 @@ | ||
const addPadding = (buffer, length = 32) => { | ||
const padding = Buffer.alloc(Math.max(length - buffer.length, 0), 0); | ||
return concat(buffer, padding); | ||
}; | ||
exports.addPadding = addPadding; | ||
const toBuffer = value => { | ||
if (Buffer.isBuffer(value) || value instanceof Uint8Array) { | ||
return value; | ||
} | ||
if (typeof value === 'string') { | ||
const stringValue = value.startsWith('0x') ? value.substring(2) : value; | ||
return Buffer.from(stringValue, 'hex'); | ||
} | ||
const hex = value.toString(16); | ||
@@ -24,4 +40,14 @@ return Buffer.from(hex.padStart(BUFFER_WIDTH * 2, '0').slice(0, BUFFER_WIDTH * 2), 'hex'); | ||
const toString = value => { | ||
if (typeof window !== 'undefined' && window.TextDecoder) { | ||
return new TextDecoder('utf-8').decode(value); | ||
} | ||
return new (require('util').TextDecoder)('utf-8').decode(value); | ||
}; | ||
exports.toString = toString; | ||
const toNumber = buffer => { | ||
const hex = buffer.toString('hex'); | ||
const hex = toHex(buffer); | ||
@@ -36,2 +62,12 @@ if (hex.length === 0) { | ||
exports.toNumber = toNumber; | ||
const numberToHex = value => { | ||
return ('0' + value.toString(16)).slice(-2); | ||
}; | ||
const toHex = buffer => { | ||
return Array.from(buffer).map(numberToHex).join(''); | ||
}; | ||
exports.toHex = toHex; | ||
//# sourceMappingURL=buffer.js.map |
@@ -8,10 +8,13 @@ "use strict"; | ||
var _buffer = require("./buffer"); | ||
const fromTwosComplement = buffer => { | ||
const bufferValue = (0, _buffer.toBuffer)(buffer); | ||
let value = 0n; | ||
for (const byte of buffer) { | ||
for (const byte of bufferValue) { | ||
value = (value << 8n) + BigInt(byte); | ||
} | ||
return BigInt.asIntN(buffer.length * 8, value); | ||
return BigInt.asIntN(bufferValue.length * 8, value); | ||
}; | ||
@@ -22,3 +25,3 @@ | ||
const toTwosComplement = (value, length) => { | ||
const buffer = Buffer.alloc(length); | ||
const buffer = new Uint8Array(length); | ||
@@ -25,0 +28,0 @@ for (let i = 0; i < buffer.length; i++) { |
@@ -1,3 +0,7 @@ | ||
import { concat } from '../utils/buffer'; | ||
import { concat, toHex } from '../utils/buffer'; | ||
export const encodeAddress = (buffer, value) => { | ||
if (value.length !== 42) { | ||
throw new Error('Invalid address length'); | ||
} | ||
const addressBuffer = Buffer.alloc(32); | ||
@@ -9,4 +13,4 @@ addressBuffer.write(value.substring(2), 12, 'hex'); | ||
const addressBuffer = value.subarray(-20); | ||
return `0x${addressBuffer.toString('hex')}`; | ||
return `0x${toHex(addressBuffer)}`; | ||
}; | ||
//# sourceMappingURL=address.js.map |
import { concat, toBuffer, toNumber } from '../utils/buffer'; | ||
import { decodeAddress, encodeAddress } from './address'; | ||
import { decodeBytes, encodeBytes } from './bytes'; | ||
import { decodeFixedBytes, encodeFixedBytes, isFixedBytes } from './fixed-bytes'; | ||
import { decodeNumber, encodeNumber, isNumber } from './number'; | ||
import { decodeString, encodeString } from './string'; | ||
const ARRAY_REGEX = /^(.*)\[]$/; | ||
@@ -28,2 +31,6 @@ export const isArray = type => { | ||
const parsers = { | ||
address: { | ||
encode: encodeAddress, | ||
decode: decodeAddress | ||
}, | ||
array: { | ||
@@ -34,9 +41,19 @@ dynamic: true, | ||
}, | ||
address: { | ||
encode: encodeAddress, | ||
decode: decodeAddress | ||
bytes: { | ||
dynamic: true, | ||
encode: encodeBytes, | ||
decode: decodeBytes | ||
}, | ||
fixedBytes: { | ||
encode: encodeFixedBytes, | ||
decode: decodeFixedBytes | ||
}, | ||
number: { | ||
encode: encodeNumber, | ||
decode: decodeNumber | ||
}, | ||
string: { | ||
dynamic: true, | ||
encode: encodeString, | ||
decode: decodeString | ||
} | ||
@@ -49,3 +66,7 @@ }; | ||
if (isNumber(type)) { | ||
if (isFixedBytes(type)) { | ||
return parsers.fixedBytes; | ||
} | ||
if (isNumber(type) || type === 'bool') { | ||
return parsers.number; | ||
@@ -97,8 +118,8 @@ } | ||
}, { | ||
staticBuffer: Buffer.alloc(0), | ||
dynamicBuffer: Buffer.alloc(0), | ||
staticBuffer: new Uint8Array(0), | ||
dynamicBuffer: new Uint8Array(0), | ||
updateFunctions: [] | ||
}); | ||
const updatedStaticBuffer = packedUpdateFunctions.reduce((target, update) => update(target), packedStaticBuffer); | ||
return Buffer.concat([buffer, updatedStaticBuffer, packedDynamicBuffer]); | ||
return new Uint8Array([...buffer, ...updatedStaticBuffer, ...packedDynamicBuffer]); | ||
}; | ||
@@ -105,0 +126,0 @@ export function* iterate(buffer, chunkSize) { |
@@ -27,6 +27,17 @@ import { concat, toBuffer, toNumber } from '../utils/buffer'; | ||
const maxValue = 2n ** bits - 1n; | ||
return value >= 0 && value <= maxValue; | ||
return value >= 0n && value <= maxValue; | ||
}; | ||
const asNumber = value => { | ||
if (typeof value === 'bigint') { | ||
return value; | ||
} | ||
return BigInt(value); | ||
}; | ||
export const encodeNumber = (buffer, value, type) => { | ||
if (!inRange(value, type)) { | ||
const numberValue = asNumber(value); | ||
if (!inRange(numberValue, type)) { | ||
throw new Error(`Cannot encode number: value is out of range for type ${type}`); | ||
@@ -36,6 +47,6 @@ } | ||
if (isSigned(type)) { | ||
return concat(buffer, toTwosComplement(value, 32)); | ||
return concat(buffer, toTwosComplement(numberValue, 32)); | ||
} | ||
return concat(buffer, toBuffer(value)); | ||
return concat(buffer, toBuffer(numberValue)); | ||
}; | ||
@@ -42,0 +53,0 @@ export const decodeNumber = (value, _, type) => { |
const BUFFER_WIDTH = 32; | ||
export const concat = (target, value, position) => { | ||
return Buffer.concat([target.subarray(0, position !== null && position !== void 0 ? position : target.length), value, target.subarray(position !== null && position !== void 0 ? position : target.length)]); | ||
return new Uint8Array([...target.subarray(0, position !== null && position !== void 0 ? position : target.length), ...value, ...target.subarray(position !== null && position !== void 0 ? position : target.length)]); | ||
}; | ||
export const addPadding = (buffer, length = 32) => { | ||
const padding = Buffer.alloc(Math.max(length - buffer.length, 0), 0); | ||
return concat(buffer, padding); | ||
}; | ||
export const toBuffer = value => { | ||
if (Buffer.isBuffer(value) || value instanceof Uint8Array) { | ||
return value; | ||
} | ||
if (typeof value === 'string') { | ||
const stringValue = value.startsWith('0x') ? value.substring(2) : value; | ||
return Buffer.from(stringValue, 'hex'); | ||
} | ||
const hex = value.toString(16); | ||
return Buffer.from(hex.padStart(BUFFER_WIDTH * 2, '0').slice(0, BUFFER_WIDTH * 2), 'hex'); | ||
}; | ||
export const toString = value => { | ||
if (typeof window !== 'undefined' && window.TextDecoder) { | ||
return new TextDecoder('utf-8').decode(value); | ||
} | ||
return new (require('util').TextDecoder)('utf-8').decode(value); | ||
}; | ||
export const toNumber = buffer => { | ||
const hex = buffer.toString('hex'); | ||
const hex = toHex(buffer); | ||
@@ -18,2 +38,10 @@ if (hex.length === 0) { | ||
}; | ||
const numberToHex = value => { | ||
return ('0' + value.toString(16)).slice(-2); | ||
}; | ||
export const toHex = buffer => { | ||
return Array.from(buffer).map(numberToHex).join(''); | ||
}; | ||
//# sourceMappingURL=buffer.js.map |
@@ -0,12 +1,14 @@ | ||
import { toBuffer } from './buffer'; | ||
export const fromTwosComplement = buffer => { | ||
const bufferValue = toBuffer(buffer); | ||
let value = 0n; | ||
for (const byte of buffer) { | ||
for (const byte of bufferValue) { | ||
value = (value << 8n) + BigInt(byte); | ||
} | ||
return BigInt.asIntN(buffer.length * 8, value); | ||
return BigInt.asIntN(bufferValue.length * 8, value); | ||
}; | ||
export const toTwosComplement = (value, length) => { | ||
const buffer = Buffer.alloc(length); | ||
const buffer = new Uint8Array(length); | ||
@@ -13,0 +15,0 @@ for (let i = 0; i < buffer.length; i++) { |
{ | ||
"name": "@findeth/abi", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "A tiny Solidity ABI encoder and decoder", | ||
@@ -30,3 +30,3 @@ "author": "Maarten Zuidhoorn <maarten@zuidhoorn.com>", | ||
"engines": { | ||
"node": "12" | ||
"node": ">=10" | ||
}, | ||
@@ -47,8 +47,8 @@ "files": [ | ||
"test": "jest", | ||
"lint": "yarn run lint:tsc && yarn run lint:tslint && yarn run lint:prettier && yarn run lint:lockfile", | ||
"lint": "yarn run lint:tsc && yarn run lint:eslint && yarn run lint:prettier && yarn run lint:lockfile", | ||
"lint:tsc": "tsc --noEmit", | ||
"lint:tslint": "tslint --project .", | ||
"lint:prettier": "prettier --check 'src/**/*.{ts,tsx}'", | ||
"lint:eslint": "eslint . --ignore-path .gitignore --ext .ts,.tsx,.js,.jsx", | ||
"lint:prettier": "prettier --check --ignore-path .gitignore '**/*.{ts,tsx,js,json}'", | ||
"lint:lockfile": "lockfile-lint --type yarn --path yarn.lock --allowed-hosts yarn --validate-https --validate-checksum --validate-integrity", | ||
"prettier": "prettier --write 'src/**/*.{ts,tsx}'", | ||
"prettier": "prettier --write --ignore-path .gitignore '**/*.{ts,tsx,js,json}'", | ||
"prepare": "yarn run build" | ||
@@ -70,5 +70,11 @@ }, | ||
"@types/jest": "^25.2.3", | ||
"@typescript-eslint/eslint-plugin": "^3.3.0", | ||
"@typescript-eslint/parser": "^3.3.0", | ||
"babel-jest": "^26.0.1", | ||
"codecov": "^3.7.0", | ||
"cross-env": "^7.0.2", | ||
"eslint": "^7.2.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-import": "^2.21.2", | ||
"eslint-plugin-jest": "^23.13.2", | ||
"husky": "^4.2.5", | ||
@@ -81,8 +87,16 @@ "jest": "^26.0.1", | ||
"ts-node": "^8.10.2", | ||
"tslint": "^6.1.2", | ||
"tslint-config-prettier": "^1.18.0", | ||
"tslint-origin-ordered-imports-rule": "^1.3.0-0", | ||
"typedoc": "^0.17.7", | ||
"typescript": "^3.9.5" | ||
}, | ||
"lint-staged": { | ||
"*.{ts,tsx}": [ | ||
"prettier --write", | ||
"eslint --fix" | ||
] | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"publishConfig": { | ||
@@ -89,0 +103,0 @@ "access": "public" |
import { decode, encode } from './abi'; | ||
import { toHex } from './utils/buffer'; | ||
describe('encode', () => { | ||
it('encodes simple values', () => { | ||
expect(encode(['uint256', 'uint256'], [12345, 12345]).toString('hex')).toBe( | ||
expect(toHex(encode(['uint256', 'uint256'], [12345, 12345]))).toBe( | ||
'00000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000003039' | ||
@@ -11,3 +12,3 @@ ); | ||
it('encodes array values', () => { | ||
expect(encode(['uint256', 'uint256[]', 'uint256'], [12345, [67890, 67890], 12345]).toString('hex')).toBe( | ||
expect(toHex(encode(['uint256', 'uint256[]', 'uint256'], [12345, [67890, 67890], 12345]))).toBe( | ||
'000000000000000000000000000000000000000000000000000000000000303900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000003039000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000109320000000000000000000000000000000000000000000000000000000000010932' | ||
@@ -19,13 +20,15 @@ ); | ||
expect( | ||
encode( | ||
['uint256', 'uint256[][]', 'uint256'], | ||
[ | ||
12345, | ||
toHex( | ||
encode( | ||
['uint256', 'uint256[][]', 'uint256'], | ||
[ | ||
[54321, 12345], | ||
[67890, 98760] | ||
], | ||
12345 | ||
] | ||
).toString('hex') | ||
12345, | ||
[ | ||
[54321, 12345], | ||
[67890, 98760] | ||
], | ||
12345 | ||
] | ||
) | ||
) | ||
).toBe( | ||
@@ -35,2 +38,16 @@ '0000000000000000000000000000000000000000000000000000000000003039000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000d43100000000000000000000000000000000000000000000000000000000000030390000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000001093200000000000000000000000000000000000000000000000000000000000181c8' | ||
}); | ||
it('encodes bytes values', () => { | ||
const bytes = Buffer.from('123456789abcdef123456789abcdef123456789abcdef', 'hex').toString('hex'); | ||
expect(toHex(encode(['bytes'], [bytes]))).toBe( | ||
'00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000016123456789abcdef123456789abcdef123456789abcde00000000000000000000' | ||
); | ||
}); | ||
it('encodes string values', () => { | ||
const string = 'foo bar baz qux quux corge'; | ||
expect(toHex(encode(['string'], [string]))).toBe( | ||
'0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001a666f6f206261722062617a20717578207175757820636f726765000000000000' | ||
); | ||
}); | ||
}); | ||
@@ -37,0 +54,0 @@ |
@@ -13,3 +13,3 @@ import { ContractFunction, ContractInput } from './contract'; | ||
*/ | ||
export const encode = (input: (ContractInput | string)[], values: unknown[]): Buffer => { | ||
export const encode = (input: Array<ContractInput | string>, values: unknown[]): Uint8Array => { | ||
const types = input.map((type) => { | ||
@@ -33,3 +33,3 @@ if (typeof type === 'string') { | ||
*/ | ||
export const encodeWithIdentifier = (contractFunction: ContractFunction, values: unknown[]): Buffer => { | ||
export const encodeWithIdentifier = (contractFunction: ContractFunction, values: unknown[]): Uint8Array => { | ||
const identifier = Buffer.from(getIdentifier(contractFunction), 'hex'); | ||
@@ -41,3 +41,3 @@ const encoded = encode(contractFunction.inputs, values); | ||
export const decode = <T extends unknown[]>(input: (ContractInput | string)[], buffer: Buffer): T => { | ||
export const decode = <T extends unknown[]>(input: Array<ContractInput | string>, buffer: Buffer): T => { | ||
const types = input.map((type) => { | ||
@@ -44,0 +44,0 @@ if (typeof type === 'string') { |
@@ -0,5 +1,5 @@ | ||
import erc20Abi from './__tests__/erc20.json'; | ||
import miscAbi from './__tests__/misc.json'; | ||
import { ContractFunction } from './contract'; | ||
import { getIdentifier, parseType } from './identifier'; | ||
import erc20Abi from './__tests__/erc20.json'; | ||
import miscAbi from './__tests__/misc.json'; | ||
@@ -6,0 +6,0 @@ const erc20 = erc20Abi as ContractFunction[]; |
@@ -1,5 +0,9 @@ | ||
import { concat } from '../utils/buffer'; | ||
import { concat, toHex } from '../utils/buffer'; | ||
import { DecodeFunction, EncodeFunction } from './parser'; | ||
export const encodeAddress: EncodeFunction = (buffer: Buffer, value: string): Buffer => { | ||
export const encodeAddress: EncodeFunction = (buffer: Uint8Array, value: string): Uint8Array => { | ||
if (value.length !== 42) { | ||
throw new Error('Invalid address length'); | ||
} | ||
const addressBuffer = Buffer.alloc(32); | ||
@@ -11,5 +15,5 @@ addressBuffer.write(value.substring(2), 12, 'hex'); | ||
export const decodeAddress: DecodeFunction = (value: Buffer): string => { | ||
export const decodeAddress: DecodeFunction = (value: Uint8Array): string => { | ||
const addressBuffer = value.subarray(-20); | ||
return `0x${addressBuffer.toString('hex')}`; | ||
return `0x${toHex(addressBuffer)}`; | ||
}; |
import { concat, toBuffer, toNumber } from '../utils/buffer'; | ||
import { decodeAddress, encodeAddress } from './address'; | ||
import { decodeBytes, encodeBytes } from './bytes'; | ||
import { decodeFixedBytes, encodeFixedBytes, isFixedBytes } from './fixed-bytes'; | ||
import { decodeNumber, encodeNumber, isNumber } from './number'; | ||
import { DecodeFunction, EncodeFunction, Parser } from './parser'; | ||
import { decodeString, encodeString } from './string'; | ||
@@ -28,3 +31,3 @@ const ARRAY_REGEX = /^(.*)\[]$/; | ||
export const encodeArray: EncodeFunction = (buffer: Buffer, values: unknown[], type: string): Buffer => { | ||
export const encodeArray: EncodeFunction = (buffer: Uint8Array, values: unknown[], type: string): Uint8Array => { | ||
const actualType = getType(type); | ||
@@ -38,3 +41,3 @@ const length = toBuffer(values.length); | ||
export const decodeArray: DecodeFunction = (value: Buffer, buffer: Buffer, type: string): unknown[] => { | ||
export const decodeArray: DecodeFunction = (value: Uint8Array, buffer: Uint8Array, type: string): unknown[] => { | ||
const actualType = getType(type); | ||
@@ -54,2 +57,6 @@ const pointer = Number(toNumber(value)); | ||
const parsers: Record<string, Parser> = { | ||
address: { | ||
encode: encodeAddress, | ||
decode: decodeAddress | ||
}, | ||
array: { | ||
@@ -60,9 +67,19 @@ dynamic: true, | ||
}, | ||
address: { | ||
encode: encodeAddress, | ||
decode: decodeAddress | ||
bytes: { | ||
dynamic: true, | ||
encode: encodeBytes, | ||
decode: decodeBytes | ||
}, | ||
fixedBytes: { | ||
encode: encodeFixedBytes, | ||
decode: decodeFixedBytes | ||
}, | ||
number: { | ||
encode: encodeNumber, | ||
decode: decodeNumber | ||
}, | ||
string: { | ||
dynamic: true, | ||
encode: encodeString, | ||
decode: decodeString | ||
} | ||
@@ -82,6 +99,13 @@ }; | ||
if (isNumber(type)) { | ||
// bytes[n] | ||
if (isFixedBytes(type)) { | ||
return parsers.fixedBytes; | ||
} | ||
// u?int[n], bool | ||
if (isNumber(type) || type === 'bool') { | ||
return parsers.number; | ||
} | ||
// type[] | ||
if (isArray(type)) { | ||
@@ -95,5 +119,5 @@ return parsers.array; | ||
interface PackState { | ||
staticBuffer: Buffer; | ||
dynamicBuffer: Buffer; | ||
updateFunctions: ((buffer: Buffer) => Buffer)[]; | ||
staticBuffer: Uint8Array; | ||
dynamicBuffer: Uint8Array; | ||
updateFunctions: Array<(buffer: Uint8Array) => Uint8Array>; | ||
} | ||
@@ -113,3 +137,3 @@ | ||
*/ | ||
export const pack = (buffer: Buffer, values: unknown[], types: string[]): Buffer => { | ||
export const pack = (buffer: Uint8Array, values: unknown[], types: string[]): Uint8Array => { | ||
const { | ||
@@ -131,3 +155,3 @@ staticBuffer: packedStaticBuffer, | ||
const update = (oldBuffer: Buffer): Buffer => { | ||
const update = (oldBuffer: Uint8Array): Uint8Array => { | ||
return Buffer.concat([ | ||
@@ -151,6 +175,6 @@ oldBuffer.subarray(0, staticOffset), | ||
}, | ||
{ staticBuffer: Buffer.alloc(0), dynamicBuffer: Buffer.alloc(0), updateFunctions: [] } | ||
{ staticBuffer: new Uint8Array(0), dynamicBuffer: new Uint8Array(0), updateFunctions: [] } | ||
); | ||
const updatedStaticBuffer = packedUpdateFunctions.reduce<Buffer>( | ||
const updatedStaticBuffer = packedUpdateFunctions.reduce<Uint8Array>( | ||
(target, update) => update(target), | ||
@@ -160,3 +184,3 @@ packedStaticBuffer | ||
return Buffer.concat([buffer, updatedStaticBuffer, packedDynamicBuffer]); | ||
return new Uint8Array([...buffer, ...updatedStaticBuffer, ...packedDynamicBuffer]); | ||
}; | ||
@@ -171,3 +195,3 @@ | ||
*/ | ||
export function* iterate(buffer: Buffer, chunkSize: number): Generator<Buffer, Buffer, void> { | ||
export function* iterate(buffer: Uint8Array, chunkSize: number): Generator<Uint8Array, Uint8Array, void> { | ||
for (let i = 0; i < buffer.length; i += chunkSize) { | ||
@@ -180,3 +204,3 @@ yield buffer.slice(i, i + chunkSize); | ||
export const unpack = (buffer: Buffer, types: string[]): unknown[] => { | ||
export const unpack = (buffer: Uint8Array, types: string[]): unknown[] => { | ||
const iterator = iterate(buffer, 32); | ||
@@ -183,0 +207,0 @@ |
@@ -29,7 +29,17 @@ import { concat, toBuffer, toNumber } from '../utils/buffer'; | ||
const maxValue = 2n ** bits - 1n; | ||
return value >= 0 && value <= maxValue; | ||
return value >= 0n && value <= maxValue; | ||
}; | ||
export const encodeNumber: EncodeFunction = (buffer: Buffer, value: bigint, type: string): Buffer => { | ||
if (!inRange(value, type)) { | ||
const asNumber = (value: string | bigint): bigint => { | ||
if (typeof value === 'bigint') { | ||
return value; | ||
} | ||
return BigInt(value); | ||
}; | ||
export const encodeNumber: EncodeFunction = (buffer: Uint8Array, value: string | bigint, type: string): Uint8Array => { | ||
const numberValue = asNumber(value); | ||
if (!inRange(numberValue, type)) { | ||
throw new Error(`Cannot encode number: value is out of range for type ${type}`); | ||
@@ -39,9 +49,9 @@ } | ||
if (isSigned(type)) { | ||
return concat(buffer, toTwosComplement(value, 32)); | ||
return concat(buffer, toTwosComplement(numberValue, 32)); | ||
} | ||
return concat(buffer, toBuffer(value)); | ||
return concat(buffer, toBuffer(numberValue)); | ||
}; | ||
export const decodeNumber: DecodeFunction = (value: Buffer, _, type: string): bigint => { | ||
export const decodeNumber: DecodeFunction = (value: Uint8Array, _, type: string): bigint => { | ||
if (isSigned(type)) { | ||
@@ -48,0 +58,0 @@ return fromTwosComplement(value); |
@@ -1,6 +0,6 @@ | ||
// tslint:disable-next-line:no-any | ||
export type EncodeFunction = (buffer: Buffer, value: any, type: string) => Buffer; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type EncodeFunction = (buffer: Uint8Array, value: any, type: string) => Uint8Array; | ||
// tslint:disable-next-line:no-any | ||
export type DecodeFunction = (value: Buffer, buffer: Buffer, type: string) => any; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type DecodeFunction = (value: Uint8Array, buffer: Uint8Array, type: string) => any; | ||
@@ -7,0 +7,0 @@ export interface Parser { |
const BUFFER_WIDTH = 32; | ||
export const concat = (target: Buffer, value: Buffer, position?: number): Buffer => { | ||
return Buffer.concat([ | ||
target.subarray(0, position ?? target.length), | ||
value, | ||
target.subarray(position ?? target.length) | ||
/** | ||
* Concatenate two buffers. If a position is specified, `value` will be put in `target` at the specified position. All | ||
* bytes after that will be moved to the end of the buffer. | ||
* | ||
* @param {Uint8Array} target | ||
* @param {Uint8Array} value | ||
* @param {number} [position] | ||
* @return {Uint8Array} | ||
*/ | ||
export const concat = (target: Uint8Array, value: Uint8Array, position?: number): Uint8Array => { | ||
return new Uint8Array([ | ||
...target.subarray(0, position ?? target.length), | ||
...value, | ||
...target.subarray(position ?? target.length) | ||
]); | ||
}; | ||
export const toBuffer = (value: number | bigint): Buffer => { | ||
/** | ||
* Add padding to a buffer. If the buffer is larger than `length`, this function won't do anything. If it's smaller, the | ||
* buffer will be padded to the specified length, with extra zeroes at the end. | ||
* | ||
* @param {Uint8Array} buffer | ||
* @param {number} [length] | ||
* @return {Uint8Array} | ||
*/ | ||
export const addPadding = (buffer: Uint8Array, length = 32): Uint8Array => { | ||
const padding = Buffer.alloc(Math.max(length - buffer.length, 0), 0); | ||
return concat(buffer, padding); | ||
}; | ||
/** | ||
* Get a value as buffer. The value can be a string, number, bigint or buffer. If the value is a string, it is assumed | ||
* that it is a hexadecimal value. | ||
* | ||
* @param {string | number | bigint | Uint8Array} value | ||
* @return {Uint8Array} | ||
*/ | ||
export const toBuffer = (value: string | number | bigint | Uint8Array): Uint8Array => { | ||
if (Buffer.isBuffer(value) || value instanceof Uint8Array) { | ||
return value; | ||
} | ||
if (typeof value === 'string') { | ||
const stringValue = value.startsWith('0x') ? value.substring(2) : value; | ||
return Buffer.from(stringValue, 'hex'); | ||
} | ||
const hex = value.toString(16); | ||
@@ -16,4 +54,24 @@ return Buffer.from(hex.padStart(BUFFER_WIDTH * 2, '0').slice(0, BUFFER_WIDTH * 2), 'hex'); | ||
export const toNumber = (buffer: Buffer): bigint => { | ||
const hex = buffer.toString('hex'); | ||
/** | ||
* Get a UTF-8 encodes buffer as string. | ||
* | ||
* @param {Uint8Array} value | ||
* @return {string} | ||
*/ | ||
export const toString = (value: Uint8Array): string => { | ||
if (typeof window !== 'undefined' && window.TextDecoder) { | ||
return new TextDecoder('utf-8').decode(value); | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
return new (require('util').TextDecoder)('utf-8').decode(value); | ||
}; | ||
/** | ||
* Get a number from a buffer. | ||
* | ||
* @param {Uint8Array} buffer | ||
*/ | ||
export const toNumber = (buffer: Uint8Array): bigint => { | ||
const hex = toHex(buffer); | ||
if (hex.length === 0) { | ||
@@ -25,1 +83,15 @@ return BigInt(0); | ||
}; | ||
const numberToHex = (value: number): string => { | ||
return ('0' + value.toString(16)).slice(-2); | ||
}; | ||
/** | ||
* Get a buffer as hexadecimal string. | ||
* | ||
* @param {Uint8Array} buffer | ||
* @return {string} | ||
*/ | ||
export const toHex = (buffer: Uint8Array): string => { | ||
return Array.from(buffer).map(numberToHex).join(''); | ||
}; |
@@ -1,16 +0,18 @@ | ||
export const fromTwosComplement = (buffer: Buffer): bigint => { | ||
import { toBuffer } from './buffer'; | ||
export const fromTwosComplement = (buffer: string | Uint8Array): bigint => { | ||
const bufferValue = toBuffer(buffer); | ||
let value = 0n; | ||
for (const byte of buffer) { | ||
// tslint:disable-next-line:no-bitwise | ||
for (const byte of bufferValue) { | ||
value = (value << 8n) + BigInt(byte); | ||
} | ||
return BigInt.asIntN(buffer.length * 8, value); | ||
return BigInt.asIntN(bufferValue.length * 8, value); | ||
}; | ||
export const toTwosComplement = (value: bigint, length: number): Buffer => { | ||
const buffer = Buffer.alloc(length); | ||
export const toTwosComplement = (value: bigint, length: number): Uint8Array => { | ||
const buffer = new Uint8Array(length); | ||
for (let i = 0; i < buffer.length; i++) { | ||
buffer[i] = Number(BigInt.asUintN(8, value)); | ||
// tslint:disable-next-line:no-bitwise | ||
value = value >> 8n; | ||
@@ -17,0 +19,0 @@ } |
@@ -10,3 +10,3 @@ /// <reference types="node" /> | ||
*/ | ||
export declare const encode: (input: (ContractInput | string)[], values: unknown[]) => Buffer; | ||
export declare const encode: (input: Array<ContractInput | string>, values: unknown[]) => Uint8Array; | ||
/** | ||
@@ -19,3 +19,3 @@ * Encode the input data with the provided types, and prepend the function identifier. | ||
*/ | ||
export declare const encodeWithIdentifier: (contractFunction: ContractFunction, values: unknown[]) => Buffer; | ||
export declare const decode: <T extends unknown[]>(input: (ContractInput | string)[], buffer: Buffer) => T; | ||
export declare const encodeWithIdentifier: (contractFunction: ContractFunction, values: unknown[]) => Uint8Array; | ||
export declare const decode: <T extends unknown[]>(input: Array<ContractInput | string>, buffer: Buffer) => T; |
@@ -1,2 +0,1 @@ | ||
/// <reference types="node" /> | ||
import { DecodeFunction, EncodeFunction, Parser } from './parser'; | ||
@@ -31,3 +30,3 @@ /** | ||
*/ | ||
export declare const pack: (buffer: Buffer, values: unknown[], types: string[]) => Buffer; | ||
export declare const pack: (buffer: Uint8Array, values: unknown[], types: string[]) => Uint8Array; | ||
/** | ||
@@ -40,3 +39,3 @@ * Iterate over a `Buffer` with provided `chunkSize`. | ||
*/ | ||
export declare function iterate(buffer: Buffer, chunkSize: number): Generator<Buffer, Buffer, void>; | ||
export declare const unpack: (buffer: Buffer, types: string[]) => unknown[]; | ||
export declare function iterate(buffer: Uint8Array, chunkSize: number): Generator<Uint8Array, Uint8Array, void>; | ||
export declare const unpack: (buffer: Uint8Array, types: string[]) => unknown[]; |
@@ -1,4 +0,3 @@ | ||
/// <reference types="node" /> | ||
export declare type EncodeFunction = (buffer: Buffer, value: any, type: string) => Buffer; | ||
export declare type DecodeFunction = (value: Buffer, buffer: Buffer, type: string) => any; | ||
export declare type EncodeFunction = (buffer: Uint8Array, value: any, type: string) => Uint8Array; | ||
export declare type DecodeFunction = (value: Uint8Array, buffer: Uint8Array, type: string) => any; | ||
export interface Parser { | ||
@@ -5,0 +4,0 @@ dynamic?: true; |
@@ -1,4 +0,47 @@ | ||
/// <reference types="node" /> | ||
export declare const concat: (target: Buffer, value: Buffer, position?: number | undefined) => Buffer; | ||
export declare const toBuffer: (value: number | bigint) => Buffer; | ||
export declare const toNumber: (buffer: Buffer) => bigint; | ||
/** | ||
* Concatenate two buffers. If a position is specified, `value` will be put in `target` at the specified position. All | ||
* bytes after that will be moved to the end of the buffer. | ||
* | ||
* @param {Uint8Array} target | ||
* @param {Uint8Array} value | ||
* @param {number} [position] | ||
* @return {Uint8Array} | ||
*/ | ||
export declare const concat: (target: Uint8Array, value: Uint8Array, position?: number | undefined) => Uint8Array; | ||
/** | ||
* Add padding to a buffer. If the buffer is larger than `length`, this function won't do anything. If it's smaller, the | ||
* buffer will be padded to the specified length, with extra zeroes at the end. | ||
* | ||
* @param {Uint8Array} buffer | ||
* @param {number} [length] | ||
* @return {Uint8Array} | ||
*/ | ||
export declare const addPadding: (buffer: Uint8Array, length?: number) => Uint8Array; | ||
/** | ||
* Get a value as buffer. The value can be a string, number, bigint or buffer. If the value is a string, it is assumed | ||
* that it is a hexadecimal value. | ||
* | ||
* @param {string | number | bigint | Uint8Array} value | ||
* @return {Uint8Array} | ||
*/ | ||
export declare const toBuffer: (value: string | number | bigint | Uint8Array) => Uint8Array; | ||
/** | ||
* Get a UTF-8 encodes buffer as string. | ||
* | ||
* @param {Uint8Array} value | ||
* @return {string} | ||
*/ | ||
export declare const toString: (value: Uint8Array) => string; | ||
/** | ||
* Get a number from a buffer. | ||
* | ||
* @param {Uint8Array} buffer | ||
*/ | ||
export declare const toNumber: (buffer: Uint8Array) => bigint; | ||
/** | ||
* Get a buffer as hexadecimal string. | ||
* | ||
* @param {Uint8Array} buffer | ||
* @return {string} | ||
*/ | ||
export declare const toHex: (buffer: Uint8Array) => string; |
@@ -1,3 +0,2 @@ | ||
/// <reference types="node" /> | ||
export declare const fromTwosComplement: (buffer: Buffer) => bigint; | ||
export declare const toTwosComplement: (value: bigint, length: number) => Buffer; | ||
export declare const fromTwosComplement: (buffer: string | Uint8Array) => bigint; | ||
export declare const toTwosComplement: (value: bigint, length: number) => Uint8Array; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
154864
111
2259
28