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:
@@ -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) =>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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]
|
||||
];
|
||||
|
||||
@@ -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 }))
|
||||
)
|
||||
})
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user