812 lines
25 KiB
TypeScript
812 lines
25 KiB
TypeScript
import * as chai from 'chai';
|
|
import 'mocha';
|
|
import ethUtil = require('ethereumjs-util');
|
|
|
|
var _ = require('lodash');
|
|
|
|
// import { assert } from '@0x/order-utils/src/assert';
|
|
|
|
import { chaiSetup } from './utils/chai_setup';
|
|
|
|
import { MethodAbi, DataItem } from 'ethereum-types';
|
|
|
|
import { BigNumber } from '@0x/utils';
|
|
import { assert } from '@0x/order-utils/src/assert';
|
|
|
|
const simpleAbi = {
|
|
constant: false,
|
|
inputs: [
|
|
{
|
|
name: 'greg',
|
|
type: 'uint256',
|
|
},
|
|
{
|
|
name: 'gregStr',
|
|
type: 'string',
|
|
},
|
|
],
|
|
name: 'simpleFunction',
|
|
outputs: [],
|
|
payable: false,
|
|
stateMutability: 'nonpayable',
|
|
type: 'function',
|
|
} 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 Word {
|
|
private value: string;
|
|
|
|
constructor(value?: string) {
|
|
if (value === undefined) {
|
|
this.value = '';
|
|
} else {
|
|
this.value = value;
|
|
}
|
|
}
|
|
|
|
public set(value: string) {
|
|
if (value.length !== 64) {
|
|
throw `Tried to create word that is not 32 bytes: ${value}`;
|
|
}
|
|
|
|
this.value = value;
|
|
}
|
|
|
|
public get(): string {
|
|
return this.value;
|
|
}
|
|
|
|
public getAsHex(): string {
|
|
return `0x${this.value}`;
|
|
}
|
|
}
|
|
|
|
enum CalldataSection {
|
|
NONE,
|
|
PARAMS,
|
|
DATA,
|
|
}
|
|
|
|
class Memblock {
|
|
private dataType: DataType;
|
|
private location: { calldataSection: CalldataSection; sectionOffset: BigNumber; offset: BigNumber };
|
|
|
|
constructor(dataType: DataType) {
|
|
this.dataType = dataType;
|
|
this.location = {
|
|
calldataSection: CalldataSection.NONE,
|
|
sectionOffset: new BigNumber(0),
|
|
offset: new BigNumber(0),
|
|
};
|
|
}
|
|
|
|
public getSize(): BigNumber {
|
|
return new BigNumber(ethUtil.toBuffer(this.dataType.getHexValue()).byteLength);
|
|
}
|
|
|
|
public assignLocation(calldataSection: CalldataSection, sectionOffset: BigNumber, offset: BigNumber) {
|
|
this.location.calldataSection = calldataSection;
|
|
this.location.sectionOffset = sectionOffset;
|
|
this.location.offset = offset;
|
|
}
|
|
|
|
public get(): string {
|
|
return ethUtil.stripHexPrefix(this.dataType.getHexValue());
|
|
}
|
|
|
|
public getOffset(): BigNumber {
|
|
return this.location.offset;
|
|
}
|
|
|
|
public getAbsoluteOffset(): BigNumber {
|
|
return this.location.sectionOffset.plus(this.location.offset);
|
|
}
|
|
|
|
public getSection(): CalldataSection {
|
|
return this.location.calldataSection;
|
|
}
|
|
}
|
|
|
|
interface BindList {
|
|
[key: string]: Memblock;
|
|
}
|
|
|
|
class Calldata {
|
|
private selector: string;
|
|
private params: Memblock[];
|
|
private data: Memblock[];
|
|
private dataOffset: BigNumber;
|
|
private currentDataOffset: BigNumber;
|
|
private currentParamOffset: BigNumber;
|
|
private bindList: BindList;
|
|
|
|
constructor(selector: string, nParams: number) {
|
|
this.selector = selector;
|
|
console.log(this.selector);
|
|
this.params = [];
|
|
this.data = [];
|
|
const evmWordSize = 32;
|
|
this.dataOffset = new BigNumber(nParams).times(evmWordSize);
|
|
this.currentDataOffset = new BigNumber(0);
|
|
this.currentParamOffset = new BigNumber(0);
|
|
this.bindList = {};
|
|
}
|
|
|
|
public bind(dataType: DataType, section: CalldataSection) {
|
|
if (dataType.getId() in this.bindList) {
|
|
throw `Rebind on ${dataType.getId()}`;
|
|
}
|
|
const memblock = new Memblock(dataType);
|
|
switch (section) {
|
|
case CalldataSection.PARAMS:
|
|
this.params.push(memblock);
|
|
memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset);
|
|
this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize());
|
|
break;
|
|
|
|
case CalldataSection.DATA:
|
|
this.data.push(memblock);
|
|
memblock.assignLocation(section, this.dataOffset, this.currentDataOffset);
|
|
this.currentDataOffset = this.currentDataOffset.plus(memblock.getSize());
|
|
break;
|
|
|
|
default:
|
|
throw `Unrecognized calldata section: ${section}`;
|
|
}
|
|
|
|
this.bindList[dataType.getId()] = memblock;
|
|
dataType.rbind(memblock);
|
|
}
|
|
|
|
public getHexValue(): string {
|
|
let hexValue = `${this.selector}`;
|
|
_.each(this.params, (memblock: Memblock) => {
|
|
hexValue += memblock.get();
|
|
});
|
|
_.each(this.data, (memblock: Memblock) => {
|
|
hexValue += memblock.get();
|
|
});
|
|
|
|
return hexValue;
|
|
}
|
|
}
|
|
|
|
export abstract class DataType {
|
|
private dataItem: DataItem;
|
|
private hexValue: string;
|
|
protected memblock: Memblock | undefined;
|
|
protected children: DataType[];
|
|
|
|
constructor(dataItem: DataItem) {
|
|
this.dataItem = dataItem;
|
|
this.hexValue = '0x';
|
|
this.memblock = undefined;
|
|
this.children = [];
|
|
}
|
|
|
|
protected assignHexValue(hexValue: string) {
|
|
this.hexValue = hexValue;
|
|
}
|
|
|
|
public getHexValue(): string {
|
|
return this.hexValue;
|
|
}
|
|
|
|
public getDataItem(): DataItem {
|
|
return this.dataItem;
|
|
}
|
|
|
|
public rbind(memblock: Memblock) {
|
|
this.memblock = memblock;
|
|
}
|
|
|
|
public bind(calldata: Calldata, section: CalldataSection) {
|
|
if (this.memblock === undefined) {
|
|
calldata.bind(this, section);
|
|
}
|
|
_.each(this.children, (child: DataType) => {
|
|
child.bind(calldata, CalldataSection.DATA);
|
|
});
|
|
}
|
|
|
|
public getId(): string {
|
|
return this.dataItem.name;
|
|
}
|
|
|
|
public getOffset(): BigNumber {
|
|
if (this.memblock === undefined) return new BigNumber(0);
|
|
return this.memblock.getOffset();
|
|
}
|
|
|
|
public getAbsoluteOffset(): BigNumber {
|
|
if (this.memblock === undefined) return new BigNumber(0);
|
|
return this.memblock.getAbsoluteOffset();
|
|
}
|
|
|
|
public abstract assignValue(value: any): void;
|
|
public abstract getSignature(): string;
|
|
public abstract encodeToCalldata(calldata: Calldata): void;
|
|
|
|
// abstract match(type: string): Bool;
|
|
}
|
|
|
|
export abstract class StaticDataType extends DataType {
|
|
constructor(dataItem: DataItem) {
|
|
super(dataItem);
|
|
}
|
|
}
|
|
|
|
export abstract class DynamicDataType extends DataType {
|
|
constructor(dataItem: DataItem) {
|
|
super(dataItem);
|
|
}
|
|
}
|
|
|
|
export class Address extends StaticDataType {
|
|
constructor(dataItem: DataItem) {
|
|
super(dataItem);
|
|
expect(Address.matchGrammar(dataItem.type)).to.be.true();
|
|
}
|
|
|
|
public assignValue(value: string) {
|
|
const hexValue = ethUtil.bufferToHex(new Buffer(value));
|
|
this.assignHexValue(hexValue);
|
|
}
|
|
|
|
public getSignature(): string {
|
|
throw 1;
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return type === 'address';
|
|
}
|
|
}
|
|
|
|
export 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 getSignature(): string {
|
|
throw 1;
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return type === 'bool';
|
|
}
|
|
}
|
|
|
|
export 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 === 2) {
|
|
this.width = new BigNumber(matches[1], 10);
|
|
}
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public assignValue(value: string) {
|
|
//const hexValue = ethUtil.bufferToHex(new Buffer(value));
|
|
//this.assignHexValue(hexValue);
|
|
}
|
|
|
|
public getSignature(): string {
|
|
throw 1;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return this.matcher.test(type);
|
|
}
|
|
}
|
|
|
|
export 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 = 256;
|
|
width: number = UInt.DEFAULT_WIDTH;
|
|
|
|
constructor(dataItem: DataItem) {
|
|
super(dataItem);
|
|
const matches = UInt.matcher.exec(dataItem.type);
|
|
expect(matches).to.be.not.null();
|
|
if (matches !== null && matches.length === 2) {
|
|
this.width = parseInt(matches[1]);
|
|
} else {
|
|
this.width = 256;
|
|
}
|
|
}
|
|
|
|
public getMaxValue(): BigNumber {
|
|
return new BigNumber(2).toPower(this.width - 1);
|
|
}
|
|
|
|
public assignValue(value: BigNumber) {
|
|
if (value.greaterThan(this.getMaxValue())) {
|
|
throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`;
|
|
} else if (value.lessThan(0)) {
|
|
throw `tried to assign value of ${value} to an unsigned integer.`;
|
|
}
|
|
|
|
const hexBase = 16;
|
|
const evmWordWidth = 32;
|
|
const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth);
|
|
const encodedValue = ethUtil.bufferToHex(valueBuf);
|
|
|
|
this.assignHexValue(encodedValue);
|
|
}
|
|
|
|
public getSignature(): string {
|
|
return `uint${this.width}`;
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return this.matcher.test(type);
|
|
}
|
|
}
|
|
|
|
export 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 === 2) {
|
|
this.width = new BigNumber(matches[1], 10);
|
|
}
|
|
}
|
|
|
|
public assignValue(value: string) {
|
|
//const hexValue = ethUtil.bufferToHex(new Buffer(value));
|
|
//this.assignHexValue(hexValue);
|
|
}
|
|
|
|
public getSignature(): string {
|
|
throw 1;
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return this.matcher.test(type);
|
|
}
|
|
}
|
|
|
|
export 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 getSignature(): string {
|
|
throw 1;
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return type === 'tuple';
|
|
}
|
|
}
|
|
|
|
export 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 getSignature(): string {
|
|
throw 1;
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return type === 'bytes';
|
|
}
|
|
}
|
|
|
|
export 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 encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return this.matcher.test(type);
|
|
}
|
|
|
|
public getSignature(): string {
|
|
throw 1;
|
|
}
|
|
}
|
|
|
|
export class SolString extends DynamicDataType {
|
|
constructor(dataItem: DataItem) {
|
|
super(dataItem);
|
|
expect(SolString.matchGrammar(dataItem.type)).to.be.true();
|
|
}
|
|
|
|
public assignValue(value: string) {
|
|
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]);
|
|
const encodedValue = ethUtil.bufferToHex(encodedValueBuf);
|
|
|
|
this.assignHexValue(encodedValue);
|
|
}
|
|
|
|
public getSignature(): string {
|
|
return 'string';
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {}
|
|
|
|
public static matchGrammar(type: string): boolean {
|
|
return type === 'string';
|
|
}
|
|
}
|
|
|
|
/* TODO
|
|
class Fixed extends StaticDataType {}
|
|
|
|
class UFixed extends StaticDataType {}*/
|
|
|
|
export class Pointer extends StaticDataType {
|
|
destDataType: DynamicDataType;
|
|
|
|
constructor(destDataType: DynamicDataType) {
|
|
const destDataItem = destDataType.getDataItem();
|
|
const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem;
|
|
super(dataItem);
|
|
this.destDataType = destDataType;
|
|
this.children.push(destDataType);
|
|
}
|
|
|
|
/*
|
|
public assignValue(destDataType: DynamicDataType) {
|
|
this.destDataType = destDataType;
|
|
}*/
|
|
|
|
public assignValue(value: any) {
|
|
this.destDataType.assignValue(value);
|
|
}
|
|
|
|
public getHexValue(): string {
|
|
let offset = new BigNumber(0);
|
|
if (this.memblock !== undefined) {
|
|
console.log('Abs Offset = ', JSON.stringify(this.destDataType.getAbsoluteOffset()));
|
|
console.log('Local Offset = ', JSON.stringify(this.destDataType.getOffset()));
|
|
switch (this.memblock.getSection()) {
|
|
case CalldataSection.PARAMS:
|
|
offset = this.destDataType.getAbsoluteOffset();
|
|
break;
|
|
case CalldataSection.DATA:
|
|
offset = this.destDataType.getOffset();
|
|
break;
|
|
}
|
|
}
|
|
|
|
const hexBase = 16;
|
|
const evmWordWidth = 32;
|
|
const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${offset.toString(hexBase)}`), evmWordWidth);
|
|
const encodedValue = ethUtil.bufferToHex(valueBuf);
|
|
return encodedValue;
|
|
}
|
|
|
|
public getSignature(): string {
|
|
return this.destDataType.getSignature();
|
|
}
|
|
|
|
public encodeToCalldata(calldata: Calldata): void {
|
|
throw 2;
|
|
}
|
|
}
|
|
|
|
export class DataTypeFactory {
|
|
public static mapDataItemToDataType(dataItem: DataItem): DataType {
|
|
console.log(`Type: ${dataItem.type}`);
|
|
|
|
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[];
|
|
signature: string;
|
|
selector: string;
|
|
|
|
constructor(abi: MethodAbi) {
|
|
// super();
|
|
this.name = abi.name;
|
|
this.params = [];
|
|
|
|
_.each(abi.inputs, (input: DataItem) => {
|
|
this.params.push(DataTypeFactory.create(input));
|
|
});
|
|
|
|
// Compute signature
|
|
this.signature = `${this.name}(`;
|
|
_.each(this.params, (param: DataType, i: number) => {
|
|
this.signature += param.getSignature();
|
|
if (i < this.params.length - 1) {
|
|
this.signature += ',';
|
|
}
|
|
});
|
|
this.signature += ')';
|
|
|
|
// Compute selector
|
|
this.selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(this.signature).slice(0, 4)));
|
|
|
|
console.log(`--SIGNATURE--\n${this.signature}\n---------\n`);
|
|
console.log(`--SELECTOR--\n${this.selector}\n---------\n`);
|
|
}
|
|
|
|
encode(args: any[]): string {
|
|
const calldata = new Calldata(this.selector, this.params.length);
|
|
const params = this.params;
|
|
_.each(params, (param: DataType, i: number) => {
|
|
param.assignValue(args[i]);
|
|
param.bind(calldata, CalldataSection.PARAMS);
|
|
});
|
|
|
|
console.log(calldata);
|
|
|
|
return calldata.getHexValue();
|
|
|
|
//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('Yessir', async () => {
|
|
const method = new AbiEncoder.Method(simpleAbi);
|
|
const calldata = method.encode([new BigNumber(5), 'five']);
|
|
console.log(calldata);
|
|
expect(true).to.be.true();
|
|
});
|
|
});
|
|
|
|
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);
|
|
});
|
|
});
|
|
});
|