diff --git a/packages/api-contract/src/Abi.ts b/packages/api-contract/src/Abi.ts index 25593761a6..7d4df9a13a 100644 --- a/packages/api-contract/src/Abi.ts +++ b/packages/api-contract/src/Abi.ts @@ -1,9 +1,9 @@ // Copyright 2017-2020 @polkadot/api-contract authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { AnyJson } from '@polkadot/types/types'; -import { ChainProperties, ContractConstructorSpec, ContractMessageSpec, ContractProject } from '@polkadot/types/interfaces'; -import { AbiConstructor, AbiMessage, AbiMessageParam } from './types'; +import { AnyJson, Codec } from '@polkadot/types/types'; +import { ChainProperties, ContractConstructorSpec, ContractEventSpec, ContractMessageSpec, ContractMessageParamSpec, ContractProject } from '@polkadot/types/interfaces'; +import { AbiConstructor, AbiEvent, AbiMessage, AbiParam, DecodedEvent } from './types'; import { assert, isNumber, isObject, isString, stringCamelCase } from '@polkadot/util'; @@ -22,6 +22,8 @@ function findMessage (list: T[], messageOrId: T | string } export default class Abi { + readonly #events: AbiEvent[]; + public readonly constructors: AbiConstructor[]; public readonly json: AnyJson; @@ -47,14 +49,17 @@ export default class Abi { this.project.types.forEach((_, index) => this.registry.getMetaTypeDef(this.registry.createType('SiLookupTypeId', index + 1))); this.constructors = this.project.spec.constructors.map((spec: ContractConstructorSpec, index) => - this.#createBase(spec, index, { + this.#createMessage(spec, index, { isConstructor: true }) ); + this.#events = this.project.spec.events.map((spec: ContractEventSpec, index) => + this.#createEvent(spec, index) + ); this.messages = this.project.spec.messages.map((spec: ContractMessageSpec, index): AbiMessage => { const typeSpec = spec.returnType.unwrapOr(null); - return this.#createBase(spec, index, { + return this.#createMessage(spec, index, { isMutating: spec.mutates.isTrue, isPayable: spec.payable.isTrue, returnType: typeSpec @@ -64,6 +69,29 @@ export default class Abi { }); } + /** + * Warning: Unstable API, bound to change + */ + public decodeEvent (eventU8a: Uint8Array): DecodedEvent { + const index = eventU8a[0]; + const event = this.#events[index]; + + assert(event, `Unable to find event with index ${index}`); + + let offset = 1; + + return { + args: event.args.map(({ type }): Codec => { + const value = this.registry.createType(type.type as 'Text', eventU8a.subarray(offset)); + + offset += value.encodedLength; + + return value; + }), + event + }; + } + public findConstructor (constructorOrId: AbiConstructor | string | number): AbiConstructor { return findMessage(this.constructors, constructorOrId); } @@ -72,9 +100,8 @@ export default class Abi { return findMessage(this.messages, messageOrId); } - #createBase = (spec: ContractMessageSpec | ContractConstructorSpec, index: number, add: Partial = {}): AbiMessage => { - const identifier = spec.name.toString(); - const args = spec.args.map((arg, index): AbiMessageParam => { + #createArgs = (args: ContractMessageParamSpec[], spec: unknown): AbiParam[] => { + return args.map((arg, index): AbiParam => { try { assert(isObject(arg.type), 'Invalid type definition found'); @@ -88,12 +115,23 @@ export default class Abi { throw error; } }); + } + #createEvent = (spec: ContractEventSpec, index: number): AbiEvent => { + return { + args: this.#createArgs(spec.args, spec), + docs: spec.docs.map((doc) => doc.toString()), + identifier: spec.name.toString(), + index + }; + } + + #createMessage = (spec: ContractMessageSpec | ContractConstructorSpec, index: number, add: Partial = {}): AbiMessage => { return { ...add, - args, + args: this.#createArgs(spec.args, spec), docs: spec.docs.map((doc) => doc.toString()), - identifier, + identifier: spec.name.toString(), index, selector: spec.selector }; diff --git a/packages/api-contract/src/types.ts b/packages/api-contract/src/types.ts index 8d86c47266..0f067f39c4 100644 --- a/packages/api-contract/src/types.ts +++ b/packages/api-contract/src/types.ts @@ -16,13 +16,25 @@ export interface ContractBase { messages: AbiMessage[]; } -export interface AbiMessageParam { +export interface AbiParam { name: string; type: TypeDef; } +export interface AbiEvent { + args: AbiParam[]; + docs: string[]; + identifier: string; + index: number; +} + +export interface DecodedEvent { + args: Codec[]; + event: AbiEvent; +} + export interface AbiMessage { - args: AbiMessageParam[]; + args: AbiParam[]; docs: string[]; identifier: string; index: number;