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 });