From b0ebc6fa14adc08c074bea4d275cc31504c95d55 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 18:42:40 -0800 Subject: Tests for decoding return values + Ability to encode return values --- packages/utils/src/abi_encoder/evm_data_types.ts | 19 +++---- packages/utils/test/abi_encoder_test.ts | 69 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 10e7b987b..b862e9396 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -276,7 +276,7 @@ export class Byte extends PayloadDataType { if (valueBuf.byteLength > this.width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { @@ -475,6 +475,7 @@ export class Method extends MemberDataType { private methodSignature: string; private methodSelector: string; private returnDataTypes: DataType[]; + private returnDataItem: DataItem; // TMP public selector: string; @@ -484,6 +485,7 @@ export class Method extends MemberDataType { this.methodSignature = this.computeSignature(); this.selector = this.methodSelector = this.computeSelector(); this.returnDataTypes = []; + this.returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; const dummy = new Byte({ type: 'byte', name: 'DUMMY' }); // @TODO TMP _.each(abi.outputs, (dataItem: DataItem) => { this.returnDataTypes.push(this.getFactory().create(dataItem, dummy)); @@ -518,20 +520,19 @@ 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)); + public encodeReturnValues(value: any, rules?: EncodingRules): string { + const returnDataType = new Tuple(this.returnDataItem); + const returndata = returnDataType.encode(value, rules); + return returndata; + } + public decodeReturnValues(returndata: string, rules?: DecodingRules): any { 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; } @@ -547,7 +548,7 @@ export class Method extends MemberDataType { export class EvmDataTypeFactory implements DataTypeFactory { private static instance: DataTypeFactory; - private constructor() {} + private constructor() { } public static getInstance(): DataTypeFactory { if (!EvmDataTypeFactory.instance) { diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index b672d0023..0220984b0 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -5,6 +5,7 @@ import { chaiSetup } from './utils/chai_setup'; import { BigNumber, AbiEncoder } from '../src/'; import * as AbiSamples from './abi_samples'; import * as OptimizedAbis from './optimizer_abis'; +import * as ReturnValueAbis from './return_value_abis'; import { DecodingRules } from '../src/abi_encoder'; import ethUtil = require('ethereumjs-util'); @@ -12,6 +13,74 @@ chaiSetup.configure(); const expect = chai.expect; describe.only('ABI Encoder', () => { + describe('Decode Return Values', () => { + it('No Return Value', async () => { + // Decode return value + const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues); + const returnValue = '0x'; + const decodedReturnValue = method.decodeReturnValues(returnValue); + const expectedDecodedReturnValue: any[] = []; + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(expectedDecodedReturnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Single static return value', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue); + const returnValue = ['0x01020304']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Multiple static return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Single dynamic return value', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue); + const returnValue = ['0x01020304']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Multiple dynamic return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Mixed static/dynamic return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + }); + describe('Optimizer', () => { it('Duplicate Dynamic Arrays with Static Elements', async () => { // Generate calldata -- cgit v1.2.3