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

Improve transaction status and events flow #1090

Closed
statictype opened this issue Jun 19, 2023 · 3 comments · Fixed by #1097
Closed

Improve transaction status and events flow #1090

statictype opened this issue Jun 19, 2023 · 3 comments · Fixed by #1097
Labels

Comments

@statictype
Copy link

statictype commented Jun 19, 2023

In our app, we want to keep users updated about transaction status changes and events.
Currently in Capi there is a way to subscribe to status changes via the .transactionStatuses method on the SignedExtrinsicRune, but there's no clear way to pull in related events with this method.

Ideally apps show events on block inclusion, this way we get feedback faster to the user and in most cases blocks get finalized so no need to wait the extra 6 seconds to display the events. This is achieved in capi by using inBlockEvents.

        call
        .signed(signature({ sender }))
        .sent()
        .dbgStatus("Transfer:")
        .inBlockEvents()
        .unhandleFailed()
        .pipe(filterEvents) // filterEvents is function that extracts event names and invokes toast notifications with them

However, we also want to make sure that the transaction went through. So, we want to show an additional notification when a transaction is finalized or reverted. Currently, Capi only allows us to use either inBlockEvents or finalized, but not both.

We'd like to have a way of working with both status and events, either by exposting events in transactionStatuses or allowing to chain methods on the rune like in the example below

        call
        .signed(signature({ sender }))
        .sent()
        .dbgStatus("Transfer:")
        .inBlockEvents()
        .unhandleFailed()
        .pipe(filterEvents) // this is where we get the events from
       .finalized() // actually return "finalized" or "reverted" here

This is just a raw example, looking forward to your suggestions.

@harrysolovay
Copy link
Contributor

At first, I was going to say that this is tracked in #1010 and will be resolved as part of the v0.1.0 milestone... however, I'm not so sure that's the case. You describe an interesting Rune scenario that we may need to address: separate runs/timing of two or more Runes that need to share a common Rune instance.

Scopes (#1084) may already solve this somewhat

Initially I was going to suggest this:

const sent = call
  .signed(signature({ sender }))
  .sent()

const [inBlockEvents, finalized] = await Rune.tuple([
  sent.inBlockEvents(),
  sent.finalized(),
  sent.transactionStatuses(cb),
]).run(scope)

... however, the resolution of inBlockEvents won't occur until finalized is also resolved... that's not what we want; we want them to be able to resolve independently (correct?). I think scopes (#1084) and our (soon-to-be-devised) approach to cache invalidation will impact the API change that would elegantly address your use case. Any ideas for how you'd like the DX to look?

@harrysolovay
Copy link
Contributor

@tjjfvi thoughts on whether the following would be the right path for such a use case?

import { westendDev } from "@capi/westend-dev"
import { createDevUsers, Scope } from "capi"
import { signature } from "capi/patterns/signature/polkadot"

const { alexa, billy } = await createDevUsers()

const scope = new Scope()

const sent = westendDev.Balances
  .transfer({
    value: 12345n,
    dest: billy.address,
  })
  .signed(signature({ sender: alexa }))
  .sent()

const inBlockEvents = sent.inBlockEvents().run(scope) // Promise<RuntimeEvent[]>
const finalized = sent.finalized().run(scope) // Promise<string>
sent
  .status((s) => {
    // use `s`
  })
  .run(scope) // Promise<void>

Note: status would be a new method similar to dbgStatus, only it would accept a callback.

@harrysolovay
Copy link
Contributor

@tjjfvi and I have settled on getting rid of Scope (hi @peetzweg ;)

Soon, you'll be able to model this use case as follows.

import { westendDev } from "@capi/westend-dev"
import { createDevUsers } from "capi"
import { signature } from "capi/patterns/signature/polkadot"

const { alexa, billy } = await createDevUsers()

const sent = westendDev.Balances
  .transfer({
    value: 12345n,
    dest: billy.address,
  })
  .signed(signature({ sender: alexa }))
  .sent()

const inBlockEvents = sent.inBlockEvents().run() // Promise<RuntimeEvent[]>
const finalized = sent.finalized().run() // Promise<string>
sent
  .status((s) => {
    // use `s`
  })
  .run() // Promise<void>

The sent Rune's execution will be memoized for use in subsequent dependents.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants