typechain-target-ethers-v5
Version:
🔌 TypeChain target for ethers-v5
283 lines (257 loc) • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.codegenAbstractContractFactory = exports.codegenContractFactory = exports.codegenContractTypings = void 0;
const lodash_1 = require("lodash");
const types_1 = require("./types");
const functions_1 = require("./functions");
const common_1 = require("../common");
const reserved_keywords_1 = require("./reserved-keywords");
function codegenContractTypings(contract) {
const contractImports = ['Contract', 'ContractTransaction'];
const allFunctions = lodash_1.values(contract.functions)
.map((fn) => functions_1.codegenFunctions({ returnResultObject: true }, fn) + functions_1.codegenFunctions({ isStaticCall: true }, fn))
.join('');
const optionalContractImports = ['Overrides', 'PayableOverrides', 'CallOverrides'];
optionalContractImports.forEach((importName) => pushImportIfUsed(importName, allFunctions, contractImports));
const template = `
import { ethers, EventFilter, Signer, BigNumber, BigNumberish, PopulatedTransaction } from 'ethers';
import { ${contractImports.join(', ')} } from '@ethersproject/contracts';
import { BytesLike } from '@ethersproject/bytes';
import { Listener, Provider } from '@ethersproject/providers';
import { FunctionFragment, EventFragment, Result } from '@ethersproject/abi';
import { TypedEventFilter, TypedEvent, TypedListener } from './commons';
interface ${contract.name}Interface extends ethers.utils.Interface {
functions: {
${lodash_1.values(contract.functions)
.map((v) => v[0])
.map(generateInterfaceFunctionDescription)
.join('\n')}
};
${lodash_1.values(contract.functions)
.map((v) => v[0])
.map(generateEncodeFunctionDataOverload)
.join('\n')}
${lodash_1.values(contract.functions)
.map((v) => v[0])
.map(generateDecodeFunctionResultOverload)
.join('\n')}
events: {
${lodash_1.values(contract.events)
.map((v) => v[0])
.map(generateInterfaceEventDescription)
.join('\n')}
};
${lodash_1.values(contract.events)
.map((v) => v[0])
.map(generateGetEventOverload)
.join('\n')}
}
export class ${contract.name} extends Contract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
listeners(eventName?: string): Array<Listener>;
off(eventName: string, listener: Listener): this;
on(eventName: string, listener: Listener): this;
once(eventName: string, listener: Listener): this;
removeListener(eventName: string, listener: Listener): this;
removeAllListeners(eventName?: string): this;
listeners<T, G>(eventFilter?: TypedEventFilter<T, G>): Array<TypedListener<T, G>>;
off<T, G>(eventFilter: TypedEventFilter<T, G>, listener: TypedListener<T, G>): this;
on<T, G>(eventFilter: TypedEventFilter<T, G>, listener: TypedListener<T, G>): this;
once<T, G>(eventFilter: TypedEventFilter<T, G>, listener: TypedListener<T, G>): this;
removeListener<T, G>(eventFilter: TypedEventFilter<T, G>, listener: TypedListener<T, G>): this;
removeAllListeners<T, G>(eventFilter: TypedEventFilter<T, G>): this;
queryFilter<T, G>(
event: TypedEventFilter<T, G>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEvent<T & G>>>;
interface: ${contract.name}Interface;
functions: {
${lodash_1.values(contract.functions)
.map(functions_1.codegenFunctions.bind(null, { returnResultObject: true }))
.join('\n')}
};
${lodash_1.values(contract.functions)
.filter((f) => !reserved_keywords_1.reservedKeywords.has(f[0].name))
.map(functions_1.codegenFunctions.bind(null, {}))
.join('\n')}
callStatic: {
${lodash_1.values(contract.functions)
.map(functions_1.codegenFunctions.bind(null, { isStaticCall: true }))
.join('\n')}
};
filters: {
${lodash_1.values(contract.events)
.map((v) => v[0])
.map(generateEvents)
.join('\n')}
};
estimateGas: {
${lodash_1.values(contract.functions)
.map(functions_1.codegenFunctions.bind(null, { overrideOutput: 'Promise<BigNumber>' }))
.join('\n')}
};
populateTransaction: {
${lodash_1.values(contract.functions)
.map(functions_1.codegenFunctions.bind(null, { overrideOutput: 'Promise<PopulatedTransaction>' }))
.join('\n')}
};
}`;
return template;
}
exports.codegenContractTypings = codegenContractTypings;
function codegenContractFactory(contract, abi, bytecode) {
var _a;
const constructorArgs = (contract.constructor && contract.constructor[0] ? types_1.generateInputTypes(contract.constructor[0].inputs) : '') +
`overrides?: ${((_a = contract.constructor[0]) === null || _a === void 0 ? void 0 : _a.stateMutability) === 'payable' ? 'PayableOverrides' : 'Overrides'}`;
const constructorArgNamesWithoutOverrides = contract.constructor && contract.constructor[0] ? generateParamNames(contract.constructor[0].inputs) : '';
const constructorArgNames = constructorArgNamesWithoutOverrides
? `${constructorArgNamesWithoutOverrides}, overrides || {}`
: 'overrides || {}';
if (!bytecode)
return codegenAbstractContractFactory(contract, abi);
// tsc with noUnusedLocals would complain about unused imports
const ethersImports = ['Signer'];
const optionalEthersImports = ['BytesLike', 'BigNumberish'];
optionalEthersImports.forEach((importName) => pushImportIfUsed(importName, constructorArgs, ethersImports));
const ethersContractImports = ['Contract', 'ContractFactory'];
const optionalContractImports = ['PayableOverrides', 'Overrides'];
optionalContractImports.forEach((importName) => pushImportIfUsed(importName, constructorArgs, ethersContractImports));
return `
import { ${ethersImports.join(', ')} } from "ethers";
import { Provider, TransactionRequest } from '@ethersproject/providers';
import { ${ethersContractImports.join(', ')} } from "@ethersproject/contracts";
import type { ${contract.name} } from "../${contract.name}";
export class ${contract.name}${common_1.FACTORY_POSTFIX} extends ContractFactory {
${generateFactoryConstructor(contract, bytecode)}
deploy(${constructorArgs}): Promise<${contract.name}> {
return super.deploy(${constructorArgNames}) as Promise<${contract.name}>;
}
getDeployTransaction(${constructorArgs}): TransactionRequest {
return super.getDeployTransaction(${constructorArgNames});
};
attach(address: string): ${contract.name} {
return super.attach(address) as ${contract.name};
}
connect(signer: Signer): ${contract.name}${common_1.FACTORY_POSTFIX} {
return super.connect(signer) as ${contract.name}${common_1.FACTORY_POSTFIX};
}
static connect(address: string, signerOrProvider: Signer | Provider): ${contract.name} {
return new Contract(address, _abi, signerOrProvider) as ${contract.name};
}
}
const _abi = ${JSON.stringify(abi, null, 2)};
const _bytecode = "${bytecode.bytecode}";
${generateLibraryAddressesInterface(contract, bytecode)}
`;
}
exports.codegenContractFactory = codegenContractFactory;
function codegenAbstractContractFactory(contract, abi) {
return `
import { Contract, Signer } from "ethers";
import { Provider } from "@ethersproject/providers";
import type { ${contract.name} } from "../${contract.name}";
export class ${contract.name}${common_1.FACTORY_POSTFIX} {
static connect(address: string, signerOrProvider: Signer | Provider): ${contract.name} {
return new Contract(address, _abi, signerOrProvider) as ${contract.name};
}
}
const _abi = ${JSON.stringify(abi, null, 2)};
`;
}
exports.codegenAbstractContractFactory = codegenAbstractContractFactory;
function generateFactoryConstructor(contract, bytecode) {
if (!bytecode.linkReferences) {
return `
constructor(signer?: Signer) {
super(_abi, _bytecode, signer);
}
`;
}
const linkRefReplacements = bytecode.linkReferences.map((linkRef) => {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
// We're using a double escape backslash, since the string will be pasted into generated code.
const escapedLinkRefRegex = linkRef.reference.replace(/[.*+?^${}()|[\]\\]/g, '\\\\$&');
const libraryKey = linkRef.name || linkRef.reference;
return `
linkedBytecode = linkedBytecode.replace(
new RegExp("${escapedLinkRefRegex}", "g"),
linkLibraryAddresses["${libraryKey}"].replace(/^0x/, '').toLowerCase(),
);`;
});
return `
constructor(linkLibraryAddresses: ${contract.name}LibraryAddresses, signer?: Signer) {
super(_abi, ${contract.name}${common_1.FACTORY_POSTFIX}.linkBytecode(linkLibraryAddresses), signer);
}
static linkBytecode(linkLibraryAddresses: ${contract.name}LibraryAddresses): string {
let linkedBytecode = _bytecode;
${linkRefReplacements.join('\n')}
return linkedBytecode;
}
`;
}
function generateLibraryAddressesInterface(contract, bytecode) {
if (!bytecode.linkReferences)
return '';
const linkLibrariesKeys = bytecode.linkReferences.map((linkRef) => ` ["${linkRef.name || linkRef.reference}"]: string;`);
return `
export interface ${contract.name}LibraryAddresses {
${linkLibrariesKeys.join('\n')}
};`;
}
function generateInterfaceFunctionDescription(fn) {
return `'${generateFunctionSignature(fn)}': FunctionFragment;`;
}
function generateFunctionSignature(fn) {
return `${fn.name}(${fn.inputs.map((input) => input.type.originalType).join(',')})`;
}
function generateEncodeFunctionDataOverload(fn) {
const methodInputs = [`functionFragment: '${fn.name}'`];
if (fn.inputs.length) {
methodInputs.push(`values: [${fn.inputs.map((input) => types_1.generateInputType(input.type)).join(', ')}]`);
}
else {
methodInputs.push('values?: undefined');
}
return `encodeFunctionData(${methodInputs.join(', ')}): string;`;
}
function generateDecodeFunctionResultOverload(fn) {
return `decodeFunctionResult(functionFragment: '${fn.name}', data: BytesLike): Result;`;
}
function generateParamNames(params) {
return params.map((param) => param.name).join(', ');
}
function generateEvents(event) {
return `
${event.name}(${generateEventTypes(event.inputs)}): TypedEventFilter<${types_1.generateOutputTypes(true, event.inputs.map((input, i) => { var _a; return ({ name: (_a = input.name) !== null && _a !== void 0 ? _a : 'arg' + i, type: input.type }); })).replace(' &', ',')}>;
`;
}
function generateInterfaceEventDescription(event) {
return `'${generateEventSignature(event)}': EventFragment;`;
}
function generateEventSignature(event) {
return `${event.name}(${event.inputs.map((input) => input.type.originalType).join(',')})`;
}
function generateEventTypes(eventArgs) {
if (eventArgs.length === 0) {
return '';
}
return (eventArgs
.map((arg) => {
return `${arg.name}: ${generateEventArgType(arg)}`;
})
.join(', ') + ', ');
}
function generateEventArgType(eventArg) {
return eventArg.isIndexed ? `${types_1.generateInputType(eventArg.type)} | null` : 'null';
}
function generateGetEventOverload(event) {
return `getEvent(nameOrSignatureOrTopic: '${event.name}'): EventFragment;`;
}
function pushImportIfUsed(importName, generatedCode, importArray) {
if (new RegExp(`\\W${importName}(\\W|$)`).test(generatedCode))
importArray.push(importName);
}
//# sourceMappingURL=index.js.map