mirror of
https://github.com/pezkuwichain/pezkuwi-api.git
synced 2026-04-22 20:47:57 +00:00
31467f90d4
- @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.
1000 lines
34 KiB
JavaScript
1000 lines
34 KiB
JavaScript
'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
|