From b27d058ca65abe4d9c1d0aa58b477ceab1ae616f Mon Sep 17 00:00:00 2001 From: Harry Solovay Date: Tue, 31 Jan 2023 06:23:19 -0500 Subject: [PATCH] north star dyn contract dx --- examples/ink_contract/main.ts | 184 +++++----------------------------- fluent/InkContract.ts | 45 +++++++-- fluent/events.ts | 21 ---- fluent/extrinsic.ts | 17 ++++ 4 files changed, 83 insertions(+), 184 deletions(-) delete mode 100644 fluent/events.ts diff --git a/examples/ink_contract/main.ts b/examples/ink_contract/main.ts index 4ea975aaa..1743405d6 100644 --- a/examples/ink_contract/main.ts +++ b/examples/ink_contract/main.ts @@ -7,7 +7,7 @@ const contract = client.inkContract({ metadataRaw: Deno.readTextFileSync("examples/ink_contract/metadata.json"), }) -const hash = await contract +const instance = contract .instantiate({ code: Deno.readFileSync("examples/ink_contract/flipper.wasm"), initiator: alice.publicKey, @@ -15,160 +15,30 @@ const hash = await contract .signed({ sender: alice }) .sent() .logEvents() - .finalized() - .unwrapError() - .run() + .instance() -console.log(hash) - -// class ExtrinsicFailed extends Error { -// override readonly name = "ExtrinsicFailedError" -// constructor( -// override readonly cause: { -// event?: Record -// phase: { value: number } -// }, -// ) { -// super() -// } -// } - -// const tx = C.contracts.instantiate(client)({ -// code, -// constructorMetadata, -// salt, -// sender: C.alice.address, -// }).signed(C.alice.sign) -// const finalizedIn = tx.watch(({ end }) => (status) => { -// console.log(status) -// if (typeof status !== "string" && (status.inBlock ?? status.finalized)) { -// return end(status.inBlock ?? status.finalized) -// } else if (C.rpc.known.TransactionStatus.isTerminal(status)) { -// return end(new Error()) -// } -// return -// }) -// const contractAddress = C.throwIfError( -// await C -// .events(tx, finalizedIn) -// .next((events) => { -// const extrinsicFailedEvent = events.find((e) => -// e.event?.type === "System" && e.event?.value?.type === "ExtrinsicFailed" -// ) -// if (extrinsicFailedEvent) { -// return new ExtrinsicFailed(extrinsicFailedEvent) -// } -// const event = events.find((e) => -// e.event?.type === "Contracts" && e.event?.value?.type === "Instantiated" -// ) -// return event?.event?.value.contract as Uint8Array -// }) -// .run(), -// ) - -// const prefix = C.throwIfError(await C.const(client)("System", "SS58Prefix").access("value").run()) -// console.log("Deployed Contract address", C.ss58.encode(prefix, contractAddress)) - -// const flipperContract = new C.fluent.Contract(client, metadata, contractAddress) -// console.log( -// ".get", -// await flipperContract.call({ -// sender: C.alice.address, -// messageLabel: "get", -// args: [], -// }).run(), -// ) -// console.log( -// "block hash and events", -// C.throwIfError( -// await flipperContract.callTx({ -// sender: C.alice.address, -// args: [], -// sign: C.alice.sign, -// messageLabel: "flip", -// }).run(), -// )[0], -// ) -// console.log( -// ".get", -// await flipperContract.call({ -// sender: C.alice.address, -// messageLabel: "get", -// args: [], -// }).run(), -// ) -// console.log( -// ".get_count", -// await flipperContract.call({ sender: C.alice.address, messageLabel: "get_count", args: [] }) -// .run(), -// ) -// console.log( -// ".inc block hash", -// C.throwIfError( -// await flipperContract.callTx({ -// sender: C.alice.address, -// messageLabel: "inc", -// args: [], -// sign: C.alice.sign, -// }).run(), -// )[0], -// ) -// console.log( -// ".inc block hash", -// C.throwIfError( -// await flipperContract.callTx({ -// sender: C.alice.address, -// messageLabel: "inc", -// args: [], -// sign: C.alice.sign, -// }).run(), -// )[0], -// ) -// console.log( -// ".get_count", -// await flipperContract.call({ sender: C.alice.address, messageLabel: "get_count", args: [] }) -// .run(), -// ) -// console.log( -// ".inc_by(3) block hash", -// C.throwIfError( -// await flipperContract.callTx({ -// sender: C.alice.address, -// messageLabel: "inc_by", -// args: [3], -// sign: C.alice.sign, -// }).run(), -// )[0], -// ) -// console.log( -// ".get_count", -// await flipperContract.call({ sender: C.alice.address, messageLabel: "get_count", args: [] }) -// .run(), -// ) -// console.log( -// ".inc_by_with_event(3) contract events", -// C.throwIfError( -// await flipperContract.callTx({ -// sender: C.alice.address, -// messageLabel: "inc_by_with_event", -// args: [3], -// sign: C.alice.sign, -// }).run(), -// )[2], -// ) -// console.log( -// ".method_returning_tuple(2,true)", -// await flipperContract.call({ -// sender: C.alice.address, -// messageLabel: "method_returning_tuple", -// args: [2, true], -// }).run(), -// ) -// console.log( -// ".method_returning_struct(3,false)", -// await flipperContract.call({ -// sender: C.alice.address, -// messageLabel: "method_returning_struct", -// args: [3, false], -// }).run(), -// ) +// TODO: what values do we want? +console.log(".get", await instance.msg("get").run()) +console.log(".flip", await instance.msg("flip").signed({ sender: alice }).run()) +console.log(".get", await instance.msg("get").run()) +console.log(".get_count", await instance.msg("get_count").run()) +console.log(".inc", await instance.msg("inc").signed({ sender: alice }).run()) +console.log(".inc", await instance.msg("inc").signed({ sender: alice }).run()) +console.log(".get_count", await instance.msg("get_count").signed({ sender: alice }).run()) +console.log( + ".inc_by(3)", + await instance.msg("inc_by", 3).signed({ sender: alice }).run(), +) +console.log(".get_count", await instance.msg("get_count").run()) +console.log( + ".inc_by_with_event(3) contract events", + await instance.msg("inc_by_with_event", 3).signed({ sender: alice }).run(), +) +console.log( + ".method_returning_tuple(2,true)", + await instance.msg("method_returning_tuple", 2, true).signed({ sender: alice }).run(), +) +console.log( + ".method_returning_struct(3,false)", + await instance.msg("method_returning_struct", 3, false).signed({ sender: alice }).run(), +) diff --git a/fluent/InkContract.ts b/fluent/InkContract.ts index 90643ca65..dbd8697e8 100644 --- a/fluent/InkContract.ts +++ b/fluent/InkContract.ts @@ -8,14 +8,14 @@ import { Rune, RunicArgs, ValueRune } from "../rune/mod.ts" import { DeriveCodec } from "../scale_info/mod.ts" import * as U from "../util/mod.ts" import { ClientRune } from "./client.ts" -import { ExtrinsicRune } from "./extrinsic.ts" +import { ExtrinsicRune, SignedExtrinsicProps } from "./extrinsic.ts" import { state } from "./rpc_known_methods.ts" export interface InkContract { metadataRaw: string } -export interface InstantiateProps { +export interface InkContractInstantiateProps { initiator: Uint8Array code: Uint8Array ctor?: string @@ -49,7 +49,8 @@ export class InkContractRune extends Rune { .unwrapError() } - instantiate(props: RunicArgs) { + // TODO: create instantiation-specific rune so that we can `.address()` from it + instantiate(props: RunicArgs) { const ctor = this.ctor(props.ctor) const key = Rune.tuple([Rune.rec(props), ctor]) .map(([{ code, initiator }, ctor]) => @@ -90,11 +91,43 @@ export class InkContractRune extends Rune { ) } - // call(props: RunicArgs) {} - // callTx(props: RunicArgs) {} + declare fromAddress: ( + ...[address]: RunicArgs + ) => InkContractInstanceRune } -export class InkContractInstanceRune extends Rune {} +export class InkContractInstanceRune extends Rune { + pallet + + constructor(_prime: InkContractInstanceRune["_prime"], readonly client: ClientRune) { + super(_prime) + this.pallet = this.client.metadata().pallet("Contracts") + } + + declare msg: ( + ...[method, ...args]: RunicArgs + ) => InkContractMsg +} + +export class InkContractMsg extends Rune { + pallet + + constructor(_prime: InkContractMsg["_prime"], readonly client: ClientRune) { + super(_prime) + this.pallet = this.client.metadata().pallet("Contracts") + } + + declare signed: (props: RunicArgs) => InkContractMsgSigned +} + +export class InkContractMsgSigned extends Rune { + pallet + + constructor(_prime: InkContractMsgSigned["_prime"], readonly client: ClientRune) { + super(_prime) + this.pallet = this.client.metadata().pallet("Contracts") + } +} export class InkContractMetadataInvalidError extends Error { override readonly name = "InkContractMetadataInvalidError" diff --git a/fluent/events.ts b/fluent/events.ts deleted file mode 100644 index 0a611d295..000000000 --- a/fluent/events.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as M from "../frame_metadata/mod.ts" -import { MultiAddress, Signer } from "../primitives/mod.ts" -import { TransactionStatus } from "../rpc/known/mod.ts" -import { MetaRune, Rune, RunicArgs, ValueRune } from "../rune/mod.ts" -import { Era, era } from "../scale_info/mod.ts" -import { HexHash } from "../util/branded.ts" -import { Blake2_256 } from "../util/hashers.ts" -import * as U from "../util/mod.ts" -import { ClientRune } from "./client.ts" -import { CodecRune } from "./codec.ts" -import { author, chain, payment, system } from "./rpc_known_methods.ts" - -export interface EventsBearer { - events: Event[] -} - -export class EventsRune extends Rune { - constructor(_prime: EventsRune["_prime"], readonly client: ClientRune) { - super(_prime) - } -} diff --git a/fluent/extrinsic.ts b/fluent/extrinsic.ts index d7925a7c7..75e0e9281 100644 --- a/fluent/extrinsic.ts +++ b/fluent/extrinsic.ts @@ -147,6 +147,22 @@ export class ExtrinsicStatusRune extends Rune + events + .as(ValueRune) + .filter(TransactionStatus.isTerminal) + .map((status) => + typeof status !== "string" && (status.inBlock ?? status.finalized) + ? (status.inBlock ?? status.finalized) + : new NeverInBlockError() + ) + .singular() + ).unwrapError() + } + finalized() { return this.as(MetaRune).flatMap((events) => events @@ -162,4 +178,5 @@ export class ExtrinsicStatusRune extends Rune