diff --git a/config/mod.ts b/config/mod.ts index 78c301dd1..e929b2b5f 100644 --- a/config/mod.ts +++ b/config/mod.ts @@ -1,17 +1,4 @@ -import * as rpc from "../rpc/mod.ts"; - -export class Config< - DiscoveryValue = any, - RpcCallMethods extends rpc.ProviderMethods = rpc.ProviderMethods, - RpcSubscriptionMethods extends rpc.ProviderMethods = rpc.ProviderMethods, - RpcErrorDetails extends rpc.ErrorDetails = rpc.ErrorDetails, -> { - // TODO: get rid of this gunk - RpcMethods!: RpcCallMethods & RpcSubscriptionMethods; - RpcCallMethods!: RpcCallMethods; - RpcSubscriptionMethods!: RpcSubscriptionMethods; - RpcErrorDetails!: RpcErrorDetails; - +export class Config { #discoveryValue?: DiscoveryValue | Promise; constructor( diff --git a/effect/BlockRead.ts b/effect/BlockRead.ts index cf86eada3..dcb83ff5b 100644 --- a/effect/BlockRead.ts +++ b/effect/BlockRead.ts @@ -1,16 +1,16 @@ +import { Config } from "../config/mod.ts"; import * as Z from "../deps/zones.ts"; -import * as known from "../known/mod.ts"; import * as U from "../util/mod.ts"; import { $extrinsic } from "./core/$extrinsic.ts"; import { deriveCodec } from "./core/deriveCodec.ts"; import { Metadata } from "./Metadata.ts"; import { RpcCall } from "./RpcCall.ts"; -export class BlockRead]> extends Z.Name { +export class BlockRead]> extends Z.Name { root; constructor( - config: known.rpc.Config, + config: Config, ...[blockHash]: [...Rest] ) { super(); @@ -23,7 +23,7 @@ export class BlockRead { + extrinsics: extrinsics.map((extrinsic: U.Hex) => { return $extrinsic_.decode(U.hex.decode(extrinsic)); }), }, diff --git a/effect/BlockWatch.ts b/effect/BlockWatch.ts index a3b6eb94d..8078477ad 100644 --- a/effect/BlockWatch.ts +++ b/effect/BlockWatch.ts @@ -1,5 +1,5 @@ +import { Config } from "../config/mod.ts"; import * as Z from "../deps/zones.ts"; -import { Extrinsic } from "../frame_metadata/mod.ts"; import * as known from "../known/mod.ts"; import * as U from "../util/mod.ts"; import { BlockRead } from "./BlockRead.ts"; @@ -11,12 +11,8 @@ export class BlockWatch extends Z.Name { root; constructor( - config: known.rpc.Config< - string, - "state_getMetadata" | "chain_getBlockHash" | "chain_getBlock" | "chain_unsubscribeNewHead", - "chain_subscribeNewHeads" - >, - createWatchHandler: U.CreateWatchHandler>, + config: Config, + createWatchHandler: U.CreateWatchHandler, ) { super(); this.root = new RpcSubscription( @@ -26,7 +22,7 @@ export class BlockWatch extends Z.Name { function subscribeNewHeadsHandler(stop) { const watchHandler = createWatchHandler(stop); return async (result) => { - const blockNum = result.params.result.number; + const blockNum: number = result.params.result.number; const blockHash = Z.sel(new RpcCall(config, "chain_getBlockHash", [blockNum]), "result"); // TODO: use derived util from Zones const block = U.throwIfError(await run(new BlockRead(config, blockHash))); diff --git a/effect/EntryRead.ts b/effect/EntryRead.ts index 8ba0fa714..9c46d9ab7 100644 --- a/effect/EntryRead.ts +++ b/effect/EntryRead.ts @@ -1,5 +1,5 @@ +import { Config } from "../config/mod.ts"; import * as Z from "../deps/zones.ts"; -import * as known from "../known/mod.ts"; import * as U from "../util/mod.ts"; import { $storageKey } from "./core/$storageKey.ts"; import { codec } from "./core/codec.ts"; @@ -13,12 +13,12 @@ export class EntryRead< PalletName extends Z.$, EntryName extends Z.$, Keys extends unknown[], - Rest extends [blockHash?: Z.$], + Rest extends [blockHash?: Z.$], > extends Z.Name { root; constructor( - config: known.rpc.Config, + config: Config, palletName: PalletName, entryName: EntryName, keys: [...Keys], diff --git a/effect/EntryWatch.ts b/effect/EntryWatch.ts index 3585890d9..7fd55792e 100644 --- a/effect/EntryWatch.ts +++ b/effect/EntryWatch.ts @@ -1,5 +1,5 @@ +import { Config } from "../config/mod.ts"; import * as Z from "../deps/zones.ts"; -import * as known from "../known/mod.ts"; import * as rpc from "../rpc/mod.ts"; import * as U from "../util/mod.ts"; import { $storageKey } from "./core/$storageKey.ts"; @@ -10,13 +10,7 @@ import { entryMetadata, Metadata, palletMetadata } from "./Metadata.ts"; import { RpcCall } from "./RpcCall.ts"; import { RpcSubscription } from "./RpcSubscription.ts"; -export type WatchEntryEvent = [key?: U.HexString, value?: unknown]; - -type Config = known.rpc.Config< - string, - "state_getMetadata" | "state_unsubscribeStorage", - "state_subscribeStorage" ->; +export type WatchEntryEvent = [key?: U.Hex, value?: unknown]; export class EntryWatch< PalletName extends Z.$, @@ -49,8 +43,8 @@ export class EntryWatch< const watchInit = Z.call($entry, function entryWatchInit($entry) { return U.mapCreateWatchHandler( createWatchHandler, - (message: rpc.NotifMessage) => { - return message.params.result.changes.map(([key, val]) => { + (message: rpc.NotifMessage) => { + return message.params.result.changes.map(([key, val]: any) => { return [key, val ? $entry.decode(U.hex.decode(val)) : undefined]; }); }, diff --git a/effect/ExtrinsicSentWatch.ts b/effect/ExtrinsicSentWatch.ts index f1d95d02d..edab74455 100644 --- a/effect/ExtrinsicSentWatch.ts +++ b/effect/ExtrinsicSentWatch.ts @@ -1,7 +1,7 @@ import { unimplemented } from "../deps/std/testing/asserts.ts"; import * as Z from "../deps/zones.ts"; import * as M from "../frame_metadata/mod.ts"; -import * as known from "../known/mod.ts"; +import { Config } from "../mod.ts"; import * as rpc from "../rpc/mod.ts"; import * as ss58 from "../ss58/mod.ts"; import * as U from "../util/mod.ts"; @@ -12,31 +12,17 @@ import { Metadata } from "./Metadata.ts"; import { RpcCall } from "./RpcCall.ts"; import { RpcSubscription } from "./RpcSubscription.ts"; -export { type Config as SendAndWatchExtrinsicConfig }; -type Config = known.rpc.Config< - string, - | "state_getMetadata" - | "state_getRuntimeVersion" - | "chain_getBlockHash" - | "system_accountNextIndex" - | "system_chain" - | "author_unwatchExtrinsic", - "author_submitAndWatchExtrinsic" ->; - export interface SendAndWatchExtrinsicProps { sender: M.MultiAddress; palletName: string; methodName: string; args: Record; - checkpoint?: U.HashHexString; + checkpoint?: U.HexHash; mortality?: [period: bigint, phase: bigint]; nonce?: string; tip?: bigint; sign: M.SignExtrinsic; - createWatchHandler: U.CreateWatchHandler< - rpc.NotifMessage - >; + createWatchHandler: U.CreateWatchHandler; } export class ExtrinsicSentWatch> extends Z.Name { @@ -63,7 +49,7 @@ export class ExtrinsicSentWatch unimplemented(); } } - })() as U.AccountIdString; + })(); }); const accountNextIndex = new RpcCall(config, "system_accountNextIndex", [senderSs58]); const genesisHash = hexDecode(Z.sel(new RpcCall(config, "chain_getBlockHash", [0]), "result")); @@ -119,7 +105,7 @@ export class ExtrinsicSentWatch additional: [specVersion, transactionVersion, checkpoint, genesisHash], }, }); - return U.hex.encode(extrinsicBytes) as U.HexString; + return U.hex.encode(extrinsicBytes); }, ); this.root = new RpcSubscription( diff --git a/effect/KeyPageRead.ts b/effect/KeyPageRead.ts index 7bb18e11f..ca22a096c 100644 --- a/effect/KeyPageRead.ts +++ b/effect/KeyPageRead.ts @@ -1,5 +1,5 @@ +import { Config } from "../config/mod.ts"; import * as Z from "../deps/zones.ts"; -import * as known from "../known/mod.ts"; import * as U from "../util/mod.ts"; import { $key } from "./core/$key.ts"; import { $storageKey } from "./core/$storageKey.ts"; @@ -12,12 +12,12 @@ export class KeyPageRead< PalletName extends Z.$, EntryName extends Z.$, Count extends Z.$, - Rest extends [start?: unknown[] | undefined, blockHash?: Z.$], + Rest extends [start?: unknown[] | undefined, blockHash?: Z.$], > extends Z.Name { root; constructor( - config: known.rpc.Config, + config: Config, palletName: PalletName, entryName: EntryName, count: Count, @@ -50,7 +50,7 @@ export class KeyPageRead< const keysDecoded = Z.call( Z.ls($key_, keysEncoded), function keysDecodedImpl([$key, keysEncoded]) { - return keysEncoded.map((keyEncoded) => { + return keysEncoded.map((keyEncoded: U.Hex) => { return $key.decode(U.hex.decode(keyEncoded)); }); }, diff --git a/effect/Metadata.ts b/effect/Metadata.ts index 9095ad2ef..c02f7dddd 100644 --- a/effect/Metadata.ts +++ b/effect/Metadata.ts @@ -1,13 +1,13 @@ import * as Z from "../deps/zones.ts"; import * as M from "../frame_metadata/mod.ts"; -import * as known from "../known/mod.ts"; +import { Config } from "../mod.ts"; import * as U from "../util/mod.ts"; import { RpcCall } from "./RpcCall.ts"; -export class Metadata]> extends Z.Name { +export class Metadata]> extends Z.Name { root; - constructor(config: known.rpc.Config, ...[blockHash]: [...Rest]) { + constructor(config: Config, ...[blockHash]: [...Rest]) { super(); this.root = Z.call( new RpcCall(config, "state_getMetadata", [blockHash]), diff --git a/effect/RpcCall.ts b/effect/RpcCall.ts index f73807673..53aff279d 100644 --- a/effect/RpcCall.ts +++ b/effect/RpcCall.ts @@ -1,18 +1,16 @@ import { Config } from "../config/mod.ts"; import * as Z from "../deps/zones.ts"; -import * as rpc from "../rpc/mod.ts"; import { RpcError } from "./common.ts"; import { rpcClient } from "./core/rpcClient.ts"; export class RpcCall< - Methods extends rpc.ProviderMethods, - MethodName extends Z.$>, - Params extends Z.Ls$]>>, + MethodName extends Z.$, + Params extends Z.Ls$, > extends Z.Name { root; constructor( - config: Config, + config: Config, methodName: MethodName, params: [...Params], ) { @@ -24,7 +22,7 @@ export class RpcCall< async function rpcCallImpl([[client, methodName, ...params], rc]) { const result = await client.call( methodName, - params as Parameters<(Methods & rpc.ProviderMethods)[Z.T]>, + params, ); if (result.error) { return new RpcError({ diff --git a/effect/RpcSubscription.ts b/effect/RpcSubscription.ts index 3215e788d..45a2d36d9 100644 --- a/effect/RpcSubscription.ts +++ b/effect/RpcSubscription.ts @@ -7,21 +7,18 @@ import { rpcClient } from "./core/rpcClient.ts"; import { run } from "./run.ts"; export class RpcSubscription< - Config_ extends Config, - MethodName extends Extract, - MethodName_ extends Z.$, - Params extends Parameters]>, - Params_ extends Z.Ls$, - CreateListenerCb extends Z.$>>, + MethodName extends Z.$, + Params extends Z.Ls$, + CreateListenerCb extends Z.$>, > extends Z.Name { root; constructor( - config: Config_, - methodName: MethodName_, - params: [...Params_], + config: Config, + methodName: MethodName, + params: [...Params], createListenerCb: CreateListenerCb, - cleanup?: (initOk: rpc.OkMessage) => Z.EffectLike, + cleanup?: (initOk: rpc.OkMessage) => Z.EffectLike, ) { super(); const client = rpcClient(config); @@ -30,10 +27,10 @@ export class RpcSubscription< Z.ls(deps, Z.rc(client, deps)), async function rpcSubscriptionImpl([[client, methodName, createListenerCb, ...params], rc]) { const result = await client.subscribe( - methodName as MethodName, - params as Parameters, - createListenerCb as any, - cleanup ? (x) => run(cleanup(x.result), undefined!) : undefined, + methodName, + params, + createListenerCb, + cleanup ? (x) => run(cleanup(x), undefined!) : undefined, ); if (result?.error) { return new RpcError({ @@ -56,8 +53,8 @@ export class RpcSubscription< } // TODO: handle elsewhere -export class RpcSubscriptionError extends U.ErrorCtor("RpcSubscription") { - constructor(readonly error: rpc.ErrMessage["error"]) { +export class RpcSubscriptionError extends U.ErrorCtor("RpcSubscription") { + constructor(readonly error: rpc.ErrMessage["error"]) { super(); } } diff --git a/effect/common.ts b/effect/common.ts index 01ebb15c9..d889ce1ff 100644 --- a/effect/common.ts +++ b/effect/common.ts @@ -1,21 +1,16 @@ -import { Config } from "../config/mod.ts"; import * as rpc from "../rpc/mod.ts"; import * as U from "../util/mod.ts"; // TODO: handle this elsewhere -export class RpcError< - Config_ extends Config, - MethodName extends keyof Config_["RpcMethods"], - Params extends Parameters, -> extends U.ErrorCtor("RpcCall") { +export class RpcError extends U.ErrorCtor("RpcCall") { code; attempt; constructor( - { code, message, attempt }: rpc.ErrMessage["error"] & { + { code, message, attempt }: rpc.ErrMessage["error"] & { attempt: { - methodName: MethodName; - params: Params; + methodName: string; + params: unknown[]; }; }, ) { diff --git a/effect/core/decoded.ts b/effect/core/decoded.ts index 429919e3f..845067278 100644 --- a/effect/core/decoded.ts +++ b/effect/core/decoded.ts @@ -4,7 +4,7 @@ import * as U from "../../util/mod.ts"; export function decoded< Codec extends Z.$<$.Codec>, - Encoded extends Z.$, + Encoded extends Z.$, Key extends Z.$, >( codec: Codec, diff --git a/effect/core/storageKey.ts b/effect/core/storageKey.ts index 23d6f54b2..5fea34201 100644 --- a/effect/core/storageKey.ts +++ b/effect/core/storageKey.ts @@ -6,5 +6,5 @@ export const storageKey = Z.call.fac(( $storageKey: $.Codec, ...keys: unknown[] ) => { - return U.hex.encode($storageKey.encode(keys)) as U.HexString; + return U.hex.encode($storageKey.encode(keys)) as U.Hex; }); diff --git a/effect/extrinsic.test.ts b/effect/extrinsic.test.ts index f9ca1fcc7..06e7fcae1 100644 --- a/effect/extrinsic.test.ts +++ b/effect/extrinsic.test.ts @@ -73,7 +73,7 @@ Deno.test({ methodName: "propose", args: { proposal_hash: U.hex.decode( - "0x123450000000000000000000000000000000000000000000000000000000000", + "0x123450000000000000000000000000000000000000000000000000000000000" as U.Hex, ), value: 2000000000000n, }, diff --git a/frame_metadata/Extrinsic.ts b/frame_metadata/Extrinsic.ts index 53e1c4aff..7fd288ea8 100644 --- a/frame_metadata/Extrinsic.ts +++ b/frame_metadata/Extrinsic.ts @@ -2,7 +2,7 @@ import * as $ from "../deps/scale.ts"; import { assert } from "../deps/std/testing/asserts.ts"; import * as H from "../hashers/mod.ts"; import * as ss58 from "../ss58/mod.ts"; -import { hex } from "../util/mod.ts"; +import { Hex, hex } from "../util/mod.ts"; import { $null, DeriveCodec } from "./Codec.ts"; import { Metadata } from "./Metadata.ts"; @@ -116,7 +116,7 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec { const signer = props.sign; buffer.writeAsync(0, async (buffer) => { const { signature } = await signer.signPayload(payload); - buffer.insertArray(hex.decode(signature)); + buffer.insertArray(hex.decode(signature as Hex)); }); buffer.insertArray(extraEncoded); buffer.insertArray(callEncoded); diff --git a/frame_metadata/Metadata.ts b/frame_metadata/Metadata.ts index dc9aabe8a..b97cbe7d1 100644 --- a/frame_metadata/Metadata.ts +++ b/frame_metadata/Metadata.ts @@ -146,7 +146,7 @@ export const $metadata: $.Codec = $.object( ); export function fromPrefixedHex(scaleEncoded: string): Metadata { - return $metadata.decode(U.hex.decode(scaleEncoded)); + return $metadata.decode(U.hex.decode(scaleEncoded as U.Hex)); } export function getPallet(metadata: Metadata, name: string): Pallet | PalletNotFoundError { diff --git a/known/configs.ts b/known/configs.ts index e902d328a..ab5da39e8 100644 --- a/known/configs.ts +++ b/known/configs.ts @@ -1,27 +1,18 @@ // TODO: ultimately delete this file upon introduction of RPC-method-specific codegen import { Config } from "../config/mod.ts"; -import * as knownRpc from "./rpc.ts"; -// @see https://github.com/paritytech/capi/issues/127 -const Config_ = Config< - string, - knownRpc.CallMethods, - knownRpc.SubscriptionMethods, - knownRpc.ErrorDetails ->; - -export const polkadot = new Config_(() => "wss://rpc.polkadot.io", 0); -export const kusama = new Config_(() => "wss://kusama-rpc.polkadot.io", 2); -export const acala = new Config_(() => "wss://acala-polkadot.api.onfinality.io/public-ws", 10); -export const rococo = new Config_( +export const polkadot = new Config(() => "wss://rpc.polkadot.io", 0); +export const kusama = new Config(() => "wss://kusama-rpc.polkadot.io", 2); +export const acala = new Config(() => "wss://acala-polkadot.api.onfinality.io/public-ws", 10); +export const rococo = new Config( () => "wss://rococo-contracts-rpc.polkadot.io", undefined!, /* TODO */ ); -export const moonbeam = new Config_(() => "wss://wss.api.moonbeam.network", 1284); -export const statemint = new Config_( +export const moonbeam = new Config(() => "wss://wss.api.moonbeam.network", 1284); +export const statemint = new Config( () => "wss://statemint-rpc.polkadot.io", undefined!, /* TODO */ ); -export const subsocial = new Config_(() => "wss://para.subsocial.network", 28); -export const westend = new Config_(() => "wss://westend-rpc.polkadot.io", 42); +export const subsocial = new Config(() => "wss://para.subsocial.network", 28); +export const westend = new Config(() => "wss://westend-rpc.polkadot.io", 42); diff --git a/known/mod.ts b/known/mod.ts index 1a07743ca..a2b1c37ab 100644 --- a/known/mod.ts +++ b/known/mod.ts @@ -1,3 +1,3 @@ export * from "./configs.ts"; -export * as rpc from "./rpc.ts"; -export * as types from "./types/mod.ts"; +export * from "./rpc.ts"; +export * from "./rpc/mod.ts"; diff --git a/known/rpc.ts b/known/rpc.ts index a5864ea8e..dd7f747ba 100644 --- a/known/rpc.ts +++ b/known/rpc.ts @@ -1,250 +1,13 @@ -import { Config as Config_ } from "../config/mod.ts"; -import * as rpc from "../rpc/mod.ts"; -import * as U from "../util/mod.ts"; -import * as T from "./types/mod.ts"; - -export type Config< - DiscoveryValue, - CallMethodName extends keyof CallMethods, - SubscriptionMethodName extends keyof SubscriptionMethods = never, -> = Config_< - DiscoveryValue, - Pick, - Pick ->; - -export type TODO_NARROW_METHOD_TYPE = (...args: any[]) => any; - -export type Methods = CallMethods & SubscriptionMethods; - -// TODO: attach type-level docs (draw from Substrate's source) -export type CallMethods = rpc.EnsureMethods<{ - account: { - nextIndex: TODO_NARROW_METHOD_TYPE; - }; - author: { - hasKey(pubKey: string, keyType: string): string; - hasSessionKeys: TODO_NARROW_METHOD_TYPE; - insertKey: TODO_NARROW_METHOD_TYPE; - pendingExtrinsics: TODO_NARROW_METHOD_TYPE; - removeExtrinsic: TODO_NARROW_METHOD_TYPE; - rotateKeys: TODO_NARROW_METHOD_TYPE; - submitExtrinsic(transaction: U.HexString): U.HashHexString; - unwatchExtrinsic(subscriptionId: U.SubscriptionIdString): unknown; - }; - babe: { - epochAuthorship: TODO_NARROW_METHOD_TYPE; - }; - beefy: { - getFinalizedHead(): U.H256String; - }; - chain: { - getBlock(hash?: U.HashHexString): T.Block; - // TODO: confirm that the following params are accurate (seems to work both ways) - getBlockHash(height?: U.HexU64String | number): U.HashHexString; - getFinalisedHead: CallMethods["chain_getFinalizedHead"]; - getFinalizedHead(): U.HashHexString; - getHead: CallMethods["chain_getBlockHash"]; - getHeader(hash?: U.HashHexString): T.Header; - getRuntimeVersion: CallMethods["state_getRuntimeVersion"]; - unsubscribeAllHeads(subscription: string): boolean; - unsubscribeFinalisedHeads: CallMethods["chain_unsubscribeFinalizedHeads"]; - unsubscribeFinalizedHeads(subscription: string): boolean; - unsubscribeNewHead: CallMethods["chain_unsubscribeNewHeads"]; - unsubscribeNewHeads(subscription: string): boolean; - unsubscribeRuntimeVersion: CallMethods["state_unsubscribeRuntimeVersion"]; - unsubscribe_newHead: CallMethods["chain_unsubscribeNewHeads"]; - }; - chainHead: { - unstable_body(followSubscription: U.HashHexString, networkConfig?: T.NetworkConfig): string; - unstable_call( - hash: U.HashHexString | undefined, - fn: string, - callParameters: U.HexString, - networkConfig?: T.NetworkConfig, - ): string; - unstable_genesisHash(): U.HashHexString; - unstable_header(followSubscription: string, hash: U.HashHexString): U.HexString | undefined; - unstable_stopBody(subscription: string): void; - unstable_stopCall(subscription: string): void; - unstable_stopStorage(subscription: string): void; - unstable_storage( - follow_subscription: U.SubscriptionIdString, - hash: U.HashHexString, - key: U.HexString, - childKey?: U.HexString, - networkConfig?: T.NetworkConfig, - ): string; - unstable_unfollow(followSubscription: U.SubscriptionIdString): void; - unstable_unpin(followSubscription: U.SubscriptionIdString, hash: U.HashHexString): void; - }; - contracts: { - call: TODO_NARROW_METHOD_TYPE; - getStorage( - accountId: string, /* TODO: Ss58 requirement */ - key: U.HexString, - hash?: U.HashHexString, - ): unknown; - instantiate: TODO_NARROW_METHOD_TYPE; - retProjection: TODO_NARROW_METHOD_TYPE; - upload_code: TODO_NARROW_METHOD_TYPE; - }; - childState: { - getKeys: TODO_NARROW_METHOD_TYPE; - getKeysPaged: TODO_NARROW_METHOD_TYPE; - getStorage: TODO_NARROW_METHOD_TYPE; - getStorageEntries: TODO_NARROW_METHOD_TYPE; - getStorageHash: TODO_NARROW_METHOD_TYPE; - getStorageSize: TODO_NARROW_METHOD_TYPE; - }; - chainSpec: { - unstable_chainName(): string; - unstable_genesisHash(): string; - unstable_properties(): unknown; - }; - dev: { - getBlockStats(at: U.HashHexString): T.BlockStats | undefined; - }; - engine: { - createBlock: TODO_NARROW_METHOD_TYPE; - finalizeBlock: TODO_NARROW_METHOD_TYPE; - }; - grandpa: { - proveFinality: TODO_NARROW_METHOD_TYPE; - roundState: TODO_NARROW_METHOD_TYPE; - }; - mmr: { - generateBatchProof: TODO_NARROW_METHOD_TYPE; - generateProof: TODO_NARROW_METHOD_TYPE; - }; - offchain: { - localStorageGet: TODO_NARROW_METHOD_TYPE; - localStorageSet: TODO_NARROW_METHOD_TYPE; - }; - payment: { - queryFeeDetails: TODO_NARROW_METHOD_TYPE; - queryInfo(extrinsic: U.HexString, hash?: U.HashHexString): T.RuntimeDispatchInfo; - }; - rpc: { - methods(): T.RpcMethods; - }; - state: { - call: TODO_NARROW_METHOD_TYPE; - callAt: CallMethods["state_call"]; - getChildKeys: TODO_NARROW_METHOD_TYPE; - getChildReadProof: TODO_NARROW_METHOD_TYPE; - getChildStorage: TODO_NARROW_METHOD_TYPE; - getChildStorageHash: TODO_NARROW_METHOD_TYPE; - getChildStorageSize: TODO_NARROW_METHOD_TYPE; - getKeys: TODO_NARROW_METHOD_TYPE; - getKeysPaged( - prefix: string | undefined, - count: number, - startKey?: U.HexString, - hash?: U.HashHexString, - ): U.HexString[]; - getKeysPagedAt: CallMethods["state_getKeysPaged"]; - getMetadata(hash?: U.HashHexString): string; - getPairs: TODO_NARROW_METHOD_TYPE; - getReadProof: TODO_NARROW_METHOD_TYPE; - getRuntimeVersion(at?: U.HashHexString): T.RuntimeVersion; - getStorage(key: U.HexString, hash?: U.HashHexString): U.HexString; - getStorageHash: TODO_NARROW_METHOD_TYPE; - getStorageHashAt: CallMethods["state_getStorageHash"]; - getStorageSize: TODO_NARROW_METHOD_TYPE; - getStorageSizeAt: CallMethods["state_getStorageSize"]; - queryStorage: TODO_NARROW_METHOD_TYPE; - queryStorageAt(keys: U.HexString[], at?: U.HashHexString): T.StorageChangeSet; - traceBlock: TODO_NARROW_METHOD_TYPE; - trieMigrationStatus: TODO_NARROW_METHOD_TYPE; - unsubscribeRuntimeVersion(subscription: string): boolean; - unsubscribeStorage(subscription: string): boolean; - }; - sudo: { - unstable_p2pDiscover(multiaddr: U.MultiAddressString): void; - unstable_version(): string; - }; - syncstate: { - genSyncSpec: TODO_NARROW_METHOD_TYPE; - }; - system: { - accountNextIndex(account: U.AccountIdString): number; - addLogFilter: TODO_NARROW_METHOD_TYPE; - addReservedPeer: TODO_NARROW_METHOD_TYPE; - chain(): string; - chainType(): T.SystemChainTypeKind; - dryRun: TODO_NARROW_METHOD_TYPE; - dryRunAt: CallMethods["system_dryRun"]; - health(): T.SystemHealth; - localListenAddresses(): string[]; - localPeerId(): string; - name(): string; - networkState: TODO_NARROW_METHOD_TYPE; - nodeRoles: TODO_NARROW_METHOD_TYPE; - peers(): T.SystemPeer[]; - properties: TODO_NARROW_METHOD_TYPE; - removeReservedPeer: TODO_NARROW_METHOD_TYPE; - reservedPeers: TODO_NARROW_METHOD_TYPE; - resetLogFilter: TODO_NARROW_METHOD_TYPE; - syncState: TODO_NARROW_METHOD_TYPE; - version(): string; - }; - transaction: { - unstable_unwatch(subscription: U.SubscriptionIdString): void; - }; -}>; - -export type SubscriptionMethods = rpc.EnsureMethods<{ - author: { - submitAndWatchExtrinsic(tx: U.HexString): T.TransactionStatus; - }; - beefy: { - subscribeJustifications(): T.beefy.SignedCommitment; - }; - chain: { - subscribeAllHeads(): T.Header; - subscribeFinalisedHeads: SubscriptionMethods["chain_subscribeFinalizedHeads"]; - subscribeFinalizedHeads(): T.Header; /* TODO: narrow to finalized? */ - subscribeNewHead: SubscriptionMethods["chain_subscribeNewHeads"]; - subscribeNewHeads(): T.Header; - subscribeRuntimeVersion: SubscriptionMethods["state_subscribeRuntimeVersion"]; - subscribe_newHead: SubscriptionMethods["chain_subscribeNewHeads"]; - }; - chainHead: { - unstable_follow(runtimeUpdates: boolean): T.ChainHeadUnstableFollowEvent; - }; - grandpa: { - subscribeJustifications: TODO_NARROW_METHOD_TYPE; - }; - state: { - subscribeRuntimeVersion: TODO_NARROW_METHOD_TYPE; - subscribeStorage(list: U.HexString[]): T.StorageNotification; - }; - transaction: { - unstable_submitAndWatch(transaction: U.HexString): unknown; - }; -}>; - -export type ErrorDetails = rpc.EnsureErrorDetails<{ - /** - * Invalid JSON was received by the server. - */ +export type ErrorDetails = { + /** Invalid JSON was received by the server. */ ParseError: [-32700]; - /** - * The JSON sent is not a valid Request object. - */ + /** The JSON sent is not a valid Request object. */ InvalidRequest: [-32600]; - /** - * The method does not exist / is not available. - */ + /** The method does not exist / is not available. */ MethodNotFound: [-32601]; - /** - * Invalid method parameter(s). - */ + /** Invalid method parameter(s). */ InvalidParams: [-32602]; - /** - * Internal JSON-RPC error. - */ + /** Internal JSON-RPC error. */ InternalError: [-32603]; /** * Other internal server error. @@ -258,4 +21,48 @@ export type ErrorDetails = rpc.EnsureErrorDetails<{ * Error code must be outside of the range -32000 to -32700. */ MethodError: [number]; -}>; +}; + +// TODO: +// type TodoRpc = { +// state_getChildKeys: TODO_NARROW_METHOD_TYPE; +// state_getChildReadProof: TODO_NARROW_METHOD_TYPE; +// state_getChildStorage: TODO_NARROW_METHOD_TYPE; +// state_getChildStorageHash: TODO_NARROW_METHOD_TYPE; +// state_getChildStorageSize: TODO_NARROW_METHOD_TYPE; +// sudo_unstable_p2pDiscover(multiaddr: U.HexEncoded): void; +// sudo_unstable_version(): string; +// syncstate_genSyncSpec: TODO_NARROW_METHOD_TYPE; +// transaction_unstable_unwatch(subscription: SubId): void; +// chainHead_unstable_body(followSubscription: U.HexHash, networkConfig?: T.NetworkConfig): string; +// chainHead_unstable_call( +// hash: U.HexHash | null, +// fn: string, +// callParameters: U.Hex, +// networkConfig?: T.NetworkConfig, +// ): string; +// chainHead_unstable_genesisHash(): U.HexHash; +// chainHead_unstable_header(followSubscription: string, hash: U.HexHash): U.Hex | null; +// chainHead_unstable_stopBody(subscription: string): void; +// chainHead_unstable_stopCall(subscription: string): void; +// chainHead_unstable_stopStorage(subscription: string): void; +// chainHead_unstable_storage( +// follow_subscription: SubId, +// hash: U.HexHash, +// key: U.Hex, +// childKey?: U.Hex, +// networkConfig?: T.NetworkConfig, +// ): string; +// chainHead_unstable_unfollow(followSubscription: SubId): void; +// chainHead_unstable_unpin(followSubscription: SubId, hash: U.HexHash): void; +// chainSpec_unstable_chainName(): string; +// chainSpec_unstable_genesisHash(): string; +// chainSpec_unstable_properties(): unknown; +// chainSpec_getBlockStats(at: U.HexHash): T.BlockStats | null; +// chainSpec_createBlock: TODO_NARROW_METHOD_TYPE; +// chainSpec_finalizeBlock: TODO_NARROW_METHOD_TYPE; +// rpc_methods(): T.RpcMethods; +// // subscriptions +// chainHead_unstable_follow(runtimeUpdates: boolean): T.ChainHeadUnstableFollowEvent; +// transaction_unstable_submitAndWatch(transaction: U.Hex): unknown; +// }; diff --git a/known/rpc/author.ts b/known/rpc/author.ts new file mode 100644 index 000000000..3fee6778b --- /dev/null +++ b/known/rpc/author.ts @@ -0,0 +1,129 @@ +import { Hash, Hex, RpcResult, SerdeEnum, Subscription } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/e0ccd00/client/transaction-pool/api/src/lib.rs#L104 +/** + * Possible transaction status events. + * + * This events are being emitted by `TransactionPool` watchers, + * which are also exposed over RPC. + * + * The status events can be grouped based on their kinds as: + * 1. Entering/Moving within the pool: + * - `Future` + * - `Ready` + * 2. Inside `Ready` queue: + * - `Broadcast` + * 3. Leaving the pool: + * - `InBlock` + * - `Invalid` + * - `Usurped` + * - `Dropped` + * 4. Re-entering the pool: + * - `Retracted` + * 5. Block finalized: + * - `Finalized` + * - `FinalityTimeout` + * + * The events will always be received in the order described above, however + * there might be cases where transactions alternate between `Future` and `Ready` + * pool, and are `Broadcast` in the meantime. + * + * There is also only single event causing the transaction to leave the pool. + * I.e. only one of the listed ones should be triggered. + * + * Note that there are conditions that may cause transactions to reappear in the pool. + * 1. Due to possible forks, the transaction that ends up being in included + * in one block, may later re-enter the pool or be marked as invalid. + * 2. Transaction `Dropped` at one point, may later re-enter the pool if some other + * transactions are removed. + * 3. `Invalid` transaction may become valid at some point in the future. + * (Note that runtimes are encouraged to use `UnknownValidity` to inform the pool about + * such case). + * 4. `Retracted` transactions might be included in some next block. + * + * The stream is considered finished only when either `Finalized` or `FinalityTimeout` + * event is triggered. You are however free to unsubscribe from notifications at any point. + * The first one will be emitted when the block, in which transaction was included gets + * finalized. The `FinalityTimeout` event will be emitted when the block did not reach finality + * within 512 blocks. This either indicates that finality is not available for your chain, + * or that finality gadget is lagging behind. If you choose to wait for finality longer, you can + * re-subscribe for a particular transaction hash manually again. + */ +export type TransactionStatus = SerdeEnum<{ + /** Transaction is part of the future queue. */ + future: void; + /** Transaction is part of the ready queue. */ + ready: void; + /** The transaction has been broadcast to the given peers. */ + broadcast: string[]; + /** Transaction has been included in block with given hash. */ + inBlock: Hash; + /** The block this transaction was included in has been retracted. */ + retracted: Hash; + /** + * Maximum number of finality watchers has been reached, + * old watchers are being removed. + */ + finalityTimeout: Hash; + /** Transaction has been finalized by a finality-gadget, e.g GRANDPA */ + finalized: Hash; + /** + * Transaction has been replaced in the pool, by another transaction + * that provides the same tags. (e.g. same (sender, nonce)). + */ + usurped: Hash; + /** Transaction has been dropped from the pool because of the limit. */ + dropped: void; + /** Transaction is no longer valid in the current state. */ + invalid: void; +}>; + +// https://github.com/paritytech/substrate/blob/e0ccd00/client/rpc-api/src/author/hash.rs +/** + * RPC Extrinsic or hash + * + * Allows to refer to extrinsic either by its raw representation or its hash. + */ +export type ExtrinsicOrHash = SerdeEnum<{ + /** The hash of the extrinsic. */ + hash: Hash; + /** Raw extrinsic bytes. */ + extrinsic: Hex; +}>; + +// https://github.com/paritytech/substrate/blob/e0ccd00/client/rpc-api/src/author/mod.rs#L30 +export type AuthorRpc = { + /** Submit hex-encoded extrinsic for inclusion in block. */ + author_submitExtrinsic(extrinsic: Hex): RpcResult; + /** Insert a key into the keystore. */ + author_insertKey(keyType: string, suri: string, publicKey: Hex): RpcResult; + /** Generate new session keys and returns the corresponding public keys. */ + author_rotateKeys(): RpcResult; + /** + * Checks if the keystore has private keys for the given session public keys. + * `sessionKeys` is the SCALE encoded session keys object from the runtime. + * Returns `true` iff all private keys could be found. + */ + author_hasSessionKeys(sessionsKeys: Hex): RpcResult; + /** + * Checks if the keystore has private keys for the given public key and key type. + * Returns `true` if a private key could be found. + */ + author_hasKey(pubKey: Hex, keyType: string): RpcResult; + /** Returns all pending extrinsics, potentially grouped by sender. */ + author_pendingExtrinsics(): RpcResult; + /** Remove given extrinsic from the pool and temporarily ban it to prevent reimporting. */ + author_removeExtrinsic(extrinsics: ExtrinsicOrHash[]): RpcResult; // todo + /** + * Submit an extrinsic to watch. + * + * See [`TransactionStatus`](sc_transaction_pool_api::TransactionStatus) for details on + * transaction life cycle. + */ + author_submitAndWatchExtrinsic( + extrinsic: Hex, + ): RpcResult>; + author_unwatchExtrinsic( + subscription: Subscription<"author_submitAndWatchExtrinsic", TransactionStatus>, + ): RpcResult; +}; diff --git a/known/rpc/babe.ts b/known/rpc/babe.ts new file mode 100644 index 000000000..c2a9153b4 --- /dev/null +++ b/known/rpc/babe.ts @@ -0,0 +1,21 @@ +import { AccountId, RpcResult } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/9b01569/client/consensus/babe/rpc/src/lib.rs#L154 +/** Holds information about the `slot`'s that can be claimed by a given key. */ +export interface EpochAuthorship { + /** the array of primary slots that can be claimed */ + primary: number[]; + /** the array of secondary slots that can be claimed */ + secondary: number[]; + /** The array of secondary VRF slots that can be claimed. */ + secondary_vrf: number[]; +} + +// https://github.com/paritytech/substrate/blob/9b01569/client/consensus/babe/rpc/src/lib.rs#L44 +export type BabeRpc = { + /** + * Returns data about which slots (primary or secondary) can be claimed in + * the current epoch with the keys in the keystore. + */ + babe_epochAuthorship(): RpcResult>; +}; diff --git a/known/rpc/beefy.ts b/known/rpc/beefy.ts new file mode 100644 index 000000000..bf935b55f --- /dev/null +++ b/known/rpc/beefy.ts @@ -0,0 +1,17 @@ +import { Hash, Hex, RpcResult, Subscription } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/317808a/client/beefy/rpc/src/lib.rs#L84 +export type BeefyRpc = { + /** Returns the block most recently finalized by BEEFY, alongside side its justification. */ + beefy_subscribeJustifications(): RpcResult< + Subscription<"beefy_subscribeJustifications", Hex> + >; + /** + * Returns hash of the latest BEEFY finalized block as seen by this client. + * + * The latest BEEFY block might not be available if the BEEFY gadget is not running + * in the network or if the client is still initializing or syncing with the network. + * In such case an error would be returned. + */ + beefy_getFinalizedHead(): RpcResult; +}; diff --git a/known/rpc/chain.ts b/known/rpc/chain.ts new file mode 100644 index 000000000..1343d0afb --- /dev/null +++ b/known/rpc/chain.ts @@ -0,0 +1,76 @@ +import { HexEncoded } from "../../util/branded.ts"; +import { Hash, Hex, ListOrValue, NumberOrHex, RpcResult, Subscription } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/0ba251c/primitives/runtime/src/generic/digest.rs +/** Generic header digest. */ +export interface Digest { + /** A list of logs in the digest. */ + logs: Hex[]; +} + +// https://github.com/paritytech/substrate/blob/01a3ad65/primitives/runtime/src/generic/header.rs#L39 +/** Abstraction over a block header for a substrate chain. */ +export interface Header { + /** The parent hash. */ + parentHash: Hash; + /** The block number. */ + number: HexEncoded; + /** The state trie merkle root */ + stateRoot: Hash; + /** The merkle root of the extrinsics. */ + extrinsicsRoot: Hash; + /** A chain-specific digest of data useful for light clients or referencing auxiliary data. */ + digest: Digest; +} + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/runtime/src/generic/block.rs#L126 +/** Abstraction over a substrate block and justification. */ +export interface SignedBlock { + /** Full block. */ + block: Block; + /** Block justification. */ + justifications?: [number[], number[]][]; +} + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/runtime/src/generic/block.rs#L88 +export interface Block { + /** The block header. */ + header: Header; + /** The accompanying extrinsics. */ + extrinsics: Hex[]; +} + +// https://github.com/paritytech/substrate/blob/934fbfd/client/rpc-api/src/chain/mod.rs#L27 +export type ChainRpc = { + /** Get header. */ + chain_getHeader(hash?: Hash): RpcResult
; + /** Get header and body of a relay chain block. */ + chain_getBlock(hash?: Hash): RpcResult; + /** + * Get hash of the n-th block in the canon chain. + * + * By default returns latest block hash. + */ + chain_getBlockHash(height?: ListOrValue): RpcResult>; + chain_getHead: ChainRpc["chain_getBlockHash"]; + /** Get hash of the last finalized block in the canon chain. */ + chain_getFinalizedHead(): RpcResult; + chain_getFinalisedHead: ChainRpc["chain_getFinalizedHead"]; + /** All head subscription. */ + chain_subscribeAllHeads(): RpcResult>; + chain_unsubscribeAllHeads( + subscription: Subscription<"chain_subscribeAllHeads", Header>, + ): RpcResult; + /** New head subscription. */ + chain_subscribeNewHeads(): RpcResult>; + chain_unsubscribeNewHeads( + subscription: Subscription<"chain_subscribeAllHeads", Header>, + ): RpcResult; + /** Finalized head subscription. */ + chain_subscribeFinalizedHeads(): RpcResult>; + chain_unsubscribeFinalizedHeads( + subscription: Subscription<"chain_subscribeAllHeads", Header>, + ): RpcResult; + chain_subscribeFinalisedHeads: ChainRpc["chain_subscribeFinalizedHeads"]; + chain_unsubscribeFinalisedHeads: ChainRpc["chain_unsubscribeFinalizedHeads"]; +}; diff --git a/known/rpc/childstate.ts b/known/rpc/childstate.ts new file mode 100644 index 000000000..75e025623 --- /dev/null +++ b/known/rpc/childstate.ts @@ -0,0 +1,69 @@ +import { Hash, Hex, RpcResult } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/4d04aba/primitives/storage/src/lib.rs +export type StorageKey = Hex; +export type PrefixedStorageKey = Hex; +export type StorageData = Hex; + +// https://github.com/paritytech/substrate/blob/ded44948/client/rpc-api/src/state/helpers.rs#L27 +export interface ReadProof { + /** Block hash used to generate the proof */ + at: Hash; + /** A proof used to prove that storage entries are included in the storage trie */ + proof: Hex[]; +} + +// https://github.com/paritytech/substrate/blob/934fbfd/client/rpc-api/src/child_state/mod.rs#L29 +export type ChildStateRpc = { + /** + * Returns the keys with prefix from a child storage, leave empty to get all the keys + * @deprecated [2.0.0] Please use `getKeysPaged` with proper paging support + */ + childState_getKeys( + childStorageKey: PrefixedStorageKey, + prefix: StorageKey, + hash?: Hash, + ): RpcResult; + /** + * Returns the keys with prefix from a child storage with pagination support. + * Up to `count` keys will be returned. + * If `start_key` is passed, return next keys in storage in lexicographic order. + */ + childState_getKeysPaged( + childStorageKey: PrefixedStorageKey, + prefix: StorageKey, + count: number, + startKey?: StorageKey, + hash?: Hash, + ): RpcResult; + /** Returns a child storage entry at a specific block's state. */ + childState_getStorage( + childStorageKey: PrefixedStorageKey, + key: StorageKey, + hash?: Hash, + ): RpcResult; + /** Returns child storage entries for multiple keys at a specific block's state. */ + childState_getStorageEntries( + childStorageKey: PrefixedStorageKey, + keys: StorageKey[], + hash?: Hash, + ): RpcResult<(StorageData | null)[]>; + /** Returns the hash of a child storage entry at a block's state. */ + childState_getStorageHash( + childStorageKey: PrefixedStorageKey, + key: StorageKey, + hash?: Hash, + ): RpcResult; + /** Returns the size of a child storage entry at a block's state. */ + childState_getStorageSize( + childStorageKey: PrefixedStorageKey, + key: StorageKey, + hash?: Hash, + ): RpcResult; + /** Returns proof of storage for child key entries at a specific block's state. */ + state_getChildReadProof( + childStorageKey: PrefixedStorageKey, + keys: StorageKey[], + hash?: Hash, + ): RpcResult; +}; diff --git a/known/rpc/contracts.ts b/known/rpc/contracts.ts new file mode 100644 index 000000000..07620ac6a --- /dev/null +++ b/known/rpc/contracts.ts @@ -0,0 +1,269 @@ +import { AccountId, Hash, Hex, NumberOrHex, RpcResult, SerdeEnum, SerdeResult } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/0246883/frame/contracts/rpc/src/lib.rs#L92 +/** A struct that encodes RPC parameters required for a call to a smart-contract. */ +export interface CallRequest { + origin: AccountId; + dest: AccountId; + value: NumberOrHex; + gasLimit: NumberOrHex; + storageDepositLimit: NumberOrHex | undefined; + inputData: Hex; +} + +// https://github.com/paritytech/substrate/blob/622f532/frame/contracts/common/src/lib.rs#L50 +/** + * Result type of a `bare_call` or `bare_instantiate` call. + * + * It contains the execution result together with some auxiliary information. + */ +export interface ContractResult { + /** How much gas was consumed during execution. */ + gasConsumed: number; + /** + * How much gas is required as gas limit in order to execute this call. + * + * This value should be used to determine the gas limit for on-chain execution. + * + * # Note + * + * This can only different from [`Self::gas_consumed`] when weight pre charging + * is used. Currently, only `seal_call_runtime` makes use of pre charging. + * Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging + * when a non-zero `gas_limit` argument is supplied. + */ + gasRequired: number; + /** + * How much balance was deposited and reserved during execution in order to pay for storage. + * + * The storage deposit is never actually charged from the caller in case of [`Self::result`] + * is `Err`. This is because on error all storage changes are rolled back. + */ + storageDeposit: StorageDeposit; + /** + * An optional debug message. This message is only filled when explicitly requested + * by the code that calls into the contract. Otherwise it is empty. + * + * The contained bytes are valid UTF-8. This is not declared as `String` because + * this type is not allowed within the runtime. + * + * Clients should not make any assumptions about the format of the buffer. + * They should just display it as-is. It is **not** only a collection of log lines + * provided by a contract but a formatted buffer with different sections. + * + * # Note + * + * The debug message is never generated during on-chain execution. It is reserved for + * RPC calls. + */ + debugMessage: string; + /** The execution result of the wasm code. */ + result: R; +} + +// https://github.com/paritytech/substrate/blob/622f532/frame/contracts/common/src/lib.rs#L200 +/** The amount of balance that was either charged or refunded in order to pay for storage. */ +export type StorageDeposit = SerdeEnum<{ + /** + * The transaction reduced storage consumption. + * + * This means that the specified amount of balance was transferred from the involved + * contracts to the call origin. + */ + Refund: NumberOrHex; + /** + * The transaction increased overall storage usage. + * + * This means that the specified amount of balance was transferred from the call origin + * to the contracts involved. + */ + Charge: NumberOrHex; +}>; + +// https://github.com/paritytech/substrate/blob/622f532/frame/contracts/common/src/lib.rs#L118 +/** Flags used by a contract to customize exit behaviour. */ +export enum ReturnFlags { + REVERT = 0x0000_0001, +} + +/** Output of a contract call or instantiation which ran to completion. */ +export interface ExecReturnValue { + /** Flags passed along by `seal_return`. Empty when `seal_return` was never called. */ + flags: ReturnFlags; + /** Buffer passed along by `seal_return`. Empty when `seal_return` was never called. */ + data: Hex; +} + +// https://github.com/paritytech/substrate/blob/dc22e48/primitives/runtime/src/lib.rs#L524 +/** Reason why a dispatch call failed. */ +export type DispatchError = SerdeEnum<{ + /** Some error occurred. */ + Other: string; + /** Failed to lookup some data. */ + CannotLookup: void; + /** A bad origin. */ + BadOrigin: void; + /** A custom error in a module. */ + Module: ModuleError; + /** At least one consumer is remaining so the account cannot be destroyed. */ + ConsumerRemaining: void; + /** There are no providers so the account cannot be created. */ + NoProviders: void; + /** There are too many consumers so the account cannot be created. */ + TooManyConsumers: void; + /** An error to do with tokens. */ + Token: TokenError; + /** An arithmetic error. */ + Arithmetic: ArithmeticError; + /** + * The number of transactional layers has been reached, or we are not in a transactional + * layer. + */ + Transactional: TransactionalError; +}>; + +// https://github.com/paritytech/substrate/blob/dc22e48/primitives/runtime/src/lib.rs#L479 +/** Reason why a pallet call failed. */ +export type ModuleError = { + /** Module index, matching the metadata module index. */ + index: number; + /** Module specific error value. */ + error: number; + /** Optional error message. */ + message: string | undefined; +}; + +// https://github.com/paritytech/substrate/blob/dc22e48/primitives/runtime/src/lib.rs#L641 +/** Arithmetic errors. */ +export type ArithmeticError = SerdeEnum<{ + /** Underflow. */ + Underflow: void; + /** Overflow. */ + Overflow: void; + /** Division by zero. */ + DivisionByZero: void; +}>; + +// https://github.com/paritytech/substrate/blob/dc22e48/primitives/runtime/src/lib.rs#L601 +/** Description of what went wrong when trying to complete an operation on a token. */ +export type TokenError = SerdeEnum<{ + /** Funds are unavailable. */ + NoFunds: void; + /** Account that must exist would die. */ + WouldDie: void; + /** Account cannot exist with the funds that would be given. */ + BelowMinimum: void; + /** Account cannot be created. */ + CannotCreate: void; + /** The asset in question is unknown. */ + UnknownAsset: void; + /** Funds exist but are frozen. */ + Frozen: void; + /** Operation is not supported by the asset. */ + Unsupported: void; +}>; + +// https://github.com/paritytech/substrate/blob/dc22e48/primitives/runtime/src/lib.rs#L499 +/** Errors related to transactional storage layers. */ +export type TransactionalError = SerdeEnum<{ + /** Too many transactional layers have been spawned. */ + LimitReached: void; + /** A transactional layer was expected, but does not exist. */ + NoLayer: void; +}>; + +// https://github.com/paritytech/substrate/blob/622f532/frame/contracts/common/src/lib.rs#L176 +/** Reference to an existing code hash or a new wasm module */ +export type Code = SerdeEnum<{ + /** A wasm module as raw bytes. */ + upload: Hex; + /** The code hash of an on-chain wasm blob. */ + existing: Hash; +}>; + +// https://github.com/paritytech/substrate/blob/0246883/frame/contracts/rpc/src/lib.rs#L105 +/** A struct that encodes RPC parameters required to instantiate a new smart-contract. */ +export interface InstantiateRequest { + origin: AccountId; + value: NumberOrHex; + gasLimit: NumberOrHex; + storageDepositLimit: NumberOrHex | undefined; + code: Code; + data: Hex; + salt: Hex; +} + +// https://github.com/paritytech/substrate/blob/0246883/frame/contracts/rpc/src/lib.rs#L119 +/** A struct that encodes RPC parameters required for a call to upload a new code. */ +export interface CodeUploadRequest { + origin: AccountId; + code: Hex; + storageDepositLimit: NumberOrHex | undefined; +} + +// https://github.com/paritytech/substrate/blob/622f532/frame/contracts/common/src/lib.rs#L164 +/** The result of succesfully uploading a contract. */ +export interface CodeUploadReturnValue { + /** The key under which the new code is stored. */ + codeHash: Hash; + /** The deposit that was reserved at the caller. Is zero when the code already existed. */ + deposit: NumberOrHex; +} + +// https://github.com/paritytech/substrate/blob/622f532/frame/contracts/common/src/lib.rs#L146 +/** The result of a successful contract instantiation. */ +export interface InstantiateReturnValue { + /** The output of the called constructor. */ + result: ExecReturnValue; + /** The account id of the new contract. */ + account_id: AccountId; +} + +// https://github.com/paritytech/substrate/blob/0246883/frame/contracts/rpc/src/lib.rs#L127 +export type ContractsRpc = { + /** + * Executes a call to a contract. + * + * This call is performed locally without submitting any transactions. Thus executing this + * won't change any state. Nonetheless, the calling state-changing contracts is still possible. + * + * This method is useful for calling getter-like methods on contracts or to dry-run a + * a contract call in order to determine the `gas_limit`. + */ + contracts_call( + callRequest: CallRequest, + at?: Hash, + ): RpcResult>>; + /** + * Instantiate a new contract. + * + * This instantiate is performed locally without submitting any transactions. Thus the contract + * is not actually created. + * + * This method is useful for UIs to dry-run contract instantiations. + */ + contracts_instantiate( + instantiateRequest: InstantiateRequest, + ): RpcResult>>; + /** + * Upload new code without instantiating a contract from it. + * + * This upload is performed locally without submitting any transactions. Thus executing this + * won't change any state. + * + * This method is useful for UIs to dry-run code upload. + */ + contracts_upload_code( + uploadRequest: CodeUploadRequest, + at?: Hash, + ): RpcResult>; + /** + * Returns the value under a specified storage `key` in a contract given by `address` param, + * or `None` if it is not set. + */ + contracts_getStorage( + accountId: AccountId, + key: Hex, + aat?: Hash, + ): RpcResult; +}; diff --git a/known/rpc/framesystem.ts b/known/rpc/framesystem.ts new file mode 100644 index 000000000..8a8a7fbeb --- /dev/null +++ b/known/rpc/framesystem.ts @@ -0,0 +1,17 @@ +import { AccountId, Hash, Hex, RpcResult } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/eddf888/utils/frame/rpc/system/src/lib.rs#L41 +export type FrameSystemRpc = { + /** + * Returns the next valid index (aka nonce) for given account. + * + * This method takes into consideration all pending transactions + * currently in the pool and if no transactions are found in the pool + * it fallbacks to query the index from the runtime (aka. state nonce). + */ + system_accountNextIndex(account: AccountId): RpcResult; + account_nextIndex: FrameSystemRpc["system_accountNextIndex"]; + /** Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult. */ + system_dryRun(extrinsic: Hex, at?: Hash): RpcResult; + system_dryRunAt: FrameSystemRpc["system_dryRun"]; +}; diff --git a/known/rpc/grandpa.ts b/known/rpc/grandpa.ts new file mode 100644 index 000000000..8bd36a368 --- /dev/null +++ b/known/rpc/grandpa.ts @@ -0,0 +1,64 @@ +import { Hex, RpcResult, Subscription } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/0ba251c/client/finality-grandpa/rpc/src/report.rs#L116 +/** + * The state of the current best round, as well as the background rounds in a + * form suitable for serialization. + */ +export interface ReportedRoundStates { + setId: number; + best: RoundState; + background: RoundState[]; +} + +// https://github.com/paritytech/substrate/blob/0ba251c/client/finality-grandpa/rpc/src/report.rs#L76 +export interface RoundState { + round: number; + totalWeight: number; + thresholdWeight: number; + prevotes: Prevotes; + precommits: Precommits; +} + +// https://github.com/paritytech/substrate/blob/0ba251c/client/finality-grandpa/rpc/src/report.rs#L62 +export interface Prevotes { + currentWeight: number; + missing: Hex[]; +} + +// https://github.com/paritytech/substrate/blob/0ba251c/client/finality-grandpa/rpc/src/report.rs#L69 +export interface Precommits { + currentWeight: number; + missing: Hex[]; +} + +// https://github.com/paritytech/substrate/blob/ded44948/client/finality-grandpa/rpc/src/notification.rs +/** An encoded justification proving that the given header has been finalized */ +export type JustificationNotification = Hex; + +// https://github.com/paritytech/substrate/blob/ded44948/client/finality-grandpa/rpc/src/finality.rs +export type EncodedFinalityProof = Hex; + +// https://github.com/paritytech/substrate/blob/9b01569/client/finality-grandpa/rpc/src/lib.rs#L48 +export type GrandpaRpc = { + /** + * Returns the state of the current best round state as well as the + * ongoing background rounds. + */ + grandpa_roundState(): RpcResult; + /** + * Returns the block most recently finalized by Grandpa, alongside + * side its justification. + */ + grandpa_subscribeJustifications(): RpcResult< + Subscription<"grandpa_subscribeJustifications", JustificationNotification> + >; + grandpa_unsubscribeJustifications( + subscription: Subscription<"grandpa_subscribeJustifications", JustificationNotification>, + ): void; + /** + * Prove finality for the given block number by returning the Justification for the last block + * in the set and all the intermediary headers to link them together. + */ + grandpa_proveFinality(block: number): RpcResult; +}; diff --git a/known/rpc/mmr.ts b/known/rpc/mmr.ts new file mode 100644 index 000000000..245b0684d --- /dev/null +++ b/known/rpc/mmr.ts @@ -0,0 +1,61 @@ +import { Hash, Hex, RpcResult } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/6c5ac31/primitives/merkle-mountain-range/src/lib.rs#L37 +/** + * A type to describe leaf position in the MMR. + * + * Note this is different from [`NodeIndex`], which can be applied to + * both leafs and inner nodes. Leafs will always have consecutive `LeafIndex`, + * but might be actually at different positions in the MMR `NodeIndex`. + */ +export type LeafIndex = number; + +// https://github.com/paritytech/substrate/blob/eddf888/frame/merkle-mountain-range/rpc/src/lib.rs#L49 +/** Retrieved MMR leaf and its proof. */ +export interface LeafProof { + /** Block hash the proof was generated for. */ + blockHash: Hash; + /** SCALE-encoded leaf data. */ + leaf: Hex; + /** SCALE-encoded proof data. See [sp_mmr_primitives::Proof]. */ + proof: Hex; +} + +// https://github.com/paritytech/substrate/blob/eddf888/frame/merkle-mountain-range/rpc/src/lib.rs#L72 +/** Retrieved MMR leaves and their proof. */ +export interface LeafBatchProof { + /** Block hash the proof was generated for. */ + blockHash: Hash; + /** SCALE-encoded vector of `LeafData`. */ + leaves: Hex; + /** SCALE-encoded proof data. See [sp_mmr_primitives::Proof]. */ + proof: Hex; +} + +// https://github.com/paritytech/substrate/blob/eddf888/frame/merkle-mountain-range/rpc/src/lib.rs#L99 +export type MmrRpc = { + /** + * Generate MMR proof for given leaf index. + * + * This method calls into a runtime with MMR pallet included and attempts to generate + * MMR proof for leaf at given `leaf_index`. + * Optionally, a block hash at which the runtime should be queried can be specified. + * + * Returns the (full) leaf itself and a proof for this leaf (compact encoding, i.e. hash of + * the leaf). Both parameters are SCALE-encoded. + */ + mmr_generateProof(leafIndex: LeafIndex, at?: Hash): RpcResult; + /** + * Generate MMR proof for the given leaf indices. + * + * This method calls into a runtime with MMR pallet included and attempts to generate + * MMR proof for a set of leaves at the given `leaf_indices`. + * Optionally, a block hash at which the runtime should be queried can be specified. + * + * Returns the leaves and a proof for these leaves (compact encoding, i.e. hash of + * the leaves). Both parameters are SCALE-encoded. + * The order of entries in the `leaves` field of the returned struct + * is the same as the order of the entries in `leaf_indices` supplied + */ + mmr_generateBatchProof(leafIndices: LeafIndex[], at?: Hash): RpcResult; +}; diff --git a/known/rpc/mod.ts b/known/rpc/mod.ts new file mode 100644 index 000000000..43fe16c6f --- /dev/null +++ b/known/rpc/mod.ts @@ -0,0 +1,16 @@ +export * from "./author.ts"; +export * from "./babe.ts"; +export * from "./beefy.ts"; +export * from "./chain.ts"; +export * from "./childstate.ts"; +export * from "./contracts.ts"; +export * from "./framesystem.ts"; +export * from "./grandpa.ts"; +export * from "./mmr.ts"; +export * from "./mod.ts"; +export * from "./offchain.ts"; +export * from "./payment.ts"; +export * from "./state.ts"; +export * from "./statemigration.ts"; +export * from "./system.ts"; +export * from "./utils.ts"; diff --git a/known/rpc/offchain.ts b/known/rpc/offchain.ts new file mode 100644 index 000000000..110e66159 --- /dev/null +++ b/known/rpc/offchain.ts @@ -0,0 +1,29 @@ +import { Hex, RpcResult, SerdeEnum } from "./utils.ts"; + +/** A type of supported crypto. */ +export type StorageKind = SerdeEnum<{ + /** + * Persistent storage is non-revertible and not fork-aware. It means that any value + * set by the offchain worker triggered at block `N(hash1)` is persisted even + * if that block is reverted as non-canonical and is available for the worker + * that is re-run at block `N(hash2)`. + * This storage can be used by offchain workers to handle forks + * and coordinate offchain workers running on different forks. + */ + PERSISTENT: void; + /** + * Local storage is revertible and fork-aware. It means that any value + * set by the offchain worker triggered at block `N(hash1)` is reverted + * if that block is reverted as non-canonical and is NOT available for the worker + * that is re-run at block `N(hash2)`. + */ + LOCAL: void; +}>; + +// https://github.com/paritytech/substrate/blob/7d233c2/client/rpc-api/src/offchain/mod.rs#L28 +export type OffchainRpc = { + /** Set offchain local storage under given key and prefix. */ + offchain_localStorageSet(kind: StorageKind, key: Hex, value: Hex): RpcResult; + /** Get offchain local storage under given key and prefix. */ + offchain_localStorageGet(kind: StorageKind, key: Hex): RpcResult; +}; diff --git a/known/rpc/payment.ts b/known/rpc/payment.ts new file mode 100644 index 000000000..753d2e30c --- /dev/null +++ b/known/rpc/payment.ts @@ -0,0 +1,81 @@ +import { Hash, Hex, NumberOrHex, SerdeEnum } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/23bb5a6/frame/transaction-payment/src/types.rs#L99 +/** + * Information related to a dispatchable's class, weight, and fee that can be queried from the + * runtime. + */ +export interface RuntimeDispatchInfo { + weight: number; + class: DispatchClass; + partialFee: number; +} + +// https://github.com/paritytech/substrate/blob/23bb5a6255bbcd7ce2999044710428bc4a7a924f/frame/support/src/dispatch.rs#L140 +/** + * A generalized group of dispatch types. + * + * NOTE whenever upgrading the enum make sure to also update + * [DispatchClass::all] and [DispatchClass::non_mandatory] helper functions. + */ +export type DispatchClass = SerdeEnum<{ + /** A normal dispatch. */ + normal: void; + /** An operational dispatch. */ + operational: void; + /** + * A mandatory dispatch. These kinds of dispatch are always included regardless of their + * weight, therefore it is critical that they are separately validated to ensure that a + * malicious validator cannot craft a valid but impossibly heavy block. Usually this just + * means ensuring that the extrinsic can only be included once and that it is always very + * light. + * + * Do *NOT* use it for extrinsics that can be heavy. + * + * The only real use case for this is inherent extrinsics that are required to execute in a + * block for the block to be valid, and it solves the issue in the case that the block + * initialization is sufficiently heavy to mean that those inherents do not fit into the + * block. Essentially, we assume that in these exceptional circumstances, it is better to + * allow an overweight block to be created than to not allow any block at all to be created. + */ + mandatory: void; +}>; + +// https://github.com/paritytech/substrate/blob/23bb5a6/frame/transaction-payment/src/types.rs#L69 +/** + * The `FeeDetails` is composed of: + * - (Optional) `inclusion_fee`: Only the `Pays::Yes` transaction can have the inclusion fee. + * - `tip`: If included in the transaction, the tip will be added on top. Only signed + * transactions can have a tip. + */ +export interface FeeDetails { + inclusionFee?: InclusionFee; + tip: NumberOrHex; +} + +// https://github.com/paritytech/substrate/blob/23bb5a6/frame/transaction-payment/src/types.rs#L33 +/** The base fee and adjusted weight and length fees constitute the _inclusion fee_. */ +export interface InclusionFee { + /** + * This is the minimum amount a user pays for a transaction. It is declared + * as a base _weight_ in the runtime and converted to a fee using `WeightToFee`. + */ + baseFee: NumberOrHex; + /** The length fee, the amount paid for the encoded length (in bytes) of the transaction. */ + lenFee: NumberOrHex; + /** + * - `targeted_fee_adjustment`: This is a multiplier that can tune the final fee based on the + * congestion of the network. + * - `weight_fee`: This amount is computed based on the weight of the transaction. Weight + * accounts for the execution time of a transaction. + * + * adjusted_weight_fee = targeted_fee_adjustment * weight_fee + */ + adjustedWeightFee: NumberOrHex; +} + +// https://github.com/paritytech/substrate/blob/eddf888/frame/transaction-payment/rpc/src/lib.rs#L41 +export type TransactionPaymentApi = { + payment_queryInfo(extrinsic: Hex, at?: Hash): RuntimeDispatchInfo; + payment_queryFeeDetails(extrinsic: Hex, at?: Hash): FeeDetails; +}; diff --git a/known/rpc/state.ts b/known/rpc/state.ts new file mode 100644 index 000000000..6ef24be2c --- /dev/null +++ b/known/rpc/state.ts @@ -0,0 +1,235 @@ +import { ReadProof, StorageData, StorageKey } from "./childstate.ts"; +import { Hash, Hex, RpcResult, SerdeEnum, Subscription } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/01a3ad65/primitives/version/src/lib.rs#L161 +/** + * Runtime version. + * This should not be thought of as classic Semver (major/minor/tiny). + * This triplet have different semantics and mis-interpretation could cause problems. + * In particular: bug fixes should result in an increment of `spec_version` and possibly + * `authoring_version`, absolutely not `impl_version` since they change the semantics of the + * runtime. + */ +export interface RuntimeVersion { + /** + * Identifies the different Substrate runtimes. There'll be at least polkadot and node. + * A different on-chain spec_name to that of the native runtime would normally result + * in node not attempting to sync or author blocks. + */ + specName: string; + + /** + * Name of the implementation of the spec. This is of little consequence for the node + * and serves only to differentiate code of different implementation teams. For this + * codebase, it will be parity-polkadot. If there were a non-Rust implementation of the + * Polkadot runtime (e.g. C++), then it would identify itself with an accordingly different + * `impl_name`. + */ + implName: string; + + /** + * `authoring_version` is the version of the authorship interface. An authoring node + * will not attempt to author blocks unless this is equal to its native runtime. + */ + authoringVersion: number; + + /** + * Version of the runtime specification. A full-node will not attempt to use its native + * runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, + * `spec_version` and `authoring_version` are the same between Wasm and native. + */ + specVersion: number; + + /** + * Version of the implementation of the specification. Nodes are free to ignore this; it + * serves only as an indication that the code is different; as long as the other two versions + * are the same then while the actual code may be different, it is nonetheless required to + * do the same thing. + * Non-consensus-breaking optimizations are about the only changes that could be made which + * would result in only the `impl_version` changing. + */ + implVersion: number; + /** List of supported API "features" along with their versions. */ + apis: [Hash, Hex | undefined][]; + /** + * All existing dispatches are fully compatible when this number doesn't change. If this + * number changes, then `spec_version` must change, also. + * + * This number must change when an existing dispatchable (module ID, dispatch ID) is changed, + * either through an alteration in its user-level semantics, a parameter + * added/removed/changed, a dispatchable being removed, a module being removed, or a + * dispatchable/module changing its index. + * + * It need *not* change when a new module is added or when a dispatchable is added. + */ + transactionVersion: number; + /** + * Version of the state implementation used by this runtime. + * Use of an incorrect version is consensus breaking. + */ + stateVersion: number; +} + +// https://github.com/paritytech/substrate/blob/4d04aba/primitives/storage/src/lib.rs#L181 +/** Storage change set */ +export interface StorageChangeSet { + /** Block hash */ + block: Hash; + /** A list of changes */ + changes: [StorageKey, StorageData | null][]; +} + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/rpc/src/tracing.rs#L96 +/** Response for the `state_traceBlock` RPC. */ +export type TraceBlockResponse = SerdeEnum<{ + /** Error block tracing response */ + traceError: TraceError; + /** Successful block tracing response */ + blockTrace: BlockTrace; +}>; + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/rpc/src/tracing.rs#L88 +/** Error response for the `state_traceBlock` RPC. */ +export interface TraceError { + /** Error message */ + error: string; +} + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/rpc/src/tracing.rs#L27 +export interface BlockTrace { + /** Hash of the block being traced */ + blockHash: Hash; + /** Parent hash */ + parentHash: Hash; + /** + * Module targets that were recorded by the tracing subscriber. + * Empty string means record all targets. + */ + tracingTargets: string; + /** + * Storage key targets used to filter out events that do not have one of the storage keys. + * Empty string means do not filter out any events. + */ + storage_keys: string; + /** + * Method targets used to filter out events that do not have one of the event method. + * Empty string means do not filter out any events. + */ + methods: string; + /** Vec of tracing spans */ + spans: Span[]; + /** Vec of tracing events */ + events: Event[]; +} + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/rpc/src/tracing.rs#L50 +/** Represents a tracing event, complete with recorded data. */ +export interface Event { + /** Event target */ + target: string; + /** Associated data */ + data: Data; + /** Parent id, if it exists */ + parent_id?: number; +} + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/rpc/src/tracing.rs#L80 +/** Holds associated values for a tracing span. */ +export interface Data { + /** HashMap of `String` values recorded while tracing */ + stringValues: Record; +} + +// https://github.com/paritytech/substrate/blob/ded44948/primitives/rpc/src/tracing.rs#L64 +/** + * Represents a single instance of a tracing span. + * + * Exiting a span does not imply that the span will not be re-entered. + */ +export interface Span { + /** id for this span */ + id: number; + /** id of the parent span, if any */ + parentId?: number; + /** Name of this span */ + name: string; + /** Target, typically module */ + target: string; + /** Indicates if the span is from wasm */ + wasm: boolean; +} + +// https://github.com/paritytech/substrate/blob/28ac0a8/client/rpc-api/src/state/mod.rs#L35 +export type StateRpc = { + /** Call a contract at a block's state. */ + state_call(name: string, bytes: Hex, at?: Hash): RpcResult; + state_callAt: StateRpc["state_call"]; + /** + * Returns the keys with prefix, leave empty to get all the keys. + * @deprecated [2.0.0] Please use `getKeysPaged` with proper paging support + */ + state_getKeys(prefix: StorageKey, at?: Hash): RpcResult; + /** Returns the keys with prefix, leave empty to get all the keys */ + state_getPairs(prefix: StorageKey, at?: Hash): RpcResult<[StorageKey, StorageData][]>; + /** + * Returns the keys with prefix with pagination support. + * Up to `count` keys will be returned. + * If `start_key` is passed, return next keys in storage in lexicographic order. + */ + state_getKeysPaged( + prefix: StorageKey | null, + count: number, + startKey?: StorageKey, + at?: Hash, + ): RpcResult; + state_getKeysPagedAt: StateRpc["state_getKeysPaged"]; + /** Returns a storage entry at a specific block's state. */ + state_getStorage(key: StorageKey, at?: Hash): RpcResult; + state_getStorageAt: StateRpc["state_getStorage"]; + /** Returns the hash of a storage entry at a block's state. */ + state_getStorageHash(key: StorageKey, at?: Hash): RpcResult; + state_getStorageHashAt: StateRpc["state_getStorageHash"]; + /** Returns the size of a storage entry at a block's state. */ + state_getStorageSize(key: StorageKey, at?: Hash): RpcResult; + state_getStorageSizeAt: StateRpc["state_getStorageSize"]; + /** Returns the runtime metadata as an opaque blob. */ + state_getMetadata(at?: Hash): RpcResult; + /** Get the runtime version. */ + state_getRuntimeVersion(at?: Hash): RpcResult; + chain_getRuntimeVersion: StateRpc["state_getRuntimeVersion"]; + /** + * Query historical storage entries (by key) starting from a block given as the second + * parameter. + * + * NOTE This first returned result contains the initial state of storage for all keys. + * Subsequent values in the vector represent changes to the previous state (diffs). + */ + state_queryStorage(keys: StorageKey[], block: Hash, at?: Hash): RpcResult; + /** Query storage entries (by key) starting at block hash given as the second parameter. */ + state_queryStorageAt(keys: StorageKey[], at?: Hash): RpcResult; + /** Returns proof of storage entries at a specific block's state. */ + state_getReadProof(keys: StorageKey[], at?: Hash): RpcResult; + /** New runtime version subscription */ + state_subscribeRuntimeVersion(): RpcResult< + Subscription<"state_subscribeRuntimeVersion", RuntimeVersion> + >; + state_unsubscribeRuntimeVersion( + subscription: Subscription<"state_subscribeRuntimeVersion", RuntimeVersion>, + ): RpcResult; + chain_subscribeRuntimeVersion: StateRpc["state_subscribeRuntimeVersion"]; + chain_unsubscribeRuntimeVersion: StateRpc["state_unsubscribeRuntimeVersion"]; + /** New storage subscription */ + state_subscribeStorage( + keys: StorageKey[] | null, + ): RpcResult>; + state_unsubscribeStorage( + subscription: Subscription<"state_subscribeStorage", StorageChangeSet>, + ): RpcResult; + /** See https://paritytech.github.io/substrate/master/sc_rpc_api/state/trait.StateApiServer.html#tymethod.trace_block */ + state_traceBlock( + block: Hash, + targets?: string, + storageKeys?: string, + methods?: string, + ): RpcResult; +}; diff --git a/known/rpc/statemigration.ts b/known/rpc/statemigration.ts new file mode 100644 index 000000000..a5dbc9a79 --- /dev/null +++ b/known/rpc/statemigration.ts @@ -0,0 +1,19 @@ +import { Hash, RpcResult } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/00cc5f1/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs#L106 +export interface MigrationStatusResult { + topRemainingToMigrate: number; + childRemainingToMigrate: number; +} + +// https://github.com/paritytech/substrate/blob/00cc5f1/utils/frame/rpc/state-trie-migration-rpc/src/lib.rs#L113 +export type StateMigrationRpc = { + /** + * Check current migration state. + * + * This call is performed locally without submitting any transactions. Thus executing this + * won't change any state. Nonetheless it is a VERY costy call that should be + * only exposed to trusted peers. + */ + state_trieMigrationStatus(at?: Hash): RpcResult; +}; diff --git a/known/rpc/system.ts b/known/rpc/system.ts new file mode 100644 index 000000000..18b3661e3 --- /dev/null +++ b/known/rpc/system.ts @@ -0,0 +1,134 @@ +import { Hash, RpcResult, SerdeEnum } from "./utils.ts"; + +// https://github.com/paritytech/substrate/blob/57e3486/client/chain-spec/src/lib.rs#L198 +export type ChainType = SerdeEnum<{ + /** A development chain that runs mainly on one node. */ + Development: void; + /** A local chain that runs locally on multiple nodes for testing purposes. */ + Local: void; + /** A live chain. */ + Live: void; + /** Some custom chain type. */ + Custom: string; +}>; + +// https://github.com/paritytech/substrate/blob/c172d0f/client/rpc-api/src/system/helpers.rs#L43 +/** Health struct returned by the RPC */ +export interface Health { + /** Number of connected peers */ + peers: number; + /** Is the node syncing */ + isSyncing: boolean; + /** + * Should this node have any peers + * + * Might be false for local chains or when running without discovery. + */ + shouldHavePeers: boolean; +} + +// https://github.com/paritytech/substrate/blob/c172d0f/client/rpc-api/src/system/helpers.rs#L63 +/** Network Peer information */ +export interface PeerInfo { + /** Peer ID */ + peerId: string; + /** Roles */ + roles: string; + /** Peer best block hash */ + best_hash: Hash; + /** Peer best block number */ + best_number: number; +} + +// https://github.com/paritytech/substrate/blob/c172d0f/client/rpc-api/src/system/helpers.rs#L76 +/** The role the node is running as */ +export type NodeRole = SerdeEnum<{ + /** The node is a full node */ + Full: void; + /** The node is an authority */ + Authority: void; +}>; + +// https://github.com/paritytech/substrate/blob/c172d0f/client/rpc-api/src/system/helpers.rs#L86 +export interface SyncState { + /** Height of the block at which syncing started. */ + startingBlock: number; + /** Height of the current best block of the node. */ + currentBlock: number; + /** Height of the highest block in the network. */ + highestBlock: number; +} + +// https://github.com/paritytech/substrate/blob/e0ccd00/client/rpc-api/src/system/mod.rs#L33 +export type SystemRpc = { + /** Get the node's implementation name. Plain old string. */ + system_name(): RpcResult; + /** Get the node implementation's version. Should be a semver string. */ + system_version(): RpcResult; + /** Get the chain's name. Given as a string identifier. */ + system_chain(): RpcResult; + /** Get the chain's type. */ + system_chainType(): RpcResult; + /** Get a custom set of properties as a JSON object, defined in the chain spec. */ + system_properties(): RpcResult>; + /** + * Return health status of the node. + * + * Node is considered healthy if it is: + * - connected to some peers (unless running in dev mode) + * - not performing a major sync + */ + system_health(): RpcResult; + /** Returns the base58-encoded PeerId of the node. */ + system_localPeerId(): RpcResult; + /** + * Returns the multi-addresses that the local node is listening on + * + * The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to + * be passed to `addReservedPeer` or as a bootnode address for example. + */ + system_localListenAddresses(): RpcResult; + /** Returns currently connected peers */ + system_peers(): RpcResult; + /** + * Returns current state of the network. + * + * **Warning**: This API is not stable. Please do not programmatically interpret its output, + * as its format might change at any time. + */ + // TODO: the future of this call is uncertain: https://github.com/paritytech/substrate/issues/1890 + // https://github.com/paritytech/substrate/issues/5541 + system_networkState(): RpcResult; + /** + * Adds a reserved peer. Returns the empty string or an error. The string + * parameter should encode a `p2p` multiaddr. + * + * `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV` + * is an example of a valid, passing multiaddr with PeerId attached. + */ + system_addReservedPeer(peer: string): RpcResult; + /** + * Remove a reserved peer. Returns the empty string or an error. The string + * should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`. + */ + system_removeReservedPeer(peerId: string): RpcResult; + /** Returns the list of reserved peers */ + system_reservedPeers(): RpcResult; + /** Returns the roles the node is running as. */ + system_nodeRoles(): RpcResult; + /** + * Returns the state of the syncing of the node: starting block, current best block, highest + * known block. + */ + system_syncState(): RpcResult; + /** + * Adds the supplied directives to the current log filter + * + * The syntax is identical to the CLI `=`: + * + * `sync=debug,state=trace` + */ + system_addLogFilter(directives: string): RpcResult; + /** Resets the log filter to Substrate defaults */ + system_resetLogFilter(): RpcResult; +}; diff --git a/known/rpc/utils.ts b/known/rpc/utils.ts new file mode 100644 index 000000000..19094b882 --- /dev/null +++ b/known/rpc/utils.ts @@ -0,0 +1,16 @@ +import { Expand } from "../../deps/scale.ts"; +import * as U from "../../util/mod.ts"; + +export type SerdeResult = SerdeEnum<{ Ok: O; Err: E }>; +export type SerdeEnum = { + [K in keyof T]: T[K] extends void ? K : Expand & Omit<{ [K in keyof T]?: never }, K>>; +}[keyof T]; + +export type Hex = U.Hex; +export type Hash = U.HexHash; +export type SubId = string; +export type AccountId = string; +export type Subscription = string & { _subscription: [T, U] }; +export type NumberOrHex = U.HexEncoded | number; +export type ListOrValue = T | T[]; +export type RpcResult = T; diff --git a/known/types/author.ts b/known/types/author.ts deleted file mode 100644 index 1083f9e38..000000000 --- a/known/types/author.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as U from "../../util/mod.ts"; - -export type TransactionStatus = - | "future" - | "ready" - | "dropped" - | "invalid" - | { - inBlock: U.HashHexString; - broadcast?: never; - retracted?: never; - finalityTimeout?: never; - finalized?: never; - usurped?: never; - } - | { - inBlock?: never; - broadcast: string[]; - retracted?: never; - finalityTimeout?: never; - finalized?: never; - usurped?: never; - } - | { - inBlock?: never; - broadcast?: never; - retracted: U.HashHexString; - finalityTimeout?: never; - finalized?: never; - usurped?: never; - } - | { - inBlock?: never; - broadcast?: never; - retracted?: never; - finalityTimeout: U.HashHexString; - finalized?: never; - usurped?: never; - } - | { - inBlock?: never; - broadcast?: never; - retracted?: never; - finalityTimeout: never; - finalized: U.HashHexString; - usurped?: never; - } - | { - inBlock?: never; - broadcast?: never; - retracted?: never; - finalityTimeout: never; - finalized?: never; - usurped: U.HashHexString; - }; diff --git a/known/types/beefy.ts b/known/types/beefy.ts deleted file mode 100644 index 38ebf914c..000000000 --- a/known/types/beefy.ts +++ /dev/null @@ -1,16 +0,0 @@ -// TODO: clean this up! - -export interface SignedCommitment { - commitment: Commitment; - signatures: ("TODO" | undefined)[]; -} - -export interface Commitment { - payload: Payload; - blockNumber: number; - validatorSetId: string; -} - -export type Payload = [[PayloadId, number[]][]]; - -export type PayloadId = number; diff --git a/known/types/chain_head/unstable/follow.ts b/known/types/chain_head/unstable/follow.ts deleted file mode 100644 index 930a670ef..000000000 --- a/known/types/chain_head/unstable/follow.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as U from "../../../../util/mod.ts"; - -export type ChainHeadUnstableFollowEvent = - | ChainHeadUnstableFollowInitializedEvent - | ChainHeadUnstableFollowNewBlockEvent - | ChainHeadUnstableFollowBestBlockChangedEvent - | ChainHeadUnstableFollowFinalizedEvent - | ChainHeadUnstableFollowStopEvent; - -export type ChainHeadUnstableFollowEventKind = - | "initialized" - | "newBlock" - | "bestBlockChanged" - | "finalized" - | "stop"; - -interface ChainHeadUnstableFollowEventBase { - event: Kind; -} - -export interface ChainHeadUnstableFollowInitializedEvent - extends ChainHeadUnstableFollowEventBase<"initialized"> -{ - finalizedBlockHash: U.HashHexString; - finalizedBlockRuntime: string; -} - -export interface ChainHeadUnstableFollowNewBlockEvent - extends ChainHeadUnstableFollowEventBase<"newBlock"> -{ - blockHash: U.HashHexString; - parentBlockHash: U.HashHexString; - newRuntime: null; // TODO -} - -export interface ChainHeadUnstableFollowBestBlockChangedEvent - extends ChainHeadUnstableFollowEventBase<"bestBlockChanged"> -{ - bestBlockHash: U.HashHexString; -} - -export interface ChainHeadUnstableFollowFinalizedEvent - extends ChainHeadUnstableFollowEventBase<"finalized"> -{ - finalizedBlocksHashes: U.HashHexString[]; - prunedBlocksHashes: U.HashHexString[]; -} - -export type ChainHeadUnstableFollowStopEvent = ChainHeadUnstableFollowEventBase<"stop">; - -export type MaybeRuntimeSpec = { - type: "valid"; - spec: RuntimeSpec; -} | { - type: "invalid"; - error: string; -}; - -export interface RuntimeSpec { - specName: string; - implName: string; - authoringVersion: number; - specVersion: number; - implVersion: number; - transactionVersion?: number; - // TODO: type this as the Serde-serialized form of `hashbrown::HashMap` - apis: unknown; -} diff --git a/known/types/common.ts b/known/types/common.ts deleted file mode 100644 index a01be3ac0..000000000 --- a/known/types/common.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as U from "../../util/mod.ts"; - -export interface Block { - block: { - header: Header; - extrinsics: Extrinsic[]; - }; - justifications?: [number[], number[]][]; -} - -export interface HeaderDigest { - logs: U.HexString[]; -} - -export interface Header { - digest: HeaderDigest; - extrinsicsRoot: U.HashHexString; - number: U.HexU64String; - parentHash: U.HashHexString; - stateRoot: U.HashHexString; -} - -export interface RuntimeVersion { - specName: string; - implName: string; - authoringVersion: number; - specVersion: number; - implVersion: number; - transactionVersion?: number; - stateVersion?: number; - apis: [U.HexString, number][]; -} - -export interface StorageChangeSet { - block: U.HashHexString; - changes: [U.HexString, U.HexString | undefined][]; -} - -export interface SystemHealth { - isSyncing: boolean; - peers: number; - shouldHavePeers: boolean; -} - -export type SystemPeerRole = - | "AUTHORITY" - | "FULL" - | "LIGHT"; - -export interface SystemPeer { - peerId: string; - roles: SystemPeerRole; - bestHash: U.HashHexString; - bestNumber: number; -} - -export interface NetworkConfig { - totalAttempts: number; - maxParallel: number; - timeoutMs: number; -} - -export interface RpcMethods { - version: bigint; - methods: string[]; -} diff --git a/known/types/mod.ts b/known/types/mod.ts deleted file mode 100644 index b22620fe5..000000000 --- a/known/types/mod.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./author.ts"; -export * as beefy from "./beefy.ts"; -export * from "./chain_head/unstable/follow.ts"; -export * from "./common.ts"; -export * from "./state.ts"; -export * from "./system.ts"; -export * from "./todo_organize_these.ts"; diff --git a/known/types/state.ts b/known/types/state.ts deleted file mode 100644 index aeba34839..000000000 --- a/known/types/state.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as U from "../../util/mod.ts"; - -export interface StorageNotification { - block: U.HashHexString; - // TODO: clean this up in accordance with https://github.com/paritytech/substrate/blob/0ba251c9388452c879bfcca425ada66f1f9bc802/client/api/src/notifications.rs#L55-L60 - changes: [U.HexString, U.HexString][]; -} diff --git a/known/types/system.ts b/known/types/system.ts deleted file mode 100644 index 4ced4c93b..000000000 --- a/known/types/system.ts +++ /dev/null @@ -1,16 +0,0 @@ -export type DispatchClassKind = - | "normal" - | "operational" - | "mandatory"; - -export interface RuntimeDispatchInfo { - weight: number; - class: DispatchClassKind; - partial_fee: number; -} - -export type SystemChainTypeKind = - | "Development" - | "Local" - | "Live" - | "Custom"; diff --git a/known/types/todo_organize_these.ts b/known/types/todo_organize_these.ts deleted file mode 100644 index 1c15f1943..000000000 --- a/known/types/todo_organize_these.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BlockStats { - witnessLen: BigInt; - witnessCompactLen: BigInt; - blockLen: BigInt; - numExtrinsics: BigInt; -} diff --git a/rpc/Base.ts b/rpc/Base.ts index 74a2a1c2a..95b32f12a 100644 --- a/rpc/Base.ts +++ b/rpc/Base.ts @@ -1,20 +1,17 @@ -import { Config } from "../config/mod.ts"; import { deferred } from "../deps/std/async.ts"; import * as U from "../util/mod.ts"; import { ClientHooks, Provider } from "./common.ts"; import * as msg from "./messages.ts"; -import { IsCorrespondingRes } from "./util.ts"; export type OnMessage = (message: Message) => void; export abstract class Client< - Config_ extends Config, RawIngressMessage, InternalError, CloseError extends Error, > { #nextId = 0; - #listenerCbs = new Map>, true>(); + #listenerCbs = new Map, true>(); /** * Construct a new RPC client @@ -22,8 +19,8 @@ export abstract class Client< * @param hooks the error handling and message hooks with which you'd like the instance to operate */ constructor( - readonly provider: Provider, - readonly hooks?: ClientHooks, + readonly provider: Provider, + readonly hooks?: ClientHooks, ) {} /** @@ -31,7 +28,7 @@ export abstract class Client< * * @param egressMessage the message you wish to send to the RPC server */ - send = (egressMessage: msg.InitMessage): void => { + send = (egressMessage: msg.InitMessage): void => { this.hooks?.send?.(egressMessage); this.provider.send(egressMessage); }; @@ -51,7 +48,7 @@ export abstract class Client< * * @param createListenerCb the factory for the callback to be triggered upon arrival of ingress messages */ - listen = (createListenerCb: U.CreateWatchHandler>): void => { + listen = (createListenerCb: U.CreateWatchHandler): void => { const stopListening = () => { this.#listenerCbs.delete(listenerCb); }; @@ -85,26 +82,22 @@ export abstract class Client< * @param params the params with which to call the method * @returns an ingress message corresponding to the given method (or a message-agnostic error) */ - call = < - MethodName extends Extract, - IngressMessage extends msg.OkMessage | msg.ErrMessage, - >( - methodName: MethodName, - params: Parameters, - ): Promise => { - const init = > { + call = ( + methodName: string, + params: unknown[], + ): Promise => { + const init = { jsonrpc: "2.0", id: this.uid(), method: methodName, params, }; - const isCorrespondingRes = IsCorrespondingRes(init); - const ingressMessagePending = deferred(); + const ingressMessagePending = deferred(); this.listen((stopListening) => { return (res) => { - if (isCorrespondingRes(res)) { + if (res.id === init.id) { stopListening(); - ingressMessagePending.resolve(res as IngressMessage); + ingressMessagePending.resolve(res as msg.IngressMessage); } }; }); @@ -119,19 +112,18 @@ export abstract class Client< * @param params the params with which to init the subscription * @param createListenerCb the factory of the callback to which notifications should be supplied */ - subscribe = async >( - methodName: MethodName, - params: Parameters, - createListenerCb: CreateListenerCb, - cleanup: SubscriptionCleanup = () => Promise.resolve(), - ): Promise> => { + subscribe = async ( + methodName: string, + params: unknown[], + createListenerCb: CreateListenerCb, + cleanup: SubscriptionCleanup = () => Promise.resolve(), + ): Promise => { const initRes = await this.call(methodName, params); if (initRes.error) { - // TODO: fix typings - return initRes as msg.ErrMessage; + return initRes; } - const cleanupApplied = () => cleanup(initRes as msg.OkMessage); - const terminalPending = deferred>(); + const cleanupApplied = () => cleanup(initRes as msg.OkMessage); + const terminalPending = deferred(); this.listen((stop) => { const listenerCb = createListenerCb( async () => { @@ -142,7 +134,7 @@ export abstract class Client< ); return (res) => { if (res.params?.subscription && res.params.subscription === initRes.result) { - listenerCb(res as msg.NotifMessage); + listenerCb(res as msg.NotifMessage); } else { // TODO: associate errors with subscriptions & exit } @@ -152,12 +144,6 @@ export abstract class Client< }; } -export type CreateListenerCb< - Config_ extends Config, - MethodName extends Extract, -> = U.CreateWatchHandler>; +export type CreateListenerCb = U.CreateWatchHandler; -export type SubscriptionCleanup< - Config_ extends Config, - MethodName extends keyof Config_["RpcMethods"], -> = (initOk: msg.OkMessage) => Promise; +export type SubscriptionCleanup = (initOk: msg.OkMessage) => Promise; diff --git a/rpc/Demux.test.ts b/rpc/Demux.test.ts index 6b1ccbc5e..01c398709 100644 --- a/rpc/Demux.test.ts +++ b/rpc/Demux.test.ts @@ -12,7 +12,7 @@ Deno.test({ const client = await proxyClient(T.polkadot); assert(!(client instanceof Error)); const groups = await Promise.all( - ([][]> [[], [], []]).map( + ( [[], [], []]).map( async (messages) => { let i = 1; await client.subscribe("chain_subscribeAllHeads", [], (stop) => { diff --git a/rpc/Demux.ts b/rpc/Demux.ts index d42db87d4..87d893c28 100644 --- a/rpc/Demux.ts +++ b/rpc/Demux.ts @@ -1,35 +1,33 @@ -import { Config } from "../config/mod.ts"; import { deferred } from "../deps/std/async.ts"; import { assert } from "../deps/std/testing/asserts.ts"; import * as U from "../util/mod.ts"; import { Client } from "./Base.ts"; import * as msg from "./messages.ts"; -class DemuxGroup { - members = new Map>, true>(); +class DemuxGroup { + members = new Map, true>(); constructor(readonly stop: () => void) {} } // TODO: decide whether this is even beneficial export class Demux< - Config_ extends Config, RawIngressMessage, InternalError, CloseError extends Error, > { - #groups = new Map>(); + #groups = new Map(); - constructor(readonly client: Client) {} + constructor(readonly client: Client) {} - subscribe = async >( - methodName: MethodName, - params: Parameters, - createWatchHandler: U.CreateWatchHandler>, - ): Promise> => { + subscribe = async ( + methodName: string, + params: unknown[], + createWatchHandler: U.CreateWatchHandler, + ): Promise => { const key = `${methodName}(${JSON.stringify(params)})`; let group = this.#groups.get(key); - const status = deferred>(); + const status = deferred(); if (!group) { const groupCreated = deferred(); this.client.subscribe(methodName, params, (stop) => { diff --git a/rpc/common.ts b/rpc/common.ts index 401ab13da..9e546d635 100644 --- a/rpc/common.ts +++ b/rpc/common.ts @@ -1,4 +1,3 @@ -import { Config } from "../config/mod.ts"; import { ErrorCtor } from "../util/mod.ts"; import * as msg from "./messages.ts"; @@ -6,7 +5,6 @@ export type ProviderMethods = Record any>; export type ErrorDetails = Record; export interface Provider< - Config_ extends Config, RawIngressMessage, CloseError extends Error, > { @@ -18,13 +16,13 @@ export interface Provider< */ parseIngressMessage: ( rawIngressMessage: RawIngressMessage, - ) => msg.IngressMessage | ParseRawIngressMessageError; + ) => msg.IngressMessage | ParseRawIngressMessageError; /** * The provider-specific send implementation * * @param egressMessage the message you wish to send to the RPC server */ - send: (egressMessage: msg.InitMessage) => void; + send: (egressMessage: msg.InitMessage) => void; // TODO: introduce `FailedToClose` error in the return type (union with `undefined`) /** * Close the connection and free up resources @@ -34,9 +32,9 @@ export interface Provider< close: () => Promise; } -export interface ClientHooks { - send?: (message: msg.InitMessage) => void; - receive?: (message: msg.IngressMessage) => void; +export interface ClientHooks { + send?: (message: msg.InitMessage) => void; + receive?: (message: msg.IngressMessage) => void; error?: (error: ParseRawIngressMessageError | InternalError) => void; close?: () => void; } diff --git a/rpc/messages.ts b/rpc/messages.ts index d4fe9466e..0b9c96b24 100644 --- a/rpc/messages.ts +++ b/rpc/messages.ts @@ -1,126 +1,46 @@ -import { Config } from "../config/mod.ts"; -import * as U from "../util/mod.ts"; -import { ProviderMethods } from "./common.ts"; - -/** Ensure valid declaration of RPC method lookup (for both calls and subscriptions) */ -export type EnsureMethods> = U.U2I< - { - [Prefix in keyof Lookup]: { - [M in Extract as `${Extract}_${M}`]: - Lookup[Prefix][M]; - }; - }[keyof Lookup] ->; - -/** Ensure valid declaration of an RPC error detail lookup */ -export type EnsureErrorDetails> = Lookup; - -/** Get a mapping from method name to init message */ -export type InitMessageByMethodName = { - [N in Extract]: InitMessageBase< - N, - Parameters - >; -}; - -/** Get a union of init messages or––if `N` is supplied––a specific init message */ -export type InitMessage< - Config_ extends Config, - N extends Extract = Extract< - keyof Config_["RpcMethods"], - string - >, -> = InitMessageByMethodName[N]; - -/** Get a mapping from method name to ok ingress message */ -export type OkMessageByMethodName = - & { - [N in Extract]: OkMessageBase< - ReturnType - >; - } - & { [N in keyof Config_["RpcSubscriptionMethods"]]: OkMessageBase }; - -/** Get a ok ingress messages or––if `N` is supplied––a specific ok ingress message */ -export type OkMessage< - Config_ extends Config, - N extends keyof OkMessageByMethodName = keyof OkMessageByMethodName, -> = OkMessageByMethodName[N]; - -/** Get a mapping from method name to "notification" ingress message */ -export type NotifByMethodName = { - [N in Extract]: NotifMessageBase< - N, - ReturnType - >; -}; - -/** Get a union of notification messages or––if `N` is supplied––a specific notification message */ -export type NotifMessage< - Config_ extends Config, - N extends keyof NotifByMethodName = keyof NotifByMethodName, -> = NotifByMethodName[N]; - -// TODO: investigate whether it's worthwhile to support somehow tacking on narrow method-specific types -/** Get a mapping from error name to error ingress message */ -export type ErrMessageByName = { - [N in keyof Config_["RpcErrorDetails"]]: ErrorMessageBase< - Config_["RpcErrorDetails"][N][0], - Config_["RpcErrorDetails"][N][1] - >; -}; - -/** Get a union of error messages */ -export type ErrMessage = U.ValueOf>; - -export type IngressMessage = - | OkMessage - | U.ValueOf> // ... for now - | NotifMessage; +export type IngressMessage = + | OkMessage + | ErrMessage + | NotifMessage; interface JsonRpcVersionBearer { jsonrpc: "2.0"; } -export interface InitMessageBase - extends JsonRpcVersionBearer -{ - method: Method; +export interface InitMessage extends JsonRpcVersionBearer { + method: string; id: string; - params: Params; + params: unknown[]; } -export interface OkMessageBase extends JsonRpcVersionBearer { +export interface OkMessage extends JsonRpcVersionBearer { id: string; - result: Result; + // TODO + result: any; params?: never; error?: never; } -export interface NotifMessageBase extends JsonRpcVersionBearer { - method: Method; +export interface NotifMessage extends JsonRpcVersionBearer { + method: string; id?: never; params: { - subscription: U.SubscriptionIdString; - result: Result; + subscription: string; + // TODO + result: any; }; result?: never; error?: never; } -interface ErrorMessageBase< - Code extends number, - Data = undefined, -> extends JsonRpcVersionBearer { +export interface ErrMessage extends JsonRpcVersionBearer { id: string; - error: - & { - code: Code; - message: string; - } - & (Data extends undefined ? Record : { - data: Data; - }); + error: { + code: number; + message: string; + // TODO + data?: any; + }; params?: never; result?: never; } diff --git a/rpc/mod.ts b/rpc/mod.ts index f3606ec24..d6120031b 100644 --- a/rpc/mod.ts +++ b/rpc/mod.ts @@ -4,4 +4,3 @@ export * from "./messages.ts"; export * from "./providers/proxy.ts"; export * from "./providers/smoldot.ts"; export * from "./providers/std.ts"; -export * from "./util.ts"; diff --git a/rpc/providers/proxy.test.ts b/rpc/providers/proxy.test.ts index 2fa054ad1..1d4d081ab 100644 --- a/rpc/providers/proxy.test.ts +++ b/rpc/providers/proxy.test.ts @@ -17,7 +17,7 @@ Deno.test({ }); await t.step("subscribe", async () => { - const result: msg.NotifMessage[] = []; + const result: msg.NotifMessage[] = []; let i = 1; await client.subscribe("chain_subscribeAllHeads", [], (stop) => { return (message) => { diff --git a/rpc/providers/proxy.ts b/rpc/providers/proxy.ts index cfbb5443a..9adc5c4cb 100644 --- a/rpc/providers/proxy.ts +++ b/rpc/providers/proxy.ts @@ -4,17 +4,17 @@ import { ErrorCtor } from "../../util/mod.ts"; import * as B from "../Base.ts"; import { ClientHooks, ParseRawIngressMessageError } from "../common.ts"; -export type ProxyClientHooks> = ClientHooks; +export type ProxyClientHooks = ClientHooks; -export async function proxyClient>( - config: Config_, - hooks?: ProxyClientHooks, -): Promise | FailedToOpenConnectionError> { +export async function proxyClient( + config: Config, + hooks?: ProxyClientHooks, +): Promise { const ws = new WebSocket(await config.discoveryValue); const client = new ProxyClient(ws, hooks); ws.addEventListener("error", client.onError); ws.addEventListener("message", client.onMessage); - const pending = deferred | FailedToOpenConnectionError>(); + const pending = deferred(); if (ws.readyState === WebSocket.CONNECTING) { const onOpenError = (e: any) => { console.log({ log: e }); @@ -37,10 +37,8 @@ export async function proxyClient>( return await pending; } -export class ProxyClient - extends B.Client -{ - constructor(ws: WebSocket, hooks?: ProxyClientHooks) { +export class ProxyClient extends B.Client { + constructor(ws: WebSocket, hooks?: ProxyClientHooks) { super( { parseIngressMessage: (e) => { diff --git a/rpc/providers/smoldot.ts b/rpc/providers/smoldot.ts index d7a821275..f855cbbc7 100644 --- a/rpc/providers/smoldot.ts +++ b/rpc/providers/smoldot.ts @@ -4,15 +4,12 @@ import { ErrorCtor } from "../../util/mod.ts"; import { Client, OnMessage } from "../Base.ts"; import { ClientHooks, ParseRawIngressMessageError } from "../common.ts"; -export type SmoldotClientHooks> = ClientHooks< - Config_, - SmoldotInternalError ->; +export type SmoldotClientHooks = ClientHooks; -export async function smoldotClient>( - config: Config_, - hooks?: SmoldotClientHooks, -): Promise | FailedToStartSmoldotError | FailedToAddChainError> { +export async function smoldotClient( + config: Config, + hooks?: SmoldotClientHooks, +): Promise { const smoldotInstance = await ensureInstance(); if (smoldotInstance instanceof Error) { return smoldotInstance; @@ -32,15 +29,13 @@ export async function smoldotClient>( } } -export class SmoldotClient> - extends Client -{ +export class SmoldotClient extends Client { #chain?: smoldot.Chain; constructor( onMessageContainer: { onMessage?: OnMessage }, readonly remove: () => void, - hooks?: SmoldotClientHooks, + hooks?: SmoldotClientHooks, ) { super( { diff --git a/rpc/providers/std.ts b/rpc/providers/std.ts index 304ae0300..b94ef3c68 100644 --- a/rpc/providers/std.ts +++ b/rpc/providers/std.ts @@ -8,18 +8,18 @@ import { smoldotClient, } from "./smoldot.ts"; -export type StdClient> = - | ProxyClient - | SmoldotClient; +export type StdClient = + | ProxyClient + | SmoldotClient; export type StdClientInitError = | FailedToOpenConnectionError | FailedToStartSmoldotError | FailedToAddChainError; -export async function stdClient>( - config: Config_, -): Promise | StdClientInitError> { +export async function stdClient( + config: Config, +): Promise { const discoveryValue = await config.discoveryValue; if (typeof discoveryValue === "string") { // TODO: improve check / move selection elsewhere diff --git a/rpc/util.ts b/rpc/util.ts deleted file mode 100644 index 6fe4e2aaa..000000000 --- a/rpc/util.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Config } from "../config/mod.ts"; -import * as msg from "./messages.ts"; - -export function IsCorrespondingRes< - Config_ extends Config, - Init_ extends msg.InitMessage, ->(init: Init_) { - return >( - inQuestion: InQuestion, - ): inQuestion is Extract< - InQuestion, - msg.OkMessage | msg.ErrMessage - > => { - return inQuestion?.id === init.id; - }; -} diff --git a/test_util/config.ts b/test_util/config.ts index e871d4deb..920f54a27 100644 --- a/test_util/config.ts +++ b/test_util/config.ts @@ -1,7 +1,6 @@ import { Config } from "../config/mod.ts"; -import { CallMethods, ErrorDetails, SubscriptionMethods } from "../known/rpc.ts"; -class TestConfig extends Config { +class TestConfig extends Config { constructor(readonly runtimeName: TestConfigRuntime.Name) { super( async () => { diff --git a/util/branded.ts b/util/branded.ts index f1e862cf6..f63f8738b 100644 --- a/util/branded.ts +++ b/util/branded.ts @@ -1,29 +1,18 @@ -export type Branded = T & { [_ in Brand]: undefined }; +import { HasherKind } from "../frame_metadata/Metadata.ts"; +export type Branded = T & { [_ in Brand]: V }; -export const HexStringBrand: unique symbol = Symbol(); -export type HexStringBrand = typeof HexStringBrand; -export type HexString = Branded; +declare const _hex: unique symbol; +export type Hex = Branded; -export const HashHexStringBrand: unique symbol = Symbol(); -export type HashHexStringBrand = typeof HashHexStringBrand; -export type HashHexString = Branded; +declare const _encoded: unique symbol; +export type Encoded = Branded; -export const AccountIdStringBrand: unique symbol = Symbol(); -export type AccountIdStringBrand = typeof AccountIdStringBrand; -export type AccountIdString = Branded; +declare const _hash: unique symbol; +export type Hash = Branded< + Uint8Array, + typeof _hash, + [T, K] +>; -export const SubscriptionIdStringBrand: unique symbol = Symbol(); -export type SubscriptionIdStringBrand = typeof SubscriptionIdStringBrand; -export type SubscriptionIdString = Branded; - -export const MultiAddressStringBrand: unique symbol = Symbol(); -export type MultiAddressStringBrand = typeof MultiAddressStringBrand; -export type MultiAddressString = Branded; - -export const HexU64StringBrand: unique symbol = Symbol(); -export type HexU64StringBrand = typeof HexU64StringBrand; -export type HexU64String = Branded; - -export const H256StringBrand: unique symbol = Symbol(); -export type H256StringBrand = typeof H256StringBrand; -export type H256String = Branded; +export type HexEncoded = Hex>; +export type HexHash = Hex>; diff --git a/util/hex.ts b/util/hex.ts index 1ec8780d3..31367a047 100644 --- a/util/hex.ts +++ b/util/hex.ts @@ -1,6 +1,9 @@ +import { Hex } from "./branded.ts"; + export { decode as decodeBuf, encode as encodeBuf } from "../deps/std/encoding/hex.ts"; -export function decode(hex: string) { +export function decode(hex: Hex): T; +export function decode(hex: string): Uint8Array { if (hex.startsWith("0x")) hex = hex.slice(2); if (hex.length % 2 === 1) hex = "0" + hex; const array = new Uint8Array(hex.length / 2); @@ -10,6 +13,7 @@ export function decode(hex: string) { return array; } +export function encode(bytes: T): Hex; export function encode(bytes: Uint8Array): string { let str = ""; for (let i = 0; i < bytes.length; i++) { diff --git a/words.txt b/words.txt index 652116aa8..48b97b4ef 100644 --- a/words.txt +++ b/words.txt @@ -183,3 +183,5 @@ ltex morfologik neovim tostring +framesystem +statemigration