From 5b0d554f7baec54837d795b6568ae5ba8d8a0908 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 15:27:07 -0800 Subject: Moved Abi Encoder into utils package --- packages/order-utils/test/abi_encoder/calldata.ts | 520 ------------ packages/order-utils/test/abi_encoder/data_type.ts | 322 ------- .../order-utils/test/abi_encoder/evm_data_types.ts | 550 ------------ packages/order-utils/test/abi_encoder/index.ts | 2 - packages/order-utils/test/abi_encoder_test.ts | 943 --------------------- packages/order-utils/test/abi_samples.ts | 814 ------------------ packages/utils/src/abi_encoder/calldata.ts | 520 ++++++++++++ packages/utils/src/abi_encoder/data_type.ts | 322 +++++++ packages/utils/src/abi_encoder/evm_data_types.ts | 550 ++++++++++++ packages/utils/src/abi_encoder/index.ts | 2 + packages/utils/src/index.ts | 1 + packages/utils/test/abi_encoder_test.ts | 935 ++++++++++++++++++++ packages/utils/test/abi_samples.ts | 814 ++++++++++++++++++ packages/utils/test/utils/chai_setup.ts | 13 + 14 files changed, 3157 insertions(+), 3151 deletions(-) delete mode 100644 packages/order-utils/test/abi_encoder/calldata.ts delete mode 100644 packages/order-utils/test/abi_encoder/data_type.ts delete mode 100644 packages/order-utils/test/abi_encoder/evm_data_types.ts delete mode 100644 packages/order-utils/test/abi_encoder/index.ts delete mode 100644 packages/order-utils/test/abi_encoder_test.ts delete mode 100644 packages/order-utils/test/abi_samples.ts create mode 100644 packages/utils/src/abi_encoder/calldata.ts create mode 100644 packages/utils/src/abi_encoder/data_type.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types.ts create mode 100644 packages/utils/src/abi_encoder/index.ts create mode 100644 packages/utils/test/abi_encoder_test.ts create mode 100644 packages/utils/test/abi_samples.ts create mode 100644 packages/utils/test/utils/chai_setup.ts diff --git a/packages/order-utils/test/abi_encoder/calldata.ts b/packages/order-utils/test/abi_encoder/calldata.ts deleted file mode 100644 index 32278e5c5..000000000 --- a/packages/order-utils/test/abi_encoder/calldata.ts +++ /dev/null @@ -1,520 +0,0 @@ -import ethUtil = require('ethereumjs-util'); -import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; -var _ = require('lodash'); - -export interface DecodingRules { - structsAsObjects: boolean; -} - -export interface EncodingRules { - optimize?: boolean; - annotate?: boolean; -} - -export abstract class CalldataBlock { - private name: string; - private signature: string; - private offsetInBytes: number; - private headerSizeInBytes: number; - private bodySizeInBytes: number; - private relocatable: boolean; - private parentName: string; - - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { - this.name = name; - this.signature = signature; - this.parentName = parentName; - this.offsetInBytes = 0; - this.headerSizeInBytes = headerSizeInBytes; - this.bodySizeInBytes = bodySizeInBytes; - this.relocatable = relocatable; - } - - protected setHeaderSize(headerSizeInBytes: number) { - this.headerSizeInBytes = headerSizeInBytes; - } - - protected setBodySize(bodySizeInBytes: number) { - this.bodySizeInBytes = bodySizeInBytes; - } - - protected setName(name: string) { - this.name = name; - } - - public getName(): string { - return this.name; - } - - public getParentName(): string { - return this.parentName; - } - - public getSignature(): string { - return this.signature; - } - - public isRelocatable(): boolean { - return this.relocatable; - } - - public getHeaderSizeInBytes(): number { - return this.headerSizeInBytes; - } - - public getBodySizeInBytes(): number { - return this.bodySizeInBytes; - } - - public getSizeInBytes(): number { - return this.headerSizeInBytes + this.bodySizeInBytes; - } - - public getOffsetInBytes(): number { - return this.offsetInBytes; - } - - public setOffset(offsetInBytes: number) { - this.offsetInBytes = offsetInBytes; - } - - public computeHash(): Buffer { - const rawData = this.getRawData(); - const hash = ethUtil.sha3(rawData); - return hash; - } - - public abstract toBuffer(): Buffer; - public abstract getRawData(): Buffer; -} - -export class PayloadCalldataBlock extends CalldataBlock { - private payload: Buffer; - - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { - const headerSizeInBytes = 0; - const bodySizeInBytes = payload.byteLength; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.payload = payload; - } - - public toBuffer(): Buffer { - return this.payload; - } - - public getRawData(): Buffer { - return this.payload; - } -} - -export class DependentCalldataBlock extends CalldataBlock { - public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - public static RAW_DATA_START = new Buffer('<'); - public static RAW_DATA_END = new Buffer('>'); - private parent: CalldataBlock; - private dependency: CalldataBlock; - private aliasFor: CalldataBlock | undefined; - - constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { - const headerSizeInBytes = 0; - const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.parent = parent; - this.dependency = dependency; - this.aliasFor = undefined; - } - - public toBuffer(): Buffer { - const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); - const parentOffset = this.parent.getOffsetInBytes(); - const parentHeaderSize = this.parent.getHeaderSizeInBytes(); - const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); - const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); - const evmWordWidthInBytes = 32; - const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); - return pointerBufPadded; - } - - public getDependency(): CalldataBlock { - return this.dependency; - } - - public setAlias(block: CalldataBlock) { - this.aliasFor = block; - this.setName(`${this.getName()} (alias for ${block.getName()})`); - } - - public getAlias(): CalldataBlock | undefined { - return this.aliasFor; - } - - public getRawData(): Buffer { - const dependencyRawData = this.dependency.getRawData(); - const rawDataComponents: Buffer[] = []; - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); - rawDataComponents.push(dependencyRawData); - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } -} - -export class MemberCalldataBlock extends CalldataBlock { - private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - private header: Buffer | undefined; - private members: CalldataBlock[]; - private contiguous: boolean; - - constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { - super(name, signature, parentName, 0, 0, relocatable); - this.members = []; - this.header = undefined; - this.contiguous = contiguous; - } - - public getRawData(): Buffer { - const rawDataComponents: Buffer[] = []; - if (this.header !== undefined) { - rawDataComponents.push(this.header); - } - _.each(this.members, (member: CalldataBlock) => { - const memberBuffer = member.getRawData(); - rawDataComponents.push(memberBuffer); - }); - - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } - - public setMembers(members: CalldataBlock[]) { - let bodySizeInBytes = 0; - _.each(members, (member: CalldataBlock) => { - bodySizeInBytes += member.getSizeInBytes(); - }); - this.members = members; - this.setBodySize(0); - } - - public isContiguous(): boolean { - return true; - } - - public setHeader(header: Buffer) { - this.setHeaderSize(header.byteLength); - this.header = header; - } - - public toBuffer(): Buffer { - if (this.header !== undefined) return this.header; - return new Buffer(''); - } - - public getMembers(): CalldataBlock[] { - return this.members; - } -} - -class Queue { - private store: T[] = []; - push(val: T) { - this.store.push(val); - } - pushFront(val: T) { - this.store.unshift(val); - } - pop(): T | undefined { - return this.store.shift(); - } - popBack(): T | undefined { - if (this.store.length === 0) return undefined; - const backElement = this.store.splice(-1, 1)[0]; - return backElement; - } - merge(q: Queue) { - this.store = this.store.concat(q.store); - } - mergeFront(q: Queue) { - this.store = q.store.concat(this.store); - } - getStore(): T[] { - return this.store; - } - peek(): T | undefined { - return this.store.length >= 0 ? this.store[0] : undefined; - } -} - -export class Calldata { - private selector: string; - private rules: EncodingRules; - private sizeInBytes: number; - private root: MemberCalldataBlock | undefined; - - constructor(rules: EncodingRules) { - this.selector = ''; - this.rules = rules; - this.sizeInBytes = 0; - this.root = undefined; - } - - private createQueue(memberBlock: MemberCalldataBlock): Queue { - const blockQueue = new Queue(); - _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof MemberCalldataBlock) { - blockQueue.mergeFront(this.createQueue(member)); - } else { - blockQueue.pushFront(member); - } - }); - - // Children - _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { - let dependency = member.getDependency(); - if (dependency instanceof MemberCalldataBlock) { - blockQueue.merge(this.createQueue(dependency)); - } else { - blockQueue.push(dependency); - } - } - }); - - blockQueue.pushFront(memberBlock); - return blockQueue; - } - - private generateAnnotatedHexString(): string { - let hexValue = `${this.selector}`; - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - - let block: CalldataBlock | undefined; - let offset = 0; - const functionBlock = valueQueue.peek(); - let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - while ((block = valueQueue.pop()) !== undefined) { - // Set f - - // Process each block 1 word at a time - const size = block.getSizeInBytes(); - const name = block.getName(); - const parentName = block.getParentName(); - - //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; - //const parentOffset = name.lastIndexOf(parentName); - const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); - const signature = block.getSignature(); - - // Current offset - let offsetStr = ''; - - // If this block is empty then it's a newline - let value = ''; - let nameStr = ''; - let line = ''; - if (size === 0) { - offsetStr = ' '.repeat(10); - value = ' '.repeat(74); - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); - if (block instanceof MemberCalldataBlock) { - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - nameStr = ` ${prettyName.padEnd(80)}`; - line = `${offsetStr}${value}${nameStr}`; - } - } - - for (let j = 32; j < size; j += 32) { - offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); - nameStr = ' '.repeat(40); - - line = `${line}\n${offsetStr}${value}${nameStr}`; - } - - // Append to hex value - hexValue = `${hexValue}\n${line}`; - offset += size; - } - - return hexValue; - } - - private generateCondensedHexString(): string { - let selectorBuffer = ethUtil.toBuffer(this.selector); - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - const valueBufs: Buffer[] = [selectorBuffer]; - let block: CalldataBlock | undefined; - while ((block = valueQueue.pop()) !== undefined) { - valueBufs.push(block.toBuffer()); - } - - const combinedBuffers = Buffer.concat(valueBufs); - const hexValue = ethUtil.bufferToHex(combinedBuffers); - return hexValue; - } - - public optimize() { - if (this.root === undefined) { - throw new Error('expected root'); - } - - const blocksByHash: { [key: string]: CalldataBlock } = {}; - - // 1. Create a queue of subtrees by hash - // Note that they are ordered the same as - const subtreeQueue = this.createQueue(this.root); - let block: CalldataBlock | undefined; - while ((block = subtreeQueue.popBack()) !== undefined) { - if (block instanceof DependentCalldataBlock) { - const blockHashBuf = block.getDependency().computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash) { - const blockWithSameHash = blocksByHash[blockHash]; - if (blockWithSameHash !== block.getDependency()) { - block.setAlias(blockWithSameHash); - } - } - continue; - } - - const blockHashBuf = block.computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash === false) { - blocksByHash[blockHash] = block; - } - } - } - - public toHexString(): string { - if (this.root === undefined) { - throw new Error('expected root'); - } - - if (this.rules.optimize) this.optimize(); - - const offsetQueue = this.createQueue(this.root); - let block: CalldataBlock | undefined; - let offset = 0; - while ((block = offsetQueue.pop()) !== undefined) { - block.setOffset(offset); - offset += block.getSizeInBytes(); - } - - const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); - return hexValue; - } - - public getSelectorHex(): string { - return this.selector; - } - - public getSizeInBytes(): number { - return this.sizeInBytes; - } - - public toAnnotatedString(): string { - return ""; - } - - public setRoot(block: MemberCalldataBlock) { - this.root = block; - this.sizeInBytes += block.getSizeInBytes(); - } - - public setSelector(selector: string) { - // Ensure we have a 0x prefix - if (selector.startsWith('0x')) { - this.selector = selector; - } else { - this.selector = `$0x${selector}`; - } - - // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) - if (this.selector.length !== 10) { - throw new Error(`Invalid selector '${this.selector}'`); - } - this.sizeInBytes += 8; - } -} - -export class RawCalldata { - private value: Buffer; - private offset: number; // tracks current offset into raw calldata; used for parsing - private selector: string; - private scopes: Queue; - - constructor(value: string | Buffer) { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Expected raw calldata to start with '0x'`); - } - const valueBuf = ethUtil.toBuffer(value); - this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); - this.value = valueBuf.slice(4); // disregard selector - this.offset = 0; - this.scopes = new Queue(); - this.scopes.push(0); - } - - public popBytes(lengthInBytes: number): Buffer { - const value = this.value.slice(this.offset, this.offset + lengthInBytes); - this.setOffset(this.offset + lengthInBytes); - return value; - } - - public popWord(): Buffer { - const wordInBytes = 32; - return this.popBytes(wordInBytes); - } - - public popWords(length: number): Buffer { - const wordInBytes = 32; - return this.popBytes(length * wordInBytes); - } - - public readBytes(from: number, to: number): Buffer { - const value = this.value.slice(from, to); - return value; - } - - public setOffset(offsetInBytes: number) { - this.offset = offsetInBytes; - } - - public startScope() { - this.scopes.pushFront(this.offset); - } - - public endScope() { - this.scopes.pop(); - } - - public getOffset(): number { - return this.offset; - } - - public toAbsoluteOffset(relativeOffset: number) { - const scopeOffset = this.scopes.peek(); - if (scopeOffset === undefined) { - throw new Error(`Tried to access undefined scope.`); - } - const absoluteOffset = relativeOffset + scopeOffset; - return absoluteOffset; - } - - public getSelector(): string { - return this.selector; - } -} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder/data_type.ts b/packages/order-utils/test/abi_encoder/data_type.ts deleted file mode 100644 index 4c4537a69..000000000 --- a/packages/order-utils/test/abi_encoder/data_type.ts +++ /dev/null @@ -1,322 +0,0 @@ -import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; -import { MethodAbi, DataItem } from 'ethereum-types'; -import { DecodingRules, EncodingRules } from './calldata'; -import { BigNumber } from '@0x/utils'; -import ethUtil = require('ethereumjs-util'); -var _ = require('lodash'); - -export interface DataTypeFactory { - create: (dataItem: DataItem, parentDataType: DataType) => DataType; - mapDataItemToDataType: (dataItem: DataItem) => DataType; -} - -export abstract class DataType { - private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; - private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; - - private dataItem: DataItem; - private factory: DataTypeFactory; - - constructor(dataItem: DataItem, factory: DataTypeFactory) { - this.dataItem = dataItem; - this.factory = factory; - } - - public getDataItem(): DataItem { - return this.dataItem; - } - - public getFactory(): DataTypeFactory { - return this.factory; - } - - public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; - const calldata = new Calldata(rules_); - if (selector) calldata.setSelector(selector); - const block = this.generateCalldataBlock(value); - calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE - const calldataHex = calldata.toHexString(); - return calldataHex; - } - - public decode(calldata: string, rules?: DecodingRules): any { - const rawCalldata = new RawCalldata(calldata); - const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; - const value = this.generateValue(rawCalldata, rules_); - return value; - } - - public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; - public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; - public abstract getSignature(): string; - public abstract isStatic(): boolean; -} - -export abstract class PayloadDataType extends DataType { - protected hasConstantSize: boolean; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { - super(dataItem, factory); - this.hasConstantSize = hasConstantSize; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { - const encodedValue = this.encodeValue(value); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; - const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const value = this.decodeValue(calldata); - return value; - } - - public isStatic(): boolean { - // If a payload has a constant size then it's static - return this.hasConstantSize; - } - - public abstract encodeValue(value: any): Buffer; - public abstract decodeValue(calldata: RawCalldata): any; -} - -export abstract class DependentDataType extends DataType { - protected dependency: DataType; - protected parent: DataType; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { - super(dataItem, factory); - this.dependency = dependency; - this.parent = parent; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { - if (parentBlock === undefined) { - throw new Error(`DependentDataType requires a parent block to generate its block`); - } - const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; - const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const destinationOffsetBuf = calldata.popWord(); - const currentOffset = calldata.getOffset(); - const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); - const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); - calldata.setOffset(destinationOffsetAbsolute); - const value = this.dependency.generateValue(calldata, rules); - calldata.setOffset(currentOffset); - return value; - } - - public isStatic(): boolean { - return true; - } -} - -export interface MemberMap { - [key: string]: number; -} - -export abstract class MemberDataType extends DataType { - private memberMap: MemberMap; - private members: DataType[]; - private isArray: boolean; - protected arrayLength: number | undefined; - protected arrayElementType: string | undefined; - - - public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { - super(dataItem, factory); - this.memberMap = {}; - this.members = []; - this.isArray = isArray; - this.arrayLength = arrayLength; - this.arrayElementType = arrayElementType; - if (isArray && arrayLength !== undefined) { - [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); - } else if (!isArray) { - [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); - } - } - - private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { - // Sanity check - if (dataItem.components === undefined) { - throw new Error(`Expected components`); - } - - let members: DataType[] = []; - let memberMap: MemberMap = {}; - _.each(dataItem.components, (memberItem: DataItem) => { - const childDataItem = { - type: memberItem.type, - name: `${dataItem.name}.${memberItem.name}`, - } as DataItem; - const components = memberItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[memberItem.name] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } - - private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - let members: DataType[] = []; - let memberMap: MemberMap = {}; - const range = _.range(length); - _.each(range, (idx: number) => { - const childDataItem = { - type: this.arrayElementType, - name: `${dataItem.name}[${idx.toString(10)}]`, - } as DataItem; - const components = dataItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[idx.toString(10)] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } - - protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { - // Sanity check length - if (this.arrayLength !== undefined && value.length !== this.arrayLength) { - throw new Error( - `Expected array of ${JSON.stringify( - this.arrayLength, - )} elements, but got array of length ${JSON.stringify(value.length)}`, - ); - } - - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); - - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - [members,] = this.createMembersWithLength(this.getDataItem(), value.length); - - const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); - methodBlock.setHeader(lenBuf); - } - - const memberBlocks: CalldataBlock[] = []; - _.each(members, (member: DataType, idx: number) => { - const block = member.generateCalldataBlock(value[idx], methodBlock); - memberBlocks.push(block); - }); - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); - const memberBlocks: CalldataBlock[] = []; - let childMap = _.cloneDeep(this.memberMap); - _.forOwn(obj, (value: any, key: string) => { - if (key in childMap === false) { - throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); - } - const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); - memberBlocks.push(block); - delete childMap[key]; - }); - - if (Object.keys(childMap).length !== 0) { - throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); - } - - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - const arrayLengthBuf = calldata.popWord(); - const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - - [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); - } - - calldata.startScope(); - let value: any[] | object; - if (rules.structsAsObjects && !this.isArray) { - value = {}; - _.each(this.memberMap, (idx: number, key: string) => { - const member = this.members[idx]; - let memberValue = member.generateValue(calldata, rules); - (value as { [key: string]: any })[key] = memberValue; - }); - } else { - value = []; - _.each(members, (member: DataType, idx: number) => { - let memberValue = member.generateValue(calldata, rules); - (value as any[]).push(memberValue); - }); - } - calldata.endScope(); - return value; - } - - protected computeSignatureOfMembers(): string { - // Compute signature of members - let signature = `(`; - _.each(this.members, (member: DataType, i: number) => { - signature += member.getSignature(); - if (i < this.members.length - 1) { - signature += ','; - } - }); - signature += ')'; - return signature; - } - - public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false - - Otherwise if the first element is a Pointer then false - */ - - if (this.isArray && this.arrayLength === undefined) { - return false; - } - - // Search for dependent members - const dependentMember = _.find(this.members, (member: DataType) => { - return (member instanceof DependentDataType); - }); - const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member - return isStatic; - } -} diff --git a/packages/order-utils/test/abi_encoder/evm_data_types.ts b/packages/order-utils/test/abi_encoder/evm_data_types.ts deleted file mode 100644 index bb7c1d81d..000000000 --- a/packages/order-utils/test/abi_encoder/evm_data_types.ts +++ /dev/null @@ -1,550 +0,0 @@ -import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; - -import { DecodingRules, EncodingRules } from './calldata'; - -import { MethodAbi, DataItem } from 'ethereum-types'; - -import ethUtil = require('ethereumjs-util'); - -import { Calldata, RawCalldata } from './calldata'; - -import { BigNumber } from '@0x/utils'; - -var _ = require('lodash'); - -export interface DataTypeStaticInterface { - matchGrammar: (type: string) => boolean; - encodeValue: (value: any) => Buffer; - decodeValue: (rawCalldata: RawCalldata) => any; -} - -export class Address extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Address.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'address'; - } - - public static matchGrammar(type: string): boolean { - return type === 'address'; - } - - public encodeValue(value: boolean): Buffer { - const evmWordWidth = 32; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(12); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } -} - -export class Bool extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bool.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'bool'; - } - - public static matchGrammar(type: string): boolean { - return type === 'bool'; - } - - public encodeValue(value: boolean): Buffer { - const evmWordWidth = 32; - const encodedValue = value === true ? '0x1' : '0x0'; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): boolean { - const valueBuf = calldata.popWord(); - const valueHex = ethUtil.bufferToHex(valueBuf); - const valueNumber = new BigNumber(valueHex, 16); - let value: boolean = (valueNumber.equals(0)) ? false : true; - if (!(valueNumber.equals(0) || valueNumber.equals(1))) { - throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); - } - return value; - } -} - -abstract class Number extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static MAX_WIDTH: number = 256; - static DEFAULT_WIDTH: number = Number.MAX_WIDTH; - width: number = Number.DEFAULT_WIDTH; - - constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); - const matches = matcher.exec(dataItem.type); - if (matches === null) { - throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); - } - if (matches !== null && matches.length === 2 && matches[1] !== undefined) { - this.width = parseInt(matches[1]); - } else { - this.width = 256; - } - } - - public encodeValue(value: BigNumber): Buffer { - if (value.greaterThan(this.getMaxValue())) { - throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; - } else if (value.lessThan(this.getMinValue())) { - throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; - } - - const hexBase = 16; - const evmWordWidth = 32; - let valueBuf: Buffer; - if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); - } else { - // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. - // Step 1/3: Convert value to positive binary string - const binBase = 2; - const valueBin = value.times(-1).toString(binBase); - - // Step 2/3: Invert binary value - const bitsInEvmWord = 256; - let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 3/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const negativeValue = invertedValue.plus(1); - - // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), - evmWordWidth, - ); - } - - return valueBuf; - } - - public decodeValue(calldata: RawCalldata): BigNumber { - const paddedValueBuf = calldata.popWord(); - const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); - let value = new BigNumber(paddedValueHex, 16); - if (this instanceof Int) { - // Check if we're negative - const binBase = 2; - const paddedValueBin = value.toString(binBase); - const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); - if (valueBin[0].startsWith('1')) { - // Negative - // Step 1/3: Invert binary value - let invertedValueBin = ''; - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 2/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const positiveValue = invertedValue.plus(1); - - // Step 3/3: Invert positive value - const negativeValue = positiveValue.times(-1); - value = negativeValue; - } - } - - return value; - } - - public abstract getMaxValue(): BigNumber; - public abstract getMinValue(): BigNumber; -} - -export class Int extends Number { - static matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, Int.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).times(-1); - } - - public getSignature(): string { - return `int${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class UInt extends Number { - static matcher = RegExp( - '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, UInt.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(0); - } - - public getSignature(): string { - return `uint${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Byte extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static matcher = RegExp( - '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', - ); - - static DEFAULT_WIDTH = 1; - width: number = Byte.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); - const matches = Byte.matcher.exec(dataItem.type); - if (!Byte.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); - } - if (matches !== null && matches.length === 3 && matches[2] !== undefined) { - this.width = parseInt(matches[2]); - } else { - this.width = Byte.DEFAULT_WIDTH; - } - } - - public getSignature(): string { - // Note that `byte` reduces to `bytes1` - return `bytes${this.width}`; - } - - public encodeValue(value: string | Buffer): Buffer { - // Convert value into a buffer and do bounds checking - const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength > this.width) { - throw new Error( - `Tried to assign ${value} (${ - valueBuf.byteLength - } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, - ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - return paddedValue; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(0, this.width); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Bytes extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = Bytes.UNDEFINED_LENGTH; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bytes.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string | Buffer): Buffer { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); - } - const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / 32); - const paddedBytesForValue = wordsForValue * 32; - const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const decodedValue = ethUtil.bufferToHex(valueBuf); - return decodedValue; - } - - public getSignature(): string { - return 'bytes'; - } - - public static matchGrammar(type: string): boolean { - return type === 'bytes'; - } -} - -export class SolString extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); - if (!SolString.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string): Buffer { - const wordsForValue = Math.ceil(value.length / 32); - const paddedBytesForValue = wordsForValue * 32; - const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const value = valueBuf.toString('ascii'); - return value; - } - - public getSignature(): string { - return 'string'; - } - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } -} - -export class Pointer extends DependentDataType { - - constructor(destDataType: DataType, parentDataType: DataType) { - const destDataItem = destDataType.getDataItem(); - const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; - super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); - } - - public getSignature(): string { - return this.dependency.getSignature(); - } -} - -export class Tuple extends MemberDataType { - private tupleSignature: string; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance()); - if (!Tuple.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); - } - this.tupleSignature = this.computeSignatureOfMembers(); - } - - public getSignature(): string { - return this.tupleSignature; - } - - public static matchGrammar(type: string): boolean { - return type === 'tuple'; - } -} - -export class SolArray extends MemberDataType { - static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); - private arraySignature: string; - private elementType: string; - - constructor(dataItem: DataItem) { - // Sanity check - const matches = SolArray.matcher.exec(dataItem.type); - if (matches === null || matches.length !== 3) { - throw new Error(`Could not parse array: ${dataItem.type}`); - } else if (matches[1] === undefined) { - throw new Error(`Could not parse array type: ${dataItem.type}`); - } else if (matches[2] === undefined) { - throw new Error(`Could not parse array length: ${dataItem.type}`); - } - - const isArray = true; - const arrayElementType = matches[1]; - const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); - super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); - this.elementType = arrayElementType; - this.arraySignature = this.computeSignature(); - } - - private computeSignature(): string { - let dataItem = { - type: this.elementType, - name: 'N/A', - } as DataItem; - const components = this.getDataItem().components; - if (components !== undefined) { - dataItem.components = components; - } - const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); - const type = elementDataType.getSignature(); - if (this.arrayLength === undefined) { - return `${type}[]`; - } else { - return `${type}[${this.arrayLength}]`; - } - } - - public getSignature(): string { - return this.arraySignature; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Method extends MemberDataType { - private methodSignature: string; - private methodSelector: string; - - // TMP - public selector: string; - - constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); - this.methodSignature = this.computeSignature(); - this.selector = this.methodSelector = this.computeSelector(); - - } - - private computeSignature(): string { - const memberSignature = this.computeSignatureOfMembers(); - const methodSignature = `${this.getDataItem().name}${memberSignature}`; - return methodSignature; - } - - private computeSelector(): string { - const signature = this.computeSignature(); - const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); - return selector; - } - - public encode(value: any, rules?: EncodingRules): string { - const calldata = super.encode(value, rules, this.selector); - return calldata; - } - - public decode(calldata: string, rules?: DecodingRules): any[] | object { - if (!calldata.startsWith(this.selector)) { - throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); - } - const value = super.decode(calldata, rules); - return value; - } - - public getSignature(): string { - return this.methodSignature; - } - - public getSelector(): string { - return this.methodSelector; - } -} - -export class EvmDataTypeFactory implements DataTypeFactory { - private static instance: DataTypeFactory; - - private constructor() { } - - public static getInstance(): DataTypeFactory { - if (!EvmDataTypeFactory.instance) { - EvmDataTypeFactory.instance = new EvmDataTypeFactory(); - } - return EvmDataTypeFactory.instance; - } - - public mapDataItemToDataType(dataItem: DataItem): DataType { - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); - if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); - if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); - if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); - if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); - if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); - if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); - if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); - //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); - //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); - - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public create(dataItem: DataItem, parentDataType: DataType): DataType { - const dataType = this.mapDataItemToDataType(dataItem); - if (dataType.isStatic()) { - return dataType; - } - - const pointer = new Pointer(dataType, parentDataType); - return pointer; - } -} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder/index.ts b/packages/order-utils/test/abi_encoder/index.ts deleted file mode 100644 index 991edb8c5..000000000 --- a/packages/order-utils/test/abi_encoder/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { EncodingRules, DecodingRules } from './calldata'; -export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts deleted file mode 100644 index f1e562bc6..000000000 --- a/packages/order-utils/test/abi_encoder_test.ts +++ /dev/null @@ -1,943 +0,0 @@ -import * as chai from 'chai'; -import 'mocha'; -import ethUtil = require('ethereumjs-util'); - -var _ = require('lodash'); - -// import { assert } from '@0x/order-utils/src/assert'; - -import { chaiSetup } from './utils/chai_setup'; - -import { MethodAbi, DataItem } from 'ethereum-types'; - -import { BigNumber } from '@0x/utils'; -import { assert } from '@0x/order-utils/src/assert'; -//import * as AbiEncoder from './abi_encoder'; - -import * as AbiEncoder from './abi_encoder'; - -import * as AbiSamples from './abi_samples'; - -chaiSetup.configure(); -const expect = chai.expect; - -describe.only('ABI Encoder', () => { - describe.only('Optimizer', () => { - - }); - - describe.only('ABI Tests at Method Level', () => { - - it('Should reuse duplicated strings in string array', async () => { - const method = new AbiEncoder.Method(AbiSamples.stringAbi); - const strings = [ - "Test String", - "Test String 2", - "Test String", - "Test String 2", - ]; - const args = [strings]; - - // Verify optimized calldata is expected - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; - //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - - // Verify args decode properly - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); - }); - - it('Should point array elements to a duplicated value from another parameter', async () => { - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); - const stringArray = [ - "Test String", - "Test String", - "Test String", - "Test String 2", - ]; - const string = 'Test String'; - const args = [stringArray, string]; - - // Verify optimized calldata is expected - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - - // Verify args decode properly - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); - }); - - - it('Optimizer #3 (tuple should point to array)', async () => { - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi3); - const uint8Array = [ - new BigNumber(100), - new BigNumber(150), - new BigNumber(200), - new BigNumber(225), - ]; - const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; - const args = [uint8Array, uintTupleArray]; - - - const TEST = method.encode(args, { optimize: true, annotate: true }); - console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - - const optimizedCalldata = method.encode(args, { optimize: true }); - - console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - console.log(JSON.stringify(decodedArgs)); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - - it('Optimizer #4 (Expect no optimization)', async () => { - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); - const uint8Array = [ - new BigNumber(100), - new BigNumber(150), - new BigNumber(200), - new BigNumber(225), - ]; - const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; - const args = [uint8Array, uintTupleArray]; - - - const TEST = method.encode(args, { optimize: true, annotate: true }); - console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - - const optimizedCalldata = method.encode(args, { optimize: true }); - - console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - console.log(JSON.stringify(decodedArgs)); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - - it('Crazy ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.crazyAbi); - console.log(method.getSignature()); - - const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; - const someStaticArrayWithDynamicMembers = [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ]; - const someDynamicArrayWithDynamicMembers = [ - '0x38745637834987324827439287423897238947239847', - '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', - '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', - ]; - const some2DArray = [ - [ - 'some string', - 'some another string', - 'there are just too many stringsup in', - 'here', - 'yall ghonna make me lose my mind', - ], - [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], - [], - ]; - const someTuple = { - someUint32: new BigNumber(4037824789), - someStr: 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.' - }; - const someTupleWithDynamicTypes = { - someUint: new BigNumber(4024789), - someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', - someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', - }; - const someTupleWithDynamicTypes2 = { - someUint: new BigNumber(9024789), - someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', - someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', - someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', - }; - const someTupleWithDynamicTypes3 = { - someUint: new BigNumber(1024789), - someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', - someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', - someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', - }; - const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; - - const args = { - someStaticArray: someStaticArray, - someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, - someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, - some2DArray: some2DArray, - someTuple: someTuple, - someTupleWithDynamicTypes: someTupleWithDynamicTypes, - someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes - }; - - const calldata = method.encode(args); - console.log(calldata); - - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - console.log(method.getSignature()); - - const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; - //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata, { structsAsObjects: true }); - const decodedValueJson = JSON.stringify(decodedValue); - console.log(`DECODED`, '*'.repeat(200), '\n', decodedValueJson); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Crazy ABI #1', async () => { - const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); - - const args = [ - new BigNumber(256745454), - new BigNumber(-256745454), - new BigNumber(434244), - '0x43', - '0x0001020304050607080911121314151617181920212223242526272829303132', - '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', - 'Little peter piper piped a piping pepper pot', - '0xe41d2489571d322189246dafa5ebde1f4699f498', - true - ]; - - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - - it('Types with default widths', async () => { - const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); - console.log(method); - const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Static Tuples (Array has defined length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Static Tuples (Array has dynamic length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Dynamic Tuples (Array has defined length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Dynamic Tuples (Array has dynamic length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Multidimensional Arrays / Static Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); - - // Eight 3-dimensional arrays of uint8[2][2][2] - let value = 0; - const args = []; - for (let i = 0; i < 8; ++i) { - args.push( - [ - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ], - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ] - ] - ); - } - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Multidimensional Arrays / Dynamic Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); - - // Eight 3-dimensional arrays of string[2][2][2] - let value = 0; - const args = []; - for (let i = 0; i < 4; ++i) { - args.push( - [ - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ], - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ] - ] - ); - } - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Fixed Lenfgth Array / Dynamic Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - const expectedCalldata = - '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Fixed Lenfgth Array / Dynamic Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - const expectedCalldata = - '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Unfixed Length Array / Dynamic Members ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Unfixed Length Array / Static Members ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); - const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - - it('Fixed Length Array / Static Members ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); - const expectedCalldata = - '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - - it('Simple ABI 2', async () => { - const method = new AbiEncoder.Method(AbiSamples.simpleAbi2); - - const args = [ - '0xaf', // e (bytes1) - '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) - '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g - 'My first name is Greg and my last name is Hysen, what do ya know!', // h - ]; - - const calldata = method.encode(args); - const expectedCalldata = - '0x7ac2bd96af000000000000000000000000000000000000000000000000000000000000000001020304050607080911121314151617181920212223242526272829303132000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.stringAbi); - console.log(method); - const args = [['five', 'six', 'seven']]; - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Static Tuple', async () => { - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); - const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Dynamic Tuple (Array input)', async () => { - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const args = [[new BigNumber(5), 'five']]; - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Dynamic Tuple (Object input)', async () => { - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - }); - - it.skip('Nested Tuples', async () => { - // Couldn't get nested tuples to work with Remix - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.nestedTuples); - const firstTuple = { - someUint32: new BigNumber(30472), - nestedTuple: { - someUint: new BigNumber('48384725243211555532'), - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' - } - }; - const secondTuple = { - someUint: new BigNumber(2984237422), - someStr: 'This string will exceed 256 bits, so it will spill into the next word of memory.', - nestedTuple: { - someUint32: new BigNumber(23), - secondNestedTuple: { - someUint: new BigNumber(234324), - someStr: 'Im also a short string -- oops I just got loooooooooooooooooonger!', - someBytes: '0x23847287fff3472984723498ff23487324987aaa237438911873429472ba', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' - } - }, - someBytes: '0x2834y3947289423u489aaaff4783924739847489', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699afaf', - }; - const thirdTuple = { - 'someUint': new BigNumber(37422), - 'someStr': 'This into the next word of memory. string will exceed 256 bits, so it will spill.', - 'nestedTuple': { - someUint32: new BigNumber(23999222), - 'secondNestedTuple': { - 'someUint': new BigNumber(324), - 'someStr': 'Im also a short st', - 'someBytes': '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', - 'someAddress': '0x46dafa5ebde1f4699f498e41d2489571d3221892' - } - }, - 'someBytes': '0x947289423u489aaaff472834y383924739847489', - 'someAddress': '0x46dafa5ebde1f46e41d2489571d322189299afaf', - }; - const fourthTuple = { - 'someUint': new BigNumber(222283488822), - 'someStr': 'exceed 256 bits, so it will spill into the. This string will next word of memory.', - 'nestedTuple': { - someUint32: new BigNumber(2300), - 'secondNestedTuple': { - 'someUint': new BigNumber(343224), - 'someStr': 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', - 'someBytes': '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', - 'someAddress': '0x71d322189246dafa5ebe41d24895de1f4699f498' - } - }, - 'someBytes': '0x2783924739847488343947289423u489aaaff490', - 'someAddress': '0xebde1d322189246dafa1f4699afafe41d2489575', - }; - const args = [ - [firstTuple], - [secondTuple, thirdTuple, fourthTuple] - ]; - - console.log('*'.repeat(250), method, '*'.repeat(250)); - - - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - console.log(JSON.stringify(args)); - - console.log(calldata); - const expectedCalldata = '0x'; - expect(calldata).to.be.equal(expectedCalldata); - }); - - it.skip('Object ABI (Object input - Missing Key)', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5) }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - // @TODO: Figure out how to catch throw - expect(calldata).to.be.equal(expectedCalldata); - }); - - it.skip('Object ABI (Object input - Too Many Keys)', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - // @TODO: Figure out how to catch throw - expect(calldata).to.be.equal(expectedCalldata); - }); - }); - /* - describe('Array', () => { - it('sample', async () => { - const testDataItem = { name: 'testArray', type: 'int[2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample undefined size', async () => { - const testDataItem = { name: 'testArray', type: 'int[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample dynamic types', async () => { - const testDataItem = { name: 'testArray', type: 'string[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue(['five', 'six', 'seven']); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - const calldata = new AbiEncoder.Calldata('0x01020304', 1); - dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); - console.log('*'.repeat(60)); - console.log(calldata.getHexValue()); - }); - }); - - describe('Address', () => { - const testAddressDataItem = { name: 'testAddress', type: 'address' }; - it('Valid Address', async () => { - const addressDataType = new AbiEncoder.Address(testAddressDataItem); - addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); - const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - - console.log(addressDataType.getHexValue()); - console.log(expectedAbiEncodedAddress); - expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); - }); - }); - - describe('Bool', () => { - const testBoolDataItem = { name: 'testBool', type: 'bool' }; - it('True', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(true); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - - it('False', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(false); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - }); - - describe('Integer', () => { - const testIntDataItem = { name: 'testInt', type: 'int' }; - it('Positive - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Positive', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-1)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-437829473)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Unsigned Integer', () => { - const testIntDataItem = { name: 'testUInt', type: 'uint' }; - it('Lower Bound', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(0)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Base Case', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Random value', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Static Bytes', () => { - it('Byte (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Byte (no padding)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect(byteDataType.assignValue('0x5')).to.throw(); - }); - - it('Bytes1', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); - const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (unpadded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x1a18bf61'); - const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Bytes32 - Too long', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect( - byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), - ).to.throw( - `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, - ); - }); - }); - - describe('Bytes (Dynamic)', () => { - const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; - it('Less than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - bytesDataType.assignValue('0x010203'); - const expectedAbiEncodedBytes = - '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; - - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - it('Greater than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - const testValue = '0x' + '61'.repeat(40); - bytesDataType.assignValue(testValue); - const expectedAbiEncodedBytes = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - // @TODO: Add test for throw on half-byte - // @TODO: Test with no 0x prefix - // @TODO: Test with Buffer as input - }); - - describe('String', () => { - const testStringDataItem = { name: 'testString', type: 'string' }; - it('Less than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - stringDataType.assignValue('five'); - const expectedAbiEncodedString = - '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - console.log(stringDataType.getHexValue()); - console.log(expectedAbiEncodedString); - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); - - it('Greater than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - const testValue = 'a'.repeat(40); - stringDataType.assignValue(testValue); - const expectedAbiEncodedString = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); - });*/ -}); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts deleted file mode 100644 index 5e8268f1a..000000000 --- a/packages/order-utils/test/abi_samples.ts +++ /dev/null @@ -1,814 +0,0 @@ -import { MethodAbi } from 'ethereum-types'; - -export const simpleAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'uint256', - }, - { - name: 'gregStr', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const stringAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'string[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi2 = { - constant: false, - inputs: [ - { - name: 'stringArray', - type: 'string[]', - }, - { - name: 'string', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi3 = { - constant: false, - inputs: [ - { - name: 'uint8Array', - type: 'uint8[]', - }, - { - components: [ - { - name: 'uint', - type: 'uint', - }, - ], - name: 'uintTuple', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi4 = { - constant: false, - inputs: [ - { - name: 'uint8Array', - type: 'uint8[4]', - }, - { - components: [ - { - name: 'uint', - type: 'uint', - }, - ], - name: 'uintTuple', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const typesWithDefaultWidthsAbi = { - constant: false, - inputs: [ - { - name: 'someUint', - type: 'uint', - }, - { - name: 'someInt', - type: 'int', - }, - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someUint', - type: 'uint[]', - }, - { - name: 'someInt', - type: 'int[]', - }, - { - name: 'someByte', - type: 'byte[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const multiDimensionalArraysStaticTypeAbi = { - constant: false, - inputs: [ - { - name: 'a', - type: 'uint8[][][]', - }, - { - name: 'b', - type: 'uint8[][][2]', - }, - { - name: 'c', - type: 'uint8[][2][]', - }, - { - name: 'd', - type: 'uint8[2][][]', - }, - { - name: 'e', - type: 'uint8[][2][2]', - }, - { - name: 'f', - type: 'uint8[2][2][]', - }, - { - name: 'g', - type: 'uint8[2][][2]', - }, - { - name: 'h', - type: 'uint8[2][2][2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const multiDimensionalArraysDynamicTypeAbi = { - constant: false, - inputs: [ - { - name: 'a', - type: 'string[][][]', - }, - { - name: 'b', - type: 'string[][][2]', - }, - { - name: 'c', - type: 'string[][2][]', - }, - { - name: 'h', - type: 'string[2][2][2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const dynamicTupleAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - ], - name: 'order', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfStaticTuplesWithDefinedLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - ], - name: 'order', - type: 'tuple[8]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfStaticTuplesWithDynamicLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfDynamicTuplesWithDefinedLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[8]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfDynamicTuplesWithUndefinedLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfDynamicTuplesAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const multidimensionalArrayOfDynamicTuplesAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[][2][]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const staticTupleAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint1', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - { - name: 'someUint3', - type: 'uint256', - }, - { - name: 'someBool', - type: 'bool', - }, - ], - name: 'order', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const staticArrayAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[3]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const staticArrayDynamicMembersAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'string[3]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const dynamicArrayDynamicMembersAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'string[]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const dynamicArrayStaticMembersAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const crazyAbi1 = { - constant: false, - inputs: [ - { - name: 'someUInt256', - type: 'uint256', - }, - { - name: 'someInt256', - type: 'int256', - }, - { - name: 'someInt32', - type: 'int32', - }, - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - }, - { - name: 'someAddress', - type: 'address', - }, - { - name: 'someBool', - type: 'bool', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const crazyAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[3]', - }, - { - name: 'someStaticArrayWithDynamicMembers', - type: 'string[2]', - }, - { - name: 'someDynamicArrayWithDynamicMembers', - type: 'bytes[]', - }, - { - name: 'some2DArray', - type: 'string[][]', - }, - { - name: 'someTuple', - type: 'tuple', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'someStr', - type: 'string', - }, - ], - }, - { - name: 'someTupleWithDynamicTypes', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - /*{ - name: 'someStrArray', - type: 'string[]', - },*/ - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - { - name: 'someArrayOfTuplesWithDynamicTypes', - type: 'tuple[]', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - /*{ - name: 'someStrArray', - type: 'string[]', - },*/ - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const nestedTuples = { - constant: false, - inputs: [ - { - name: 'firstTuple', - type: 'tuple[1]', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'nestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - }, - { - name: 'secondTuple', - type: 'tuple[]', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - { - name: 'nestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'secondNestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const simpleAbi2 = { - constant: false, - inputs: [ - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const fillOrderAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'makerAddress', - type: 'address', - }, - { - name: 'takerAddress', - type: 'address', - }, - { - name: 'feeRecipientAddress', - type: 'address', - }, - { - name: 'senderAddress', - type: 'address', - }, - { - name: 'makerAssetAmount', - type: 'uint256', - }, - { - name: 'takerAssetAmount', - type: 'uint256', - }, - { - name: 'makerFee', - type: 'uint256', - }, - { - name: 'takerFee', - type: 'uint256', - }, - { - name: 'expirationTimeSeconds', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'makerAssetData', - type: 'bytes', - }, - { - name: 'takerAssetData', - type: 'bytes', - }, - ], - name: 'order', - type: 'tuple', - }, - { - name: 'takerAssetFillAmount', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'orderSignature', - type: 'bytes', - }, - { - name: 'takerSignature', - type: 'bytes', - }, - ], - name: 'fillOrder', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts new file mode 100644 index 000000000..32278e5c5 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -0,0 +1,520 @@ +import ethUtil = require('ethereumjs-util'); +import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; +var _ = require('lodash'); + +export interface DecodingRules { + structsAsObjects: boolean; +} + +export interface EncodingRules { + optimize?: boolean; + annotate?: boolean; +} + +export abstract class CalldataBlock { + private name: string; + private signature: string; + private offsetInBytes: number; + private headerSizeInBytes: number; + private bodySizeInBytes: number; + private relocatable: boolean; + private parentName: string; + + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + this.name = name; + this.signature = signature; + this.parentName = parentName; + this.offsetInBytes = 0; + this.headerSizeInBytes = headerSizeInBytes; + this.bodySizeInBytes = bodySizeInBytes; + this.relocatable = relocatable; + } + + protected setHeaderSize(headerSizeInBytes: number) { + this.headerSizeInBytes = headerSizeInBytes; + } + + protected setBodySize(bodySizeInBytes: number) { + this.bodySizeInBytes = bodySizeInBytes; + } + + protected setName(name: string) { + this.name = name; + } + + public getName(): string { + return this.name; + } + + public getParentName(): string { + return this.parentName; + } + + public getSignature(): string { + return this.signature; + } + + public isRelocatable(): boolean { + return this.relocatable; + } + + public getHeaderSizeInBytes(): number { + return this.headerSizeInBytes; + } + + public getBodySizeInBytes(): number { + return this.bodySizeInBytes; + } + + public getSizeInBytes(): number { + return this.headerSizeInBytes + this.bodySizeInBytes; + } + + public getOffsetInBytes(): number { + return this.offsetInBytes; + } + + public setOffset(offsetInBytes: number) { + this.offsetInBytes = offsetInBytes; + } + + public computeHash(): Buffer { + const rawData = this.getRawData(); + const hash = ethUtil.sha3(rawData); + return hash; + } + + public abstract toBuffer(): Buffer; + public abstract getRawData(): Buffer; +} + +export class PayloadCalldataBlock extends CalldataBlock { + private payload: Buffer; + + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { + const headerSizeInBytes = 0; + const bodySizeInBytes = payload.byteLength; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); + this.payload = payload; + } + + public toBuffer(): Buffer { + return this.payload; + } + + public getRawData(): Buffer { + return this.payload; + } +} + +export class DependentCalldataBlock extends CalldataBlock { + public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + public static RAW_DATA_START = new Buffer('<'); + public static RAW_DATA_END = new Buffer('>'); + private parent: CalldataBlock; + private dependency: CalldataBlock; + private aliasFor: CalldataBlock | undefined; + + constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { + const headerSizeInBytes = 0; + const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); + this.parent = parent; + this.dependency = dependency; + this.aliasFor = undefined; + } + + public toBuffer(): Buffer { + const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); + const parentOffset = this.parent.getOffsetInBytes(); + const parentHeaderSize = this.parent.getHeaderSizeInBytes(); + const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); + const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); + const evmWordWidthInBytes = 32; + const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); + return pointerBufPadded; + } + + public getDependency(): CalldataBlock { + return this.dependency; + } + + public setAlias(block: CalldataBlock) { + this.aliasFor = block; + this.setName(`${this.getName()} (alias for ${block.getName()})`); + } + + public getAlias(): CalldataBlock | undefined { + return this.aliasFor; + } + + public getRawData(): Buffer { + const dependencyRawData = this.dependency.getRawData(); + const rawDataComponents: Buffer[] = []; + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); + rawDataComponents.push(dependencyRawData); + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } +} + +export class MemberCalldataBlock extends CalldataBlock { + private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private header: Buffer | undefined; + private members: CalldataBlock[]; + private contiguous: boolean; + + constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { + super(name, signature, parentName, 0, 0, relocatable); + this.members = []; + this.header = undefined; + this.contiguous = contiguous; + } + + public getRawData(): Buffer { + const rawDataComponents: Buffer[] = []; + if (this.header !== undefined) { + rawDataComponents.push(this.header); + } + _.each(this.members, (member: CalldataBlock) => { + const memberBuffer = member.getRawData(); + rawDataComponents.push(memberBuffer); + }); + + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } + + public setMembers(members: CalldataBlock[]) { + let bodySizeInBytes = 0; + _.each(members, (member: CalldataBlock) => { + bodySizeInBytes += member.getSizeInBytes(); + }); + this.members = members; + this.setBodySize(0); + } + + public isContiguous(): boolean { + return true; + } + + public setHeader(header: Buffer) { + this.setHeaderSize(header.byteLength); + this.header = header; + } + + public toBuffer(): Buffer { + if (this.header !== undefined) return this.header; + return new Buffer(''); + } + + public getMembers(): CalldataBlock[] { + return this.members; + } +} + +class Queue { + private store: T[] = []; + push(val: T) { + this.store.push(val); + } + pushFront(val: T) { + this.store.unshift(val); + } + pop(): T | undefined { + return this.store.shift(); + } + popBack(): T | undefined { + if (this.store.length === 0) return undefined; + const backElement = this.store.splice(-1, 1)[0]; + return backElement; + } + merge(q: Queue) { + this.store = this.store.concat(q.store); + } + mergeFront(q: Queue) { + this.store = q.store.concat(this.store); + } + getStore(): T[] { + return this.store; + } + peek(): T | undefined { + return this.store.length >= 0 ? this.store[0] : undefined; + } +} + +export class Calldata { + private selector: string; + private rules: EncodingRules; + private sizeInBytes: number; + private root: MemberCalldataBlock | undefined; + + constructor(rules: EncodingRules) { + this.selector = ''; + this.rules = rules; + this.sizeInBytes = 0; + this.root = undefined; + } + + private createQueue(memberBlock: MemberCalldataBlock): Queue { + const blockQueue = new Queue(); + _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof MemberCalldataBlock) { + blockQueue.mergeFront(this.createQueue(member)); + } else { + blockQueue.pushFront(member); + } + }); + + // Children + _.each(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { + let dependency = member.getDependency(); + if (dependency instanceof MemberCalldataBlock) { + blockQueue.merge(this.createQueue(dependency)); + } else { + blockQueue.push(dependency); + } + } + }); + + blockQueue.pushFront(memberBlock); + return blockQueue; + } + + private generateAnnotatedHexString(): string { + let hexValue = `${this.selector}`; + if (this.root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = this.createQueue(this.root); + + let block: CalldataBlock | undefined; + let offset = 0; + const functionBlock = valueQueue.peek(); + let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); + while ((block = valueQueue.pop()) !== undefined) { + // Set f + + // Process each block 1 word at a time + const size = block.getSizeInBytes(); + const name = block.getName(); + const parentName = block.getParentName(); + + //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; + //const parentOffset = name.lastIndexOf(parentName); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); + const signature = block.getSignature(); + + // Current offset + let offsetStr = ''; + + // If this block is empty then it's a newline + let value = ''; + let nameStr = ''; + let line = ''; + if (size === 0) { + offsetStr = ' '.repeat(10); + value = ' '.repeat(74); + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); + if (block instanceof MemberCalldataBlock) { + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + nameStr = ` ${prettyName.padEnd(80)}`; + line = `${offsetStr}${value}${nameStr}`; + } + } + + for (let j = 32; j < size; j += 32) { + offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); + nameStr = ' '.repeat(40); + + line = `${line}\n${offsetStr}${value}${nameStr}`; + } + + // Append to hex value + hexValue = `${hexValue}\n${line}`; + offset += size; + } + + return hexValue; + } + + private generateCondensedHexString(): string { + let selectorBuffer = ethUtil.toBuffer(this.selector); + if (this.root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = this.createQueue(this.root); + const valueBufs: Buffer[] = [selectorBuffer]; + let block: CalldataBlock | undefined; + while ((block = valueQueue.pop()) !== undefined) { + valueBufs.push(block.toBuffer()); + } + + const combinedBuffers = Buffer.concat(valueBufs); + const hexValue = ethUtil.bufferToHex(combinedBuffers); + return hexValue; + } + + public optimize() { + if (this.root === undefined) { + throw new Error('expected root'); + } + + const blocksByHash: { [key: string]: CalldataBlock } = {}; + + // 1. Create a queue of subtrees by hash + // Note that they are ordered the same as + const subtreeQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + while ((block = subtreeQueue.popBack()) !== undefined) { + if (block instanceof DependentCalldataBlock) { + const blockHashBuf = block.getDependency().computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash) { + const blockWithSameHash = blocksByHash[blockHash]; + if (blockWithSameHash !== block.getDependency()) { + block.setAlias(blockWithSameHash); + } + } + continue; + } + + const blockHashBuf = block.computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash === false) { + blocksByHash[blockHash] = block; + } + } + } + + public toHexString(): string { + if (this.root === undefined) { + throw new Error('expected root'); + } + + if (this.rules.optimize) this.optimize(); + + const offsetQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + let offset = 0; + while ((block = offsetQueue.pop()) !== undefined) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + + const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); + return hexValue; + } + + public getSelectorHex(): string { + return this.selector; + } + + public getSizeInBytes(): number { + return this.sizeInBytes; + } + + public toAnnotatedString(): string { + return ""; + } + + public setRoot(block: MemberCalldataBlock) { + this.root = block; + this.sizeInBytes += block.getSizeInBytes(); + } + + public setSelector(selector: string) { + // Ensure we have a 0x prefix + if (selector.startsWith('0x')) { + this.selector = selector; + } else { + this.selector = `$0x${selector}`; + } + + // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) + if (this.selector.length !== 10) { + throw new Error(`Invalid selector '${this.selector}'`); + } + this.sizeInBytes += 8; + } +} + +export class RawCalldata { + private value: Buffer; + private offset: number; // tracks current offset into raw calldata; used for parsing + private selector: string; + private scopes: Queue; + + constructor(value: string | Buffer) { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Expected raw calldata to start with '0x'`); + } + const valueBuf = ethUtil.toBuffer(value); + this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); + this.value = valueBuf.slice(4); // disregard selector + this.offset = 0; + this.scopes = new Queue(); + this.scopes.push(0); + } + + public popBytes(lengthInBytes: number): Buffer { + const value = this.value.slice(this.offset, this.offset + lengthInBytes); + this.setOffset(this.offset + lengthInBytes); + return value; + } + + public popWord(): Buffer { + const wordInBytes = 32; + return this.popBytes(wordInBytes); + } + + public popWords(length: number): Buffer { + const wordInBytes = 32; + return this.popBytes(length * wordInBytes); + } + + public readBytes(from: number, to: number): Buffer { + const value = this.value.slice(from, to); + return value; + } + + public setOffset(offsetInBytes: number) { + this.offset = offsetInBytes; + } + + public startScope() { + this.scopes.pushFront(this.offset); + } + + public endScope() { + this.scopes.pop(); + } + + public getOffset(): number { + return this.offset; + } + + public toAbsoluteOffset(relativeOffset: number) { + const scopeOffset = this.scopes.peek(); + if (scopeOffset === undefined) { + throw new Error(`Tried to access undefined scope.`); + } + const absoluteOffset = relativeOffset + scopeOffset; + return absoluteOffset; + } + + public getSelector(): string { + return this.selector; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts new file mode 100644 index 000000000..3b4028abd --- /dev/null +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -0,0 +1,322 @@ +import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; +import { MethodAbi, DataItem } from 'ethereum-types'; +import { DecodingRules, EncodingRules } from './calldata'; +import { BigNumber } from '../configured_bignumber'; +import ethUtil = require('ethereumjs-util'); +var _ = require('lodash'); + +export interface DataTypeFactory { + create: (dataItem: DataItem, parentDataType: DataType) => DataType; + mapDataItemToDataType: (dataItem: DataItem) => DataType; +} + +export abstract class DataType { + private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; + private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; + + private dataItem: DataItem; + private factory: DataTypeFactory; + + constructor(dataItem: DataItem, factory: DataTypeFactory) { + this.dataItem = dataItem; + this.factory = factory; + } + + public getDataItem(): DataItem { + return this.dataItem; + } + + public getFactory(): DataTypeFactory { + return this.factory; + } + + public encode(value: any, rules?: EncodingRules, selector?: string): string { + const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; + const calldata = new Calldata(rules_); + if (selector) calldata.setSelector(selector); + const block = this.generateCalldataBlock(value); + calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE + const calldataHex = calldata.toHexString(); + return calldataHex; + } + + public decode(calldata: string, rules?: DecodingRules): any { + const rawCalldata = new RawCalldata(calldata); + const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; + const value = this.generateValue(rawCalldata, rules_); + return value; + } + + public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; + public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; + public abstract getSignature(): string; + public abstract isStatic(): boolean; +} + +export abstract class PayloadDataType extends DataType { + protected hasConstantSize: boolean; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + super(dataItem, factory); + this.hasConstantSize = hasConstantSize; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { + const encodedValue = this.encodeValue(value); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const relocatable = false; + const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const value = this.decodeValue(calldata); + return value; + } + + public isStatic(): boolean { + // If a payload has a constant size then it's static + return this.hasConstantSize; + } + + public abstract encodeValue(value: any): Buffer; + public abstract decodeValue(calldata: RawCalldata): any; +} + +export abstract class DependentDataType extends DataType { + protected dependency: DataType; + protected parent: DataType; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + super(dataItem, factory); + this.dependency = dependency; + this.parent = parent; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { + if (parentBlock === undefined) { + throw new Error(`DependentDataType requires a parent block to generate its block`); + } + const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const relocatable = false; + const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const destinationOffsetBuf = calldata.popWord(); + const currentOffset = calldata.getOffset(); + const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); + const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); + calldata.setOffset(destinationOffsetAbsolute); + const value = this.dependency.generateValue(calldata, rules); + calldata.setOffset(currentOffset); + return value; + } + + public isStatic(): boolean { + return true; + } +} + +export interface MemberMap { + [key: string]: number; +} + +export abstract class MemberDataType extends DataType { + private memberMap: MemberMap; + private members: DataType[]; + private isArray: boolean; + protected arrayLength: number | undefined; + protected arrayElementType: string | undefined; + + + public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { + super(dataItem, factory); + this.memberMap = {}; + this.members = []; + this.isArray = isArray; + this.arrayLength = arrayLength; + this.arrayElementType = arrayElementType; + if (isArray && arrayLength !== undefined) { + [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); + } else if (!isArray) { + [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); + } + } + + private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } + + let members: DataType[] = []; + let memberMap: MemberMap = {}; + _.each(dataItem.components, (memberItem: DataItem) => { + const childDataItem = { + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, + } as DataItem; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[memberItem.name] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } + + private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + let members: DataType[] = []; + let memberMap: MemberMap = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem = { + type: this.arrayElementType, + name: `${dataItem.name}[${idx.toString(10)}]`, + } as DataItem; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[idx.toString(10)] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } + + protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { + // Sanity check length + if (this.arrayLength !== undefined && value.length !== this.arrayLength) { + throw new Error( + `Expected array of ${JSON.stringify( + this.arrayLength, + )} elements, but got array of length ${JSON.stringify(value.length)}`, + ); + } + + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + + let members = this.members; + if (this.isArray && this.arrayLength === undefined) { + [members,] = this.createMembersWithLength(this.getDataItem(), value.length); + + const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); + methodBlock.setHeader(lenBuf); + } + + const memberBlocks: CalldataBlock[] = []; + _.each(members, (member: DataType, idx: number) => { + const block = member.generateCalldataBlock(value[idx], methodBlock); + memberBlocks.push(block); + }); + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + const memberBlocks: CalldataBlock[] = []; + let childMap = _.cloneDeep(this.memberMap); + _.forOwn(obj, (value: any, key: string) => { + if (key in childMap === false) { + throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); + } + const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); + memberBlocks.push(block); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { + let members = this.members; + if (this.isArray && this.arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); + + [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + let value: any[] | object; + if (rules.structsAsObjects && !this.isArray) { + value = {}; + _.each(this.memberMap, (idx: number, key: string) => { + const member = this.members[idx]; + let memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + let memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } + calldata.endScope(); + return value; + } + + protected computeSignatureOfMembers(): string { + // Compute signature of members + let signature = `(`; + _.each(this.members, (member: DataType, i: number) => { + signature += member.getSignature(); + if (i < this.members.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; + } + + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? + + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this.isArray && this.arrayLength === undefined) { + return false; + } + + // Search for dependent members + const dependentMember = _.find(this.members, (member: DataType) => { + return (member instanceof DependentDataType); + }); + const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member + return isStatic; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts new file mode 100644 index 000000000..2973596fe --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -0,0 +1,550 @@ +import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; + +import { DecodingRules, EncodingRules } from './calldata'; + +import { MethodAbi, DataItem } from 'ethereum-types'; + +import ethUtil = require('ethereumjs-util'); + +import { Calldata, RawCalldata } from './calldata'; + +import { BigNumber } from '../configured_bignumber'; + +var _ = require('lodash'); + +export interface DataTypeStaticInterface { + matchGrammar: (type: string) => boolean; + encodeValue: (value: any) => Buffer; + decodeValue: (rawCalldata: RawCalldata) => any; +} + +export class Address extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Address.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'address'; + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } + + public encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(12); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } +} + +export class Bool extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bool.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'bool'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + + public encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValue = value === true ? '0x1' : '0x0'; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): boolean { + const valueBuf = calldata.popWord(); + const valueHex = ethUtil.bufferToHex(valueBuf); + const valueNumber = new BigNumber(valueHex, 16); + let value: boolean = (valueNumber.equals(0)) ? false : true; + if (!(valueNumber.equals(0) || valueNumber.equals(1))) { + throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); + } + return value; + } +} + +abstract class Number extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static MAX_WIDTH: number = 256; + static DEFAULT_WIDTH: number = Number.MAX_WIDTH; + width: number = Number.DEFAULT_WIDTH; + + constructor(dataItem: DataItem, matcher: RegExp) { + super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = matcher.exec(dataItem.type); + if (matches === null) { + throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 2 && matches[1] !== undefined) { + this.width = parseInt(matches[1]); + } else { + this.width = 256; + } + } + + public encodeValue(value: BigNumber): Buffer { + if (value.greaterThan(this.getMaxValue())) { + throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + } else if (value.lessThan(this.getMinValue())) { + throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + } + + const hexBase = 16; + const evmWordWidth = 32; + let valueBuf: Buffer; + if (value.greaterThanOrEqualTo(0)) { + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); + } else { + // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. + // Step 1/3: Convert value to positive binary string + const binBase = 2; + const valueBin = value.times(-1).toString(binBase); + + // Step 2/3: Invert binary value + const bitsInEvmWord = 256; + let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 3/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const negativeValue = invertedValue.plus(1); + + // Convert the negated value to a hex string + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), + evmWordWidth, + ); + } + + return valueBuf; + } + + public decodeValue(calldata: RawCalldata): BigNumber { + const paddedValueBuf = calldata.popWord(); + const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); + let value = new BigNumber(paddedValueHex, 16); + if (this instanceof Int) { + // Check if we're negative + const binBase = 2; + const paddedValueBin = value.toString(binBase); + const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); + if (valueBin[0].startsWith('1')) { + // Negative + // Step 1/3: Invert binary value + let invertedValueBin = ''; + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 2/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const positiveValue = invertedValue.plus(1); + + // Step 3/3: Invert positive value + const negativeValue = positiveValue.times(-1); + value = negativeValue; + } + } + + return value; + } + + public abstract getMaxValue(): BigNumber; + public abstract getMinValue(): BigNumber; +} + +export class Int extends Number { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, Int.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).times(-1); + } + + public getSignature(): string { + return `int${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class UInt extends Number { + static matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, UInt.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(0); + } + + public getSignature(): string { + return `uint${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Byte extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + static DEFAULT_WIDTH = 1; + width: number = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = Byte.matcher.exec(dataItem.type); + if (!Byte.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 3 && matches[2] !== undefined) { + this.width = parseInt(matches[2]); + } else { + this.width = Byte.DEFAULT_WIDTH; + } + } + + public getSignature(): string { + // Note that `byte` reduces to `bytes1` + return `bytes${this.width}`; + } + + public encodeValue(value: string | Buffer): Buffer { + // Convert value into a buffer and do bounds checking + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength > this.width) { + throw new Error( + `Tried to assign ${value} (${ + valueBuf.byteLength + } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, + ); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + // Store value as hex + const evmWordWidth = 32; + const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); + return paddedValue; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(0, this.width); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Bytes extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = Bytes.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bytes.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string | Buffer): Buffer { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + const wordsForValue = Math.ceil(valueBuf.byteLength / 32); + const paddedBytesForValue = wordsForValue * 32; + const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); + const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); + const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const decodedValue = ethUtil.bufferToHex(valueBuf); + return decodedValue; + } + + public getSignature(): string { + return 'bytes'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } +} + +export class SolString extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); + if (!SolString.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string): Buffer { + const wordsForValue = Math.ceil(value.length / 32); + const paddedBytesForValue = wordsForValue * 32; + const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const value = valueBuf.toString('ascii'); + return value; + } + + public getSignature(): string { + return 'string'; + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } +} + +export class Pointer extends DependentDataType { + + constructor(destDataType: DataType, parentDataType: DataType) { + const destDataItem = destDataType.getDataItem(); + const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; + super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); + } + + public getSignature(): string { + return this.dependency.getSignature(); + } +} + +export class Tuple extends MemberDataType { + private tupleSignature: string; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + if (!Tuple.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); + } + this.tupleSignature = this.computeSignatureOfMembers(); + } + + public getSignature(): string { + return this.tupleSignature; + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } +} + +export class SolArray extends MemberDataType { + static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + private arraySignature: string; + private elementType: string; + + constructor(dataItem: DataItem) { + // Sanity check + const matches = SolArray.matcher.exec(dataItem.type); + if (matches === null || matches.length !== 3) { + throw new Error(`Could not parse array: ${dataItem.type}`); + } else if (matches[1] === undefined) { + throw new Error(`Could not parse array type: ${dataItem.type}`); + } else if (matches[2] === undefined) { + throw new Error(`Could not parse array length: ${dataItem.type}`); + } + + const isArray = true; + const arrayElementType = matches[1]; + const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); + super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); + this.elementType = arrayElementType; + this.arraySignature = this.computeSignature(); + } + + private computeSignature(): string { + let dataItem = { + type: this.elementType, + name: 'N/A', + } as DataItem; + const components = this.getDataItem().components; + if (components !== undefined) { + dataItem.components = components; + } + const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); + const type = elementDataType.getSignature(); + if (this.arrayLength === undefined) { + return `${type}[]`; + } else { + return `${type}[${this.arrayLength}]`; + } + } + + public getSignature(): string { + return this.arraySignature; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Method extends MemberDataType { + private methodSignature: string; + private methodSelector: string; + + // TMP + public selector: string; + + constructor(abi: MethodAbi) { + super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); + this.methodSignature = this.computeSignature(); + this.selector = this.methodSelector = this.computeSelector(); + + } + + private computeSignature(): string { + const memberSignature = this.computeSignatureOfMembers(); + const methodSignature = `${this.getDataItem().name}${memberSignature}`; + return methodSignature; + } + + private computeSelector(): string { + const signature = this.computeSignature(); + const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); + return selector; + } + + public encode(value: any, rules?: EncodingRules): string { + const calldata = super.encode(value, rules, this.selector); + return calldata; + } + + public decode(calldata: string, rules?: DecodingRules): any[] | object { + if (!calldata.startsWith(this.selector)) { + throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); + } + const value = super.decode(calldata, rules); + return value; + } + + public getSignature(): string { + return this.methodSignature; + } + + public getSelector(): string { + return this.methodSelector; + } +} + +export class EvmDataTypeFactory implements DataTypeFactory { + private static instance: DataTypeFactory; + + private constructor() { } + + public static getInstance(): DataTypeFactory { + if (!EvmDataTypeFactory.instance) { + EvmDataTypeFactory.instance = new EvmDataTypeFactory(); + } + return EvmDataTypeFactory.instance; + } + + public mapDataItemToDataType(dataItem: DataItem): DataType { + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); + if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); + if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); + if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); + if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); + if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); + if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); + if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); + //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); + //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); + + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public create(dataItem: DataItem, parentDataType: DataType): DataType { + const dataType = this.mapDataItemToDataType(dataItem); + if (dataType.isStatic()) { + return dataType; + } + + const pointer = new Pointer(dataType, parentDataType); + return pointer; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts new file mode 100644 index 000000000..991edb8c5 --- /dev/null +++ b/packages/utils/src/abi_encoder/index.ts @@ -0,0 +1,2 @@ +export { EncodingRules, DecodingRules } from './calldata'; +export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 0723e5788..c44530bbc 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -10,3 +10,4 @@ export { NULL_BYTES } from './constants'; export { errorUtils } from './error_utils'; export { fetchAsync } from './fetch_async'; export { signTypedDataUtils } from './sign_typed_data_utils'; +export * from './abi_encoder'; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts new file mode 100644 index 000000000..2a8fba450 --- /dev/null +++ b/packages/utils/test/abi_encoder_test.ts @@ -0,0 +1,935 @@ +import * as chai from 'chai'; +import 'mocha'; + +// import { assert } from '@0x/order-utils/src/assert'; + +import { chaiSetup } from './utils/chai_setup'; +import { BigNumber } from '../src/'; +//import * as AbiEncoder from './abi_encoder'; + +import * as AbiEncoder from '../src/abi_encoder'; +import * as AbiSamples from './abi_samples'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe.only('ABI Encoder', () => { + describe.only('Optimizer', () => { + + }); + + describe.only('ABI Tests at Method Level', () => { + + it('Should reuse duplicated strings in string array', async () => { + const method = new AbiEncoder.Method(AbiSamples.stringAbi); + const strings = [ + "Test String", + "Test String 2", + "Test String", + "Test String 2", + ]; + const args = [strings]; + + // Verify optimized calldata is expected + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; + //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + + // Verify args decode properly + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); + }); + + it('Should point array elements to a duplicated value from another parameter', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); + const stringArray = [ + "Test String", + "Test String", + "Test String", + "Test String 2", + ]; + const string = 'Test String'; + const args = [stringArray, string]; + + // Verify optimized calldata is expected + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + + // Verify args decode properly + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); + }); + + + it('Optimizer #3 (tuple should point to array)', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi3); + const uint8Array = [ + new BigNumber(100), + new BigNumber(150), + new BigNumber(200), + new BigNumber(225), + ]; + const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; + const args = [uint8Array, uintTupleArray]; + + + const TEST = method.encode(args, { optimize: true, annotate: true }); + console.log('*'.repeat(50), ' ENCODED DATA ', TEST); + + const optimizedCalldata = method.encode(args, { optimize: true }); + + console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + console.log(JSON.stringify(decodedArgs)); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + + it('Optimizer #4 (Expect no optimization)', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); + const uint8Array = [ + new BigNumber(100), + new BigNumber(150), + new BigNumber(200), + new BigNumber(225), + ]; + const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; + const args = [uint8Array, uintTupleArray]; + + + const TEST = method.encode(args, { optimize: true, annotate: true }); + console.log('*'.repeat(50), ' ENCODED DATA ', TEST); + + const optimizedCalldata = method.encode(args, { optimize: true }); + + console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + console.log(JSON.stringify(decodedArgs)); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + + it('Crazy ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.crazyAbi); + console.log(method.getSignature()); + + const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; + const someStaticArrayWithDynamicMembers = [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ]; + const someDynamicArrayWithDynamicMembers = [ + '0x38745637834987324827439287423897238947239847', + '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', + '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', + ]; + const some2DArray = [ + [ + 'some string', + 'some another string', + 'there are just too many stringsup in', + 'here', + 'yall ghonna make me lose my mind', + ], + [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], + [], + ]; + const someTuple = { + someUint32: new BigNumber(4037824789), + someStr: 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.' + }; + const someTupleWithDynamicTypes = { + someUint: new BigNumber(4024789), + someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + }; + const someTupleWithDynamicTypes2 = { + someUint: new BigNumber(9024789), + someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', + someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', + someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', + }; + const someTupleWithDynamicTypes3 = { + someUint: new BigNumber(1024789), + someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', + someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', + someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', + }; + const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; + + const args = { + someStaticArray: someStaticArray, + someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, + someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, + some2DArray: some2DArray, + someTuple: someTuple, + someTupleWithDynamicTypes: someTupleWithDynamicTypes, + someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes + }; + + const calldata = method.encode(args); + console.log(calldata); + + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + console.log(method.getSignature()); + + const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; + //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata, { structsAsObjects: true }); + const decodedValueJson = JSON.stringify(decodedValue); + console.log(`DECODED`, '*'.repeat(200), '\n', decodedValueJson); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Crazy ABI #1', async () => { + const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); + + const args = [ + new BigNumber(256745454), + new BigNumber(-256745454), + new BigNumber(434244), + '0x43', + '0x0001020304050607080911121314151617181920212223242526272829303132', + '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', + 'Little peter piper piped a piping pepper pot', + '0xe41d2489571d322189246dafa5ebde1f4699f498', + true + ]; + + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + + it('Types with default widths', async () => { + const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); + console.log(method); + const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Static Tuples (Array has defined length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Static Tuples (Array has dynamic length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Dynamic Tuples (Array has defined length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Dynamic Tuples (Array has dynamic length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Multidimensional Arrays / Static Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); + + // Eight 3-dimensional arrays of uint8[2][2][2] + let value = 0; + const args = []; + for (let i = 0; i < 8; ++i) { + args.push( + [ + [ + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ], + [ + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ] + ] + ); + } + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Multidimensional Arrays / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); + + // Eight 3-dimensional arrays of string[2][2][2] + let value = 0; + const args = []; + for (let i = 0; i < 4; ++i) { + args.push( + [ + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ], + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ] + ] + ); + } + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Fixed Lenfgth Array / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Fixed Lenfgth Array / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Unfixed Length Array / Dynamic Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Unfixed Length Array / Static Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + + it('Fixed Length Array / Static Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + const expectedCalldata = + '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + + it('Simple ABI 2', async () => { + const method = new AbiEncoder.Method(AbiSamples.simpleAbi2); + + const args = [ + '0xaf', // e (bytes1) + '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) + '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g + 'My first name is Greg and my last name is Hysen, what do ya know!', // h + ]; + + const calldata = method.encode(args); + const expectedCalldata = + '0x7ac2bd96af000000000000000000000000000000000000000000000000000000000000000001020304050607080911121314151617181920212223242526272829303132000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.stringAbi); + console.log(method); + const args = [['five', 'six', 'seven']]; + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Static Tuple', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); + const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Dynamic Tuple (Array input)', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const args = [[new BigNumber(5), 'five']]; + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Dynamic Tuple (Object input)', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Nested Tuples', async () => { + // Couldn't get nested tuples to work with Remix + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.nestedTuples); + const firstTuple = { + someUint32: new BigNumber(30472), + nestedTuple: { + someUint: new BigNumber('48384725243211555532'), + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' + } + }; + const secondTuple = { + someUint: new BigNumber(2984237422), + someStr: 'This string will exceed 256 bits, so it will spill into the next word of memory.', + nestedTuple: { + someUint32: new BigNumber(23), + secondNestedTuple: { + someUint: new BigNumber(234324), + someStr: 'Im also a short string -- oops I just got loooooooooooooooooonger!', + someBytes: '0x23847287fff3472984723498ff23487324987aaa237438911873429472ba', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' + } + }, + someBytes: '0x2834y3947289423u489aaaff4783924739847489', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699afaf', + }; + const thirdTuple = { + 'someUint': new BigNumber(37422), + 'someStr': 'This into the next word of memory. string will exceed 256 bits, so it will spill.', + 'nestedTuple': { + someUint32: new BigNumber(23999222), + 'secondNestedTuple': { + 'someUint': new BigNumber(324), + 'someStr': 'Im also a short st', + 'someBytes': '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', + 'someAddress': '0x46dafa5ebde1f4699f498e41d2489571d3221892' + } + }, + 'someBytes': '0x947289423u489aaaff472834y383924739847489', + 'someAddress': '0x46dafa5ebde1f46e41d2489571d322189299afaf', + }; + const fourthTuple = { + 'someUint': new BigNumber(222283488822), + 'someStr': 'exceed 256 bits, so it will spill into the. This string will next word of memory.', + 'nestedTuple': { + someUint32: new BigNumber(2300), + 'secondNestedTuple': { + 'someUint': new BigNumber(343224), + 'someStr': 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', + 'someBytes': '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', + 'someAddress': '0x71d322189246dafa5ebe41d24895de1f4699f498' + } + }, + 'someBytes': '0x2783924739847488343947289423u489aaaff490', + 'someAddress': '0xebde1d322189246dafa1f4699afafe41d2489575', + }; + const args = [ + [firstTuple], + [secondTuple, thirdTuple, fourthTuple] + ]; + + console.log('*'.repeat(250), method, '*'.repeat(250)); + + + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + console.log(JSON.stringify(args)); + + console.log(calldata); + const expectedCalldata = '0x'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Object ABI (Object input - Missing Key)', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5) }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + // @TODO: Figure out how to catch throw + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Object ABI (Object input - Too Many Keys)', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + // @TODO: Figure out how to catch throw + expect(calldata).to.be.equal(expectedCalldata); + }); + }); + /* + describe('Array', () => { + it('sample', async () => { + const testDataItem = { name: 'testArray', type: 'int[2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); + + it('sample undefined size', async () => { + const testDataItem = { name: 'testArray', type: 'int[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); + + it('sample dynamic types', async () => { + const testDataItem = { name: 'testArray', type: 'string[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue(['five', 'six', 'seven']); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + const calldata = new AbiEncoder.Calldata('0x01020304', 1); + dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); + console.log('*'.repeat(60)); + console.log(calldata.getHexValue()); + }); + }); + + describe('Address', () => { + const testAddressDataItem = { name: 'testAddress', type: 'address' }; + it('Valid Address', async () => { + const addressDataType = new AbiEncoder.Address(testAddressDataItem); + addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); + const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + + console.log(addressDataType.getHexValue()); + console.log(expectedAbiEncodedAddress); + expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); + }); + }); + + describe('Bool', () => { + const testBoolDataItem = { name: 'testBool', type: 'bool' }; + it('True', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(true); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + + it('False', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(false); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + }); + + describe('Integer', () => { + const testIntDataItem = { name: 'testInt', type: 'int' }; + it('Positive - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Positive', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-1)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-437829473)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Unsigned Integer', () => { + const testIntDataItem = { name: 'testUInt', type: 'uint' }; + it('Lower Bound', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(0)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Base Case', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Random value', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Static Bytes', () => { + it('Byte (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Byte (no padding)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect(byteDataType.assignValue('0x5')).to.throw(); + }); + + it('Bytes1', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); + const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (unpadded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x1a18bf61'); + const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Bytes32 - Too long', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect( + byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), + ).to.throw( + `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, + ); + }); + }); + + describe('Bytes (Dynamic)', () => { + const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; + it('Less than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + bytesDataType.assignValue('0x010203'); + const expectedAbiEncodedBytes = + '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; + + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + it('Greater than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + const testValue = '0x' + '61'.repeat(40); + bytesDataType.assignValue(testValue); + const expectedAbiEncodedBytes = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + // @TODO: Add test for throw on half-byte + // @TODO: Test with no 0x prefix + // @TODO: Test with Buffer as input + }); + + describe('String', () => { + const testStringDataItem = { name: 'testString', type: 'string' }; + it('Less than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + stringDataType.assignValue('five'); + const expectedAbiEncodedString = + '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + console.log(stringDataType.getHexValue()); + console.log(expectedAbiEncodedString); + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + + it('Greater than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + const testValue = 'a'.repeat(40); + stringDataType.assignValue(testValue); + const expectedAbiEncodedString = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + });*/ +}); diff --git a/packages/utils/test/abi_samples.ts b/packages/utils/test/abi_samples.ts new file mode 100644 index 000000000..5e8268f1a --- /dev/null +++ b/packages/utils/test/abi_samples.ts @@ -0,0 +1,814 @@ +import { MethodAbi } from 'ethereum-types'; + +export const simpleAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'uint256', + }, + { + name: 'gregStr', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const stringAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const optimizerAbi2 = { + constant: false, + inputs: [ + { + name: 'stringArray', + type: 'string[]', + }, + { + name: 'string', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const optimizerAbi3 = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const optimizerAbi4 = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[4]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const typesWithDefaultWidthsAbi = { + constant: false, + inputs: [ + { + name: 'someUint', + type: 'uint', + }, + { + name: 'someInt', + type: 'int', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someUint', + type: 'uint[]', + }, + { + name: 'someInt', + type: 'int[]', + }, + { + name: 'someByte', + type: 'byte[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multiDimensionalArraysStaticTypeAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'uint8[][][]', + }, + { + name: 'b', + type: 'uint8[][][2]', + }, + { + name: 'c', + type: 'uint8[][2][]', + }, + { + name: 'd', + type: 'uint8[2][][]', + }, + { + name: 'e', + type: 'uint8[][2][2]', + }, + { + name: 'f', + type: 'uint8[2][2][]', + }, + { + name: 'g', + type: 'uint8[2][][2]', + }, + { + name: 'h', + type: 'uint8[2][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multiDimensionalArraysDynamicTypeAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'string[][][]', + }, + { + name: 'b', + type: 'string[][][2]', + }, + { + name: 'c', + type: 'string[][2][]', + }, + { + name: 'h', + type: 'string[2][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicTupleAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfStaticTuplesWithDefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfStaticTuplesWithDynamicLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesWithDefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesWithUndefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multidimensionalArrayOfDynamicTuplesAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[][2][]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticTupleAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint1', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + { + name: 'someUint3', + type: 'uint256', + }, + { + name: 'someBool', + type: 'bool', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticArrayAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticArrayDynamicMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[3]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicArrayDynamicMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicArrayStaticMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const crazyAbi1 = { + constant: false, + inputs: [ + { + name: 'someUInt256', + type: 'uint256', + }, + { + name: 'someInt256', + type: 'int256', + }, + { + name: 'someInt32', + type: 'int32', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + { + name: 'someAddress', + type: 'address', + }, + { + name: 'someBool', + type: 'bool', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const crazyAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + }, + { + name: 'someStaticArrayWithDynamicMembers', + type: 'string[2]', + }, + { + name: 'someDynamicArrayWithDynamicMembers', + type: 'bytes[]', + }, + { + name: 'some2DArray', + type: 'string[][]', + }, + { + name: 'someTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'someStr', + type: 'string', + }, + ], + }, + { + name: 'someTupleWithDynamicTypes', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + /*{ + name: 'someStrArray', + type: 'string[]', + },*/ + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + { + name: 'someArrayOfTuplesWithDynamicTypes', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + /*{ + name: 'someStrArray', + type: 'string[]', + },*/ + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const nestedTuples = { + constant: false, + inputs: [ + { + name: 'firstTuple', + type: 'tuple[1]', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'secondTuple', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'secondNestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const simpleAbi2 = { + constant: false, + inputs: [ + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const fillOrderAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'makerAddress', + type: 'address', + }, + { + name: 'takerAddress', + type: 'address', + }, + { + name: 'feeRecipientAddress', + type: 'address', + }, + { + name: 'senderAddress', + type: 'address', + }, + { + name: 'makerAssetAmount', + type: 'uint256', + }, + { + name: 'takerAssetAmount', + type: 'uint256', + }, + { + name: 'makerFee', + type: 'uint256', + }, + { + name: 'takerFee', + type: 'uint256', + }, + { + name: 'expirationTimeSeconds', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'makerAssetData', + type: 'bytes', + }, + { + name: 'takerAssetData', + type: 'bytes', + }, + ], + name: 'order', + type: 'tuple', + }, + { + name: 'takerAssetFillAmount', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'orderSignature', + type: 'bytes', + }, + { + name: 'takerSignature', + type: 'bytes', + }, + ], + name: 'fillOrder', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; diff --git a/packages/utils/test/utils/chai_setup.ts b/packages/utils/test/utils/chai_setup.ts new file mode 100644 index 000000000..1a8733093 --- /dev/null +++ b/packages/utils/test/utils/chai_setup.ts @@ -0,0 +1,13 @@ +import * as chai from 'chai'; +import chaiAsPromised = require('chai-as-promised'); +import ChaiBigNumber = require('chai-bignumber'); +import * as dirtyChai from 'dirty-chai'; + +export const chaiSetup = { + configure(): void { + chai.config.includeStack = true; + chai.use(ChaiBigNumber()); + chai.use(dirtyChai); + chai.use(chaiAsPromised); + }, +}; -- cgit v1.2.3