Skip to content
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

chore: add events util tests #1218

Merged
merged 10 commits into from
Sep 5, 2024
363 changes: 363 additions & 0 deletions __tests__/utils/events.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,363 @@
import type {
AbiEntry,
AbiEnums,
AbiEvent,
AbiStructs,
CairoEventVariant,
FunctionAbi,
InterfaceAbi,
InvokeTransactionReceiptResponse,
RPC,
} from '../../src';
import { isAbiEvent, getAbiEvents, parseEvents, parseUDCEvent } from '../../src/utils/events';

const getAbiEventEntry = (): AbiEntry => ({ name: 'test', type: 'event' });

const getFunctionAbi = (): FunctionAbi => ({
inputs: [getAbiEventEntry()],
name: 'test',
outputs: [getAbiEventEntry()],
stateMutability: 'view',
type: 'function',
});

const getInterfaceAbi = (): InterfaceAbi => ({
items: [getFunctionAbi()],
name: 'test_interface_abi',
type: 'interface',
});

const getBaseTxReceiptData = (): InvokeTransactionReceiptResponse => ({
type: 'INVOKE',
transaction_hash: '0x6eebff0d931f36222268705ca791fd0de8d059eaf01887eecf1ce99a6c27f49',
actual_fee: { unit: 'WEI', amount: '0x33d758c09000' },
messages_sent: [],
events: [],
execution_status: 'SUCCEEDED',
finality_status: 'ACCEPTED_ON_L2',
block_hash: '0xdfc9b788478b2a2b9bcba19ab7d86996bcc45c4f8a865435469334e9077b24',
block_number: 584,
execution_resources: {
steps: 9490,
memory_holes: 143,
range_check_builtin_applications: 198,
pedersen_builtin_applications: 34,
ec_op_builtin_applications: 3,
data_availability: { l1_gas: 0, l1_data_gas: 544 },
},
});

describe('isAbiEvent', () => {
test('should return true if it is Abi event', () => {
expect(isAbiEvent(getAbiEventEntry())).toEqual(true);
});

test('should return false if it is not Abi event', () => {
const abiEntry: AbiEntry = { name: 'test', type: 'felt ' };
expect(isAbiEvent(abiEntry)).toEqual(false);
});
});

describe('getAbiEvents', () => {
test('should get Cairo1 ABI 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 result = {
'0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87': abiCairoEventStruct,
};
expect(abiEvents).toStrictEqual(result);
});

test('should throw and error if Cairo1 ABI events definition is inconsistent', () => {
const abiCairoEventStruct: AbiEvent = {
kind: 'struct',
members: [
{
name: 'test_name',
type: 'test_type',
kind: 'data',
},
],
name: 'cairo_event_struct',
type: 'event',
};

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

expect(() => getAbiEvents([getInterfaceAbi(), abiCairoEventStruct, abiCairoEventEnum])).toThrow(
new Error('inconsistency in ABI events definition.')
);
});

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

const abiEvents = getAbiEvents([getFunctionAbi(), abiCairoEventStruct]);
const result = {
'0x27b21abc103381e154ea5c557dfe64466e0d25add7ef91a45718f5b8ee8fae3': abiCairoEventStruct,
};
expect(abiEvents).toStrictEqual(result);
});
});

describe('parseEvents', () => {
test('should return parsed 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.Event = {
from_address: 'test_address',
keys: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'],
data: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'],
};

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

const result = [
{
cairo_event_struct: {
test_name: 1708719217404197029088109386680815809747762070431461851150711916567020191623n,
},
},
];

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 = {
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.Event = {
from_address: 'test_address',
keys: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'],
data: ['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'],
};

abiEvents['0x3c719ce4f57dd2d9059b9ffed65417d694a29982d35b188574144d6ae6c3f87'].name = '';
expect(() => parseEvents([event], abiEvents, abiStructs, abiEnums)).toBeTruthy();
});
});

