Comprehensive Cairo Smart Contract For Starknet Indexer Event Decoding Testing
To effectively test the event decoding capabilities of the Starknet indexer, we need a Cairo smart contract that emits events with various data types. This comprehensive approach ensures that the indexer can accurately decode different event structures and data types, thereby validating its robustness and reliability.
Current Limitations in Testing
Currently, our testing methodology faces several limitations that hinder our ability to thoroughly evaluate the Starknet indexer. Our current testing primarily relies on existing contracts, such as the STRK token, which limits the scope of our evaluation. The focus is predominantly on basic Transfer events with simple data types, leaving a significant gap in our understanding of how the indexer handles more complex scenarios. This limited approach means we lack comprehensive testing for different event structures and validation for complex data type decoding, which are crucial for ensuring the indexer's performance in real-world applications. To address these limitations, we propose a solution that involves creating a dedicated test Cairo smart contract.
Proposed Solution: A Comprehensive Test Cairo Smart Contract
To overcome the current testing limitations, a comprehensive test Cairo smart contract needs to be created. This test contract is designed to emit events with a wide array of data types, ensuring thorough validation of the Starknet indexer's capabilities. By including a variety of event structures and data types, we can effectively assess the indexer's ability to handle real-world scenarios and edge cases. The contract will feature events with basic types, complex types, and diverse event structures to provide a robust testing environment. This approach allows us to move beyond simple event structures and delve into the intricacies of decoding complex data, which is essential for ensuring the indexer's reliability and accuracy.
Event Data Types to Test
To ensure comprehensive testing, the Cairo smart contract will emit events with a wide range of data types. These data types are categorized into basic types, complex types, and various event structures, providing a robust foundation for validating the Starknet indexer's decoding capabilities.
- Basic Types: The contract will include events that utilize fundamental data types. These include
u8
,u16
,u32
,u64
,u128
, andu256
, which are standard unsigned integer types. Additionally, the native Starknet type,felt252
, and the boolean type,bool
, will be tested. These basic types form the building blocks of more complex data structures and are essential for ensuring the indexer's core functionality. - Complex Types: To simulate real-world scenarios, the contract will emit events with complex data types. Arrays, such as
Array<felt252>
andArray<u32>
, will be used to test the indexer's ability to handle collections of data. Custom structs with multiple fields, nested structs (structs containing other structs), and option types (Option<felt252>
) will also be included. These complex types are critical for testing the indexer's ability to manage intricate data relationships and structures. - Event Structures: The contract will define events with varying structures to cover different scenarios. Events with no parameters, single parameters, and multiple parameters will be tested. Additionally, events with indexed parameters (keys) and non-indexed parameters (data) will be included. This variety ensures that the indexer can handle different event formats and correctly identify and decode relevant information.
Contract Features: Event Definitions
The Cairo smart contract will include a comprehensive set of event definitions to thoroughly test the Starknet indexer's capabilities. These event definitions cover a wide range of data types and structures, ensuring that the indexer can accurately decode various event formats. By defining events that utilize basic types, complex types, and different parameter configurations, we create a robust testing environment for validating the indexer's performance.
#[event]
#[derive(Drop, starknet::Event)]
enum TestEvents {
BasicTypes: BasicTypes,
ComplexStruct: ComplexStruct,
ArrayData: ArrayData,
NestedStruct: NestedStruct,
MixedTypes: MixedTypes,
NoParams: NoParams,
IndexedOnly: IndexedOnly,
DataOnly: DataOnly,
}
#[derive(Drop, starknet::Event)]
struct BasicTypes {
#[key]
user: felt252,
uint8_val: u8,
uint16_val: u16,
uint32_val: u32,
uint64_val: u64,
uint128_val: u128,
uint256_val: u256,
bool_val: bool,
felt_val: felt252,
}
#[derive(Drop, starknet::Event)]
struct ComplexStruct {
#[key]
transaction_id: felt252,
user_info: UserInfo,
amounts: Array<u128>,
flags: Array<bool>,
}
#[derive(Drop, starknet::Event)]
struct ArrayData {
#[key]
event_id: felt252,
addresses: Array<felt252>,
values: Array<u256>,
metadata: Array<felt252>,
}
#[derive(Drop, starknet::Event)]
struct NestedStruct {
#[key]
root_id: felt252,
parent: ParentStruct,
children: Array<ChildStruct>,
}
#[derive(Drop, starknet::Event)]
struct MixedTypes {
#[key]
primary_key: felt252,
#[key]
secondary_key: u64,
simple_data: felt252,
complex_data: ComplexStruct,
optional_data: Option<felt252>,
}
#[derive(Drop, starknet::Event)]
struct NoParams {
#[key]
timestamp: u64,
}
#[derive(Drop, starknet::Event)]
struct IndexedOnly {
#[key]
user: felt252,
#[key]
action: felt252,
#[key]
timestamp: u64,
}
#[derive(Drop, starknet::Event)]
struct DataOnly {
message: felt252,
amount: u128,
metadata: Array<felt252>,
}
The TestEvents
enum defines a variety of events, each designed to test different aspects of the indexer's capabilities. The BasicTypes event includes a comprehensive set of basic data types, such as u8
, u16
, u32
, u64
, u128
, u256
, bool
, and felt252
, ensuring that the indexer can handle fundamental data types accurately. The ComplexStruct
event incorporates a custom struct (UserInfo
) and arrays, allowing us to test the indexer's ability to decode nested structures and collections of data. The ArrayData
event focuses on testing the handling of arrays of felt252
and u256
types, which are commonly used in Starknet contracts. The NestedStruct
event includes a struct (ParentStruct
) containing an array of another struct (ChildStruct
), providing a more complex nested structure for testing.
The MixedTypes
event combines basic and complex data types, including an option type (Option<felt252>
), to test the indexer's versatility in handling mixed data structures. The NoParams
event tests the indexer's ability to handle events with no parameters, while the IndexedOnly
event focuses on events with only indexed parameters. The DataOnly
event, conversely, tests events with only non-indexed parameters. By including these diverse event definitions, we can comprehensively validate the Starknet indexer's ability to decode various event structures and data types, ensuring its robustness and reliability.
Struct Definitions
To support the complex event structures, the Cairo smart contract includes several struct definitions. These structs are designed to represent various data structures commonly used in Starknet contracts, such as user information, parent-child relationships, and metadata. By defining these structs, we can create events that simulate real-world scenarios and thoroughly test the Starknet indexer's ability to decode complex data types.
#[derive(Drop, starknet::Store)]
struct UserInfo {
name: felt252,
age: u8,
balance: u256,
is_active: bool,
}
#[derive(Drop, starknet::Store)]
struct ParentStruct {
id: felt252,
name: felt252,
children_count: u32,
}
#[derive(Drop, starknet::Store)]
struct ChildStruct {
id: felt252,
value: u128,
metadata: felt252,
}
The UserInfo
struct is designed to represent user-specific data, including a name (felt252
), age (u8
), balance (u256
), and active status (bool
). This struct is used within the ComplexStruct
event to test the indexer's ability to decode nested structures. The ParentStruct
and ChildStruct
structs are used in the NestedStruct
event to represent hierarchical data relationships. The ParentStruct
includes an ID (felt252
), name (felt252
), and children count (u32
), while the ChildStruct
includes an ID (felt252
), value (u128
), and metadata (felt252
). These structs allow us to test the indexer's ability to handle complex nested data structures, ensuring that it can accurately decode events with intricate data relationships.
Contract Functions
The Cairo smart contract includes a set of external functions designed to emit the various event types defined in the contract. These functions are crafted to cover a wide range of data types and event structures, ensuring comprehensive testing of the Starknet indexer. Each function corresponds to a specific event type, allowing us to systematically test the indexer's ability to decode different event formats and data structures.
#[external(v0)]
fn emit_basic_types(
ref self: ContractState,
user: felt252,
uint8_val: u8,
uint16_val: u16,
uint32_val: u32,
uint64_val: u64,
uint128_val: u128,
uint256_val: u256,
bool_val: bool,
felt_val: felt252,
) {
self.emit(TestEvents::BasicTypes(BasicTypes {
user,
uint8_val,
uint16_val,
uint32_val,
uint64_val,
uint128_val,
uint256_val,
bool_val,
felt_val,
}));
}
#[external(v0)]
fn emit_complex_struct(
ref self: ContractState,
transaction_id: felt252,
user_name: felt252,
user_age: u8,
user_balance: u256,
user_active: bool,
amounts: Array<u128>,
flags: Array<bool>,
) {
let user_info = UserInfo {
name: user_name,
age: user_age,
balance: user_balance,
is_active: user_active,
};
self.emit(TestEvents::ComplexStruct(ComplexStruct {
transaction_id,
user_info,
amounts,
flags,
}));
}
#[external(v0)]
fn emit_array_data(
ref self: ContractState,
event_id: felt252,
addresses: Array<felt252>,
values: Array<u256>,
metadata: Array<felt252>,
) {
self.emit(TestEvents::ArrayData(ArrayData {
event_id,
addresses,
values,
metadata,
}));
}
#[external(v0)]
fn emit_nested_struct(
ref self: ContractState,
root_id: felt252,
parent_id: felt252,
parent_name: felt252,
children_count: u32,
children_data: Array<(felt252, u128, felt252)>,
) {
let parent = ParentStruct {
id: parent_id,
name: parent_name,
children_count,
};
let mut children = ArrayTrait::new();
let mut i = 0;
let len = children_data.len();
while i < len {
let (child_id, value, metadata) = children_data.at(i);
children.append(ChildStruct {
id: child_id,
value,
metadata,
});
i += 1;
}
self.emit(TestEvents::NestedStruct(NestedStruct {
root_id,
parent,
children,
}));
}
#[external(v0)]
fn emit_mixed_types(
ref self: ContractState,
primary_key: felt252,
secondary_key: u64,
simple_data: felt252,
optional_data: Option<felt252>,
) {
// Create a complex struct for this event
let complex_data = UserInfo {
name: 'test_user',
age: 25,
balance: 1000000000000000000,
is_active: true,
};
self.emit(TestEvents::MixedTypes(MixedTypes {
primary_key,
secondary_key,
simple_data,
complex_data,
optional_data,
}));
}
#[external(v0)]
fn emit_no_params(ref self: ContractState, timestamp: u64) {
self.emit(TestEvents::NoParams(NoParams { timestamp }));
}
#[external(v0)]
fn emit_indexed_only(
ref self: ContractState,
user: felt252,
action: felt252,
timestamp: u64,
) {
self.emit(TestEvents::IndexedOnly(IndexedOnly {
user,
action,
timestamp,
}));
}
#[external(v0)]
fn emit_data_only(
ref self: ContractState,
message: felt252,
amount: u128,
metadata: Array<felt252>,
) {
self.emit(TestEvents::DataOnly(DataOnly {
message,
amount,
metadata,
}));
}
The emit_basic_types
function emits the BasicTypes
event, which includes a comprehensive set of basic data types such as u8
, u16
, u32
, u64
, u128
, u256
, bool
, and felt252
. This function is crucial for testing the indexer's ability to handle fundamental data types accurately. The emit_complex_struct
function emits the ComplexStruct
event, which incorporates a custom struct (UserInfo
) and arrays, allowing us to test the indexer's ability to decode nested structures and collections of data. The emit_array_data
function emits the ArrayData
event, focusing on testing the handling of arrays of felt252
and u256
types, which are commonly used in Starknet contracts. The emit_nested_struct
function emits the NestedStruct
event, which includes a struct (ParentStruct
) containing an array of another struct (ChildStruct
), providing a more complex nested structure for testing.
The emit_mixed_types
function emits the MixedTypes
event, which combines basic and complex data types, including an option type (Option<felt252>
), to test the indexer's versatility in handling mixed data structures. The emit_no_params
function emits the NoParams
event, testing the indexer's ability to handle events with no parameters. The emit_indexed_only
function emits the IndexedOnly
event, focusing on events with only indexed parameters. The emit_data_only
function emits the DataOnly
event, conversely, testing events with only non-indexed parameters. By providing these diverse functions, we can systematically test the indexer's ability to decode various event structures and data types, ensuring its robustness and reliability.
Requirements for Implementation
To successfully implement this comprehensive testing solution, several key requirements must be met. These requirements span the creation of the Cairo contract, its deployment, the development of test scripts, and the necessary updates and validations for the indexer. Meeting these requirements will ensure that the Starknet indexer is thoroughly tested and can reliably decode a wide range of event data types.
- [ ] Create Cairo contract with all event types
- [ ] Deploy contract to testnet (Goerli/Testnet)
- [ ] Create test scripts to emit events
- [ ] Update indexer to handle the new contract
- [ ] Test event decoding for all data types
- [ ] Validate ABI parsing and event matching
- [ ] Create comprehensive test cases
- [ ] Document expected event structures
Firstly, a Cairo contract must be created that includes all the event types and structures previously defined. This contract will serve as the foundation for our testing efforts, providing a standardized way to emit events with diverse data types. Secondly, this contract needs to be deployed to a Starknet testnet, such as Goerli or a dedicated testnet, to simulate a real-world environment. Deploying to a testnet allows us to interact with the contract and generate events in a controlled setting.
Thirdly, test scripts must be developed to emit events from the deployed contract. These scripts will be designed to trigger each event type with various input values, ensuring comprehensive coverage of different scenarios. Fourthly, the Starknet indexer needs to be updated to handle the new contract and its event types. This involves ensuring that the indexer can correctly parse the contract's Application Binary Interface (ABI) and decode the emitted events. Fifthly, event decoding for all data types must be thoroughly tested. This includes verifying that the indexer can accurately extract and interpret the data from each event, regardless of its type or structure.
Sixthly, ABI parsing and event matching need to be validated. The indexer must be able to correctly parse the contract's ABI to understand the structure of the events and accurately match incoming events to their corresponding definitions. Seventhly, comprehensive test cases should be created to cover all possible scenarios and edge cases. These test cases will serve as a benchmark for the indexer's performance, ensuring that it meets the required standards. Lastly, the expected event structures and decoding results should be documented. This documentation will provide a clear reference for developers and testers, ensuring that the indexer's behavior is well-understood and can be easily verified.
Testing Strategy for Robust Validation
To ensure the Starknet indexer is robust and reliable, a comprehensive testing strategy is essential. This strategy encompasses contract deployment, event emission tests, and indexer testing, each designed to validate different aspects of the indexer's functionality. By systematically testing each component, we can identify and address potential issues, ensuring the indexer performs optimally in real-world scenarios.
1. Contract Deployment: Ensuring a Solid Foundation
The first step in our testing strategy is contract deployment. This involves deploying the Cairo contract, which includes all the defined event types, to a Starknet testnet. The primary goals of this phase are to verify that the contract compiles successfully and to ensure that all functions emit events correctly. Successful compilation confirms that the contract's code is valid and adheres to the Cairo language specifications. Verifying that all functions emit events as expected confirms that the contract's core functionality is working as intended. This step is crucial for establishing a solid foundation for subsequent testing phases.
2. Event Emission Tests: Simulating Real-World Scenarios
Once the contract is deployed, the next step is to conduct event emission tests. These tests involve generating events from the contract using various input values. This step is designed to simulate real-world scenarios and to ensure that the contract emits events with the correct data types and structures. The following examples illustrate how events can be emitted using the starknet call
command:
# Test basic types
starknet call --address $CONTRACT_ADDRESS --function emit_basic_types \
--inputs 0x123 255 65535 4294967295 18446744073709551615 \
340282366920938463463374607431768211455 \
115792089237316195423570985008687907853269984665640564039457584007913129639935 \
1 0xabc
# Test complex struct
starknet call --address $CONTRACT_ADDRESS --function emit_complex_struct \
--inputs 0x456 "alice" 25 1000000000000000000 1 \
1000000000000000000 2000000000000000000 \
1 0 1
These commands invoke the emit_basic_types
and emit_complex_struct
functions, passing in specific input values to generate events. By varying the input values, we can test the contract's ability to handle different data ranges and edge cases. This phase is critical for ensuring that the contract functions correctly and that the emitted events are properly structured for the indexer to decode.
3. Indexer Testing: Validating Decoding Capabilities
The final step in our testing strategy is indexer testing. This involves using the Starknet indexer to process the events emitted by the contract. The goal is to validate that the indexer can correctly decode all event types and data structures. The following example demonstrates how to test the indexer using a curl
command:
# Test with indexer
curl -X POST http://localhost:3000/ \
-H "Content-Type: application/json" \
-d '{
"address": "CONTRACT_ADDRESS",
"chunk_size": 10
}'
This command sends a POST request to the indexer, specifying the contract address and chunk size. The indexer then processes the events emitted by the contract and decodes the data. By analyzing the indexer's output, we can verify that it correctly extracts and interprets the event data. This phase is essential for ensuring that the indexer can accurately process events from the contract and provide meaningful insights into the data.
Expected Results: Decoding with Precision
To ensure the Starknet indexer functions correctly, specific results are expected from the event decoding process. These expectations cover the proper extraction of field names, accurate data type conversions, and the correct handling of complex data structures. By validating these results, we can confirm the indexer's ability to process and interpret event data effectively.
Decoded Events Should Include:
- Proper field names from ABI
- Correct data type conversions
- Nested struct decoding
- Array data handling
- Optional value handling
- Indexed vs non-indexed parameter separation
The decoded events should include proper field names derived from the contract's Application Binary Interface (ABI). This ensures that the indexer can accurately identify and label the data within the events. Additionally, the indexer should perform correct data type conversions, transforming raw event data into meaningful values. This includes converting integers, strings, and other data types into their appropriate representations. The indexer must also be capable of handling nested structs, correctly decoding the hierarchical relationships between data elements.
Array data handling is another crucial aspect of event decoding. The indexer should be able to process arrays of various data types, extracting and organizing the individual elements. Furthermore, the indexer should handle optional values, such as Option types, gracefully, correctly identifying and processing the data when present and handling the absence of data when it is not. Proper separation of indexed and non-indexed parameters is also essential, ensuring that the indexer can distinguish between key fields and data fields within the events. By meeting these expectations, the indexer can provide a clear and accurate representation of the event data.
Test Cases to Validate:
- Basic Types: Verify u8, u16, u32, u64, u128, u256, bool, felt252
- Complex Structs: Verify nested struct decoding
- Arrays: Verify array data handling
- Mixed Types: Verify complex event structures
- Indexed vs Data: Verify proper parameter separation
- Optional Values: Verify Option type handling
To ensure comprehensive testing, specific test cases are designed to validate the indexer's performance across various scenarios. The test cases for basic types verify that the indexer can accurately decode fundamental data types, such as u8
, u16
, u32
, u64
, u128
, u256
, bool
, and felt252
. These tests confirm the indexer's ability to handle the building blocks of event data. For complex structs, the test cases focus on verifying nested struct decoding, ensuring that the indexer can correctly interpret hierarchical data structures. The array test cases validate array data handling, confirming the indexer's ability to process collections of data elements.
Test cases for mixed types are designed to verify complex event structures, ensuring that the indexer can handle events with a combination of basic and complex data types. The indexed vs data test cases focus on verifying proper parameter separation, confirming that the indexer can distinguish between key fields and data fields within events. Finally, test cases for optional values validate Option type handling, ensuring that the indexer can gracefully manage the presence or absence of data. By passing these test cases, the indexer demonstrates its ability to accurately and reliably decode a wide range of event data.
Benefits of Comprehensive Testing
Implementing a comprehensive testing strategy for the Starknet indexer offers numerous benefits. These benefits span from ensuring thorough validation to providing valuable documentation and enhancing overall quality assurance. By rigorously testing the indexer's capabilities, we can build a more robust and reliable system.
- Comprehensive Testing: Test all data types the indexer might encounter
- Real-world Validation: Ensure indexer works with complex contracts
- Edge Case Coverage: Test unusual event structures
- Documentation: Provide examples for users
- Quality Assurance: Validate indexer robustness
Comprehensive testing ensures that all data types the indexer might encounter are thoroughly validated. This includes basic types, complex structures, arrays, and optional values, providing a complete picture of the indexer's capabilities. Real-world validation ensures that the indexer works effectively with complex contracts, simulating the scenarios it will face in production environments. This step is crucial for confirming that the indexer can handle the intricacies of real-world data.
Edge case coverage is another significant benefit, allowing us to test unusual event structures that might not be common but are still possible. By identifying and addressing these edge cases, we can enhance the indexer's robustness and prevent potential issues. The testing process also generates valuable documentation, providing examples for users on how to interact with the indexer and interpret its output. This documentation is essential for promoting adoption and ensuring that users can effectively leverage the indexer's capabilities.
Ultimately, comprehensive testing enhances quality assurance by validating the indexer's robustness. By confirming that the indexer can accurately decode a wide range of event data, we can build confidence in its reliability and performance. This leads to a more stable and dependable system, benefiting both developers and users.
Acceptance Criteria: Defining Success
To ensure the success of this comprehensive testing initiative, clear acceptance criteria must be defined. These criteria serve as benchmarks for the indexer's performance, specifying the conditions that must be met for the testing to be considered successful. By establishing these criteria, we can objectively evaluate the indexer's capabilities and confirm that it meets the required standards.
- [ ] Contract compiles and deploys successfully
- [ ] All event types emit correctly
- [ ] Indexer can decode all event types
- [ ] ABI parsing works for complex structures
- [ ] Field names are correctly extracted
- [ ] Data types are properly converted
- [ ] Test cases pass for all scenarios
- [ ] Documentation includes expected outputs
Firstly, the Cairo contract must compile and deploy successfully to a Starknet testnet. This confirms that the contract's code is valid and that it can be deployed in a real-world environment. Secondly, all event types defined in the contract must emit correctly, generating the expected event data. This verifies that the contract's event emission functions are working as intended. Thirdly, the Starknet indexer must be able to decode all event types emitted by the contract. This confirms that the indexer can process the events and extract the relevant data.
Fourthly, ABI parsing must work correctly for complex structures, ensuring that the indexer can interpret the contract's ABI and understand the structure of its events. Fifthly, field names must be correctly extracted from the events, allowing the indexer to label and organize the data effectively. Sixthly, data types must be properly converted, ensuring that the indexer can transform raw event data into meaningful values. Seventhly, all test cases designed to validate the indexer's performance must pass, confirming that the indexer meets the required standards across various scenarios.
Lastly, the documentation must include expected outputs for each event type, providing a clear reference for developers and users. By meeting these acceptance criteria, we can confidently assert that the Starknet indexer has been thoroughly tested and is capable of accurately decoding a wide range of event data.