Instantly share code, notes, and snippets.

Embed
What would you like to do?
// solidityPack
// Extracted from https://github.com/ethereumjs/ethereumjs-util with TypeScript typings
// Original Copyright (c) 2015 Martin Becze
// LICENSE: MPL 2.0 - https://raw.githubusercontent.com/ethereumjs/ethereumjs-util/master/LICENSE
import {
hexToBuffer,
hexToEvenLengthHex,
isHex,
keccak256,
numberToHex,
setLengthLeft,
setLengthRight,
strip0x
} from './util'
import BN from 'bn.js'
export function solidityPack (types: string[], values: any[]): Buffer {
if (types.length !== values.length) {
throw new Error('Number of types are not matching the values')
}
let size: number
let num: BN
const ret: Buffer[] = []
for (let i = 0; i < types.length; i++) {
const type = elementaryName(types[i])
const value = values[i]
if (type === 'bytes') {
ret.push(toBuffer(value))
} else if (type === 'string') {
ret.push(Buffer.from(value, 'utf8'))
} else if (type === 'bool') {
ret.push(Buffer.from(value ? '01' : '00', 'hex'))
} else if (type === 'address') {
ret.push(setLengthLeft(toBuffer(value), 20))
} else if (type.startsWith('bytes')) {
size = parseTypeN(type)
if (size < 1 || size > 32) {
throw new Error('Invalid bytes<N> width: ' + size)
}
ret.push(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()
)
}
ret.push(num.toArrayLike(Buffer, 'be', size / 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()
)
}
ret.push(num.toTwos(size).toArrayLike(Buffer, 'be', size / 8))
} else {
// FIXME: support all other types
throw new Error('Unsupported or invalid type: ' + type)
}
}
return Buffer.concat(ret)
}
export function soliditySHA3 (types: string[], values: any[]): Buffer {
return keccak256(solidityPack(types, values))
}
function elementaryName (name: string): string {
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
}
// Parse N from type<N>
function parseTypeN (type: string): number {
const match = /^\D+(\d+)$/.exec(type)
return match ? parseInt(match[1], 10) : 0
}
function parseNumber (arg: string | number | BN): BN {
const type = typeof arg
if (type === 'string') {
const str = arg as string
if (str.match(/^0x/i)) {
return new BN(strip0x(str), 16)
} else {
return new BN(arg, 10)
}
} else if (type === 'number') {
return new BN(arg)
} else if (BN.isBN(arg)) {
return arg as BN
} else {
throw new Error('Argument is not a number')
}
}
function toBuffer (
value: string | number[] | number | Buffer | BN | null | undefined
): Buffer {
if (Buffer.isBuffer(value)) {
return value
} else if (value == null) {
return Buffer.alloc(0)
} else if (typeof value === 'string') {
if (isHex(value)) {
return hexToBuffer(value)
} else {
return Buffer.from(value)
}
} else if (typeof value === 'number') {
return hexToBuffer(hexToEvenLengthHex(numberToHex(value)))
} else if (Array.isArray(value)) {
return Buffer.from(value)
} else if (BN.isBN(value)) {
return Buffer.from(value.toArray())
} else {
throw new Error('invalid type')
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment