From 51f9a6d645180dcf95634294207bb11900abf3a7 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 29 May 2023 16:42:19 +0200 Subject: [PATCH 01/22] feat: add snip-5 --- SNIPS/snip-5.md | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 SNIPS/snip-5.md diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md new file mode 100644 index 0000000..6418b84 --- /dev/null +++ b/SNIPS/snip-5.md @@ -0,0 +1,132 @@ +--- +snip: 5 +title: Standard Interface Detection +author: Eric Nordelo +status: Draft +type: Standards Track +category: SRC +created: 2023-05-29 +--- + +## Simple Summary + +Standard method to publish and detect what interfaces a smart contract implements. + +Inspired by [ERC-165](https://eips.ethereum.org/EIPS/eip-165). + +## Abstract + +This standardizes: + +1. How interfaces are identified. +2. How a contract will publish the interfaces it implements. +3. How to detect if a contract implements SRC-165. +4. How to detect if a contract implements any given interface. + + +## Motivation + +For some "standard interfaces" like the ERC-721 token interface, it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, in order to adapt the way in which the contract is to be interacted with. This proposal standardizes the concept of interfaces and standardizes the identification (naming) of interfaces. + +## Specification + +### Extended Function Selector + +In Starknet, a function selector is the `starknet_keccak` of the function name (ASCII encoded). For this standard we define the extended function selector as the `starknet_keccak` of the function signature, having this signature the following format: + +``` +fn_name(param1_type,param2_type,...) +``` + +Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. + +### How Interfaces are Identified + +For this standard, an *interface* is a set of [extended function selectors](#extended-function-selector-efns). + +We define the interface identifier as the XOR of all extended function selectors in the interface. This code example shows how to calculate an interface identifier: + +From this Cairo interface: + +```cairo +struct Call { + to: ContractAddress, + selector: felt252, + calldata: Array +} + +trait IAccount { + fn supports_interface(felt252); + fn is_valid_signature(felt252, Array); + fn __execute__(Array); + fn __validate__(Array); + fn __validate_declare__(felt252); +} +``` + +This is the python code that computes the interface id: + +```python +# pip install cairo-lang +from starkware.starknet.public.abi import starknet_keccak + +# This is the interface +extended_function_selector_list = [ + 'supports_interface(felt252)', + 'is_valid_signature(felt252,Array)', + '__execute__(Array<(ContractAddress,felt252,Array)>)', + '__validate__(Array<(ContractAddress,felt252,Array)>)', + '__validate_declare__(felt252)' +] + +def main(): + interface_id = 0x0 + for function in extended_function_selector_list: + function_id = starknet_keccak(function.encode()) + interface_id ^= function_id + print('IAccount ID:') + print(hex(interface_id)) + + +if __name__ == "__main__": + main() +``` + +### How a Contract will Publish the Interfaces it Implements + +A contract that is compliant with SRC-5 shall implement the following interface (referred as `ISRC5.sol`): + +```cairo +trait ISRC5 { + /// @notice Query if a contract implements an interface + /// @param interface_id The interface identifier, as specified in SRC-5 + /// @return `true` if the contract implements `interface_id`, `false` otherwise + fn supports_interface(interface_id: felt252) -> bool; +} +``` + +The interface identifier for this interface is `0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60`. You can calculate this by running `starknet_keccak('supports_interface(felt252)')`. + +Therefore the implementing contract will have a `supports_interface` function that returns: + +- `true` when `interface_id` is `0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60` (SNIP-5 interface) +- `true` for any other `interface_id` this contract implements +- `false` for any other `interface_id` + +This function must return a bool. + +### How to Detect if a Contract Implements SRC-5 + +1. The source contract makes a `call_contract_syscall` to the destination address with `entrypoint_selector` as: `0xfe80f537b66d12a00b6d3c072b44afbb716e78dde5c3f0ef116ee93d3e3283` and calldata as a one element Span containing: `0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60`. This corresponds to `contract.supports_interface(0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60)`. +2. If the call fails or return false, the destination contract does not implement SRC-5. +5. Otherwise it implements SRC-5. + +### How to Detect if a Contract Implements any Given Interface + +1. If you are not sure if the contract implements SRC-5, use the above procedure to confirm. +2. If it does not implement it, then you will have to see what methods it uses the old-fashioned way. +3. If it implements it call `supports_interface(interface_id)` to determine if it implements an interface you can use. + +## Copyright + +Copyright and related rights waived via [MIT](../LICENSE). From a4a4a362c033216c6a360cdac2bd77fa1a7906e5 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 30 May 2023 15:29:31 +0200 Subject: [PATCH 02/22] feat: add complex types --- SNIPS/snip-5.md | 80 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 6418b84..ed6f692 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -10,7 +10,7 @@ created: 2023-05-29 ## Simple Summary -Standard method to publish and detect what interfaces a smart contract implements. +A standard method to publish and detect what interfaces a smart contract implements. Inspired by [ERC-165](https://eips.ethereum.org/EIPS/eip-165). @@ -26,7 +26,7 @@ This standardizes: ## Motivation -For some "standard interfaces" like the ERC-721 token interface, it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, in order to adapt the way in which the contract is to be interacted with. This proposal standardizes the concept of interfaces and standardizes the identification (naming) of interfaces. +For some "standard interfaces" like the ERC-721 token interface, it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, to adapt how the contract is to be interacted with. This proposal standardizes the concept of interfaces and standardizes the identification (naming) of interfaces. ## Specification @@ -38,7 +38,75 @@ In Starknet, a function selector is the `starknet_keccak` of the function name ( fn_name(param1_type,param2_type,...) ``` -Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. +Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. Next, we will define how to treat complex types for this signature definition. + +### Special types (Tuples, Structs, Enums, and Generics) + +We define how to provide these parameters to the signature for getting the extended function selector: + +#### Tuples + +The signature for a tuple of `n` elements is: `(elem1_type,elem2_type,...)`, where `elemN_type` is the type of the n-th tuple member. + +#### Structs + +The signature for a struct having `n` fields is: `S(field1_type,field2_type,...)`, where `fieldN_type` is the type of the n-th struct field. + +The leading `S` avoid clashes with similar signatures using tuples. + +#### Enums + +The signature for an enum having `n` fields is: `E(variant1_type,variant2_type,...)`, where `variantN_type` is the type of the n-th enum variant. + +The leading `E` avoid clashes with similar signatures using tuples or structs. + +#### Generics + +The signature for a generic argument required to implement `n` traits is: `{trait1_name,trait2_name,...}`, where `traitN_name` is the type of the n-th trait required from the function signature in the same specified order. + +#### Examples + +1. From the Cairo function: + +```cairo +fn foo>(param1: Array) -> bool; +``` + +The signature is: + +```cairo +foo(Array<{Drop}>) +``` + +2. From the Cairo function: + +```cairo +enum MyEnum { + FirstVariant: (felt252, u32), + SecondVariant: Array, +} + +impl MyEnumDrop> of Drop>; + +struct MyStruct { + field1: MyEnum, + field2: E, +} + +impl MyStructDrop, impl EDrop: Drop> of Drop>; + +fn foo, impl EDrop: Drop>( + param1: MyEnum, param2: MyStruct +) -> bool { + return true; +} +``` + +The signature is: + +```cairo +foo(E((felt252,u32),Array<{Drop}>),S(E((felt252,u32),Array<{Drop}>),{Drop})); +``` ### How Interfaces are Identified @@ -94,7 +162,7 @@ if __name__ == "__main__": ### How a Contract will Publish the Interfaces it Implements -A contract that is compliant with SRC-5 shall implement the following interface (referred as `ISRC5.sol`): +A contract that is compliant with SRC-5 shall implement the following interface (referred to as `ISRC5.sol`): ```cairo trait ISRC5 { @@ -118,14 +186,14 @@ This function must return a bool. ### How to Detect if a Contract Implements SRC-5 1. The source contract makes a `call_contract_syscall` to the destination address with `entrypoint_selector` as: `0xfe80f537b66d12a00b6d3c072b44afbb716e78dde5c3f0ef116ee93d3e3283` and calldata as a one element Span containing: `0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60`. This corresponds to `contract.supports_interface(0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60)`. -2. If the call fails or return false, the destination contract does not implement SRC-5. +2. If the call fails or returns false, the destination contract does not implement SRC-5. 5. Otherwise it implements SRC-5. ### How to Detect if a Contract Implements any Given Interface 1. If you are not sure if the contract implements SRC-5, use the above procedure to confirm. 2. If it does not implement it, then you will have to see what methods it uses the old-fashioned way. -3. If it implements it call `supports_interface(interface_id)` to determine if it implements an interface you can use. +3. If it does implement it, call `supports_interface(interface_id)` to determine if it implements an interface you can use. ## Copyright From 8f0e8ea87ebd5d316ec7082a0e86c0cee89089d2 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 30 May 2023 15:32:12 +0200 Subject: [PATCH 03/22] fix: wording --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index ed6f692..98ac4c9 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -42,7 +42,7 @@ Where `fn_name` is the function name, and `paramN_type` is the type of the n-th ### Special types (Tuples, Structs, Enums, and Generics) -We define how to provide these parameters to the signature for getting the extended function selector: +A definition on how to provide these parameters to the signature for getting the extended function selector: #### Tuples From cfdf495f2611ba6663ebdbd716586a171cee15cd Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 30 May 2023 15:46:51 +0200 Subject: [PATCH 04/22] fix: typo --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 98ac4c9..449ad7e 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -20,7 +20,7 @@ This standardizes: 1. How interfaces are identified. 2. How a contract will publish the interfaces it implements. -3. How to detect if a contract implements SRC-165. +3. How to detect if a contract implements SRC-5. 4. How to detect if a contract implements any given interface. From c627cbf2ece466d8e925a64148b727d2a9fc51ae Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 30 May 2023 21:12:31 +0200 Subject: [PATCH 05/22] fix: signatures --- SNIPS/snip-5.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 449ad7e..75f3cba 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -105,7 +105,7 @@ fn foo, impl EDrop: Drop>( The signature is: ```cairo -foo(E((felt252,u32),Array<{Drop}>),S(E((felt252,u32),Array<{Drop}>),{Drop})); +foo(E((felt252,u32),Array<{Drop}>),S(E((felt252,u32),Array<{Drop}>),{Drop})) ``` ### How Interfaces are Identified @@ -142,8 +142,8 @@ from starkware.starknet.public.abi import starknet_keccak extended_function_selector_list = [ 'supports_interface(felt252)', 'is_valid_signature(felt252,Array)', - '__execute__(Array<(ContractAddress,felt252,Array)>)', - '__validate__(Array<(ContractAddress,felt252,Array)>)', + '__execute__(Array)>)', + '__validate__(Array)>)', '__validate_declare__(felt252)' ] From 08c61b83bbd1582d051ad183888a1f4af9eea1e3 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 31 May 2023 17:04:25 +0200 Subject: [PATCH 06/22] feat: remove generics --- SNIPS/snip-5.md | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 75f3cba..88cc67a 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -40,7 +40,7 @@ fn_name(param1_type,param2_type,...) Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. Next, we will define how to treat complex types for this signature definition. -### Special types (Tuples, Structs, Enums, and Generics) +### Special types (Tuples, Structs, and Enums) A definition on how to provide these parameters to the signature for getting the extended function selector: @@ -60,52 +60,30 @@ The signature for an enum having `n` fields is: `E(variant1_type,variant2_type,. The leading `E` avoid clashes with similar signatures using tuples or structs. -#### Generics - -The signature for a generic argument required to implement `n` traits is: `{trait1_name,trait2_name,...}`, where `traitN_name` is the type of the n-th trait required from the function signature in the same specified order. - #### Examples 1. From the Cairo function: ```cairo -fn foo>(param1: Array) -> bool; -``` - -The signature is: - -```cairo -foo(Array<{Drop}>) -``` - -2. From the Cairo function: - -```cairo -enum MyEnum { +#[derive(Drop, Serde)] +enum MyEnum { FirstVariant: (felt252, u32), - SecondVariant: Array, + SecondVariant: Array, } -impl MyEnumDrop> of Drop>; - -struct MyStruct { - field1: MyEnum, - field2: E, +#[derive(Drop, Serde)] +struct MyStruct { + field1: MyEnum, + field2: felt252, } -impl MyStructDrop, impl EDrop: Drop> of Drop>; - -fn foo, impl EDrop: Drop>( - param1: MyEnum, param2: MyStruct -) -> bool { - return true; -} +fn foo>(param1: MyEnum, param2: MyStruct) -> bool; ``` The signature is: ```cairo -foo(E((felt252,u32),Array<{Drop}>),S(E((felt252,u32),Array<{Drop}>),{Drop})) +foo(E((felt252,u32),Array),S(E((felt252,u32),Array),felt252)) ``` ### How Interfaces are Identified From 528103b2f6907b2081d0b5af2a1eafb9617343c5 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 1 Jun 2023 18:03:26 +0200 Subject: [PATCH 07/22] feat: add serialization and base type entries --- SNIPS/snip-5.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 88cc67a..714c612 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -30,6 +30,13 @@ For some "standard interfaces" like the ERC-721 token interface, it is sometimes ## Specification +### Serialization compatibility + +One of the objectives of this standard is to ensure that all the contracts which state they implement a given interface, will behave in a similar expected way for callers. In Cairo, the language doesn't enforce an encoding format for Structs and Enums, needing the developers to implement a Serde trait on these types when they are part of an external function signature, and different implementations may lead to incompatibility between contracts exposing the same interface. + +We define that, to be compliant with this standard, Structs and Enums used as parameters of external functions, must +implement the default format given from the `[#derive(Serde)]` attribute, which is: the concatenation of the serialized fields for Structs, and the concatenation of a felt252 acting as a variant identifier and the serialized value for Enums. + ### Extended Function Selector In Starknet, a function selector is the `starknet_keccak` of the function name (ASCII encoded). For this standard we define the extended function selector as the `starknet_keccak` of the function signature, having this signature the following format: @@ -38,11 +45,11 @@ In Starknet, a function selector is the `starknet_keccak` of the function name ( fn_name(param1_type,param2_type,...) ``` -Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. Next, we will define how to treat complex types for this signature definition. +Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types by following the rules defined below. ### Special types (Tuples, Structs, and Enums) -A definition on how to provide these parameters to the signature for getting the extended function selector: +A definition of how to provide these parameters to the signature for getting the extended function selector: #### Tuples @@ -50,9 +57,7 @@ The signature for a tuple of `n` elements is: `(elem1_type,elem2_type,...)`, whe #### Structs -The signature for a struct having `n` fields is: `S(field1_type,field2_type,...)`, where `fieldN_type` is the type of the n-th struct field. - -The leading `S` avoid clashes with similar signatures using tuples. +The signature for a struct having `n` fields is: `(field1_type,field2_type,...)`, where `fieldN_type` is the type of the n-th struct field. #### Enums @@ -67,7 +72,7 @@ The leading `E` avoid clashes with similar signatures using tuples or structs. ```cairo #[derive(Drop, Serde)] enum MyEnum { - FirstVariant: (felt252, u32), + FirstVariant: (felt252, u256), SecondVariant: Array, } @@ -83,7 +88,7 @@ fn foo>(param1: MyEnum, param2: MyStruct) -> bool; The signature is: ```cairo -foo(E((felt252,u32),Array),S(E((felt252,u32),Array),felt252)) +foo(E((felt252,(u128,u128)),Array),(E((felt252,(u128,u128)),Array),felt252)) ``` ### How Interfaces are Identified @@ -120,8 +125,8 @@ from starkware.starknet.public.abi import starknet_keccak extended_function_selector_list = [ 'supports_interface(felt252)', 'is_valid_signature(felt252,Array)', - '__execute__(Array)>)', - '__validate__(Array)>)', + '__execute__(Array<(ContractAddress,felt252,Array)>)', + '__validate__(Array<(ContractAddress,felt252,Array)>)', '__validate_declare__(felt252)' ] From a2c0a6958f0a9562d02a5b93ef191cbd566d5a92 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 2 Jun 2023 12:54:03 +0200 Subject: [PATCH 08/22] fix: typo --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 714c612..c78ba7e 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -82,7 +82,7 @@ struct MyStruct { field2: felt252, } -fn foo>(param1: MyEnum, param2: MyStruct) -> bool; +fn foo(param1: MyEnum, param2: MyStruct) -> bool; ``` The signature is: From 9c9eb80d9b7b5dd5e172d069473d75fe92c59f4b Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 7 Jun 2023 12:32:49 +0200 Subject: [PATCH 09/22] refactor: format --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index c78ba7e..9b8c077 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -65,7 +65,7 @@ The signature for an enum having `n` fields is: `E(variant1_type,variant2_type,. The leading `E` avoid clashes with similar signatures using tuples or structs. -#### Examples +### Examples 1. From the Cairo function: From b4bd19b876d108a8573f7403265f3afffa3d95d6 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 16 Jun 2023 17:56:32 +0200 Subject: [PATCH 10/22] feat: add new syntax entries --- SNIPS/snip-5.md | 51 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 9b8c077..0fedb2d 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -30,26 +30,59 @@ For some "standard interfaces" like the ERC-721 token interface, it is sometimes ## Specification +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + ### Serialization compatibility One of the objectives of this standard is to ensure that all the contracts which state they implement a given interface, will behave in a similar expected way for callers. In Cairo, the language doesn't enforce an encoding format for Structs and Enums, needing the developers to implement a Serde trait on these types when they are part of an external function signature, and different implementations may lead to incompatibility between contracts exposing the same interface. -We define that, to be compliant with this standard, Structs and Enums used as parameters of external functions, must -implement the default format given from the `[#derive(Serde)]` attribute, which is: the concatenation of the serialized fields for Structs, and the concatenation of a felt252 acting as a variant identifier and the serialized value for Enums. +We define that, to be compliant with this standard, Structs and Enums used as parameters of external functions, SHOULD +implement the default format given from the `[#derive(Serde)]` attribute, which is: the concatenation of the serialized fields for Structs, and the concatenation of a felt252 acting as a variant identifier and the serialized value for Enums. Other types MUST use the serialization format specified in the language core library. + +### Interface Blueprints + +Since Cairo 2.0, we can define traits leveraging generic types to represent a set of interfaces: + +```cairo +#[starknet::interface] +trait IMyContract { + fn foo(self: @TContractState, some: TNumber) -> felt252; +} +``` + +For this standard, we call these modules Interface Blueprints, not representing the actual interface, but a set of them. The real interfaces contain only concrete types since no generic type inputs are allowed in smart contract external methods. + +Different implementations of these blueprints, specifyng concrete types, define different interfaces. + +### Interface + +For this standard, an interface is a set of functions with no generic type parameters. + +The actual interface can be represented as a trait with no generic types. From the [Interface Blueprint](#interface-blueprints) presented above, we can the following valid interfaces among others: + +```cairo +trait IMyContract1 { + fn foo(some: u256) -> felt252; +} +// or +trait IMyContract2 { + fn foo(some: felt252) -> felt252; +} +``` ### Extended Function Selector -In Starknet, a function selector is the `starknet_keccak` of the function name (ASCII encoded). For this standard we define the extended function selector as the `starknet_keccak` of the function signature, having this signature the following format: +In Starknet, a function selector is the `starknet_keccak` of the function name (ASCII encoded). For this standard we define the Extended Function Selector as the `starknet_keccak` of the function signature, having this signature the following format: ``` fn_name(param1_type,param2_type,...) ``` -Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types by following the rules defined below. +Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types. ### Special types (Tuples, Structs, and Enums) -A definition of how to provide these parameters to the signature for getting the extended function selector: +A definition of how to provide these parameters to the signature for getting the [Extended Function Selectorr](#extended-function-selector): #### Tuples @@ -82,20 +115,18 @@ struct MyStruct { field2: felt252, } -fn foo(param1: MyEnum, param2: MyStruct) -> bool; +fn foo(param1: @MyEnum, param2: MyStruct) -> bool; ``` The signature is: ```cairo -foo(E((felt252,(u128,u128)),Array),(E((felt252,(u128,u128)),Array),felt252)) +foo(@E((felt252,(u128,u128)),Array),(E((felt252,(u128,u128)),Array),felt252)) ``` ### How Interfaces are Identified -For this standard, an *interface* is a set of [extended function selectors](#extended-function-selector-efns). - -We define the interface identifier as the XOR of all extended function selectors in the interface. This code example shows how to calculate an interface identifier: +For this standard, we define the interface identifier as the XOR of all [Extended Function Selectors](#extended-function-selector) in the [Interface](#inteface). This code example shows how to calculate an interface identifier: From this Cairo interface: From 3abc9f20bc952b118f0a95ffd52a79d79bef5dde Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 16 Jun 2023 17:58:05 +0200 Subject: [PATCH 11/22] fix: typo --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 0fedb2d..6096a1e 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -126,7 +126,7 @@ foo(@E((felt252,(u128,u128)),Array),(E((felt252,(u128,u128)),Array), ### How Interfaces are Identified -For this standard, we define the interface identifier as the XOR of all [Extended Function Selectors](#extended-function-selector) in the [Interface](#inteface). This code example shows how to calculate an interface identifier: +For this standard, we define the interface identifier as the XOR of all [Extended Function Selectors](#extended-function-selector) in the [Interface](#interface). This code example shows how to calculate an interface identifier: From this Cairo interface: From 60c1252fe3cffec0bc4113bfe77c37ff1515190e Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 16 Jun 2023 17:59:22 +0200 Subject: [PATCH 12/22] fix: typo --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 6096a1e..2a0850b 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -82,7 +82,7 @@ Where `fn_name` is the function name, and `paramN_type` is the type of the n-th ### Special types (Tuples, Structs, and Enums) -A definition of how to provide these parameters to the signature for getting the [Extended Function Selectorr](#extended-function-selector): +A definition of how to provide these parameters to the signature for getting the [Extended Function Selector](#extended-function-selector): #### Tuples From 6d685a26777149122f2357fd9b6d67a5bbfa00a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Fri, 16 Jun 2023 16:03:39 -0300 Subject: [PATCH 13/22] Update snip-5.md --- SNIPS/snip-5.md | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 2a0850b..46b6050 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -39,26 +39,9 @@ One of the objectives of this standard is to ensure that all the contracts which We define that, to be compliant with this standard, Structs and Enums used as parameters of external functions, SHOULD implement the default format given from the `[#derive(Serde)]` attribute, which is: the concatenation of the serialized fields for Structs, and the concatenation of a felt252 acting as a variant identifier and the serialized value for Enums. Other types MUST use the serialization format specified in the language core library. -### Interface Blueprints - -Since Cairo 2.0, we can define traits leveraging generic types to represent a set of interfaces: - -```cairo -#[starknet::interface] -trait IMyContract { - fn foo(self: @TContractState, some: TNumber) -> felt252; -} -``` - -For this standard, we call these modules Interface Blueprints, not representing the actual interface, but a set of them. The real interfaces contain only concrete types since no generic type inputs are allowed in smart contract external methods. - -Different implementations of these blueprints, specifyng concrete types, define different interfaces. - ### Interface -For this standard, an interface is a set of functions with no generic type parameters. - -The actual interface can be represented as a trait with no generic types. From the [Interface Blueprint](#interface-blueprints) presented above, we can the following valid interfaces among others: +An interface is a set of function signatures with concrete type parameters, usually represented by a `trait`. These are meant to be implemented as `external` by contracts complying with such interface. For example: ```cairo trait IMyContract1 { @@ -70,6 +53,19 @@ trait IMyContract2 { } ``` +#### Generic types + +Since Cairo 2.0, we can define traits leveraging generic types to represent a set of interfaces: + +```cairo +#[starknet::interface] +trait IMyContract { + fn foo(self: @TContractState, some: TNumber) -> felt252; +} +``` + +Since these traits don't represent actual interfaces, but a category, they're not covered by this standard. SRC-5 interfaces contain only concrete types since no generic type inputs are allowed in smart contract external methods. + ### Extended Function Selector In Starknet, a function selector is the `starknet_keccak` of the function name (ASCII encoded). For this standard we define the Extended Function Selector as the `starknet_keccak` of the function signature, having this signature the following format: From 78eea3e7a8b257601792816ca6de258c1d5a9823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Triay?= Date: Sat, 17 Jun 2023 03:18:31 -0300 Subject: [PATCH 14/22] Update snip-5.md --- SNIPS/snip-5.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 46b6050..1a78bcb 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -55,7 +55,7 @@ trait IMyContract2 { #### Generic types -Since Cairo 2.0, we can define traits leveraging generic types to represent a set of interfaces: +Since Cairo 2.0 we can define traits leveraging generic types to represent a set of interfaces: ```cairo #[starknet::interface] @@ -64,7 +64,7 @@ trait IMyContract { } ``` -Since these traits don't represent actual interfaces, but a category, they're not covered by this standard. SRC-5 interfaces contain only concrete types since no generic type inputs are allowed in smart contract external methods. +Because these traits don't represent actual interfaces but categories, they're not covered by this standard. SRC-5 interfaces contain only concrete types since no generic type inputs are allowed in smart contract external methods. ### Extended Function Selector From 053993f00b2b60a9d417b516a6329aef2edb099a Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 19 Jun 2023 13:26:43 +0200 Subject: [PATCH 15/22] feat: update example --- SNIPS/snip-5.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 1a78bcb..e30abc7 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -44,13 +44,9 @@ implement the default format given from the `[#derive(Serde)]` attribute, which An interface is a set of function signatures with concrete type parameters, usually represented by a `trait`. These are meant to be implemented as `external` by contracts complying with such interface. For example: ```cairo -trait IMyContract1 { +trait IMyContract { fn foo(some: u256) -> felt252; } -// or -trait IMyContract2 { - fn foo(some: felt252) -> felt252; -} ``` #### Generic types From d86eb6a347c60ba67c5d103c6c0de26812cc0587 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 20 Jun 2023 11:36:48 +0200 Subject: [PATCH 16/22] feat: add output to the function signature --- SNIPS/snip-5.md | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index e30abc7..b5c4157 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -11,7 +11,6 @@ created: 2023-05-29 ## Simple Summary A standard method to publish and detect what interfaces a smart contract implements. - Inspired by [ERC-165](https://eips.ethereum.org/EIPS/eip-165). ## Abstract @@ -36,8 +35,7 @@ The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SH One of the objectives of this standard is to ensure that all the contracts which state they implement a given interface, will behave in a similar expected way for callers. In Cairo, the language doesn't enforce an encoding format for Structs and Enums, needing the developers to implement a Serde trait on these types when they are part of an external function signature, and different implementations may lead to incompatibility between contracts exposing the same interface. -We define that, to be compliant with this standard, Structs and Enums used as parameters of external functions, SHOULD -implement the default format given from the `[#derive(Serde)]` attribute, which is: the concatenation of the serialized fields for Structs, and the concatenation of a felt252 acting as a variant identifier and the serialized value for Enums. Other types MUST use the serialization format specified in the language core library. +We define that, to be compliant with this standard, Structs and Enums used as parameters, or return types of external functions SHOULD implement the default format given from the `[#derive(Serde)]` attribute, which is: the concatenation of the serialized fields for Structs, and the concatenation of a felt252 acting as a variant identifier and the serialized value for Enums. Other types MUST use the serialization format specified in the language core library. ### Interface @@ -67,10 +65,12 @@ Because these traits don't represent actual interfaces but categories, they're n In Starknet, a function selector is the `starknet_keccak` of the function name (ASCII encoded). For this standard we define the Extended Function Selector as the `starknet_keccak` of the function signature, having this signature the following format: ``` -fn_name(param1_type,param2_type,...) +fn_name(param1_type,param2_type,...)->output_type ``` -Where `fn_name` is the function name, and `paramN_type` is the type of the n-th function parameter. Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types. +Where `fn_name` is the function name, `paramN_type` is the type of the n-th function parameter, and `output_type` is the type of the returned value. + +Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types. For example, `u256` is represented as `(u128,u128)`, being `u128` a type, and `u256` a Struct. ### Special types (Tuples, Structs, and Enums) @@ -113,7 +113,7 @@ fn foo(param1: @MyEnum, param2: MyStruct) -> bool; The signature is: ```cairo -foo(@E((felt252,(u128,u128)),Array),(E((felt252,(u128,u128)),Array),felt252)) +foo(@E((felt252,(u128,u128)),Array),(E((felt252,(u128,u128)),Array),felt252))->E((),()) ``` ### How Interfaces are Identified @@ -130,33 +130,33 @@ struct Call { } trait IAccount { - fn supports_interface(felt252); - fn is_valid_signature(felt252, Array); - fn __execute__(Array); - fn __validate__(Array); - fn __validate_declare__(felt252); + fn supports_interface(felt252) -> bool; + fn is_valid_signature(felt252, Array) -> bool; + fn __execute__(Array) -> Array>; + fn __validate__(Array) -> felt252; + fn __validate_declare__(felt252) -> felt252; } ``` -This is the python code that computes the interface id: +This is the Python code that computes the interface id: ```python # pip install cairo-lang from starkware.starknet.public.abi import starknet_keccak -# This is the interface -extended_function_selector_list = [ - 'supports_interface(felt252)', - 'is_valid_signature(felt252,Array)', - '__execute__(Array<(ContractAddress,felt252,Array)>)', - '__validate__(Array<(ContractAddress,felt252,Array)>)', - '__validate_declare__(felt252)' +# These are the public interface function signatures +extended_function_selector_signatures_list = [ + '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 main(): interface_id = 0x0 - for function in extended_function_selector_list: - function_id = starknet_keccak(function.encode()) + for function_signature in extended_function_selector_signatures_list: + function_id = starknet_keccak(function_signature.encode()) interface_id ^= function_id print('IAccount ID:') print(hex(interface_id)) @@ -179,11 +179,11 @@ trait ISRC5 { } ``` -The interface identifier for this interface is `0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60`. You can calculate this by running `starknet_keccak('supports_interface(felt252)')`. +The interface identifier for this interface is `0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055`. You can calculate this by running `starknet_keccak('supports_interface(felt252)->E((),())')`. Therefore the implementing contract will have a `supports_interface` function that returns: -- `true` when `interface_id` is `0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60` (SNIP-5 interface) +- `true` when `interface_id` is `0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055` (SNIP-5 interface) - `true` for any other `interface_id` this contract implements - `false` for any other `interface_id` @@ -191,7 +191,7 @@ This function must return a bool. ### How to Detect if a Contract Implements SRC-5 -1. The source contract makes a `call_contract_syscall` to the destination address with `entrypoint_selector` as: `0xfe80f537b66d12a00b6d3c072b44afbb716e78dde5c3f0ef116ee93d3e3283` and calldata as a one element Span containing: `0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60`. This corresponds to `contract.supports_interface(0x1ba86cc668fafde77705c7bfcafa3ee47934b5631ff0e32841dcdcd4e100a60)`. +1. The source contract makes a `call_contract_syscall` to the destination address with `entrypoint_selector` as: `0xfe80f537b66d12a00b6d3c072b44afbb716e78dde5c3f0ef116ee93d3e3283` and calldata as a one element Span containing: `0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055`. This corresponds to `contract.supports_interface(0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055)`. 2. If the call fails or returns false, the destination contract does not implement SRC-5. 5. Otherwise it implements SRC-5. From 6107d0fda839b67db06ef3dc28dc911108b6eef7 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 20 Jun 2023 11:39:09 +0200 Subject: [PATCH 17/22] fix: casing --- SNIPS/snip-5.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index b5c4157..dcb5e7b 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -31,7 +31,7 @@ For some "standard interfaces" like the ERC-721 token interface, it is sometimes The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). -### Serialization compatibility +### Serialization Compatibility One of the objectives of this standard is to ensure that all the contracts which state they implement a given interface, will behave in a similar expected way for callers. In Cairo, the language doesn't enforce an encoding format for Structs and Enums, needing the developers to implement a Serde trait on these types when they are part of an external function signature, and different implementations may lead to incompatibility between contracts exposing the same interface. @@ -47,7 +47,7 @@ trait IMyContract { } ``` -#### Generic types +#### Generic Types Since Cairo 2.0 we can define traits leveraging generic types to represent a set of interfaces: @@ -72,7 +72,7 @@ Where `fn_name` is the function name, `paramN_type` is the type of the n-th func Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types. For example, `u256` is represented as `(u128,u128)`, being `u128` a type, and `u256` a Struct. -### Special types (Tuples, Structs, and Enums) +### Special Types (Tuples, Structs, and Enums) A definition of how to provide these parameters to the signature for getting the [Extended Function Selector](#extended-function-selector): From d9448f8ed2db273c7dd56918b412fdb10bc35973 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 20 Jun 2023 14:09:58 +0200 Subject: [PATCH 18/22] feat: add entry for empty return value --- SNIPS/snip-5.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index dcb5e7b..24d1a41 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -70,6 +70,12 @@ fn_name(param1_type,param2_type,...)->output_type Where `fn_name` is the function name, `paramN_type` is the type of the n-th function parameter, and `output_type` is the type of the returned value. +The signature for a function with zero parameters and no return value is: + +``` +fn_name() +``` + Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types. For example, `u256` is represented as `(u128,u128)`, being `u128` a type, and `u256` a Struct. ### Special Types (Tuples, Structs, and Enums) From ceeb1049fdba314fc7bd0cc2ab37b7fa788cc62e Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 26 Jun 2023 14:10:32 +0200 Subject: [PATCH 19/22] feat: add link to corelib --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 24d1a41..51bb43d 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -76,7 +76,7 @@ The signature for a function with zero parameters and no return value is: fn_name() ``` -Types are those defined as such in the corelib (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types. For example, `u256` is represented as `(u128,u128)`, being `u128` a type, and `u256` a Struct. +Types are those defined as such in the [corelib](https://github.com/starkware-libs/cairo/blob/main/corelib/src/lib.cairo) (ex: `type felt252`). Tuples, Structs, and Enums are treated as special types. For example, `u256` is represented as `(u128,u128)`, being `u128` a type, and `u256` a Struct. ### Special Types (Tuples, Structs, and Enums) From 9daf66a69bd75caa7d57cdad4baac3133fe6b7ef Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 3 Jul 2023 14:31:31 +0200 Subject: [PATCH 20/22] fix: word case --- SNIPS/snip-5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index 51bb43d..cbb5b02 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -193,7 +193,7 @@ Therefore the implementing contract will have a `supports_interface` function th - `true` for any other `interface_id` this contract implements - `false` for any other `interface_id` -This function must return a bool. +This function MUST return a bool. ### How to Detect if a Contract Implements SRC-5 From 64471767eb53f2b8b077cbfcf82f9645ea778e39 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 6 Jul 2023 16:33:19 +0200 Subject: [PATCH 21/22] feat: add discussions link --- SNIPS/snip-5.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index cbb5b02..bad5bf5 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -2,6 +2,7 @@ snip: 5 title: Standard Interface Detection author: Eric Nordelo +discussions-to: https://community.starknet.io/t/starknet-standard-interface-detection/92664/30 status: Draft type: Standards Track category: SRC From 3d17f1d468f8ccaaa360c73cb06faef46d92daf3 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 12 Jul 2023 13:21:11 +0200 Subject: [PATCH 22/22] feat: improve generic types section --- SNIPS/snip-5.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SNIPS/snip-5.md b/SNIPS/snip-5.md index bad5bf5..a3136ad 100644 --- a/SNIPS/snip-5.md +++ b/SNIPS/snip-5.md @@ -59,7 +59,9 @@ trait IMyContract { } ``` -Because these traits don't represent actual interfaces but categories, they're not covered by this standard. SRC-5 interfaces contain only concrete types since no generic type inputs are allowed in smart contract external methods. +Notice that these traits don't represent the actual public interface of a specific contract, but sort of a category of them. The generic `TContractState` type self parameter is not included in the exposed API of the function. It is used internally to restrict how the function can access the local storage, and is not part of the public API of the function. The `TNumber` type parameter must be concretized in the implementation, because generic type parameters are not allowed in contract's external functions. + +To this standard, generic traits anotated with the `#[starknet::interface]` attribute represent a set of interfaces, while each real interface can be represented as a non-generic trait, as presented above. ### Extended Function Selector