Adjust type encoding (#3805)
* Adjust type encoding * Adjust * displayName adjustments
This commit is contained in:
@@ -20,7 +20,7 @@ export function paramsNotation <T> (outer: string, inner?: T | T[], transform: (
|
||||
}`;
|
||||
}
|
||||
|
||||
function encodeWithParams (registry: Registry, typeDef: TypeDef, lookupCounter: number, outer: string): string {
|
||||
function encodeWithParams (registry: Registry, typeDef: TypeDef, outer: string): string {
|
||||
const { info, sub } = typeDef;
|
||||
|
||||
switch (info) {
|
||||
@@ -32,25 +32,25 @@ function encodeWithParams (registry: Registry, typeDef: TypeDef, lookupCounter:
|
||||
case TypeDefInfo.Option:
|
||||
case TypeDefInfo.Result:
|
||||
case TypeDefInfo.Vec:
|
||||
return paramsNotation(outer, sub, (param) => encodeTypeDef(registry, param, lookupCounter));
|
||||
return paramsNotation(outer, sub, (param) => encodeTypeDef(registry, param));
|
||||
}
|
||||
|
||||
throw new Error(`Unable to encode ${stringify(typeDef)} with params`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function encodeDoNotConstruct (registry: Registry, { displayName }: TypeDef, lookupCounter: number): string {
|
||||
function encodeDoNotConstruct (registry: Registry, { displayName }: TypeDef): string {
|
||||
return `DoNotConstruct<${displayName || 'Unknown'}>`;
|
||||
}
|
||||
|
||||
function encodeSubTypes (registry: Registry, sub: TypeDef[], lookupCounter: number, asEnum?: boolean): string {
|
||||
function encodeSubTypes (registry: Registry, sub: TypeDef[], asEnum?: boolean): string {
|
||||
const names = sub.map(({ name }) => name);
|
||||
|
||||
assert(names.every((n) => !!n), () => `Subtypes does not have consistent names, ${names.join(', ')}`);
|
||||
|
||||
const inner = sub.reduce< Record<string, string>>((result, type) => ({
|
||||
...result,
|
||||
[type.name as string]: encodeTypeDef(registry, type, lookupCounter)
|
||||
[type.name as string]: encodeTypeDef(registry, type)
|
||||
}), {});
|
||||
|
||||
return stringify(
|
||||
@@ -60,7 +60,7 @@ function encodeSubTypes (registry: Registry, sub: TypeDef[], lookupCounter: numb
|
||||
);
|
||||
}
|
||||
|
||||
function encodeEnum (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
|
||||
function encodeEnum (registry: Registry, typeDef: TypeDef): string {
|
||||
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Enum type');
|
||||
|
||||
const sub = typeDef.sub;
|
||||
@@ -69,29 +69,29 @@ function encodeEnum (registry: Registry, typeDef: TypeDef, lookupCounter: number
|
||||
// TODO We need to take the disciminant into account and auto-add empty entries
|
||||
return sub.every(({ type }) => type === 'Null')
|
||||
? stringify({ _enum: sub.map(({ name }, index) => `${name || `Empty${index}`}`) })
|
||||
: encodeSubTypes(registry, sub, lookupCounter, true);
|
||||
: encodeSubTypes(registry, sub, true);
|
||||
}
|
||||
|
||||
function encodeStruct (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
|
||||
function encodeStruct (registry: Registry, typeDef: TypeDef): string {
|
||||
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Struct type');
|
||||
|
||||
return encodeSubTypes(registry, typeDef.sub, lookupCounter);
|
||||
return encodeSubTypes(registry, typeDef.sub);
|
||||
}
|
||||
|
||||
function encodeTuple (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
|
||||
function encodeTuple (registry: Registry, typeDef: TypeDef): string {
|
||||
assert(typeDef.sub && Array.isArray(typeDef.sub), 'Unable to encode Tuple type');
|
||||
|
||||
return `(${typeDef.sub.map((type) => encodeTypeDef(registry, type, lookupCounter)).join(',')})`;
|
||||
return `(${typeDef.sub.map((type) => encodeTypeDef(registry, type)).join(',')})`;
|
||||
}
|
||||
|
||||
function encodeUInt (registry: Registry, { length }: TypeDef, lookupCounter: number, type: 'Int' | 'UInt'): string {
|
||||
function encodeUInt (registry: Registry, { length }: TypeDef, type: 'Int' | 'UInt'): string {
|
||||
assert(isNumber(length), 'Unable to encode VecFixed type');
|
||||
|
||||
return `${type}<${length}>`;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function encodeVecFixed (registry: Registry, { length, sub }: TypeDef, lookupCounter: number): string {
|
||||
function encodeVecFixed (registry: Registry, { length, sub }: TypeDef): string {
|
||||
assert(isNumber(length) && !isUndefined(sub) && !Array.isArray(sub), 'Unable to encode VecFixed type');
|
||||
|
||||
return `[${sub.type};${length}]`;
|
||||
@@ -99,62 +99,60 @@ function encodeVecFixed (registry: Registry, { length, sub }: TypeDef, lookupCou
|
||||
|
||||
// We setup a record here to ensure we have comprehensive coverage (any item not covered will result
|
||||
// in a compile-time error with the missing index)
|
||||
const encoders: Record<TypeDefInfo, (registry: Registry, typeDef: TypeDef, lookupCounter: number) => string> = {
|
||||
[TypeDefInfo.BTreeMap]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'BTreeMap'),
|
||||
[TypeDefInfo.BTreeSet]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'BTreeSet'),
|
||||
[TypeDefInfo.Compact]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'Compact'),
|
||||
[TypeDefInfo.DoNotConstruct]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeDoNotConstruct(registry, typeDef, lookupCounter),
|
||||
[TypeDefInfo.Enum]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeEnum(registry, typeDef, lookupCounter),
|
||||
[TypeDefInfo.HashMap]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'HashMap'),
|
||||
[TypeDefInfo.Int]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeUInt(registry, typeDef, lookupCounter, 'Int'),
|
||||
[TypeDefInfo.Linkage]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'Linkage'),
|
||||
const encoders: Record<TypeDefInfo, (registry: Registry, typeDef: TypeDef) => string> = {
|
||||
[TypeDefInfo.BTreeMap]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'BTreeMap'),
|
||||
[TypeDefInfo.BTreeSet]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'BTreeSet'),
|
||||
[TypeDefInfo.Compact]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'Compact'),
|
||||
[TypeDefInfo.DoNotConstruct]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeDoNotConstruct(registry, typeDef),
|
||||
[TypeDefInfo.Enum]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeEnum(registry, typeDef),
|
||||
[TypeDefInfo.HashMap]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'HashMap'),
|
||||
[TypeDefInfo.Int]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeUInt(registry, typeDef, 'Int'),
|
||||
[TypeDefInfo.Linkage]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'Linkage'),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
[TypeDefInfo.Null]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
[TypeDefInfo.Null]: (registry: Registry, typeDef: TypeDef) =>
|
||||
'Null',
|
||||
[TypeDefInfo.Option]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'Option'),
|
||||
[TypeDefInfo.Option]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'Option'),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
[TypeDefInfo.Plain]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
[TypeDefInfo.Plain]: (registry: Registry, typeDef: TypeDef) =>
|
||||
typeDef.displayName || typeDef.type,
|
||||
[TypeDefInfo.Result]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'Result'),
|
||||
[TypeDefInfo.Result]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'Result'),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
[TypeDefInfo.Set]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
[TypeDefInfo.Set]: (registry: Registry, typeDef: TypeDef) =>
|
||||
typeDef.type,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
[TypeDefInfo.Si]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
[TypeDefInfo.Si]: (registry: Registry, typeDef: TypeDef) =>
|
||||
typeDef.lookupName || typeDef.type,
|
||||
[TypeDefInfo.Struct]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeStruct(registry, typeDef, lookupCounter),
|
||||
[TypeDefInfo.Tuple]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeTuple(registry, typeDef, lookupCounter),
|
||||
[TypeDefInfo.UInt]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeUInt(registry, typeDef, lookupCounter, 'UInt'),
|
||||
[TypeDefInfo.Vec]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeWithParams(registry, typeDef, lookupCounter, 'Vec'),
|
||||
[TypeDefInfo.VecFixed]: (registry: Registry, typeDef: TypeDef, lookupCounter: number) =>
|
||||
encodeVecFixed(registry, typeDef, lookupCounter)
|
||||
[TypeDefInfo.Struct]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeStruct(registry, typeDef),
|
||||
[TypeDefInfo.Tuple]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeTuple(registry, typeDef),
|
||||
[TypeDefInfo.UInt]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeUInt(registry, typeDef, 'UInt'),
|
||||
[TypeDefInfo.Vec]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeWithParams(registry, typeDef, 'Vec'),
|
||||
[TypeDefInfo.VecFixed]: (registry: Registry, typeDef: TypeDef) =>
|
||||
encodeVecFixed(registry, typeDef)
|
||||
};
|
||||
|
||||
function encodeType (registry: Registry, typeDef: TypeDef, lookupCounter: number): string {
|
||||
function encodeType (registry: Registry, typeDef: TypeDef): string {
|
||||
const encoder = encoders[typeDef.info];
|
||||
|
||||
assert(encoder, () => `Cannot encode type ${stringify(typeDef)}`);
|
||||
|
||||
return lookupCounter < 0 && isNumber(typeDef.lookupIndex)
|
||||
? typeDef.lookupName || registry.createLookupType(typeDef.lookupIndex)
|
||||
: encoder(registry, typeDef, lookupCounter--);
|
||||
return typeDef.lookupName || encoder(registry, typeDef);
|
||||
}
|
||||
|
||||
export function encodeTypeDef (registry: Registry, typeDef: TypeDef, lookupCounter = 0): string {
|
||||
export function encodeTypeDef (registry: Registry, typeDef: TypeDef): string {
|
||||
assert(!isUndefined(typeDef.info), () => `Invalid type definition with no instance info, typeDef=${stringify(typeDef)}`);
|
||||
|
||||
// In the case of contracts we do have the unfortunate situation where the displayName would
|
||||
@@ -164,12 +162,17 @@ export function encodeTypeDef (registry: Registry, typeDef: TypeDef, lookupCount
|
||||
return typeDef.displayName;
|
||||
}
|
||||
|
||||
return encodeType(registry, typeDef, lookupCounter);
|
||||
return encodeType(registry, typeDef);
|
||||
}
|
||||
|
||||
export function withTypeString (registry: Registry, typeDef: Omit<TypeDef, 'type'>, lookupCounter = 0): TypeDef {
|
||||
export function withTypeString (registry: Registry, typeDef: Omit<TypeDef, 'type'>): TypeDef {
|
||||
const partial = { ...typeDef } as TypeDef;
|
||||
|
||||
// for the outer-most type, we ignore the lookupName
|
||||
delete partial.lookupName;
|
||||
|
||||
return {
|
||||
...typeDef,
|
||||
type: encodeType(registry, typeDef as TypeDef, lookupCounter)
|
||||
type: encodeType(registry, partial)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,11 +23,18 @@ const PRIMITIVE_INK = ['AccountId', 'AccountIndex', 'Address', 'Balance'];
|
||||
|
||||
// These are types where we have a specific decoding/encoding override + helpers
|
||||
const PRIMITIVE_SP = [
|
||||
'BTreeMap',
|
||||
'BTreeSet',
|
||||
'HashMap',
|
||||
'HashSet',
|
||||
'sp_core::crypto::AccountId32',
|
||||
'sp_runtime::generic::era::Era',
|
||||
'sp_runtime::multiaddress::MultiAddress',
|
||||
'pallet_democracy::vote::Vote',
|
||||
'pallet_identity::types::Data'
|
||||
'pallet_identity::types::Data',
|
||||
'primitive_types::H160',
|
||||
'primitive_types::H256',
|
||||
'primitive_types::H512'
|
||||
];
|
||||
|
||||
function removeDuplicateNames (names: (string | null)[]): (string | null)[] {
|
||||
@@ -53,7 +60,7 @@ function extractNames (types: PortableType[]): Record<number, string> {
|
||||
|
||||
const typeName = parts.join('');
|
||||
|
||||
return ['BTreeMap', 'Cow', 'Result', 'Option'].includes(typeName) || ['frame_support'].includes(path[0].toString())
|
||||
return ['Cow', 'Result', 'Option'].includes(typeName) || ['frame_support'].includes(path[0].toString())
|
||||
? null
|
||||
: typeName;
|
||||
})
|
||||
@@ -126,15 +133,15 @@ export class GenericPortableRegistry extends Struct {
|
||||
const typeDef = this.getTypeDef(lookupId);
|
||||
const lookupIndex = lookupId.toNumber();
|
||||
|
||||
// Flatten Plain types immediately, otherwise setup for a lookup
|
||||
return typeDef.info === TypeDefInfo.Plain
|
||||
? typeDef
|
||||
: {
|
||||
// Setup for a lookup on complex types
|
||||
return [TypeDefInfo.Enum, TypeDefInfo.Struct].includes(typeDef.info)
|
||||
? {
|
||||
info: TypeDefInfo.Si,
|
||||
lookupIndex,
|
||||
lookupName: this.#names[lookupIndex],
|
||||
type: this.registry.createLookupType(lookupId)
|
||||
};
|
||||
}
|
||||
: typeDef;
|
||||
}
|
||||
|
||||
#getLookupId (lookupId: SiLookupTypeId | string | number): number {
|
||||
@@ -190,17 +197,8 @@ export class GenericPortableRegistry extends Struct {
|
||||
throw new Error(`PortableRegistry: Error extracting ${stringify(type)}: ${(error as Error).message}`);
|
||||
}
|
||||
|
||||
const displayName = path.pop()?.toString();
|
||||
|
||||
return {
|
||||
...(displayName
|
||||
? { displayName }
|
||||
: {}
|
||||
),
|
||||
...(path.length > 1
|
||||
? { namespace: path.map((s) => s.toString()).join('::') }
|
||||
: {}
|
||||
),
|
||||
namespace: path.join('::'),
|
||||
...typeDef
|
||||
};
|
||||
}
|
||||
@@ -226,7 +224,7 @@ export class GenericPortableRegistry extends Struct {
|
||||
return this.#extractFields(lookupIndex, fields);
|
||||
}
|
||||
|
||||
#extractFields (_: number, fields: SiField[]): TypeDef {
|
||||
#extractFields (lookupIndex: number, fields: SiField[]): TypeDef {
|
||||
const [isStruct, isTuple] = fields.reduce(([isAllNamed, isAllUnnamed], { name }) => ([
|
||||
isAllNamed && name.isSome,
|
||||
isAllUnnamed && name.isNone
|
||||
@@ -262,14 +260,24 @@ export class GenericPortableRegistry extends Struct {
|
||||
info: isTuple // Tuple check first
|
||||
? TypeDefInfo.Tuple
|
||||
: TypeDefInfo.Struct,
|
||||
...(
|
||||
lookupIndex === -1
|
||||
? {}
|
||||
: {
|
||||
lookupIndex,
|
||||
lookupName: this.#names[lookupIndex]
|
||||
}
|
||||
),
|
||||
sub
|
||||
});
|
||||
}
|
||||
|
||||
#extractHistoric (_: number, type: Type): TypeDef {
|
||||
const inner = getTypeDef(type);
|
||||
|
||||
return { ...inner, displayName: type.toString(), isFromSi: true };
|
||||
return {
|
||||
...getTypeDef(type),
|
||||
displayName: type.toString(),
|
||||
isFromSi: true
|
||||
};
|
||||
}
|
||||
|
||||
#extractPrimitive (_: number, type: SiType): TypeDef {
|
||||
@@ -282,22 +290,27 @@ export class GenericPortableRegistry extends Struct {
|
||||
}
|
||||
|
||||
#extractPrimitivePath (_: number, type: SiType): TypeDef {
|
||||
const displayName = type.path[type.path.length - 1].toString();
|
||||
|
||||
return {
|
||||
displayName,
|
||||
info: TypeDefInfo.Plain,
|
||||
type: type.path[type.path.length - 1].toString()
|
||||
type: displayName
|
||||
};
|
||||
}
|
||||
|
||||
#extractSequence (_: number, { type }: SiTypeDefSequence): TypeDef {
|
||||
#extractSequence (lookupIndex: number, { type }: SiTypeDefSequence): TypeDef {
|
||||
const sub = this.#createSiDef(type);
|
||||
|
||||
return withTypeString(this.registry, {
|
||||
info: TypeDefInfo.Vec,
|
||||
lookupIndex,
|
||||
lookupName: this.#names[lookupIndex],
|
||||
sub
|
||||
});
|
||||
}
|
||||
|
||||
#extractTuple (_: number, ids: SiTypeDefTuple): TypeDef {
|
||||
#extractTuple (lookupIndex: number, ids: SiTypeDefTuple): TypeDef {
|
||||
if (ids.length === 0) {
|
||||
return {
|
||||
info: TypeDefInfo.Null,
|
||||
@@ -311,6 +324,8 @@ export class GenericPortableRegistry extends Struct {
|
||||
|
||||
return withTypeString(this.registry, {
|
||||
info: TypeDefInfo.Tuple,
|
||||
lookupIndex,
|
||||
lookupName: this.#names[lookupIndex],
|
||||
sub
|
||||
});
|
||||
}
|
||||
@@ -360,7 +375,7 @@ export class GenericPortableRegistry extends Struct {
|
||||
}
|
||||
|
||||
sub.push({
|
||||
...this.#extractFields(lookupIndex, fields),
|
||||
...this.#extractFields(-1, fields),
|
||||
index: index.toNumber(),
|
||||
name: name.toString()
|
||||
});
|
||||
@@ -368,6 +383,8 @@ export class GenericPortableRegistry extends Struct {
|
||||
|
||||
return withTypeString(this.registry, {
|
||||
info: TypeDefInfo.Enum,
|
||||
lookupIndex,
|
||||
lookupName: this.#names[lookupIndex],
|
||||
sub
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user