import type { Codec, StringRecord } from "@pezkuwi/bizinikiwi-bindings" import * as scale from "@pezkuwi/bizinikiwi-bindings" import type { LookupEntry } from "./lookups" import { withCache } from "./with-cache" const _bytes = scale.Bin() const _buildCodec = ( input: LookupEntry, cache: Map>, stack: Set, _accountId: Codec, ): Codec => { if (input.type === "primitive") return scale[input.value] if (input.type === "void") return scale._void if (input.type === "AccountId32") return _accountId if (input.type === "AccountId20") return scale.ethAccount if (input.type === "compact") return input.isBig ? scale.compactBn : scale.compactNumber if (input.type === "bitSequence") return scale.BitSeq(input.isLSB) const buildNextCodec = (nextInput: LookupEntry): Codec => buildCodec(nextInput, cache, stack, _accountId) const buildVector = (inner: LookupEntry, len?: number) => { const innerCodec = buildNextCodec(inner) return len ? scale.Vector(innerCodec, len) : scale.Vector(innerCodec) } const buildTuple = (value: LookupEntry[]) => scale.Tuple(...value.map(buildNextCodec)) const buildStruct = (value: StringRecord) => { const inner = Object.fromEntries( Object.entries(value).map(([key, value]) => [key, buildNextCodec(value)]), ) as StringRecord> return scale.Struct(inner) } if ( input.type === "sequence" && input.value.type === "primitive" && input.value.value === "u8" ) { return _bytes } if (input.type === "array") { // Bytes case if (input.value.type === "primitive" && input.value.value === "u8") return scale.Bin(input.len) return buildVector(input.value, input.len) } if (input.type === "sequence") return buildVector(input.value) if (input.type === "tuple") return buildTuple(input.value) if (input.type === "struct") return buildStruct(input.value) if (input.type === "option") return scale.Option(buildNextCodec(input.value)) if (input.type === "result") return scale.Result( buildNextCodec(input.value.ok), buildNextCodec(input.value.ko), ) // it has to be an enum by now const dependencies = Object.values(input.value).map((v) => { switch (v.type) { case "void": return scale._void case "lookupEntry": return buildNextCodec(v.value) case "tuple": return buildTuple(v.value) case "struct": return buildStruct(v.value) case "array": return buildVector(v.value, v.len) } }) const inner = Object.fromEntries( Object.keys(input.value).map((key, idx) => { return [key, dependencies[idx]] }), ) as StringRecord> const indexes = Object.values(input.value).map((x) => x.idx) const areIndexesSorted = indexes.every((idx, i) => idx === i) const variantCodec = areIndexesSorted ? scale.Variant(inner) : scale.Variant(inner, indexes as any) return input.byteLength ? fixedSizeCodec(variantCodec, input.byteLength) : variantCodec } const buildCodec = withCache(_buildCodec, scale.Self, (res) => res) export const getLookupCodecBuilder = ( lookup: (id: number) => LookupEntry, accountId = scale.AccountId(), ) => { const cache = new Map() const buildDefinition = (id: number) => buildCodec(lookup(id), cache, new Set(), accountId) return (id: number) => buildDefinition(id) } const fixedSizeCodec = (codec: Codec, size: number): Codec => { const allBytes = scale.Bytes(size) return scale.createCodec( (value: T) => allBytes.enc(codec.enc(value)), (data) => codec.dec(allBytes.dec(data)), ) }