Support for ink! metadata v3 (#4432)

* Support for ink! metadata v3

* Flatten check for V1-V3

* DRY-er is/as conversions

* Test for v2 conversion

* Remove stray console.log

* Generic converter

* Cleanups
This commit is contained in:
Jaco
2022-01-18 07:27:10 +02:00
committed by GitHub
parent 91989e1a2b
commit dfbfaf12eb
7 changed files with 124 additions and 28 deletions
+11 -15
View File
@@ -10,7 +10,7 @@ import { TypeRegistry } from '@polkadot/types';
import { TypeDefInfo } from '@polkadot/types-create';
import { assert, assertReturn, compactAddLength, compactStripLength, isNumber, isObject, isString, logger, stringCamelCase, stringify, u8aConcat, u8aToHex } from '@polkadot/util';
import { v0ToLatest, v1ToLatest } from './toLatest';
import { convertVersions, enumVersions } from './toLatest';
const l = logger('Abi');
@@ -26,23 +26,18 @@ function findMessage <T extends AbiMessage> (list: T[], messageOrId: T | string
return assertReturn(message, () => `Attempted to call an invalid contract interface, ${stringify(messageOrId)}`);
}
// FIXME: This is still workable with V0, V1 & V2, but certainly is not a scalable
// approach (right at this point don't quite have better ideas that is not as complex
// as the conversion tactics in the runtime Metadata)
function getLatestMeta (registry: Registry, json: Record<string, unknown>): ContractMetadataLatest {
const vx = enumVersions.find((v) => isObject(json[v]));
const metadata = registry.createType('ContractMetadata',
isObject(json.V2)
? { V2: json.V2 }
: isObject(json.V1)
? { V1: json.V1 }
: { V0: json }
vx
? { [vx]: json[vx] }
: { V0: json }
) as unknown as ContractMetadata;
const converter = convertVersions.find(([v]) => metadata[`is${v}`]);
return metadata.isV2
? metadata.asV2
: metadata.isV1
? v1ToLatest(registry, metadata.asV1)
: v0ToLatest(registry, metadata.asV0);
assert(converter, () => `Unable to convert ABI with version ${metadata.type} to latest`);
return converter[1](registry, metadata[`as${converter[0]}`]);
}
function parseJson (json: Record<string, unknown>, chainProperties?: ChainProperties): [Record<string, unknown>, Registry, ContractMetadataLatest, ContractProjectInfo] {
@@ -90,7 +85,8 @@ export class Abi {
);
this.constructors = this.metadata.spec.constructors.map((spec: ContractConstructorSpecLatest, index) =>
this.#createMessage(spec, index, {
isConstructor: true
isConstructor: true,
isPayable: spec.payable.isTrue
})
);
this.events = this.metadata.spec.events.map((spec: ContractEventSpecLatest, index) =>
+13 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2017-2022 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { v0ToLatest, v1ToLatest } from '@polkadot/api-contract/Abi/toLatest';
import { v0ToLatest, v1ToLatest, v2ToLatest } from '@polkadot/api-contract/Abi/toLatest';
import { TypeRegistry } from '@polkadot/types';
import abis from '../test/contracts';
@@ -71,3 +71,15 @@ describe('v1ToLatest', (): void => {
).toEqual(['init_value']);
});
});
describe('v2ToLatest', (): void => {
const registry = new TypeRegistry();
const contract = registry.createType('ContractMetadata', { V2: abis.ink_v2_flipper.V2 });
const latest = v2ToLatest(registry, contract.asV2);
it('has the correct constructor flag', (): void => {
expect(
latest.spec.constructors[0].payable.isTrue
).toEqual(true);
});
});
+27 -5
View File
@@ -1,16 +1,38 @@
// Copyright 2017-2022 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ContractMetadataLatest, ContractMetadataV0, ContractMetadataV1 } from '@polkadot/types/interfaces';
import type { ContractMetadataLatest, ContractMetadataV3 } from '@polkadot/types/interfaces';
import type { Registry } from '@polkadot/types/types';
import { v0ToV1 } from './toV1';
import { v1ToV2 } from './toV2';
import { v2ToV3 } from './toV3';
export function v1ToLatest (registry: Registry, v1: ContractMetadataV1): ContractMetadataLatest {
return v1ToV2(registry, v1);
type Versions = 'V3' | 'V2' | 'V1' | 'V0';
type Converter = (registry: Registry, vx: any) => ContractMetadataLatest;
// Helper to convert metadata from one step to the next
function createConverter <I, O> (next: (registry: Registry, input: O) => ContractMetadataLatest, step: (registry: Registry, input: I) => O): (registry: Registry, input: I) => ContractMetadataLatest {
return (registry: Registry, input: I): ContractMetadataLatest =>
next(registry, step(registry, input));
}
export function v0ToLatest (registry: Registry, v0: ContractMetadataV0): ContractMetadataLatest {
return v1ToLatest(registry, v0ToV1(registry, v0));
export function v3ToLatest (registry: Registry, v3: ContractMetadataV3): ContractMetadataLatest {
return v3;
}
export const v2ToLatest = createConverter(v3ToLatest, v2ToV3);
export const v1ToLatest = createConverter(v2ToLatest, v1ToV2);
export const v0ToLatest = createConverter(v1ToLatest, v0ToV1);
// The versions where an enum is used, aka V0 is missing
// (Order from newest, i.e. we expect more on newest vs oldest)
export const enumVersions = ['V3', 'V2', 'V1'];
export const convertVersions: [Versions, Converter][] = [
['V3', v3ToLatest],
['V2', v2ToLatest],
['V1', v1ToLatest],
['V0', v0ToLatest]
];
+18
View File
@@ -0,0 +1,18 @@
// Copyright 2017-2022 @polkadot/api-contract authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ContractMetadataV2, ContractMetadataV3 } from '@polkadot/types/interfaces';
import type { Registry } from '@polkadot/types/types';
import { objectSpread } from '@polkadot/util';
export function v2ToV3 (registry: Registry, v2: ContractMetadataV2): ContractMetadataV3 {
return registry.createType('ContractMetadataV3', objectSpread({}, v2, {
spec: objectSpread({}, v2.spec, {
constructors: v2.spec.constructors.map((c) =>
// V3 introduces the payable flag on constructors, for <V3, it is always true
registry.createType('ContractConstructorSpecV3', objectSpread({}, c, { payable: true }))
)
})
}));
}