mirror of
https://github.com/pezkuwichain/pezkuwi-api.git
synced 2026-07-21 06:15:42 +00:00
feat: add PAPI rebrand packages
- @pezkuwi/papi-utils (rebrand of @polkadot-api/utils) - @pezkuwi/bizinikiwi-bindings (rebrand of @polkadot-api/substrate-bindings) - @pezkuwi/metadata-builders (rebrand of @polkadot-api/metadata-builders) - @pezkuwi/merkleize-metadata (rebrand of @polkadot-api/merkleize-metadata) All @polkadot-api references replaced with @pezkuwi equivalents.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Josep M Sobrepere
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,121 @@
|
||||
# @polkadot-api/metadata-builders
|
||||
|
||||
This package has multiple functions that read a metadata object, denormalizes it, and builds other structures needed for different processes
|
||||
|
||||
## getLookupFn
|
||||
|
||||
```ts
|
||||
interface MetadataLookup {
|
||||
(id: number): LookupEntry
|
||||
metadata: V14 | V15
|
||||
}
|
||||
|
||||
function getLookupFn(metadata: Metadata): MetadataLookup
|
||||
```
|
||||
|
||||
Given the a metadata, returns a function that will give the `LookupEntry` for an id along. The function also has access to the original metadata, as it's usually needed to work with the actual lookup.
|
||||
|
||||
The `LookupEntry` is a denormalized data structure for one entry in the metadata. It also "shortcuts" type references when those are pointers (composites or tuples of length 1). Essentially, it's a union of each of the different types that can be found in the lookup, mostly equivalent to something like:
|
||||
|
||||
```ts
|
||||
type TerminalVar =
|
||||
| PrimitiveVar // u8, str, char, i128, etc.
|
||||
| CompactVar
|
||||
| BitSequenceVar
|
||||
| AccountId32
|
||||
|
||||
type ComposedVar =
|
||||
| TupleVar
|
||||
| StructVar
|
||||
| SequenceVar
|
||||
| ArrayVar
|
||||
| OptionVar
|
||||
| ResultVar
|
||||
| EnumVar
|
||||
|
||||
type LookupEntry = TerminalVar | ComposedVar
|
||||
```
|
||||
|
||||
Where, for instance, a StructVar is of the shape
|
||||
|
||||
```ts
|
||||
type StructVar = {
|
||||
type: "struct"
|
||||
value: Record<string, LookupEntry>
|
||||
}
|
||||
```
|
||||
|
||||
It's useful to get types referenced by storage calls, etc.
|
||||
|
||||
## getDynamicBuilder
|
||||
|
||||
```ts
|
||||
function getDynamicBuilder(metadataLookup: MetadataLookup): {
|
||||
buildDefinition: (id: number) => Codec
|
||||
buildConstant: (pallet: string, name: string) => Codec
|
||||
buildEvent: (pallet: string, name: string) => VariantEntry
|
||||
buildError: (pallet: string, name: string) => VariantEntry
|
||||
buildCall: (pallet: string, name: string) => VariantEntry
|
||||
buildStorage: (pallet: string, entry: string) => StorageEntry
|
||||
buildRuntimeCall: (api: string, method: string) => RuntimeEntry
|
||||
}
|
||||
```
|
||||
|
||||
Generates all the codecs needed to SCALE encode or decode the data for any interaction with the chain.
|
||||
|
||||
`buildDefinition` returns the codec for the type identified by the parameter `id`
|
||||
|
||||
`buildConstant` returns the codec for the requested constant (equivalent as calling `buildDefinition` with the type id of that constant)
|
||||
|
||||
`buildEvent`, `buildError` and `buildCall` return an object with the codec, and the indices of the pallet and entry within the metadata:
|
||||
|
||||
```ts
|
||||
interface VariantEntry {
|
||||
location: [number, number] // [palletIdx, entryIdx],
|
||||
codec: Codec
|
||||
}
|
||||
```
|
||||
|
||||
`buildStorage` creates all the encoders/decoders needed to encode a storage call and decode its result:
|
||||
|
||||
```ts
|
||||
interface StorageEntry {
|
||||
// Encodes the arguments of the storage call.
|
||||
enc: (...args: any[]) => string
|
||||
// Decodes the result from the storage call.
|
||||
dec: (value: string) => any
|
||||
// Decodes the arguments of the storage call
|
||||
keyDecoder: (value: string) => any[]
|
||||
// Expected number of arguments
|
||||
len: number
|
||||
// Decoded fallback value as defined in the metadata entry
|
||||
fallback: unknown
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, `buildRuntimeCall` returns the codecs for both encoding the arguments of the runtime call, and the codec for decoding the result
|
||||
|
||||
```ts
|
||||
interface RuntimeEntry {
|
||||
args: Codec<any[]>
|
||||
value: Codec<any>
|
||||
}
|
||||
```
|
||||
|
||||
## getChecksumBuilder
|
||||
|
||||
```ts
|
||||
function getChecksumBuilder(metadataLookup: MetadataLookup): {
|
||||
buildDefinition: (id: number) => string | null
|
||||
buildRuntimeCall: (api: string, method: string) => string | null
|
||||
buildStorage: (pallet: string, entry: string) => string | null
|
||||
buildCall: (pallet: string, name: string) => string | null
|
||||
buildEvent: (pallet: string, name: string) => string | null
|
||||
buildError: (pallet: string, name: string) => string | null
|
||||
buildConstant: (pallet: string, constantName: string) => string | null
|
||||
}
|
||||
```
|
||||
|
||||
Generates the checksums for the different components defined in the metadata.
|
||||
|
||||
`buildDefinition` builds the checksum of one of the types in the lookup. The rest of the methods build the checksum for each of the interfaces of the chain.
|
||||
@@ -0,0 +1,370 @@
|
||||
import { h64 } from '@pezkuwi/bizinikiwi-bindings';
|
||||
import { buildLookupGraph, getSubgraph, getStronglyConnectedComponents, mergeSCCsWithCommonNodes } from './lookup-graph.mjs';
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
const encodeText = textEncoder.encode.bind(textEncoder);
|
||||
const getChecksum = (values) => {
|
||||
const res = new Uint8Array(values.length * 8);
|
||||
const dv = new DataView(res.buffer);
|
||||
for (let i = 0; i < values.length; i++) dv.setBigUint64(i * 8, values[i]);
|
||||
return h64(res);
|
||||
};
|
||||
const getStringChecksum = (values) => getChecksum(values.map((v) => h64(encodeText(v))));
|
||||
const shapeIds = {
|
||||
primitive: 0n,
|
||||
vector: 1n,
|
||||
tuple: 2n,
|
||||
struct: 3n,
|
||||
option: 4n,
|
||||
result: 5n,
|
||||
enum: 6n,
|
||||
void: 7n
|
||||
};
|
||||
const runtimePrimitiveIds = {
|
||||
undefined: 0n,
|
||||
number: 1n,
|
||||
string: 2n,
|
||||
bigint: 3n,
|
||||
boolean: 4n,
|
||||
bitSequence: 5n,
|
||||
// {bitsLen: number, bytes: Uint8Array}
|
||||
byteSequence: 6n,
|
||||
// Binary
|
||||
accountId32: 7n,
|
||||
// SS58String
|
||||
accountId20: 8n
|
||||
// EthAccount
|
||||
};
|
||||
const metadataPrimitiveIds = {
|
||||
bool: runtimePrimitiveIds.boolean,
|
||||
char: runtimePrimitiveIds.string,
|
||||
str: runtimePrimitiveIds.string,
|
||||
u8: runtimePrimitiveIds.number,
|
||||
u16: runtimePrimitiveIds.number,
|
||||
u32: runtimePrimitiveIds.number,
|
||||
u64: runtimePrimitiveIds.bigint,
|
||||
u128: runtimePrimitiveIds.bigint,
|
||||
u256: runtimePrimitiveIds.bigint,
|
||||
i8: runtimePrimitiveIds.number,
|
||||
i16: runtimePrimitiveIds.number,
|
||||
i32: runtimePrimitiveIds.number,
|
||||
i64: runtimePrimitiveIds.bigint,
|
||||
i128: runtimePrimitiveIds.bigint,
|
||||
i256: runtimePrimitiveIds.bigint
|
||||
};
|
||||
const structLikeBuilder = (shapeId, input, innerChecksum) => {
|
||||
const sortedEntries = Object.entries(input).sort(
|
||||
([a], [b]) => a.localeCompare(b)
|
||||
);
|
||||
const keysChecksum = getStringChecksum(sortedEntries.map(([key]) => key));
|
||||
const valuesChecksum = getChecksum(
|
||||
sortedEntries.map(([, entry]) => innerChecksum(entry))
|
||||
);
|
||||
return getChecksum([shapeId, keysChecksum, valuesChecksum]);
|
||||
};
|
||||
const _buildChecksum = (input, buildNextChecksum) => {
|
||||
if (input.type === "primitive")
|
||||
return getChecksum([shapeIds.primitive, metadataPrimitiveIds[input.value]]);
|
||||
if (input.type === "void") return getChecksum([shapeIds.void]);
|
||||
if (input.type === "compact")
|
||||
return getChecksum([
|
||||
shapeIds.primitive,
|
||||
runtimePrimitiveIds[input.isBig ? "bigint" : "number"]
|
||||
]);
|
||||
if (input.type === "bitSequence")
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.bitSequence]);
|
||||
if (input.type === "AccountId32") {
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.accountId32]);
|
||||
}
|
||||
if (input.type === "AccountId20") {
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.accountId20]);
|
||||
}
|
||||
const buildVector = (entry, length) => {
|
||||
const innerChecksum = buildNextChecksum(entry);
|
||||
return getChecksum(
|
||||
length !== void 0 ? [shapeIds.vector, innerChecksum, BigInt(length)] : [shapeIds.vector, innerChecksum]
|
||||
);
|
||||
};
|
||||
if (input.type === "array") {
|
||||
const innerValue = input.value;
|
||||
if (innerValue.type === "primitive" && innerValue.value === "u8") {
|
||||
return getChecksum([
|
||||
shapeIds.primitive,
|
||||
runtimePrimitiveIds.byteSequence,
|
||||
BigInt(input.len)
|
||||
]);
|
||||
}
|
||||
return buildVector(innerValue, input.len);
|
||||
}
|
||||
if (input.type === "sequence") {
|
||||
const innerValue = input.value;
|
||||
if (innerValue.type === "primitive" && innerValue.value === "u8") {
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.byteSequence]);
|
||||
}
|
||||
return buildVector(innerValue);
|
||||
}
|
||||
const buildTuple = (entries) => getChecksum([shapeIds.tuple, ...entries.map(buildNextChecksum)]);
|
||||
const buildStruct = (entries) => structLikeBuilder(shapeIds.struct, entries, buildNextChecksum);
|
||||
if (input.type === "tuple") return buildTuple(input.value);
|
||||
if (input.type === "struct") return buildStruct(input.value);
|
||||
if (input.type === "option")
|
||||
return getChecksum([shapeIds.option, buildNextChecksum(input.value)]);
|
||||
if (input.type === "result")
|
||||
return getChecksum([
|
||||
shapeIds.result,
|
||||
buildNextChecksum(input.value.ok),
|
||||
buildNextChecksum(input.value.ko)
|
||||
]);
|
||||
return structLikeBuilder(shapeIds.enum, input.value, (entry) => {
|
||||
if (entry.type === "lookupEntry") return buildNextChecksum(entry.value);
|
||||
switch (entry.type) {
|
||||
case "void":
|
||||
return getChecksum([shapeIds.void]);
|
||||
case "tuple":
|
||||
return buildTuple(entry.value);
|
||||
case "struct":
|
||||
return buildStruct(entry.value);
|
||||
case "array":
|
||||
return buildVector(entry.value, entry.len);
|
||||
}
|
||||
});
|
||||
};
|
||||
const sortCyclicGroups = (groups, graph) => {
|
||||
const getReachableNodes = (group) => {
|
||||
const result2 = /* @__PURE__ */ new Set();
|
||||
const toVisit = Array.from(group);
|
||||
while (toVisit.length) {
|
||||
const id = toVisit.pop();
|
||||
if (result2.has(id)) continue;
|
||||
result2.add(id);
|
||||
graph.get(id)?.refs.forEach((id2) => toVisit.push(id2));
|
||||
}
|
||||
return Array.from(result2);
|
||||
};
|
||||
const result = new Array();
|
||||
function dependentsFirst(group) {
|
||||
if (result.includes(group)) return;
|
||||
const dependents = groups.filter(
|
||||
(candidate) => candidate !== group && getReachableNodes(group).some((node) => candidate.has(node))
|
||||
);
|
||||
dependents.forEach((group2) => dependentsFirst(group2));
|
||||
if (result.includes(group)) return;
|
||||
result.push(group);
|
||||
}
|
||||
groups.forEach((group) => dependentsFirst(group));
|
||||
return result;
|
||||
};
|
||||
function iterateChecksums(group, iterations, cache, graph) {
|
||||
const groupReadCache = new Map([...group].map((id) => [id, 0n]));
|
||||
const groupWriteCache = /* @__PURE__ */ new Map();
|
||||
const recursiveBuildChecksum = (entry, skipCache = true) => {
|
||||
if (!skipCache && (groupReadCache.has(entry.id) || cache.has(entry.id))) {
|
||||
return groupReadCache.get(entry.id) ?? cache.get(entry.id);
|
||||
}
|
||||
const result = _buildChecksum(
|
||||
entry,
|
||||
(nextEntry) => recursiveBuildChecksum(nextEntry, false)
|
||||
);
|
||||
if (group.has(entry.id)) {
|
||||
groupWriteCache.set(entry.id, result);
|
||||
} else {
|
||||
cache.set(entry.id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
group.forEach((id) => recursiveBuildChecksum(graph.get(id).entry));
|
||||
group.forEach((id) => groupReadCache.set(id, groupWriteCache.get(id)));
|
||||
}
|
||||
return groupReadCache;
|
||||
}
|
||||
function getMirroredNodes(cyclicGroups, graph) {
|
||||
const maxSize = cyclicGroups.reduce(
|
||||
(acc, group) => Math.max(acc, group.size),
|
||||
0
|
||||
);
|
||||
const allEntries = new Set([...graph.values()].map((v) => v.entry.id));
|
||||
const resultingChecksums = iterateChecksums(
|
||||
allEntries,
|
||||
maxSize,
|
||||
// Cache won't be used, since it's using the internal one for every node.
|
||||
/* @__PURE__ */ new Map(),
|
||||
graph
|
||||
);
|
||||
const checksumToNodes = /* @__PURE__ */ new Map();
|
||||
for (const id of allEntries) {
|
||||
const checksum = resultingChecksums.get(id);
|
||||
if (checksum == void 0) throw new Error("Unreachable");
|
||||
if (!checksumToNodes.has(checksum)) {
|
||||
checksumToNodes.set(checksum, []);
|
||||
}
|
||||
checksumToNodes.get(checksum).push(id);
|
||||
}
|
||||
const checksumsWithDuplicates = [...checksumToNodes.entries()].filter(
|
||||
([, nodes]) => nodes.length > 1
|
||||
);
|
||||
const duplicatesMap = {};
|
||||
checksumsWithDuplicates.forEach(([, nodes]) => {
|
||||
nodes.forEach((n) => duplicatesMap[n] = nodes);
|
||||
});
|
||||
return duplicatesMap;
|
||||
}
|
||||
const buildChecksum = (entry, cache, graph) => {
|
||||
if (cache.has(entry.id)) return cache.get(entry.id);
|
||||
const subGraph = getSubgraph(entry.id, graph);
|
||||
const cycles = getStronglyConnectedComponents(subGraph).filter(
|
||||
// SCCs can be of length=1, but for those we're only interested with those that are circular with themselves
|
||||
(group) => group.size > 1 || isSelfCircular(group, subGraph)
|
||||
);
|
||||
const cyclicGroups = mergeSCCsWithCommonNodes(cycles).filter((group) => {
|
||||
return !cache.has(group.values().next().value);
|
||||
});
|
||||
const mirrored = getMirroredNodes(cyclicGroups, subGraph);
|
||||
const sortedCyclicGroups = sortCyclicGroups(cyclicGroups, subGraph);
|
||||
sortedCyclicGroups.forEach((group) => {
|
||||
if (cache.has(group.values().next().value)) {
|
||||
return;
|
||||
}
|
||||
const result = iterateChecksums(group, group.size, cache, graph);
|
||||
group.forEach((id) => {
|
||||
const checksum = result.get(id);
|
||||
if (id in mirrored) {
|
||||
mirrored[id].forEach((id2) => cache.set(id2, checksum));
|
||||
} else {
|
||||
cache.set(id, checksum);
|
||||
}
|
||||
});
|
||||
});
|
||||
const getChecksum2 = (entry2) => {
|
||||
if (cache.has(entry2.id)) return cache.get(entry2.id);
|
||||
return _buildChecksum(entry2, getChecksum2);
|
||||
};
|
||||
return getChecksum2(entry);
|
||||
};
|
||||
const isSelfCircular = (group, graph) => {
|
||||
if (group.size !== 1) return false;
|
||||
const [id] = group;
|
||||
return graph.get(id).refs.has(id);
|
||||
};
|
||||
const getChecksumBuilder = (getLookupEntryDef) => {
|
||||
const { metadata } = getLookupEntryDef;
|
||||
const graph = buildLookupGraph(getLookupEntryDef, metadata.lookup.length);
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const buildDefinition = (id) => buildChecksum(getLookupEntryDef(id), cache, graph);
|
||||
const buildStorage = (pallet, entry) => {
|
||||
try {
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).storage.items.find((s) => s.name === entry);
|
||||
if (storageEntry.type.tag === "plain")
|
||||
return buildDefinition(storageEntry.type.value);
|
||||
const { key, value } = storageEntry.type.value;
|
||||
const val = buildDefinition(value);
|
||||
const returnKey = buildDefinition(key);
|
||||
return getChecksum([val, returnKey]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildViewFns = (pallet, entry) => {
|
||||
try {
|
||||
const viewFn = metadata.pallets.find((x) => x.name === pallet)?.viewFns.find((x) => x.name === entry);
|
||||
if (!viewFn) throw null;
|
||||
const argNamesChecksum = getStringChecksum(
|
||||
viewFn.inputs.map((x) => x.name)
|
||||
);
|
||||
const argValuesChecksum = getChecksum(
|
||||
viewFn.inputs.map((x) => buildDefinition(x.type))
|
||||
);
|
||||
const outputChecksum = buildDefinition(viewFn.output);
|
||||
return getChecksum([argNamesChecksum, argValuesChecksum, outputChecksum]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildRuntimeCall = (api, method) => {
|
||||
try {
|
||||
const entry = metadata.apis.find((x) => x.name === api)?.methods.find((x) => x.name === method);
|
||||
if (!entry) throw null;
|
||||
const argNamesChecksum = getStringChecksum(
|
||||
entry.inputs.map((x) => x.name)
|
||||
);
|
||||
const argValuesChecksum = getChecksum(
|
||||
entry.inputs.map((x) => buildDefinition(x.type))
|
||||
);
|
||||
const outputChecksum = buildDefinition(entry.output);
|
||||
return getChecksum([argNamesChecksum, argValuesChecksum, outputChecksum]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildComposite = (input) => {
|
||||
if (input.type === "void") return getChecksum([0n]);
|
||||
if (input.type === "tuple") {
|
||||
const values = Object.values(input.value).map(
|
||||
(entry) => buildDefinition(entry.id)
|
||||
);
|
||||
return getChecksum([shapeIds.tuple, ...values]);
|
||||
}
|
||||
if (input.type === "array") {
|
||||
return getChecksum([
|
||||
shapeIds.vector,
|
||||
buildDefinition(input.value.id),
|
||||
BigInt(input.len)
|
||||
]);
|
||||
}
|
||||
return structLikeBuilder(
|
||||
shapeIds.struct,
|
||||
input.value,
|
||||
(entry) => buildDefinition(entry.id)
|
||||
);
|
||||
};
|
||||
const buildNamedTuple = (input) => {
|
||||
return structLikeBuilder(
|
||||
shapeIds.tuple,
|
||||
input.value,
|
||||
(entry) => buildDefinition(entry.id)
|
||||
);
|
||||
};
|
||||
const variantShapeId = {
|
||||
errors: 1n,
|
||||
events: 2n,
|
||||
calls: 3n
|
||||
};
|
||||
const buildVariant = (variantType) => (pallet, name) => {
|
||||
try {
|
||||
const palletEntry = metadata.pallets.find((x) => x.name === pallet);
|
||||
const enumLookup = getLookupEntryDef(palletEntry[variantType].type);
|
||||
buildDefinition(enumLookup.id);
|
||||
if (enumLookup.type !== "enum") throw null;
|
||||
const entry = enumLookup.value[name];
|
||||
const valueChecksum = entry.type === "lookupEntry" ? buildDefinition(entry.value.id) : buildComposite(entry);
|
||||
return getChecksum([variantShapeId[variantType], valueChecksum]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildConstant = (pallet, constantName) => {
|
||||
try {
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).constants.find((s) => s.name === constantName);
|
||||
return buildDefinition(storageEntry.type);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const toStringEnhancer = (fn) => (...args) => fn(...args)?.toString(32) ?? null;
|
||||
return {
|
||||
buildDefinition: toStringEnhancer(buildDefinition),
|
||||
buildRuntimeCall: toStringEnhancer(buildRuntimeCall),
|
||||
buildStorage: toStringEnhancer(buildStorage),
|
||||
buildViewFns: toStringEnhancer(buildViewFns),
|
||||
buildCall: toStringEnhancer(buildVariant("calls")),
|
||||
buildEvent: toStringEnhancer(buildVariant("events")),
|
||||
buildError: toStringEnhancer(buildVariant("errors")),
|
||||
buildConstant: toStringEnhancer(buildConstant),
|
||||
buildComposite: toStringEnhancer(buildComposite),
|
||||
buildNamedTuple: toStringEnhancer(buildNamedTuple),
|
||||
getAllGeneratedChecksums: () => Array.from(cache.values()).map((v) => v.toString(32))
|
||||
};
|
||||
};
|
||||
|
||||
export { getChecksumBuilder };
|
||||
//# sourceMappingURL=checksum-builder.mjs.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,136 @@
|
||||
import * as scale from '@pezkuwi/bizinikiwi-bindings';
|
||||
import { mapObject } from '@pezkuwi/papi-utils';
|
||||
import { getLookupCodecBuilder } from './lookup-codec-builder.mjs';
|
||||
|
||||
const nullCodec = scale.enhanceCodec(
|
||||
scale._void,
|
||||
() => void 0,
|
||||
() => null
|
||||
);
|
||||
const getDynamicBuilder = (getLookupEntryDef) => {
|
||||
const { metadata } = getLookupEntryDef;
|
||||
let buildDefinition = getLookupCodecBuilder(getLookupEntryDef);
|
||||
const prefix = metadata.pallets.find((x) => x.name === "System")?.constants.find((x) => x.name === "SS58Prefix");
|
||||
let ss58Prefix;
|
||||
if (prefix) {
|
||||
try {
|
||||
const prefixVal = buildDefinition(prefix.type).dec(prefix.value);
|
||||
if (typeof prefixVal === "number") {
|
||||
ss58Prefix = prefixVal;
|
||||
buildDefinition = getLookupCodecBuilder(
|
||||
getLookupEntryDef,
|
||||
scale.AccountId(prefixVal)
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
const storagePallets = /* @__PURE__ */ new Map();
|
||||
const buildStorage = (pallet, entry) => {
|
||||
let storagePallet = storagePallets.get(pallet);
|
||||
if (!storagePallet)
|
||||
storagePallets.set(pallet, storagePallet = scale.Storage(pallet));
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).storage.items.find((s) => s.name === entry);
|
||||
const withNullVoid = (codec) => codec === scale._void ? nullCodec : codec;
|
||||
const storageWithFallback = (len, value2, ...args) => {
|
||||
const keys = storagePallet(...args);
|
||||
const [, ...encodersWithHash] = args;
|
||||
return {
|
||||
args: scale.Tuple(...encodersWithHash.map(([codec]) => codec)),
|
||||
keys,
|
||||
value: value2,
|
||||
len,
|
||||
fallback: storageEntry.modifier === 1 ? value2.dec(storageEntry.fallback) : void 0
|
||||
};
|
||||
};
|
||||
if (storageEntry.type.tag === "plain")
|
||||
return storageWithFallback(
|
||||
0,
|
||||
withNullVoid(buildDefinition(storageEntry.type.value)),
|
||||
entry
|
||||
);
|
||||
const { key, value, hashers } = storageEntry.type.value;
|
||||
const val = withNullVoid(buildDefinition(value));
|
||||
const hashes = hashers.map((x) => scale[x.tag]);
|
||||
const hashArgs = (() => {
|
||||
if (hashes.length === 1) {
|
||||
return [[buildDefinition(key), hashes[0]]];
|
||||
}
|
||||
const keyDef = getLookupEntryDef(key);
|
||||
switch (keyDef.type) {
|
||||
case "array":
|
||||
return hashes.map((hash) => [buildDefinition(keyDef.value.id), hash]);
|
||||
case "tuple":
|
||||
return keyDef.value.map((x, idx) => [
|
||||
buildDefinition(x.id),
|
||||
hashes[idx]
|
||||
]);
|
||||
default:
|
||||
throw new Error("Invalid key type");
|
||||
}
|
||||
})();
|
||||
return storageWithFallback(hashes.length, val, entry, ...hashArgs);
|
||||
};
|
||||
const buildEnumEntry = (entry) => {
|
||||
switch (entry.type) {
|
||||
case "void":
|
||||
return scale._void;
|
||||
case "lookupEntry":
|
||||
return buildDefinition(entry.value.id);
|
||||
case "tuple":
|
||||
return scale.Tuple(
|
||||
...Object.values(entry.value).map((l) => buildDefinition(l.id))
|
||||
);
|
||||
case "struct":
|
||||
return scale.Struct(
|
||||
mapObject(entry.value, (x) => buildDefinition(x.id))
|
||||
);
|
||||
case "array":
|
||||
return scale.Vector(buildDefinition(entry.value.id), entry.len);
|
||||
}
|
||||
};
|
||||
const buildConstant = (pallet, constantName) => {
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).constants.find((s) => s.name === constantName);
|
||||
return buildDefinition(storageEntry.type);
|
||||
};
|
||||
const buildVariant = (type) => (pallet, name) => {
|
||||
const palletEntry = metadata.pallets.find((x) => x.name === pallet);
|
||||
const lookup = getLookupEntryDef(palletEntry[type].type);
|
||||
if (lookup.type !== "enum") throw null;
|
||||
const entry = lookup.value[name];
|
||||
return {
|
||||
location: [palletEntry.index, entry.idx],
|
||||
codec: buildEnumEntry(lookup.value[name])
|
||||
};
|
||||
};
|
||||
const buildViewFn = (pallet, entry) => {
|
||||
const fn = metadata.pallets.find((x) => x.name === pallet)?.viewFns.find((x) => x.name === entry);
|
||||
if (!fn) throw null;
|
||||
return {
|
||||
args: scale.Tuple(...fn.inputs.map((x) => buildDefinition(x.type))),
|
||||
value: buildDefinition(fn.output)
|
||||
};
|
||||
};
|
||||
const buildRuntimeCall = (api, method) => {
|
||||
const entry = metadata.apis.find((x) => x.name === api)?.methods.find((x) => x.name === method);
|
||||
if (!entry) throw null;
|
||||
return {
|
||||
args: scale.Tuple(...entry.inputs.map((x) => buildDefinition(x.type))),
|
||||
value: buildDefinition(entry.output)
|
||||
};
|
||||
};
|
||||
return {
|
||||
buildDefinition,
|
||||
buildStorage,
|
||||
buildEvent: buildVariant("events"),
|
||||
buildError: buildVariant("errors"),
|
||||
buildViewFn,
|
||||
buildRuntimeCall,
|
||||
buildCall: buildVariant("calls"),
|
||||
buildConstant,
|
||||
ss58Prefix
|
||||
};
|
||||
};
|
||||
|
||||
export { getDynamicBuilder };
|
||||
//# sourceMappingURL=dynamic-builder.mjs.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
export { denormalizeLookup, getLookupFn } from './lookups.mjs';
|
||||
export { getDynamicBuilder } from './dynamic-builder.mjs';
|
||||
export { getChecksumBuilder } from './checksum-builder.mjs';
|
||||
export { getLookupCodecBuilder } from './lookup-codec-builder.mjs';
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
||||
@@ -0,0 +1,81 @@
|
||||
import * as scale from '@pezkuwi/bizinikiwi-bindings';
|
||||
import { withCache } from './with-cache.mjs';
|
||||
|
||||
const _bytes = scale.Bin();
|
||||
const _buildCodec = (input, cache, stack, _accountId) => {
|
||||
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) => buildCodec(nextInput, cache, stack, _accountId);
|
||||
const buildVector = (inner2, len) => {
|
||||
const innerCodec = buildNextCodec(inner2);
|
||||
return len ? scale.Vector(innerCodec, len) : scale.Vector(innerCodec);
|
||||
};
|
||||
const buildTuple = (value) => scale.Tuple(...value.map(buildNextCodec));
|
||||
const buildStruct = (value) => {
|
||||
const inner2 = Object.fromEntries(
|
||||
Object.entries(value).map(([key, value2]) => [key, buildNextCodec(value2)])
|
||||
);
|
||||
return scale.Struct(inner2);
|
||||
};
|
||||
if (input.type === "sequence" && input.value.type === "primitive" && input.value.value === "u8") {
|
||||
return _bytes;
|
||||
}
|
||||
if (input.type === "array") {
|
||||
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)
|
||||
);
|
||||
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]];
|
||||
})
|
||||
);
|
||||
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);
|
||||
return input.byteLength ? fixedSizeCodec(variantCodec, input.byteLength) : variantCodec;
|
||||
};
|
||||
const buildCodec = withCache(_buildCodec, scale.Self, (res) => res);
|
||||
const getLookupCodecBuilder = (lookup, accountId = scale.AccountId()) => {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const buildDefinition = (id) => buildCodec(lookup(id), cache, /* @__PURE__ */ new Set(), accountId);
|
||||
return (id) => buildDefinition(id);
|
||||
};
|
||||
const fixedSizeCodec = (codec, size) => {
|
||||
const allBytes = scale.Bytes(size);
|
||||
return scale.createCodec(
|
||||
(value) => allBytes.enc(codec.enc(value)),
|
||||
(data) => codec.dec(allBytes.dec(data))
|
||||
);
|
||||
};
|
||||
|
||||
export { getLookupCodecBuilder };
|
||||
//# sourceMappingURL=lookup-codec-builder.mjs.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,161 @@
|
||||
function buildLookupGraph(lookupFn, lookupLength) {
|
||||
const result = /* @__PURE__ */ new Map();
|
||||
const visited = /* @__PURE__ */ new Set();
|
||||
const addEdge = (from, to) => {
|
||||
if (!result.has(from))
|
||||
result.set(from, {
|
||||
entry: lookupFn(from),
|
||||
backRefs: /* @__PURE__ */ new Set(),
|
||||
refs: /* @__PURE__ */ new Set()
|
||||
});
|
||||
if (!result.has(to))
|
||||
result.set(to, {
|
||||
entry: lookupFn(to),
|
||||
backRefs: /* @__PURE__ */ new Set(),
|
||||
refs: /* @__PURE__ */ new Set()
|
||||
});
|
||||
result.get(from).refs.add(to);
|
||||
result.get(to).backRefs.add(from);
|
||||
};
|
||||
for (let i = 0; i < lookupLength; i++) {
|
||||
const entry = lookupFn(i);
|
||||
if (i !== entry.id) {
|
||||
addEdge(i, entry.id);
|
||||
}
|
||||
if (visited.has(entry.id)) continue;
|
||||
visited.add(entry.id);
|
||||
switch (entry.type) {
|
||||
case "array":
|
||||
case "option":
|
||||
case "sequence":
|
||||
addEdge(entry.id, entry.value.id);
|
||||
break;
|
||||
case "enum":
|
||||
Object.values(entry.value).forEach((enumEntry) => {
|
||||
switch (enumEntry.type) {
|
||||
case "array":
|
||||
case "lookupEntry":
|
||||
addEdge(entry.id, enumEntry.value.id);
|
||||
break;
|
||||
case "struct":
|
||||
case "tuple":
|
||||
Object.values(enumEntry.value).forEach(
|
||||
(v) => addEdge(entry.id, v.id)
|
||||
);
|
||||
break;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "result":
|
||||
addEdge(entry.id, entry.value.ok.id);
|
||||
addEdge(entry.id, entry.value.ko.id);
|
||||
break;
|
||||
case "struct":
|
||||
case "tuple":
|
||||
Object.values(entry.value).forEach((v) => addEdge(entry.id, v.id));
|
||||
break;
|
||||
}
|
||||
if (!result.has(entry.id)) {
|
||||
result.set(entry.id, {
|
||||
backRefs: /* @__PURE__ */ new Set(),
|
||||
refs: /* @__PURE__ */ new Set(),
|
||||
entry
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const subgraphCache = /* @__PURE__ */ new WeakMap();
|
||||
function _getSubgraph(id, graph, result, cache) {
|
||||
if (result.has(id)) return;
|
||||
const node = graph.get(id);
|
||||
result.set(id, node);
|
||||
cache.set(id, result);
|
||||
node.refs.forEach((ref) => _getSubgraph(ref, graph, result, cache));
|
||||
node.backRefs.forEach((ref) => _getSubgraph(ref, graph, result, cache));
|
||||
}
|
||||
function getSubgraph(id, graph) {
|
||||
if (!subgraphCache.has(graph)) {
|
||||
subgraphCache.set(graph, /* @__PURE__ */ new Map());
|
||||
}
|
||||
const cache = subgraphCache.get(graph);
|
||||
if (cache.has(id)) return cache.get(id);
|
||||
const result = /* @__PURE__ */ new Map();
|
||||
_getSubgraph(id, graph, result, cache);
|
||||
return result;
|
||||
}
|
||||
function getStronglyConnectedComponents(graph) {
|
||||
const tarjanState = /* @__PURE__ */ new Map();
|
||||
let index = 0;
|
||||
const stack = [];
|
||||
const result = [];
|
||||
function strongConnect(v) {
|
||||
const state = {
|
||||
index,
|
||||
lowLink: index,
|
||||
onStack: true
|
||||
};
|
||||
tarjanState.set(v, state);
|
||||
index++;
|
||||
stack.push(v);
|
||||
const edges = graph.get(v).refs;
|
||||
for (let w of edges) {
|
||||
const edgeState = tarjanState.get(w);
|
||||
if (!edgeState) {
|
||||
strongConnect(w);
|
||||
state.lowLink = Math.min(state.lowLink, tarjanState.get(w).lowLink);
|
||||
} else if (edgeState.onStack) {
|
||||
state.lowLink = Math.min(state.lowLink, edgeState.index);
|
||||
}
|
||||
}
|
||||
if (state.lowLink === state.index) {
|
||||
const component = /* @__PURE__ */ new Set();
|
||||
let poppedNode = -1;
|
||||
do {
|
||||
poppedNode = stack.pop();
|
||||
tarjanState.get(poppedNode).onStack = false;
|
||||
component.add(poppedNode);
|
||||
} while (poppedNode !== v);
|
||||
result.push(component);
|
||||
}
|
||||
}
|
||||
for (const node of graph.keys()) {
|
||||
if (!tarjanState.has(node)) {
|
||||
strongConnect(node);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function mergeSCCsWithCommonNodes(stronglyConnectedComponents) {
|
||||
const scc = stronglyConnectedComponents;
|
||||
const ungroupedCycles = new Set(scc.map((_, i) => i));
|
||||
const edges = new Map(scc.map((_, i) => [i, /* @__PURE__ */ new Set()]));
|
||||
scc.forEach((cycle, i) => {
|
||||
scc.slice(i + 1).forEach((otherCycle, _j) => {
|
||||
const j = _j + i + 1;
|
||||
const combined = /* @__PURE__ */ new Set([...cycle, ...otherCycle]);
|
||||
if (combined.size !== cycle.size + otherCycle.size) {
|
||||
edges.get(i).add(j);
|
||||
edges.get(j).add(i);
|
||||
}
|
||||
});
|
||||
});
|
||||
const groups = [];
|
||||
while (ungroupedCycles.size) {
|
||||
const group = /* @__PURE__ */ new Set();
|
||||
const toVisit = [ungroupedCycles.values().next().value];
|
||||
while (toVisit.length) {
|
||||
const idx = toVisit.pop();
|
||||
if (!ungroupedCycles.has(idx)) continue;
|
||||
ungroupedCycles.delete(idx);
|
||||
const cycle = scc[idx];
|
||||
cycle.forEach((v) => group.add(Number(v)));
|
||||
edges.get(idx).forEach((n) => toVisit.push(n));
|
||||
}
|
||||
groups.push(group);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
export { buildLookupGraph, getStronglyConnectedComponents, getSubgraph, mergeSCCsWithCommonNodes };
|
||||
//# sourceMappingURL=lookup-graph.mjs.map
|
||||
File diff suppressed because one or more lines are too long
+224
@@ -0,0 +1,224 @@
|
||||
const isBytes = (value, nBytes) => value.type === "array" && value.len === nBytes && value.value.type === "primitive" && value.value.value === "u8";
|
||||
const _void = { type: "void" };
|
||||
const _denormalizeLookup = (lookupData, customMap = () => null) => {
|
||||
const lookups = /* @__PURE__ */ new Map();
|
||||
const from = /* @__PURE__ */ new Set();
|
||||
const withCache = (fn) => {
|
||||
return (id) => {
|
||||
let entry = lookups.get(id);
|
||||
if (entry) return entry;
|
||||
if (from.has(id)) {
|
||||
const entry2 = {
|
||||
id
|
||||
};
|
||||
lookups.set(id, entry2);
|
||||
return entry2;
|
||||
}
|
||||
from.add(id);
|
||||
const value = fn(id);
|
||||
entry = lookups.get(id);
|
||||
if (entry) {
|
||||
Object.assign(entry, value);
|
||||
} else {
|
||||
entry = {
|
||||
id,
|
||||
...value
|
||||
};
|
||||
lookups.set(id, entry);
|
||||
}
|
||||
from.delete(id);
|
||||
return entry;
|
||||
};
|
||||
};
|
||||
let isAccountId32SearchOn = true;
|
||||
let isAccountId20SearchOn = true;
|
||||
const getLookupEntryDef = withCache((id) => {
|
||||
const custom = customMap(lookupData[id]);
|
||||
if (custom) return custom;
|
||||
const { def, path, params } = lookupData[id];
|
||||
if (def.tag === "composite") {
|
||||
if (def.value.length === 0) return _void;
|
||||
if (def.value.length === 1) {
|
||||
const inner = getLookupEntryDef(def.value[0].type);
|
||||
if (isAccountId32SearchOn && path.at(-1) === "AccountId32" && isBytes(inner, 32)) {
|
||||
isAccountId32SearchOn = false;
|
||||
return { type: "AccountId32" };
|
||||
}
|
||||
if (isAccountId20SearchOn && path.at(-1) === "AccountId20" && isBytes(inner, 20)) {
|
||||
isAccountId20SearchOn = false;
|
||||
return { type: "AccountId20" };
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
return getComplexVar(def.value);
|
||||
}
|
||||
if (def.tag === "variant") {
|
||||
if (path.length === 1 && path[0] === "Option" && params.length === 1 && params[0].name === "T") {
|
||||
const value = getLookupEntryDef(params[0].type);
|
||||
return value.type === "void" ? (
|
||||
// Option<void> would return a Codec<undefined> which makes no sense
|
||||
// Therefore, we better treat it as a bool
|
||||
{ type: "primitive", value: "bool" }
|
||||
) : {
|
||||
type: "option",
|
||||
value
|
||||
};
|
||||
}
|
||||
if (path.length === 1 && path[0] === "Result" && params.length === 2 && params[0].name === "T" && params[1].name === "E") {
|
||||
return {
|
||||
type: "result",
|
||||
value: {
|
||||
ok: getLookupEntryDef(params[0].type),
|
||||
ko: getLookupEntryDef(params[1].type)
|
||||
}
|
||||
};
|
||||
}
|
||||
if (def.value.length === 0) return _void;
|
||||
const enumValue = {};
|
||||
const enumDocs = {};
|
||||
def.value.forEach((x) => {
|
||||
const key = x.name;
|
||||
enumDocs[key] = x.docs;
|
||||
if (x.fields.length === 0) {
|
||||
enumValue[key] = { ..._void, idx: x.index };
|
||||
return;
|
||||
}
|
||||
if (x.fields.length === 1 && !x.fields[0].name) {
|
||||
enumValue[key] = {
|
||||
type: "lookupEntry",
|
||||
value: getLookupEntryDef(x.fields[0].type),
|
||||
idx: x.index
|
||||
};
|
||||
return;
|
||||
}
|
||||
enumValue[key] = { ...getComplexVar(x.fields), idx: x.index };
|
||||
});
|
||||
return {
|
||||
type: "enum",
|
||||
value: enumValue,
|
||||
innerDocs: enumDocs
|
||||
};
|
||||
}
|
||||
if (def.tag === "sequence")
|
||||
return {
|
||||
type: "sequence",
|
||||
value: getLookupEntryDef(def.value)
|
||||
};
|
||||
if (def.tag === "array") {
|
||||
const { len } = def.value;
|
||||
const value = getLookupEntryDef(def.value.type);
|
||||
return !len || value.type === "void" ? _void : len > 1 ? {
|
||||
type: "array",
|
||||
value,
|
||||
len: def.value.len
|
||||
} : value;
|
||||
}
|
||||
if (def.tag === "tuple") {
|
||||
if (def.value.length === 0) return _void;
|
||||
return def.value.length > 1 ? getArrayOrTuple(
|
||||
def.value.map((x) => getLookupEntryDef(x)),
|
||||
def.value.map((x) => lookupData[x].docs)
|
||||
) : getLookupEntryDef(def.value[0]);
|
||||
}
|
||||
if (def.tag === "primitive") {
|
||||
return {
|
||||
type: "primitive",
|
||||
value: def.value.tag
|
||||
};
|
||||
}
|
||||
if (def.tag === "compact") {
|
||||
const translated = getLookupEntryDef(def.value);
|
||||
if (translated.type === "void") return _void;
|
||||
const isBig = Number(translated.value.slice(1)) > 32;
|
||||
return {
|
||||
type: "compact",
|
||||
isBig,
|
||||
size: translated.value
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: def.tag,
|
||||
isLSB: (lookupData[def.value.bitOrderType].path.at(-1) ?? "LSB").toUpperCase().startsWith("LSB")
|
||||
};
|
||||
});
|
||||
const getComplexVar = (input) => {
|
||||
let allKey = true;
|
||||
const values = {};
|
||||
const innerDocs = {};
|
||||
input.forEach((x, idx) => {
|
||||
allKey = allKey && !!x.name;
|
||||
const key = x.name || idx;
|
||||
const value = getLookupEntryDef(x.type);
|
||||
if (value.type !== "void") {
|
||||
values[key] = value;
|
||||
innerDocs[key] = x.docs;
|
||||
}
|
||||
});
|
||||
return allKey ? {
|
||||
type: "struct",
|
||||
value: values,
|
||||
innerDocs
|
||||
} : getArrayOrTuple(Object.values(values), Object.values(innerDocs));
|
||||
};
|
||||
const getArrayOrTuple = (values, innerDocs) => {
|
||||
if (values.every((v) => v.id === values[0].id) && innerDocs.every((doc) => !doc.length)) {
|
||||
const [value] = values;
|
||||
return value.type === "void" ? _void : {
|
||||
type: "array",
|
||||
value: values[0],
|
||||
len: values.length
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: "tuple",
|
||||
value: values,
|
||||
innerDocs
|
||||
};
|
||||
};
|
||||
return getLookupEntryDef;
|
||||
};
|
||||
const denormalizeLookup = (lookupData) => _denormalizeLookup(lookupData);
|
||||
const getLookupFn = (metadata) => {
|
||||
const getLookupEntryDef = _denormalizeLookup(metadata.lookup, ({ def }) => {
|
||||
if (def.tag === "composite") {
|
||||
const moduleErrorLength = getModuleErrorLength(def);
|
||||
if (moduleErrorLength) {
|
||||
return {
|
||||
type: "enum",
|
||||
innerDocs: {},
|
||||
value: Object.fromEntries(
|
||||
metadata.pallets.map((p) => [
|
||||
p.name,
|
||||
p.errors == null ? { ..._void, idx: p.index } : {
|
||||
type: "lookupEntry",
|
||||
value: getLookupEntryDef(p.errors.type),
|
||||
idx: p.index
|
||||
}
|
||||
])
|
||||
),
|
||||
byteLength: moduleErrorLength
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
function getModuleErrorLength(def) {
|
||||
const preChecks = def.value.length === 2 && def.value[0].name === "index" && def.value[1].name === "error";
|
||||
if (!preChecks) return null;
|
||||
const index = getLookupEntryDef(def.value[0].type);
|
||||
const error = getLookupEntryDef(def.value[1].type);
|
||||
return index.type === "primitive" && index.value === "u8" && error.type === "array" && error.value.type === "primitive" && error.value.value === "u8" ? 1 + error.len : null;
|
||||
}
|
||||
const getCall = () => {
|
||||
if ("call" in metadata.extrinsic) {
|
||||
return metadata.extrinsic.call;
|
||||
}
|
||||
const extrinsic = metadata.lookup[metadata.extrinsic.type];
|
||||
const call = extrinsic?.params.find((p) => p.name === "Call");
|
||||
return call?.type ?? null;
|
||||
};
|
||||
return Object.assign(getLookupEntryDef, { metadata, call: getCall() });
|
||||
};
|
||||
|
||||
export { denormalizeLookup, getLookupFn };
|
||||
//# sourceMappingURL=lookups.mjs.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,19 @@
|
||||
const withCache = (fn, onEnterCircular, onExitCircular) => (input, cache, stack, ...rest) => {
|
||||
const { id } = input;
|
||||
if (cache.has(id)) return cache.get(id);
|
||||
if (stack.has(id)) {
|
||||
const res = onEnterCircular(() => cache.get(id), input, ...rest);
|
||||
cache.set(id, res);
|
||||
return res;
|
||||
}
|
||||
stack.add(id);
|
||||
let result = fn(input, cache, stack, ...rest);
|
||||
stack.delete(id);
|
||||
if (cache.has(id))
|
||||
result = onExitCircular(result, cache.get(id), input, ...rest);
|
||||
cache.set(id, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
export { withCache };
|
||||
//# sourceMappingURL=with-cache.mjs.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"with-cache.mjs","sources":["../../src/with-cache.ts"],"sourcesContent":["import { LookupEntry } from \"./lookups\"\n\ntype FnWithStack<Other extends Array<any>, T> = (\n input: LookupEntry,\n cache: Map<number, T>,\n stack: Set<number>,\n ...rest: Other\n) => T\n\nexport const withCache =\n <Other extends Array<any>, T>(\n fn: FnWithStack<Other, T>,\n onEnterCircular: (\n cacheGetter: () => T,\n circular: LookupEntry,\n ...rest: Other\n ) => T,\n onExitCircular: (\n outter: T,\n inner: T,\n circular: LookupEntry,\n ...rest: Other\n ) => T,\n ): FnWithStack<Other, T> =>\n (input, cache, stack, ...rest) => {\n const { id } = input\n if (cache.has(id)) return cache.get(id)!\n\n if (stack.has(id)) {\n const res = onEnterCircular(() => cache.get(id)!, input, ...rest)\n cache.set(id, res)\n return res\n }\n\n stack.add(id)\n let result = fn(input, cache, stack, ...rest)\n stack.delete(id)\n\n if (cache.has(id))\n result = onExitCircular(result, cache.get(id)!, input, ...rest)\n\n cache.set(id, result)\n return result\n }\n"],"names":[],"mappings":"AASO,MAAM,SAAA,GACX,CACE,EAAA,EACA,eAAA,EAKA,mBAOF,CAAC,KAAA,EAAO,KAAA,EAAO,KAAA,EAAA,GAAU,IAAA,KAAS;AAChC,EAAA,MAAM,EAAE,IAAG,GAAI,KAAA;AACf,EAAA,IAAI,MAAM,GAAA,CAAI,EAAE,GAAG,OAAO,KAAA,CAAM,IAAI,EAAE,CAAA;AAEtC,EAAA,IAAI,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA,EAAG;AACjB,IAAA,MAAM,GAAA,GAAM,gBAAgB,MAAM,KAAA,CAAM,IAAI,EAAE,CAAA,EAAI,KAAA,EAAO,GAAG,IAAI,CAAA;AAChE,IAAA,KAAA,CAAM,GAAA,CAAI,IAAI,GAAG,CAAA;AACjB,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,KAAA,CAAM,IAAI,EAAE,CAAA;AACZ,EAAA,IAAI,SAAS,EAAA,CAAG,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,GAAG,IAAI,CAAA;AAC5C,EAAA,KAAA,CAAM,OAAO,EAAE,CAAA;AAEf,EAAA,IAAI,KAAA,CAAM,IAAI,EAAE,CAAA;AACd,IAAA,MAAA,GAAS,cAAA,CAAe,QAAQ,KAAA,CAAM,GAAA,CAAI,EAAE,CAAA,EAAI,KAAA,EAAO,GAAG,IAAI,CAAA;AAEhE,EAAA,KAAA,CAAM,GAAA,CAAI,IAAI,MAAM,CAAA;AACpB,EAAA,OAAO,MAAA;AACT;;;;"}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
import * as scale from '@pezkuwi/bizinikiwi-bindings';
|
||||
import { StringRecord, UnifiedMetadata, V14Lookup, Codec } from '@pezkuwi/bizinikiwi-bindings';
|
||||
|
||||
type SignedPrimitive = "i8" | "i16" | "i32" | "i64" | "i128" | "i256";
|
||||
type UnsignedPrimitive = "u8" | "u16" | "u32" | "u64" | "u128" | "u256";
|
||||
type MetadataPrimitives = "bool" | "char" | "str" | SignedPrimitive | UnsignedPrimitive;
|
||||
type PrimitiveVar = {
|
||||
type: "primitive";
|
||||
value: MetadataPrimitives;
|
||||
};
|
||||
type VoidVar = {
|
||||
type: "void";
|
||||
};
|
||||
type CompactVar = {
|
||||
type: "compact";
|
||||
isBig: boolean;
|
||||
size: UnsignedPrimitive;
|
||||
};
|
||||
type BitSequenceVar = {
|
||||
type: "bitSequence";
|
||||
isLSB: boolean;
|
||||
};
|
||||
type AccountId32 = {
|
||||
type: "AccountId32";
|
||||
};
|
||||
type AccountId20 = {
|
||||
type: "AccountId20";
|
||||
};
|
||||
type TerminalVar = PrimitiveVar | VoidVar | CompactVar | BitSequenceVar | AccountId32 | AccountId20;
|
||||
type TupleVar = {
|
||||
type: "tuple";
|
||||
value: LookupEntry[];
|
||||
innerDocs: Array<string[]>;
|
||||
};
|
||||
type StructVar = {
|
||||
type: "struct";
|
||||
value: StringRecord<LookupEntry>;
|
||||
innerDocs: StringRecord<string[]>;
|
||||
};
|
||||
type EnumVar = {
|
||||
type: "enum";
|
||||
value: StringRecord<({
|
||||
type: "lookupEntry";
|
||||
value: LookupEntry;
|
||||
} | VoidVar | TupleVar | StructVar | ArrayVar) & {
|
||||
idx: number;
|
||||
}>;
|
||||
innerDocs: StringRecord<string[]>;
|
||||
byteLength?: number;
|
||||
};
|
||||
type OptionVar = {
|
||||
type: "option";
|
||||
value: LookupEntry;
|
||||
};
|
||||
type ResultVar = {
|
||||
type: "result";
|
||||
value: {
|
||||
ok: LookupEntry;
|
||||
ko: LookupEntry;
|
||||
};
|
||||
};
|
||||
type SequenceVar = {
|
||||
type: "sequence";
|
||||
value: LookupEntry;
|
||||
};
|
||||
type ArrayVar = {
|
||||
type: "array";
|
||||
value: LookupEntry;
|
||||
len: number;
|
||||
};
|
||||
type ComposedVar = TupleVar | StructVar | SequenceVar | ArrayVar | OptionVar | ResultVar | EnumVar;
|
||||
type Var = TerminalVar | ComposedVar;
|
||||
type LookupEntry = {
|
||||
id: number;
|
||||
} & Var;
|
||||
interface MetadataLookup {
|
||||
(id: number): LookupEntry;
|
||||
metadata: UnifiedMetadata;
|
||||
call: number | null;
|
||||
}
|
||||
declare const denormalizeLookup: (lookupData: V14Lookup) => (id: number) => LookupEntry;
|
||||
declare const getLookupFn: (metadata: UnifiedMetadata) => MetadataLookup;
|
||||
|
||||
declare const getDynamicBuilder: (getLookupEntryDef: MetadataLookup) => {
|
||||
buildDefinition: (id: number) => Codec<any>;
|
||||
buildStorage: (pallet: string, entry: string) => {
|
||||
args: [scale.Encoder<any[]>, scale.Decoder<any[]>] & {
|
||||
enc: scale.Encoder<any[]>;
|
||||
dec: scale.Decoder<any[]>;
|
||||
} & {
|
||||
inner: Codec<any>[];
|
||||
};
|
||||
keys: {
|
||||
enc: (...args: any[]) => string;
|
||||
dec: (value: string) => any[];
|
||||
};
|
||||
value: Codec<any>;
|
||||
len: number;
|
||||
fallback: any;
|
||||
};
|
||||
buildEvent: (pallet: string, name: string) => {
|
||||
codec: Codec<any>;
|
||||
location: [number, number];
|
||||
};
|
||||
buildError: (pallet: string, name: string) => {
|
||||
codec: Codec<any>;
|
||||
location: [number, number];
|
||||
};
|
||||
buildViewFn: (pallet: string, entry: string) => {
|
||||
args: [scale.Encoder<any[]>, scale.Decoder<any[]>] & {
|
||||
enc: scale.Encoder<any[]>;
|
||||
dec: scale.Decoder<any[]>;
|
||||
} & {
|
||||
inner: Codec<any>[];
|
||||
};
|
||||
value: Codec<any>;
|
||||
};
|
||||
buildRuntimeCall: (api: string, method: string) => {
|
||||
args: [scale.Encoder<any[]>, scale.Decoder<any[]>] & {
|
||||
enc: scale.Encoder<any[]>;
|
||||
dec: scale.Decoder<any[]>;
|
||||
} & {
|
||||
inner: Codec<any>[];
|
||||
};
|
||||
value: Codec<any>;
|
||||
};
|
||||
buildCall: (pallet: string, name: string) => {
|
||||
codec: Codec<any>;
|
||||
location: [number, number];
|
||||
};
|
||||
buildConstant: (pallet: string, constantName: string) => Codec<any>;
|
||||
ss58Prefix: number | undefined;
|
||||
};
|
||||
|
||||
declare const getChecksumBuilder: (getLookupEntryDef: MetadataLookup) => {
|
||||
buildDefinition: (id: number) => string | null;
|
||||
buildRuntimeCall: (api: string, method: string) => string | null;
|
||||
buildStorage: (pallet: string, entry: string) => string | null;
|
||||
buildViewFns: (pallet: string, entry: string) => string | null;
|
||||
buildCall: (pallet: string, name: string) => string | null;
|
||||
buildEvent: (pallet: string, name: string) => string | null;
|
||||
buildError: (pallet: string, name: string) => string | null;
|
||||
buildConstant: (pallet: string, constantName: string) => string | null;
|
||||
buildComposite: (input: VoidVar | TupleVar | StructVar | ArrayVar) => string | null;
|
||||
buildNamedTuple: (input: StructVar) => string | null;
|
||||
getAllGeneratedChecksums: () => string[];
|
||||
};
|
||||
|
||||
declare const getLookupCodecBuilder: (lookup: (id: number) => LookupEntry, accountId?: Codec<scale.SS58String>) => (id: number) => Codec<any>;
|
||||
|
||||
export { denormalizeLookup, getChecksumBuilder, getDynamicBuilder, getLookupCodecBuilder, getLookupFn };
|
||||
export type { AccountId20, AccountId32, ArrayVar, BitSequenceVar, CompactVar, ComposedVar, EnumVar, LookupEntry, MetadataLookup, MetadataPrimitives, OptionVar, PrimitiveVar, ResultVar, SequenceVar, SignedPrimitive, StructVar, TerminalVar, TupleVar, UnsignedPrimitive, Var, VoidVar };
|
||||
+999
@@ -0,0 +1,999 @@
|
||||
'use strict';
|
||||
|
||||
var scale = require('@pezkuwi/bizinikiwi-bindings');
|
||||
var utils = require('@pezkuwi/papi-utils');
|
||||
|
||||
function _interopNamespaceDefault(e) {
|
||||
var n = Object.create(null);
|
||||
if (e) {
|
||||
Object.keys(e).forEach(function (k) {
|
||||
if (k !== 'default') {
|
||||
var d = Object.getOwnPropertyDescriptor(e, k);
|
||||
Object.defineProperty(n, k, d.get ? d : {
|
||||
enumerable: true,
|
||||
get: function () { return e[k]; }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
n.default = e;
|
||||
return Object.freeze(n);
|
||||
}
|
||||
|
||||
var scale__namespace = /*#__PURE__*/_interopNamespaceDefault(scale);
|
||||
|
||||
const isBytes = (value, nBytes) => value.type === "array" && value.len === nBytes && value.value.type === "primitive" && value.value.value === "u8";
|
||||
const _void = { type: "void" };
|
||||
const _denormalizeLookup = (lookupData, customMap = () => null) => {
|
||||
const lookups = /* @__PURE__ */ new Map();
|
||||
const from = /* @__PURE__ */ new Set();
|
||||
const withCache = (fn) => {
|
||||
return (id) => {
|
||||
let entry = lookups.get(id);
|
||||
if (entry) return entry;
|
||||
if (from.has(id)) {
|
||||
const entry2 = {
|
||||
id
|
||||
};
|
||||
lookups.set(id, entry2);
|
||||
return entry2;
|
||||
}
|
||||
from.add(id);
|
||||
const value = fn(id);
|
||||
entry = lookups.get(id);
|
||||
if (entry) {
|
||||
Object.assign(entry, value);
|
||||
} else {
|
||||
entry = {
|
||||
id,
|
||||
...value
|
||||
};
|
||||
lookups.set(id, entry);
|
||||
}
|
||||
from.delete(id);
|
||||
return entry;
|
||||
};
|
||||
};
|
||||
let isAccountId32SearchOn = true;
|
||||
let isAccountId20SearchOn = true;
|
||||
const getLookupEntryDef = withCache((id) => {
|
||||
const custom = customMap(lookupData[id]);
|
||||
if (custom) return custom;
|
||||
const { def, path, params } = lookupData[id];
|
||||
if (def.tag === "composite") {
|
||||
if (def.value.length === 0) return _void;
|
||||
if (def.value.length === 1) {
|
||||
const inner = getLookupEntryDef(def.value[0].type);
|
||||
if (isAccountId32SearchOn && path.at(-1) === "AccountId32" && isBytes(inner, 32)) {
|
||||
isAccountId32SearchOn = false;
|
||||
return { type: "AccountId32" };
|
||||
}
|
||||
if (isAccountId20SearchOn && path.at(-1) === "AccountId20" && isBytes(inner, 20)) {
|
||||
isAccountId20SearchOn = false;
|
||||
return { type: "AccountId20" };
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
return getComplexVar(def.value);
|
||||
}
|
||||
if (def.tag === "variant") {
|
||||
if (path.length === 1 && path[0] === "Option" && params.length === 1 && params[0].name === "T") {
|
||||
const value = getLookupEntryDef(params[0].type);
|
||||
return value.type === "void" ? (
|
||||
// Option<void> would return a Codec<undefined> which makes no sense
|
||||
// Therefore, we better treat it as a bool
|
||||
{ type: "primitive", value: "bool" }
|
||||
) : {
|
||||
type: "option",
|
||||
value
|
||||
};
|
||||
}
|
||||
if (path.length === 1 && path[0] === "Result" && params.length === 2 && params[0].name === "T" && params[1].name === "E") {
|
||||
return {
|
||||
type: "result",
|
||||
value: {
|
||||
ok: getLookupEntryDef(params[0].type),
|
||||
ko: getLookupEntryDef(params[1].type)
|
||||
}
|
||||
};
|
||||
}
|
||||
if (def.value.length === 0) return _void;
|
||||
const enumValue = {};
|
||||
const enumDocs = {};
|
||||
def.value.forEach((x) => {
|
||||
const key = x.name;
|
||||
enumDocs[key] = x.docs;
|
||||
if (x.fields.length === 0) {
|
||||
enumValue[key] = { ..._void, idx: x.index };
|
||||
return;
|
||||
}
|
||||
if (x.fields.length === 1 && !x.fields[0].name) {
|
||||
enumValue[key] = {
|
||||
type: "lookupEntry",
|
||||
value: getLookupEntryDef(x.fields[0].type),
|
||||
idx: x.index
|
||||
};
|
||||
return;
|
||||
}
|
||||
enumValue[key] = { ...getComplexVar(x.fields), idx: x.index };
|
||||
});
|
||||
return {
|
||||
type: "enum",
|
||||
value: enumValue,
|
||||
innerDocs: enumDocs
|
||||
};
|
||||
}
|
||||
if (def.tag === "sequence")
|
||||
return {
|
||||
type: "sequence",
|
||||
value: getLookupEntryDef(def.value)
|
||||
};
|
||||
if (def.tag === "array") {
|
||||
const { len } = def.value;
|
||||
const value = getLookupEntryDef(def.value.type);
|
||||
return !len || value.type === "void" ? _void : len > 1 ? {
|
||||
type: "array",
|
||||
value,
|
||||
len: def.value.len
|
||||
} : value;
|
||||
}
|
||||
if (def.tag === "tuple") {
|
||||
if (def.value.length === 0) return _void;
|
||||
return def.value.length > 1 ? getArrayOrTuple(
|
||||
def.value.map((x) => getLookupEntryDef(x)),
|
||||
def.value.map((x) => lookupData[x].docs)
|
||||
) : getLookupEntryDef(def.value[0]);
|
||||
}
|
||||
if (def.tag === "primitive") {
|
||||
return {
|
||||
type: "primitive",
|
||||
value: def.value.tag
|
||||
};
|
||||
}
|
||||
if (def.tag === "compact") {
|
||||
const translated = getLookupEntryDef(def.value);
|
||||
if (translated.type === "void") return _void;
|
||||
const isBig = Number(translated.value.slice(1)) > 32;
|
||||
return {
|
||||
type: "compact",
|
||||
isBig,
|
||||
size: translated.value
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: def.tag,
|
||||
isLSB: (lookupData[def.value.bitOrderType].path.at(-1) ?? "LSB").toUpperCase().startsWith("LSB")
|
||||
};
|
||||
});
|
||||
const getComplexVar = (input) => {
|
||||
let allKey = true;
|
||||
const values = {};
|
||||
const innerDocs = {};
|
||||
input.forEach((x, idx) => {
|
||||
allKey = allKey && !!x.name;
|
||||
const key = x.name || idx;
|
||||
const value = getLookupEntryDef(x.type);
|
||||
if (value.type !== "void") {
|
||||
values[key] = value;
|
||||
innerDocs[key] = x.docs;
|
||||
}
|
||||
});
|
||||
return allKey ? {
|
||||
type: "struct",
|
||||
value: values,
|
||||
innerDocs
|
||||
} : getArrayOrTuple(Object.values(values), Object.values(innerDocs));
|
||||
};
|
||||
const getArrayOrTuple = (values, innerDocs) => {
|
||||
if (values.every((v) => v.id === values[0].id) && innerDocs.every((doc) => !doc.length)) {
|
||||
const [value] = values;
|
||||
return value.type === "void" ? _void : {
|
||||
type: "array",
|
||||
value: values[0],
|
||||
len: values.length
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: "tuple",
|
||||
value: values,
|
||||
innerDocs
|
||||
};
|
||||
};
|
||||
return getLookupEntryDef;
|
||||
};
|
||||
const denormalizeLookup = (lookupData) => _denormalizeLookup(lookupData);
|
||||
const getLookupFn = (metadata) => {
|
||||
const getLookupEntryDef = _denormalizeLookup(metadata.lookup, ({ def }) => {
|
||||
if (def.tag === "composite") {
|
||||
const moduleErrorLength = getModuleErrorLength(def);
|
||||
if (moduleErrorLength) {
|
||||
return {
|
||||
type: "enum",
|
||||
innerDocs: {},
|
||||
value: Object.fromEntries(
|
||||
metadata.pallets.map((p) => [
|
||||
p.name,
|
||||
p.errors == null ? { ..._void, idx: p.index } : {
|
||||
type: "lookupEntry",
|
||||
value: getLookupEntryDef(p.errors.type),
|
||||
idx: p.index
|
||||
}
|
||||
])
|
||||
),
|
||||
byteLength: moduleErrorLength
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
function getModuleErrorLength(def) {
|
||||
const preChecks = def.value.length === 2 && def.value[0].name === "index" && def.value[1].name === "error";
|
||||
if (!preChecks) return null;
|
||||
const index = getLookupEntryDef(def.value[0].type);
|
||||
const error = getLookupEntryDef(def.value[1].type);
|
||||
return index.type === "primitive" && index.value === "u8" && error.type === "array" && error.value.type === "primitive" && error.value.value === "u8" ? 1 + error.len : null;
|
||||
}
|
||||
const getCall = () => {
|
||||
if ("call" in metadata.extrinsic) {
|
||||
return metadata.extrinsic.call;
|
||||
}
|
||||
const extrinsic = metadata.lookup[metadata.extrinsic.type];
|
||||
const call = extrinsic?.params.find((p) => p.name === "Call");
|
||||
return call?.type ?? null;
|
||||
};
|
||||
return Object.assign(getLookupEntryDef, { metadata, call: getCall() });
|
||||
};
|
||||
|
||||
const withCache = (fn, onEnterCircular, onExitCircular) => (input, cache, stack, ...rest) => {
|
||||
const { id } = input;
|
||||
if (cache.has(id)) return cache.get(id);
|
||||
if (stack.has(id)) {
|
||||
const res = onEnterCircular(() => cache.get(id), input, ...rest);
|
||||
cache.set(id, res);
|
||||
return res;
|
||||
}
|
||||
stack.add(id);
|
||||
let result = fn(input, cache, stack, ...rest);
|
||||
stack.delete(id);
|
||||
if (cache.has(id))
|
||||
result = onExitCircular(result, cache.get(id), input, ...rest);
|
||||
cache.set(id, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
const _bytes = scale__namespace.Bin();
|
||||
const _buildCodec = (input, cache, stack, _accountId) => {
|
||||
if (input.type === "primitive") return scale__namespace[input.value];
|
||||
if (input.type === "void") return scale__namespace._void;
|
||||
if (input.type === "AccountId32") return _accountId;
|
||||
if (input.type === "AccountId20") return scale__namespace.ethAccount;
|
||||
if (input.type === "compact")
|
||||
return input.isBig ? scale__namespace.compactBn : scale__namespace.compactNumber;
|
||||
if (input.type === "bitSequence") return scale__namespace.BitSeq(input.isLSB);
|
||||
const buildNextCodec = (nextInput) => buildCodec(nextInput, cache, stack, _accountId);
|
||||
const buildVector = (inner2, len) => {
|
||||
const innerCodec = buildNextCodec(inner2);
|
||||
return len ? scale__namespace.Vector(innerCodec, len) : scale__namespace.Vector(innerCodec);
|
||||
};
|
||||
const buildTuple = (value) => scale__namespace.Tuple(...value.map(buildNextCodec));
|
||||
const buildStruct = (value) => {
|
||||
const inner2 = Object.fromEntries(
|
||||
Object.entries(value).map(([key, value2]) => [key, buildNextCodec(value2)])
|
||||
);
|
||||
return scale__namespace.Struct(inner2);
|
||||
};
|
||||
if (input.type === "sequence" && input.value.type === "primitive" && input.value.value === "u8") {
|
||||
return _bytes;
|
||||
}
|
||||
if (input.type === "array") {
|
||||
if (input.value.type === "primitive" && input.value.value === "u8")
|
||||
return scale__namespace.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__namespace.Option(buildNextCodec(input.value));
|
||||
if (input.type === "result")
|
||||
return scale__namespace.Result(
|
||||
buildNextCodec(input.value.ok),
|
||||
buildNextCodec(input.value.ko)
|
||||
);
|
||||
const dependencies = Object.values(input.value).map((v) => {
|
||||
switch (v.type) {
|
||||
case "void":
|
||||
return scale__namespace._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]];
|
||||
})
|
||||
);
|
||||
const indexes = Object.values(input.value).map((x) => x.idx);
|
||||
const areIndexesSorted = indexes.every((idx, i) => idx === i);
|
||||
const variantCodec = areIndexesSorted ? scale__namespace.Variant(inner) : scale__namespace.Variant(inner, indexes);
|
||||
return input.byteLength ? fixedSizeCodec(variantCodec, input.byteLength) : variantCodec;
|
||||
};
|
||||
const buildCodec = withCache(_buildCodec, scale__namespace.Self, (res) => res);
|
||||
const getLookupCodecBuilder = (lookup, accountId = scale__namespace.AccountId()) => {
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const buildDefinition = (id) => buildCodec(lookup(id), cache, /* @__PURE__ */ new Set(), accountId);
|
||||
return (id) => buildDefinition(id);
|
||||
};
|
||||
const fixedSizeCodec = (codec, size) => {
|
||||
const allBytes = scale__namespace.Bytes(size);
|
||||
return scale__namespace.createCodec(
|
||||
(value) => allBytes.enc(codec.enc(value)),
|
||||
(data) => codec.dec(allBytes.dec(data))
|
||||
);
|
||||
};
|
||||
|
||||
const nullCodec = scale__namespace.enhanceCodec(
|
||||
scale__namespace._void,
|
||||
() => void 0,
|
||||
() => null
|
||||
);
|
||||
const getDynamicBuilder = (getLookupEntryDef) => {
|
||||
const { metadata } = getLookupEntryDef;
|
||||
let buildDefinition = getLookupCodecBuilder(getLookupEntryDef);
|
||||
const prefix = metadata.pallets.find((x) => x.name === "System")?.constants.find((x) => x.name === "SS58Prefix");
|
||||
let ss58Prefix;
|
||||
if (prefix) {
|
||||
try {
|
||||
const prefixVal = buildDefinition(prefix.type).dec(prefix.value);
|
||||
if (typeof prefixVal === "number") {
|
||||
ss58Prefix = prefixVal;
|
||||
buildDefinition = getLookupCodecBuilder(
|
||||
getLookupEntryDef,
|
||||
scale__namespace.AccountId(prefixVal)
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
const storagePallets = /* @__PURE__ */ new Map();
|
||||
const buildStorage = (pallet, entry) => {
|
||||
let storagePallet = storagePallets.get(pallet);
|
||||
if (!storagePallet)
|
||||
storagePallets.set(pallet, storagePallet = scale__namespace.Storage(pallet));
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).storage.items.find((s) => s.name === entry);
|
||||
const withNullVoid = (codec) => codec === scale__namespace._void ? nullCodec : codec;
|
||||
const storageWithFallback = (len, value2, ...args) => {
|
||||
const keys = storagePallet(...args);
|
||||
const [, ...encodersWithHash] = args;
|
||||
return {
|
||||
args: scale__namespace.Tuple(...encodersWithHash.map(([codec]) => codec)),
|
||||
keys,
|
||||
value: value2,
|
||||
len,
|
||||
fallback: storageEntry.modifier === 1 ? value2.dec(storageEntry.fallback) : void 0
|
||||
};
|
||||
};
|
||||
if (storageEntry.type.tag === "plain")
|
||||
return storageWithFallback(
|
||||
0,
|
||||
withNullVoid(buildDefinition(storageEntry.type.value)),
|
||||
entry
|
||||
);
|
||||
const { key, value, hashers } = storageEntry.type.value;
|
||||
const val = withNullVoid(buildDefinition(value));
|
||||
const hashes = hashers.map((x) => scale__namespace[x.tag]);
|
||||
const hashArgs = (() => {
|
||||
if (hashes.length === 1) {
|
||||
return [[buildDefinition(key), hashes[0]]];
|
||||
}
|
||||
const keyDef = getLookupEntryDef(key);
|
||||
switch (keyDef.type) {
|
||||
case "array":
|
||||
return hashes.map((hash) => [buildDefinition(keyDef.value.id), hash]);
|
||||
case "tuple":
|
||||
return keyDef.value.map((x, idx) => [
|
||||
buildDefinition(x.id),
|
||||
hashes[idx]
|
||||
]);
|
||||
default:
|
||||
throw new Error("Invalid key type");
|
||||
}
|
||||
})();
|
||||
return storageWithFallback(hashes.length, val, entry, ...hashArgs);
|
||||
};
|
||||
const buildEnumEntry = (entry) => {
|
||||
switch (entry.type) {
|
||||
case "void":
|
||||
return scale__namespace._void;
|
||||
case "lookupEntry":
|
||||
return buildDefinition(entry.value.id);
|
||||
case "tuple":
|
||||
return scale__namespace.Tuple(
|
||||
...Object.values(entry.value).map((l) => buildDefinition(l.id))
|
||||
);
|
||||
case "struct":
|
||||
return scale__namespace.Struct(
|
||||
utils.mapObject(entry.value, (x) => buildDefinition(x.id))
|
||||
);
|
||||
case "array":
|
||||
return scale__namespace.Vector(buildDefinition(entry.value.id), entry.len);
|
||||
}
|
||||
};
|
||||
const buildConstant = (pallet, constantName) => {
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).constants.find((s) => s.name === constantName);
|
||||
return buildDefinition(storageEntry.type);
|
||||
};
|
||||
const buildVariant = (type) => (pallet, name) => {
|
||||
const palletEntry = metadata.pallets.find((x) => x.name === pallet);
|
||||
const lookup = getLookupEntryDef(palletEntry[type].type);
|
||||
if (lookup.type !== "enum") throw null;
|
||||
const entry = lookup.value[name];
|
||||
return {
|
||||
location: [palletEntry.index, entry.idx],
|
||||
codec: buildEnumEntry(lookup.value[name])
|
||||
};
|
||||
};
|
||||
const buildViewFn = (pallet, entry) => {
|
||||
const fn = metadata.pallets.find((x) => x.name === pallet)?.viewFns.find((x) => x.name === entry);
|
||||
if (!fn) throw null;
|
||||
return {
|
||||
args: scale__namespace.Tuple(...fn.inputs.map((x) => buildDefinition(x.type))),
|
||||
value: buildDefinition(fn.output)
|
||||
};
|
||||
};
|
||||
const buildRuntimeCall = (api, method) => {
|
||||
const entry = metadata.apis.find((x) => x.name === api)?.methods.find((x) => x.name === method);
|
||||
if (!entry) throw null;
|
||||
return {
|
||||
args: scale__namespace.Tuple(...entry.inputs.map((x) => buildDefinition(x.type))),
|
||||
value: buildDefinition(entry.output)
|
||||
};
|
||||
};
|
||||
return {
|
||||
buildDefinition,
|
||||
buildStorage,
|
||||
buildEvent: buildVariant("events"),
|
||||
buildError: buildVariant("errors"),
|
||||
buildViewFn,
|
||||
buildRuntimeCall,
|
||||
buildCall: buildVariant("calls"),
|
||||
buildConstant,
|
||||
ss58Prefix
|
||||
};
|
||||
};
|
||||
|
||||
function buildLookupGraph(lookupFn, lookupLength) {
|
||||
const result = /* @__PURE__ */ new Map();
|
||||
const visited = /* @__PURE__ */ new Set();
|
||||
const addEdge = (from, to) => {
|
||||
if (!result.has(from))
|
||||
result.set(from, {
|
||||
entry: lookupFn(from),
|
||||
backRefs: /* @__PURE__ */ new Set(),
|
||||
refs: /* @__PURE__ */ new Set()
|
||||
});
|
||||
if (!result.has(to))
|
||||
result.set(to, {
|
||||
entry: lookupFn(to),
|
||||
backRefs: /* @__PURE__ */ new Set(),
|
||||
refs: /* @__PURE__ */ new Set()
|
||||
});
|
||||
result.get(from).refs.add(to);
|
||||
result.get(to).backRefs.add(from);
|
||||
};
|
||||
for (let i = 0; i < lookupLength; i++) {
|
||||
const entry = lookupFn(i);
|
||||
if (i !== entry.id) {
|
||||
addEdge(i, entry.id);
|
||||
}
|
||||
if (visited.has(entry.id)) continue;
|
||||
visited.add(entry.id);
|
||||
switch (entry.type) {
|
||||
case "array":
|
||||
case "option":
|
||||
case "sequence":
|
||||
addEdge(entry.id, entry.value.id);
|
||||
break;
|
||||
case "enum":
|
||||
Object.values(entry.value).forEach((enumEntry) => {
|
||||
switch (enumEntry.type) {
|
||||
case "array":
|
||||
case "lookupEntry":
|
||||
addEdge(entry.id, enumEntry.value.id);
|
||||
break;
|
||||
case "struct":
|
||||
case "tuple":
|
||||
Object.values(enumEntry.value).forEach(
|
||||
(v) => addEdge(entry.id, v.id)
|
||||
);
|
||||
break;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "result":
|
||||
addEdge(entry.id, entry.value.ok.id);
|
||||
addEdge(entry.id, entry.value.ko.id);
|
||||
break;
|
||||
case "struct":
|
||||
case "tuple":
|
||||
Object.values(entry.value).forEach((v) => addEdge(entry.id, v.id));
|
||||
break;
|
||||
}
|
||||
if (!result.has(entry.id)) {
|
||||
result.set(entry.id, {
|
||||
backRefs: /* @__PURE__ */ new Set(),
|
||||
refs: /* @__PURE__ */ new Set(),
|
||||
entry
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const subgraphCache = /* @__PURE__ */ new WeakMap();
|
||||
function _getSubgraph(id, graph, result, cache) {
|
||||
if (result.has(id)) return;
|
||||
const node = graph.get(id);
|
||||
result.set(id, node);
|
||||
cache.set(id, result);
|
||||
node.refs.forEach((ref) => _getSubgraph(ref, graph, result, cache));
|
||||
node.backRefs.forEach((ref) => _getSubgraph(ref, graph, result, cache));
|
||||
}
|
||||
function getSubgraph(id, graph) {
|
||||
if (!subgraphCache.has(graph)) {
|
||||
subgraphCache.set(graph, /* @__PURE__ */ new Map());
|
||||
}
|
||||
const cache = subgraphCache.get(graph);
|
||||
if (cache.has(id)) return cache.get(id);
|
||||
const result = /* @__PURE__ */ new Map();
|
||||
_getSubgraph(id, graph, result, cache);
|
||||
return result;
|
||||
}
|
||||
function getStronglyConnectedComponents(graph) {
|
||||
const tarjanState = /* @__PURE__ */ new Map();
|
||||
let index = 0;
|
||||
const stack = [];
|
||||
const result = [];
|
||||
function strongConnect(v) {
|
||||
const state = {
|
||||
index,
|
||||
lowLink: index,
|
||||
onStack: true
|
||||
};
|
||||
tarjanState.set(v, state);
|
||||
index++;
|
||||
stack.push(v);
|
||||
const edges = graph.get(v).refs;
|
||||
for (let w of edges) {
|
||||
const edgeState = tarjanState.get(w);
|
||||
if (!edgeState) {
|
||||
strongConnect(w);
|
||||
state.lowLink = Math.min(state.lowLink, tarjanState.get(w).lowLink);
|
||||
} else if (edgeState.onStack) {
|
||||
state.lowLink = Math.min(state.lowLink, edgeState.index);
|
||||
}
|
||||
}
|
||||
if (state.lowLink === state.index) {
|
||||
const component = /* @__PURE__ */ new Set();
|
||||
let poppedNode = -1;
|
||||
do {
|
||||
poppedNode = stack.pop();
|
||||
tarjanState.get(poppedNode).onStack = false;
|
||||
component.add(poppedNode);
|
||||
} while (poppedNode !== v);
|
||||
result.push(component);
|
||||
}
|
||||
}
|
||||
for (const node of graph.keys()) {
|
||||
if (!tarjanState.has(node)) {
|
||||
strongConnect(node);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function mergeSCCsWithCommonNodes(stronglyConnectedComponents) {
|
||||
const scc = stronglyConnectedComponents;
|
||||
const ungroupedCycles = new Set(scc.map((_, i) => i));
|
||||
const edges = new Map(scc.map((_, i) => [i, /* @__PURE__ */ new Set()]));
|
||||
scc.forEach((cycle, i) => {
|
||||
scc.slice(i + 1).forEach((otherCycle, _j) => {
|
||||
const j = _j + i + 1;
|
||||
const combined = /* @__PURE__ */ new Set([...cycle, ...otherCycle]);
|
||||
if (combined.size !== cycle.size + otherCycle.size) {
|
||||
edges.get(i).add(j);
|
||||
edges.get(j).add(i);
|
||||
}
|
||||
});
|
||||
});
|
||||
const groups = [];
|
||||
while (ungroupedCycles.size) {
|
||||
const group = /* @__PURE__ */ new Set();
|
||||
const toVisit = [ungroupedCycles.values().next().value];
|
||||
while (toVisit.length) {
|
||||
const idx = toVisit.pop();
|
||||
if (!ungroupedCycles.has(idx)) continue;
|
||||
ungroupedCycles.delete(idx);
|
||||
const cycle = scc[idx];
|
||||
cycle.forEach((v) => group.add(Number(v)));
|
||||
edges.get(idx).forEach((n) => toVisit.push(n));
|
||||
}
|
||||
groups.push(group);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
const encodeText = textEncoder.encode.bind(textEncoder);
|
||||
const getChecksum = (values) => {
|
||||
const res = new Uint8Array(values.length * 8);
|
||||
const dv = new DataView(res.buffer);
|
||||
for (let i = 0; i < values.length; i++) dv.setBigUint64(i * 8, values[i]);
|
||||
return scale.h64(res);
|
||||
};
|
||||
const getStringChecksum = (values) => getChecksum(values.map((v) => scale.h64(encodeText(v))));
|
||||
const shapeIds = {
|
||||
primitive: 0n,
|
||||
vector: 1n,
|
||||
tuple: 2n,
|
||||
struct: 3n,
|
||||
option: 4n,
|
||||
result: 5n,
|
||||
enum: 6n,
|
||||
void: 7n
|
||||
};
|
||||
const runtimePrimitiveIds = {
|
||||
undefined: 0n,
|
||||
number: 1n,
|
||||
string: 2n,
|
||||
bigint: 3n,
|
||||
boolean: 4n,
|
||||
bitSequence: 5n,
|
||||
// {bitsLen: number, bytes: Uint8Array}
|
||||
byteSequence: 6n,
|
||||
// Binary
|
||||
accountId32: 7n,
|
||||
// SS58String
|
||||
accountId20: 8n
|
||||
// EthAccount
|
||||
};
|
||||
const metadataPrimitiveIds = {
|
||||
bool: runtimePrimitiveIds.boolean,
|
||||
char: runtimePrimitiveIds.string,
|
||||
str: runtimePrimitiveIds.string,
|
||||
u8: runtimePrimitiveIds.number,
|
||||
u16: runtimePrimitiveIds.number,
|
||||
u32: runtimePrimitiveIds.number,
|
||||
u64: runtimePrimitiveIds.bigint,
|
||||
u128: runtimePrimitiveIds.bigint,
|
||||
u256: runtimePrimitiveIds.bigint,
|
||||
i8: runtimePrimitiveIds.number,
|
||||
i16: runtimePrimitiveIds.number,
|
||||
i32: runtimePrimitiveIds.number,
|
||||
i64: runtimePrimitiveIds.bigint,
|
||||
i128: runtimePrimitiveIds.bigint,
|
||||
i256: runtimePrimitiveIds.bigint
|
||||
};
|
||||
const structLikeBuilder = (shapeId, input, innerChecksum) => {
|
||||
const sortedEntries = Object.entries(input).sort(
|
||||
([a], [b]) => a.localeCompare(b)
|
||||
);
|
||||
const keysChecksum = getStringChecksum(sortedEntries.map(([key]) => key));
|
||||
const valuesChecksum = getChecksum(
|
||||
sortedEntries.map(([, entry]) => innerChecksum(entry))
|
||||
);
|
||||
return getChecksum([shapeId, keysChecksum, valuesChecksum]);
|
||||
};
|
||||
const _buildChecksum = (input, buildNextChecksum) => {
|
||||
if (input.type === "primitive")
|
||||
return getChecksum([shapeIds.primitive, metadataPrimitiveIds[input.value]]);
|
||||
if (input.type === "void") return getChecksum([shapeIds.void]);
|
||||
if (input.type === "compact")
|
||||
return getChecksum([
|
||||
shapeIds.primitive,
|
||||
runtimePrimitiveIds[input.isBig ? "bigint" : "number"]
|
||||
]);
|
||||
if (input.type === "bitSequence")
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.bitSequence]);
|
||||
if (input.type === "AccountId32") {
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.accountId32]);
|
||||
}
|
||||
if (input.type === "AccountId20") {
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.accountId20]);
|
||||
}
|
||||
const buildVector = (entry, length) => {
|
||||
const innerChecksum = buildNextChecksum(entry);
|
||||
return getChecksum(
|
||||
length !== void 0 ? [shapeIds.vector, innerChecksum, BigInt(length)] : [shapeIds.vector, innerChecksum]
|
||||
);
|
||||
};
|
||||
if (input.type === "array") {
|
||||
const innerValue = input.value;
|
||||
if (innerValue.type === "primitive" && innerValue.value === "u8") {
|
||||
return getChecksum([
|
||||
shapeIds.primitive,
|
||||
runtimePrimitiveIds.byteSequence,
|
||||
BigInt(input.len)
|
||||
]);
|
||||
}
|
||||
return buildVector(innerValue, input.len);
|
||||
}
|
||||
if (input.type === "sequence") {
|
||||
const innerValue = input.value;
|
||||
if (innerValue.type === "primitive" && innerValue.value === "u8") {
|
||||
return getChecksum([shapeIds.primitive, runtimePrimitiveIds.byteSequence]);
|
||||
}
|
||||
return buildVector(innerValue);
|
||||
}
|
||||
const buildTuple = (entries) => getChecksum([shapeIds.tuple, ...entries.map(buildNextChecksum)]);
|
||||
const buildStruct = (entries) => structLikeBuilder(shapeIds.struct, entries, buildNextChecksum);
|
||||
if (input.type === "tuple") return buildTuple(input.value);
|
||||
if (input.type === "struct") return buildStruct(input.value);
|
||||
if (input.type === "option")
|
||||
return getChecksum([shapeIds.option, buildNextChecksum(input.value)]);
|
||||
if (input.type === "result")
|
||||
return getChecksum([
|
||||
shapeIds.result,
|
||||
buildNextChecksum(input.value.ok),
|
||||
buildNextChecksum(input.value.ko)
|
||||
]);
|
||||
return structLikeBuilder(shapeIds.enum, input.value, (entry) => {
|
||||
if (entry.type === "lookupEntry") return buildNextChecksum(entry.value);
|
||||
switch (entry.type) {
|
||||
case "void":
|
||||
return getChecksum([shapeIds.void]);
|
||||
case "tuple":
|
||||
return buildTuple(entry.value);
|
||||
case "struct":
|
||||
return buildStruct(entry.value);
|
||||
case "array":
|
||||
return buildVector(entry.value, entry.len);
|
||||
}
|
||||
});
|
||||
};
|
||||
const sortCyclicGroups = (groups, graph) => {
|
||||
const getReachableNodes = (group) => {
|
||||
const result2 = /* @__PURE__ */ new Set();
|
||||
const toVisit = Array.from(group);
|
||||
while (toVisit.length) {
|
||||
const id = toVisit.pop();
|
||||
if (result2.has(id)) continue;
|
||||
result2.add(id);
|
||||
graph.get(id)?.refs.forEach((id2) => toVisit.push(id2));
|
||||
}
|
||||
return Array.from(result2);
|
||||
};
|
||||
const result = new Array();
|
||||
function dependentsFirst(group) {
|
||||
if (result.includes(group)) return;
|
||||
const dependents = groups.filter(
|
||||
(candidate) => candidate !== group && getReachableNodes(group).some((node) => candidate.has(node))
|
||||
);
|
||||
dependents.forEach((group2) => dependentsFirst(group2));
|
||||
if (result.includes(group)) return;
|
||||
result.push(group);
|
||||
}
|
||||
groups.forEach((group) => dependentsFirst(group));
|
||||
return result;
|
||||
};
|
||||
function iterateChecksums(group, iterations, cache, graph) {
|
||||
const groupReadCache = new Map([...group].map((id) => [id, 0n]));
|
||||
const groupWriteCache = /* @__PURE__ */ new Map();
|
||||
const recursiveBuildChecksum = (entry, skipCache = true) => {
|
||||
if (!skipCache && (groupReadCache.has(entry.id) || cache.has(entry.id))) {
|
||||
return groupReadCache.get(entry.id) ?? cache.get(entry.id);
|
||||
}
|
||||
const result = _buildChecksum(
|
||||
entry,
|
||||
(nextEntry) => recursiveBuildChecksum(nextEntry, false)
|
||||
);
|
||||
if (group.has(entry.id)) {
|
||||
groupWriteCache.set(entry.id, result);
|
||||
} else {
|
||||
cache.set(entry.id, result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
group.forEach((id) => recursiveBuildChecksum(graph.get(id).entry));
|
||||
group.forEach((id) => groupReadCache.set(id, groupWriteCache.get(id)));
|
||||
}
|
||||
return groupReadCache;
|
||||
}
|
||||
function getMirroredNodes(cyclicGroups, graph) {
|
||||
const maxSize = cyclicGroups.reduce(
|
||||
(acc, group) => Math.max(acc, group.size),
|
||||
0
|
||||
);
|
||||
const allEntries = new Set([...graph.values()].map((v) => v.entry.id));
|
||||
const resultingChecksums = iterateChecksums(
|
||||
allEntries,
|
||||
maxSize,
|
||||
// Cache won't be used, since it's using the internal one for every node.
|
||||
/* @__PURE__ */ new Map(),
|
||||
graph
|
||||
);
|
||||
const checksumToNodes = /* @__PURE__ */ new Map();
|
||||
for (const id of allEntries) {
|
||||
const checksum = resultingChecksums.get(id);
|
||||
if (checksum == void 0) throw new Error("Unreachable");
|
||||
if (!checksumToNodes.has(checksum)) {
|
||||
checksumToNodes.set(checksum, []);
|
||||
}
|
||||
checksumToNodes.get(checksum).push(id);
|
||||
}
|
||||
const checksumsWithDuplicates = [...checksumToNodes.entries()].filter(
|
||||
([, nodes]) => nodes.length > 1
|
||||
);
|
||||
const duplicatesMap = {};
|
||||
checksumsWithDuplicates.forEach(([, nodes]) => {
|
||||
nodes.forEach((n) => duplicatesMap[n] = nodes);
|
||||
});
|
||||
return duplicatesMap;
|
||||
}
|
||||
const buildChecksum = (entry, cache, graph) => {
|
||||
if (cache.has(entry.id)) return cache.get(entry.id);
|
||||
const subGraph = getSubgraph(entry.id, graph);
|
||||
const cycles = getStronglyConnectedComponents(subGraph).filter(
|
||||
// SCCs can be of length=1, but for those we're only interested with those that are circular with themselves
|
||||
(group) => group.size > 1 || isSelfCircular(group, subGraph)
|
||||
);
|
||||
const cyclicGroups = mergeSCCsWithCommonNodes(cycles).filter((group) => {
|
||||
return !cache.has(group.values().next().value);
|
||||
});
|
||||
const mirrored = getMirroredNodes(cyclicGroups, subGraph);
|
||||
const sortedCyclicGroups = sortCyclicGroups(cyclicGroups, subGraph);
|
||||
sortedCyclicGroups.forEach((group) => {
|
||||
if (cache.has(group.values().next().value)) {
|
||||
return;
|
||||
}
|
||||
const result = iterateChecksums(group, group.size, cache, graph);
|
||||
group.forEach((id) => {
|
||||
const checksum = result.get(id);
|
||||
if (id in mirrored) {
|
||||
mirrored[id].forEach((id2) => cache.set(id2, checksum));
|
||||
} else {
|
||||
cache.set(id, checksum);
|
||||
}
|
||||
});
|
||||
});
|
||||
const getChecksum2 = (entry2) => {
|
||||
if (cache.has(entry2.id)) return cache.get(entry2.id);
|
||||
return _buildChecksum(entry2, getChecksum2);
|
||||
};
|
||||
return getChecksum2(entry);
|
||||
};
|
||||
const isSelfCircular = (group, graph) => {
|
||||
if (group.size !== 1) return false;
|
||||
const [id] = group;
|
||||
return graph.get(id).refs.has(id);
|
||||
};
|
||||
const getChecksumBuilder = (getLookupEntryDef) => {
|
||||
const { metadata } = getLookupEntryDef;
|
||||
const graph = buildLookupGraph(getLookupEntryDef, metadata.lookup.length);
|
||||
const cache = /* @__PURE__ */ new Map();
|
||||
const buildDefinition = (id) => buildChecksum(getLookupEntryDef(id), cache, graph);
|
||||
const buildStorage = (pallet, entry) => {
|
||||
try {
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).storage.items.find((s) => s.name === entry);
|
||||
if (storageEntry.type.tag === "plain")
|
||||
return buildDefinition(storageEntry.type.value);
|
||||
const { key, value } = storageEntry.type.value;
|
||||
const val = buildDefinition(value);
|
||||
const returnKey = buildDefinition(key);
|
||||
return getChecksum([val, returnKey]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildViewFns = (pallet, entry) => {
|
||||
try {
|
||||
const viewFn = metadata.pallets.find((x) => x.name === pallet)?.viewFns.find((x) => x.name === entry);
|
||||
if (!viewFn) throw null;
|
||||
const argNamesChecksum = getStringChecksum(
|
||||
viewFn.inputs.map((x) => x.name)
|
||||
);
|
||||
const argValuesChecksum = getChecksum(
|
||||
viewFn.inputs.map((x) => buildDefinition(x.type))
|
||||
);
|
||||
const outputChecksum = buildDefinition(viewFn.output);
|
||||
return getChecksum([argNamesChecksum, argValuesChecksum, outputChecksum]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildRuntimeCall = (api, method) => {
|
||||
try {
|
||||
const entry = metadata.apis.find((x) => x.name === api)?.methods.find((x) => x.name === method);
|
||||
if (!entry) throw null;
|
||||
const argNamesChecksum = getStringChecksum(
|
||||
entry.inputs.map((x) => x.name)
|
||||
);
|
||||
const argValuesChecksum = getChecksum(
|
||||
entry.inputs.map((x) => buildDefinition(x.type))
|
||||
);
|
||||
const outputChecksum = buildDefinition(entry.output);
|
||||
return getChecksum([argNamesChecksum, argValuesChecksum, outputChecksum]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildComposite = (input) => {
|
||||
if (input.type === "void") return getChecksum([0n]);
|
||||
if (input.type === "tuple") {
|
||||
const values = Object.values(input.value).map(
|
||||
(entry) => buildDefinition(entry.id)
|
||||
);
|
||||
return getChecksum([shapeIds.tuple, ...values]);
|
||||
}
|
||||
if (input.type === "array") {
|
||||
return getChecksum([
|
||||
shapeIds.vector,
|
||||
buildDefinition(input.value.id),
|
||||
BigInt(input.len)
|
||||
]);
|
||||
}
|
||||
return structLikeBuilder(
|
||||
shapeIds.struct,
|
||||
input.value,
|
||||
(entry) => buildDefinition(entry.id)
|
||||
);
|
||||
};
|
||||
const buildNamedTuple = (input) => {
|
||||
return structLikeBuilder(
|
||||
shapeIds.tuple,
|
||||
input.value,
|
||||
(entry) => buildDefinition(entry.id)
|
||||
);
|
||||
};
|
||||
const variantShapeId = {
|
||||
errors: 1n,
|
||||
events: 2n,
|
||||
calls: 3n
|
||||
};
|
||||
const buildVariant = (variantType) => (pallet, name) => {
|
||||
try {
|
||||
const palletEntry = metadata.pallets.find((x) => x.name === pallet);
|
||||
const enumLookup = getLookupEntryDef(palletEntry[variantType].type);
|
||||
buildDefinition(enumLookup.id);
|
||||
if (enumLookup.type !== "enum") throw null;
|
||||
const entry = enumLookup.value[name];
|
||||
const valueChecksum = entry.type === "lookupEntry" ? buildDefinition(entry.value.id) : buildComposite(entry);
|
||||
return getChecksum([variantShapeId[variantType], valueChecksum]);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const buildConstant = (pallet, constantName) => {
|
||||
try {
|
||||
const storageEntry = metadata.pallets.find((x) => x.name === pallet).constants.find((s) => s.name === constantName);
|
||||
return buildDefinition(storageEntry.type);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const toStringEnhancer = (fn) => (...args) => fn(...args)?.toString(32) ?? null;
|
||||
return {
|
||||
buildDefinition: toStringEnhancer(buildDefinition),
|
||||
buildRuntimeCall: toStringEnhancer(buildRuntimeCall),
|
||||
buildStorage: toStringEnhancer(buildStorage),
|
||||
buildViewFns: toStringEnhancer(buildViewFns),
|
||||
buildCall: toStringEnhancer(buildVariant("calls")),
|
||||
buildEvent: toStringEnhancer(buildVariant("events")),
|
||||
buildError: toStringEnhancer(buildVariant("errors")),
|
||||
buildConstant: toStringEnhancer(buildConstant),
|
||||
buildComposite: toStringEnhancer(buildComposite),
|
||||
buildNamedTuple: toStringEnhancer(buildNamedTuple),
|
||||
getAllGeneratedChecksums: () => Array.from(cache.values()).map((v) => v.toString(32))
|
||||
};
|
||||
};
|
||||
|
||||
exports.denormalizeLookup = denormalizeLookup;
|
||||
exports.getChecksumBuilder = getChecksumBuilder;
|
||||
exports.getDynamicBuilder = getDynamicBuilder;
|
||||
exports.getLookupCodecBuilder = getLookupCodecBuilder;
|
||||
exports.getLookupFn = getLookupFn;
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@pezkuwi/metadata-builders",
|
||||
"version": "1.0.0",
|
||||
"author": "Dijital Kurdistan Tech Institute <info@pezkuwichain.io>",
|
||||
"bugs": "https://github.com/pezkuwichain/pezkuwi-api/issues",
|
||||
"homepage": "https://github.com/pezkuwichain/pezkuwi-api/tree/master/packages/metadata-builders#readme",
|
||||
"repository": {
|
||||
"directory": "packages/metadata-builders",
|
||||
"type": "git",
|
||||
"url": "https://github.com/pezkuwichain/pezkuwi-api.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"node": {
|
||||
"production": {
|
||||
"import": "./dist/esm/index.mjs",
|
||||
"require": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"import": "./dist/esm/index.mjs",
|
||||
"require": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"module": "./dist/esm/index.mjs",
|
||||
"import": "./dist/esm/index.mjs",
|
||||
"require": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/esm/index.mjs",
|
||||
"browser": "./dist/esm/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@pezkuwi/bizinikiwi-bindings": "1.0.0",
|
||||
"@pezkuwi/papi-utils": "1.0.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user