Last active
April 12, 2018 17:22
-
-
Save petejkim/9c2db21ecdd4441c72b231ab736eb47c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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