Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC6909 #1040

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open

Add ERC6909 #1040

wants to merge 36 commits into from

Conversation

swan-of-bodom
Copy link

Adding the new multi-token standard ERC6909 written in Cairo. This token standard will most likely be used by UniswapV4 so might be interesting to have one for Cairo too as discussed with @andrew-fleming

  • Tests
  • Documentation
  • Added entry to CHANGELOG.md
  • Tried the feature on a public network

Deploy transaction:
0x004297aadb57195520d58b6de3db91c14bb30284080c78b7f3d65407d2d06e79

Mint transaction:
0x008debb59434cc64f432110c118d8ba64725545764a71529c7226d127d46df5a

Thanks!

@andrew-fleming
Copy link
Collaborator

Thanks for opening this PR @swan-of-bodom! It may take some time to review this since it's quite big, but we'll get to it as soon as we can

@swan-of-bodom
Copy link
Author

Thanks for opening this PR @swan-of-bodom! It may take some time to review this since it's quite big, but we'll get to it as soon as we can

No worries, thank you!

Copy link
Collaborator

@andrew-fleming andrew-fleming left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start on this! I still have to review tests, but I think it's worth giving feedback now on design choices, implementations, and styling

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We include dual dispatchers to provide a means of supporting camelCase entrypoints from the (inconsistent) Cairo v0 convention. Since ERC6909 is a new standard to this lib and it's not backwards compatible with the existing token standards, we don't need to include the dual dispatcher for it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the dual dispatcher in a27fe02.

Thanks!

Comment on lines 219 to 222
fn supports_interface(
self: @ComponentState<TContractState>, interface_id: felt252
) -> bool {
interface_id == interface::IERC6909_ID || interface_id == ISRC5_ID
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this component have SRC5 as a dependency? If a contract implemented this and access control, for example, supports_interface would clash when exposing them. SRC5 stores a contract's supported interfaces in storage, so I think the IERC6909 should be set in SRC5's storage during construction (in the initializer)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely, you are right, implementing this with access control would clash. Will set it in storage

Comment on lines 226 to 229
#[embeddable_as(ERC6909CamelOnlyImpl)]
impl ERC6909CamelOnly<
TContractState, +HasComponent<TContractState>, +ERC6909HooksTrait<TContractState>
> of interface::IERC6909CamelOnly<ComponentState<TContractState>> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove these. See the comment in the dual dispatchers module

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in a27fe02

Comment on lines 34 to 39
/// @notice The event emitted when a transfer occurs.
/// @param caller The caller of the transfer.
/// @param sender The address of the sender.
/// @param receiver The address of the receiver.
/// @param id The id of the token.
/// @param amount The amount of the token.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is well-documented! Though, we don't follow natspec. We use a rust-like style. Would you mind updating the documentation to be consistent with our style (see our other components)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the comments to match as close as possible to the other components. 03e577e

Comment on lines 82 to 96
/// @dev Thrown when owner balance for id is insufficient.
pub const INSUFFICIENT_BALANCE: felt252 = 'ERC6909: insufficient balance';
/// @dev Thrown when spender allowance for id is insufficient.
pub const INSUFFICIENT_ALLOWANCE: felt252 = 'ERC6909: insufficient allowance';
/// @dev Thrown when transferring from the zero address
pub const TRANSFER_FROM_ZERO: felt252 = 'ERC6909: transfer from 0';
/// @dev Thrown when transferring to the zero address
pub const TRANSFER_TO_ZERO: felt252 = 'ERC6909: transfer to 0';
/// @dev Thrown when minting to the zero address
pub const MINT_TO_ZERO: felt252 = 'ERC6909: mint to 0';
/// @dev Thrown when burning from the zero address
pub const BURN_FROM_ZERO: felt252 = 'ERC6909: burn from 0';
/// @dev Thrown when approving from the zero address
pub const APPROVE_FROM_ZERO: felt252 = 'ERC6909: approve from 0';
/// @dev Thrown when approving to the zero address
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I know this is directly from the reference in the EIP, but we can omit these comments. The const name conveys enough IMO. (We should also be using panics instead of thrown in cairoland)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, updated comments on 03e577e

* xref:#ERC6909Component-_set_operator[`++_set_operator(self, owner, spender, approved)++`]
* xref:#ERC6909Component-_spend_allowance[`++_spend_allowance(self, sender, spender, id, amount)++`]
* xref:#ERC6909Component-_approve[`++_approve(self, owner, spender, id, amount)++`]
* xref:#ERC6909Component-_transfer[`++_approve(self, caller, sender, receiver, id, amount)++`]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* xref:#ERC6909Component-_transfer[`++_approve(self, caller, sender, receiver, id, amount)++`]
* xref:#ERC6909Component-_transfer[`++_transfer(self, caller, sender, receiver, id, amount)++`]


[.contract-item]
[[ERC6909Component-Approval]]
==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, spender: ContractAddress, value: u256)++` [.item-kind]#event#
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, spender: ContractAddress, value: u256)++` [.item-kind]#event#
==== `[.contract-item-name]#++Approval++#++(owner: ContractAddress, spender: ContractAddress, id: u256, amount: u256)++` [.item-kind]#event#


