-
Notifications
You must be signed in to change notification settings - Fork 375
feat: ink! v5 #5791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: ink! v5 #5791
Changes from 2 commits
db3822d
9f93a19
0cf749e
cfba268
7d404f4
1f9e6f5
985365f
5db72af
cc946da
54fd609
013ea59
a028c55
3e0d8d1
ada84b3
0029e18
f000f24
0124e14
fcb684d
64feb63
df9956c
f1a1b9d
1cda4b9
246570d
f88fcf7
f67a88a
27d6b46
0e039e7
3b48940
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,9 @@ | |
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import type { Bytes, Vec } from '@polkadot/types'; | ||
| import type { ChainProperties, ContractConstructorSpecLatest, ContractMessageParamSpecLatest, ContractMessageSpecLatest, ContractMetadata, ContractMetadataV4, ContractMetadataV5, ContractProjectInfo, ContractTypeSpec, EventRecord } from '@polkadot/types/interfaces'; | ||
| import type { ChainProperties, ContractConstructorSpecLatest, ContractEventParamSpecLatest, ContractMessageParamSpecLatest, ContractMessageSpecLatest, ContractMetadata, ContractMetadataV4, ContractMetadataV5, 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'; | ||
| import type { AbiConstructor, AbiEvent, AbiEventParam, AbiMessage, AbiMessageParam, AbiParam, DecodedEvent, DecodedMessage } from '../types.js'; | ||
|
|
||
| import { Option, TypeRegistry } from '@polkadot/types'; | ||
| import { TypeDefInfo } from '@polkadot/types-create'; | ||
|
|
@@ -181,15 +181,43 @@ export class Abi { | |
| } | ||
|
|
||
| #decodeEventV5 = (record: EventRecord): DecodedEvent => { | ||
peetzweg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const data = record.event.data[1] as Bytes; | ||
| // Find event by first topic, which potentially is the signature_topic | ||
| const signatureTopic = record.topics[0]; | ||
| const event = this.events.find((e) => e.signatureTopic !== undefined && e.signatureTopic === signatureTopic.toHex()); | ||
| const data = record.event.data[1] as Bytes; | ||
|
|
||
| if (!event) { | ||
| throw new Error(`Unable to find event with signature_topic ${signatureTopic.toHex()}`); | ||
| if (signatureTopic) { | ||
| const event = this.events.find((e) => e.signatureTopic !== undefined && e.signatureTopic !== null && e.signatureTopic === signatureTopic.toHex()); | ||
|
|
||
| // Early return if event found by signature topic | ||
| if (event) { | ||
| return event.fromU8a(data); | ||
| } | ||
| } | ||
|
|
||
| return event.fromU8a(data.subarray(0)); | ||
| // If no event returned yet, it might be anonymous | ||
| const amountOfTopics = record.topics.length; | ||
| const potentialEvents = this.events.filter((e) => { | ||
| // event can't have a signature topic | ||
| if (e.signatureTopic !== null && e.signatureTopic !== undefined) { | ||
| return false; | ||
| } | ||
|
|
||
| // event should have same amount of indexed fields as emitted topics | ||
| const amountIndexed = e.args.filter((a) => a.indexed).length; | ||
|
|
||
| if (amountIndexed !== amountOfTopics) { | ||
| return false; | ||
| } | ||
|
|
||
| // If all conditions met, it's a potential event | ||
| return true; | ||
| }); | ||
|
|
||
| if (potentialEvents.length === 1) { | ||
| return potentialEvents[0].fromU8a(data); | ||
| } | ||
|
|
||
| throw new Error('Unable to determine event'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other case to consider is if a cross contract call occurs, which in turn raises an event. Since the event is raised from another contract we don't have the metadata here...so we might not want to raise an error and just give back the raw bytes instead of attempting to decode? This scenario might also lead to a false positive in the heuristic for above for determining anon events, if the foreign event has the same number of topics 🤔
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh okay, so if contract A calls a function on B which emits an event, the event will be emitted by A and not B? Feels like it makes sense as users might not know anything about B so can only listen to A. As of now I have to little knowledge on what's happening above this little 'Abi.decodeEvent world' to have an idea how to handle this the best, yet.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It depends, using The other case is using
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah okay! Will tackle this in a follow up feature pr! 🚀 |
||
| }; | ||
|
|
||
| #decodeEventV4 = (record: EventRecord): DecodedEvent => { | ||
|
|
@@ -226,7 +254,7 @@ export class Abi { | |
| return findMessage(this.messages, messageOrId); | ||
| } | ||
|
|
||
| #createArgs = (args: ContractMessageParamSpecLatest[], spec: unknown): AbiParam[] => { | ||
| #createArgs = (args: ContractMessageParamSpecLatest[] | ContractEventParamSpecLatest[], spec: unknown): AbiParam[] => { | ||
| return args.map(({ label, type }, index): AbiParam => { | ||
| try { | ||
| if (!isObject(type)) { | ||
|
|
@@ -264,6 +292,16 @@ export class Abi { | |
| }); | ||
| }; | ||
|
|
||
| #createMessageParams = (args: ContractMessageParamSpecLatest[], spec: unknown): AbiMessageParam[] => { | ||
| return this.#createArgs(args, spec); | ||
| }; | ||
|
|
||
| #createEventParams = (args: ContractEventParamSpecLatest[], spec: unknown): AbiEventParam[] => { | ||
| const params = this.#createArgs(args, spec); | ||
|
|
||
| return params.map((p, index): AbiEventParam => ({ ...p, indexed: args[index].indexed.toPrimitive() })); | ||
| }; | ||
|
|
||
| #createEvent = (index: number): AbiEvent => { | ||
| // TODO TypeScript would narrow this type to the correct version, | ||
| // but version is `Text` so I need to call `toString()` here, | ||
|
|
@@ -277,7 +315,7 @@ export class Abi { | |
| }; | ||
|
|
||
| #createEventV5 = (spec: EventOf<ContractMetadataV5>, index: number): AbiEvent => { | ||
| const args = this.#createArgs(spec.args, spec); | ||
| const args = this.#createEventParams(spec.args, spec); | ||
| const event = { | ||
| args, | ||
| docs: spec.docs.map((d) => d.toString()), | ||
|
|
@@ -287,14 +325,14 @@ export class Abi { | |
| }), | ||
| identifier: [spec.module_path, spec.label].join('::'), | ||
| index, | ||
| signatureTopic: spec.signature_topic.toHex() | ||
| signatureTopic: spec.signature_topic.isSome ? spec.signature_topic.unwrap().toHex() : null | ||
| }; | ||
|
|
||
| return event; | ||
| }; | ||
|
|
||
| #createEventV4 = (spec: EventOf<ContractMetadataV4>, index: number): AbiEvent => { | ||
| const args = this.#createArgs(spec.args, spec); | ||
| const args = this.#createEventParams(spec.args, spec); | ||
| const event = { | ||
| args, | ||
| docs: spec.docs.map((d) => d.toString()), | ||
|
|
@@ -310,7 +348,7 @@ export class Abi { | |
| }; | ||
|
|
||
| #createMessage = (spec: ContractMessageSpecLatest | ContractConstructorSpecLatest, index: number, add: Partial<AbiMessage> = {}): AbiMessage => { | ||
| const args = this.#createArgs(spec.args, spec); | ||
| const args = this.#createMessageParams(spec.args, spec); | ||
| const identifier = spec.label.toString(); | ||
| const message = { | ||
| ...add, | ||
|
|
@@ -327,7 +365,7 @@ export class Abi { | |
| path: identifier.split('::').map((s) => stringCamelCase(s)), | ||
| selector: spec.selector, | ||
| toU8a: (params: unknown[]) => | ||
| this.#encodeArgs(spec, args, params) | ||
| this.#encodeMessageArgs(spec, args, params) | ||
| }; | ||
|
|
||
| return message; | ||
|
|
@@ -359,7 +397,7 @@ export class Abi { | |
| return message.fromU8a(trimmed.subarray(4)); | ||
| }; | ||
|
|
||
| #encodeArgs = ({ label, selector }: ContractMessageSpecLatest | ContractConstructorSpecLatest, args: AbiParam[], data: unknown[]): Uint8Array => { | ||
| #encodeMessageArgs = ({ label, selector }: ContractMessageSpecLatest | ContractConstructorSpecLatest, args: AbiMessageParam[], data: unknown[]): Uint8Array => { | ||
| if (data.length !== args.length) { | ||
| throw new Error(`Expected ${args.length} arguments to contract message '${label.toString()}', found ${data.length}`); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.