Skip to content

Commit

Permalink
add types to typegen
Browse files Browse the repository at this point in the history
  • Loading branch information
vanruch committed Mar 27, 2024
1 parent 1111312 commit 8d49e80
Show file tree
Hide file tree
Showing 6 changed files with 3,386 additions and 16 deletions.
4 changes: 4 additions & 0 deletions evm/evm-codec/src/abi-components/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export interface EventRecord {
data: string;
}

export type EventParams<T extends AbiEvent<any>> = T extends AbiEvent<infer U>
? StructTypes<IndexedCodecs<U>>
: never;

type EventArgs = {
[key: string]: Pretty<Codec<any> & { indexed?: boolean }>;
};
Expand Down
10 changes: 8 additions & 2 deletions evm/evm-codec/src/abi-components/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { slotsCount } from "../utils";
import { Src } from "../src";
import assert from "node:assert";

type FunctionReturn<T> = T extends Codec<infer U>
export type FunctionArguments<F extends AbiFunction<any, any>> =
F extends AbiFunction<infer T, any> ? StructTypes<T> : never;

export type FunctionReturn<F extends AbiFunction<any, any>> =
F extends AbiFunction<any, infer R> ? ReturnType<R> : never;

type ReturnType<T> = T extends Codec<infer U>
? U
: T extends Struct
? StructTypes<T>
Expand Down Expand Up @@ -60,7 +66,7 @@ export class AbiFunction<
return "decode" in value && "encode" in value;
}

decodeResult(output: string): FunctionReturn<R> {
decodeResult(output: string): ReturnType<R> {
if (!this.returnType) {
return undefined as any;
}
Expand Down
2 changes: 1 addition & 1 deletion evm/evm-codec/src/contract-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class ContractBase {
async eth_call<
const T extends Struct,
const R extends Codec<any> | Struct | undefined
>(func: AbiFunction<T, R>, args: StructTypes<T>): Promise<R> {
>(func: AbiFunction<T, R>, args: StructTypes<T>) {
let data = func.encode(args);
let result = await this._chain.client.call("eth_call", [
{ to: this.address, data },
Expand Down
5 changes: 5 additions & 0 deletions evm/evm-codec/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@ export { Sink } from "./sink";
export type { Codec } from "./codec";
export * from "./codecs/primitives";
export * from "./contract-base";
export type { EventParams } from "./abi-components/event";
export type {
FunctionReturn,
FunctionArguments,
} from "./abi-components/function";
import keccak256 from "keccak256";
export { keccak256 };
65 changes: 52 additions & 13 deletions evm/evm-typegen/src/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export class Typegen {

async generate() {
this.out.line(`import * as p from "@subsquid/evm-codec";`);
this.out.line(
`import type { EventParams as EParams, FunctionArguments, FunctionReturn } from "@subsquid/evm-codec";`,
);
this.out.line(
"const { event, fun, indexed, struct, array, fixedArray, ContractBase } = p;",
);
Expand All @@ -27,6 +30,8 @@ export class Typegen {
this.generateEvents();
this.generateFunctions();
this.generateContract();
this.generateEventTypes();
this.generateFunctionTypes();

await this.out.write();
this.log.info(`saved ${this.out.file}`);
Expand Down Expand Up @@ -69,6 +74,9 @@ export class Typegen {
if (f.outputs?.length === 1) {
returnType = getType({ ...f.outputs[0], name: undefined });
}
if (f.outputs?.length > 1) {
returnType = `{${this.toTypes(f.outputs)}}`;
}

this.out.line(
`${this.getPropName(f)}: fun("${this.functionSelector(
Expand All @@ -94,33 +102,24 @@ export class Typegen {
f.outputs?.length
) {
this.out.line();
let argNames = f.inputs.map((a, idx) => a.name || `arg${idx}`);
const ref = this.getRef(f);
let argNames = f.inputs.map((a, idx) => a.name || `_${idx}`);
const ref = this.getPropNameGetter(f);
let args = f.inputs
.map(
(a, idx) =>
`${argNames[idx]}: Parameters<typeof functions${ref}["encode"]>[${idx}]`,
`${argNames[idx]}: Functions["${this.getPropName(f)}"]["Args"]["${argNames[idx]}"]`,
)
.join(", ");
this.out.block(`${this.getPropName(f)}(${args})`, () => {
this.out.line(
`return this.eth_call(functions${ref}, [${argNames.join(", ")}])`,
`return this.eth_call(functions${ref}, {${argNames.join(", ")}})`,
);
});
}
}
});
}

private getRef(item: AbiEvent | AbiFunction): string {
let key = this.getPropName(item);
if (key[0] == "'") {
return `[${key}]`;
} else {
return "." + key;
}
}

private cannonicalType(param: AbiParameter): string {
if (!param.type.startsWith("tuple")) {
return param.type;
Expand All @@ -145,6 +144,14 @@ export class Typegen {
}
}

private getPropNameGetter(item: AbiEvent | AbiFunction): string {
if (this.getOverloads(item) == 1) {
return "." + item.name;
} else {
return `["${this.sighash(item)}"]`;
}
}

private getOverloads(item: AbiEvent | AbiFunction): number {
if (item.type === "event") {
return this.eventOverloads()[item.name];
Expand All @@ -153,6 +160,38 @@ export class Typegen {
}
}

private generateEventTypes() {
const events = this.getEvents();
if (events.length == 0) {
return;
}
this.out.line();
this.out.block(`export type EventParams =`, () => {
for (let e of events) {
const propName = this.getPropNameGetter(e);
this.out.line(
`${this.getPropName(e)}: EParams<typeof events${propName}>,`,
);
}
});
}

private generateFunctionTypes() {
let functions = this.getFunctions();
if (functions.length == 0) {
return;
}
this.out.line();
this.out.block(`export type Functions =`, () => {
for (let f of functions) {
const propName = this.getPropNameGetter(f);
this.out.line(
`${this.getPropName(f)}: { Args: FunctionArguments<typeof functions${propName}>, Return: FunctionReturn<typeof functions${propName}> },`,
);
}
});
}

@def
private functionOverloads(): Record<string, number> {
let overloads: Record<string, number> = {};
Expand Down
Loading

0 comments on commit 8d49e80

Please sign in to comment.