TypeChain icon indicating copy to clipboard operation
TypeChain copied to clipboard

Give names for intermediate structs

Open Pzixel opened this issue 3 years ago • 4 comments

Consider following contract

contract TypechainTest {
    struct Input {
        uint256 a;
        int128 b;
        bool c;
    }

    struct Output {
        uint256 d;
        int128 e;
    }

    function test(Input calldata input) public pure returns (Output memory result) {
        result.d = input.a;
        result.e = input.b;
    }
}

it leads to following generation:

...
  test(
    input: { a: BigNumberish; b: BigNumberish; c: boolean },
    overrides?: CallOverrides
  ): Promise<[BigNumber, BigNumber] & { d: BigNumber; e: BigNumber }>;

  callStatic: {
    test(
      input: { a: BigNumberish; b: BigNumberish; c: boolean },
      overrides?: CallOverrides
    ): Promise<[BigNumber, BigNumber] & { d: BigNumber; e: BigNumber }>;
  };
...

Which isn't ideal for several reasons. One of the biggest problems that it's not possible to write function like:

let x = prepareArg();
contract.test(x);

Because prepareArg should contain struct Input as part of signature but it's not generated.

I propose generation changed to:

export type Input = { a: BigNumberish; b: BigNumberish; c: boolean };
export type Output = { d: BigNumber; e: BigNumber };

...
  test(
    input: Input,
    overrides?: CallOverrides
  ): Promise<[BigNumber, BigNumber] & Output>;

  callStatic: {
    test(
      input: Input,
      overrides?: CallOverrides
    ): Promise<[BigNumber, BigNumber] & Output>;
  };
...

I can try implementing it if you're agreed this is the way to go.

Pzixel avatar Jun 09 '22 17:06 Pzixel

That's actually weird because there are tests for this case (solidity method, test case). Are you sure this is the case using latest version? Anyway i'll try the example probably later today and confirm.

zemse avatar Jun 13 '22 17:06 zemse

I know that some contracts get their structs generated and some don't. I didn't find the correspondence when happens what.

Thanks for looking at it

Pzixel avatar Jun 13 '22 20:06 Pzixel

I tried with the solidity code and the generated types contain the struct in input. Can you try this in a fresh project? Also can you post the exact ABI that is used to generate types?

Generated types
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
  BaseContract,
  BigNumber,
  BigNumberish,
  BytesLike,
  CallOverrides,
  PopulatedTransaction,
  Signer,
  utils,
} from "ethers";
import type { FunctionFragment, Result } from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
  TypedEventFilter,
  TypedEvent,
  TypedListener,
  OnEvent,
  PromiseOrValue,
} from "../../common";

export declare namespace TypechainTest {
  export type InputStruct = {
    a: PromiseOrValue<BigNumberish>;
    b: PromiseOrValue<BigNumberish>;
    c: PromiseOrValue<boolean>;
  };

  export type InputStructOutput = [BigNumber, BigNumber, boolean] & {
    a: BigNumber;
    b: BigNumber;
    c: boolean;
  };

  export type OutputStruct = {
    d: PromiseOrValue<BigNumberish>;
    e: PromiseOrValue<BigNumberish>;
  };

  export type OutputStructOutput = [BigNumber, BigNumber] & {
    d: BigNumber;
    e: BigNumber;
  };
}

export interface TypechainTestInterface extends utils.Interface {
  functions: {
    "test((uint256,int128,bool))": FunctionFragment;
  };

  getFunction(nameOrSignatureOrTopic: "test"): FunctionFragment;

  encodeFunctionData(
    functionFragment: "test",
    values: [TypechainTest.InputStruct]
  ): string;

  decodeFunctionResult(functionFragment: "test", data: BytesLike): Result;

  events: {};
}

export interface TypechainTest extends BaseContract {
  connect(signerOrProvider: Signer | Provider | string): this;
  attach(addressOrName: string): this;
  deployed(): Promise<this>;

  interface: TypechainTestInterface;

  queryFilter<TEvent extends TypedEvent>(
    event: TypedEventFilter<TEvent>,
    fromBlockOrBlockhash?: string | number | undefined,
    toBlock?: string | number | undefined
  ): Promise<Array<TEvent>>;

  listeners<TEvent extends TypedEvent>(
    eventFilter?: TypedEventFilter<TEvent>
  ): Array<TypedListener<TEvent>>;
  listeners(eventName?: string): Array<Listener>;
  removeAllListeners<TEvent extends TypedEvent>(
    eventFilter: TypedEventFilter<TEvent>
  ): this;
  removeAllListeners(eventName?: string): this;
  off: OnEvent<this>;
  on: OnEvent<this>;
  once: OnEvent<this>;
  removeListener: OnEvent<this>;

  functions: {
    test(
      input: TypechainTest.InputStruct,
      overrides?: CallOverrides
    ): Promise<
      [TypechainTest.OutputStructOutput] & {
        result: TypechainTest.OutputStructOutput;
      }
    >;
  };

  test(
    input: TypechainTest.InputStruct,
    overrides?: CallOverrides
  ): Promise<TypechainTest.OutputStructOutput>;

  callStatic: {
    test(
      input: TypechainTest.InputStruct,
      overrides?: CallOverrides
    ): Promise<TypechainTest.OutputStructOutput>;
  };

  filters: {};

  estimateGas: {
    test(
      input: TypechainTest.InputStruct,
      overrides?: CallOverrides
    ): Promise<BigNumber>;
  };

  populateTransaction: {
    test(
      input: TypechainTest.InputStruct,
      overrides?: CallOverrides
    ): Promise<PopulatedTransaction>;
  };
}

zemse avatar Jun 19 '22 11:06 zemse

All right, let me check it locally once more. Thanks for reply

Pzixel avatar Jun 19 '22 11:06 Pzixel