@polkadot/types
Version:
Implementation of the Parity codec
200 lines (199 loc) • 6.42 kB
JavaScript
import { Bytes } from '@polkadot/types-codec';
import { isFunction, isString, isU8a } from '@polkadot/util';
import { getSiName } from '../metadata/util/index.js';
import { unwrapStorageType } from '../util/index.js';
const HASHER_MAP = {
// opaque
Blake2_128: [16, false], // eslint-disable-line camelcase
Blake2_128Concat: [16, true], // eslint-disable-line camelcase
Blake2_256: [32, false], // eslint-disable-line camelcase
Identity: [0, true],
Twox128: [16, false],
Twox256: [32, false],
Twox64Concat: [8, true]
};
/** @internal */
function decodeStorageKey(value) {
if (isU8a(value) || !value || isString(value)) {
// let Bytes handle these inputs
return { key: value };
}
else if (value instanceof StorageKey) {
return {
key: value,
method: value.method,
section: value.section
};
}
else if (isFunction(value)) {
return {
key: value(),
method: value.method,
section: value.section
};
}
else if (Array.isArray(value)) {
const [fn, args = []] = value;
if (!isFunction(fn)) {
throw new Error('Expected function input for key construction');
}
if (fn.meta && fn.meta.type.isMap) {
const map = fn.meta.type.asMap;
if (!Array.isArray(args) || args.length !== map.hashers.length) {
throw new Error(`Expected an array of ${map.hashers.length} values as params to a Map query`);
}
}
return {
key: fn(...args),
method: fn.method,
section: fn.section
};
}
throw new Error(`Unable to convert input ${value} to StorageKey`);
}
/** @internal */
function decodeHashers(registry, value, hashers) {
// the storage entry is xxhashAsU8a(prefix, 128) + xxhashAsU8a(method, 128), 256 bits total
let offset = 32;
const count = hashers.length;
const result = new Array(count);
for (let i = 0; i < count; i++) {
const [hasher, type] = hashers[i];
const [hashLen, canDecode] = HASHER_MAP[hasher.type];
const decoded = canDecode
? registry.createTypeUnsafe(getSiName(registry.lookup, type), [value.subarray(offset + hashLen)])
: registry.createTypeUnsafe('Raw', [value.subarray(offset, offset + hashLen)]);
offset += hashLen + (canDecode ? decoded.encodedLength : 0);
result[i] = decoded;
}
return result;
}
/** @internal */
function decodeArgsFromMeta(registry, value, meta) {
if (!meta || !meta.type.isMap) {
return [];
}
const { hashers, key } = meta.type.asMap;
const keys = hashers.length === 1
? [key]
: registry.lookup.getSiType(key).def.asTuple;
return decodeHashers(registry, value, hashers.map((h, i) => [h, keys[i]]));
}
/** @internal */
function getMeta(value) {
if (value instanceof StorageKey) {
return value.meta;
}
else if (isFunction(value)) {
return value.meta;
}
else if (Array.isArray(value)) {
const [fn] = value;
return fn.meta;
}
return undefined;
}
/** @internal */
function getType(registry, value) {
if (value instanceof StorageKey) {
return value.outputType;
}
else if (isFunction(value)) {
return unwrapStorageType(registry, value.meta.type);
}
else if (Array.isArray(value)) {
const [fn] = value;
if (fn.meta) {
return unwrapStorageType(registry, fn.meta.type);
}
}
// If we have no type set, default to Raw
return 'Raw';
}
/**
* @name StorageKey
* @description
* A representation of a storage key (typically hashed) in the system. It can be
* constructed by passing in a raw key or a StorageEntry with (optional) arguments.
*/
export class StorageKey extends Bytes {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This is assigned via this.decodeArgsFromMeta()
__internal__args;
__internal__meta;
__internal__outputType;
__internal__method;
__internal__section;
constructor(registry, value, override = {}) {
const { key, method, section } = decodeStorageKey(value);
super(registry, key);
this.__internal__outputType = getType(registry, value);
// decode the args (as applicable based on the key and the hashers, after all init)
this.setMeta(getMeta(value), override.section || section, override.method || method);
}
/**
* @description Return the decoded arguments (applicable to map with decodable values)
*/
get args() {
return this.__internal__args;
}
/**
* @description The metadata or `undefined` when not available
*/
get meta() {
return this.__internal__meta;
}
/**
* @description The key method or `undefined` when not specified
*/
get method() {
return this.__internal__method;
}
/**
* @description The output type
*/
get outputType() {
return this.__internal__outputType;
}
/**
* @description The key section or `undefined` when not specified
*/
get section() {
return this.__internal__section;
}
is(key) {
return key.section === this.section && key.method === this.method;
}
/**
* @description Sets the meta for this key
*/
setMeta(meta, section, method) {
this.__internal__meta = meta;
this.__internal__method = method || this.__internal__method;
this.__internal__section = section || this.__internal__section;
if (meta) {
this.__internal__outputType = unwrapStorageType(this.registry, meta.type);
}
try {
this.__internal__args = decodeArgsFromMeta(this.registry, this.toU8a(true), meta);
}
catch {
// ignore...
}
return this;
}
/**
* @description Returns the Human representation for this type
*/
toHuman(_isExtended, disableAscii) {
return this.__internal__args.length
? this.__internal__args.map((a) => a.toHuman(undefined, disableAscii))
: super.toHuman(undefined, disableAscii);
}
/**
* @description Returns the raw type for this
*/
toRawType() {
return 'StorageKey';
}
}