Skip to content
This repository has been archived by the owner on Sep 14, 2023. It is now read-only.

Commit

Permalink
feat: add contract.emissions variants
Browse files Browse the repository at this point in the history
  • Loading branch information
kratico committed Apr 5, 2023
1 parent 3db7b05 commit a6a0581
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 38 deletions.
10 changes: 7 additions & 3 deletions examples/ink/interact.eg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
21 changes: 20 additions & 1 deletion patterns/ink/InkMetadataRune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -66,6 +66,25 @@ export class InkMetadataRune<out U> extends Rune<InkMetadata, U> {
)
}

decodeEvent<X>(...[event]: RunicArgs<X, [event: Uint8Array]>) {
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<C extends Chain, U, X>(
chain: ChainRune<C, U>,
props: RunicArgs<X, InstantiateProps>,
Expand Down
78 changes: 63 additions & 15 deletions patterns/ink/InkRune.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { equals } from "../../deps/std/bytes.ts"
import {
$,
ArrayRune,
Chain,
CodecRune,
Event,
Expand All @@ -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 {
Expand Down Expand Up @@ -87,16 +89,21 @@ export class InkRune<out C extends Chain, out U>
}

filterContractEvents = <X>(...[events]: RunicArgs<X, [events: Event[]]>) => {
// 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 = <X>(...[events]: RunicArgs<X, [events: Event[]]>) => {
return this.filterContractEvents(events).map((events) => events.filter(isContractEmitted))
}

// TODO: improve
decodeErrorEvent = <X>(...[failRuntimeEvent]: RunicArgs<X, [any]>) => {
const $error = this.chain
Expand All @@ -116,17 +123,58 @@ export class InkRune<out C extends Chain, out U>
.unhandle(FailedToDecodeErrorError)
}

// // TODO: finish this
// emissions<X>(...[events]: RunicArgs<X, [events: Event[]]>) {
// const $event: $.Codec<unknown> = null!
// return Rune
// .tuple([Rune.resolve(events), $event])
// .map(([events, $event]) =>
// events
// .filter(isContractEmittedEvent)
// .map((event) => $event.decode(event.event.value.data))
// )
// }
emissions<X>(...[events]: RunicArgs<X, [events: Event[]]>) {
// 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<X>(...[events]: RunicArgs<X, [events: Event[]]>) {
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<X>(...[events]: RunicArgs<X, [events: Event[]]>) {
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 {
Expand Down
43 changes: 24 additions & 19 deletions patterns/ink/events.ts
Original file line number Diff line number Diff line change
@@ -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<V = { contract?: Uint8Array }> 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<X>(
Expand Down

0 comments on commit a6a0581

Please sign in to comment.