Last active
April 11, 2018 19:27
-
-
Save petejkim/b3ee2eba0d1fc0d7400d293ee06fd8f6 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 module in Cipher Browser | |
// HardFork Inc. - MPL 2.0 | |
import { | |
hexToBuffer, | |
hexToEvenLengthHex, | |
isHex, | |
keccak256, | |
numberToHex, | |
setLengthLeft, | |
setLengthRight, | |
strip0x | |
} from './util' | |
import BN from 'bn.js' | |
// ported from ethereumjs/ethereumjs-util (MPL 2.0), converted to TypeScript | |
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