Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Use Compressed Calldata in Contract Wrappers #1475

Merged
merged 28 commits into from
Jan 14, 2019

Conversation

hysz
Copy link
Contributor

@hysz hysz commented Dec 24, 2018

Description

The contract templates now use the optimized ABI encoder to compress all calldata.

We wanted to do this without changing the interface of the templates at all, so a few minor changes were made to the encoder:

  1. Enforce lowercase addresses
    (this also fixes a bug in current return value decoding, where nested addresses would not always be decoded)
  2. The types uint8 and int8 are decoded as number instead of BigNumber
  3. Convenience functions to get decoded data as an array
  4. Convenience function to create an encoder from a signature.
    Ex, signatures like 'string' and '(string,uint256)' and '(foo string,bar uint256)' can now all be used to instantiate an optimized encoder.

In a subsequent PR we'll add the ability for templates to decode calldata.

Testing instructions

Types of changes

Checklist:

  • Prefix PR title with [WIP] if necessary.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • Add new entries to the relevant CHANGELOG.jsons.

@coveralls
Copy link

coveralls commented Dec 24, 2018

Coverage Status

Coverage decreased (-0.06%) to 85.381% when pulling b081785 on feature/monorepo/useNewAbiEncoder into 797d7c7 on development.

@hysz hysz requested a review from abandeali1 December 24, 2018 05:56
@hysz hysz changed the title [WIP] Use Compressed Calldata in Contract Wrappers Use Compressed Calldata in Contract Wrappers Dec 24, 2018
@LogvinovLeon
Copy link
Contributor

Why did we remove all generated wrappers?

@@ -275,6 +278,19 @@ export class ExchangeWrapper {
);
return data;
}
public abiDecodeFillOrder(data: string): AbiDecodedFillOrderData {
// Lookup fillOrder ABI in exchange abi
const fillOrderAbi = _.find(this._exchange.abi, (value: AbiDefinition) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

Suggested change
const fillOrderAbi = _.find(this._exchange.abi, (value: AbiDefinition) => {
const fillOrderAbi = _.find(this._exchange.abi, { name: 'fillOrder' }) as MethodAbi;

We don't usually use that form but I feel like here it's more readable. It's not type safe, but the current solution is not either.

@@ -51,8 +51,26 @@ export abstract class DataType {
return value;
}

public decodeAsArray(returndata: string, rules?: DecodingRules): any {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public decodeAsArray(returndata: string, rules?: DecodingRules): any {
public decodeAsArray(returndata: string, rules?: DecodingRules): any[] {


export interface AbiDecodedFillOrderData {
order: SignedOrder;
akerAssetFillAmount: BigNumber;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
akerAssetFillAmount: BigNumber;
makerAssetFillAmount: BigNumber;

@@ -1,4 +1,4 @@
import { abiUtils, BigNumber } from '@0x/utils';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were we able to remove abiUtils and ethers.js entirely from our codebase?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abiUtils is still used to rename overloaded methods and assert correct encoding.

ethers is used by deployAsync.
There is a task to remove this from deployAsync after which we can remove ethers.js.

);
}
const memberBlock = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, block);
const memberValue: any = (obj as { [key: string]: any })[memberName];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const memberValue: any = (obj as { [key: string]: any })[memberName];
const memberValue: any = (obj as ObjectMap<any>)[memberName];

@@ -44,7 +44,19 @@ export class MethodDataType extends AbstractSetDataType {
return returnValues;
}

public getSignature(): string {
public decodeReturnValuesAsArray(returndata: string, rules?: DecodingRules): any {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public decodeReturnValuesAsArray(returndata: string, rules?: DecodingRules): any {
public decodeReturnValuesAsArray(returnData: string, rules?: DecodingRules): any {

Always camel--case variable names

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the return type here be any[]?

return this._destination.getSignature(false);
}

public getSignature(detailed?: boolean): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public getSignature(detailed?: boolean): string {
public getSignature(isDetailed?: boolean): string {

Stick to boolean naming conventions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's investigate why was this not caught by linter rule

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it's because it's a param name and not a variable. We should improve our linter rule.

@@ -0,0 +1,101 @@
import { DataItem } from 'ethereum-types';
Copy link
Contributor

@fabioberger fabioberger Jan 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this file name not snake-case? It should be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean snake case?

params.order,
params.takerAssetFillAmount,
params.signature,
);
expect(libsAbiEncodedData).to.be.equal(expectedAbiEncodedData, 'ABIEncodedFillOrderData');
const paramsDecodeddByClient = this.exchangeWrapper.abiDecodeFillOrder(abiDataEncodedByContract);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paramsDecodedByClient

@@ -44,7 +44,19 @@ export class MethodDataType extends AbstractSetDataType {
return returnValues;
}

public getSignature(): string {
public decodeReturnValuesAsArray(returndata: string, rules?: DecodingRules): any {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the return type here be any[]?

@@ -1,1877 +0,0 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma whitespace class-name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are all of the generated wrappers deleted rather than replaced?

@hysz hysz force-pushed the feature/monorepo/useNewAbiEncoder branch 4 times, most recently from 8f2c221 to b183be4 Compare January 10, 2019 22:28

export interface AbiDecodedFillOrderData {
order: SignedOrder;
makerAssetFillAmount: BigNumber;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

takerAssetFillAmount

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

facepalm

]);
const ethersFunction = self._lookupEthersInterface(functionSignature).functions.owners;
const encodedData = ethersFunction.encode([index_0
const encodedData = self._strictEncodeArguments('owners(uint256)', [index_0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't particularly matter since these are auto-generated, but I'm curious why the formatting is so strange (looks like it was this way before too).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good question. I always figured it had something to do with Handlebars. @LogvinovLeon any ideas?

return returnValuesAsArray;
}

public decodeReturnValuesAsArrayOrNull(returnData: string, rules?: DecodingRules): any {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also return any[].

@@ -44,7 +44,19 @@ export class MethodDataType extends AbstractSetDataType {
return returnValues;
}

public getSignature(): string {
public decodeReturnValuesAsArray(returnData: string, rules?: DecodingRules): any[] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For all of these functions with an any return type (or variant), should the signatures include a generic type? E.g:

public decodeReturnValuesAsArray<T>(returnData: string, rules?: DecodingRules): T[]

Thoughts @fabioberger @LogvinovLeon @hysz ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this idea.

@@ -44,7 +44,20 @@ export class MethodDataType extends AbstractSetDataType {
return returnValues;
}

public getSignature(): string {
public strictDecodeReturnValue<T>(returndata: string, rules?: DecodingRules): T {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a point in having a non-strict version of this still? I feel like we could probably add generic types to the regular decodeReturnValues.

Also this doesn't have to be in this PR if it's a lot of changes, but we should add generic types to the regular decode function as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created a task to use generics for all decoding. Shouldn't take long but I think it makes sense to have in a separate PR as well.

@hysz hysz force-pushed the feature/monorepo/useNewAbiEncoder branch from b6a298f to b081785 Compare January 14, 2019 18:50
@hysz hysz merged commit 1c25d8e into development Jan 14, 2019
@hysz hysz deleted the feature/monorepo/useNewAbiEncoder branch March 16, 2019 04:35
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants