import { capitalize } from "@axelarjs/utils/string"; import type { ABIItem } from "./types"; import { parseInputType } from "./utils"; export const INDEX_FILE = ({ fileName = "", constantName = "", pascalName = "", clientPath = "", hasArgs = false, hasReadFns = false, }) => ` import { Chain } from "viem"; import { PublicContractClient } from "${clientPath}"; import ABI_FILE from "./${fileName}.abi"; ${ hasReadFns && hasArgs ? `import { create${pascalName}ReadClient } from "./${fileName}.args";` : "" } ${hasReadFns ? `const createReadClient = create${pascalName}ReadClient;` : ""} ${hasArgs ? `export * from "./${fileName}.args";` : ""} export const ${constantName}_ABI = ABI_FILE.abi; /** * ${pascalName}Client * * @description Type-safe contract client for ${pascalName} * * @example * * import { sepolia } from "viem/chains"; * * const client = create${pascalName}Client({ * chain: sepolia, * address: "0x1234..." * }); */ export class ${pascalName}Client extends PublicContractClient< typeof ABI_FILE.abi > { static ABI = ABI_FILE.abi; static contractName = ABI_FILE.contractName; ${ hasReadFns ? `public readonly reads: ReturnType;` : "" } constructor(options: { chain: Chain; address: \`0x\${string}\` }) { super({ abi: ${constantName}_ABI, address: options.address, chain: options.chain, }); ${hasReadFns ? `this.reads = createReadClient(this);` : ""} } } export const create${pascalName}Client = (options: { chain: Chain; address: \`0x\${string}\` }) => new ${pascalName}Client(options); `; function getDefaultArgName(functionName: string, argIndex: number) { if (functionName === "allowance") { return ["owner", "spender"][argIndex]; } return `${functionName}Arg${argIndex}`; } export const ARGS_FILE = ({ pascalName = "", abiFns = [] as ABIItem[], readFns = [] as ABIItem[], fileName = "", constantName = "", clientPath = "", }) => { const toABIFnEncoder = ({ name, inputs }: ABIItem) => { const argNames = inputs .map((input, i) => input.name || getDefaultArgName(name, i)) .join(", "); const argsType = inputs .map( (input, i) => `${input.name || getDefaultArgName(name, i)}: ${parseInputType( input )}` ) .join("; "); const fnName = capitalize(name); const typeName = `${pascalName}${fnName}Args`; return ` export type ${typeName} = {${argsType}} /** * Factory function for ${pascalName}.${name} function args */ export const encode${pascalName}${fnName}Args = ({${argNames}}: ${typeName}) => [${argNames}] as const; /** * Encoder function for ${pascalName}.${name} function data */ export const encode${pascalName}${fnName}Data = ({${argNames}}: ${typeName}): \`0x\${string}\` => encodeFunctionData({ functionName: "${name}", abi: ABI_FILE.abi, args: [${argNames}] });`; }; const readsClient = readFns?.length ? `export function create${pascalName}ReadClient( publicClient: PublicContractClient ) { return { ${readFns .map(({ name, inputs }) => inputs.length > 0 ? `"${name}"(${name}Args: ${pascalName}${capitalize( name )}Args) { const encoder = ${constantName}_ENCODERS["${name}"]; const encodedArgs = encoder.args(${name}Args); return publicClient.read("${name}", { args: encodedArgs }); }` : `"${name}"() { return publicClient.read("${name}"); }` ) .join(",\n")} } }` : ""; const abiFnsWithInputs = abiFns.filter((x) => x.inputs.length > 0); return ` ${ abiFnsWithInputs.length ? `import { encodeFunctionData } from "viem"` : "" }; ${ readFns.length > 0 ? `import type { PublicContractClient } from "${ clientPath.startsWith(".") ? "../../PublicContractClient" : "@axelarjs/evm" }";` : "" } import ABI_FILE from "./${fileName}.abi"; ${abiFnsWithInputs.map(toABIFnEncoder).join("\n\n")} export const ${constantName}_ENCODERS = { ${abiFnsWithInputs .map( ({ name }) => `"${name}": { args: encode${pascalName}${capitalize(name)}Args, data: encode${pascalName}${capitalize(name)}Data, }` ) .join(",\n")} } ${readsClient}`; }; export const GENERATED_DISCLAIMER = ({ abiPath = "" }) => ` /* eslint-disable @typescript-eslint/no-explicit-any */ /** * This file was generated by scripts/codegen.ts * * Original abi file: * - ${abiPath} * * DO NOT EDIT MANUALLY */ `;