import { ConfigurableModule, NoConfig, Presets } from "@proto-kit/common"; import { container, injectable } from "tsyringe"; import { NetworkState, RuntimeTransaction, RuntimeMethodExecutionContext, RuntimeMethodExecutionData, RuntimeMethodExecutionDataStruct, } from "@proto-kit/protocol"; import { FlexibleProvablePure, Provable, Bool } from "o1js"; import { runtimeMethodNamesMetadataKey } from "../method/runtimeMethod"; import { OutgoingMessages, OutgoingMessagesRecord, } from "../messages/OutgoingMessages"; import { RuntimeEnvironment } from "./RuntimeEnvironment"; const errors = { inputDataNotSet: () => new Error("Input data for runtime execution not set"), }; type EventRecord = Record>; type InferProvable> = T extends Provable ? U : never; export class RuntimeEvents { public constructor(private readonly events: Events) {} public emitIf( condition: Bool, eventName: Key, event: InferProvable ) { if (this.events === undefined) { throw new Error( "'events' property not defined, make sure to define the event types on your runtimemodule" ); } const eventType: FlexibleProvablePure = this.events[eventName]; if (typeof eventName !== "string") { throw new Error("Only string"); } return container .resolve(RuntimeMethodExecutionContext) .addEvent(eventType, event, eventName, condition); } public emit( eventName: Key, event: InferProvable ) { this.emitIf(Bool(true), eventName, event); } } /** * Base class for runtime modules providing the necessary utilities. */ @injectable() export class RuntimeModule< Config = NoConfig, > extends ConfigurableModule { public static presets: Presets = {}; /** * Holds all method names that are callable throw transactions */ public readonly runtimeMethodNames: string[] = []; /** * This property exists only to typecheck that the RuntimeModule * was extended correctly in e.g. a decorator. We need at least * one non-optional property in this class to make the typechecking work. */ public isRuntimeModule = true; public name?: string; public parent?: RuntimeEnvironment; public events?: RuntimeEvents = undefined; public messages?: OutgoingMessages = undefined; public constructor() { super(); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const methodNames: string[] | undefined = Reflect.getMetadata( runtimeMethodNamesMetadataKey, this ); this.runtimeMethodNames = methodNames ?? []; } public getInputs(): RuntimeMethodExecutionData { return Provable.witness(RuntimeMethodExecutionDataStruct, () => { const { input } = container.resolve( RuntimeMethodExecutionContext ); if (input === undefined) { throw errors.inputDataNotSet(); } return input; }); } public get transaction(): RuntimeTransaction { return this.getInputs().transaction; } public get network(): NetworkState { return this.getInputs().networkState; } }