diff --git a/listings/advanced-concepts/store_using_packing/src/contract.cairo b/listings/advanced-concepts/store_using_packing/src/contract.cairo index 5cdb79b5..5f124ac2 100644 --- a/listings/advanced-concepts/store_using_packing/src/contract.cairo +++ b/listings/advanced-concepts/store_using_packing/src/contract.cairo @@ -24,14 +24,14 @@ pub mod TimeContract { fn pack(value: Time) -> felt252 { let msb: felt252 = 256 * value.hour.into(); let lsb: felt252 = value.minute.into(); - return msb + lsb; + msb + lsb } fn unpack(value: felt252) -> Time { let value: u16 = value.try_into().unwrap(); let (q, r) = DivRem::div_rem(value, 256_u16.try_into().unwrap()); let hour: u8 = Into::::into(q).try_into().unwrap(); let minute: u8 = Into::::into(r).try_into().unwrap(); - return Time { hour, minute }; + Time { hour, minute } } } diff --git a/listings/advanced-concepts/storing_arrays/src/contract.cairo b/listings/advanced-concepts/storing_arrays/src/contract.cairo index 949e0e8a..991631be 100644 --- a/listings/advanced-concepts/storing_arrays/src/contract.cairo +++ b/listings/advanced-concepts/storing_arrays/src/contract.cairo @@ -1,7 +1,6 @@ use starknet::SyscallResultTrait; use starknet::{Store, SyscallResult}; -use starknet::storage_access::{StorageBaseAddress, storage_address_from_base_and_offset}; -use starknet::syscalls::{storage_read_syscall, storage_write_syscall}; +use starknet::storage_access::StorageBaseAddress; // ANCHOR: StorageAccessImpl impl StoreFelt252Array of Store> { @@ -20,7 +19,7 @@ impl StoreFelt252Array of Store> { ) -> SyscallResult> { let mut arr: Array = array![]; - // Read the stored array's length. If the length is superior to 255, the read will fail. + // Read the stored array's length. If the length is greater than 255, the read will fail. let len: u8 = Store::::read_at_offset(address_domain, base, offset) .expect('Storage Span too large'); offset += 1; @@ -44,7 +43,7 @@ impl StoreFelt252Array of Store> { fn write_at_offset( address_domain: u32, base: StorageBaseAddress, mut offset: u8, mut value: Array ) -> SyscallResult<()> { - // // Store the length of the array in the first storage slot. + // Store the length of the array in the first storage slot. let len: u8 = value.len().try_into().expect('Storage - Span too large'); Store::::write_at_offset(address_domain, base, offset, len).unwrap(); offset += 1; diff --git a/listings/advanced-concepts/using_lists/src/contract.cairo b/listings/advanced-concepts/using_lists/src/contract.cairo index 35101fa8..9ee274db 100644 --- a/listings/advanced-concepts/using_lists/src/contract.cairo +++ b/listings/advanced-concepts/using_lists/src/contract.cairo @@ -23,14 +23,14 @@ pub mod ListExample { #[storage] pub struct Storage { - amount: List, + amounts: List, tasks: List } #[abi(embed_v0)] impl ListExample of super::IListExample { fn add_in_amount(ref self: ContractState, number: u128) { - let mut current_amount_list = self.amount.read(); + let mut current_amount_list = self.amounts.read(); current_amount_list.append(number).unwrap(); } @@ -41,31 +41,31 @@ pub mod ListExample { } fn is_empty_list(self: @ContractState) -> bool { - let mut current_amount_list = self.amount.read(); + let mut current_amount_list = self.amounts.read(); current_amount_list.is_empty() } fn list_length(self: @ContractState) -> u32 { - let mut current_amount_list = self.amount.read(); + let mut current_amount_list = self.amounts.read(); current_amount_list.len() } fn get_from_index(self: @ContractState, index: u32) -> u128 { - self.amount.read()[index] + self.amounts.read()[index] } fn set_from_index(ref self: ContractState, index: u32, number: u128) { - let mut current_amount_list = self.amount.read(); + let mut current_amount_list = self.amounts.read(); current_amount_list.set(index, number).unwrap(); } fn pop_front_list(ref self: ContractState) { - let mut current_amount_list = self.amount.read(); + let mut current_amount_list = self.amounts.read(); current_amount_list.pop_front().unwrap().unwrap(); } fn array_conversion(self: @ContractState) -> Array { - let mut current_amount_list = self.amount.read(); + let mut current_amount_list = self.amounts.read(); current_amount_list.array().unwrap() } } diff --git a/listings/advanced-concepts/using_lists/src/tests.cairo b/listings/advanced-concepts/using_lists/src/tests.cairo index 8ce5c390..98f667c3 100644 --- a/listings/advanced-concepts/using_lists/src/tests.cairo +++ b/listings/advanced-concepts/using_lists/src/tests.cairo @@ -1,7 +1,7 @@ use using_lists::contract::IListExample; use using_lists::contract::{Task, ListExample}; use using_lists::contract::ListExample::{ - amountContractMemberStateTrait, tasksContractMemberStateTrait + amountsContractMemberStateTrait, tasksContractMemberStateTrait }; fn STATE() -> ListExample::ContractState { @@ -13,7 +13,7 @@ fn STATE() -> ListExample::ContractState { fn test_add_in_amount() { let mut state = STATE(); state.add_in_amount(200); - assert(state.amount.read()[0] == 200, 'should be 200'); + assert(state.amounts.read()[0] == 200, 'should be 200'); } #[test] @@ -67,7 +67,7 @@ fn test_set_from_index() { let mut state = STATE(); state.add_in_amount(200); state.set_from_index(0, 400); - assert(state.amount.read()[0] == 400, 'should be 400'); + assert(state.amounts.read()[0] == 400, 'should be 400'); } #[test] diff --git a/po/es.po b/po/es.po index 803c2c97..3adf59e9 100644 --- a/po/es.po +++ b/po/es.po @@ -10840,7 +10840,7 @@ msgid "" "\n" " #[storage]\n" " struct Storage {\n" -" amount: List,\n" +" amounts: List,\n" " tasks: List\n" " }\n" "\n" @@ -10854,7 +10854,7 @@ msgid "" " #[abi(embed_v0)]\n" " impl ListExample of super::IListExample {\n" " fn add_in_amount(ref self: ContractState, number: u128) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.append(number);\n" " }\n" "\n" @@ -10867,32 +10867,32 @@ msgid "" " }\n" "\n" " fn is_empty_list(self: @ContractState) -> bool {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.is_empty()\n" " }\n" "\n" " fn list_length(self: @ContractState) -> u32 {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.len()\n" " }\n" "\n" " fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" -" self.amount.read()[index]\n" +" self.amounts.read()[index]\n" " }\n" "\n" " fn set_from_index(ref self: ContractState, index: u32, number: u128) " "{\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.set(index, number);\n" " }\n" "\n" " fn pop_front_list(ref self: ContractState) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.pop_front();\n" " }\n" "\n" " fn array_conversion(self: @ContractState) -> Array {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.array()\n" " }\n" " }\n" @@ -10933,7 +10933,7 @@ msgstr "" " #[abi(embed_v0)]\n" " impl ListExample of super::IListExample {\n" " fn add_in_amount(ref self: ContractState, number: u128) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.append(number);\n" " }\n" "\n" @@ -10946,32 +10946,32 @@ msgstr "" " }\n" "\n" " fn is_empty_list(self: @ContractState) -> bool {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.is_empty()\n" " }\n" "\n" " fn list_length(self: @ContractState) -> u32 {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.len()\n" " }\n" "\n" " fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" -" self.amount.read()[index]\n" +" self.amounts.read()[index]\n" " }\n" "\n" " fn set_from_index(ref self: ContractState, index: u32, number: u128) " "{\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.set(index, number);\n" " }\n" "\n" " fn pop_front_list(ref self: ContractState) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.pop_front();\n" " }\n" "\n" " fn array_conversion(self: @ContractState) -> Array {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.array()\n" " }\n" " }\n" diff --git a/po/zh-cn.po b/po/zh-cn.po index 90953985..3ff5d71c 100644 --- a/po/zh-cn.po +++ b/po/zh-cn.po @@ -6945,7 +6945,7 @@ msgid "" "\n" " #[storage]\n" " struct Storage {\n" -" amount: List,\n" +" amounts: List,\n" " tasks: List\n" " }\n" "\n" @@ -6959,7 +6959,7 @@ msgid "" " #[abi(embed_v0)]\n" " impl ListExample of super::IListExample {\n" " fn add_in_amount(ref self: ContractState, number: u128) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.append(number);\n" " }\n" "\n" @@ -6972,32 +6972,32 @@ msgid "" " }\n" "\n" " fn is_empty_list(self: @ContractState) -> bool {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.is_empty()\n" " }\n" "\n" " fn list_length(self: @ContractState) -> u32 {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.len()\n" " }\n" "\n" " fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" -" self.amount.read()[index]\n" +" self.amounts.read()[index]\n" " }\n" "\n" " fn set_from_index(ref self: ContractState, index: u32, number: u128) " "{\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.set(index, number);\n" " }\n" "\n" " fn pop_front_list(ref self: ContractState) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.pop_front();\n" " }\n" "\n" " fn array_conversion(self: @ContractState) -> Array {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.array()\n" " }\n" " }\n" @@ -7038,7 +7038,7 @@ msgstr "" " #[abi(embed_v0)]\n" " impl ListExample of super::IListExample {\n" " fn add_in_amount(ref self: ContractState, number: u128) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.append(number);\n" " }\n" "\n" @@ -7051,32 +7051,32 @@ msgstr "" " }\n" "\n" " fn is_empty_list(self: @ContractState) -> bool {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.is_empty()\n" " }\n" "\n" " fn list_length(self: @ContractState) -> u32 {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.len()\n" " }\n" "\n" " fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" -" self.amount.read()[index]\n" +" self.amounts.read()[index]\n" " }\n" "\n" " fn set_from_index(ref self: ContractState, index: u32, number: u128) " "{\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.set(index, number);\n" " }\n" "\n" " fn pop_front_list(ref self: ContractState) {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.pop_front();\n" " }\n" "\n" " fn array_conversion(self: @ContractState) -> Array {\n" -" let mut current_amount_list = self.amount.read();\n" +" let mut current_amount_list = self.amounts.read();\n" " current_amount_list.array()\n" " }\n" " }\n" diff --git a/src/ch02/hashing.md b/src/ch02/hashing.md index ad6377c3..54c233d8 100644 --- a/src/ch02/hashing.md +++ b/src/ch02/hashing.md @@ -5,11 +5,11 @@ The resulting output is called a hash and it's completely different from the inp Hash functions are deterministic, meaning that the same input will always produce the same output. The two hash functions provided by the Cairo library are `Poseidon` and `Pedersen`. -Pedersen hashes were used in the past (but still used in some scenario for backward compatibility) while Poseidon hashes are the standard nowadays since they were designed to be very efficient for Zero Knowledge proof systems. +Pedersen hashes were used in the past (but are still used in some scenarios for backward compatibility), while Poseidon hashes are the standard nowadays since they were designed to be very efficient for Zero Knowledge proof systems. -In Cairo it's possible to hash all the types that can be converted to `felt252` since they implement natively the `Hash` trait. It's also possible to hash more complex types like structs by deriving the Hash trait with the attribute `#[derive(Hash)]` but only if all the struct's fields are themselves hashable. +In Cairo, it's possible to hash all types that can be converted to `felt252` since they natively implement the `Hash` trait. It's also possible to hash more complex types, like structs, by deriving the Hash trait with the `#[derive(Hash)]` attribute, but only if all the struct's fields are themselves hashable. -You first need to initialize a hash state with the `new` method of the `HashStateTrait` and then you can update it with the `update` method. You can accumulate multiple updates. Then, the `finalize` method returns the final hash value as a `felt252`. +To hash a value, you first need to initialize a hash state with the `new` method of the `HashStateTrait`. Then, you can update the hash state with the `update` method. You can accumulate multiple updates if necessary. Finally, the `finalize` method returns the final hash value as a `felt252`. ```rust {{#rustdoc_include ../../listings/advanced-concepts/hash_trait/src/hash_trait.cairo:hash}} diff --git a/src/ch02/library_calls.md b/src/ch02/library_calls.md index 85813bba..f33aff17 100644 --- a/src/ch02/library_calls.md +++ b/src/ch02/library_calls.md @@ -1,4 +1,4 @@ -# Library Dispatcher +# Library Calls External calls can be made on Starknet by two means: Contract dispatchers or Library dispatchers. Dispatchers are automatically created and exported by the compiler when a contract interface is defined. @@ -11,6 +11,3 @@ For further reading: [Cairo book](https://book.cairo-lang.org/ch15-02-contract-d ```rust {{#rustdoc_include ../../listings/advanced-concepts/library_calls/src/library_call.cairo:library_dispatcher}} ``` - - - diff --git a/src/ch02/list.md b/src/ch02/list.md index 637f6123..3bcc5fa1 100644 --- a/src/ch02/list.md +++ b/src/ch02/list.md @@ -1,6 +1,6 @@ # List -By default, there is no list type supported in Cairo, but you can use Alexandria. You can refer to the [Alexandria documentation](https://github.com/keep-starknet-strange/alexandria/tree/main/src/storage) for more details. +By default, there is no list type supported in Cairo, but you can use the Alexandria standard library. You can refer to the [Alexandria documentation](https://github.com/keep-starknet-strange/alexandria/tree/main/packages/storage) for more details. ## What is `List`? @@ -37,13 +37,13 @@ Note that unlike `get`, using this bracket notation panics when accessing an out ### Support for custom types -`List` supports most of the corelib types out of the box. If you want to store a your own custom type in a `List`, it has to implement the `Store` trait. You can have the compiler derive it for you using the `#[derive(starknet::Store)]` attribute. +`List` supports most of the corelib types out of the box. If you want to store your own custom type in a `List`, you have to implement the `Store` trait for it. You can have the compiler derive it for you using the `#[derive(starknet::Store)]` attribute. ### Caveats -There are two idiosyncacies you should be aware of when using `List` +There are two idiosyncrasies you should be aware of when using `List`: -1. The `append` operation costs 2 storage writes - one for the value itself and another one for updating the List's length +1. The `append` operation costs 2 storage writes - one for the value itself, and another one for updating the List's length 2. Due to a compiler limitation, it is not possible to use mutating operations with a single inline statement. For example, `self.amounts.read().append(42);` will not work. You have to do it in 2 steps: ```rust @@ -53,7 +53,8 @@ amounts.append(42); ### Dependencies -Update your project dependencies by in the `Scarb.toml` file: +Update your project dependencies in the `Scarb.toml` file: + ```rust [dependencies] (...) @@ -64,4 +65,4 @@ For example, let's use `List` to create a contract that tracks a list of amounts ```rust {{#include ../../listings/advanced-concepts/using_lists/src/contract.cairo}} -``` \ No newline at end of file +``` diff --git a/src/ch02/optimisations/store_using_packing.md b/src/ch02/optimisations/store_using_packing.md index 8b736797..c9317604 100644 --- a/src/ch02/optimisations/store_using_packing.md +++ b/src/ch02/optimisations/store_using_packing.md @@ -1,4 +1,4 @@ -# Storage optimisation +# Storage optimisation A smart contract has a limited amount of **storage slots**. Each slot can store a single `felt252` value. Writing to a storage slot has a cost, so we want to use as few storage slots as possible. @@ -8,9 +8,9 @@ This design is quite simple, but it does have a drawback: it is not storage effi ## Packing -When storing multiple values, we can use a technique called **packing**. Packing is a technique that allows us to store multiple values in a single felt value. This is done by using the bits of the felt value to store multiple values. +When storing multiple values, we can use a technique called **packing**. Packing is a technique that allows us to store multiple values in a single `felt252` value. This is done by using the bits of the `felt252` value to store multiple values. -For example, if we want to store two `u8` values, we can use the first 8 bits of the felt value to store the first `u8` value, and the last 8 bits to store the second `u8` value. This way, we can store two `u8` values in a single felt value. +For example, if we want to store two `u8` values, we can use the first 8 bits of the `felt252` value to store the first `u8` value, and the last 8 bits to store the second `u8` value. This way, we can store two `u8` values in a single `felt252` value. Cairo provides a built-in store using packing that you can use with the `StorePacking` trait. @@ -21,7 +21,7 @@ trait StorePacking { } ``` -This allows to store the type `T` by first packing it into the type `PackedT` with the `pack` function, and then storing the `PackedT` value with it's `Store` implementation. When reading the value, we first retrieve the `PackedT` value, and then unpack it into the type `T` using the `unpack` function. +This allows us to store the type `T` by first packing it into the type `PackedT` with the `pack` function, and then storing the `PackedT` value with it's `Store` implementation. When reading the value, we first retrieve the `PackedT` value, and then unpack it into the type `T` using the `unpack` function. Here's an example of storing a `Time` struct with two `u8` values using the `StorePacking` trait: diff --git a/src/ch02/plugins.md b/src/ch02/plugins.md index 355756b6..b037ab84 100644 --- a/src/ch02/plugins.md +++ b/src/ch02/plugins.md @@ -1,7 +1,7 @@ # Plugins -Compilers plugin in Cairo are a way to generate additional code for specific items during the compilation process. +Compiler plugins in Cairo are a way to generate additional code for specific items during the compilation process. There are already a set of core plugins that you may have already used, such as `#[derive]`, `#[cfg]`, `#[generate_trait]`, etc. -It is possible to create your own plugins in rust. You can learn more about how to do that in the [hello-cairo-plugin](https://github.com/piwonskp/hello-cairo-plugin) repository. +It is possible to create your own plugins in Cairo. You can learn more about how to do that in the [hello-cairo-plugin](https://github.com/piwonskp/hello-cairo-plugin) repository. diff --git a/src/ch02/signature_verification.md b/src/ch02/signature_verification.md index 3cbd8dc6..7c51ca50 100644 --- a/src/ch02/signature_verification.md +++ b/src/ch02/signature_verification.md @@ -1,6 +1,6 @@ # ECDSA Verification -This is the Cairo adaptation of the [Solidity by example Verifying Signature](https://solidity-by-example.org/signature/) +This is the Cairo adaptation of the [Solidity by Example - Verifying Signature](https://solidity-by-example.org/signature/). Messages can be signed off chain and then verified on chain using a smart contract. ```rust diff --git a/src/ch02/storing_arrays.md b/src/ch02/storing_arrays.md index b3bfeb77..d980a7f4 100644 --- a/src/ch02/storing_arrays.md +++ b/src/ch02/storing_arrays.md @@ -1,8 +1,8 @@ # Storing Arrays -On Starknet, complex values (e.g., tuples or structs), are stored in a continuous segment starting from the address of the storage variable. There is a 256 field elements limitation to the maximal size of a complex storage value, meaning that to store arrays of more than 255 elements in storage, we would need to split it into segments of size `n <= 255` and store these segments in multiple storage addresses. There is currently no native support for storing arrays in Cairo, so you will need to write your own implementation of the `Store` trait for the type of array you wish to store. +On Starknet, complex values (e.g. tuples or structs) are stored in a continuous segment starting from the address of the storage variable. There is a limitation in Cairo that restricts complex storage values to a maximum of 256 field elements. This means that to store arrays with more than 255 elements, you would have to split them into segments of size `n <= 255` and store these segments at multiple storage addresses. There is currently no native support for storing arrays in Cairo, so you would need to write your own implementation of the `Store` trait for the array type you wish to store. -However, the `ByteArray` struct can be used to store `Array` in storage without additional implementation. Before implementing your own `Store` trait, consider wether the `ByteArray` struct can be used to store the data you need! See the [ByteArray](../ch00/basics/bytearrays-strings.md#bytearray-long-strings) section for more information. +However, the `ByteArray` struct can be used to store `Array` in storage without additional implementation. Before implementing your own `Store` trait, consider whether the `ByteArray` struct can be used to store the data you need! See the [ByteArray](../ch00/basics/bytearrays-strings.md#bytearray-long-strings) section for more information. > Note: While storing arrays in storage is possible, it is not always recommended, as the read and write operations can get very costly. For example, reading an array of size `n` requires `n` storage reads, and writing to an array of size `n` requires `n` storage writes. If you only need to access a single element of the array at a time, it is recommended to use a `LegacyMap` and store the length in another variable instead. diff --git a/src/ch02/write_to_any_slot.md b/src/ch02/write_to_any_slot.md index 9c677818..ef957757 100644 --- a/src/ch02/write_to_any_slot.md +++ b/src/ch02/write_to_any_slot.md @@ -1,6 +1,6 @@ # Writing to any storage slot -On Starknet, a contract's storage is a map with 2^251 slots, where each slot is a felt which is initialized to 0. +On Starknet, a contract's storage is a map with \\( 2^{251} \\) slots, where each slot is a `felt252` which is initialized to 0. The address of storage variables is computed at compile time using the formula: `storage variable address := pedersen(keccak(variable name), keys)`. Interactions with storage variables are commonly performed using the `self.var.read()` and `self.var.write()` functions. Nevertheless, we can use the `storage_write_syscall` and `storage_read_syscall` syscalls, to write to and read from any storage slot. diff --git a/src/components/how_to.md b/src/components/how_to.md index 41e18f86..30f8cbea 100644 --- a/src/components/how_to.md +++ b/src/components/how_to.md @@ -33,7 +33,7 @@ It doesn't have a constructor, but you can create an `_init` internal function a > It's currently not possible to use the same component multiple times in the same contract. > This is a known limitation that may be lifted in the future. > -> For now, you can view components as implementations of a specific interfaces or features (`Ownable`, `Upgradeable`, ... `~able`). +> For now, you can view components as implementations of specific interfaces or features (`Ownable`, `Upgradeable`, ... `~able`). > This is why we called the component in the above example `Switchable`, and not `Switch`; the contract _is switchable_, it does not _have a switch_. ## How to use a component