NOTE: Implementing xref:#ERC6909Component[ERC6909Component] is a requirement for this component to be implemented.

This extension allows to set the contract URI (ideally) in the constructor via `initializer(uri: ByteArray)`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This extension allows to set the contract URI (ideally) in the constructor via `initializer(uri: ByteArray)`.
This extension allows contracts to set the contract URI in the constructor via `initializer`.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this may include setting the base_uri depending on the design discussion


NOTE: Implementing xref:#ERC6909Component[ERC6909Component] is a requirement for this component to be implemented.

WARNING: To individual token metadata, this extension requires that the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean by "To individual token metadata"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing the ERC6909TokenSupply section and the actual API for ERC6909Metadata

Copy link
Collaborator

@immrsd immrsd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your efforts! I've left several suggestions. Please note that to approve the implementation we'll have to wait till the EIP-6909 is finalized, since atm it's still in the draft state

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts for Cairo v0.14.0 (token/erc6909/erc6909.cairo)

use core::starknet::{ContractAddress};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use core::starknet::{ContractAddress};
use starknet::ContractAddress;

Comment on lines 16 to 17
use starknet::ContractAddress;
use starknet::get_caller_address;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use starknet::ContractAddress;
use starknet::get_caller_address;
use starknet::{ContractAddress, get_caller_address};

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 56570aa


let zero_address = Zero::zero();

if (sender != zero_address) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (sender != zero_address) {
if (sender.is_non_zero()) {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 40538f6

self.ERC6909_balances.write((sender, id), sender_balance - amount);
}

if (receiver != zero_address) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (receiver != zero_address) {
if (receiver.is_non_zero()) {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 40538f6

/// Does not update the allowance value in case of infinite allowance or if spender is operator.
fn _spend_allowance(
ref self: ComponentState<TContractState>,
sender: ContractAddress,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this parameter be named owner as in ERC20?

Copy link
Author

@swan-of-bodom swan-of-bodom Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed it to owner in 40538f6 to match ERC20!

Comment on lines 332 to 334
caller: ContractAddress, // For the `Transfer` event
sender: ContractAddress, // from
receiver: ContractAddress, // to
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1
I think getting caller by calling get_caller_address in the function makes total sense and can reduce the amount of parameters

src/token/erc6909/extensions/erc6909_content_uri.cairo Outdated Show resolved Hide resolved
let zero_address = Zero::zero();

// In case of new ID mints update the token metadata
if (sender == zero_address) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (sender == zero_address) {
if (sender.is_zero()) {

Copy link
Author

@swan-of-bodom swan-of-bodom Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 40538f6

let zero_address = Zero::zero();

// In case of mints we increase the total supply of this token ID
if (sender == zero_address) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (sender == zero_address) {
if (sender.is_zero()) {

Copy link
Author

@swan-of-bodom swan-of-bodom Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 40538f6.

}

// In case of burns we decrease the total supply of this token ID
if (receiver == zero_address) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (receiver == zero_address) {
if (receiver.is_zero()) {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 40538f6 thank you!

@swan-of-bodom
Copy link
Author

Thanks for your efforts! I've left several suggestions. Please note that to approve the implementation we'll have to wait till the EIP-6909 is finalized, since atm it's still in the draft state

Thanks, happy to contribute to this great library :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants