Skip to content

Commit

Permalink
changes Abi.decodeEvent(data:Bytes) method interface to `Abi.decode…
Browse files Browse the repository at this point in the history
…Event(record:EventRecord)` which includes the event and the topic for decoding.
  • Loading branch information
peetzweg committed Feb 26, 2024
1 parent 54fd609 commit 013ea59
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 56 deletions.
106 changes: 58 additions & 48 deletions packages/api-contract/src/Abi/Abi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import fs from 'node:fs';
import process from 'node:process';

import { TypeDefInfo } from '@polkadot/types/types';
import { hexToU8a } from '@polkadot/util';
import rpcMetadata from '@polkadot/types-support/metadata/static-substrate-contracts-node';
import { blake2AsHex } from '@polkadot/util-crypto';

import { Metadata, TypeRegistry } from '../../../types/src/bundle.js';
import abis from '../test/contracts/index.js';
import { Abi } from './index.js';

Expand Down Expand Up @@ -124,68 +125,77 @@ describe('Abi', (): void => {
expect(bundle.source.hash).toEqual(abi.info.source.wasmHash.toHex());
});

it('decoding <=ink!v4 events', (): void => {
const abi = new Abi(abis['ink_v4_erc20Metadata']);
describe('Events', (): void => {
const registry = new TypeRegistry();

const dataHex = '0x0001d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d018eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800505a4f7e9f4eb10600000000000000';
const dataU8A = hexToU8a(dataHex);
beforeAll((): void => {
const metadata = new Metadata(registry, rpcMetadata);

const decodedEvent = abi.decodeEvent(dataU8A);
registry.setMetadata(metadata);
});

expect(decodedEvent.event.args.length).toEqual(3);
expect(decodedEvent.args.length).toEqual(3);
expect(decodedEvent.event.identifier).toEqual('Transfer');
it('decoding <=ink!v4 events', (): void => {
const abiJson = abis['ink_v4_erc20Metadata'];

const decodedEventHuman = decodedEvent.event.args.reduce((prev, cur, index) => {
return {
...prev,
[cur.name]: decodedEvent.args[index].toHuman()
};
}, {});
expect(abiJson).toBeDefined();
const abi = new Abi(abiJson);

const expectedEvent = {
from: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
to: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
value: '123.4567 MUnit'
};
const eventRecordHex =
'0x0001000000080360951b8baf569bca905a279c12d6ce17db7cdce23a42563870ef585129ce5dc64d010001d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d018eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800505a4f7e9f4eb106000000000000000c0045726332303a3a5472616e7366657200000000000000000000000000000000da2d695d3b5a304e0039e7fc4419c34fa0c1f239189c99bb72a6484f1634782b2b00c7d40fe6d84d660f3e6bed90f218e022a0909f7e1a7ea35ada8b6e003564';
const record = registry.createType('EventRecord', eventRecordHex);

expect(decodedEventHuman).toEqual(expectedEvent);
});
const decodedEvent = abi.decodeEvent(record);

expect(decodedEvent.event.args.length).toEqual(3);
expect(decodedEvent.args.length).toEqual(3);
expect(decodedEvent.event.identifier).toEqual('Transfer');

const decodedEventHuman = decodedEvent.event.args.reduce((prev, cur, index) => {
return {
...prev,
[cur.name]: decodedEvent.args[index].toHuman()
};
}, {});

it('decoding <=ink!v5 events', (): void => {
const metadataJson = abis['ink_v5_erc20Metadata'];
const expectedEvent = {
from: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
to: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
value: '123.4567 MUnit'
};

expect(metadataJson).toBeDefined();
const abi = new Abi(metadataJson);
expect(decodedEventHuman).toEqual(expectedEvent);
});

const dataHex = '0x01d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d018eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800505a4f7e9f4eb10600000000000000';
const dataU8A = hexToU8a(dataHex);
it('decoding <=ink!v5 events', (): void => {
const abiJson = abis['ink_v5_erc20Metadata'];

const signatureTopicHex = '0xb5b61a3e6a21a16be4f044b517c28ac692492f73c5bfd3f60178ad98c767f4cb';
expect(abiJson).toBeDefined();
const abi = new Abi(abiJson);

expect(abi.events.length).toEqual(2);
expect(abi.events[0].signatureTopic).toEqual(signatureTopicHex);
const eventRecordHex =
'0x00010000000803da17150e96b3955a4db6ad35ddeb495f722f9c1d84683113bfb096bf3faa30f2490101d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d018eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4800505a4f7e9f4eb106000000000000000cb5b61a3e6a21a16be4f044b517c28ac692492f73c5bfd3f60178ad98c767f4cbd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48';
const record = registry.createType('EventRecord', eventRecordHex);

const signatureTopicHash = abi.registry.createType('Hash', signatureTopicHex);
const decodedEvent = abi.decodeEvent(dataU8A, signatureTopicHash);
const decodedEvent = abi.decodeEvent(record);

expect(decodedEvent.event.args.length).toEqual(3);
expect(decodedEvent.args.length).toEqual(3);
expect(decodedEvent.event.identifier).toEqual('erc20::erc20::Transfer');
expect(decodedEvent.event.args.length).toEqual(3);
expect(decodedEvent.args.length).toEqual(3);
expect(decodedEvent.event.identifier).toEqual('erc20::erc20::Transfer');

const decodedEventHuman = decodedEvent.event.args.reduce((prev, cur, index) => {
return {
...prev,
[cur.name]: decodedEvent.args[index].toHuman()
};
}, {});
const decodedEventHuman = decodedEvent.event.args.reduce((prev, cur, index) => {
return {
...prev,
[cur.name]: decodedEvent.args[index].toHuman()
};
}, {});

const expectedEvent = {
from: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
to: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
value: '123.4567 MUnit'
};
const expectedEvent = {
from: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
to: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
value: '123.4567 MUnit'
};

expect(decodedEventHuman).toEqual(expectedEvent);
expect(decodedEventHuman).toEqual(expectedEvent);
});
});
});
16 changes: 12 additions & 4 deletions packages/api-contract/src/Abi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import type { Bytes } from '@polkadot/types';
import type { ChainProperties, ContractConstructorSpecLatest, ContractEventSpecLatest, ContractMessageParamSpecLatest, ContractMessageSpecLatest, ContractMetadata, ContractMetadataLatest, ContractProjectInfo, ContractTypeSpec } from '@polkadot/types/interfaces';
import type { Hash } from '@polkadot/types/interfaces/runtime';
import type { ChainProperties, ContractConstructorSpecLatest, ContractEventSpecLatest, ContractMessageParamSpecLatest, ContractMessageSpecLatest, ContractMetadata, ContractMetadataLatest, ContractProjectInfo, ContractTypeSpec, EventRecord } from '@polkadot/types/interfaces';
import type { Codec, Registry, TypeDef } from '@polkadot/types/types';
import type { AbiConstructor, AbiEvent, AbiMessage, AbiParam, DecodedEvent, DecodedMessage } from '../types.js';

