github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/js/src/contracts/abi.test.ts (about)

     1  import * as assert from 'assert';
     2  import { transformToFullName } from './abi';
     3  import { compile } from './compile';
     4  
     5  const source = `
     6  pragma solidity >=0.0.0;
     7  
     8  contract foo {
     9  	uint foobar;
    10  
    11  	function addFoobar(uint n, bool b) public {
    12  	  if (b) {
    13  		  foobar += n;
    14  	  }
    15  	}
    16  
    17  	function getFoobar() public view returns (uint n) {
    18  		n = foobar;
    19  	}
    20  }
    21  `;
    22  
    23  describe('ABI helpers', () => {
    24    it('Can extract correct names from ABI', () => {
    25      const {
    26        code: { abi },
    27      } = compile(source, 'foo');
    28      console.log(JSON.stringify(abi));
    29      assert.strictEqual(transformToFullName(abi[0]), 'addFoobar(uint256,bool)');
    30    });
    31  
    32    it('Derive Typescript contract type from ABI', () => {
    33      // This is a nearly-working proof of concept for generating a contract's type definition
    34      // directly from the JSON ABI...
    35  
    36      const abiConst = [
    37        {
    38          name: 'addFoobar',
    39          inputs: [
    40            { internalType: 'uint256', name: 'n', type: 'uint256' },
    41            { internalType: 'bool', name: 'b', type: 'bool' },
    42          ],
    43          outputs: [],
    44          stateMutability: 'nonpayable',
    45          type: 'function',
    46        },
    47        {
    48          name: 'getFoobar',
    49          inputs: [{ internalType: 'address', name: 'addr', type: 'address' }],
    50          outputs: [{ internalType: 'uint256', name: 'n', type: 'uint256' }],
    51          stateMutability: 'view',
    52          type: 'function',
    53        },
    54      ] as const;
    55  
    56      type abi = typeof abiConst;
    57  
    58      type Func = abi[number];
    59  
    60      type FunctionName = Func['name'];
    61  
    62      type FuncByName<T, Name extends FunctionName> = T extends { name: Name; type: 'function' } ? T : never;
    63  
    64      type Picker<name extends FunctionName> = {
    65        [i in keyof abi]: FuncByName<abi[i], name>;
    66      };
    67  
    68      type ValueOf<T> = T[keyof T];
    69  
    70      type PickFunctionByName<name extends FunctionName> = ValueOf<Picker<name>>;
    71  
    72      type Address = string;
    73  
    74      type Type<T> = T extends 'uint256' ? number : T extends 'bool' ? boolean : T extends 'address' ? Address : never;
    75  
    76      // **mumble** something about distribution
    77      type PickValue<T, U> = U extends keyof T ? Pick<T, U>[keyof Pick<T, U>] : never;
    78  
    79      type TypesOf<T> = { [k in keyof T]: Type<PickValue<T[k], 'type'>> };
    80  
    81      type FunctionInputs<T extends FunctionName> = PickFunctionByName<T>['inputs'];
    82  
    83      type FunctionOutputs<T extends FunctionName> = PickFunctionByName<T>['outputs'];
    84  
    85      const getFoobarABI: PickFunctionByName<'getFoobar'> = {
    86        name: 'getFoobar',
    87        inputs: [{ internalType: 'address', name: 'addr', type: 'address' }],
    88        outputs: [{ internalType: 'uint256', name: 'n', type: 'uint256' }],
    89        stateMutability: 'view',
    90        type: 'function',
    91      };
    92  
    93      const addFoobarInputs: FunctionInputs<'addFoobar'> = [
    94        { internalType: 'uint256', name: 'n', type: 'uint256' },
    95        { internalType: 'bool', name: 'b', type: 'bool' },
    96      ];
    97  
    98      const addFoobarArgs: TypesOf<FunctionInputs<'addFoobar'>> = [1, true];
    99      const getFoobarArgs: TypesOf<FunctionInputs<'getFoobar'>> = ['address'];
   100  
   101      type Args<Name extends FunctionName> = TypesOf<FunctionInputs<Name>>;
   102  
   103      // Everything above this line compiles, which make me think the following should work, however...
   104  
   105      type Contract = {
   106        // This line is a compiler error: TS2370: A rest parameter must be of an array type.
   107        // Uncomment to experiment further
   108        // [name in FunctionName]: (...args: Args<name>) => TypesOf<FunctionOutputs<name>>
   109        [name in FunctionName]: (...args: any[]) => TypesOf<FunctionOutputs<name>>;
   110      };
   111  
   112      // Note: my IDE actually actually seem to be type-checking these correctly
   113      // despite the compiler error on the function arg spread above! So close!
   114      const contract: Contract = {
   115        // uint256, bool => ()
   116        addFoobar: (n: number, b: boolean) => [],
   117        // address => uint256
   118        getFoobar: (n: Address) => [3],
   119      };
   120    });
   121  });