diff --git a/listings/src5_interface/.gitignore b/listings/src5_interface/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/listings/src5_interface/.gitignore @@ -0,0 +1 @@ +target diff --git a/listings/src5_interface/Scarb.lock b/listings/src5_interface/Scarb.lock new file mode 100644 index 00000000..de553c60 --- /dev/null +++ b/listings/src5_interface/Scarb.lock @@ -0,0 +1,6 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "src5_interface" +version = "0.1.0" diff --git a/listings/src5_interface/Scarb.toml b/listings/src5_interface/Scarb.toml new file mode 100644 index 00000000..04ae8aad --- /dev/null +++ b/listings/src5_interface/Scarb.toml @@ -0,0 +1,8 @@ +[package] +name = "src5_interface" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] diff --git a/listings/src5_interface/src/lib.cairo b/listings/src5_interface/src/lib.cairo new file mode 100644 index 00000000..13ff0d58 --- /dev/null +++ b/listings/src5_interface/src/lib.cairo @@ -0,0 +1,4 @@ +pub mod user_account; + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/listings/src5_interface/src/src5_interface.cairo b/listings/src5_interface/src/src5_interface.cairo new file mode 100644 index 00000000..e45a1561 --- /dev/null +++ b/listings/src5_interface/src/src5_interface.cairo @@ -0,0 +1,71 @@ +use openzeppelin::account::interface::ISRC5_ID; +use openzeppelin::introspection::src5::SRC5Component; +use starknet::contract::ContractAddress; +use starknet::syscalls::call_contract_syscall; +use starknet::alloc::arrays::ArrayTrait; +use starknet::core::keccak::starknet_keccak; +// Define ISRC5 interface +trait ISRC5 { + fn supports_interface(interface_id: felt252) -> bool; +} +// Define example interfaces +trait IExample1 { + fn example_function1(param1: felt252) -> felt252; +} +trait IExample2 { + fn example_function2(param1: felt252, param2: felt252) -> felt252; +} +// Storage structure +struct Storage { + public_key: felt252, + src5: SRC5Component::Storage, +} +// Event definitions +enum Event { + AccountCreated: felt252, + SRC5Event: SRC5Component::Event, +} +// Contract implementation +@contract +namespace ExampleContract { + struct State { + storage: Storage, + } + // Constructor + #[constructor] + fn constructor(ref state: State, public_key: felt252) { + state.storage.public_key = public_key; + emit(Event::AccountCreated(public_key)); + state.storage.src5.register_interface(ISRC5_ID); + } + // Implementation of ISRC5 + #[external] + fn supports_interface(ref state: State, interface_id: felt252) -> bool { + state.storage.src5.supports_interface(interface_id) + } + // Implementation of IExample1 + #[external] + fn example_function1(param1: felt252) -> felt252 { + // Your implementation here + return starknet_keccak(param1.to_bytes()); + } + // Implementation of IExample2 + #[external] + fn example_function2(param1: felt252, param2: felt252) -> felt252 { + // Your implementation here + return param1 + param2; + } + // Additional functions or internal logic can be added here +} +// ISRC5 trait implementation +impl ISRC5 for State { + fn supports_interface(ref self, interface_id: felt252) -> bool { + self.storage.src5.supports_interface(interface_id) + } +} + + + +#[cfg(test)] +mod tests { // TODO +} diff --git a/listings/src5_interface/src5_snippet.py b/listings/src5_interface/src5_snippet.py new file mode 100644 index 00000000..e89a3b6e --- /dev/null +++ b/listings/src5_interface/src5_snippet.py @@ -0,0 +1,18 @@ +from starkware.starknet.public.abi import starknet_keccak + + signatures = [ + 'supports_interface(felt252)->E((),())', + 'is_valid_signature(felt252,Array)->E((),())', + '__execute__(Array<(ContractAddress,felt252,Array)>)->Array<(@Array)>', + '__validate__(Array<(ContractAddress,felt252,Array)>)->felt252', + '__validate_declare__(felt252)->felt252' + ] + + def compute_interface_id(): + interface_id = 0x0 + for sig in signatures: + function_id = starknet_keccak(sig.encode()) + interface_id ^= function_id + print('IAccount ID:', hex(interface_id)) + + compute_interface_id() \ No newline at end of file diff --git a/src/advanced-concepts/account_abstraction/index.md b/src/advanced-concepts/account_abstraction/index.md index f345ab99..45dfbfa9 100644 --- a/src/advanced-concepts/account_abstraction/index.md +++ b/src/advanced-concepts/account_abstraction/index.md @@ -9,5 +9,5 @@ This replaces EOA with Account Contracts, which are smart contracts that impleme On Starknet, Account Abstraction is natively supported, and all accounts are Account Contracts. -In this section we will how to implement an Account. +In this section we will show how to implement an Account. diff --git a/src/src5 implementation.md b/src/src5 implementation.md new file mode 100644 index 00000000..5abdd3fe --- /dev/null +++ b/src/src5 implementation.md @@ -0,0 +1,94 @@ +## SRC-5: A comprehensive Overview + +SRC-5 is a standard for smart contract interface introspection in Starknet, inspired by the [Ethereum ERC-165 standard](https://eips.ethereum.org/EIPS/eip-165). It provides a standardize method for contracts to publish and detect the interfaces they implement, ensuring consistent interaction. For more information, refer to the [SRC-5 specification](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md#simple-summary). + +### SRC-5 offers a standardized method to: + +* Identify interfaces. +* Publish the interfaces a contract implements. +* Detect if a contract implements any given interface. (including SRC-5) + +### Interface Definition + +An interface is a set of function signatures with specific type parameters, represented as traits. These traits are meant to be implemented externally by compliant contracts. + +Example: + + +```rust +trait IMyContract { + fn foo(some: u256) -> felt252; +} +``` + +With Cairo, generic traits can represent a set of interfaces: + +```rust + #[starknet::interface] + trait IMyContract { + fn foo(self: @TContractState, some: TNumber) -> felt252; +} +``` + +#### Extended Function Selector + +The function selector in Starknet is the starknet_keccak hash of the function name. The extended function selector, for SRC-5, is the starknet_keccak hash of the function signature: + +`fn_name(param1_type,param2_type,...)->output_type` + +For example, for a function with zero parameters and no return value: `fn_name()` + +Special Types: + +* Tuples: (elem1_type,elem2_type,...) +* Structs: (field1_type,field2_type,...) +* Enums: E(variant1_type,variant2_type,...) + +#### Interface Identification + +An interface identifier is the XOR of all extended function selectors in the interface. + +This Python code computes the interface id: + +```rust +{{#rustdoc_include ../../../listings/src5_interface/src5_snippet.py}} +``` + + For more details refer to the [SRC-5 repository on GitHub](https://github.com/ericnordelo/src5-rs). + +### Publishing and Detecting Interfaces + +To comply with SRC-5, a contract must implement the ISRC5 trait: + +```rust +trait ISRC5 { + fn supports_interface(interface_id: felt252) -> bool; +} +``` + +The supports_interface function returns a boolean that indicate if the contract implements a given interface. + +The interface identifier for ISRC5 is + +```0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055``` + +#### Detecting SRC-5 Implementation + +To check if a contract implements SRC-5: + +* Call `contract.supports_interface(0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055)`. +* If the call fails or returns false, the contract does not implement SRC-5. +* Otherwise, it implements SRC-5. + +#### Detecting Any Given Interface + +* Confirm if the contract implements SRC-5. +* If confirmed, `call supports_interface(interface_id)` to check for specific interfaces. +* If not, manually inspect the contract methods. + +Below shows a contract implementing the `SRC5` to expose the `Isupports_interface(interface_id)`: + +```rust +{{#rustdoc_include ../../../listings/src5_interface/src/src5_interface.cairo}} +``` +