import "../../_dnt.polyfills.js"; import { Polkadot } from "@capi/polkadot" import { AddressPrefixChain, Chain, ChainRune } from "../../fluent/ChainRune.js" import { ExtrinsicSender, SignatureData } from "../../fluent/ExtrinsicRune.js" import { $, hex, ss58, ValueRune } from "../../mod.js" import { is, Rune, RunicArgs } from "../../rune/mod.js" import { Era } from "../../scale_info/overrides/Era.js" export interface SignatureProps { sender: ExtrinsicSender checkpoint?: string mortality?: Era nonce?: number tip?: bigint } export interface PolkadotSignatureChain extends AddressPrefixChain { metadata: AddressPrefixChain["metadata"] & { pallets: { System: { constants: { Version: { codec: $.Codec> } } } } extrinsic: Omit } } export function signature(_props: RunicArgs>) { return (chain: ChainRune) => { const props = RunicArgs.resolve(_props) const addrPrefix = chain.addressPrefix() const versions = chain.pallet("System").constant("Version").decoded const specVersion = versions.access("specVersion") const transactionVersion = versions.access("transactionVersion") // TODO: create union rune (with `matchTag` method) and utilize here // TODO: MultiAddress conversion utils const senderSs58 = Rune .tuple([addrPrefix, props.sender]) .map(([addrPrefix, sender]) => { switch (sender.address.type) { case "Id": { return ss58.encode(addrPrefix, sender.address.value) } default: { throw new Error("unimplemented") } } }) .throws(is(ss58.InvalidPayloadLengthError)) const nonce = Rune.resolve(props.nonce) .handle(is(undefined), () => chain.connection.call("system_accountNextIndex", senderSs58)) const genesisHashHex = chain.connection.call("chain_getBlockHash", 0).unsafeAs() .into(ValueRune) const genesisHash = genesisHashHex.map(hex.decode) const checkpointHash = Rune.tuple([props.checkpoint, genesisHashHex]).map(([a, b]) => a ?? b) .map(hex.decode) const mortality = Rune.resolve(props.mortality).map((x) => x ?? Era.Immortal) const tip = Rune.resolve(props.tip).map((x) => x ?? 0n) return Rune.object({ sender: props.sender, extra: Rune.object({ CheckMortality: mortality, CheckNonce: nonce, ChargeTransactionPayment: tip, }), additional: Rune.object({ CheckSpecVersion: specVersion, CheckTxVersion: transactionVersion, CheckGenesis: genesisHash, CheckMortality: checkpointHash, }), }) satisfies Rune, unknown> } }