diff --git a/CHANGELOG.md b/CHANGELOG.md index a19642e90..f5d62baa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Utilities documentation (#825) + ### Changed - Use ComponentState in tests (#836) + + diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 6e442514d..6b918f1b8 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -28,6 +28,6 @@ ** xref:/api/introspection.adoc[API Reference] // * xref:udc.adoc[Universal Deployer Contract] -// * xref:utilities.adoc[Utilities] +* xref:utilities.adoc[Utilities] * xref:contracts::index.adoc[Contracts for Solidity] diff --git a/docs/modules/ROOT/pages/interfaces.adoc b/docs/modules/ROOT/pages/interfaces.adoc index 44125f15c..892d699d4 100644 --- a/docs/modules/ROOT/pages/interfaces.adoc +++ b/docs/modules/ROOT/pages/interfaces.adoc @@ -167,13 +167,13 @@ This is done by simply executing the `snake_case` version of the function (e.g. ```javascript fn try_selector_with_fallback( - target: ContractAddress, snake_selector: felt252, camel_selector: felt252, args: Span + target: ContractAddress, selector: felt252, fallback: felt252, args: Span ) -> SyscallResult> { - match call_contract_syscall(target, snake_selector, args) { + match call_contract_syscall(target, selector, args) { Result::Ok(ret) => Result::Ok(ret), Result::Err(errors) => { if *errors.at(0) == 'ENTRYPOINT_NOT_FOUND' { - return call_contract_syscall(target, camel_selector, args); + return call_contract_syscall(target, fallback, args); } else { Result::Err(errors) } diff --git a/docs/modules/ROOT/pages/utilities.adoc b/docs/modules/ROOT/pages/utilities.adoc index a313c5558..ad29ed2a1 100644 --- a/docs/modules/ROOT/pages/utilities.adoc +++ b/docs/modules/ROOT/pages/utilities.adoc @@ -1,320 +1,290 @@ = Utilities -The following documentation provides context, reasoning, and examples for methods and constants found in `tests/utils.py`. +The following documentation provides reasoning and examples for functions and constants found in `openzeppelin::utils` +and `openzeppelin::tests::utils`. CAUTION: Expect this module to evolve (as it has already done). -== Table of Contents +== Core utilities -* <> -* <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> - ** <> -* <> - ** <> - ** <> -* <> -* <> -* <> +[.contract] +[[utils]] +=== `++utils++` -== Constants +```javascript +use openzeppelin::utils; +``` -To ease the readability of Cairo contracts, this project includes reusable https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.6.1/src/openzeppelin/utils/constants/library.cairo[constant variables] like `UINT8_MAX`, or EIP165 interface IDs such as `IERC165_ID` or `IERC721_ID`. -For more information on how interface ids are calculated, see the xref:introspection.adoc#interface_calculations[ERC165 documentation]. +Module containing core utilities of the library. -== Strings +[.contract-index] +.Members +-- +.Functions +* xref:#utils-try_selector_with_fallback[`++try_selector_with_fallback(target, selector, fallback, args)++`] -Cairo currently only provides support for short string literals (less than 32 characters). -Note that short strings aren't really strings, rather, they're representations of Cairo field elements. -The following methods provide a simple conversion to/from field elements. +.Traits +* xref:#utils-UnwrapAndCast[`++UnwrapAndCast++`] -=== `str_to_felt` +.Inner modules +* xref:#utils-selectors[`++selectors++`] +* xref:#utils-serde[`++serde++`] +-- -Takes an ASCII string and converts it to a field element via big endian representation. +[#utils-Functions] +==== Functions -=== `felt_to_str` +[.contract-item] +[[utils-try_selector_with_fallback]] +==== `[.contract-item-name]#++try_selector_with_fallback++#++(target: ContractAddress, selector: felt252, fallback: felt252, args: Span) → SyscallResult>++` [.item-kind]#function# -Takes an integer and converts it to an ASCII string by trimming the null bytes and decoding the remaining bits. +Tries to call a given selector on a given contract, and if it fails, tries to call a fallback selector. -== Uint256 +It was designed for falling back to the `camelCase` selector for backward compatiblity in the +case of a failure of the `snake_case` selector. -Cairo's native data type is a field element (felt). -Felts equate to 252 bits which poses a problem regarding 256-bit integer integration. -To resolve the bit discrepancy, Cairo represents 256-bit integers as a struct of two 128-bit integers. -Further, the low bits precede the high bits e.g. +Returns a `SyscallResult` with the result of the successful call. -[,python] ----- -1 = (1, 0) -1 << 128 = (0, 1) -(1 << 128) - 1 = (340282366920938463463374607431768211455, 0) ----- +Note that: -=== `uint` +- If the first call succeeds, the second call is not attempted. -Converts a simple integer into a uint256-ish tuple. +- If the first call fails with an error different than `ENTRYPOINT_NOT_FOUND`, the error is returned +without falling back to the second selector. -____ -Note `to_uint` should be used in favor of `uint`, as `uint` only returns the low bits of the tuple. -____ +- If the first call fails with `ENTRYPOINT_NOT_FOUND`, the second call is attempted, and if it fails its +error is returned. -=== `to_uint` +WARNING: The fallback mechanism won't work on live chains (mainnet or testnets) until +they implement panic handling in their runtime. -Converts an integer into a uint256-ish tuple. +[#utils-Traits] +==== Traits -[,python] ----- -x = to_uint(340282366920938463463374607431768211456) -print(x) -# prints (0, 1) ----- - -=== `from_uint` - -Converts a uint256-ish tuple into an integer. - -[,python] ----- -x = (0, 1) -y = from_uint(x) -print(y) -# prints 340282366920938463463374607431768211456 ----- - -=== `add_uint` - -Performs addition between two uint256-ish tuples and returns the sum as a uint256-ish tuple. - -[,python] ----- -x = (0, 1) -y = (1, 0) -z = add_uint(x, y) -print(z) -# prints (1, 1) ----- - -=== `sub_uint` - -Performs subtraction between two uint256-ish tuples and returns the difference as a uint256-ish tuple. - -[,python] ----- -x = (0, 1) -y = (1, 0) -z = sub_uint(x, y) -print(z) -# prints (340282366920938463463374607431768211455, 0) ----- - -=== `mul_uint` - -Performs multiplication between two uint256-ish tuples and returns the product as a uint256-ish tuple. - -[,python] ----- -x = (0, 10) -y = (2, 0) -z = mul_uint(x, y) -print(z) -# prints (0, 20) ----- - -=== `div_rem_uint` - -Performs division between two uint256-ish tuples and returns both the quotient and remainder as uint256-ish tuples respectively. - -[,python] ----- -x = (1, 100) -y = (0, 25) -z = div_rem_uint(x, y) -print(z) -# prints ((4, 0), (1, 0)) ----- - -== Assertions - -In order to abstract away some of the verbosity regarding test assertions on StarkNet transactions, this project includes the following helper methods: - -=== `assert_revert` - -An asynchronous wrapper method that executes a try-except pattern for transactions that should fail. -Note that this wrapper does not check for a StarkNet error code. -This allows for more flexibility in checking that a transaction simply failed. -If you wanted to check for an exact error code, you could use StarkNet's https://github.com/starkware-libs/cairo-lang/blob/ed6cf8d6cec50a6ad95fa36d1eb4a7f48538019e/src/starkware/starknet/definitions/error_codes.py[error_codes module] and implement additional logic to the `assert_revert` method. - -To successfully use this wrapper, the transaction method should be wrapped with `assert_revert`; -however, `await` should precede the wrapper itself like this: - -[,python] ----- -await assert_revert(signer.send_transaction( - account, contract.contract_address, 'foo', [ - recipient, - *token - ]) -) ----- - -This wrapper also includes the option to check that an error message was included in the reversion. -To check that the reversion sends the correct error message, add the `reverted_with` keyword argument outside of the actual transaction (but still inside the wrapper) like this: - -[,python] ----- -await assert_revert(signer.send_transaction( - account, contract.contract_address, 'foo', [ - recipient, - *token - ]), - reverted_with="insert error message here" -) ----- - -=== `assert_revert_entry_point` - -An extension of `assert_revert` that asserts an entry point error occurs with the given `invalid_selector` parameter. -This assertion is especially useful in checking proxy/implementation contracts. -To use `assert_revert_entry_point`: - -[,python] ----- -await assert_revert_entry_point( - signer.send_transaction( - account, contract.contract_address, 'nonexistent_selector', [] - ), - invalid_selector='nonexistent_selector' -) ----- - -=== `assert_event_emitted` - -A helper method that checks a transaction receipt for the contract emitting the event (`from_address`), the emitted event itself (`name`), and the arguments emitted (`data`). -To use `assert_event_emitted`: - -[,python] ----- -# capture the tx receipt -tx_exec_info = await signer.send_transaction( - account, contract.contract_address, 'foo', [ - recipient, - *token - ]) - -# insert arguments to assert -assert_event_emitted( - tx_exec_info, - from_address=contract.contract_address, - name='Foo_emitted', - data=[ - account.contract_address, - recipient, - *token - ] -) ----- - -== Memoization - -Memoizing functions allow for quicker and computationally cheaper calculations which is immensely beneficial while testing smart contracts. - -=== `get_contract_class` - -A helper method that returns the contract class from the contract's name. -To capture the contract class, simply add the contract's name as an argument like this: - -[,python] ----- -contract_class = get_contract_class('ContractName') ----- - -If multiple contracts exist with the same name, then the contract's path must be passed along with the `is_path` flag instead of the name. -To pass the contract's path: - -[,python] ----- -contract_class = get_contract_class('path/to/Contract.cairo', is_path=True) ----- - -=== `cached_contract` - -A helper method that returns the cached state of a given contract. -It's recommended to first deploy all the relevant contracts before caching the state. -The requisite contracts in the testing module should each be instantiated with `cached_contract` in a fixture after the state has been copied. -The memoization pattern with `cached_contract` should look something like this: - -[,python] ----- -# get contract classes -@pytest.fixture(scope='module') -def contract_classes(): - foo_cls = get_contract_class('Foo') - return foo_cls - -# deploy contracts -@pytest.fixture(scope='module') -async def foo_init(contract_classes): - foo_cls = contract_classes - starknet = await Starknet.empty() - foo = await starknet.deploy( - contract_class=foo_cls, - constructor_calldata=[] - ) - return starknet.state, foo # return state and all deployed contracts - -# memoization -@pytest.fixture -def foo_factory(contract_classes, foo_init): - foo_cls = contract_classes # contract classes - state, foo = foo_init # state and deployed contracts - _state = state.copy() # copy the state - cached_foo = cached_contract(_state, foo_cls, foo) # cache contracts - return cached_foo # return cached contracts ----- - -== State - -The `State` class provides a wrapper for initializing the StarkNet state which acts as a helper for the `Account` class. This wrapper allows `Account` deployments to share the same initialized state without explicitly passing the instantiated StarkNet state to `Account`. Initializing the state should look like this: - -[,python] ----- -from utils import State - -starknet = await State.init() ----- - -== Account - -The `Account` class abstracts away most of the boilerplate for deploying accounts. Instantiating accounts with this class requires the StarkNet state to be instantiated first with the `State.init()` method like this: - -[,python] ----- -from utils import State, Account - -starknet = await State.init() -account1 = await Account.deploy(public_key) -account2 = await Account.deploy(public_key) ----- - -The Account class also provides access to the account contract class which is useful for following the <> pattern. To fetch the account contract class: - -[,python] ----- -fetch_class = Account.get_class ----- - -== MockSigner - -`MockSigner` is used to perform transactions with an instance of https://github.com/OpenZeppelin/nile/blob/main/src/nile/signer.py[Nile's Signer] on a given Account, crafting the transaction and managing nonces. -The `Signer` instance manages signatures and is leveraged by `MockSigner` to operate with the Account contract's `\\__execute__` method. -See xref:accounts.adoc#mocksigner_utility[MockSigner utility] for more information. +[.contract-item] +[[utils-UnwrapAndCast]] +==== `[.contract-item-name]#++UnwrapAndCast++#` [.item-kind]#trait# + +Trait for exposing an `unwrap_and_cast` function to `SyscallResult` objects. This may be useful +when unwrapping a syscall result to a type implementing the `Serde` trait, and you want to avoid the boilerplate of +casting and unwrapping the result multiple times. + +Usage example: + +```javascript +use openzeppelin::utils::selectors; +use openzeppelin::utils::UnwrapAndCast; + +fn call_and_cast_to_bool(target: ContractAddress, args: Span) -> bool { + try_selector_with_fallback( + target, selectors::has_role, selectors::hasRole, args + ).unwrap_and_cast() +} + +fn call_and_cast_to_felt252(target: ContractAddress, args: Span) -> felt252 { + try_selector_with_fallback( + target, selectors::get_role_admin, selectors::getRoleAdmin, args + ).unwrap_and_cast() +} +``` + +Note that it can be automatically casted to any type implementing the `Serde` trait. + +[#utils-Inner-Modules] +==== Inner modules + +[.contract-item] +[[utils-selectors]] +==== `[.contract-item-name]#++selectors++#` [.item-kind]#module# + +See xref:#selectors[`openzeppelin::utils::selectors`]. + +[.contract-item] +[[utils-serde]] +==== `[.contract-item-name]#++serde++#` [.item-kind]#module# + +See xref:#serde[`openzeppelin::utils::serde`]. + +[.contract] +[[selectors]] +=== `++selectors++` + +```javascript +use openzeppelin::utils::selectors; +``` + +:selectors: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.8.0-beta.0/src/utils/selectors.cairo[selectors.cairo] + +Module containing constants matching multiple selectors used through the library. +To see the full list of selectors, see {selectors}. + +[.contract] +[[serde]] +=== `++serde++` + +```javascript +use openzeppelin::utils::serde; +``` + +Module containing utilities related to serialization and deserialization of Cairo data structures. + +[.contract-index] +.Members +-- +.Traits +* xref:#serde-SerializedAppend[`++SerializedAppend++`] +-- + +[#serde-Traits] +==== Traits + +[.contract-item] +[[serde-SerializedAppend]] +==== `[.contract-item-name]#++SerializedAppend++#` [.item-kind]#trait# + +Importing this trait allows the ability to append a serialized representation of a Cairo data structure already +implementing the `Serde` trait to a `felt252` buffer. + +Usage example: + +```javascript +use openzeppelin::utils::serde::SerializedAppend; +use starknet::ContractAddress; + +fn to_calldata(recipient: ContractAddress, amount: u256) -> Array { + let mut calldata = array![]; + calldata.append_serde(recipient); + calldata.append_serde(amount); + calldata +} +``` + +Note that the `append_serde` method is automatically available for arrays of felts, and it accepts any data structure +that implements the `Serde` trait. + +== Test utilities + + +[.contract] +[[testutils]] +=== `++utils++` + +```javascript +use openzeppelin::tests::utils; +``` + +Module containing utilities for testing the library. + +[.contract-index] +.Members +-- +.Functions +* xref:#testutils-deploy[`++deploy(contract_class_hash, calldata)++`] +* xref:#testutils-pop_log[`++pop_log(address)++`] +* xref:#testutils-assert_indexed_keys[`++assert_indexed_keys(event, expected_keys)++`] +* xref:#testutils-assert_no_events_left[`++assert_no_events_left(address)++`] +* xref:#testutils-drop_event[`++drop_event(address)++`] + +.Inner modules +* xref:#testutils-constants[`++constants++`] +-- + +[#testutils-Functions] +==== Functions + +[.contract-item] +[[testutils-deploy]] +==== `[.contract-item-name]#++deploy++#++(contract_class_hash: felt252, calldata: Array) → ContractAddress++` [.item-kind]#function# + +:deploy_syscall: https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/system-calls/#deploy[deploy_syscall] + +Uses the `{deploy_syscall}` to deploy an instance of the contract given the class hash and the calldata. + +The `contract_address_salt` is always set to zero, and `deploy_from_zero` is set to false. + +Usage example: + +```javascript +use openzeppelin::presets::Account; +use openzeppelin::tests::utils; +use starknet::ContractAddress; + +const PUBKEY: felt252 = 'PUBKEY'; + +fn deploy_test_contract() -> ContractAddress { + let calldata = array![PUBKEY]; + utils::deploy(Account::TEST_CLASS_HASH, calldata) +} +``` + +[.contract-item] +[[testutils-pop_log]] +==== `[.contract-item-name]#++pop_log++#++(address: ContractAddress) → Option++` [.item-kind]#function# + +Pops the earliest unpopped logged event for the contract as the requested type +and checks that there's no more keys or data left on the event, preventing unaccounted params. + +This function also removes the first key from the event, to match the event structure key params without +the event ID. + +Required traits for `T`: + +- `Drop` +- `starknet::Event` + +Requirements: + +- No extra data or keys are left on the raw event after deserialization. + +WARNING: This method doesn't currently work for component events that are not flattened +because an extra key is added, pushing the event ID key to the second position. + +[.contract-item] +[[testutils-assert_indexed_keys]] +==== `[.contract-item-name]#++assert_indexed_keys++#(event: T, expected_keys: Span)` [.item-kind]#function# + +Asserts that `expected_keys` exactly matches the indexed keys from `event`. + +`expected_keys` must include all indexed event keys for `event` in the order +that they're defined. + +Required traits for `T`: + +- `Drop` +- `starknet::Event` + +[.contract-item] +[[testutils-assert_no_events_left]] +==== `[.contract-item-name]#++assert_no_events_left++#++(address: ContractAddress)++` [.item-kind]#function# + +Asserts that there are no more events left in the queue for the given address. + +[.contract-item] +[[testutils-drop_event]] +==== `[.contract-item-name]#++drop_event++#++(address: ContractAddress)++` [.item-kind]#function# + +Removes an event from the queue for the given address. + +If the queue is empty, this function won't do anything. + +[#testutils-Inner-Modules] +==== Inner modules + +[.contract-item] +[[testutils-constants]] +==== `[.contract-item-name]#++constants++#` [.item-kind]#module# + +See xref:#constants[`openzeppelin::tests::utils::constants`]. + +[.contract] +[[constants]] +=== `++constants++` + +```javascript +use openzeppelin::tests::utils::constants; +``` + +:constants: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.8.0-beta.0/src/tests/utils/constants.cairo[constants.cairo] + +Module containing constants that are repeatedly used among tests. +To see the full list, see {constants}. diff --git a/src/access/accesscontrol/dual_accesscontrol.cairo b/src/access/accesscontrol/dual_accesscontrol.cairo index 04413984f..7ac28dbc0 100644 --- a/src/access/accesscontrol/dual_accesscontrol.cairo +++ b/src/access/accesscontrol/dual_accesscontrol.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.8.0 (access/accesscontrol/dual_accesscontrol.cairo) -use openzeppelin::utils::Felt252TryIntoBool; use openzeppelin::utils::UnwrapAndCast; use openzeppelin::utils::selectors; use openzeppelin::utils::serde::SerializedAppend; diff --git a/src/tests/utils.cairo b/src/tests/utils.cairo index 56d8544c9..24409ccad 100644 --- a/src/tests/utils.cairo +++ b/src/tests/utils.cairo @@ -13,15 +13,13 @@ fn deploy(contract_class_hash: felt252, calldata: Array) -> ContractAdd /// Pop the earliest unpopped logged event for the contract as the requested type /// and checks there's no more keys or data left on the event, preventing unaccounted params. -/// This function also removes the first key from the event. This is because indexed -/// params are set as event keys, but the first event key is always set as the -/// event ID. +/// +/// This function also removes the first key from the event, to match the event +/// structure key params without the event ID. /// /// This method doesn't currently work for components events that are not flattened /// because an extra key is added, pushing the event ID key to the second position. -fn pop_log, impl TEvent: starknet::Event>( - address: ContractAddress -) -> Option { +fn pop_log, +starknet::Event>(address: ContractAddress) -> Option { let (mut keys, mut data) = testing::pop_log_raw(address)?; // Remove the event ID from the keys @@ -34,11 +32,10 @@ fn pop_log, impl TEvent: starknet::Event>( } /// Asserts that `expected_keys` exactly matches the indexed keys from `event`. +/// /// `expected_keys` must include all indexed event keys for `event` in the order /// that they're defined. -fn assert_indexed_keys, impl TEvent: starknet::Event>( - event: T, expected_keys: Span -) { +fn assert_indexed_keys, +starknet::Event>(event: T, expected_keys: Span) { let mut keys = array![]; let mut data = array![]; diff --git a/src/utils.cairo b/src/utils.cairo index fdf795415..6dda24868 100644 --- a/src/utils.cairo +++ b/src/utils.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.8.0 (utils.cairo) -mod constants; mod selectors; mod serde; mod unwrap_and_cast; @@ -13,38 +12,16 @@ use starknet::call_contract_syscall; use unwrap_and_cast::UnwrapAndCast; fn try_selector_with_fallback( - target: ContractAddress, snake_selector: felt252, camel_selector: felt252, args: Span + target: ContractAddress, selector: felt252, fallback: felt252, args: Span ) -> SyscallResult> { - match call_contract_syscall(target, snake_selector, args) { + match call_contract_syscall(target, selector, args) { Result::Ok(ret) => Result::Ok(ret), Result::Err(errors) => { if *errors.at(0) == 'ENTRYPOINT_NOT_FOUND' { - return call_contract_syscall(target, camel_selector, args); + return call_contract_syscall(target, fallback, args); } else { Result::Err(errors) } } } } - -impl BoolIntoFelt252 of Into { - fn into(self: bool) -> felt252 { - if self { - return 1; - } else { - return 0; - } - } -} - -impl Felt252TryIntoBool of TryInto { - fn try_into(self: felt252) -> Option { - if self == 0 { - Option::Some(false) - } else if self == 1 { - Option::Some(true) - } else { - Option::None(()) - } - } -} diff --git a/src/utils/constants.cairo b/src/utils/constants.cairo deleted file mode 100644 index a290676d0..000000000 --- a/src/utils/constants.cairo +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.8.0 (utils/constants.cairo) - -// -// Interface ids -// - -// SRC5 -// See: https://github.com/starknet-io/SNIPs/pull/24 -const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055; - -// SRC6 -// See: https://github.com/starknet-io/SNIPs/pull/27 -const ISRC6_ID: felt252 = 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd; - -// ERC721 -// See: https://eips.ethereum.org/EIPS/eip-721 -const IERC721_ID: felt252 = 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943; -const IERC721_METADATA_ID: felt252 = - 0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd; -const IERC721_RECEIVER_ID: felt252 = - 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc; - -// AccessControl -// See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/IAccessControl.sol -const IACCESSCONTROL_ID: felt252 = - 0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751; - -// -// Roles -// - -const DEFAULT_ADMIN_ROLE: felt252 = 0; diff --git a/src/utils/unwrap_and_cast.cairo b/src/utils/unwrap_and_cast.cairo index 1bbed2fef..73e856ef0 100644 --- a/src/utils/unwrap_and_cast.cairo +++ b/src/utils/unwrap_and_cast.cairo @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.8.0 (utils/unwrap_and_cast.cairo) -use openzeppelin::utils::Felt252TryIntoBool; -use starknet::ContractAddress; use starknet::SyscallResult; use starknet::SyscallResultTrait; @@ -10,36 +8,9 @@ trait UnwrapAndCast { fn unwrap_and_cast(self: SyscallResult>) -> T; } -impl UnwrapAndCastBool of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> bool { - (*self.unwrap_syscall().at(0)).try_into().unwrap() - } -} - -impl UnwrapAndCastContractAddress of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> ContractAddress { - (*self.unwrap_syscall().at(0)).try_into().unwrap() - } -} - -impl UnwrapFelt of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> felt252 { - *self.unwrap_syscall().at(0) - } -} - -impl UnwrapAndCastU8 of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> u8 { - (*self.unwrap_syscall().at(0)).try_into().unwrap() - } -} - -impl UnwrapAndCastU256 of UnwrapAndCast { - fn unwrap_and_cast(self: SyscallResult>) -> u256 { - let unwrapped = self.unwrap_syscall(); - u256 { - low: (*unwrapped.at(0)).try_into().unwrap(), - high: (*unwrapped.at(1)).try_into().unwrap(), - } +impl UnwrapAndCastSerde> of UnwrapAndCast { + fn unwrap_and_cast(self: SyscallResult>) -> T { + let mut result = self.unwrap_syscall(); + Serde::::deserialize(ref result).unwrap() } }