import { JSONSchema4 } from "json-schema"; import { JsonFragmentType, JsonFragment } from "../abis.model"; const types: any = { address: "string", bool: "boolean", bytes64: "string", bytes32: "string", bytes16: "string", bytes4: "string", bytes: "string", uint256: "number", uint128: "number", uint64: "number", uint32: "number", uint16: "number", uint8: "number", string: "string", "address[]": "string[]", "string[]": "string[]", "uint256[]": "number[]", "bytes[]": "string[]", "uint16[]": "number[]", "uint32[]": "number[]", "uint128[]": "number[]", "uint8[]": "number[]", }; const generateArguments = (config: ReadonlyArray) => { return config .map((type, index) => { if (!type.name) { type.name = `argument_${index}`; } return type; }) .map(({ name, type, components }) => { if (components) { if (type === 'tuple[]') { const tupleTypes = generateTupleArgumentsWithTypes(components); return `${name}: {${tupleTypes}}[]`; } else if (type === 'tuple') { const tupleTypes = generateTupleArgumentsWithTypes(components); return `${name}: {${tupleTypes}}`; } } return `${name}: ${types[type as any] || "string"}`; }) .join(", "); }; const generateTupleArguments = (config: ReadonlyArray) => { return config .map((type, index) => { if (!type.name) { type.name = `argument_${index}`; } return type; }) .map(({ type }) => types[type as any] || "string") .join(","); }; const generateTupleArgumentsWithTypes = ( config: ReadonlyArray ) => { return config .map((type, index) => { if (!type.name) { type.name = `argument_${index}`; } return type; }) .map(({ type, name, components }) => { if (components) { if (type === 'tuple[]') { const nestedTypes = generateTupleArgumentsWithTypes(components); return `${name}: {${nestedTypes}}[]`; } else if (type === 'tuple') { const nestedTypes = generateTupleArgumentsWithTypes(components); return `${name}: {${nestedTypes}}`; } } return `${name}: ${types[type as any] || "string"}`; }) .join(";\n"); }; const getReturnTypes = ( config: ReadonlyArray, stateMutability: string, eventType: string ) => { let names: string; if (config.length === 1 && stateMutability === "view") { const [{ type, components }] = config; if (components) { names = generateTupleArgumentsWithTypes(components); if (type === "tuple[]") { return [names, `{${generateArguments(components)}}[]`]; } return [names, `{${names}}`]; } else { return [null, `${types[type as string]}`]; } } const args = generateArguments(config); if (stateMutability !== "view") { if (config.length > 0) { return [null, `{${args}, wait: () => Promise<${eventType}>}`]; } return [null, `{wait: () => Promise<${eventType}>}`]; } if (args.length) { return [null, `{${args}}`]; } return [null, `void`]; }; const generateMainFunctions = ( mainFunctions: JsonFragment[], eventType: string ) => { const main = mainFunctions.reduce((prev, curr) => { let args = generateArguments(curr.inputs as ReadonlyArray); if (args?.length) { args = `${args}, overrides?: Overrides`; } else { args = `overrides?: Overrides`; } const [names, types] = getReturnTypes( curr.outputs as ReadonlyArray, curr.stateMutability as string, eventType ); return { ...prev, [curr.name as string]: { description: `${names ? "Response type names are: \n\n" + names : ""}`, instanceOf: "Function", tsType: `(${args}) => Promise<${types}>`, }, }; }, {}); return main; }; const generageEventTypes = (eventsType: string) => { return { events: { type: "array", title: `AutEvent`, items: { type: "object", properties: { event: { $ref: `#/definitions/${eventsType}`, }, args: { tsType: "Result", }, eventSignature: { type: "string", }, decodeError: { tsType: "Error", }, decode: { tsType: `(data: string, topics?: Array) => any`, }, removeListener: { tsType: `() => void`, }, getBlock: { tsType: `() => Promise`, }, getTransaction: { tsType: `() => Promise`, }, getTransactionReceipt: { tsType: `() => Promise`, }, blockNumber: { type: "number", }, blockHash: { type: "string", }, transactionIndex: { type: "number", }, removed: { type: "boolean", }, address: { type: "string", }, data: { type: "string", }, transactionHash: { type: "string", }, logIndex: { type: "number", }, }, required: ["event", "args"], additionalProperties: false, }, }, }; }; export const AutTypeFactory = ( abi: JsonFragment[], typeNamePreffix: string ): JSONSchema4 => { const { functions, functionNames, eventNames } = abi.reduce( (prev, curr: JsonFragment) => { if (curr.type === "function") { (prev.functions as JsonFragment[]).push(curr); (prev.functionNames as string[]).push(curr.name as string); } if (curr.type === "event") { (prev.events as JsonFragment[]).push(curr); (prev.eventNames as string[]).push(curr.name as string); } return prev; }, { functions: [], functionNames: [], events: [], eventNames: [], } ); return { definitions: { [`${typeNamePreffix}ContractEventType`]: { title: `${typeNamePreffix}ContractEventType`, enum: eventNames, tsEnumNames: eventNames, type: "string", }, }, items: [ { title: `${typeNamePreffix}ContractFunctions`, type: "object", properties: generateMainFunctions( functions, `ContractTransactionReceipt` ), required: functionNames, additionalProperties: false, }, { title: `${typeNamePreffix}ContractEvents`, type: "object", properties: generageEventTypes(`${typeNamePreffix}ContractEventType`), required: ["events"], additionalProperties: false, }, ], } as JSONSchema4; };