0xweb
Version:
Contract package manager and other web3 tools
109 lines (91 loc) • 3.91 kB
text/typescript
import alot from 'alot';
import type {
ContractDefinition, StructDefinition
} from '@solidity-parser/parser/dist/src/ast-types';
import { SourceFile, TSourceFileContract } from './SlotsParser/SourceFile';
import { Ast } from './SlotsParser/Ast';
import { ISlotsParserOption } from './SlotsParser/models';
import type { TAbiItem } from '@dequanto/types/TAbi';
import { $require } from '@dequanto/utils/$require';
export namespace SolidityParser {
export async function extractAbi (source: { path: string, code?: string }, contractName?: string, opts?: ISlotsParserOption): Promise<{
abi: TAbiItem[]
source: {
contractName: string,
files: {
[path: string]: { content: string }
}
}
}> {
const sourceFile = new SourceFile(source.path, source.code, opts?.files);
const chain = await sourceFile.getContractInheritanceChain(contractName);
$require.notEq(chain.length, 0, `No contract "${contractName}" found in ${source.path}`);
const abi = await extractAbiInner(chain, opts);
const contract = await sourceFile.getContract();
const content = await sourceFile.getContent();
return {
abi,
source: {
contractName: contract.name ?? contractName,
files: {
[sourceFile.path]: {
content: content
}
}
}
};
}
async function extractAbiInner(inheritanceChain: TSourceFileContract[], opts?: ISlotsParserOption) {
let count = inheritanceChain.length;
let abisDef = await alot(inheritanceChain)
.mapManyAsync(async (item, i) => {
let cloned = { ...item };
// get the base classes for the item, as inheritanceChain has the base(root)->...->derived direction
let inheritance = inheritanceChain.slice(0, i).reverse();
let abis = await extractAbiInnerSingle(cloned, inheritance, opts, count - i - 1);
return abis;
})
.toArrayAsync({ threads: 1 });
return abisDef;
}
async function extractAbiInnerSingle (ctx: ITypeCtx, inheritanceChain: TSourceFileContract[], opts: {
withConstants?: boolean
withImmutables?: boolean
}, inheritanceChainIndex?: number) {
let contract = ctx.contract as ContractDefinition;
if (Ast.isContractDefinition(contract) === false) {
throw new Error(`Contract expected for ${ctx.contract.name}`);
}
let abiFns = await alot(Ast.getFunctionDeclarations(contract))
.filter($fn => {
if ($fn.isConstructor) {
// Skip constructors in base classes
return inheritanceChainIndex === 0;
}
let isPublic = $fn.visibility === 'external' || $fn.visibility === 'public' || $fn.isConstructor;
return isPublic;
})
.mapAsync(async $fn => Ast.getAbi($fn, ctx, inheritanceChain))
.toArrayAsync();
let abiGetters = await alot(Ast.getVariableDeclarations(contract))
.filter($var => {
let isPublic = $var.visibility === 'public';
return isPublic;
})
.mapAsync(async $var => Ast.getAbi($var, ctx, inheritanceChain))
.toArrayAsync();
let abiEvents = await alot(Ast.getEventDefinitions(contract))
.mapAsync(async $event => Ast.getAbi($event, ctx, inheritanceChain))
.toArrayAsync();
return [
...abiFns,
...abiGetters,
...abiEvents,
];
}
}
interface ITypeCtx {
contract: ContractDefinition | StructDefinition
contractBase?: ContractDefinition[]
file: SourceFile
};