dethcrypto / TypeChain

🔌 TypeScript bindings for Ethereum smart contracts
MIT License
2.77k stars 365 forks source link

Give names for intermediate structs #715

Open Pzixel opened 2 years ago

Pzixel commented 2 years ago

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.

zemse commented 2 years ago

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.

Pzixel commented 2 years ago

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

zemse commented 2 years ago

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 ```ts /* 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; b: PromiseOrValue; c: PromiseOrValue; }; export type InputStructOutput = [BigNumber, BigNumber, boolean] & { a: BigNumber; b: BigNumber; c: boolean; }; export type OutputStruct = { d: PromiseOrValue; e: PromiseOrValue; }; 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; interface: TypechainTestInterface; queryFilter( event: TypedEventFilter, fromBlockOrBlockhash?: string | number | undefined, toBlock?: string | number | undefined ): Promise>; listeners( eventFilter?: TypedEventFilter ): Array>; listeners(eventName?: string): Array; removeAllListeners( eventFilter: TypedEventFilter ): this; removeAllListeners(eventName?: string): this; off: OnEvent; on: OnEvent; once: OnEvent; removeListener: OnEvent; functions: { test( input: TypechainTest.InputStruct, overrides?: CallOverrides ): Promise< [TypechainTest.OutputStructOutput] & { result: TypechainTest.OutputStructOutput; } >; }; test( input: TypechainTest.InputStruct, overrides?: CallOverrides ): Promise; callStatic: { test( input: TypechainTest.InputStruct, overrides?: CallOverrides ): Promise; }; filters: {}; estimateGas: { test( input: TypechainTest.InputStruct, overrides?: CallOverrides ): Promise; }; populateTransaction: { test( input: TypechainTest.InputStruct, overrides?: CallOverrides ): Promise; }; } ```
Pzixel commented 2 years ago

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