paritytech / capi

[WIP] A framework for crafting interactions with Substrate chains
https://docs.capi.dev
Apache License 2.0
104 stars 9 forks source link

passing dynamic data to codegened API methods #1186

Open harrysolovay opened 1 year ago

harrysolovay commented 1 year ago

Problem Statement

Developers will want to colocate static and dynamic usage of a given chain. Examples of both:

Static

chain.Balances

Dynamic

const palletName = await fetch("...").then((v) => v.text())

chain.pallet(palletName)

In the dynamic case, devs may run into issues when supplying the dynamic data to methods of the fluent API.

chain.pallet(palletName)
//           ~~~~~~~~~~
//           ^
//           `string` is not assignable to Chain.PalletName<Chain>

Possible Solutions

1. chain.untyped()

We could introduce a dynamic method to ChainRune. This would allow devs to opt out of the narrow, chain-specific constraints.

myChain.untyped().pallet(palletName) // no more type error

2. Generate Guards + Assertions

Alternatively, we could generate guards that do this narrowing.

import { chain, assertPallet, isStorage } from "@capi/chain"

palletName // `string`
assertPallet(palletName)
palletName // `Chain.PalletName<Chain>`

const pallet = chain.pallet(palletName) // no more type error

declare const storageName: string

if (isStorage(pallet, storageName)) {
  const storageName = pallet.storage(storageName)
  // ...
}

Tradeoffs

The first solution probably lends itself to faster prototyping. The second solution probably results in nicer error messages and safer code (albeit more code). We could also have both, and prefix untyped with unsafe to discourage devs from shipping prod code that uses it. unsafeUntyped sounds scary indeed!


Thank you @arrudagates for pointing out this use case!