aboutsummaryrefslogtreecommitdiffstats
path: root/packages/utils
diff options
context:
space:
mode:
authorGreg Hysen <greg.hysen@gmail.com>2018-11-15 07:27:07 +0800
committerGreg Hysen <greg.hysen@gmail.com>2018-11-29 08:38:10 +0800
commit5b0d554f7baec54837d795b6568ae5ba8d8a0908 (patch)
treeb3ed8128c0c6006633790e2af70931d23ded7198 /packages/utils
parent71c050375b23336e7bc2700b595032eefe0ca4c1 (diff)
downloaddexon-sol-tools-5b0d554f7baec54837d795b6568ae5ba8d8a0908.tar
dexon-sol-tools-5b0d554f7baec54837d795b6568ae5ba8d8a0908.tar.gz
dexon-sol-tools-5b0d554f7baec54837d795b6568ae5ba8d8a0908.tar.bz2
dexon-sol-tools-5b0d554f7baec54837d795b6568ae5ba8d8a0908.tar.lz
dexon-sol-tools-5b0d554f7baec54837d795b6568ae5ba8d8a0908.tar.xz
dexon-sol-tools-5b0d554f7baec54837d795b6568ae5ba8d8a0908.tar.zst
dexon-sol-tools-5b0d554f7baec54837d795b6568ae5ba8d8a0908.zip
Moved Abi Encoder into utils package
Diffstat (limited to 'packages/utils')
-rw-r--r--packages/utils/src/abi_encoder/calldata.ts520
-rw-r--r--packages/utils/src/abi_encoder/data_type.ts322
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types.ts550
-rw-r--r--packages/utils/src/abi_encoder/index.ts2
-rw-r--r--packages/utils/src/index.ts1
-rw-r--r--packages/utils/test/abi_encoder_test.ts935
-rw-r--r--packages/utils/test/abi_samples.ts814
-rw-r--r--packages/utils/test/utils/chai_setup.ts13
8 files changed, 3157 insertions, 0 deletions
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<T> {
+ 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<T>) {
+ this.store = this.store.concat(q.store);
+ }
+ mergeFront(q: Queue<T>) {
+ 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<CalldataBlock> {
+ const blockQueue = new Queue<CalldataBlock>();
+ _.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<number>;
+
+ 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<number>();
+ 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);
+ },
+};