diff options
Diffstat (limited to 'packages/order-utils')
-rw-r--r-- | packages/order-utils/test/abi_encoder.ts | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts new file mode 100644 index 000000000..46449662a --- /dev/null +++ b/packages/order-utils/test/abi_encoder.ts @@ -0,0 +1,483 @@ +import * as chai from 'chai'; +import 'mocha'; +import ethUtil = require('ethereumjs-util'); + +var _ = require('lodash'); + +import { assert } from '@0x/order-utils/src/assert'; + +import { chaiSetup } from '@0x/order-utils/test/utils/chai_setup'; +import { web3Wrapper } from '@0x/order-utils/test/utils/web3_wrapper'; + +import { MethodAbi, DataItem } from 'ethereum-types'; +import { throwError } from 'ethers/errors'; + +import { BigNumber } from '@0x/utils'; +import { MethodNotFound } from 'json-rpc-error'; +import { power } from 'js-combinatorics'; + +const simpleAbi = { + name: 'SimpleAbi', + inputs: [ + { + components: [ + { + name: 'greg', + type: 'uint256', + }, + { + name: 'gregStr', + type: 'string', + }, + ], + }, + ], +} as MethodAbi; +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; + +chaiSetup.configure(); +const expect = chai.expect; + +namespace AbiEncoder { + class Memory {} + + class Word {} + + abstract class DataType { + private dataItem: DataItem; + private hexValue: string; + + constructor(dataItem: DataItem) { + this.dataItem = dataItem; + this.hexValue = '0x'; + } + + protected assignHexValue(hexValue: string) { + this.hexValue = hexValue; + } + + public getHexValue(): string { + return this.hexValue; + } + + public abstract assignValue(value: any): void; + + // abstract match(type: string): Bool; + } + + class Calldata {} + + abstract class StaticDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } + } + + abstract class DynamicDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } + } + + class Address extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const hexValue = ethUtil.bufferToHex(new Buffer(value)); + this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } + } + + class Bool extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + } + + class Int extends StaticDataType { + 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}$', + ); + + static DEFAULT_WIDTH = new BigNumber(1); + width: BigNumber = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class UInt extends StaticDataType { + 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}$', + ); + + static DEFAULT_WIDTH: number = 1; + width: number = UInt.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = parseInt(matches[1]); + } + } + + public getMaxValue() { + return new BigNumber(2).toPower(this.width - 1); + } + + public assignValue(value: BigNumber) { + if (value > this.getMaxValue()) { + throw 1; + } + + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const encodedValue = ethUtil.bufferToHex(valueBuf); + + this.assignHexValue(encodedValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class Byte extends StaticDataType { + 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 = new BigNumber(1); + width: BigNumber = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class Tuple extends DynamicDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } + } + + class Bytes extends StaticDataType { + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem); + expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } + } + + class SolArray extends DynamicDataType { + static matcher = RegExp('^.+\\[([0-9]d*)\\]$'); + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = SolArray.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.length = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class SolString extends DynamicDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(SolString.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + const encodedValue = ethUtil.bufferToHex(encodedValueBuf); + + this.assignHexValue(encodedValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } + } + + /* TODO + class Fixed extends StaticDataType {} + + class UFixed extends StaticDataType {}*/ + + class Pointer extends StaticDataType { + destDataType: DynamicDataType; + static metaDataItem = { name: '[ptr]', type: '[ptr]' } as DataItem; + + constructor(destDataType: DynamicDataType) { + super(Pointer.metaDataItem); + this.destDataType = destDataType; + } + + public assignValue(destDataType: DynamicDataType) { + this.destDataType = destDataType; + } + } + + class DataTypeFactory { + public static 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 (SolArray.matchGrammar(dataItem.type)) return new SolArray(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 static create(dataItem: DataItem): DataType { + const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); + if (dataType instanceof StaticDataType) { + return dataType; + } else if (dataType instanceof DynamicDataType) { + const pointer = new Pointer(dataType); + return pointer; + } + + throw new Error(`Unrecognized instance type: '${dataType}'`); + } + } + + export class Method { + name: string; + params: DataType[]; + + constructor(abi: MethodAbi) { + // super(); + this.name = abi.name; + this.params = []; + + _.each(abi.inputs, function(this: Method, input: DataItem) { + this.params.push(DataTypeFactory.create(input)); + }); + } + + encode(args: any[]): string { + //const calldata = new Calldata(this.name, this.params.length); + let params = this.params; + _.each(params, function(args: any[], i: number, param: DataType) { + param.assignValue(args[i]); + console.log(param.getHexValue()); + //param.encodeToCalldata(calldata); + }); + + return ''; + + //return calldata.getRaw(); + } + + /* + encodeOptimized(args: any[]): string { + const calldata = new Memory(); + // Assign values + optimizableParams : StaticDataType = []; + _.each(this.params, function(args: any[], i: number, param: DataType) { + param.assignValue(args[i]); + if (param instanceof DynamicDataType) { + + } + }); + + // Find non-parameter leaves + + + return ''; + } */ + + /* + decode(rawCalldata: string): any[] { + const calldata = new Calldata(this.name, this.params.length); + calldata.assignRaw(rawCalldata); + let args: any[]; + let params = this.params; + _.each(params, function(args: any[], i: number, param: DataType) { + param.decodeFromCalldata(calldata); + args.push(param.getValue()); + }); + + return args; + }*/ + } +} + +describe.only('ABI Encoder', () => { + describe.only('Just a Greg, Eh', () => { + it.only('Yessir', async () => { + const method = new AbiEncoder.Method(simpleAbi); + method.encode([new BigNumber(5), 'five']); + expect(true).to.be.true(); + }); + }); +}); |