Use new ABI Encoder for contracts
This commit is contained in:
@@ -456,13 +456,19 @@ export class RawCalldata {
|
||||
private selector: string;
|
||||
private scopes: Queue<number>;
|
||||
|
||||
constructor(value: string | Buffer) {
|
||||
constructor(value: string | Buffer, hasSelectorPrefix: boolean = true) {
|
||||
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
|
||||
if (hasSelectorPrefix) {
|
||||
this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4));
|
||||
this.value = valueBuf.slice(4); // disregard selector
|
||||
} else {
|
||||
this.selector = '0x';
|
||||
this.value = valueBuf;
|
||||
}
|
||||
|
||||
this.offset = 0;
|
||||
this.scopes = new Queue<number>();
|
||||
this.scopes.push(0);
|
||||
|
||||
@@ -6,7 +6,7 @@ import ethUtil = require('ethereumjs-util');
|
||||
var _ = require('lodash');
|
||||
|
||||
export interface DataTypeFactory {
|
||||
create: (dataItem: DataItem, parentDataType: DataType) => DataType;
|
||||
create: (dataItem: DataItem, parentDataType?: DataType) => DataType;
|
||||
mapDataItemToDataType: (dataItem: DataItem) => DataType;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,8 @@ abstract class Number extends PayloadDataType {
|
||||
}
|
||||
}
|
||||
|
||||
public encodeValue(value: BigNumber): Buffer {
|
||||
public encodeValue(value_: BigNumber | string | number): Buffer {
|
||||
const value = new BigNumber(value_, 10);
|
||||
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())) {
|
||||
@@ -465,6 +466,7 @@ export class SolArray extends MemberDataType {
|
||||
export class Method extends MemberDataType {
|
||||
private methodSignature: string;
|
||||
private methodSelector: string;
|
||||
private returnDataTypes: DataType[];
|
||||
|
||||
// TMP
|
||||
public selector: string;
|
||||
@@ -473,7 +475,11 @@ export class Method extends MemberDataType {
|
||||
super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance());
|
||||
this.methodSignature = this.computeSignature();
|
||||
this.selector = this.methodSelector = this.computeSelector();
|
||||
|
||||
this.returnDataTypes = [];
|
||||
const dummy = new Byte({ type: 'byte', name: 'DUMMY' }); // @TODO TMP
|
||||
_.each(abi.outputs, (dataItem: DataItem) => {
|
||||
this.returnDataTypes.push(this.getFactory().create(dataItem, dummy));
|
||||
});
|
||||
}
|
||||
|
||||
private computeSignature(): string {
|
||||
@@ -501,6 +507,23 @@ export class Method extends MemberDataType {
|
||||
return value;
|
||||
}
|
||||
|
||||
public decodeReturnValues(returndata: string, rules?: DecodingRules): any {
|
||||
//console.log('O'.repeat(100), '\n', returndata, '\n', this.returnDataTypes, 'P'.repeat(100));
|
||||
|
||||
const returnValues: any[] = [];
|
||||
const rules_ = rules ? rules : { structsAsObjects: false } as DecodingRules;
|
||||
const rawReturnData = new RawCalldata(returndata, false);
|
||||
_.each(this.returnDataTypes, (dataType: DataType) => {
|
||||
returnValues.push(dataType.generateValue(rawReturnData, rules_));
|
||||
});
|
||||
|
||||
//console.log('*'.repeat(40), '\n', JSON.stringify(returnValues), '\n', '*'.repeat(100));
|
||||
/*if (returnValues.length === 1) {
|
||||
return returnValues[0];
|
||||
}*/
|
||||
return returnValues;
|
||||
}
|
||||
|
||||
public getSignature(): string {
|
||||
return this.methodSignature;
|
||||
}
|
||||
@@ -538,12 +561,15 @@ export class EvmDataTypeFactory implements DataTypeFactory {
|
||||
throw new Error(`Unrecognized data type: '${dataItem.type}'`);
|
||||
}
|
||||
|
||||
public create(dataItem: DataItem, parentDataType: DataType): DataType {
|
||||
public create(dataItem: DataItem, parentDataType?: DataType): DataType {
|
||||
const dataType = this.mapDataItemToDataType(dataItem);
|
||||
if (dataType.isStatic()) {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
if (parentDataType === undefined) { // @Todo -- will this work for return values?
|
||||
throw new Error(`Trying to create a pointer`);
|
||||
}
|
||||
const pointer = new Pointer(dataType, parentDataType);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,35 @@ describe.only('ABI Encoder', () => {
|
||||
|
||||
describe.only('ABI Tests at Method Level', () => {
|
||||
|
||||
it.only('Should reuse duplicated strings in string array', async () => {
|
||||
const method = new AbiEncoder.Method(AbiSamples.GAbi);
|
||||
|
||||
const args = [
|
||||
{
|
||||
a: new BigNumber(5),
|
||||
e: '0x616161',
|
||||
b: 'aaa',
|
||||
f: '0xe41d2489571d322189246dafa5ebde1f4699f498'
|
||||
}
|
||||
]
|
||||
|
||||
// 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(method.getSignature());
|
||||
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 reuse duplicated strings in string array', async () => {
|
||||
const method = new AbiEncoder.Method(AbiSamples.stringAbi);
|
||||
const strings = [
|
||||
|
||||
@@ -34,6 +34,40 @@ export const stringAbi = {
|
||||
type: 'function',
|
||||
} as MethodAbi;
|
||||
|
||||
|
||||
export const GAbi = {
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
components: [{
|
||||
name: 'a',
|
||||
type: 'uint256',
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'e',
|
||||
type: 'bytes',
|
||||
},
|
||||
{
|
||||
name: 'f',
|
||||
type: 'address',
|
||||
}],
|
||||
|
||||
name: 'f',
|
||||
type: 'tuple',
|
||||
|
||||
}
|
||||
],
|
||||
name: 'simpleFunction',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function',
|
||||
} as MethodAbi;
|
||||
|
||||
export const optimizerAbi2 = {
|
||||
constant: false,
|
||||
inputs: [
|
||||
|
||||
Reference in New Issue
Block a user