Expand Down Expand Up @@ -163,7 +162,12 @@ export class Abi {
/**
* Warning: Unstable API, bound to change
*/
public decodeEvent (data: Bytes | Uint8Array, signatureTopic?: Hash): DecodedEvent {
public decodeEvent (record: EventRecord): DecodedEvent {
// TODO we could double check here if record is actually section `contract` and type `ContractExecution` or `ContractEmitted`
const data = record.event.data[1] as Bytes;

const signatureTopic = record.topics[0];

if (signatureTopic !== undefined) {
const event = this.events.find((e) => e.signatureTopic !== undefined && e.signatureTopic === signatureTopic.toHex());

Expand All @@ -173,7 +177,11 @@ export class Abi {
throw new Error(`Unable to find event with signature_topic ${signatureTopic.toHex()}`);
}
} else {
// otherwise fallback to using the index to determine event - ink! v4 downwards
if (!data) {
throw new Error('Unable to find event data');
}

// otherwise fallback to using the index to determine event - ink! v4 downwards
const index = data[0];

const event = this.events[index];
Expand Down
5 changes: 2 additions & 3 deletions packages/api-contract/src/base/Contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import type { ApiBase } from '@polkadot/api/base';
import type { SubmittableExtrinsic } from '@polkadot/api/submittable/types';
import type { ApiTypes, DecorateMethod } from '@polkadot/api/types';
import type { Bytes } from '@polkadot/types';
import type { AccountId, ContractExecResult, EventRecord, Weight, WeightV2 } from '@polkadot/types/interfaces';
import type { ISubmittableResult } from '@polkadot/types/types';
import type { Abi } from '../Abi/index.js';
Expand Down Expand Up @@ -115,9 +114,9 @@ export class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
// ContractEmitted is the current generation, ContractExecution is the previous generation
new ContractSubmittableResult(result, applyOnEvent(result, ['ContractEmitted', 'ContractExecution'], (records: EventRecord[]) =>
records
.map(({ event: { data: [, data] }, topics }): DecodedEvent | null => {
.map((record): DecodedEvent | null => {
try {
return this.abi.decodeEvent(data as Bytes, topics[0]);
return this.abi.decodeEvent(record);
} catch (error) {
l.error(`Unable to decode contract event: ${(error as Error).message}`);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2017-2024 @polkadot/types-support authors & contributors
// SPDX-License-Identifier: Apache-2.0

import metadata from './v14/substrate-contracts-node-hex.js';
import rpc from './v14/substrate-contracts-node-rpc.js';
import version from './v14/substrate-contracts-node-ver.js';

export { rpc, version };

export default metadata;

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2017-2024 @polkadot/types-support authors & contributors
// SPDX-License-Identifier: Apache-2.0

/* eslint-disable */

// cargo run --release -- purge-chain -y --dev && cargo run --release -- --dev

export default {
"methods": [
"account_nextIndex",
"author_hasKey",
"author_hasSessionKeys",
"author_insertKey",
"author_pendingExtrinsics",
"author_removeExtrinsic",
"author_rotateKeys",
"author_submitAndWatchExtrinsic",
"author_submitExtrinsic",
"author_unwatchExtrinsic",
"chainHead_unstable_body",
"chainHead_unstable_call",
"chainHead_unstable_continue",
"chainHead_unstable_follow",
"chainHead_unstable_genesisHash",
"chainHead_unstable_header",
"chainHead_unstable_stopOperation",
"chainHead_unstable_storage",
"chainHead_unstable_unfollow",
"chainHead_unstable_unpin",
"chain_getBlock",
"chain_getBlockHash",
"chain_getFinalisedHead",
"chain_getFinalizedHead",
"chain_getHead",
"chain_getHeader",
"chain_getRuntimeVersion",
"chain_subscribeAllHeads",
"chain_subscribeFinalisedHeads",
"chain_subscribeFinalizedHeads",
"chain_subscribeNewHead",
"chain_subscribeNewHeads",
"chain_subscribeRuntimeVersion",
"chain_unsubscribeAllHeads",
"chain_unsubscribeFinalisedHeads",
"chain_unsubscribeFinalizedHeads",
"chain_unsubscribeNewHead",
"chain_unsubscribeNewHeads",
"chain_unsubscribeRuntimeVersion",
"childstate_getKeys",
"childstate_getKeysPaged",
"childstate_getKeysPagedAt",
"childstate_getStorage",
"childstate_getStorageEntries",
"childstate_getStorageHash",
"childstate_getStorageSize",
"offchain_localStorageGet",
"offchain_localStorageSet",
"payment_queryFeeDetails",
"payment_queryInfo",
"state_call",
"state_callAt",
"state_getChildReadProof",
"state_getKeys",
"state_getKeysPaged",
"state_getKeysPagedAt",
"state_getMetadata",
"state_getPairs",
"state_getReadProof",
"state_getRuntimeVersion",
"state_getStorage",
"state_getStorageAt",
"state_getStorageHash",
"state_getStorageHashAt",
"state_getStorageSize",
"state_getStorageSizeAt",
"state_queryStorage",
"state_queryStorageAt",
"state_subscribeRuntimeVersion",
"state_subscribeStorage",
"state_traceBlock",
"state_unsubscribeRuntimeVersion",
"state_unsubscribeStorage",
"subscribe_newHead",
"system_accountNextIndex",
"system_addLogFilter",
"system_addReservedPeer",
"system_chain",
"system_chainType",
"system_dryRun",
"system_dryRunAt",
"system_health",
"system_localListenAddresses",
"system_localPeerId",
"system_name",
"system_nodeRoles",
"system_peers",
"system_properties",
"system_removeReservedPeer",
"system_reservedPeers",
"system_resetLogFilter",
"system_syncState",
"system_unstable_networkState",
"system_version",
"transaction_unstable_submitAndWatch",
"transaction_unstable_unwatch",
"unsubscribe_newHead"
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2017-2024 @polkadot/types-support authors & contributors
// SPDX-License-Identifier: Apache-2.0

/* eslint-disable */

// cargo run --release -- purge-chain -y --dev && cargo run --release -- --dev

export default {
"specName": "substrate-contracts-node",
"implName": "substrate-contracts-node",
"authoringVersion": 1,
"specVersion": 100,
"implVersion": 1,
"apis": [
[
"0xdf6acb689907609b",
4
],
[
"0x37e397fc7c91f5e4",
2
],
[
"0x40fe3ad401f8959a",
6
],
[
"0xd2bc9897eed08f15",
3
],
[
"0xf78b278be53f454c",
2
],
[
"0xab3c0572291feb8b",
1
],
[
"0xbc9d89904f5b923f",
1
],
[
"0x37c8bb1350a9a2a8",
4
],
[
"0xf3ff14d5ab527059",
3
],
[
"0x68b66ba122c93fa7",
2
]
],
"transactionVersion": 1,
"stateVersion": 1
};
3 changes: 2 additions & 1 deletion scripts/metadata-get.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const PREAMBLE = '// Copyright 2017-2024 @polkadot/types-support authors & contr
const CMD = {
kusama: `${PREAMBLE}// cargo run --release -- purge-chain -y --chain kusama-dev && cargo run --release -- --chain kusama-dev --alice --force-authoring\n\nexport default`,
polkadot: `${PREAMBLE}// cargo run --release -- purge-chain -y --dev && cargo run --release -- --dev\n\nexport default`,
substrate: `${PREAMBLE}// cargo run --release -- purge-chain -y --dev && cargo run --release -- --dev\n\nexport default`
substrate: `${PREAMBLE}// cargo run --release -- purge-chain -y --dev && cargo run --release -- --dev\n\nexport default`,
'substrate-contracts-node': `${PREAMBLE}// cargo run --release -- purge-chain -y --dev && cargo run --release -- --dev\n\nexport default`
};

let requestId = 0;
Expand Down

0 comments on commit 013ea59

Please sign in to comment.