From a6a058120c7d4c1d34b5d80695ac463f137b9f6b Mon Sep 17 00:00:00 2001 From: Matias Volpe Date: Wed, 5 Apr 2023 12:45:10 -0300 Subject: [PATCH] feat: add contract.emissions variants --- examples/ink/interact.eg.ts | 10 +++-- patterns/ink/InkMetadataRune.ts | 21 ++++++++- patterns/ink/InkRune.ts | 78 ++++++++++++++++++++++++++------- patterns/ink/events.ts | 43 ++++++++++-------- 4 files changed, 114 insertions(+), 38 deletions(-) diff --git a/examples/ink/interact.eg.ts b/examples/ink/interact.eg.ts index 70adc3b61..053fe30e2 100644 --- a/examples/ink/interact.eg.ts +++ b/examples/ink/interact.eg.ts @@ -45,14 +45,18 @@ console.log("initial state:", initialState) await contract .tx({ sender: alice.publicKey, - method: "flip", + // TODO: change ink contract to an ERC-20 implementation + method: "inc_by_with_event", + args: [7], }) .signed(signature({ sender: alice })) .sent() .dbgStatus("Flip:") .inBlockEvents() - // .pipe((events) => Rune.resolve(events)) - .pipe(contract.filterContractEvents) + // .pipe(contract.filterContractEmittedEvents) + // .pipe(contract.emissions.bind(contract)) + // .pipe(contract.emissionsVariant2.bind(contract)) + .pipe(contract.emissionsVariant3.bind(contract)) .dbg("filtered events") .run() diff --git a/patterns/ink/InkMetadataRune.ts b/patterns/ink/InkMetadataRune.ts index dca5d8af4..6395061c5 100644 --- a/patterns/ink/InkMetadataRune.ts +++ b/patterns/ink/InkMetadataRune.ts @@ -13,7 +13,7 @@ import { } from "../../mod.ts" import { transformTys } from "../../scale_info/mod.ts" import { $contractsApiInstantiateArgs, $contractsApiInstantiateResult, Weight } from "./codecs.ts" -import { Callable, InkMetadata, parse } from "./InkMetadata.ts" +import { Callable, Event, InkMetadata, parse } from "./InkMetadata.ts" import { InkRune } from "./InkRune.ts" // TODO: `onInstantiated` @@ -66,6 +66,25 @@ export class InkMetadataRune extends Rune { ) } + decodeEvent(...[event]: RunicArgs) { + return Rune.tuple([ + this.into(ValueRune).access("V3", "spec", "events"), + this.codecs, + ]).map(([events, codecs]) => + $.taggedUnion( + "type", + events.map((event) => + $.variant( + event.label, + $.object(...event.args.map((arg) => $.field(arg.label, codecs[arg.type.type]!))), + ) + ), + ) + ) + .into(CodecRune) + .decoded(event) + } + instantiation( chain: ChainRune, props: RunicArgs, diff --git a/patterns/ink/InkRune.ts b/patterns/ink/InkRune.ts index dbd05f839..498654b47 100644 --- a/patterns/ink/InkRune.ts +++ b/patterns/ink/InkRune.ts @@ -1,5 +1,7 @@ import { equals } from "../../deps/std/bytes.ts" import { + $, + ArrayRune, Chain, CodecRune, Event, @@ -11,7 +13,7 @@ import { ValueRune, } from "../../mod.ts" import { $contractsApiCallArgs, $contractsApiCallResult, Weight } from "./codecs.ts" -import { isContractsRuntimeEvent } from "./events.ts" +import { ContractsRuntimeEvent, isContractEmitted, isContractsRuntimeEvent } from "./events.ts" import { InkMetadataRune } from "./InkMetadataRune.ts" export interface MsgProps { @@ -87,16 +89,21 @@ export class InkRune } filterContractEvents = (...[events]: RunicArgs) => { - // TODO: return all relevant events, not just instantiated return Rune .tuple([Rune.resolve(events), this]) .map(([events, publicKey]) => - events.filter((e) => - isContractsRuntimeEvent(e) && equals(e.event.value.contract, publicKey) + events.filter((e): e is ContractsRuntimeEvent => + isContractsRuntimeEvent(e) + && !!e.event.value.contract + && equals(e.event.value.contract, publicKey) ) ) } + filterContractEmittedEvents = (...[events]: RunicArgs) => { + return this.filterContractEvents(events).map((events) => events.filter(isContractEmitted)) + } + // TODO: improve decodeErrorEvent = (...[failRuntimeEvent]: RunicArgs) => { const $error = this.chain @@ -116,17 +123,58 @@ export class InkRune .unhandle(FailedToDecodeErrorError) } - // // TODO: finish this - // emissions(...[events]: RunicArgs) { - // const $event: $.Codec = null! - // return Rune - // .tuple([Rune.resolve(events), $event]) - // .map(([events, $event]) => - // events - // .filter(isContractEmittedEvent) - // .map((event) => $event.decode(event.event.value.data)) - // ) - // } + emissions(...[events]: RunicArgs) { + // const $event = this.parent.into(ValueRune) + // .access("V3", "spec", "events").map( + // (events) => + // $.taggedUnion( + // "type", + // events.map((event) => { + // return $.variant( + // event.label, + // $.tuple(event.args.map((arg) => this.parent.codecs.access(arg.type.type))), + // ) + // }), + // ), + // ) + const $event = Rune.tuple([ + this.parent.into(ValueRune).access("V3", "spec", "events"), + this.parent.codecs, + ]).map(([events, codecs]) => + $.taggedUnion( + "type", + events.map((event) => + $.variant( + event.label, + $.object(...event.args.map((arg) => $.field(arg.label, codecs[arg.type.type]!))), + ) + ), + ) + ) + return Rune + .tuple([events, $event]) + .map(([events, $event]) => + events + .filter(isContractEmitted) + .map((event) => $event.decode(event.event.value.data)) + ) + } + + emissionsVariant2(...[events]: RunicArgs) { + return Rune.resolve(events) + .map((events) => events.filter(isContractEmitted).map((event) => event.event.value.data)) + .into(ArrayRune) + .mapArray((event) => this.parent.decodeEvent(event)) + .into(ValueRune) + } + + emissionsVariant3(...[events]: RunicArgs) { + return this.filterContractEmittedEvents(events) + .map((events) => events.filter(isContractEmitted).map((event) => event.event.value.data)) + .into(ArrayRune) + .mapArray((event) => this.parent.decodeEvent(event)) + .into(ValueRune) + } } export class MethodNotFoundError extends Error { diff --git a/patterns/ink/events.ts b/patterns/ink/events.ts index 97c404f90..c14148d93 100644 --- a/patterns/ink/events.ts +++ b/patterns/ink/events.ts @@ -1,33 +1,38 @@ -import { AccountIdRune, ApplyExtrinsicEventPhase, Rune, RunicArgs } from "../../mod.ts" +import { AccountIdRune, ApplyExtrinsicEventPhase, Event, Rune, RunicArgs } from "../../mod.ts" -export interface ContractsRuntimeEvent { - phase: ApplyExtrinsicEventPhase - event: { - type: "Contracts" - value: { - contract?: Uint8Array +export interface ContractsRuntimeEvent extends + Event< + { + type: "Contracts" + value: V } - } + > +{ + phase: ApplyExtrinsicEventPhase } export function isContractsRuntimeEvent(event: any): event is ContractsRuntimeEvent { return event.event.type === "Contracts" } -export interface InstantiatedEvent { - phase: ApplyExtrinsicEventPhase - event: { - type: "Contracts" - value: { - type: "Instantiated" - deployer: Uint8Array - contract: Uint8Array - } - } +export interface ContractEmittedEvent + extends ContractsRuntimeEvent<{ type: "ContractEmitted"; contract: Uint8Array; data: Uint8Array }> +{} + +export function isContractEmitted(event: any): event is ContractEmittedEvent { + return isContractsRuntimeEvent(event) && (event.event.value as any).type === "ContractEmitted" } +export interface InstantiatedEvent extends + ContractsRuntimeEvent<{ + type: "Instantiated" + deployer: Uint8Array + contract: Uint8Array + }> +{} + export function isInstantiatedEvent(event: any): event is InstantiatedEvent { - return event.event.type === "Contracts" && (event.event.value as any).type === "Instantiated" + return isContractsRuntimeEvent(event) && (event.event.value as any).type === "Instantiated" } export function instantiationEventIntoPublicKey(