Skip to content

Commit

Permalink
feat: add support for parsing emitted events
Browse files Browse the repository at this point in the history
  • Loading branch information
3alpha committed Sep 13, 2024
1 parent b4198bd commit ec5331c
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 45 deletions.
12 changes: 12 additions & 0 deletions __tests__/cairo1v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,9 @@ describe('Cairo 1', () => {
maker_source: 418413900385n,
taker_source: 418413900385n,
},
block_hash: '0x39f27ab4cd508ab99e818512b261a7e4ae01072eb4ec8bb86aeb64755f99f2c',
block_number: 69198,
transaction_hash: '0x4e38fcce79c115b6fe2c486e3514efc1bd4da386b91c104e97230177d0bf181',
},
]);
// From component `DepositComponent`, event `Deposit` (same event name than next)
Expand Down Expand Up @@ -1093,6 +1096,9 @@ describe('Cairo 1', () => {
funder: 1466771120193999006693452314154095230636738457276435850562375218974960297344n,
amount: 4956000000000000n,
},
block_hash: '0x31afd649a5042cb1855ce820708a555eab62fe6ea07a2a538fa9100cdc80383',
block_number: 69198,
transaction_hash: '0x7768860d79bfb4c8463d215abea3c267899e373407c6882077f7447051c50de',
},
]);
const parsedEventNestedDeposit2 = events.parseEvents(
Expand All @@ -1109,6 +1115,9 @@ describe('Cairo 1', () => {
funder: 1466771120193999006693452314154095230636738457276435850562375218974960297344n,
amount: 4956000000000000n,
},
block_hash: '0x39f27ab4cd508ab99e818512b261a7e4ae01072eb4ec8bb86aeb64755f99f2c',
block_number: 69198,
transaction_hash: '0x2d5210e5334a83306abe6f7f5e7e65cd1feed72ad3b8e359a2f4614fa948e1d',
},
]);

Expand All @@ -1133,6 +1142,9 @@ describe('Cairo 1', () => {
to: 2087021424722619777119509474943472645767659996348769578120564519014510906823n,
value: 4956000000000000n,
},
block_hash: '0x39f27ab4cd508ab99e818512b261a7e4ae01072eb4ec8bb86aeb64755f99f2c',
block_number: 69198,
transaction_hash: '0x2da31a929a9848e9630906275a75a531e1718d4830501e10b0bccacd55f6fe0',
},
]);
});
Expand Down
85 changes: 85 additions & 0 deletions __tests__/utils/events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,91 @@ describe('parseEvents', () => {
expect(parsedEvents).toStrictEqual(result);
});

test('should return parsed emitted events', () => {
const abiEventAndVariantName = 'cairo_event_struct';
const abiCairoEventStruct: AbiEvent = {
kind: 'struct',
members: [
{
name: 'test_name',
type: 'test_type',
kind: 'data',
},
],
name: abiEventAndVariantName,
type: 'event',
};

const abiCairoEventEnum: CairoEventVariant = {
kind: 'enum',
variants: [
{
name: 'test_name',
type: abiEventAndVariantName,
kind: 'data',
},
],
name: 'test_cairo_event',
type: 'event',
};

const abiEvents = getAbiEvents([getInterfaceAbi(), abiCairoEventStruct, abiCairoEventEnum]);

const abiStructs: AbiStructs = {
abi_structs: {
members: [
{
name: 'test_name',
type: 'test_type',
offset: 1,
},
],
size: 2,
name: 'cairo_event_struct',
type: 'struct',
},
};

const abiEnums: AbiEnums = {
abi_enums: {
variants: [
{
name: 'test_name',
type: 'cairo_event_struct_variant',
offset: 1,
},
],
size: 2,
name: 'test_cairo_event',
type: 'enum',
},
};

const event: RPC.EmittedEvent = {
from_address: 'test_address',
keys: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'],
data: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'],
block_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34d',
block_number: 1,
transaction_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34c',
};

const parsedEvents = parseEvents([event], abiEvents, abiStructs, abiEnums);

const result = [
{
cairo_event_struct: {
test_name: 1708719217404197029088109386680815809747762070431461851150711916567020191623n,
},
block_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34d',
block_number: 1,
transaction_hash: '0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34c',
},
];

expect(parsedEvents).toStrictEqual(result);
});

