Skip to content

Commit

Permalink
Remove DualCase dispatchers (#1163)
Browse files Browse the repository at this point in the history
* feat: remove access dual dispatchers (#1154)

* feat: remove account dual dispatchers (#1168)

* Remove token dual dispatchers (#1175)

* feat: remove modules

* fix: mock

* fix: linter

* fix: tests

* fix: mock

* feat: apply review suggestions

* feat: fix import

* Remove dual dispatchers docs and CHANGELOG (#1177)

* feat: update docs

* feat: update CHANGELOG

* fix: typo

* fix: mod

* feat: remove unused imports

* fix: README

* feat: remove selectors

* Update docs/modules/ROOT/pages/interfaces.adoc

Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>

* feat: add mention and link to dispatcher types

* feat: remove import

* Update docs/modules/ROOT/pages/interfaces.adoc

Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>

* fix: README

---------

Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>

---------

Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
  • Loading branch information
ericnordelo and andrew-fleming authored Oct 7, 2024
1 parent c54eda3 commit 805e9cb
Show file tree
Hide file tree
Showing 72 changed files with 75 additions and 6,420 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Bump scarb to v2.8.3 (#1166)

### Changed (Breaking)

- Remove openzeppelin_utils::selectors (#1163)
- Remove `DualCase dispatchers` (#1163)
- Remove `try_selector_with_fallback` from `openzeppelin_utils`
- Remove `unwrap_and_cast` module from `openzeppelin_utils`
- Remove `openzeppelin_access::accesscontrol::dual_accesscontrol`
- Remove `openzeppelin_access::ownable::dual_ownable`
- Remove `openzeppelin_account::dual_account`
- Remove `openzeppelin_account::dual_eth_account`
- Remove `openzeppelin_token::erc20::dual20`
- Remove `openzeppelin_token::erc721::dual721`
- Remove `openzeppelin_token::erc721::dual721_receiver`
- Remove `openzeppelin_token::erc1155::dual1155`
- Remove `openzeppelin_token::erc1155::dual1155_receiver`
- `SRC9Component` now uses `ISRC6Dispatcher` instead of `DualCaseAccount` (#1163)
- `ERC20VotesComponent` now uses `ISRC6Dispatcher` instead of `DualCaseAccount` (#1163)
- `ERC721Component` now uses `IERC721ReceiverDispatcher` instead of `DualCaseERC721Receiver` (#1163)
- `ERC1155Component` now uses `IERC1155ReceiverDispatcher` instead of `DualCaseERC1155Receiver` (#1163)

## 0.17.0 (2024-09-23)

### Added
Expand Down
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,6 @@ mod MyToken {
}
```

### Unsupported

[`DualCase` dispatchers](https://docs.openzeppelin.com/contracts-cairo/0.17.0/interfaces#dualcase_dispatchers) rely on Sierra's ability to catch a revert to resume execution. Currently, Starknet live chains (testnets and mainnet) don't implement that behavior. Starknet's testing framework does support it.

## Learn

### Documentation
Expand Down
85 changes: 0 additions & 85 deletions docs/modules/ROOT/pages/api/utilities.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,79 +19,13 @@ Module containing core utilities of the library.
[.contract-index]
.Members
--
.Functions
* xref:#utils-try_selector_with_fallback[`++try_selector_with_fallback(target, selector, fallback, args)++`]

.Traits
* xref:#utils-UnwrapAndCast[`++UnwrapAndCast++`]

.Inner modules
* xref:#utils-cryptography[`++cryptography++`]
* xref:#utils-deployments[`++deployments++`]
* xref:#utils-math[`++math++`]
* xref:#utils-selectors[`++selectors++`]
* xref:#utils-serde[`++serde++`]
--

[#utils-Functions]
==== Functions

[.contract-item]
[[utils-try_selector_with_fallback]]
==== `[.contract-item-name]#++try_selector_with_fallback++#++(target: ContractAddress, selector: felt252, fallback: felt252, args: Span<felt252>) → SyscallResult<Span<felt252>>++` [.item-kind]#function#

Tries to call a given selector on a given contract, and if it fails, tries to call a fallback selector.

It was designed for falling back to the `camelCase` selector for backward compatibility in the
case of a failure of the `snake_case` selector.

Returns a `SyscallResult` with the result of the successful call.

Note that:

- If the first call succeeds, the second call is not attempted.

- If the first call fails with an error different than `ENTRYPOINT_NOT_FOUND`, the error is returned
without falling back to the second selector.

- If the first call fails with `ENTRYPOINT_NOT_FOUND`, the second call is attempted, and if it fails its
error is returned.

WARNING: The fallback mechanism won't work on live chains (mainnet or testnets) until
they implement panic handling in their runtime.

[#utils-Traits]
==== Traits

[.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:

```cairo
use openzeppelin_utils::selectors;
use openzeppelin_utils::UnwrapAndCast;

fn call_and_cast_to_bool(target: ContractAddress, args: Span<felt252>) -> 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>) -> 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

Expand All @@ -113,12 +47,6 @@ See xref:#deployments[`openzeppelin_utils::deployments`].

See xref:#math[`openzeppelin_utils::math`].

[.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#
Expand Down Expand Up @@ -250,19 +178,6 @@ Returns the average of two values. The result is rounded down.

NOTE: `T` is a generic value matching different numeric implementations.

[.contract]
[[selectors]]
=== `++selectors++`

```cairo
use openzeppelin_utils::selectors;
```

:selectors: https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.17.0/packages/utils/src/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++`
Expand Down
8 changes: 4 additions & 4 deletions docs/modules/ROOT/pages/guides/snip12.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,12 @@ TIP: The expected parameter for the `get_message_hash` function is the address o

== Full Implementation

:dualcase_dispatchers: xref:/interfaces#dualcase_dispatchers
:isrc6_dispatcher: xref:/api/account#ISRC6
:nonces: xref:/api/utilities#NoncesComponent

Finally, the full implementation of the `CustomERC20` contract looks like this:

NOTE: We are using the {dualcase_dispatchers}[`DualCaseAccount`] to verify the signature,
NOTE: We are using the {isrc6_dispatcher}[`ISRC6Dispatcher`] to verify the signature,
and the {nonces}[`NoncesComponent`] to handle nonces to prevent replay attacks.

[,cairo]
Expand Down Expand Up @@ -278,7 +278,7 @@ impl StructHashImpl of StructHash<Message> {
#[starknet::contract]
mod CustomERC20 {
use openzeppelin_account::dual_account::{DualCaseAccount, DualCaseAccountTrait};
use openzeppelin_account::interface::{ISRC6Dispatcher, ISRC6DispatcherTrait};
use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl};
use openzeppelin_utils::cryptography::nonces::NoncesComponent;
use starknet::ContractAddress;
Expand Down Expand Up @@ -348,7 +348,7 @@ mod CustomERC20 {
let message = Message { recipient, amount, nonce, expiry };
let hash = message.get_message_hash(owner);
let is_valid_signature_felt = DualCaseAccount { contract_address: owner }
let is_valid_signature_felt = ISRC6Dispatcher { contract_address: owner }
.is_valid_signature(hash, signature);
// Check either 'VALID' or true for backwards compatibility
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ before proceeding, and run the following command to check that the installation
----
$ scarb --version
scarb 2.8.2 (a37b4cbfc 2024-09-09)
scarb 2.8.3 (54938ce3b 2024-09-26)
cairo: 2.8.2 (https://crates.io/crates/cairo-lang-compiler/2.8.2)
sierra: 1.6.0
----
Expand Down
83 changes: 22 additions & 61 deletions docs/modules/ROOT/pages/interfaces.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ use openzeppelin_token::erc20::interface::IERC20;
or

```cairo
use openzeppelin_token::erc20::dual20::DualCaseERC20;
use openzeppelin_token::erc20::interface::ERC20ABI;
```

NOTE: For simplicity, we'll use ERC20 as example but the same concepts apply to other modules.

== Interface traits

The library offers three types of traits to implement or interact with contracts:

=== Standard traits
Expand All @@ -44,6 +45,9 @@ pub trait IERC20<TState> {

They describe a contract's complete interface. This is useful to interface with a preset contract offered by this library, such as the ERC20 preset that includes functions from different traits such as `IERC20` and `IERC20Camel`.

NOTE: The library offers an ABI trait for most components, providing all external function signatures
even when most of the time all of them don't need to be implemented at the same time. This can be helpful when interacting with a contract implementing the component, instead of defining a new dispatcher.

```cairo
#[starknet::interface]
pub trait ERC20ABI<TState> {
Expand Down Expand Up @@ -72,34 +76,30 @@ pub trait ERC20ABI<TState> {
```

=== Dispatcher traits
This is a utility trait to interface with contracts whose interface is unknown. Read more in the xref:#dualcase_dispatchers[DualCase Dispatchers] section.

```cairo
#[derive(Copy, Drop)]
pub struct DualCaseERC20 {
contract_address: ContractAddress
}
:interacting-with-another-contract: https://book.cairo-lang.org/ch15-02-interacting-with-another-contract.html[Interacting with another contract]

pub trait DualCaseERC20Trait {
fn name(self: @DualCaseERC20) -> ByteArray;
fn symbol(self: @DualCaseERC20) -> ByteArray;
fn decimals(self: @DualCaseERC20) -> u8;
fn total_supply(self: @DualCaseERC20) -> u256;
fn balance_of(self: @DualCaseERC20, account: ContractAddress) -> u256;
fn allowance(self: @DualCaseERC20, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(self: @DualCaseERC20, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
self: @DualCaseERC20, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(self: @DualCaseERC20, spender: ContractAddress, amount: u256) -> bool;
}
Traits annotated with `#[starknet::interface]` automatically generate a dispatcher that can be used to interact with contracts that implement the given interface. They can be imported by appending the `Dispatcher` and `DispatcherTrait` suffixes to the trait name, like this:

```cairo
use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
```

Other types of dispatchers are also auto-generated from the annotated trait. See the
{interacting-with-another-contract} section of the Cairo book for more information.

NOTE: In the example, the `IERC20Dispatcher` is the one used to interact with contracts, but the
`IERC20DispatcherTrait` needs to be in scope for the functions to be available.

== Dual interfaces

WARNING: `camelCase` functions are deprecated and maintained only for Backwards Compatibility.
It's recommended to only use `snake_case` interfaces with contracts and components. The `camelCase` functions will be removed in
future versions.

Following the {great-interface-migration} plan, we added `snake_case` functions to all of our preexisting `camelCase` contracts with the goal of eventually dropping support for the latter.

In short, we offer two types of interfaces and utilities to handle them:
In short, the library offers two types of interfaces and utilities to handle them:

1. `camelCase` interfaces, which are the ones we've been using so far.
2. `snake_case` interfaces, which are the ones we're migrating to.
Expand Down Expand Up @@ -131,7 +131,7 @@ pub trait IERC20<TState> {

=== `IERC20Camel`

On top of that, we also offer a `camelCase` version of the same interface:
On top of that, the library also offers a `camelCase` version of the same interface:

```cairo
#[starknet::interface]
Expand All @@ -149,42 +149,3 @@ pub trait IERC20Camel<TState> {
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
}
```

== `DualCase` dispatchers

WARNING: `DualCase` dispatchers are deprecated, and they will be removed from the library soon.

WARNING: `DualCase` dispatchers won't work on live chains (`mainnet` or testnets) until they implement panic handling in their runtime. Dispatchers work fine in testing environments.

In order to ease this transition, OpenZeppelin Contracts for Cairo offer what we call `DualCase` dispatchers such as `DualCaseERC721` or `DualCaseAccount`.

These modules wrap a target contract with a compatibility layer to expose a `snake_case` interface no matter what casing the underlying contract uses.
This way, an AMM wouldn't have problems integrating tokens independently of their interface.

For example:

```cairo
let token = DualCaseERC20 { contract_address: target };
token.transfer_from(OWNER(), RECIPIENT(), VALUE);
```

This is done by simply executing the `snake_case` version of the function (e.g. `transfer_from`) and falling back to the `camelCase` one (e.g. `transferFrom`) in case it reverts with `ENTRYPOINT_NOT_FOUND`, like this:

```cairo
fn try_selector_with_fallback(
target: ContractAddress, selector: felt252, fallback: felt252, args: Span<felt252>
) -> SyscallResult<Span<felt252>> {
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, fallback, args);
} else {
Result::Err(errors)
}
}
}
}
```

Trying the `snake_case` interface first renders `camelCase` calls a bit more expensive since a failed `snake_case` call will always happen before. This is a design choice to incentivize casing adoption/transition as per the {great-interface-migration}.
1 change: 0 additions & 1 deletion packages/access/src/accesscontrol.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pub mod accesscontrol;
pub mod dual_accesscontrol;
pub mod interface;

pub use accesscontrol::AccessControlComponent;
Expand Down
Loading

0 comments on commit 805e9cb

Please sign in to comment.