diff --git a/codegen.ts b/codegen.ts index e2cbf017b..d4d044960 100644 --- a/codegen.ts +++ b/codegen.ts @@ -31,7 +31,7 @@ await codegen({ async function getMetadata(src: string): Promise { if (src.startsWith("ws")) { - const client = U.throwIfError(await proxyClient(new Config(() => src, undefined!))); + const client = U.throwIfError(await proxyClient(new Config(() => src))); const metadata = U.throwIfError(await client.call("state_getMetadata", [])); U.throwIfError(await client.close()); if (metadata.error) fail(); diff --git a/config/mod.ts b/config/mod.ts index e929b2b5f..9489e1bec 100644 --- a/config/mod.ts +++ b/config/mod.ts @@ -1,10 +1,7 @@ export class Config { #discoveryValue?: DiscoveryValue | Promise; - constructor( - readonly initDiscoveryValue: () => DiscoveryValue | Promise, - readonly addressPrefix: number, - ) {} + constructor(readonly initDiscoveryValue: () => DiscoveryValue | Promise) {} get discoveryValue() { if (!this.#discoveryValue) { diff --git a/effects/const.ts b/effects/const.ts new file mode 100644 index 000000000..b9507950d --- /dev/null +++ b/effects/const.ts @@ -0,0 +1,32 @@ +import { Config } from "../config/mod.ts"; +import * as Z from "../deps/zones.ts"; +import * as U from "../util/mod.ts"; +import { codec } from "./core/codec.ts"; +import { decoded } from "./core/decoded.ts"; +import { deriveCodec } from "./core/deriveCodec.ts"; +import { constMetadata, metadata, palletMetadata } from "./metadata.ts"; + +function const_< + PalletName extends Z.$, + ConstName extends Z.$, + Rest extends [blockHash?: Z.$], +>( + config: Config, + palletName: PalletName, + constName: ConstName, + ...[blockHash]: [...Rest] +) { + const metadata_ = metadata(config, blockHash); + const deriveCodec_ = deriveCodec(metadata_); + const palletMetadata_ = palletMetadata(metadata_, palletName); + const constMetadata_ = constMetadata(palletMetadata_, constName); + const entryValueTypeI = constMetadata_.access("ty").access("id"); + const constValue = constMetadata_.access("value"); + const $const = codec(deriveCodec_, entryValueTypeI); + return decoded($const, constValue, "value"); +} +Object.defineProperty(const_, "name", { + value: "const", + writable: false, +}); +export { const_ as const }; diff --git a/effects/core/decoded.ts b/effects/core/decoded.ts index 845067278..9cc0afde1 100644 --- a/effects/core/decoded.ts +++ b/effects/core/decoded.ts @@ -1,10 +1,9 @@ import * as $ from "../../deps/scale.ts"; import * as Z from "../../deps/zones.ts"; -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, @@ -14,7 +13,7 @@ export function decoded< return Z.call( Z.ls(codec, encoded, key), function decodedImpl([codec, encoded, key]): Record, any> { - return { [key]: codec.decode(U.hex.decode(encoded)) } as any; + return { [key]: codec.decode(encoded) } as any; }, ); } diff --git a/effects/entryRead.ts b/effects/entryRead.ts index 2ad04d4b3..f311f393c 100644 --- a/effects/entryRead.ts +++ b/effects/entryRead.ts @@ -5,6 +5,7 @@ import { $storageKey } from "./core/$storageKey.ts"; import { codec } from "./core/codec.ts"; import { decoded } from "./core/decoded.ts"; import { deriveCodec } from "./core/deriveCodec.ts"; +import { hexDecode } from "./core/hex.ts"; import { storageKey } from "./core/storageKey.ts"; import { entryMetadata, metadata, palletMetadata } from "./metadata.ts"; import { rpcCall } from "./rpcCall.ts"; @@ -30,6 +31,6 @@ export function entryRead< const storageCall = rpcCall(config, "state_getStorage", [storageKey_, blockHash]); const entryValueTypeI = entryMetadata_.access("value"); const $entry = codec(deriveCodec_, entryValueTypeI); - const resultHex = storageCall.access("result"); - return decoded($entry, resultHex, "value"); + const result = storageCall.access("result"); + return decoded($entry, hexDecode(result), "value"); } diff --git a/effects/extrinsic.ts b/effects/extrinsic.ts index 0ab8ac171..add4a2dfc 100644 --- a/effects/extrinsic.ts +++ b/effects/extrinsic.ts @@ -6,6 +6,7 @@ import { Config } from "../mod.ts"; import { NotifMessage } from "../rpc/mod.ts"; import * as ss58 from "../ss58/mod.ts"; import * as U from "../util/mod.ts"; +import { const as const_ } from "./const.ts"; import { $extrinsic } from "./core/$extrinsic.ts"; import { deriveCodec } from "./core/deriveCodec.ts"; import { hexDecode } from "./core/hex.ts"; @@ -49,21 +50,27 @@ export class SignedExtrinsic< const props = props_ as Z.Rec$Access; const metadata_ = metadata(config); const deriveCodec_ = deriveCodec(metadata_); - const $extrinsic_ = $extrinsic(deriveCodec_, metadata_, this.sign, config.addressPrefix); + const addrPrefix = const_(config, "System", "SS58Prefix") + .access("value") + .as(); + const $extrinsic_ = $extrinsic(deriveCodec_, metadata_, this.sign, addrPrefix); const runtimeVersion = rpcCall(config, "state_getRuntimeVersion", []); - const senderSs58 = Z.call(props.sender, function senderSs58(sender) { - return ((): string => { - switch (sender.type) { - case "Id": { - return ss58.encode(config.addressPrefix, sender.value); + const senderSs58 = Z.call( + Z.ls(addrPrefix, props.sender), + function senderSs58([addrPrefix, sender]) { + return ((): string => { + switch (sender.type) { + case "Id": { + return ss58.encode(addrPrefix, sender.value); + } + // TODO: other types + default: { + unimplemented(); + } } - // TODO: other types - default: { - unimplemented(); - } - } - })(); - }); + })(); + }, + ); const accountNextIndex = rpcCall(config, "system_accountNextIndex", [senderSs58]); const genesisHash = hexDecode(rpcCall(config, "chain_getBlockHash", [0]).access("result")); const checkpointHash = props.checkpoint diff --git a/effects/metadata.ts b/effects/metadata.ts index 4d0e56ef4..9a38cdac9 100644 --- a/effects/metadata.ts +++ b/effects/metadata.ts @@ -45,6 +45,21 @@ export function entryMetadata, EntryName ex ); } +export function constMetadata< + PalletMetadata extends Z.$, + ConstName extends Z.$, +>( + palletMetadata: PalletMetadata, + constName: ConstName, +) { + return Z.call( + Z.ls(palletMetadata, constName), + function constMetadataImpl([palletMetadata, constName]) { + return M.getConst(palletMetadata, constName); + }, + ); +} + export function mapMetadata, EntryName extends Z.$>( palletMetadata: PalletMetadata, entryName: EntryName, diff --git a/effects/mod.ts b/effects/mod.ts index 0246f2049..de40bc9f1 100644 --- a/effects/mod.ts +++ b/effects/mod.ts @@ -1,5 +1,6 @@ export * from "./blockRead.ts"; export * from "./blockWatch.ts"; +export * from "./const.ts"; export * from "./entryRead.ts"; export * from "./entryWatch.ts"; export * from "./extrinsic.ts"; diff --git a/frame_metadata/Metadata.ts b/frame_metadata/Metadata.ts index b97cbe7d1..5a89223aa 100644 --- a/frame_metadata/Metadata.ts +++ b/frame_metadata/Metadata.ts @@ -159,6 +159,11 @@ export function getEntry(pallet: Pallet, name: string): StorageEntry | EntryNotF } export class EntryNotFoundError extends U.ErrorCtor("EntryNotFound") {} +export function getConst(pallet: Pallet, name: string): Constant | ConstNotFoundError { + return pallet.constants?.find((constant) => constant.name === name) || new ConstNotFoundError(); +} +export class ConstNotFoundError extends U.ErrorCtor("ConstNotFound") {} + export function getPalletAndEntry( metadata: Metadata, palletName: string, diff --git a/known/configs.ts b/known/configs.ts index ab5da39e8..d0ee04a1d 100644 --- a/known/configs.ts +++ b/known/configs.ts @@ -2,17 +2,11 @@ import { Config } from "../config/mod.ts"; -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( - () => "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 polkadot = new Config(() => "wss://rpc.polkadot.io"); +export const kusama = new Config(() => "wss://kusama-rpc.polkadot.io"); +export const acala = new Config(() => "wss://acala-polkadot.api.onfinality.io/public-ws"); +export const rococo = new Config(() => "wss://rococo-contracts-rpc.polkadot.io"); +export const moonbeam = new Config(() => "wss://wss.api.moonbeam.network"); +export const statemint = new Config(() => "wss://statemint-rpc.polkadot.io"); +export const subsocial = new Config(() => "wss://para.subsocial.network"); +export const westend = new Config(() => "wss://westend-rpc.polkadot.io"); diff --git a/test_util/config.ts b/test_util/config.ts index 326ec5a77..fdf8f7ae1 100644 --- a/test_util/config.ts +++ b/test_util/config.ts @@ -2,31 +2,23 @@ import { Config } from "../config/mod.ts"; export class TestConfig extends Config { constructor(readonly runtimeName: TestConfigRuntime.Name) { - super( - async () => { - const hostname = Deno.env.get("TEST_CTX_HOSTNAME"); - const portRaw = Deno.env.get("TEST_CTX_PORT"); - if (!hostname || !portRaw) await testCtx(); - const conn = await Deno.connect({ - hostname, - port: parseInt(portRaw!), - }); - conn.write(new Uint8Array([TestConfigRuntime.CODES[runtimeName]])); - const port = await (async () => { - for await (const x of conn.readable) { - return new DataView(x.buffer).getUint16(0); - } - return undefined!; - })(); - return `ws://127.0.0.1:${port}`; - }, - { - kusama: 2, - rococo: undefined!, // TODO - westend: 0, - polkadot: 0, - }[runtimeName], - ); + super(async () => { + const hostname = Deno.env.get("TEST_CTX_HOSTNAME"); + const portRaw = Deno.env.get("TEST_CTX_PORT"); + if (!hostname || !portRaw) await testCtx(); + const conn = await Deno.connect({ + hostname, + port: parseInt(portRaw!), + }); + conn.write(new Uint8Array([TestConfigRuntime.CODES[runtimeName]])); + const port = await (async () => { + for await (const x of conn.readable) { + return new DataView(x.buffer).getUint16(0); + } + return undefined!; + })(); + return `ws://127.0.0.1:${port}`; + }); } }