describe('parseUDCEvent', () => {
test('should return parsed UDC event', () => {
const txReceipt: InvokeTransactionReceiptResponse = {
...getBaseTxReceiptData(),
events: [
{
from_address: '0x41a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf',
keys: ['0x26b160f10156dea0639bec90696772c640b9706a47f5b8c52ea1abe5858b34d'],
data: [
'0x1f1209f331cda3e84202f5495446028cd8730159ab24e08a5fd96125257673f',
'0x6cee47a1571f83b30b3549fce4aceda18d2533a51b0016b75a50466c708daad',
'0x0',
'0x54328a1075b8820eb43caf0caa233923148c983742402dcfc38541dd843d01a',
'0x3',
'0x546f6b656e',
'0x4552433230',
'0x6cee47a1571f83b30b3549fce4aceda18d2533a51b0016b75a50466c708daad',
'0x76d9fae688efa7dc5defa712c1fa7df537e4c0f5f8b05842a1fd4a6d8d9d3a1',
],
},
{
from_address: '0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7',
keys: [
'0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9',
'0x6cee47a1571f83b30b3549fce4aceda18d2533a51b0016b75a50466c708daad',
'0x1000',
],

data: ['0x33d758c09000', '0x0'],
},
],
};

const parsedUDCEvent = parseUDCEvent(txReceipt);
const result = {
transaction_hash: '0x6eebff0d931f36222268705ca791fd0de8d059eaf01887eecf1ce99a6c27f49',
contract_address: '0x1f1209f331cda3e84202f5495446028cd8730159ab24e08a5fd96125257673f',
address: '0x1f1209f331cda3e84202f5495446028cd8730159ab24e08a5fd96125257673f',
deployer: '0x6cee47a1571f83b30b3549fce4aceda18d2533a51b0016b75a50466c708daad',
unique: '0x0',
classHash: '0x54328a1075b8820eb43caf0caa233923148c983742402dcfc38541dd843d01a',
calldata_len: '0x3',
calldata: [
'0x546f6b656e',
'0x4552433230',
'0x6cee47a1571f83b30b3549fce4aceda18d2533a51b0016b75a50466c708daad',
],
salt: '0x76d9fae688efa7dc5defa712c1fa7df537e4c0f5f8b05842a1fd4a6d8d9d3a1',
};
expect(parsedUDCEvent).toStrictEqual(result);
});

test('should throw an error if events are empty', () => {
const txReceipt: InvokeTransactionReceiptResponse = {
...getBaseTxReceiptData(),
events: [],
};

expect(() => parseUDCEvent(txReceipt)).toThrow(new Error('UDC emitted event is empty'));
});
});
1 change: 1 addition & 0 deletions src/utils/calldata/cairo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export function getAbiContractVersion(abi: Abi): ContractVersion {
const testFunction = abi.find(
(it) => it.type === 'function' && (it.inputs.length || it.outputs.length)
);

if (!testFunction) {
return { cairo: undefined, compiler: undefined };
}
Expand Down
8 changes: 5 additions & 3 deletions src/utils/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export function parseEvents(
}
while (!abiEvent.name) {
const hashName = recEvent.keys.shift();
assert(!!hashName, 'Not enough data in "key" property of this event.');
assert(!!hashName, 'Not enough data in "keys" property of this event.');
abiEvent = (abiEvent as AbiEvents)[hashName];
}
// Create our final event object
Expand Down Expand Up @@ -251,12 +251,14 @@ export function parseEvents(
/**
* Parse Transaction Receipt Event from UDC invoke transaction and
* create DeployContractResponse compatible response with addition of the UDC Event data
* @param {InvokeTransactionReceiptResponse} txReceipt
*
* @returns {DeployContractUDCResponse} */
* @returns {DeployContractUDCResponse} parsed UDC event data
*/
export function parseUDCEvent(
txReceipt: InvokeTransactionReceiptResponse
): DeployContractUDCResponse {
if (!txReceipt.events) {
if (!txReceipt.events?.length) {
lukasaric marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('UDC emitted event is empty');
}
const event = txReceipt.events.find(
Expand Down