test('should throw if ABI events has not enough data in "keys" property', () => {
const abiEventAndVariantName = 'cairo_event_struct';
const abiCairoEventStruct: AbiEvent = {
Expand Down
8 changes: 7 additions & 1 deletion src/types/contract.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { BlockHash, TransactionHash } from 'starknet-types-07';
import { CairoEnum } from './cairoEnum';
import {
BigNumberish,
BlockIdentifier,
BlockNumber,
Calldata,
ParsedStruct,
RawArgsArray,
Expand Down Expand Up @@ -44,6 +46,10 @@ export type InvokeOptions = Pick<
'maxFee' | 'nonce' | 'signature' | 'parseRequest'
>;

export type ParsedEvent = { [name: string]: ParsedStruct };
export type ParsedEvent = { [name: string]: ParsedStruct } & {
block_hash?: BlockHash;
block_number?: BlockNumber;
transaction_hash?: TransactionHash;
};

export type ParsedEvents = Array<ParsedEvent>;
93 changes: 49 additions & 44 deletions src/utils/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,57 +194,62 @@ function mergeAbiEvents(target: any, source: any): Object {
* ```
*/
export function parseEvents(
providerReceivedEvents: RPC.Event[],
providerReceivedEvents: RPC.EmittedEvent[] | RPC.Event[],
abiEvents: AbiEvents,
abiStructs: AbiStructs,
abiEnums: AbiEnums
): ParsedEvents {
const ret = providerReceivedEvents.flat().reduce((acc, recEvent: RPC.Event) => {
let abiEvent: AbiEvent | AbiEvents = abiEvents[recEvent.keys.shift() ?? 0];
if (!abiEvent) {
return acc;
}
while (!abiEvent.name) {
const hashName = recEvent.keys.shift();
assert(!!hashName, 'Not enough data in "keys" property of this event.');
abiEvent = (abiEvent as AbiEvents)[hashName];
}
// Create our final event object
const parsedEvent: ParsedEvent = {};
parsedEvent[abiEvent.name as string] = {};
// Remove the event's name hashed from the keys array
const keysIter = recEvent.keys[Symbol.iterator]();
const dataIter = recEvent.data[Symbol.iterator]();
const ret = providerReceivedEvents
.flat()
.reduce((acc, recEvent: RPC.EmittedEvent | RPC.Event) => {
let abiEvent: AbiEvent | AbiEvents = abiEvents[recEvent.keys.shift() ?? 0];
if (!abiEvent) {
return acc;
}
while (!abiEvent.name) {
const hashName = recEvent.keys.shift();
assert(!!hashName, 'Not enough data in "keys" property of this event.');
abiEvent = (abiEvent as AbiEvents)[hashName];
}
// Create our final event object
const parsedEvent: ParsedEvent = {};
parsedEvent[abiEvent.name as string] = {};
// Remove the event's name hashed from the keys array
const keysIter = recEvent.keys[Symbol.iterator]();
const dataIter = recEvent.data[Symbol.iterator]();

const abiEventKeys =
(abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'key') ||
(abiEvent as LegacyEvent).keys;
const abiEventData =
(abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'data') ||
(abiEvent as LegacyEvent).data;
const abiEventKeys =
(abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'key') ||
(abiEvent as LegacyEvent).keys;
const abiEventData =
(abiEvent as CairoEventDefinition).members?.filter((it) => it.kind === 'data') ||
(abiEvent as LegacyEvent).data;

abiEventKeys.forEach((key) => {
parsedEvent[abiEvent.name as string][key.name] = responseParser(
keysIter,
key,
abiStructs,
abiEnums,
parsedEvent[abiEvent.name as string]
);
});
abiEventKeys.forEach((key) => {
parsedEvent[abiEvent.name as string][key.name] = responseParser(
keysIter,
key,
abiStructs,
abiEnums,
parsedEvent[abiEvent.name as string]
);
});

abiEventData.forEach((data) => {
parsedEvent[abiEvent.name as string][data.name] = responseParser(
dataIter,
data,
abiStructs,
abiEnums,
parsedEvent[abiEvent.name as string]
);
});
acc.push(parsedEvent);
return acc;
}, [] as ParsedEvents);
abiEventData.forEach((data) => {
parsedEvent[abiEvent.name as string][data.name] = responseParser(
dataIter,
data,
abiStructs,
abiEnums,
parsedEvent[abiEvent.name as string]
);
});
if ('block_hash' in recEvent) parsedEvent.block_hash = recEvent.block_hash;
if ('block_number' in recEvent) parsedEvent.block_number = recEvent.block_number;
if ('transaction_hash' in recEvent) parsedEvent.transaction_hash = recEvent.transaction_hash;
acc.push(parsedEvent);
return acc;
}, [] as ParsedEvents);
return ret;
}

Expand Down

0 comments on commit ec5331c

Please sign in to comment.