From 8051a84ed8dfcd49cd0ee5a5321443ac359a2563 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 5 Jun 2025 11:58:16 +0200 Subject: [PATCH 01/33] add initial files --- contracts/src/token/erc6909/extensions/token_supply.rs | 0 contracts/src/token/erc6909/mod.rs | 0 contracts/src/token/mod.rs | 1 + 3 files changed, 1 insertion(+) create mode 100644 contracts/src/token/erc6909/extensions/token_supply.rs create mode 100644 contracts/src/token/erc6909/mod.rs diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs new file mode 100644 index 000000000..e69de29bb diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/contracts/src/token/mod.rs b/contracts/src/token/mod.rs index 113b8654e..4a0340222 100644 --- a/contracts/src/token/mod.rs +++ b/contracts/src/token/mod.rs @@ -3,3 +3,4 @@ pub mod common; pub mod erc1155; pub mod erc20; pub mod erc721; +pub mod erc6909; \ No newline at end of file From a6a3be9cd0b06964497827089a9b5d768629617b Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 5 Jun 2025 17:58:31 +0200 Subject: [PATCH 02/33] implement erc6909 add one mint test --- contracts/src/token/erc6909/mod.rs | 700 +++++++++++++++++++++++++++++ 1 file changed, 700 insertions(+) diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs index e69de29bb..c5c0e103d 100644 --- a/contracts/src/token/erc6909/mod.rs +++ b/contracts/src/token/erc6909/mod.rs @@ -0,0 +1,700 @@ +//! Implementation of the ERC-6909 token standard. + +use alloc::{vec, vec::Vec}; + +use alloy_primitives::{Address, FixedBytes, U256}; +use openzeppelin_stylus_proc::interface_id; +use stylus_sdk::{ + call::MethodError, evm, msg, + prelude::*, + storage::{StorageMap, StorageU256, StorageBool}, +}; + +use crate::utils::{ + introspection::erc165::IErc165, + math::storage::AddAssignUnchecked, +}; + +pub use sol::*; +#[cfg_attr(coverage_nightly, coverage(off))] +mod sol { + use alloy_sol_macro::sol; + + sol! { + /// Emitted when the allowance of a `spender` for an `owner` is set for a token of type `id`. + /// The new allowance is `amount`. + #[derive(Debug)] + #[allow(missing_docs)] + event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount); + /// Emitted when `owner` grants or revokes operator status for a `spender`. + #[derive(Debug)] + #[allow(missing_docs)] + event OperatorSet(address indexed owner, address indexed spender, bool approved); + /// Emitted when `amount` tokens of type `id` are moved from `sender` to `receiver` initiated by `caller`. + #[derive(Debug)] + #[allow(missing_docs)] + event Transfer(address caller, address indexed sender, address indexed receiver, uint256 indexed id, uint256 amount); + } + + sol! { + /// Indicates an error related to the current `balance` of `sender`. Used + /// in transfers. + /// + /// * `sender` - Address whose tokens are being transferred. + /// * `balance` - Current balance for the interacting account. + /// * `needed` - Minimum amount required to perform a transfer. + /// * `id`- Identifier number of a token. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC6909InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 id); + /// Indicates a failure with the `spender`’s `allowance`. Used in + /// transfers. + /// + /// * `spender` - Address that may be allowed to operate on tokens without + /// being their owner. + /// * `allowance` - Amount of tokens a `spender` is allowed to operate + /// with. + /// * `needed` - Minimum amount required to perform a transfer. + /// * `id` - Identifier number of a token. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC6909InsufficientAllowance(address spender, uint256 allowance, uint256 needed, uint256 id); + /// Indicates a failure with the `approver` of a token to be approved. Used in approvals. + /// approver Address initiating an approval operation. + /// + /// * `approver` - Address initiating an approval operation. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC6909InvalidApprover(address approver); + /// Indicates a failure with the token `receiver`. Used in transfers. + /// + /// * `receiver` - Address to which the tokens are being transferred. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC6909InvalidReceiver(address receiver); + /// Indicates a failure with the token `sender`. Used in transfers. + /// + /// * `sender` - Address whose tokens are being transferred. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC6909InvalidSender(address sender); + /// Indicates a failure with the `spender` to be approved. Used in + /// approvals. + /// + /// * `spender` - Address that may be allowed to operate on tokens without + /// being their owner. + #[derive(Debug)] + #[allow(missing_docs)] + error ERC6909InvalidSpender(address spender); + } +} + +/// An [`Erc6909`] error defined as described in [ERC-6909]. +/// +/// [ERC-6909]: https://eips.ethereum.org/EIPS/eip-6909 +#[derive(SolidityError, Debug)] +pub enum Error { + /// Indicates an error related to the current `balance` of `sender`. Used + /// in transfers. + InsufficientBalance(ERC6909InsufficientBalance), + /// Indicates a failure with the `spender`’s `allowance`. Used in + /// transfers. + InsufficientAllowance(ERC6909InsufficientAllowance), + /// Indicates a failure with the `approver` of a token to be approved. Used in approvals. + InvalidApprover(ERC6909InvalidApprover), + /// Indicates a failure with the token `receiver`. Used in transfers. + InvalidReceiver(ERC6909InvalidReceiver), + /// Indicates a failure with the token `sender`. Used in transfers. + InvalidSender(ERC6909InvalidSender), + /// Indicates a failure with the `spender` to be approved. Used in + /// approvals. + InvalidSpender(ERC6909InvalidSpender), +} + +impl MethodError for Error { + fn encode(self) -> alloc::vec::Vec { + self.into() + } +} + +/// State of an [`Erc6909`] token. +#[storage] +pub struct Erc6909 { + pub(crate) balances: StorageMap>, + pub(crate) operator_approvals: StorageMap>, + pub(crate) allowances: StorageMap>> +} + +/// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when +/// calling other contracts and not `&mut (impl TopLevelStorage + +/// BorrowMut)`. Should be fixed in the future by the Stylus team. +unsafe impl TopLevelStorage for Erc6909 {} + +/// Required interface of an [`Erc6909`] compliant contract. +#[interface_id] +pub trait IErc6909: IErc165 { + /// The error type associated to this ERC-6909 trait implementation. + type Error: Into>; + + /// Returns the amount of tokens of type `id` owned by `owner`. + /// + /// # Arguments + /// + /// * `&self` - Read access to the contract's state. + /// * `owner` - Account that owns the tokens. + /// * `id` - Token id as a number. + fn balance_of(&self, owner: Address, id: U256) -> U256; + + /// Returns the amount of tokens of type `id` that `spender` is allowed to spend on behalf of `owner`. + /// + /// NOTE: Does not include operator allowances. + /// + /// # Arguments + /// + /// * `&self` - Read access to the contract's state. + /// * `owner` - Account that owns the tokens. + /// * `spender` - Account that will spend the tokens. + /// * `id` - Token id as a number. + fn allowance( + &self, + owner: Address, + spender: Address, + id: U256, + ) -> U256; + + /// Returns true if `spender` is set as an operator for `owner`. + /// + /// # Arguments + /// + /// * `&self` - Read access to the contract's state. + /// * `owner` - Account that owns the tokens. + /// * `spender` - Account that is an operator for the owner. + fn is_operator(&self, owner: Address, spender: Address) -> bool; + + /// Sets an approval to `spender` for `amount` of tokens of type `id` from the caller's tokens. + /// An `amount` of [`U256::MAX`] signifies an unlimited approval. + /// + /// Must return true. + /// + /// # Arguments + /// * `&mut self` - Write access to the contract's state. + /// * `spender` - Account that will spend the tokens. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InvalidSpender`] - If the `spender` address is + /// [`Address::ZERO`]. + /// # Events + /// + /// * [`Approval`]. + fn approve( + &mut self, + spender: Address, + id: U256, + amount: U256, + ) -> Result; + + /// Grants or revokes unlimited transfer permission of any token id to `spender` for the caller's tokens. + /// + /// Must return true. + /// + /// # Arguments + /// * `&mut self` - Write access to the contract's state. + /// * `spender` - Account that will spend the tokens. + /// * `approved` - Flag that determines whether or not permission will be + /// granted to `operator`. + /// + /// # Errors + /// + /// * [`Error::InvalidSpender`] - If the `spender` address is + /// [`Address::ZERO`]. + /// + /// # Events + /// + /// * [`OperatorSet`]. + fn set_operator( + &mut self, + spender: Address, + approved: bool, + ) -> Result; + + /// Transfers `amount` of token type `id` from the caller's account to `receiver`. + /// + /// Must return true. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `receiver`- Account to transfer tokens to. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InvalidReceiver`] - If the `receiver` address is [`Address::ZERO`]. + /// * [`Error::InsufficientBalance`] - If the caller doesn't have a balance + /// + /// # Events + /// + /// * [`Transfer`]. + fn transfer( + &mut self, + receiver: Address, + id: U256, + amount: U256, + ) -> Result; + + /// Transfers `amount` of token type `id` from `sender` to `receiver`. + /// + /// Must return true. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `sender` - Account to transfer tokens from. + /// * `receiver` - Account to transfer tokens to. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InvalidSender`] - If the `sender` address is [`Address::ZERO`]. + /// * [`Error::InvalidReceiver`] - If the `receiver` address is [`Address::ZERO`]. + /// * [`Error::InsufficientBalance`] - If the `sender` doesn't have a balance + /// + /// # Events + /// + /// * [`Transfer`]. + fn transfer_from( + &mut self, + sender: Address, + receiver: Address, + id: U256, + amount: U256, + ) -> Result; +} + +#[public] +#[implements(IErc6909, IErc165)] +impl Erc6909 {} + +#[public] +impl IErc6909 for Erc6909 { + type Error = Error; + + fn balance_of(&self, owner: Address, id: U256) -> U256 { + self.balances.get(owner).get(id) + } + + fn allowance( + &self, + owner: Address, + spender: Address, + id: U256, + ) -> U256 { + self.allowances.get(owner).get(spender).get(id) + } + + fn is_operator(&self, owner: Address, spender: Address) -> bool { + self.operator_approvals.get(owner).get(spender) + } + + fn approve( + &mut self, + spender: Address, + id: U256, + amount: U256, + ) -> Result { + let owner = msg::sender(); + self._approve(owner, spender, id, amount, true) + } + + fn set_operator( + &mut self, + spender: Address, + approved: bool, + ) -> Result { + let owner = msg::sender(); + self._set_operator(owner, spender, approved)?; + Ok(true) + } + + fn transfer( + &mut self, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + let sender = msg::sender(); + self._transfer(sender, receiver, id, amount)?; + Ok(true) + } + + fn transfer_from( + &mut self, + sender: Address, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + let caller = msg::sender(); + if (sender != caller) && !self.is_operator(sender, caller) { + self._spend_allowance(sender, caller, id, amount)?; + } + self._transfer(sender, receiver, id, amount)?; + Ok(true) + } + +} + +impl Erc6909 { + /// Sets `amount` as the allowance of `spender` over the `owner`'s `id` tokens. + /// + /// This internal function is equivalent to `approve`, and can be used to e.g. + /// set automatic allowances for certain subsystems, etc. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `owner` - Account that owns the tokens. + /// * `spender` - Account that will spend the tokens. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// * `emit_event` - Emit an [`Approval`] event flag. + /// + /// # Errors + /// + /// * [`Error::InvalidSpender`] - If the `spender` address is + /// [`Address::ZERO`]. + /// * [`Error::InvalidApprover`] - If the `owner` address is + /// [`Address::ZERO`]. + /// + /// # Events + /// + /// * [`Approval`]. + fn _approve( + &mut self, + owner: Address, + spender: Address, + id: U256, + amount: U256, + emit_event: bool, + ) -> Result { + if owner.is_zero() { + return Err(Error::InvalidApprover(ERC6909InvalidApprover{ + approver: Address::ZERO, + })); + } + + if spender.is_zero() { + return Err(Error::InvalidSpender(ERC6909InvalidSpender{ + spender: Address::ZERO, + })); + } + + self.allowances.setter(owner).setter(spender).insert(id, amount); + + if emit_event { + evm::log(Approval {owner, spender, id, amount}); + } + + Ok(true) + } + + /// Approve `spender` to operate on all of `owner`'s tokens + /// + /// This internal function is equivalent to `setOperator`, and can be used to e.g. + /// set automatic allowances for certain subsystems, etc. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `owner` - Account that owns the tokens. + /// * `spender` - Account that will spend the tokens. + /// * `approved` - Flag that determines whether or not permission will be + /// granted to `operator`. + /// + /// # Errors + /// + /// * [`Error::InvalidSpender`] - If the `spender` address is + /// [`Address::ZERO`]. + /// * [`Error::InvalidApprover`] - If the `owner` address is + /// [`Address::ZERO`]. + /// + /// # Events + /// + /// * [`OperatorSet`]. + fn _set_operator( + &mut self, + owner: Address, + spender: Address, + approved: bool, + ) -> Result { + if owner.is_zero() { + return Err(Error::InvalidApprover(ERC6909InvalidApprover{ + approver: Address::ZERO, + })); + } + + if spender.is_zero() { + return Err(Error::InvalidSpender(ERC6909InvalidSpender{ + spender: Address::ZERO, + })); + } + + self.operator_approvals.setter(owner).insert(spender, approved); + + evm::log(OperatorSet {owner, spender, approved}); + + Ok(true) + } + /// Moves `amount` of token `id` from `from` to `to` without checking for approvals. + /// This function verifies that neither the sender nor the receiver are [`Address::ZERO`], + /// which means it cannot mint or burn tokens. + /// + /// Relies on the `_update` mechanism. + /// + /// NOTE: This function is not virtual, {_update} should be overridden instead. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `from` - Account to transfer tokens from. + /// * `to` - Account to transfer tokens to. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InvalidSender`] - If the `from` address is [`Address::ZERO`]. + /// * [`Error::InvalidReceiver`] - If the `to` address is [`Address::ZERO`]. + /// * [`Error::InsufficientBalance`] - If the `from` address doesn't have + /// enough tokens. + /// + /// # Events + /// + /// * [`Transfer`]. + fn _transfer( + &mut self, + from: Address, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + if from.is_zero() { + return Err(Error::InvalidSender(ERC6909InvalidSender { + sender: Address::ZERO, + })); + } + + if to.is_zero() { + return Err(Error::InvalidReceiver(ERC6909InvalidReceiver { + receiver: Address::ZERO, + })); + } + + self._update(from, to, id, amount)?; + + Ok(()) + } + + /// Transfers `amount` of token `id` from `from` to `to`, or alternatively + /// mints (or burns) if `from` (or `to`) is the zero address. + /// All customizations to transfers, mints, and burns should be done by overriding this function. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `from` - Account to transfer tokens from. + /// * `to` - Account to transfer tokens to. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InsufficientBalance`] - If the `from` address doesn't have + /// enough tokens. + /// + /// # Events + /// + /// * [`Transfer`]. + pub fn _update( + &mut self, + from: Address, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + let caller = msg::sender(); + + if !from.is_zero() { + let from_balance = self.balances.get(from).get(id); + if from_balance < amount { + return Err(Error::InsufficientBalance(ERC6909InsufficientBalance { + sender: from, + balance: from_balance, + needed: amount, + id, + })); + } + self.balances.setter(from).setter(id).set(from_balance - amount); + } + + if !to.is_zero() { + self.balances.setter(to).setter(id).add_assign_unchecked(amount); + } + + evm::log(Transfer {caller, sender: from, receiver: to, id, amount}); + Ok(()) + } + + /// Updates `owner`'s allowance for `spender` based on spent `amount`. + /// + /// Does not update the allowance value in case of infinite allowance. + /// Revert if not enough allowance is available. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `owner` - Account that owns the tokens. + /// * `spender` - Account that will spend the tokens. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InsufficientAllowance`] - If the `spender` does not have + /// enough allowance to spend `amount` of tokens. + /// * [`Error::InvalidSpender`] - If the `spender` address is + /// [`Address::ZERO`]. + /// * [`Error::InvalidApprover`] - If the `owner` address is + /// [`Address::ZERO`]. + /// + /// # Events + /// + /// * Does not emit an [`Approval`] event. + pub fn _spend_allowance( + &mut self, + owner: Address, + spender: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + let current_allowance = self.allowances.get(owner).get(spender).get(id); + if current_allowance < U256::MAX { + if current_allowance < amount { + return Err(Error::InsufficientAllowance(ERC6909InsufficientAllowance { + spender, + allowance: current_allowance, + needed: amount, + id, + })); + } + self._approve(owner, spender, id, current_allowance - amount, false)?; + } + Ok(()) + } + /// Creates `amount` of token `id` and assigns them to `account`, by transferring it from [`Address::ZERO`]. + /// Relies on the `_update` mechanism. + /// + /// NOTE: This function is not virtual, {_update} should be overridden instead. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `to` - Account to transfer tokens to. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InvalidReceiver`] - If the `to` address is [`Address::ZERO`]. + /// + /// # Events + /// + /// * [`Transfer`] with `from` set to the zero address. + pub fn _mint( + &mut self, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + if to.is_zero() { + return Err(Error::InvalidReceiver(ERC6909InvalidReceiver { + receiver: Address::ZERO, + })); + } + + self._update(Address::ZERO, to, id, amount) + } + + /// Destroys a `amount` of token `id` from `account`. + /// Relies on the `_update` mechanism. + /// + /// NOTE: This function is not virtual, {_update} should be overridden instead. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `from` - Account to transfer tokens from. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InvalidSender`] - If the `from` address is [`Address::ZERO`]. + /// * [`Error::InsufficientBalance`] - If the `from` address doesn't have + /// enough tokens. + /// + /// # Events + /// + /// * [`Transfer`] with `to` set to the zero address. + pub fn _burn( + &mut self, + from: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + if from.is_zero() { + return Err(Error::InvalidSender(ERC6909InvalidSender { + sender: Address::ZERO, + })); + } + + self._update(from, Address::ZERO, id, amount) + } +} + +#[public] +impl IErc165 for Erc6909 { + fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + ::interface_id() == interface_id + || ::interface_id() == interface_id + } +} + +#[cfg(test)] +mod tests { + use alloy_primitives::{uint, Address, FixedBytes, U256}; + use motsu::prelude::*; + + use super::{Approval, Transfer, OperatorSet, IErc165, IErc6909, Erc6909, Error}; + + #[motsu::test] + fn mint(contract: Contract, alice: Address) { + let one = uint!(1_U256); + let two = uint!(2_U256); + + // Store the initial balance & supply. + contract + .sender(alice) + ._mint(alice, one, two) + .expect("should mint tokens for Alice"); + + let balance = contract.sender(alice).balance_of(alice, one); + + assert_eq!(balance, two, "Alice's balance should be 2"); + } +} \ No newline at end of file From d0ee57ef5320e7f9664124c1a10650b5da29fa68 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Fri, 6 Jun 2025 13:56:50 +0200 Subject: [PATCH 03/33] complete erc6909 unit tests --- contracts/src/token/erc6909/mod.rs | 422 ++++++++++++++++++++++++++++- 1 file changed, 417 insertions(+), 5 deletions(-) diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs index c5c0e103d..970008d9e 100644 --- a/contracts/src/token/erc6909/mod.rs +++ b/contracts/src/token/erc6909/mod.rs @@ -684,17 +684,429 @@ mod tests { #[motsu::test] fn mint(contract: Contract, alice: Address) { - let one = uint!(1_U256); - let two = uint!(2_U256); + let id = uint!(1_U256); + let ten = uint!(10_U256); // Store the initial balance & supply. contract .sender(alice) - ._mint(alice, one, two) + ._mint(alice, id, ten) + .expect("should mint tokens for Alice"); + + let balance = contract.sender(alice).balance_of(alice, id); + + assert_eq!(balance, ten, "Alice's balance should be 10"); + + contract.assert_emitted(&Transfer { + caller: alice, + sender: Address::ZERO, + receiver: alice, + id, + amount: ten, + }); + } + + #[motsu::test] + fn mint_errors_invalid_receiver(contract: Contract, alice: Address) { + let receiver = Address::ZERO; + let id = uint!(1_U256); + let ten = uint!(10_U256); + + let initial_balance = contract.sender(alice).balance_of(receiver, id); + + contract + .sender(alice) + ._mint(alice, id, ten) .expect("should mint tokens for Alice"); - let balance = contract.sender(alice).balance_of(alice, one); + let err = contract + .sender(alice) + ._mint(receiver, id, ten) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InvalidReceiver(_))); + + assert_eq!( + initial_balance, + contract.sender(alice).balance_of(receiver, id) + ); + } + + #[motsu::test] + fn burn(contract: Contract, alice: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + let ten = uint!(10_U256); + + contract + .sender(alice) + ._mint(alice, id, ten) + .expect("should mint tokens for Alice"); - assert_eq!(balance, two, "Alice's balance should be 2"); + let balance = contract.sender(alice).balance_of(alice, id); + + contract + .sender(alice) + ._burn(alice, id, one) + .motsu_unwrap(); + + assert_eq!( + balance - one, + contract.sender(alice).balance_of(alice, id) + ) + } + + #[motsu::test] + fn burn_errors_insufficient_balance(contract: Contract, alice: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + let ten = uint!(10_U256); + + contract + .sender(alice) + ._mint(alice, id, one) + .expect("should mint tokens for Alice"); + + let balance = contract.sender(alice).balance_of(alice, id); + + let err = contract + .sender(alice) + ._burn(alice, id, ten) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InsufficientBalance(_))); + + assert_eq!( + balance, + contract.sender(alice).balance_of(alice, id) + ) + } + + #[motsu::test] + fn burn_errors_invalid_sender(contract: Contract, alice: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + let invalid_sender = Address::ZERO; + + let err = contract + .sender(alice) + ._burn(invalid_sender, id, one) + .expect_err("should not burn token for invalid sender"); + + assert!(matches!(err, Error::InvalidSender(_))); + } + + + #[motsu::test] + fn transfer(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + contract + .sender(alice) + ._mint(alice, id, one) + .motsu_expect("should mint tokens for Alice"); + + contract + .sender(alice) + ._mint(bob, id, one) + .motsu_expect("should mint tokens for Bob"); + + let alice_balance = contract.sender(alice).balance_of(alice, id); + let bob_balance = contract.sender(alice).balance_of(bob, id); + + let result = contract.sender(alice).transfer(bob, id, one); + assert!(result.is_ok()); + + assert_eq!( + alice_balance - one, + contract.sender(alice).balance_of(alice, id) + ); + + assert_eq!( + bob_balance + one, + contract.sender(alice).balance_of(bob, id) + ); + + contract.assert_emitted(&Transfer { + caller: alice, + sender: alice, + receiver: bob, + id, + amount: one, + }); + } + + #[motsu::test] + fn transfer_errors_insufficient_balance(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + contract + .sender(alice) + ._mint(alice, id, one) + .motsu_expect("should mint tokens for Alice"); + + contract + .sender(alice) + ._mint(bob, id, one) + .motsu_expect("should mint tokens for Bob"); + + let alice_balance = contract.sender(alice).balance_of(alice, id); + let bob_balance = contract.sender(alice).balance_of(bob, id); + + let err = + contract.sender(alice).transfer(bob, id, one + one).motsu_unwrap_err(); + assert!(matches!(err, Error::InsufficientBalance(_))); + + assert_eq!( + alice_balance, + contract.sender(alice).balance_of(alice, id) + ); + + assert_eq!( + bob_balance, + contract.sender(alice).balance_of(bob, id) + ); + + } + + #[motsu::test] + fn transfer_from(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + let ten = uint!(10_U256); + + contract + .sender(alice) + .approve(bob, id, one) + .motsu_expect("should Alice approves Bob"); + + contract + .sender(alice) + ._mint(alice, id, ten) + .motsu_expect("should mint tokens for Alice"); + + assert_eq!( + ten, + contract.sender(alice).balance_of(alice, id) + ); + + contract + .sender(bob) + .transfer_from(alice, bob, id, one) + .motsu_expect("should transfer from Alice to Bob"); + + assert_eq!( + ten - one, + contract.sender(alice).balance_of(alice, id) + ); + + assert_eq!( + one, + contract.sender(alice).balance_of(bob, id) + ); + + contract.assert_emitted(&Transfer { + caller: bob, + sender: alice, + receiver: bob, + id, + amount: one, + }); + + } + + #[motsu::test] + fn transfer_from_errors_insufficient_balance(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + let ten = uint!(10_U256); + + contract + .sender(alice) + .approve(bob, id, ten) + .motsu_expect("should Alice approves Bob"); + + contract + .sender(alice) + ._mint(alice, id, one) + .motsu_expect("should mint tokens for Alice"); + + assert_eq!( + one, + contract.sender(alice).balance_of(alice, id) + ); + + let err = contract + .sender(bob) + .transfer_from(alice, bob, id, ten) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InsufficientBalance(_))); + + assert_eq!( + one, + contract.sender(alice).balance_of(alice, id) + ); + } + + #[motsu::test] + fn transfer_from_errors_invalid_receiver(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + contract + .sender(alice) + .approve(bob, id, one) + .motsu_expect("should Alice approves Bob"); + + contract + .sender(alice) + ._mint(alice, id, one) + .motsu_expect("should mint tokens for Alice"); + + let err = contract + .sender(bob) + .transfer_from(alice, Address::ZERO, id, one) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InvalidReceiver(_))); + + assert_eq!( + one, + contract.sender(alice).balance_of(alice, id) + ); + } + + #[motsu::test] + fn transfer_from_errors_insufficient_allowance(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + contract + .sender(alice) + ._mint(alice, id, one) + .motsu_expect("should mint tokens for Alice"); + + let err = contract + .sender(bob) + .transfer_from(alice, bob, id, one) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InsufficientAllowance(_))); + } + + #[motsu::test] + fn approves_and_reads_allowance(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + let allowance = contract.sender(alice).allowance(alice, bob, id); + assert_eq!(U256::ZERO, allowance); + + contract + .sender(alice) + .approve(bob, id, one) + .motsu_expect("should Alice approves Bob"); + + let current_allowance = contract.sender(alice).allowance(alice, bob, id); + assert_eq!(one, current_allowance); + + contract.assert_emitted(&Approval { + owner: alice, + spender: bob, + id, + amount: one, + }); + } + + #[motsu::test] + fn approve_errors_invalid_spender(contract: Contract, alice: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + let err = contract + .sender(alice) + .approve(Address::ZERO, id, one) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InvalidSpender(_))); + } + + #[motsu::test] + fn approve_errors_invalid_approver(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + + let err = contract + .sender(alice) + ._approve(Address::ZERO, bob, id, one, false) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InvalidApprover(_))); + } + + #[motsu::test] + fn set_operator_and_reads_operator(contract: Contract, alice: Address, bob: Address) { + let is_operator = contract.sender(alice).is_operator(alice, bob); + assert_eq!(false, is_operator); + + contract + .sender(alice) + .set_operator(bob, true) + .motsu_expect("should Alice sets Bob as operator"); + + let is_operator = contract.sender(alice).is_operator(alice, bob); + assert_eq!(true, is_operator); + + contract.assert_emitted(&OperatorSet { + owner: alice, + spender: bob, + approved: true, + }); + } + + #[motsu::test] + fn set_operator_errors_invalid_spender(contract: Contract, alice: Address, bob: Address) { + let err = contract + .sender(alice) + .set_operator(Address::ZERO, true) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InvalidSpender(_))); + } + + #[motsu::test] + fn set_operator_errors_invalid_approver(contract: Contract, alice: Address, bob: Address) { + let err = contract + .sender(alice) + ._set_operator(Address::ZERO, bob, true) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InvalidApprover(_))); + } + + #[motsu::test] + fn interface_id() { + let actual = ::interface_id(); + let expected: FixedBytes<4> = 0x0f632fb3.into(); + assert_eq!(actual, expected); + } + + #[motsu::test] + fn supports_interface(contract: Contract, alice: Address) { + assert!(contract + .sender(alice) + .supports_interface(::interface_id())); + assert!(contract + .sender(alice) + .supports_interface(::interface_id())); + + let fake_interface_id = 0x12345678u32; + assert!(!contract + .sender(alice) + .supports_interface(fake_interface_id.into())); } } \ No newline at end of file From 58c07e5d872c2a58cf47e5c380ae177609cb3f5a Mon Sep 17 00:00:00 2001 From: onurinanc Date: Fri, 6 Jun 2025 16:39:46 +0200 Subject: [PATCH 04/33] finish supply extension and related motsu tests --- contracts/src/token/erc6909/extensions/mod.rs | 4 + .../token/erc6909/extensions/token_supply.rs | 457 ++++++++++++++++++ contracts/src/token/erc6909/mod.rs | 2 + 3 files changed, 463 insertions(+) create mode 100644 contracts/src/token/erc6909/extensions/mod.rs diff --git a/contracts/src/token/erc6909/extensions/mod.rs b/contracts/src/token/erc6909/extensions/mod.rs new file mode 100644 index 000000000..854edd09c --- /dev/null +++ b/contracts/src/token/erc6909/extensions/mod.rs @@ -0,0 +1,4 @@ +//! Common extensions to the ERC-6909 standard. +pub mod token_supply; + +pub use token_supply::{Erc6909TokenSupply, IErc6909TokenSupply}; \ No newline at end of file diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index e69de29bb..4b6d46d9b 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -0,0 +1,457 @@ +//! Implementation of the Token Supply extension defined in ERC6909. +//! Tracks the total supply of each token id individually. + +use alloc::{vec, vec::Vec}; +use core::ops::{Deref, DerefMut}; + +use alloy_primitives::{Address, FixedBytes, U256}; +use openzeppelin_stylus_proc::interface_id; +use stylus_sdk::{ + abi::Bytes, + msg, + prelude::*, + storage::{StorageMap, StorageU256}, +}; + +use crate::{ + token::erc6909::{self, Erc6909, Error, IErc6909}, + utils::{ + introspection::erc165::IErc165, + math::storage::{AddAssignChecked, SubAssignUnchecked}, + }, +}; + +/// State of an [`Erc6909TokenSupply`] contract. +#[storage] +pub struct Erc6909TokenSupply { + /// [`Erc6909`] contract. + pub erc6909: Erc6909, + /// Mapping from token id to total supply. + pub(crate) total_supplies: StorageMap, +} + +impl Deref for Erc6909TokenSupply { + type Target = Erc6909; + + fn deref(&self) -> &Self::Target { + &self.erc6909 + } +} + +impl DerefMut for Erc6909TokenSupply { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.erc6909 + } +} + +/// Required interface of a [`Erc6909TokenSupply`] contract. +#[interface_id] +pub trait IErc6909TokenSupply: IErc165 { + /// Returns the total supply of the token of type `id`. + /// + /// # Arguments + /// + /// * `&self` - Read access to the contract's state. + /// * `id` - Token id as a number. + fn total_supply(&self, id: U256) -> U256; +} + +#[public] +#[implements(IErc6909, IErc6909TokenSupply, IErc165)] +impl Erc6909TokenSupply {} + +#[public] +impl IErc6909TokenSupply for Erc6909TokenSupply { + fn total_supply(&self, id: U256) -> U256 { + self.total_supplies.get(id) + } +} + +#[public] +impl IErc6909 for Erc6909TokenSupply { + type Error = erc6909::Error; + + fn balance_of(&self, owner: Address, id: U256) -> U256 { + self.erc6909.balance_of(owner, id) + } + + fn allowance( + &self, + owner: Address, + spender: Address, + id: U256, + ) -> U256 { + self.erc6909.allowance(owner, spender, id) + } + + fn is_operator(&self, owner: Address, spender: Address) -> bool { + self.erc6909.is_operator(owner, spender) + } + + fn approve( + &mut self, + spender: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909.approve(spender, id, amount) + } + + fn set_operator( + &mut self, + spender: Address, + approved: bool, + ) -> Result { + self.erc6909.set_operator(spender, approved) + } + + fn transfer( + &mut self, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + let sender = msg::sender(); + self._transfer(sender, receiver, id, amount)?; + Ok(true) + } + + fn transfer_from( + &mut self, + sender: Address, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + let caller = msg::sender(); + if (sender != caller) && !self.is_operator(sender, caller) { + self.erc6909._spend_allowance(sender, caller, id, amount)?; + } + self._transfer(sender, receiver, id, amount)?; + Ok(true) + } + +} + +impl Erc6909TokenSupply{ + /// Creates `amount` of token `id` and assigns them to `account`, by transferring it from [`Address::ZERO`]. + /// Relies on the `_update` mechanism. + /// + /// Re-export of [`Erc6909::_mint`]. + pub fn _mint( + &mut self, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + if to.is_zero() { + return Err(erc6909::Error::InvalidReceiver(erc6909::ERC6909InvalidReceiver { + receiver: Address::ZERO, + })); + } + + self._update(Address::ZERO, to, id, amount) + } + + /// Destroys a `amount` of token `id` from `account`. + /// Relies on the `_update` mechanism. + /// + /// Re-export of [`Erc6909::_burn`]. + pub fn _burn( + &mut self, + from: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + if from.is_zero() { + return Err(erc6909::Error::InvalidSender(erc6909::ERC6909InvalidSender { + sender: Address::ZERO, + })); + } + + self._update(from, Address::ZERO, id, amount) + } + + /// Moves `amount` of token `id` from `from` to `to` without checking for approvals. + /// This function verifies that neither the sender nor the receiver are [`Address::ZERO`], + /// which means it cannot mint or burn tokens. + /// + /// Relies on the `_update` mechanism. + /// + /// Re-export of [`Erc6909::_transfer`]. + fn _transfer( + &mut self, + from: Address, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), Error> { + if from.is_zero() { + return Err(erc6909::Error::InvalidSender(erc6909::ERC6909InvalidSender { + sender: Address::ZERO, + })); + } + + if to.is_zero() { + return Err(erc6909::Error::InvalidReceiver(erc6909::ERC6909InvalidReceiver { + receiver: Address::ZERO, + })); + } + + self._update(from, to, id, amount)?; + + Ok(()) + } +} + +impl Erc6909TokenSupply{ + /// Extended version of [`Erc6909::_update`] that updates the supply of + /// tokens. + /// + /// # Arguments + /// + /// * `&mut self` - Write access to the contract's state. + /// * `from` - Account to transfer tokens from. + /// * `to` - Account to transfer tokens to. + /// * `id` - Token id as a number. + /// * `amount` - Amount to be transferred. + /// + /// # Errors + /// + /// * [`Error::InsufficientBalance`] - If the `from` address doesn't have + /// enough tokens. + /// + /// # Events + /// + /// * [`Transfer`]. + /// + /// # Panics + /// + /// * If updated balance and/or supply exceeds [`U256::MAX`], may happen + /// during the `mint` operation. + fn _update( + &mut self, + from: Address, + to: Address, + id: U256, + amount: U256 + ) -> Result<(), erc6909::Error> { + self.erc6909._update(from, to, id, amount)?; + + if from.is_zero() { + self.total_supplies.setter(id).add_assign_checked( + amount, + "should not exceed `U256::MAX` for `total_supplies`", + ); + } + + if to.is_zero() { + self.total_supplies.setter(id).sub_assign_unchecked(amount); + } + Ok(()) + } +} + +#[public] +impl IErc165 for Erc6909TokenSupply { + fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + ::interface_id() == interface_id + || self.erc6909.supports_interface(interface_id) + || ::interface_id() == interface_id + } +} + +#[cfg(test)] +mod tests { + use motsu::prelude::*; + + use stylus_sdk::{ + alloy_primitives::{uint, Address, fixed_bytes, FixedBytes, U256}, + prelude::*, + }; + + use super::*; + use crate::token::erc6909::{ + ERC6909InvalidReceiver, ERC6909InvalidSender + }; + + unsafe impl TopLevelStorage for Erc6909TokenSupply {} + + #[motsu::test] + fn mint(contract: Contract, alice: Address) { + let id = uint!(1_U256); + let ten = uint!(10_U256); + + assert_eq!( + U256::ZERO, + contract.sender(alice).total_supply(id) + ); + + contract + .sender(alice) + ._mint(alice, id, ten) + .expect("should mint tokens for Alice"); + + assert_eq!( + ten, + contract.sender(alice).balance_of(alice, id) + ); + + assert_eq!( + ten, + contract.sender(alice).total_supply(id) + ); + } + + #[motsu::test] + fn mint_twice(contract: Contract, alice: Address, bob: Address) { + let id = uint!(1_U256); + let five = uint!(5_U256); + let ten = uint!(10_U256); + + assert_eq!( + U256::ZERO, + contract.sender(alice).total_supply(id) + ); + + contract + .sender(alice) + ._mint(alice, id, ten) + .expect("should mint tokens for Alice"); + + contract + .sender(alice) + ._mint(bob, id, five) + .expect("should mint tokens for Bob"); + + assert_eq!( + ten, + contract.sender(alice).balance_of(alice, id) + ); + + assert_eq!( + five, + contract.sender(alice).balance_of(bob, id) + ); + + assert_eq!( + ten + five, + contract.sender(alice).total_supply(id) + ); + } + + #[motsu::test] + fn mint_errors_invalid_receiver(contract: Contract, alice: Address) { + let id = uint!(1_U256); + let ten = uint!(10_U256); + + assert_eq!( + U256::ZERO, + contract.sender(alice).total_supply(id) + ); + + let invalid_receiver = Address::ZERO; + + let err = contract + .sender(alice) + ._mint(invalid_receiver, id, ten) + .motsu_unwrap_err(); + + assert!(matches!(err, Error::InvalidReceiver(_))); + } + + #[motsu::test] + #[should_panic = "should not exceed `U256::MAX` for `total_supplies`"] + fn mint_panics_on_total_supply_overflow(contract: Contract, alice: Address, bob: Address) { + let id = uint!(2_U256); + let one = uint!(1_U256); + let ten = uint!(10_U256); + + assert_eq!( + U256::ZERO, + contract.sender(alice).total_supply(id) + ); + + contract + .sender(alice) + ._mint(alice, id, U256::MAX - ten) + .expect("should mint tokens for Alice"); + + // This should panic + contract + .sender(alice) + ._mint(bob, id, ten + one) + .expect("should mint tokens for Bob"); + } + + #[motsu::test] + fn burn(contract: Contract, alice: Address) { + let id = uint!(2_U256); + let ten = uint!(10_U256); + let one = uint!(1_U256); + + assert_eq!( + U256::ZERO, + contract.sender(alice).total_supply(id) + ); + + contract + .sender(alice) + ._mint(alice, id, ten) + .expect("should mint tokens for Alice"); + + contract + .sender(alice) + ._burn(alice, id, one) + .expect("should burn tokens for Alice"); + + assert_eq!( + ten - one, + contract.sender(alice).total_supply(id) + ); + } + + #[motsu::test] + fn burn_errors_invalid_sender(contract: Contract, alice: Address) { + let id = uint!(2_U256); + let ten = uint!(10_U256); + + let invalid_sender = Address::ZERO; + + assert_eq!( + U256::ZERO, + contract.sender(alice).total_supply(id) + ); + + let err = contract + .sender(alice) + ._burn(invalid_sender, id, ten) + .motsu_unwrap_err(); + assert!(matches!(err, Error::InvalidSender(ERC6909InvalidSender { sender }) if sender == invalid_sender)); + } + + #[motsu::test] + fn interface_id() { + let actual = ::interface_id(); + let expected: FixedBytes<4> = fixed_bytes!("0xbd85b039"); + assert_eq!(actual, expected); + } + + #[motsu::test] + fn supports_interface(contract: Contract, alice: Address) { + assert!(contract.sender(alice).supports_interface( + ::interface_id() + )); + assert!(contract + .sender(alice) + .supports_interface(::interface_id())); + assert!(contract + .sender(alice) + .supports_interface(::interface_id())); + + let fake_interface_id = 0x12345678u32; + assert!(!contract + .sender(alice) + .supports_interface(fake_interface_id.into())); + } +} \ No newline at end of file diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs index 970008d9e..3d9d64552 100644 --- a/contracts/src/token/erc6909/mod.rs +++ b/contracts/src/token/erc6909/mod.rs @@ -15,6 +15,8 @@ use crate::utils::{ math::storage::AddAssignUnchecked, }; +pub mod extensions; + pub use sol::*; #[cfg_attr(coverage_nightly, coverage(off))] mod sol { From 4140384e2c97fbcd81ca3cfc131386f3d2c21691 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 10:39:46 +0200 Subject: [PATCH 05/33] remove redundant --- contracts/src/token/erc6909/extensions/token_supply.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index 4b6d46d9b..aad4eea6f 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -7,7 +7,6 @@ use core::ops::{Deref, DerefMut}; use alloy_primitives::{Address, FixedBytes, U256}; use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ - abi::Bytes, msg, prelude::*, storage::{StorageMap, StorageU256}, From 5b45733265b82e6d20421d4554dce31e337f011f Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 11:14:57 +0200 Subject: [PATCH 06/33] add Erc6909Example and Erc6909TokenSupplyExample --- Cargo.lock | 26 ++++ Cargo.toml | 4 + examples/erc6909-supply/Cargo.toml | 29 +++++ examples/erc6909-supply/src/lib.rs | 118 ++++++++++++++++++ examples/erc6909-supply/src/main.rs | 10 ++ examples/erc6909-supply/tests/abi/mod.rs | 0 .../erc6909-supply/tests/erc6909-supply.rs | 0 examples/erc6909/Cargo.toml | 29 +++++ examples/erc6909/src/lib.rs | 109 ++++++++++++++++ examples/erc6909/src/main.rs | 10 ++ examples/erc6909/tests/abi/mod.rs | 0 examples/erc6909/tests/erc6909.rs | 0 12 files changed, 335 insertions(+) create mode 100644 examples/erc6909-supply/Cargo.toml create mode 100644 examples/erc6909-supply/src/lib.rs create mode 100644 examples/erc6909-supply/src/main.rs create mode 100644 examples/erc6909-supply/tests/abi/mod.rs create mode 100644 examples/erc6909-supply/tests/erc6909-supply.rs create mode 100644 examples/erc6909/Cargo.toml create mode 100644 examples/erc6909/src/lib.rs create mode 100644 examples/erc6909/src/main.rs create mode 100644 examples/erc6909/tests/abi/mod.rs create mode 100644 examples/erc6909/tests/erc6909.rs diff --git a/Cargo.lock b/Cargo.lock index 68010960c..5fcf71295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1883,6 +1883,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "erc6909-example" +version = "0.2.0-rc.0" +dependencies = [ + "alloy", + "alloy-primitives", + "e2e", + "eyre", + "openzeppelin-stylus", + "stylus-sdk", + "tokio", +] + +[[package]] +name = "erc6909-supply-example" +version = "0.2.0-rc.0" +dependencies = [ + "alloy", + "alloy-primitives", + "e2e", + "eyre", + "openzeppelin-stylus", + "stylus-sdk", + "tokio", +] + [[package]] name = "erc721-consecutive-example" version = "0.3.0-alpha.1" diff --git a/Cargo.toml b/Cargo.toml index 7b7df0763..ac2a1f14f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,8 @@ members = [ "examples/erc1155-metadata-uri", "examples/erc1155-supply", "examples/erc4626", + "examples/erc6909", + "examples/erc6909-supply", "examples/safe-erc20", "examples/merkle-proofs", "examples/ownable", @@ -57,6 +59,8 @@ default-members = [ "examples/erc1155-metadata-uri", "examples/erc1155-supply", "examples/erc4626", + "examples/erc6909", + "examples/erc6909-supply", "examples/safe-erc20", "examples/merkle-proofs", "examples/ownable", diff --git a/examples/erc6909-supply/Cargo.toml b/examples/erc6909-supply/Cargo.toml new file mode 100644 index 000000000..4d36339d0 --- /dev/null +++ b/examples/erc6909-supply/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "erc6909-supply-example" +edition.workspace = true +license.workspace = true +repository.workspace = true +publish = false +version.workspace = true + +[dependencies] +openzeppelin-stylus.workspace = true +alloy-primitives.workspace = true +stylus-sdk.workspace = true + +[dev-dependencies] +alloy.workspace = true +e2e.workspace = true +tokio.workspace = true +eyre.workspace = true + +[features] +e2e = [] +export-abi = ["stylus-sdk/export-abi", "openzeppelin-stylus/export-abi"] + +[lib] +crate-type = ["lib", "cdylib"] + +[[bin]] +name = "erc6909-supply-example" +path = "src/main.rs" \ No newline at end of file diff --git a/examples/erc6909-supply/src/lib.rs b/examples/erc6909-supply/src/lib.rs new file mode 100644 index 000000000..3c0aa8059 --- /dev/null +++ b/examples/erc6909-supply/src/lib.rs @@ -0,0 +1,118 @@ +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +#![allow(clippy::result_large_err)] +extern crate alloc; + +use alloc::vec::Vec; + +use alloy_primitives::{Address, FixedBytes, U256}; + +use openzeppelin_stylus::{ + token::erc6909::{ + self, + extensions::{Erc6909TokenSupply, IErc6909TokenSupply}, + IErc6909, + }, + utils::introspection::erc165::IErc165, +}; + +use stylus_sdk::prelude::*; + +#[entrypoint] +#[storage] +struct Erc6909TokenSupplyExample { + erc6909_token_supply: Erc6909TokenSupply, +} + +#[public] +#[implements(IErc6909, IErc6909TokenSupply, IErc165)] +impl Erc6909TokenSupplyExample { + fn mint( + &mut self, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909_token_supply._mint(to, id, amount) + } + + fn burn( + &mut self, + from: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909_token_supply._burn(from, id, amount) + } +} + +#[public] +impl IErc6909 for Erc6909TokenSupplyExample { + type Error = erc6909::Error; + + fn balance_of(&self, owner: Address, id: U256) -> U256 { + self.erc6909_token_supply.balance_of(owner, id) + } + + fn allowance( + &self, + owner: Address, + spender: Address, + id: U256, + ) -> U256 { + self.erc6909_token_supply.allowance(owner, spender, id) + } + + fn is_operator(&self, owner: Address, spender: Address) -> bool { + self.erc6909_token_supply.is_operator(owner, spender) + } + + fn approve( + &mut self, + spender: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909_token_supply.approve(spender, id, amount) + } + + fn set_operator( + &mut self, + spender: Address, + approved: bool, + ) -> Result { + self.erc6909_token_supply.set_operator(spender, approved) + } + + fn transfer( + &mut self, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909_token_supply.transfer(receiver, id, amount) + } + + fn transfer_from( + &mut self, + sender: Address, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909_token_supply.transfer_from(sender, receiver, id, amount) + } +} + +#[public] +impl IErc6909TokenSupply for Erc6909TokenSupplyExample { + fn total_supply(&self, id: U256) -> U256 { + self.erc6909_token_supply.total_supply(id) + } +} + +#[public] +impl IErc165 for Erc6909TokenSupplyExample { + fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + self.erc6909_token_supply.supports_interface(interface_id) + } +} \ No newline at end of file diff --git a/examples/erc6909-supply/src/main.rs b/examples/erc6909-supply/src/main.rs new file mode 100644 index 000000000..4ee0329e2 --- /dev/null +++ b/examples/erc6909-supply/src/main.rs @@ -0,0 +1,10 @@ +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] + +#[cfg(not(any(test, feature = "export-abi")))] +#[no_mangle] +pub extern "C" fn main() {} + +#[cfg(feature = "export-abi")] +fn main() { + erc6909_supply_example::print_from_args(); +} \ No newline at end of file diff --git a/examples/erc6909-supply/tests/abi/mod.rs b/examples/erc6909-supply/tests/abi/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/examples/erc6909-supply/tests/erc6909-supply.rs b/examples/erc6909-supply/tests/erc6909-supply.rs new file mode 100644 index 000000000..e69de29bb diff --git a/examples/erc6909/Cargo.toml b/examples/erc6909/Cargo.toml new file mode 100644 index 000000000..06001f7e2 --- /dev/null +++ b/examples/erc6909/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "erc6909-example" +edition.workspace = true +license.workspace = true +repository.workspace = true +publish = false +version.workspace = true + +[dependencies] +openzeppelin-stylus.workspace = true +alloy-primitives.workspace = true +stylus-sdk.workspace = true + +[dev-dependencies] +alloy.workspace = true +e2e.workspace = true +tokio.workspace = true +eyre.workspace = true + +[features] +e2e = [] +export-abi = ["stylus-sdk/export-abi", "openzeppelin-stylus/export-abi"] + +[lib] +crate-type = ["lib", "cdylib"] + +[[bin]] +name = "erc6909-example" +path = "src/main.rs" \ No newline at end of file diff --git a/examples/erc6909/src/lib.rs b/examples/erc6909/src/lib.rs new file mode 100644 index 000000000..ac41811ed --- /dev/null +++ b/examples/erc6909/src/lib.rs @@ -0,0 +1,109 @@ +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +#![allow(clippy::result_large_err)] +extern crate alloc; + +use alloc::vec::Vec; + +use openzeppelin_stylus::{ + token::erc6909::{self, Erc6909, IErc6909}, + utils::introspection::erc165::IErc165, +}; + +use stylus_sdk::{ + alloy_primitives::{Address, FixedBytes, U256}, + prelude::*, +}; + +#[entrypoint] +#[storage] +struct Erc6909Example { + erc6909: Erc6909 +} + +#[public] +#[implements(IErc6909, IErc165)] +impl Erc6909Example { + fn mint( + &mut self, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909._mint(to, id, amount) + } + + fn burn( + &mut self, + from: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909._burn(from, id, amount) + + } +} + +#[public] +impl IErc6909 for Erc6909Example { + type Error = erc6909::Error; + + fn balance_of(&self, owner: Address, id: U256) -> U256 { + self.erc6909.balance_of(owner, id) + } + + fn allowance( + &self, + owner: Address, + spender: Address, + id: U256, + ) -> U256 { + self.erc6909.allowance(owner, spender, id) + } + + fn is_operator(&self, owner: Address, spender: Address) -> bool { + self.erc6909.is_operator(owner, spender) + } + + fn approve( + &mut self, + spender: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909.approve(spender, id, amount) + } + + fn set_operator( + &mut self, + spender: Address, + approved: bool, + ) -> Result { + self.erc6909.set_operator(spender, approved) + } + + fn transfer( + &mut self, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909.transfer(receiver, id, amount) + } + + fn transfer_from( + &mut self, + sender: Address, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909.transfer_from(sender, receiver, id, amount) + } +} + +#[public] +impl IErc165 for Erc6909Example { + fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + self.erc6909.supports_interface(interface_id) + } +} \ No newline at end of file diff --git a/examples/erc6909/src/main.rs b/examples/erc6909/src/main.rs new file mode 100644 index 000000000..05eafbb30 --- /dev/null +++ b/examples/erc6909/src/main.rs @@ -0,0 +1,10 @@ +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] + +#[cfg(not(any(test, feature = "export-abi")))] +#[no_mangle] +pub extern "C" fn main() {} + +#[cfg(feature = "export-abi")] +fn main() { + erc6909_example::print_from_args(); +} \ No newline at end of file diff --git a/examples/erc6909/tests/abi/mod.rs b/examples/erc6909/tests/abi/mod.rs new file mode 100644 index 000000000..e69de29bb diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs new file mode 100644 index 000000000..e69de29bb From b56e5691a8bb2d6a94aa8166e609fad2158a3efb Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 11:29:32 +0200 Subject: [PATCH 07/33] fmt --- contracts/src/token/erc6909/extensions/mod.rs | 2 +- .../token/erc6909/extensions/token_supply.rs | 175 ++++----- contracts/src/token/erc6909/mod.rs | 367 ++++++++++-------- contracts/src/token/mod.rs | 2 +- examples/erc6909-supply/src/lib.rs | 11 +- examples/erc6909-supply/src/main.rs | 2 +- .../erc6909-supply/tests/erc6909-supply.rs | 1 + examples/erc6909/src/lib.rs | 37 +- examples/erc6909/src/main.rs | 2 +- examples/erc6909/tests/erc6909.rs | 1 + 10 files changed, 299 insertions(+), 301 deletions(-) diff --git a/contracts/src/token/erc6909/extensions/mod.rs b/contracts/src/token/erc6909/extensions/mod.rs index 854edd09c..c95715a5c 100644 --- a/contracts/src/token/erc6909/extensions/mod.rs +++ b/contracts/src/token/erc6909/extensions/mod.rs @@ -1,4 +1,4 @@ //! Common extensions to the ERC-6909 standard. pub mod token_supply; -pub use token_supply::{Erc6909TokenSupply, IErc6909TokenSupply}; \ No newline at end of file +pub use token_supply::{Erc6909TokenSupply, IErc6909TokenSupply}; diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index aad4eea6f..69e59c3a4 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -74,12 +74,7 @@ impl IErc6909 for Erc6909TokenSupply { self.erc6909.balance_of(owner, id) } - fn allowance( - &self, - owner: Address, - spender: Address, - id: U256, - ) -> U256 { + fn allowance(&self, owner: Address, spender: Address, id: U256) -> U256 { self.erc6909.allowance(owner, spender, id) } @@ -97,13 +92,13 @@ impl IErc6909 for Erc6909TokenSupply { } fn set_operator( - &mut self, - spender: Address, + &mut self, + spender: Address, approved: bool, ) -> Result { self.erc6909.set_operator(spender, approved) } - + fn transfer( &mut self, receiver: Address, @@ -129,12 +124,12 @@ impl IErc6909 for Erc6909TokenSupply { self._transfer(sender, receiver, id, amount)?; Ok(true) } - } -impl Erc6909TokenSupply{ - /// Creates `amount` of token `id` and assigns them to `account`, by transferring it from [`Address::ZERO`]. - /// Relies on the `_update` mechanism. +impl Erc6909TokenSupply { + /// Creates `amount` of token `id` and assigns them to `account`, by + /// transferring it from [`Address::ZERO`]. Relies on the `_update` + /// mechanism. /// /// Re-export of [`Erc6909::_mint`]. pub fn _mint( @@ -144,9 +139,9 @@ impl Erc6909TokenSupply{ amount: U256, ) -> Result<(), Error> { if to.is_zero() { - return Err(erc6909::Error::InvalidReceiver(erc6909::ERC6909InvalidReceiver { - receiver: Address::ZERO, - })); + return Err(erc6909::Error::InvalidReceiver( + erc6909::ERC6909InvalidReceiver { receiver: Address::ZERO }, + )); } self._update(Address::ZERO, to, id, amount) @@ -163,20 +158,21 @@ impl Erc6909TokenSupply{ amount: U256, ) -> Result<(), Error> { if from.is_zero() { - return Err(erc6909::Error::InvalidSender(erc6909::ERC6909InvalidSender { - sender: Address::ZERO, - })); + return Err(erc6909::Error::InvalidSender( + erc6909::ERC6909InvalidSender { sender: Address::ZERO }, + )); } self._update(from, Address::ZERO, id, amount) } - /// Moves `amount` of token `id` from `from` to `to` without checking for approvals. - /// This function verifies that neither the sender nor the receiver are [`Address::ZERO`], - /// which means it cannot mint or burn tokens. + /// Moves `amount` of token `id` from `from` to `to` without checking for + /// approvals. This function verifies that neither the sender nor the + /// receiver are [`Address::ZERO`], which means it cannot mint or burn + /// tokens. /// /// Relies on the `_update` mechanism. - /// + /// /// Re-export of [`Erc6909::_transfer`]. fn _transfer( &mut self, @@ -186,15 +182,15 @@ impl Erc6909TokenSupply{ amount: U256, ) -> Result<(), Error> { if from.is_zero() { - return Err(erc6909::Error::InvalidSender(erc6909::ERC6909InvalidSender { - sender: Address::ZERO, - })); + return Err(erc6909::Error::InvalidSender( + erc6909::ERC6909InvalidSender { sender: Address::ZERO }, + )); } if to.is_zero() { - return Err(erc6909::Error::InvalidReceiver(erc6909::ERC6909InvalidReceiver { - receiver: Address::ZERO, - })); + return Err(erc6909::Error::InvalidReceiver( + erc6909::ERC6909InvalidReceiver { receiver: Address::ZERO }, + )); } self._update(from, to, id, amount)?; @@ -203,7 +199,7 @@ impl Erc6909TokenSupply{ } } -impl Erc6909TokenSupply{ +impl Erc6909TokenSupply { /// Extended version of [`Erc6909::_update`] that updates the supply of /// tokens. /// @@ -233,7 +229,7 @@ impl Erc6909TokenSupply{ from: Address, to: Address, id: U256, - amount: U256 + amount: U256, ) -> Result<(), erc6909::Error> { self.erc6909._update(from, to, id, amount)?; @@ -263,55 +259,44 @@ impl IErc165 for Erc6909TokenSupply { #[cfg(test)] mod tests { use motsu::prelude::*; - use stylus_sdk::{ - alloy_primitives::{uint, Address, fixed_bytes, FixedBytes, U256}, + alloy_primitives::{fixed_bytes, uint, Address, FixedBytes, U256}, prelude::*, }; use super::*; - use crate::token::erc6909::{ - ERC6909InvalidReceiver, ERC6909InvalidSender - }; + use crate::token::erc6909::{ERC6909InvalidReceiver, ERC6909InvalidSender}; unsafe impl TopLevelStorage for Erc6909TokenSupply {} - + #[motsu::test] fn mint(contract: Contract, alice: Address) { let id = uint!(1_U256); let ten = uint!(10_U256); - assert_eq!( - U256::ZERO, - contract.sender(alice).total_supply(id) - ); + assert_eq!(U256::ZERO, contract.sender(alice).total_supply(id)); contract .sender(alice) ._mint(alice, id, ten) .expect("should mint tokens for Alice"); - assert_eq!( - ten, - contract.sender(alice).balance_of(alice, id) - ); + assert_eq!(ten, contract.sender(alice).balance_of(alice, id)); - assert_eq!( - ten, - contract.sender(alice).total_supply(id) - ); + assert_eq!(ten, contract.sender(alice).total_supply(id)); } #[motsu::test] - fn mint_twice(contract: Contract, alice: Address, bob: Address) { + fn mint_twice( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(1_U256); let five = uint!(5_U256); let ten = uint!(10_U256); - assert_eq!( - U256::ZERO, - contract.sender(alice).total_supply(id) - ); + assert_eq!(U256::ZERO, contract.sender(alice).total_supply(id)); contract .sender(alice) @@ -323,31 +308,22 @@ mod tests { ._mint(bob, id, five) .expect("should mint tokens for Bob"); - assert_eq!( - ten, - contract.sender(alice).balance_of(alice, id) - ); + assert_eq!(ten, contract.sender(alice).balance_of(alice, id)); - assert_eq!( - five, - contract.sender(alice).balance_of(bob, id) - ); + assert_eq!(five, contract.sender(alice).balance_of(bob, id)); - assert_eq!( - ten + five, - contract.sender(alice).total_supply(id) - ); + assert_eq!(ten + five, contract.sender(alice).total_supply(id)); } #[motsu::test] - fn mint_errors_invalid_receiver(contract: Contract, alice: Address) { + fn mint_errors_invalid_receiver( + contract: Contract, + alice: Address, + ) { let id = uint!(1_U256); let ten = uint!(10_U256); - assert_eq!( - U256::ZERO, - contract.sender(alice).total_supply(id) - ); + assert_eq!(U256::ZERO, contract.sender(alice).total_supply(id)); let invalid_receiver = Address::ZERO; @@ -361,15 +337,16 @@ mod tests { #[motsu::test] #[should_panic = "should not exceed `U256::MAX` for `total_supplies`"] - fn mint_panics_on_total_supply_overflow(contract: Contract, alice: Address, bob: Address) { + fn mint_panics_on_total_supply_overflow( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); let ten = uint!(10_U256); - assert_eq!( - U256::ZERO, - contract.sender(alice).total_supply(id) - ); + assert_eq!(U256::ZERO, contract.sender(alice).total_supply(id)); contract .sender(alice) @@ -389,10 +366,7 @@ mod tests { let ten = uint!(10_U256); let one = uint!(1_U256); - assert_eq!( - U256::ZERO, - contract.sender(alice).total_supply(id) - ); + assert_eq!(U256::ZERO, contract.sender(alice).total_supply(id)); contract .sender(alice) @@ -404,53 +378,56 @@ mod tests { ._burn(alice, id, one) .expect("should burn tokens for Alice"); - assert_eq!( - ten - one, - contract.sender(alice).total_supply(id) - ); + assert_eq!(ten - one, contract.sender(alice).total_supply(id)); } #[motsu::test] - fn burn_errors_invalid_sender(contract: Contract, alice: Address) { + fn burn_errors_invalid_sender( + contract: Contract, + alice: Address, + ) { let id = uint!(2_U256); let ten = uint!(10_U256); let invalid_sender = Address::ZERO; - assert_eq!( - U256::ZERO, - contract.sender(alice).total_supply(id) - ); + assert_eq!(U256::ZERO, contract.sender(alice).total_supply(id)); let err = contract .sender(alice) ._burn(invalid_sender, id, ten) .motsu_unwrap_err(); - assert!(matches!(err, Error::InvalidSender(ERC6909InvalidSender { sender }) if sender == invalid_sender)); + assert!( + matches!(err, Error::InvalidSender(ERC6909InvalidSender { sender }) if sender == invalid_sender) + ); } #[motsu::test] fn interface_id() { - let actual = ::interface_id(); + let actual = + ::interface_id(); let expected: FixedBytes<4> = fixed_bytes!("0xbd85b039"); assert_eq!(actual, expected); } #[motsu::test] - fn supports_interface(contract: Contract, alice: Address) { + fn supports_interface( + contract: Contract, + alice: Address, + ) { assert!(contract.sender(alice).supports_interface( ::interface_id() )); - assert!(contract - .sender(alice) - .supports_interface(::interface_id())); - assert!(contract - .sender(alice) - .supports_interface(::interface_id())); + assert!(contract.sender(alice).supports_interface( + ::interface_id() + )); + assert!(contract.sender(alice).supports_interface( + ::interface_id() + )); let fake_interface_id = 0x12345678u32; assert!(!contract .sender(alice) .supports_interface(fake_interface_id.into())); } -} \ No newline at end of file +} diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs index 3d9d64552..c6a0e2941 100644 --- a/contracts/src/token/erc6909/mod.rs +++ b/contracts/src/token/erc6909/mod.rs @@ -5,14 +5,14 @@ use alloc::{vec, vec::Vec}; use alloy_primitives::{Address, FixedBytes, U256}; use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ - call::MethodError, evm, msg, + call::MethodError, + evm, msg, prelude::*, - storage::{StorageMap, StorageU256, StorageBool}, + storage::{StorageBool, StorageMap, StorageU256}, }; use crate::utils::{ - introspection::erc165::IErc165, - math::storage::AddAssignUnchecked, + introspection::erc165::IErc165, math::storage::AddAssignUnchecked, }; pub mod extensions; @@ -102,7 +102,8 @@ pub enum Error { /// Indicates a failure with the `spender`’s `allowance`. Used in /// transfers. InsufficientAllowance(ERC6909InsufficientAllowance), - /// Indicates a failure with the `approver` of a token to be approved. Used in approvals. + /// Indicates a failure with the `approver` of a token to be approved. Used + /// in approvals. InvalidApprover(ERC6909InvalidApprover), /// Indicates a failure with the token `receiver`. Used in transfers. InvalidReceiver(ERC6909InvalidReceiver), @@ -123,8 +124,10 @@ impl MethodError for Error { #[storage] pub struct Erc6909 { pub(crate) balances: StorageMap>, - pub(crate) operator_approvals: StorageMap>, - pub(crate) allowances: StorageMap>> + pub(crate) operator_approvals: + StorageMap>, + pub(crate) allowances: + StorageMap>>, } /// NOTE: Implementation of [`TopLevelStorage`] to be able use `&mut self` when @@ -146,8 +149,9 @@ pub trait IErc6909: IErc165 { /// * `owner` - Account that owns the tokens. /// * `id` - Token id as a number. fn balance_of(&self, owner: Address, id: U256) -> U256; - - /// Returns the amount of tokens of type `id` that `spender` is allowed to spend on behalf of `owner`. + + /// Returns the amount of tokens of type `id` that `spender` is allowed to + /// spend on behalf of `owner`. /// /// NOTE: Does not include operator allowances. /// @@ -157,13 +161,8 @@ pub trait IErc6909: IErc165 { /// * `owner` - Account that owns the tokens. /// * `spender` - Account that will spend the tokens. /// * `id` - Token id as a number. - fn allowance( - &self, - owner: Address, - spender: Address, - id: U256, - ) -> U256; - + fn allowance(&self, owner: Address, spender: Address, id: U256) -> U256; + /// Returns true if `spender` is set as an operator for `owner`. /// /// # Arguments @@ -172,9 +171,10 @@ pub trait IErc6909: IErc165 { /// * `owner` - Account that owns the tokens. /// * `spender` - Account that is an operator for the owner. fn is_operator(&self, owner: Address, spender: Address) -> bool; - - /// Sets an approval to `spender` for `amount` of tokens of type `id` from the caller's tokens. - /// An `amount` of [`U256::MAX`] signifies an unlimited approval. + + /// Sets an approval to `spender` for `amount` of tokens of type `id` from + /// the caller's tokens. An `amount` of [`U256::MAX`] signifies an + /// unlimited approval. /// /// Must return true. /// @@ -192,13 +192,14 @@ pub trait IErc6909: IErc165 { /// /// * [`Approval`]. fn approve( - &mut self, - spender: Address, - id: U256, + &mut self, + spender: Address, + id: U256, amount: U256, ) -> Result; - - /// Grants or revokes unlimited transfer permission of any token id to `spender` for the caller's tokens. + + /// Grants or revokes unlimited transfer permission of any token id to + /// `spender` for the caller's tokens. /// /// Must return true. /// @@ -217,12 +218,13 @@ pub trait IErc6909: IErc165 { /// /// * [`OperatorSet`]. fn set_operator( - &mut self, - spender: Address, + &mut self, + spender: Address, approved: bool, ) -> Result; - - /// Transfers `amount` of token type `id` from the caller's account to `receiver`. + + /// Transfers `amount` of token type `id` from the caller's account to + /// `receiver`. /// /// Must return true. /// @@ -235,21 +237,22 @@ pub trait IErc6909: IErc165 { /// /// # Errors /// - /// * [`Error::InvalidReceiver`] - If the `receiver` address is [`Address::ZERO`]. + /// * [`Error::InvalidReceiver`] - If the `receiver` address is + /// [`Address::ZERO`]. /// * [`Error::InsufficientBalance`] - If the caller doesn't have a balance /// /// # Events /// /// * [`Transfer`]. fn transfer( - &mut self, - receiver: Address, - id: U256, + &mut self, + receiver: Address, + id: U256, amount: U256, ) -> Result; - + /// Transfers `amount` of token type `id` from `sender` to `receiver`. - /// + /// /// Must return true. /// /// # Arguments @@ -262,18 +265,21 @@ pub trait IErc6909: IErc165 { /// /// # Errors /// - /// * [`Error::InvalidSender`] - If the `sender` address is [`Address::ZERO`]. - /// * [`Error::InvalidReceiver`] - If the `receiver` address is [`Address::ZERO`]. - /// * [`Error::InsufficientBalance`] - If the `sender` doesn't have a balance + /// * [`Error::InvalidSender`] - If the `sender` address is + /// [`Address::ZERO`]. + /// * [`Error::InvalidReceiver`] - If the `receiver` address is + /// [`Address::ZERO`]. + /// * [`Error::InsufficientBalance`] - If the `sender` doesn't have a + /// balance /// /// # Events /// /// * [`Transfer`]. fn transfer_from( - &mut self, - sender: Address, - receiver: Address, - id: U256, + &mut self, + sender: Address, + receiver: Address, + id: U256, amount: U256, ) -> Result; } @@ -290,12 +296,7 @@ impl IErc6909 for Erc6909 { self.balances.get(owner).get(id) } - fn allowance( - &self, - owner: Address, - spender: Address, - id: U256, - ) -> U256 { + fn allowance(&self, owner: Address, spender: Address, id: U256) -> U256 { self.allowances.get(owner).get(spender).get(id) } @@ -348,14 +349,14 @@ impl IErc6909 for Erc6909 { self._transfer(sender, receiver, id, amount)?; Ok(true) } - } impl Erc6909 { - /// Sets `amount` as the allowance of `spender` over the `owner`'s `id` tokens. + /// Sets `amount` as the allowance of `spender` over the `owner`'s `id` + /// tokens. /// - /// This internal function is equivalent to `approve`, and can be used to e.g. - /// set automatic allowances for certain subsystems, etc. + /// This internal function is equivalent to `approve`, and can be used to + /// e.g. set automatic allowances for certain subsystems, etc. /// /// # Arguments /// @@ -370,7 +371,7 @@ impl Erc6909 { /// /// * [`Error::InvalidSpender`] - If the `spender` address is /// [`Address::ZERO`]. - /// * [`Error::InvalidApprover`] - If the `owner` address is + /// * [`Error::InvalidApprover`] - If the `owner` address is /// [`Address::ZERO`]. /// /// # Events @@ -385,13 +386,13 @@ impl Erc6909 { emit_event: bool, ) -> Result { if owner.is_zero() { - return Err(Error::InvalidApprover(ERC6909InvalidApprover{ + return Err(Error::InvalidApprover(ERC6909InvalidApprover { approver: Address::ZERO, })); } if spender.is_zero() { - return Err(Error::InvalidSpender(ERC6909InvalidSpender{ + return Err(Error::InvalidSpender(ERC6909InvalidSpender { spender: Address::ZERO, })); } @@ -399,7 +400,7 @@ impl Erc6909 { self.allowances.setter(owner).setter(spender).insert(id, amount); if emit_event { - evm::log(Approval {owner, spender, id, amount}); + evm::log(Approval { owner, spender, id, amount }); } Ok(true) @@ -407,8 +408,8 @@ impl Erc6909 { /// Approve `spender` to operate on all of `owner`'s tokens /// - /// This internal function is equivalent to `setOperator`, and can be used to e.g. - /// set automatic allowances for certain subsystems, etc. + /// This internal function is equivalent to `setOperator`, and can be used + /// to e.g. set automatic allowances for certain subsystems, etc. /// /// # Arguments /// @@ -435,30 +436,33 @@ impl Erc6909 { approved: bool, ) -> Result { if owner.is_zero() { - return Err(Error::InvalidApprover(ERC6909InvalidApprover{ + return Err(Error::InvalidApprover(ERC6909InvalidApprover { approver: Address::ZERO, })); } if spender.is_zero() { - return Err(Error::InvalidSpender(ERC6909InvalidSpender{ + return Err(Error::InvalidSpender(ERC6909InvalidSpender { spender: Address::ZERO, })); } self.operator_approvals.setter(owner).insert(spender, approved); - evm::log(OperatorSet {owner, spender, approved}); + evm::log(OperatorSet { owner, spender, approved }); Ok(true) } - /// Moves `amount` of token `id` from `from` to `to` without checking for approvals. - /// This function verifies that neither the sender nor the receiver are [`Address::ZERO`], - /// which means it cannot mint or burn tokens. + + /// Moves `amount` of token `id` from `from` to `to` without checking for + /// approvals. This function verifies that neither the sender nor the + /// receiver are [`Address::ZERO`], which means it cannot mint or burn + /// tokens. /// /// Relies on the `_update` mechanism. /// - /// NOTE: This function is not virtual, {_update} should be overridden instead. + /// NOTE: This function is not virtual, {_update} should be overridden + /// instead. /// /// # Arguments /// @@ -502,9 +506,10 @@ impl Erc6909 { Ok(()) } - /// Transfers `amount` of token `id` from `from` to `to`, or alternatively - /// mints (or burns) if `from` (or `to`) is the zero address. - /// All customizations to transfers, mints, and burns should be done by overriding this function. + /// Transfers `amount` of token `id` from `from` to `to`, or alternatively + /// mints (or burns) if `from` (or `to`) is the zero address. + /// All customizations to transfers, mints, and burns should be done by + /// overriding this function. /// /// # Arguments /// @@ -534,12 +539,14 @@ impl Erc6909 { if !from.is_zero() { let from_balance = self.balances.get(from).get(id); if from_balance < amount { - return Err(Error::InsufficientBalance(ERC6909InsufficientBalance { - sender: from, - balance: from_balance, - needed: amount, - id, - })); + return Err(Error::InsufficientBalance( + ERC6909InsufficientBalance { + sender: from, + balance: from_balance, + needed: amount, + id, + }, + )); } self.balances.setter(from).setter(id).set(from_balance - amount); } @@ -548,7 +555,7 @@ impl Erc6909 { self.balances.setter(to).setter(id).add_assign_unchecked(amount); } - evm::log(Transfer {caller, sender: from, receiver: to, id, amount}); + evm::log(Transfer { caller, sender: from, receiver: to, id, amount }); Ok(()) } @@ -571,7 +578,7 @@ impl Erc6909 { /// enough allowance to spend `amount` of tokens. /// * [`Error::InvalidSpender`] - If the `spender` address is /// [`Address::ZERO`]. - /// * [`Error::InvalidApprover`] - If the `owner` address is + /// * [`Error::InvalidApprover`] - If the `owner` address is /// [`Address::ZERO`]. /// /// # Events @@ -587,21 +594,32 @@ impl Erc6909 { let current_allowance = self.allowances.get(owner).get(spender).get(id); if current_allowance < U256::MAX { if current_allowance < amount { - return Err(Error::InsufficientAllowance(ERC6909InsufficientAllowance { - spender, - allowance: current_allowance, - needed: amount, - id, - })); + return Err(Error::InsufficientAllowance( + ERC6909InsufficientAllowance { + spender, + allowance: current_allowance, + needed: amount, + id, + }, + )); } - self._approve(owner, spender, id, current_allowance - amount, false)?; + self._approve( + owner, + spender, + id, + current_allowance - amount, + false, + )?; } Ok(()) } - /// Creates `amount` of token `id` and assigns them to `account`, by transferring it from [`Address::ZERO`]. - /// Relies on the `_update` mechanism. + + /// Creates `amount` of token `id` and assigns them to `account`, by + /// transferring it from [`Address::ZERO`]. Relies on the `_update` + /// mechanism. /// - /// NOTE: This function is not virtual, {_update} should be overridden instead. + /// NOTE: This function is not virtual, {_update} should be overridden + /// instead. /// /// # Arguments /// @@ -635,7 +653,8 @@ impl Erc6909 { /// Destroys a `amount` of token `id` from `account`. /// Relies on the `_update` mechanism. /// - /// NOTE: This function is not virtual, {_update} should be overridden instead. + /// NOTE: This function is not virtual, {_update} should be overridden + /// instead. /// /// # Arguments /// @@ -666,7 +685,7 @@ impl Erc6909 { } self._update(from, Address::ZERO, id, amount) - } + } } #[public] @@ -682,7 +701,9 @@ mod tests { use alloy_primitives::{uint, Address, FixedBytes, U256}; use motsu::prelude::*; - use super::{Approval, Transfer, OperatorSet, IErc165, IErc6909, Erc6909, Error}; + use super::{ + Approval, Erc6909, Error, IErc165, IErc6909, OperatorSet, Transfer, + }; #[motsu::test] fn mint(contract: Contract, alice: Address) { @@ -709,7 +730,10 @@ mod tests { } #[motsu::test] - fn mint_errors_invalid_receiver(contract: Contract, alice: Address) { + fn mint_errors_invalid_receiver( + contract: Contract, + alice: Address, + ) { let receiver = Address::ZERO; let id = uint!(1_U256); let ten = uint!(10_U256); @@ -721,10 +745,8 @@ mod tests { ._mint(alice, id, ten) .expect("should mint tokens for Alice"); - let err = contract - .sender(alice) - ._mint(receiver, id, ten) - .motsu_unwrap_err(); + let err = + contract.sender(alice)._mint(receiver, id, ten).motsu_unwrap_err(); assert!(matches!(err, Error::InvalidReceiver(_))); @@ -746,20 +768,17 @@ mod tests { .expect("should mint tokens for Alice"); let balance = contract.sender(alice).balance_of(alice, id); - - contract - .sender(alice) - ._burn(alice, id, one) - .motsu_unwrap(); - assert_eq!( - balance - one, - contract.sender(alice).balance_of(alice, id) - ) + contract.sender(alice)._burn(alice, id, one).motsu_unwrap(); + + assert_eq!(balance - one, contract.sender(alice).balance_of(alice, id)) } #[motsu::test] - fn burn_errors_insufficient_balance(contract: Contract, alice: Address) { + fn burn_errors_insufficient_balance( + contract: Contract, + alice: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); let ten = uint!(10_U256); @@ -770,18 +789,13 @@ mod tests { .expect("should mint tokens for Alice"); let balance = contract.sender(alice).balance_of(alice, id); - - let err = contract - .sender(alice) - ._burn(alice, id, ten) - .motsu_unwrap_err(); + + let err = + contract.sender(alice)._burn(alice, id, ten).motsu_unwrap_err(); assert!(matches!(err, Error::InsufficientBalance(_))); - assert_eq!( - balance, - contract.sender(alice).balance_of(alice, id) - ) + assert_eq!(balance, contract.sender(alice).balance_of(alice, id)) } #[motsu::test] @@ -799,7 +813,6 @@ mod tests { assert!(matches!(err, Error::InvalidSender(_))); } - #[motsu::test] fn transfer(contract: Contract, alice: Address, bob: Address) { let id = uint!(2_U256); @@ -841,7 +854,11 @@ mod tests { } #[motsu::test] - fn transfer_errors_insufficient_balance(contract: Contract, alice: Address, bob: Address) { + fn transfer_errors_insufficient_balance( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); @@ -858,24 +875,23 @@ mod tests { let alice_balance = contract.sender(alice).balance_of(alice, id); let bob_balance = contract.sender(alice).balance_of(bob, id); - let err = - contract.sender(alice).transfer(bob, id, one + one).motsu_unwrap_err(); + let err = contract + .sender(alice) + .transfer(bob, id, one + one) + .motsu_unwrap_err(); assert!(matches!(err, Error::InsufficientBalance(_))); - assert_eq!( - alice_balance, - contract.sender(alice).balance_of(alice, id) - ); - - assert_eq!( - bob_balance, - contract.sender(alice).balance_of(bob, id) - ); + assert_eq!(alice_balance, contract.sender(alice).balance_of(alice, id)); + assert_eq!(bob_balance, contract.sender(alice).balance_of(bob, id)); } #[motsu::test] - fn transfer_from(contract: Contract, alice: Address, bob: Address) { + fn transfer_from( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); let ten = uint!(10_U256); @@ -889,26 +905,17 @@ mod tests { .sender(alice) ._mint(alice, id, ten) .motsu_expect("should mint tokens for Alice"); - - assert_eq!( - ten, - contract.sender(alice).balance_of(alice, id) - ); + + assert_eq!(ten, contract.sender(alice).balance_of(alice, id)); contract .sender(bob) .transfer_from(alice, bob, id, one) .motsu_expect("should transfer from Alice to Bob"); - assert_eq!( - ten - one, - contract.sender(alice).balance_of(alice, id) - ); + assert_eq!(ten - one, contract.sender(alice).balance_of(alice, id)); - assert_eq!( - one, - contract.sender(alice).balance_of(bob, id) - ); + assert_eq!(one, contract.sender(alice).balance_of(bob, id)); contract.assert_emitted(&Transfer { caller: bob, @@ -917,11 +924,14 @@ mod tests { id, amount: one, }); - } #[motsu::test] - fn transfer_from_errors_insufficient_balance(contract: Contract, alice: Address, bob: Address) { + fn transfer_from_errors_insufficient_balance( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); let ten = uint!(10_U256); @@ -935,11 +945,8 @@ mod tests { .sender(alice) ._mint(alice, id, one) .motsu_expect("should mint tokens for Alice"); - - assert_eq!( - one, - contract.sender(alice).balance_of(alice, id) - ); + + assert_eq!(one, contract.sender(alice).balance_of(alice, id)); let err = contract .sender(bob) @@ -948,14 +955,15 @@ mod tests { assert!(matches!(err, Error::InsufficientBalance(_))); - assert_eq!( - one, - contract.sender(alice).balance_of(alice, id) - ); + assert_eq!(one, contract.sender(alice).balance_of(alice, id)); } #[motsu::test] - fn transfer_from_errors_invalid_receiver(contract: Contract, alice: Address, bob: Address) { + fn transfer_from_errors_invalid_receiver( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); @@ -976,14 +984,15 @@ mod tests { assert!(matches!(err, Error::InvalidReceiver(_))); - assert_eq!( - one, - contract.sender(alice).balance_of(alice, id) - ); + assert_eq!(one, contract.sender(alice).balance_of(alice, id)); } #[motsu::test] - fn transfer_from_errors_insufficient_allowance(contract: Contract, alice: Address, bob: Address) { + fn transfer_from_errors_insufficient_allowance( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); @@ -1001,7 +1010,11 @@ mod tests { } #[motsu::test] - fn approves_and_reads_allowance(contract: Contract, alice: Address, bob: Address) { + fn approves_and_reads_allowance( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); @@ -1013,7 +1026,8 @@ mod tests { .approve(bob, id, one) .motsu_expect("should Alice approves Bob"); - let current_allowance = contract.sender(alice).allowance(alice, bob, id); + let current_allowance = + contract.sender(alice).allowance(alice, bob, id); assert_eq!(one, current_allowance); contract.assert_emitted(&Approval { @@ -1025,7 +1039,10 @@ mod tests { } #[motsu::test] - fn approve_errors_invalid_spender(contract: Contract, alice: Address) { + fn approve_errors_invalid_spender( + contract: Contract, + alice: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); @@ -1038,7 +1055,11 @@ mod tests { } #[motsu::test] - fn approve_errors_invalid_approver(contract: Contract, alice: Address, bob: Address) { + fn approve_errors_invalid_approver( + contract: Contract, + alice: Address, + bob: Address, + ) { let id = uint!(2_U256); let one = uint!(1_U256); @@ -1051,7 +1072,11 @@ mod tests { } #[motsu::test] - fn set_operator_and_reads_operator(contract: Contract, alice: Address, bob: Address) { + fn set_operator_and_reads_operator( + contract: Contract, + alice: Address, + bob: Address, + ) { let is_operator = contract.sender(alice).is_operator(alice, bob); assert_eq!(false, is_operator); @@ -1059,7 +1084,7 @@ mod tests { .sender(alice) .set_operator(bob, true) .motsu_expect("should Alice sets Bob as operator"); - + let is_operator = contract.sender(alice).is_operator(alice, bob); assert_eq!(true, is_operator); @@ -1071,22 +1096,30 @@ mod tests { } #[motsu::test] - fn set_operator_errors_invalid_spender(contract: Contract, alice: Address, bob: Address) { + fn set_operator_errors_invalid_spender( + contract: Contract, + alice: Address, + bob: Address, + ) { let err = contract .sender(alice) .set_operator(Address::ZERO, true) .motsu_unwrap_err(); - + assert!(matches!(err, Error::InvalidSpender(_))); } #[motsu::test] - fn set_operator_errors_invalid_approver(contract: Contract, alice: Address, bob: Address) { + fn set_operator_errors_invalid_approver( + contract: Contract, + alice: Address, + bob: Address, + ) { let err = contract .sender(alice) ._set_operator(Address::ZERO, bob, true) .motsu_unwrap_err(); - + assert!(matches!(err, Error::InvalidApprover(_))); } @@ -1111,4 +1144,4 @@ mod tests { .sender(alice) .supports_interface(fake_interface_id.into())); } -} \ No newline at end of file +} diff --git a/contracts/src/token/mod.rs b/contracts/src/token/mod.rs index 4a0340222..d02cf8766 100644 --- a/contracts/src/token/mod.rs +++ b/contracts/src/token/mod.rs @@ -2,5 +2,5 @@ pub mod common; pub mod erc1155; pub mod erc20; +pub mod erc6909; pub mod erc721; -pub mod erc6909; \ No newline at end of file diff --git a/examples/erc6909-supply/src/lib.rs b/examples/erc6909-supply/src/lib.rs index 3c0aa8059..4bc871a97 100644 --- a/examples/erc6909-supply/src/lib.rs +++ b/examples/erc6909-supply/src/lib.rs @@ -5,7 +5,6 @@ extern crate alloc; use alloc::vec::Vec; use alloy_primitives::{Address, FixedBytes, U256}; - use openzeppelin_stylus::{ token::erc6909::{ self, @@ -14,7 +13,6 @@ use openzeppelin_stylus::{ }, utils::introspection::erc165::IErc165, }; - use stylus_sdk::prelude::*; #[entrypoint] @@ -53,12 +51,7 @@ impl IErc6909 for Erc6909TokenSupplyExample { self.erc6909_token_supply.balance_of(owner, id) } - fn allowance( - &self, - owner: Address, - spender: Address, - id: U256, - ) -> U256 { + fn allowance(&self, owner: Address, spender: Address, id: U256) -> U256 { self.erc6909_token_supply.allowance(owner, spender, id) } @@ -115,4 +108,4 @@ impl IErc165 for Erc6909TokenSupplyExample { fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { self.erc6909_token_supply.supports_interface(interface_id) } -} \ No newline at end of file +} diff --git a/examples/erc6909-supply/src/main.rs b/examples/erc6909-supply/src/main.rs index 4ee0329e2..afa262362 100644 --- a/examples/erc6909-supply/src/main.rs +++ b/examples/erc6909-supply/src/main.rs @@ -7,4 +7,4 @@ pub extern "C" fn main() {} #[cfg(feature = "export-abi")] fn main() { erc6909_supply_example::print_from_args(); -} \ No newline at end of file +} diff --git a/examples/erc6909-supply/tests/erc6909-supply.rs b/examples/erc6909-supply/tests/erc6909-supply.rs index e69de29bb..8b1378917 100644 --- a/examples/erc6909-supply/tests/erc6909-supply.rs +++ b/examples/erc6909-supply/tests/erc6909-supply.rs @@ -0,0 +1 @@ + diff --git a/examples/erc6909/src/lib.rs b/examples/erc6909/src/lib.rs index ac41811ed..c6e7bf681 100644 --- a/examples/erc6909/src/lib.rs +++ b/examples/erc6909/src/lib.rs @@ -8,7 +8,6 @@ use openzeppelin_stylus::{ token::erc6909::{self, Erc6909, IErc6909}, utils::introspection::erc165::IErc165, }; - use stylus_sdk::{ alloy_primitives::{Address, FixedBytes, U256}, prelude::*, @@ -17,7 +16,7 @@ use stylus_sdk::{ #[entrypoint] #[storage] struct Erc6909Example { - erc6909: Erc6909 + erc6909: Erc6909, } #[public] @@ -39,7 +38,6 @@ impl Erc6909Example { amount: U256, ) -> Result<(), erc6909::Error> { self.erc6909._burn(from, id, amount) - } } @@ -51,12 +49,7 @@ impl IErc6909 for Erc6909Example { self.erc6909.balance_of(owner, id) } - fn allowance( - &self, - owner: Address, - spender: Address, - id: U256, - ) -> U256 { + fn allowance(&self, owner: Address, spender: Address, id: U256) -> U256 { self.erc6909.allowance(owner, spender, id) } @@ -65,36 +58,36 @@ impl IErc6909 for Erc6909Example { } fn approve( - &mut self, - spender: Address, - id: U256, + &mut self, + spender: Address, + id: U256, amount: U256, ) -> Result { self.erc6909.approve(spender, id, amount) } fn set_operator( - &mut self, - spender: Address, + &mut self, + spender: Address, approved: bool, ) -> Result { self.erc6909.set_operator(spender, approved) } fn transfer( - &mut self, - receiver: Address, - id: U256, + &mut self, + receiver: Address, + id: U256, amount: U256, ) -> Result { self.erc6909.transfer(receiver, id, amount) } fn transfer_from( - &mut self, - sender: Address, - receiver: Address, - id: U256, + &mut self, + sender: Address, + receiver: Address, + id: U256, amount: U256, ) -> Result { self.erc6909.transfer_from(sender, receiver, id, amount) @@ -106,4 +99,4 @@ impl IErc165 for Erc6909Example { fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { self.erc6909.supports_interface(interface_id) } -} \ No newline at end of file +} diff --git a/examples/erc6909/src/main.rs b/examples/erc6909/src/main.rs index 05eafbb30..9b99e02ad 100644 --- a/examples/erc6909/src/main.rs +++ b/examples/erc6909/src/main.rs @@ -7,4 +7,4 @@ pub extern "C" fn main() {} #[cfg(feature = "export-abi")] fn main() { erc6909_example::print_from_args(); -} \ No newline at end of file +} diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index e69de29bb..8b1378917 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -0,0 +1 @@ + From 513781c78393281177e4103dc3936cdbfdfea54d Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 12:09:37 +0200 Subject: [PATCH 08/33] add abi for erc6909 and erc6909-supply example and a mint test --- examples/erc6909-supply/tests/abi/mod.rs | 38 +++++++++++++++++++ examples/erc6909/tests/abi/mod.rs | 37 +++++++++++++++++++ examples/erc6909/tests/erc6909.rs | 47 ++++++++++++++++++++++++ 3 files changed, 122 insertions(+) diff --git a/examples/erc6909-supply/tests/abi/mod.rs b/examples/erc6909-supply/tests/abi/mod.rs index e69de29bb..3680d256c 100644 --- a/examples/erc6909-supply/tests/abi/mod.rs +++ b/examples/erc6909-supply/tests/abi/mod.rs @@ -0,0 +1,38 @@ +#![allow(dead_code)] +use alloy::sol; + +sol!( + #[sol(rpc)] + contract Erc6909TokenSupply { + function balanceOf(address owner, uint256 id) external view returns (uint256 balance); + function allowance(address owner, address spender, uint256 id) external view returns (uint256 allowance); + function isOperator(address owner, address spender) external view returns (bool approved); + function approve(address spender, uint256 id, uint256 amount) external returns (bool); + function setOperator(address spender, bool approved) external returns (bool); + function transfer(address receiver, uint256 id, uint256 amount) external returns (bool); + function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool); + function totalSupply(uint256 id) external view returns (uint256 totalSupply); + + function mint(address to, uint256 id, uint256 amount) external; + function burn(address from, uint256 id, uint256 amount) external; + + function supportsInterface(bytes4 interface_id) external view returns (bool supportsInterface); + + error ERC6909InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 id); + error ERC6909InsufficientAllowance(address spender, uint256 allowance, uint256 needed, uint256 id); + error ERC6909InvalidApprover(address approver); + error ERC6909InvalidReceiver(address receiver); + error ERC6909InvalidSender(address sender); + error ERC6909InvalidSpender(address spender); + + error Error(string message); + error Panic(uint256 code); + + #[derive(Debug, PartialEq)] + event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount); + #[derive(Debug, PartialEq)] + event OperatorSet(address indexed owner, address indexed spender, bool approved); + #[derive(Debug, PartialEq)] + event Transfer(address caller, address indexed sender, address indexed receiver, uint256 indexed id, uint256 amount); + } +); \ No newline at end of file diff --git a/examples/erc6909/tests/abi/mod.rs b/examples/erc6909/tests/abi/mod.rs index e69de29bb..21c6536bd 100644 --- a/examples/erc6909/tests/abi/mod.rs +++ b/examples/erc6909/tests/abi/mod.rs @@ -0,0 +1,37 @@ +#![allow(dead_code)] +use alloy::sol; + +sol!( + #[sol(rpc)] + contract Erc6909 { + function balanceOf(address owner, uint256 id) external view returns (uint256 balance); + function allowance(address owner, address spender, uint256 id) external view returns (uint256 allowance); + function isOperator(address owner, address spender) external view returns (bool approved); + function approve(address spender, uint256 id, uint256 amount) external returns (bool); + function setOperator(address spender, bool approved) external returns (bool); + function transfer(address receiver, uint256 id, uint256 amount) external returns (bool); + function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool); + + function mint(address to, uint256 id, uint256 amount) external; + function burn(address from, uint256 id, uint256 amount) external; + + function supportsInterface(bytes4 interface_id) external view returns (bool supportsInterface); + + error ERC6909InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 id); + error ERC6909InsufficientAllowance(address spender, uint256 allowance, uint256 needed, uint256 id); + error ERC6909InvalidApprover(address approver); + error ERC6909InvalidReceiver(address receiver); + error ERC6909InvalidSender(address sender); + error ERC6909InvalidSpender(address spender); + + error Error(string message); + error Panic(uint256 code); + + #[derive(Debug, PartialEq)] + event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount); + #[derive(Debug, PartialEq)] + event OperatorSet(address indexed owner, address indexed spender, bool approved); + #[derive(Debug, PartialEq)] + event Transfer(address caller, address indexed sender, address indexed receiver, uint256 indexed id, uint256 amount); + } +); diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index 8b1378917..e8c812959 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -1 +1,48 @@ +#![cfg(feature = "e2e")] +use abi::Erc6909; +use alloy::primitives::{uint, Address, U256}; +use e2e::{ + constructor, receipt, send, watch, Account, Constructor, + ContractInitializationError, EventExt, Panic, PanicCode, Revert, +}; +use eyre::Result; + +mod abi; + +// ============================================================================ +// Integration Tests: ERC-6909 Token +// ============================================================================ + +#[e2e::test] +async fn mints(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + + let id = uint!(2); + let one = uint!(1); + + let Erc6909::balanceOfReturn { initial_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + assert_eq!(U256::ZERO, initial_balance); + + let receipt = receipt!(contract.mint(alice_addr, id, one))?; + + assert!(receipt.emits(Erc6909::Transfer { + caller: alice_addr, + sender: Address::ZERO, + receiver: alice_addr, + id, + amount: one, + })); + + let Erc6909::balanceOfReturn { balance } = + contract.balanceOf(alice_addr, id).call().await?; + + assert_eq!(one, balance); + + Ok(()) +} From 6463d84b840c4dc3d0ffc9ffa4301ddb2bdf0445 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 12:30:09 +0200 Subject: [PATCH 09/33] fix mint test and add mints_invalid_receiver --- examples/erc6909/tests/erc6909.rs | 36 +++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index e8c812959..7afca7d3f 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -21,10 +21,10 @@ async fn mints(alice: Account) -> Result<()> { let contract = Erc6909::new(contract_addr, &alice.wallet); let alice_addr = alice.address(); - let id = uint!(2); - let one = uint!(1); + let id = uint!(2_U256); + let one = uint!(1_U256); - let Erc6909::balanceOfReturn { initial_balance } = + let Erc6909::balanceOfReturn { balance: initial_balance } = contract.balanceOf(alice_addr, id).call().await?; assert_eq!(U256::ZERO, initial_balance); @@ -39,10 +39,38 @@ async fn mints(alice: Account) -> Result<()> { amount: one, })); - let Erc6909::balanceOfReturn { balance } = + let Erc6909::balanceOfReturn { balance: balance } = contract.balanceOf(alice_addr, id).call().await?; assert_eq!(one, balance); Ok(()) } + +#[e2e::test] +async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let invalid_receiver = Address::ZERO; + + let id = uint!(2_U256); + let one = uint!(1_U256); + + let Erc6909::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(invalid_receiver, id).call().await?; + + let error = send!(contract.mint(invalid_receiver, id, one)) + .expect_err("should not mint tokens for Address::ZERO"); + + assert!(err.reverted_with(Erc6909::ERC6909InvalidReceiver { + receiver: invalid_receiver + })); + + let Erc6909::balanceOfReturn { balance: balance } = + contract.balanceOf(invalid_receiver, id).call().await?; + + assert_eq!(initial_balance, balance); + + Ok(()) +} From de57e8ddf52089e62eab28676fdb2d14b0f9be01 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 12:31:47 +0200 Subject: [PATCH 10/33] typo name --- examples/erc6909/tests/erc6909.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index 7afca7d3f..993bbbe31 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -60,7 +60,7 @@ async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> { let Erc6909::balanceOfReturn { balance: initial_balance } = contract.balanceOf(invalid_receiver, id).call().await?; - let error = send!(contract.mint(invalid_receiver, id, one)) + let err = send!(contract.mint(invalid_receiver, id, one)) .expect_err("should not mint tokens for Address::ZERO"); assert!(err.reverted_with(Erc6909::ERC6909InvalidReceiver { From e6761f5f39a003c94b1823fcaeffdfcc1ced1bc7 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 14:36:59 +0200 Subject: [PATCH 11/33] complete erc6909 integration tests --- examples/erc6909/tests/erc6909.rs | 594 ++++++++++++++++++++++++++++++ 1 file changed, 594 insertions(+) diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index 993bbbe31..88b303018 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -74,3 +74,597 @@ async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> { Ok(()) } + +#[e2e::test] +async fn burns(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + + let id = uint!(2_U256); + let balance = uint!(10_U256); + let one = uint!(1_U256); + + watch!(contract.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let receipt = receipt!(contract.burn(alice_addr, id, one))?; + + let Erc6909::balanceOfReturn { balance: balance } = + contract.balanceOf(alice_addr, id).call().await?; + + assert!(receipt.emits(Erc6909::Transfer { + caller: alice_addr, + sender: alice_addr, + receiver: Address::ZERO, + id, + amount: one, + })); + + assert_eq!(initial_balance - one, balance); + + Ok(()) +} + +#[e2e::test] +async fn burn_rejects_insufficient_balance(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + + let id = uint!(2_U256); + let balance = uint!(10_U256); + let balance_plus_one = uint!(11_U256); + + watch!(contract.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let err = send!(contract.burn(alice_addr, id, balance_plus_one)) + .expect_err("should not burn when balance is insufficient"); + + assert!(err.reverted_with(Erc6909::ERC6909InsufficientBalance { + sender: alice_addr, + balance, + needed: balance_plus_one + })); + + let Erc6909::balanceOfReturn { balance: balance } = + contract.balanceOf(alice_addr, id).call().await?; + + assert_eq!(initial_balance, balance); + + Ok(()) +} + +#[e2e::test] +async fn burn_rejects_invalid_sender(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let invalid_sender = Address::ZERO; + + let id = uint!(2_U256); + let one = uint!(1_U256); + + let err = send!(contract.burn(invalid_sender, id, one)) + .expect_err("should not burn for invalid sender"); + + assert!(err.reverted_with(Erc6909::ERC6909InvalidSender { + sender: invalid_sender + })); + + Ok(()) +} + +#[e2e::test] +async fn transfers(alice: Account, bob: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let one = uint!(1_U256); + let balance = uint!(10_U256); + + watch!(contract.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_alice_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_bob_balance } = + contract.balanceOf(bob_addr, id).call().await?; + + assert_eq!(U256::ZERO, initial_bob_balance); + + let receipt = receipt!(contract.transfer(bob_addr, id, one))?; + + let Erc6909::balanceOfReturn { balance: alice_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: bob_balance } = + contract.balanceOf(bob_addr, id).call().await?; + + assert!(receipt.emits(Erc6909::Transfer { + caller: alice_addr, + sender: alice_addr, + receiver: bob_addr, + id, + amount: one, + })); + + assert_eq!(initial_alice_balance - one, alice_balance); + assert_eq!(initial_bob_balance + one, bob_balance); + + Ok(()) +} + +#[e2e::test] +async fn transfer_rejects_insufficient_balance( + alice: Account, + bob: Account, +) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let balance = uint!(10_U256); + let balance_plus_one = uint!(11_U256); + + watch!(contract.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_alice_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_bob_balance } = + contract.balanceOf(bob_addr, id).call().await?; + + let err = send!(contract.transfer(bob_addr, id, balance_plus_one)) + .expect_err("should not transfer when balance is insufficient"); + + assert!(err.reverted_with(Erc6909::ERC6909InsufficientBalance { + sender: alice_addr, + balance, + needed: balance_plus_one + })); + + let Erc6909::balanceOfReturn { balance: alice_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: bob_balance } = + contract.balanceOf(bob_addr, id).call().await?; + + assert_eq!(initial_alice_balance, alice_balance); + assert_eq!(initial_bob_balance, bob_balance); + + Ok(()) +} + +#[e2e::test] +async fn transfers_from(alice: Account, bob: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract_alice = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let one = uint!(1_U256); + let balance = uint!(10_U256); + + watch!(contract_alice.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + watch!(contract_alice.approve(bob_addr, id, one))?; + + let Erc6909::allowanceReturn { allowance: initial_allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + let receipt = + receipt!(contract_bob.transferFrom(alice_addr, bob_addr, id, one))?; + + let Erc6909::balanceOfReturn { balance: alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + let Erc6909::allowanceReturn { allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + assert!(receipt.emits(Erc6909::Transfer { + caller: bob_addr, + sender: alice_addr, + receiver: bob_addr, + id, + amount: one, + })); + + assert_eq!(initial_alice_balance - one, alice_balance); + assert_eq!(initial_bob_balance + one, bob_balance); + assert_eq!(initial_allowance - one, allowance); + + Ok(()) +} + +#[e2e::test] +async fn transfer_from_reverts_insufficient_balance( + alice: Account, + bob: Account, +) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract_alice = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let balance = uint!(10_U256); + let balance_plus_one = uint!(11_U256); + + watch!(contract_alice.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + watch!(contract_alice.approve(bob_addr, id, balance))?; + + let Erc6909::allowanceReturn { allowance: initial_allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + let err = send!(contract_bob.transferFrom( + alice_addr, + bob_addr, + id, + balance_plus_one + )) + .expect_err("should not transfer when balance is insufficient"); + + assert!(err.reverted_with(Erc6909::ERC6909InsufficientBalance { + sender: alice_addr, + balance, + needed: balance_plus_one + })); + + let Erc6909::balanceOfReturn { balance: alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + let Erc6909::allowanceReturn { allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + assert_eq!(initial_alice_balance, alice_balance); + assert_eq!(initial_bob_balance, bob_balance); + assert_eq!(initial_allowance, allowance); + + Ok(()) +} + +#[e2e::test] +async fn transfer_from_rejects_insufficient_allowance( + alice: Account, + bob: Account, +) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract_alice = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let one = uint!(1_U256); + let balance = uint!(10_U256); + + watch!(contract_alice.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + let Erc6909::allowanceReturn { allowance: initial_allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + assert_eq!(initial_allowance, U256::ZERO); + + let err = send!(contract_bob.transferFrom(alice_addr, bob_addr, id, one)) + .expect_err("should not transfer when insufficient allowance"); + + assert!(err.reverted_with(Erc6909::ERC6909InsufficientAllowance { + spender: bob_addr, + allowance: U256::ZERO, + needed: one + })); + + let Erc6909::balanceOfReturn { balance: alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + let Erc6909::allowanceReturn { allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + assert_eq!(initial_alice_balance, alice_balance); + assert_eq!(initial_bob_balance, bob_balance); + assert_eq!(initial_allowance, allowance); + + Ok(()) +} + +#[e2e::test] +async fn transfer_from_rejects_invalid_receiver( + alice: Account, + bob: Account, +) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract_alice = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + let invalid_receiver = Address::ZERO; + + let id = uint!(2_U256); + let one = uint!(1_U256); + let balance = uint!(10_U256); + + watch!(contract_alice.mint(alice_addr, id, balance))?; + + let Erc6909::balanceOfReturn { balance: initial_alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + watch!(contract_alice.approve(bob_addr, id, one))?; + + let Erc6909::allowanceReturn { allowance: initial_allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + let err = + send!(contract_bob.transferFrom(alice_addr, invalid_receiver, id, one)) + .expect_err("should not transfer to Address::ZERO"); + + assert!(err.reverted_with(Erc6909::ERC6909InvalidReceiver { + receiver: invalid_receiver + })); + + let Erc6909::balanceOfReturn { balance: alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + let Erc6909::allowanceReturn { allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + assert_eq!(initial_alice_balance, alice_balance); + assert_eq!(initial_bob_balance, bob_balance); + assert_eq!(initial_allowance, allowance); + + Ok(()) +} + +#[e2e::test] +async fn approves(alice: Account, bob: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract_alice = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let one = uint!(1_U256); + let balance = uint!(10_U256); + + let Erc6909::allowanceReturn { allowance: initial_alice_to_bob_allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + let Erc6909::allowanceReturn { allowance: initial_bob_to_alice_allowance } = + contract_alice.allowance(bob_addr, alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: initial_bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + assert_eq!(U256::ZERO, initial_alice_to_bob_allowance); + assert_eq!(U256::ZERO, initial_bob_to_alice_allowance); + + let receipt = receipt!(contract_alice.approve(bob_addr, id, one))?; + + assert!(receipt.emits(Erc6909::Approval { + caller: alice_addr, + owner: alice_addr, + spender: bob_addr, + id, + amount: one, + })); + + let Erc6909::allowanceReturn { allowance: alice_to_bob_allowance } = + contract_alice.allowance(alice_addr, bob_addr, id).call().await?; + + let Erc6909::allowanceReturn { allowance: bob_to_alice_allowance } = + contract_alice.allowance(bob_addr, alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: alice_balance } = + contract_alice.balanceOf(alice_addr, id).call().await?; + + let Erc6909::balanceOfReturn { balance: bob_balance } = + contract_alice.balanceOf(bob_addr, id).call().await?; + + assert_eq!(initial_alice_balance, alice_balance); + assert_eq!(initial_bob_balance, bob_balance); + assert_eq!(initial_alice_to_bob_allowance + one, alice_to_bob_allowance); + assert_eq!(initial_bob_to_alice_allowance, bob_to_alice_allowance); + + Ok(()) +} + +#[e2e::test] +async fn approve_rejects_invalid_spender(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let invalid_spender = Address::ZERO; + + let id = uint!(2_U256); + let one = uint!(1_U256); + + let Erc6909::allowanceReturn { allowance: initial_alice_spender_allowance } = + contract.allowance(alice_addr, invalid_spender, id).call().await?; + + let Erc6909::allowanceReturn { allowance: initial_spender_alice_allowance } = + contract.allowance(invalid_spender, alice_addr, id).call().await?; + + assert_eq!(U256::ZERO, initial_alice_spender_allowance); + assert_eq!(U256::ZERO, initial_spender_alice_allowance); + + let err = send!(contract.approve(invalid_spender, id, one)) + .expect_err("should not approve for Address::ZERO"); + + assert!(err.reverted_with(Erc6909::ERC6909InvalidSpender { + spender: invalid_spender + })); + + let Erc6909::allowanceReturn { allowance: alice_spender_allowance } = + contract.allowance(alice_addr, invalid_spender, id).call().await?; + + let Erc6909::allowanceReturn { allowance: spender_alice_allowance } = + contract.allowance(invalid_spender, alice_addr, id).call().await?; + + assert_eq!(initial_alice_spender_allowance, alice_spender_allowance); + assert_eq!(initial_spender_alice_allowance, spender_alice_allowance); + + Ok(()) +} + +#[e2e::test] +async fn sets_operator(alice: Account, bob: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let one = uint!(1_U256); + let balance = uint!(10_U256); + + let Erc6909::isOperatorReturn { approved: initial_approved } = + contract.isOperator(alice_addr, bob_addr, id).call().await?; + + assert_eq!(false, initial_approved); + + let receipt = receipt!(contract.setOperator(bob_addr, id, true))?; + + let Erc6909::isOperatorReturn { approved } = + contract.isOperator(alice_addr, bob_addr, id).call().await?; + + assert_eq!(true, approved); + + assert!(receipt.emits(Erc6909::OperatorSet { + caller: alice_addr, + owner: alice_addr, + operator: bob_addr, + id, + approved: true, + })); + + Ok(()) +} + +#[e2e::test] +async fn set_operator_rejects_invalid_spender(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let invalid_spender = Address::ZERO; + + let id = uint!(2_U256); + let one = uint!(1_U256); + + let Erc6909::isOperatorReturn { approved: initial_approved } = + contract.isOperator(alice_addr, invalid_spender, id).call().await?; + + assert_eq!(false, initial_approved); + + let err = send!(contract.setOperator(invalid_spender, id, true)) + .expect_err("should not set operator for Address::ZERO"); + + assert!(err.reverted_with(Erc6909::ERC6909InvalidSpender { + spender: invalid_spender + })); + + let Erc6909::isOperatorReturn { approved } = + contract.isOperator(alice_addr, invalid_spender, id).call().await?; + + assert_eq!(false, approved); + + Ok(()) +} + +#[e2e::test] +async fn supports_interface(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909::new(contract_addr, &alice.wallet); + + let invalid_interface_id: u32 = 0xffffffff; + let supports_interface = contract + .supportsInterface(invalid_interface_id.into()) + .call() + .await? + .supportsInterface; + + assert!(!supports_interface); + + let erc6909_interface_id: u32 = 0x0f632fb3; + let supports_interface = contract + .supportsInterface(erc6909_interface_id.into()) + .call() + .await? + .supportsInterface; + + assert!(supports_interface); + + let erc165_interface_id: u32 = 0x01ffc9a7; + let supports_interface_erc165 = contract + .supportsInterface(erc165_interface_id.into()) + .call() + .await? + .supportsInterface; + + assert!(supports_interface_erc165); + + Ok(()) +} From 058c82d53c0353588a7b2cf1dfa9345269b96c46 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 16:22:43 +0200 Subject: [PATCH 12/33] add erc6909-supply integrations tests --- .../erc6909-supply/tests/erc6909-supply.rs | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) diff --git a/examples/erc6909-supply/tests/erc6909-supply.rs b/examples/erc6909-supply/tests/erc6909-supply.rs index 8b1378917..16e8b8d31 100644 --- a/examples/erc6909-supply/tests/erc6909-supply.rs +++ b/examples/erc6909-supply/tests/erc6909-supply.rs @@ -1 +1,297 @@ +#![cfg(feature = "e2e")] +use abi::Erc6909TokenSupply; +use alloy::primitives::{uint, Address, U256}; +use e2e::{receipt, send, watch, Account, EventExt, Panic, PanicCode}; +use eyre::Result; + +mod abi; + +#[e2e::test] +async fn mints(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + let contract = Erc6909TokenSupply::new(contract_addr, &alice.wallet); + + let alice_addr = alice.address(); + let id = uint!(2_U256); + let one = uint!(1_U256); + + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = + contract.totalSupply().call().await?; + + assert_eq!(U256::ZERO, initial_balance); + + let receipt = receipt!(contract.mint(alice_addr, id, one))?; + + assert!(receipt.emits(Erc6909TokenSupply::Transfer { + caller: alice_addr, + sender: Address::ZERO, + receiver: alice_addr, + id, + amount: one, + })); + + let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = + contract.totalSupply().call().await?; + + assert_eq!(initial_balance + one, balance); + assert_eq!(initial_supply + one, total_supply); + + Ok(()) +} + +#[e2e::test] +async fn mints_twice(alice: Account, bob: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + let contract = Erc6909TokenSupply::new(contract_addr, &alice.wallet); + + let alice_addr = alice.address(); + let bob_addr = bob.address(); + + let id = uint!(2_U256); + let one = uint!(1_U256); + let ten = uint!(10_U256); + + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance_alice } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance_bob } = + contract.balanceOf(bob_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = + contract.totalSupply().call().await?; + + assert_eq!(U256::ZERO, initial_balance); + + let receipt = receipt!(contract.mint(alice_addr, id, one))?; + let receipt = receipt!(contract.mint(bob_addr, id, ten))?; + + assert!(receipt.emits(Erc6909TokenSupply::Transfer { + caller: alice_addr, + sender: Address::ZERO, + receiver: alice_addr, + id, + amount: one, + })); + + assert!(receipt.emits(Erc6909TokenSupply::Transfer { + caller: alice_addr, + sender: Address::ZERO, + receiver: bob_addr, + id, + amount: ten, + })); + + let Erc6909TokenSupply::balanceOfReturn { balance: balance_alice } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::balanceOfReturn { balance: balance_bob } = + contract.balanceOf(bob_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = + contract.totalSupply().call().await?; + + assert_eq!(initial_balance + one, initial_balance_alice); + assert_eq!(initial_balance + ten, initial_balance_bob); + assert_eq!(initial_supply + one + ten, total_supply); + + Ok(()) +} + +#[e2e::test] +async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909TokenSupply::new(contract_addr, &alice.wallet); + let invalid_receiver = Address::ZERO; + + let id = uint!(2_U256); + let one = uint!(1_U256); + + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(invalid_receiver, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = + contract.totalSupply().call().await?; + + let err = send!(contract.mint(invalid_receiver, id, one)) + .expect_err("should not mint tokens for Address::ZERO"); + + assert!(err.reverted_with( + Erc6909TokenSupply::ERC6909TokenSupplyInvalidReceiver { + receiver: invalid_receiver + } + )); + + let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + contract.balanceOf(invalid_receiver, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = + contract.totalSupply().call().await?; + + assert_eq!(initial_balance, balance); + assert_eq!(initial_supply, total_supply); + + Ok(()) +} + +#[e2e::test] +async fn mints_rejects_overflow(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + let contract = Erc6909TokenSupply::new(contract_addr, &alice.wallet); + + let max_cap = U256::MAX; + let alice_addr = alice.address(); + + let id = uint!(2_U256); + let one = uint!(1_U256); + + watch!(contract.mint(alice_addr, id, max_cap))?; + + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = + contract.totalSupply().call().await?; + + assert_eq!(U256::ZERO, initial_balance); + assert_eq!(U256::ZERO, initial_supply); + + let err = send!(contract.mint(alice_addr, id, one)) + .expect_err("should not exceed U256::MAX"); + + assert!(err.panicked_with(Panic::new(PanicCode::ArithmeticOverflow))); + + let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = + contract.totalSupply().call().await?; + + assert_eq!(initial_balance, balance); + assert_eq!(initial_supply, total_supply); + + Ok(()) +} + +#[e2e::test] +async fn burns(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909TokenSupply::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + + let id = uint!(2_U256); + let balance = uint!(10_U256); + let one = uint!(1_U256); + + watch!(contract.mint(alice_addr, id, balance))?; + + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = + contract.totalSupply().call().await?; + + let receipt = receipt!(contract.burn(alice_addr, id, one))?; + + let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = + contract.totalSupply().call().await?; + + assert!(receipt.emits(Erc6909::Transfer { + caller: alice_addr, + sender: alice_addr, + receiver: Address::ZERO, + id, + amount: one, + })); + + assert_eq!(initial_balance - one, balance); + assert_eq!(initial_supply - one, total_supply); + + Ok(()) +} + +#[e2e::test] +async fn burn_rejects_invalid_sender(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909TokenSupply::new(contract_addr, &alice.wallet); + let alice_addr = alice.address(); + let invalid_sender = Address::ZERO; + + let id = uint!(2_U256); + let balance = uint!(10_U256); + let one = uint!(1_U256); + + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = + contract.totalSupply().call().await?; + + let err = send!(contract.burn(invalid_sender, id, balance_plus_one)) + .expect_err("should not burn when balance is insufficient"); + + assert!(err.reverted_with( + Erc6909TokenSupply::ERC6909TokenSupplyInvalidSender { + sender: invalid_sender + } + )); + + let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + contract.balanceOf(alice_addr, id).call().await?; + + let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = + contract.totalSupply().call().await?; + + assert_eq!(initial_balance, balance); + assert_eq!(initial_supply, total_supply); + + Ok(()) +} + +#[e2e::test] +async fn supports_interface(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.contract_address; + + let contract = Erc6909TokenSupply::new(contract_addr, &alice.wallet); + + let invalid_interface_id: u32 = 0xffffffff; + let supports_interface = contract + .supportsInterface(invalid_interface_id.into()) + .call() + .await? + .supportsInterface; + + assert!(!supports_interface); + + let erc6909_interface_id: u32 = 0xbd85b039; + let supports_interface = contract + .supportsInterface(erc6909_interface_id.into()) + .call() + .await? + .supportsInterface; + + assert!(supports_interface); + + let erc165_interface_id: u32 = 0x01ffc9a7; + let supports_interface_erc165 = contract + .supportsInterface(erc165_interface_id.into()) + .call() + .await? + .supportsInterface; + + assert!(supports_interface_erc165); + + Ok(()) +} From d8598095315ce10e990c14649fa2229f3d0d3b60 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 16:29:12 +0200 Subject: [PATCH 13/33] add comment & fmt all --- examples/erc6909-supply/tests/abi/mod.rs | 2 +- examples/erc6909-supply/tests/erc6909-supply.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/erc6909-supply/tests/abi/mod.rs b/examples/erc6909-supply/tests/abi/mod.rs index 3680d256c..da571c71a 100644 --- a/examples/erc6909-supply/tests/abi/mod.rs +++ b/examples/erc6909-supply/tests/abi/mod.rs @@ -35,4 +35,4 @@ sol!( #[derive(Debug, PartialEq)] event Transfer(address caller, address indexed sender, address indexed receiver, uint256 indexed id, uint256 amount); } -); \ No newline at end of file +); diff --git a/examples/erc6909-supply/tests/erc6909-supply.rs b/examples/erc6909-supply/tests/erc6909-supply.rs index 16e8b8d31..31b9e9007 100644 --- a/examples/erc6909-supply/tests/erc6909-supply.rs +++ b/examples/erc6909-supply/tests/erc6909-supply.rs @@ -7,6 +7,10 @@ use eyre::Result; mod abi; +// ============================================================================ +// Integration Tests: ERC-6909 Token Supply Extension +// ============================================================================ + #[e2e::test] async fn mints(alice: Account) -> Result<()> { let contract_addr = alice.as_deployer().deploy().await?.contract_address; From 6781c92cbbb71f4d365de2748ae23208de999d2f Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 16:35:34 +0200 Subject: [PATCH 14/33] fix errors --- examples/erc6909/tests/erc6909.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index 88b303018..6f97a8611 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -131,7 +131,8 @@ async fn burn_rejects_insufficient_balance(alice: Account) -> Result<()> { assert!(err.reverted_with(Erc6909::ERC6909InsufficientBalance { sender: alice_addr, balance, - needed: balance_plus_one + needed: balance_plus_one, + id })); let Erc6909::balanceOfReturn { balance: balance } = @@ -147,7 +148,6 @@ async fn burn_rejects_invalid_sender(alice: Account) -> Result<()> { let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc6909::new(contract_addr, &alice.wallet); - let alice_addr = alice.address(); let invalid_sender = Address::ZERO; let id = uint!(2_U256); @@ -236,7 +236,8 @@ async fn transfer_rejects_insufficient_balance( assert!(err.reverted_with(Erc6909::ERC6909InsufficientBalance { sender: alice_addr, balance, - needed: balance_plus_one + needed: balance_plus_one, + id })); let Erc6909::balanceOfReturn { balance: alice_balance } = @@ -337,7 +338,8 @@ async fn transfer_from_reverts_insufficient_balance( alice_addr, bob_addr, id, - balance_plus_one + balance_plus_one, + id )) .expect_err("should not transfer when balance is insufficient"); @@ -398,7 +400,8 @@ async fn transfer_from_rejects_insufficient_allowance( assert!(err.reverted_with(Erc6909::ERC6909InsufficientAllowance { spender: bob_addr, allowance: U256::ZERO, - needed: one + needed: one, + id })); let Erc6909::balanceOfReturn { balance: alice_balance } = @@ -501,7 +504,6 @@ async fn approves(alice: Account, bob: Account) -> Result<()> { let receipt = receipt!(contract_alice.approve(bob_addr, id, one))?; assert!(receipt.emits(Erc6909::Approval { - caller: alice_addr, owner: alice_addr, spender: bob_addr, id, @@ -580,22 +582,20 @@ async fn sets_operator(alice: Account, bob: Account) -> Result<()> { let balance = uint!(10_U256); let Erc6909::isOperatorReturn { approved: initial_approved } = - contract.isOperator(alice_addr, bob_addr, id).call().await?; + contract.isOperator(alice_addr, bob_addr).call().await?; assert_eq!(false, initial_approved); - let receipt = receipt!(contract.setOperator(bob_addr, id, true))?; + let receipt = receipt!(contract.setOperator(bob_addr, true))?; let Erc6909::isOperatorReturn { approved } = - contract.isOperator(alice_addr, bob_addr, id).call().await?; + contract.isOperator(alice_addr, bob_addr).call().await?; assert_eq!(true, approved); assert!(receipt.emits(Erc6909::OperatorSet { - caller: alice_addr, owner: alice_addr, - operator: bob_addr, - id, + spender: bob_addr, approved: true, })); @@ -614,11 +614,11 @@ async fn set_operator_rejects_invalid_spender(alice: Account) -> Result<()> { let one = uint!(1_U256); let Erc6909::isOperatorReturn { approved: initial_approved } = - contract.isOperator(alice_addr, invalid_spender, id).call().await?; + contract.isOperator(alice_addr, invalid_spender).call().await?; assert_eq!(false, initial_approved); - let err = send!(contract.setOperator(invalid_spender, id, true)) + let err = send!(contract.setOperator(invalid_spender, true)) .expect_err("should not set operator for Address::ZERO"); assert!(err.reverted_with(Erc6909::ERC6909InvalidSpender { @@ -626,7 +626,7 @@ async fn set_operator_rejects_invalid_spender(alice: Account) -> Result<()> { })); let Erc6909::isOperatorReturn { approved } = - contract.isOperator(alice_addr, invalid_spender, id).call().await?; + contract.isOperator(alice_addr, invalid_spender).call().await?; assert_eq!(false, approved); From dfe4e7ff4b862ef88448602653aa96350a66c476 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 16:39:05 +0200 Subject: [PATCH 15/33] fix --- examples/erc6909/tests/erc6909.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index 6f97a8611..3baaf34f3 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -3,8 +3,8 @@ use abi::Erc6909; use alloy::primitives::{uint, Address, U256}; use e2e::{ - constructor, receipt, send, watch, Account, Constructor, - ContractInitializationError, EventExt, Panic, PanicCode, Revert, + receipt, send, watch, Account, + EventExt, Revert, }; use eyre::Result; @@ -339,14 +339,14 @@ async fn transfer_from_reverts_insufficient_balance( bob_addr, id, balance_plus_one, - id )) .expect_err("should not transfer when balance is insufficient"); assert!(err.reverted_with(Erc6909::ERC6909InsufficientBalance { sender: alice_addr, balance, - needed: balance_plus_one + needed: balance_plus_one, + id })); let Erc6909::balanceOfReturn { balance: alice_balance } = From de65d99099fff7c5d9ba4469783a5d79b9873f8b Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 16:48:49 +0200 Subject: [PATCH 16/33] fix tests --- examples/erc6909/tests/erc6909.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index 3baaf34f3..837b958ca 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -257,7 +257,8 @@ async fn transfers_from(alice: Account, bob: Account) -> Result<()> { let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract_alice = Erc6909::new(contract_addr, &alice.wallet); - let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &bob.wallet); + let alice_addr = alice.address(); let bob_addr = bob.address(); @@ -313,7 +314,7 @@ async fn transfer_from_reverts_insufficient_balance( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract_alice = Erc6909::new(contract_addr, &alice.wallet); - let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &bob.wallet); let alice_addr = alice.address(); let bob_addr = bob.address(); @@ -373,7 +374,7 @@ async fn transfer_from_rejects_insufficient_allowance( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract_alice = Erc6909::new(contract_addr, &alice.wallet); - let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &bob.wallet); let alice_addr = alice.address(); let bob_addr = bob.address(); @@ -428,7 +429,7 @@ async fn transfer_from_rejects_invalid_receiver( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract_alice = Erc6909::new(contract_addr, &alice.wallet); - let contract_bob = Erc6909::new(contract_addr, &alice.wallet); + let contract_bob = Erc6909::new(contract_addr, &bob.wallet); let alice_addr = alice.address(); let bob_addr = bob.address(); let invalid_receiver = Address::ZERO; From c0f55d5a6f8ad1059ac7cd2d49136e6f65879833 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 16:52:51 +0200 Subject: [PATCH 17/33] fix test --- examples/erc6909/tests/erc6909.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index 837b958ca..c928ebd58 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -2,10 +2,7 @@ use abi::Erc6909; use alloy::primitives::{uint, Address, U256}; -use e2e::{ - receipt, send, watch, Account, - EventExt, Revert, -}; +use e2e::{receipt, send, watch, Account, EventExt, Revert}; use eyre::Result; mod abi; @@ -330,7 +327,7 @@ async fn transfer_from_reverts_insufficient_balance( let Erc6909::balanceOfReturn { balance: initial_bob_balance } = contract_alice.balanceOf(bob_addr, id).call().await?; - watch!(contract_alice.approve(bob_addr, id, balance))?; + watch!(contract_alice.approve(bob_addr, id, balance_plus_one))?; let Erc6909::allowanceReturn { allowance: initial_allowance } = contract_alice.allowance(alice_addr, bob_addr, id).call().await?; @@ -485,7 +482,6 @@ async fn approves(alice: Account, bob: Account) -> Result<()> { let id = uint!(2_U256); let one = uint!(1_U256); - let balance = uint!(10_U256); let Erc6909::allowanceReturn { allowance: initial_alice_to_bob_allowance } = contract_alice.allowance(alice_addr, bob_addr, id).call().await?; @@ -578,8 +574,6 @@ async fn sets_operator(alice: Account, bob: Account) -> Result<()> { let alice_addr = alice.address(); let bob_addr = bob.address(); - let id = uint!(2_U256); - let one = uint!(1_U256); let balance = uint!(10_U256); let Erc6909::isOperatorReturn { approved: initial_approved } = @@ -611,9 +605,6 @@ async fn set_operator_rejects_invalid_spender(alice: Account) -> Result<()> { let alice_addr = alice.address(); let invalid_spender = Address::ZERO; - let id = uint!(2_U256); - let one = uint!(1_U256); - let Erc6909::isOperatorReturn { approved: initial_approved } = contract.isOperator(alice_addr, invalid_spender).call().await?; From e395d59072dc8a382e4bf5cc1197d0887d3bb7f5 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 17:01:25 +0200 Subject: [PATCH 18/33] fix erc6909-supply tests --- .../erc6909-supply/tests/erc6909-supply.rs | 54 +++++++++---------- examples/erc6909/tests/erc6909.rs | 10 ++-- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/examples/erc6909-supply/tests/erc6909-supply.rs b/examples/erc6909-supply/tests/erc6909-supply.rs index 31b9e9007..2327ca66a 100644 --- a/examples/erc6909-supply/tests/erc6909-supply.rs +++ b/examples/erc6909-supply/tests/erc6909-supply.rs @@ -2,7 +2,7 @@ use abi::Erc6909TokenSupply; use alloy::primitives::{uint, Address, U256}; -use e2e::{receipt, send, watch, Account, EventExt, Panic, PanicCode}; +use e2e::{receipt, send, watch, Account, EventExt, Panic, PanicCode, Revert}; use eyre::Result; mod abi; @@ -24,7 +24,7 @@ async fn mints(alice: Account) -> Result<()> { contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; assert_eq!(U256::ZERO, initial_balance); @@ -42,7 +42,7 @@ async fn mints(alice: Account) -> Result<()> { contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; assert_eq!(initial_balance + one, balance); assert_eq!(initial_supply + one, total_supply); @@ -69,9 +69,9 @@ async fn mints_twice(alice: Account, bob: Account) -> Result<()> { contract.balanceOf(bob_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; - assert_eq!(U256::ZERO, initial_balance); + assert_eq!(U256::ZERO, initial_supply); let receipt = receipt!(contract.mint(alice_addr, id, one))?; let receipt = receipt!(contract.mint(bob_addr, id, ten))?; @@ -99,10 +99,10 @@ async fn mints_twice(alice: Account, bob: Account) -> Result<()> { contract.balanceOf(bob_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; - assert_eq!(initial_balance + one, initial_balance_alice); - assert_eq!(initial_balance + ten, initial_balance_bob); + assert_eq!(initial_balance_alice + one, balance_alice); + assert_eq!(initial_balance_bob + ten, balance_bob); assert_eq!(initial_supply + one + ten, total_supply); Ok(()) @@ -122,22 +122,20 @@ async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> { contract.balanceOf(invalid_receiver, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; let err = send!(contract.mint(invalid_receiver, id, one)) .expect_err("should not mint tokens for Address::ZERO"); - assert!(err.reverted_with( - Erc6909TokenSupply::ERC6909TokenSupplyInvalidReceiver { - receiver: invalid_receiver - } - )); + assert!(err.reverted_with(Erc6909TokenSupply::ERC6909InvalidReceiver { + receiver: invalid_receiver + })); let Erc6909TokenSupply::balanceOfReturn { balance: balance } = contract.balanceOf(invalid_receiver, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; assert_eq!(initial_balance, balance); assert_eq!(initial_supply, total_supply); @@ -162,7 +160,7 @@ async fn mints_rejects_overflow(alice: Account) -> Result<()> { contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; assert_eq!(U256::ZERO, initial_balance); assert_eq!(U256::ZERO, initial_supply); @@ -170,13 +168,13 @@ async fn mints_rejects_overflow(alice: Account) -> Result<()> { let err = send!(contract.mint(alice_addr, id, one)) .expect_err("should not exceed U256::MAX"); - assert!(err.panicked_with(Panic::new(PanicCode::ArithmeticOverflow))); + assert!(err.panicked_with(PanicCode::ArithmeticOverflow)); let Erc6909TokenSupply::balanceOfReturn { balance: balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; assert_eq!(initial_balance, balance); assert_eq!(initial_supply, total_supply); @@ -201,7 +199,7 @@ async fn burns(alice: Account) -> Result<()> { contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; let receipt = receipt!(contract.burn(alice_addr, id, one))?; @@ -209,9 +207,9 @@ async fn burns(alice: Account) -> Result<()> { contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; - assert!(receipt.emits(Erc6909::Transfer { + assert!(receipt.emits(Erc6909TokenSupply::Transfer { caller: alice_addr, sender: alice_addr, receiver: Address::ZERO, @@ -241,22 +239,20 @@ async fn burn_rejects_invalid_sender(alice: Account) -> Result<()> { contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; - let err = send!(contract.burn(invalid_sender, id, balance_plus_one)) + let err = send!(contract.burn(invalid_sender, id, one)) .expect_err("should not burn when balance is insufficient"); - assert!(err.reverted_with( - Erc6909TokenSupply::ERC6909TokenSupplyInvalidSender { - sender: invalid_sender - } - )); + assert!(err.reverted_with(Erc6909TokenSupply::ERC6909InvalidSender { + sender: invalid_sender + })); let Erc6909TokenSupply::balanceOfReturn { balance: balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = - contract.totalSupply().call().await?; + contract.totalSupply(id).call().await?; assert_eq!(initial_balance, balance); assert_eq!(initial_supply, total_supply); diff --git a/examples/erc6909/tests/erc6909.rs b/examples/erc6909/tests/erc6909.rs index c928ebd58..814a860c7 100644 --- a/examples/erc6909/tests/erc6909.rs +++ b/examples/erc6909/tests/erc6909.rs @@ -36,7 +36,7 @@ async fn mints(alice: Account) -> Result<()> { amount: one, })); - let Erc6909::balanceOfReturn { balance: balance } = + let Erc6909::balanceOfReturn { balance } = contract.balanceOf(alice_addr, id).call().await?; assert_eq!(one, balance); @@ -64,7 +64,7 @@ async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> { receiver: invalid_receiver })); - let Erc6909::balanceOfReturn { balance: balance } = + let Erc6909::balanceOfReturn { balance } = contract.balanceOf(invalid_receiver, id).call().await?; assert_eq!(initial_balance, balance); @@ -90,7 +90,7 @@ async fn burns(alice: Account) -> Result<()> { let receipt = receipt!(contract.burn(alice_addr, id, one))?; - let Erc6909::balanceOfReturn { balance: balance } = + let Erc6909::balanceOfReturn { balance } = contract.balanceOf(alice_addr, id).call().await?; assert!(receipt.emits(Erc6909::Transfer { @@ -132,7 +132,7 @@ async fn burn_rejects_insufficient_balance(alice: Account) -> Result<()> { id })); - let Erc6909::balanceOfReturn { balance: balance } = + let Erc6909::balanceOfReturn { balance } = contract.balanceOf(alice_addr, id).call().await?; assert_eq!(initial_balance, balance); @@ -574,8 +574,6 @@ async fn sets_operator(alice: Account, bob: Account) -> Result<()> { let alice_addr = alice.address(); let bob_addr = bob.address(); - let balance = uint!(10_U256); - let Erc6909::isOperatorReturn { approved: initial_approved } = contract.isOperator(alice_addr, bob_addr).call().await?; From d35436184125511ff73552bd2795ee68d068ebd1 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 17:06:52 +0200 Subject: [PATCH 19/33] fix --- .../erc6909-supply/tests/erc6909-supply.rs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/erc6909-supply/tests/erc6909-supply.rs b/examples/erc6909-supply/tests/erc6909-supply.rs index 2327ca66a..a132b0266 100644 --- a/examples/erc6909-supply/tests/erc6909-supply.rs +++ b/examples/erc6909-supply/tests/erc6909-supply.rs @@ -38,7 +38,7 @@ async fn mints(alice: Account) -> Result<()> { amount: one, })); - let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + let Erc6909TokenSupply::balanceOfReturn { balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = @@ -73,10 +73,10 @@ async fn mints_twice(alice: Account, bob: Account) -> Result<()> { assert_eq!(U256::ZERO, initial_supply); - let receipt = receipt!(contract.mint(alice_addr, id, one))?; - let receipt = receipt!(contract.mint(bob_addr, id, ten))?; + let receipt1 = receipt!(contract.mint(alice_addr, id, one))?; + let receipt2 = receipt!(contract.mint(bob_addr, id, ten))?; - assert!(receipt.emits(Erc6909TokenSupply::Transfer { + assert!(receipt1.emits(Erc6909TokenSupply::Transfer { caller: alice_addr, sender: Address::ZERO, receiver: alice_addr, @@ -84,7 +84,7 @@ async fn mints_twice(alice: Account, bob: Account) -> Result<()> { amount: one, })); - assert!(receipt.emits(Erc6909TokenSupply::Transfer { + assert!(receipt2.emits(Erc6909TokenSupply::Transfer { caller: alice_addr, sender: Address::ZERO, receiver: bob_addr, @@ -131,7 +131,7 @@ async fn mints_rejects_invalid_receiver(alice: Account) -> Result<()> { receiver: invalid_receiver })); - let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + let Erc6909TokenSupply::balanceOfReturn { balance } = contract.balanceOf(invalid_receiver, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = @@ -154,23 +154,22 @@ async fn mints_rejects_overflow(alice: Account) -> Result<()> { let id = uint!(2_U256); let one = uint!(1_U256); - watch!(contract.mint(alice_addr, id, max_cap))?; - let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = contract.totalSupply(id).call().await?; - assert_eq!(U256::ZERO, initial_balance); assert_eq!(U256::ZERO, initial_supply); + watch!(contract.mint(alice_addr, id, max_cap))?; + let err = send!(contract.mint(alice_addr, id, one)) .expect_err("should not exceed U256::MAX"); assert!(err.panicked_with(PanicCode::ArithmeticOverflow)); - let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + let Erc6909TokenSupply::balanceOfReturn { balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = @@ -203,7 +202,7 @@ async fn burns(alice: Account) -> Result<()> { let receipt = receipt!(contract.burn(alice_addr, id, one))?; - let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + let Erc6909TokenSupply::balanceOfReturn { balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = @@ -232,7 +231,6 @@ async fn burn_rejects_invalid_sender(alice: Account) -> Result<()> { let invalid_sender = Address::ZERO; let id = uint!(2_U256); - let balance = uint!(10_U256); let one = uint!(1_U256); let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = @@ -248,7 +246,7 @@ async fn burn_rejects_invalid_sender(alice: Account) -> Result<()> { sender: invalid_sender })); - let Erc6909TokenSupply::balanceOfReturn { balance: balance } = + let Erc6909TokenSupply::balanceOfReturn { balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: total_supply } = From 4c7fdb2e3f228756befe2d9649494f8b5895a4ad Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 17:09:21 +0200 Subject: [PATCH 20/33] fix test --- examples/erc6909-supply/tests/erc6909-supply.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/erc6909-supply/tests/erc6909-supply.rs b/examples/erc6909-supply/tests/erc6909-supply.rs index a132b0266..f5b528eae 100644 --- a/examples/erc6909-supply/tests/erc6909-supply.rs +++ b/examples/erc6909-supply/tests/erc6909-supply.rs @@ -154,15 +154,16 @@ async fn mints_rejects_overflow(alice: Account) -> Result<()> { let id = uint!(2_U256); let one = uint!(1_U256); + watch!(contract.mint(alice_addr, id, max_cap))?; + let Erc6909TokenSupply::balanceOfReturn { balance: initial_balance } = contract.balanceOf(alice_addr, id).call().await?; let Erc6909TokenSupply::totalSupplyReturn { totalSupply: initial_supply } = contract.totalSupply(id).call().await?; - assert_eq!(U256::ZERO, initial_supply); - - watch!(contract.mint(alice_addr, id, max_cap))?; + assert_eq!(initial_supply, max_cap); + assert_eq!(initial_balance, max_cap); let err = send!(contract.mint(alice_addr, id, one)) .expect_err("should not exceed U256::MAX"); From 23b6d8365721aa6af3023b23d4a343bc14e36d2d Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 23:25:00 +0200 Subject: [PATCH 21/33] add bench for erc6909 --- benches/src/erc6909.rs | 84 +++++++++++++++++++++++++++++++++++ benches/src/erc6909_supply.rs | 0 benches/src/lib.rs | 2 + benches/src/main.rs | 5 +-- 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 benches/src/erc6909.rs create mode 100644 benches/src/erc6909_supply.rs diff --git a/benches/src/erc6909.rs b/benches/src/erc6909.rs new file mode 100644 index 000000000..2d11599c4 --- /dev/null +++ b/benches/src/erc6909.rs @@ -0,0 +1,84 @@ +use alloy::{ + network::{AnyNetwork, EthereumWallet}, + primitives::Address, + providers::ProviderBuilder, + sol, + sol_types::SolCall, + uint, +}; +use e2e::{receipt, Account}; + +use crate::{ + report::{ContractReport, FunctionReport}, + Opt, +}; + +sol!( + #[sol(rpc)] + contract Erc6909 { + function balanceOf(address owner, uint256 id) external view returns (uint256 balance); + function allowance(address owner, address spender, uint256 id) external view returns (uint256 allowance); + function isOperator(address owner, address spender) external view returns (bool approved); + function approve(address spender, uint256 id, uint256 amount) external returns (bool); + function setOperator(address spender, bool approved) external returns (bool); + function transfer(address receiver, uint256 id, uint256 amount) external returns (bool); + function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool); + function mint(address to, uint256 id, uint256 amount) external; + function burn(address from, uint256 id, uint256 amount) external; + } +); + +pub async fn bench() -> eyre::Result { + ContractReport::generate("Erc6909", run).await +} + +pub async fn run(cache_opt: Opt) -> eyre::Result> { + let alice = Account::new().await?; + let alice_addr = alice.address(); + let alice_wallet = ProviderBuilder::new() + .network::() + .with_recommended_fillers() + .wallet(EthereumWallet::from(alice.signer.clone())) + .on_http(alice.url().parse()?); + + let bob = Account::new().await?; + let bob_addr = bob.address(); + let bob_wallet = ProviderBuilder::new() + .network::() + .with_recommended_fillers() + .wallet(EthereumWallet::from(bob.signer.clone())) + .on_http(bob.url().parse()?); + + let contract_addr = deploy(&alice, cache_opt).await?; + + let contract = Erc6909::new(contract_addr, &alice_wallet); + let contract_bob = Erc6909::new(contract_addr, &bob_wallet); + + let token_id = uint!(1_U256); + let amount = uint!(100_U256); + let one = uint!(1_U256); + + // IMPORTANT: Order matters! + use Erc6909::*; + #[rustfmt::skip] + let receipts = vec![ + (mintCall::SIGNATURE, receipt!(contract.mint(alice_addr, token_id, amount))?), + (balanceOfCall::SIGNATURE, receipt!(contract.balanceOf(alice_addr, token_id))?), + (allowanceCall::SIGNATURE, receipt!(contract.allowance(alice_addr, bob_addr, token_id))?), + (isOperatorCall::SIGNATURE, receipt!(contract.isOperator(alice_addr, bob_addr))?), + (setOperatorCall::SIGNATURE, receipt!(contract.setOperator(bob_addr, true))?), + (transferCall::SIGNATURE, receipt!(contract.transfer(bob_addr, token_id, one))?), + (approveCall::SIGNATURE, receipt!(contract.approve(bob_addr, token_id, one))?), + (transferFromCall::SIGNATURE, receipt!(contract_bob.transferFrom(alice_addr, bob_addr, token_id, one))?), + (burnCall::SIGNATURE, receipt!(contract.burn(alice_addr, token_id, one))?), + ]; + + receipts + .into_iter() + .map(FunctionReport::new) + .collect::>>() +} + +async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result
{ + crate::deploy(account, "erc6909", None, cache_opt).await +} \ No newline at end of file diff --git a/benches/src/erc6909_supply.rs b/benches/src/erc6909_supply.rs new file mode 100644 index 000000000..e69de29bb diff --git a/benches/src/lib.rs b/benches/src/lib.rs index ab15bd810..dbbb0abce 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -12,6 +12,8 @@ pub mod erc1155_metadata_uri; pub mod erc1155_supply; pub mod erc20; pub mod erc721; +pub mod erc6909; +pub mod erc6909_supply; pub mod merkle_proofs; pub mod ownable; pub mod pedersen; diff --git a/benches/src/main.rs b/benches/src/main.rs index 7f9211a02..88473bae9 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,7 +1,5 @@ use benches::{ - access_control, erc1155, erc1155_metadata_uri, erc20, erc721, - merkle_proofs, ownable, pedersen, poseidon, poseidon_asm_sol, poseidon_sol, - report::BenchmarkReport, + access_control, erc1155, erc1155_metadata_uri, erc20, erc6909, erc721, merkle_proofs, ownable, pedersen, poseidon, poseidon_asm_sol, poseidon_sol, report::BenchmarkReport }; use futures::FutureExt; use itertools::Itertools; @@ -16,6 +14,7 @@ async fn main() -> eyre::Result<()> { ownable::bench().boxed(), erc1155::bench().boxed(), erc1155_metadata_uri::bench().boxed(), + erc6909::bench().boxed(), pedersen::bench().boxed(), poseidon_sol::bench().boxed(), poseidon_asm_sol::bench().boxed(), From 976569b7963e45e9faca23f5e08597a60bb2b4a5 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Mon, 9 Jun 2025 23:33:55 +0200 Subject: [PATCH 22/33] add bench for erc6909 token supply --- benches/src/erc6909.rs | 2 +- benches/src/erc6909_supply.rs | 62 +++++++++++++++++++++++++++++++++++ benches/src/lib.rs | 2 +- benches/src/main.rs | 4 ++- 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/benches/src/erc6909.rs b/benches/src/erc6909.rs index 2d11599c4..94421155b 100644 --- a/benches/src/erc6909.rs +++ b/benches/src/erc6909.rs @@ -81,4 +81,4 @@ pub async fn run(cache_opt: Opt) -> eyre::Result> { async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result
{ crate::deploy(account, "erc6909", None, cache_opt).await -} \ No newline at end of file +} diff --git a/benches/src/erc6909_supply.rs b/benches/src/erc6909_supply.rs index e69de29bb..c1f60f453 100644 --- a/benches/src/erc6909_supply.rs +++ b/benches/src/erc6909_supply.rs @@ -0,0 +1,62 @@ +use alloy::{ + network::{AnyNetwork, EthereumWallet}, + primitives::Address, + providers::ProviderBuilder, + sol, + sol_types::SolCall, + uint, +}; +use e2e::{receipt, Account}; + +use crate::{ + report::{ContractReport, FunctionReport}, + Opt, +}; + +sol!( + #[sol(rpc)] + contract Erc6909TokenSupply { + function totalSupply(uint256 id) external view returns (uint256 totalSupply); + function mint(address to, uint256 id, uint256 amount) external; + function burn(address from, uint256 id, uint256 amount) external; + } +); + +pub async fn bench() -> eyre::Result { + ContractReport::generate("Erc6909TokenSupply", run).await +} + +pub async fn run(cache_opt: Opt) -> eyre::Result> { + let alice = Account::new().await?; + let alice_addr = alice.address(); + let alice_wallet = ProviderBuilder::new() + .network::() + .with_recommended_fillers() + .wallet(EthereumWallet::from(alice.signer.clone())) + .on_http(alice.url().parse()?); + + let contract_addr = deploy(&alice, cache_opt).await?; + + let contract = Erc6909TokenSupply::new(contract_addr, &alice_wallet); + + let token_id = uint!(1_U256); + let amount = uint!(100_U256); + + // IMPORTANT: Order matters! + use Erc6909TokenSupply::*; + #[rustfmt::skip] + let receipts = vec![ + (mintCall::SIGNATURE, receipt!(contract.mint(alice_addr, token_id, amount))?), + (totalSupplyCall::SIGNATURE, receipt!(contract.totalSupply(token_id))?), + (burnCall::SIGNATURE, receipt!(contract.burn(alice_addr, token_id, amount))?), + ]; + + receipts + .into_iter() + .map(FunctionReport::new) + .collect::>>() +} + +async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result
{ + crate::deploy(account, "erc6909-supply", None, cache_opt).await +} diff --git a/benches/src/lib.rs b/benches/src/lib.rs index dbbb0abce..8a63b68ab 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -11,9 +11,9 @@ pub mod erc1155; pub mod erc1155_metadata_uri; pub mod erc1155_supply; pub mod erc20; -pub mod erc721; pub mod erc6909; pub mod erc6909_supply; +pub mod erc721; pub mod merkle_proofs; pub mod ownable; pub mod pedersen; diff --git a/benches/src/main.rs b/benches/src/main.rs index 88473bae9..8733f5c74 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,5 +1,7 @@ use benches::{ - access_control, erc1155, erc1155_metadata_uri, erc20, erc6909, erc721, merkle_proofs, ownable, pedersen, poseidon, poseidon_asm_sol, poseidon_sol, report::BenchmarkReport + access_control, erc1155, erc1155_metadata_uri, erc20, erc6909, erc721, + merkle_proofs, ownable, pedersen, poseidon, poseidon_asm_sol, poseidon_sol, + report::BenchmarkReport, }; use futures::FutureExt; use itertools::Itertools; From fdadc2acc23dfba8d9544b0300eb3b882efc883b Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 7 Aug 2025 12:41:41 +0300 Subject: [PATCH 23/33] update cargo for CI --- Cargo.lock | 622 +++++++++++++++++++++++++++-------------------------- 1 file changed, 319 insertions(+), 303 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fcf71295..7580daab1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" @@ -433,7 +433,7 @@ dependencies = [ "derive_more", "foldhash", "getrandom 0.2.16", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "indexmap", "itoa", "k256", @@ -542,7 +542,7 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -759,7 +759,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -776,7 +776,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "syn-solidity", "tiny-keccak", ] @@ -794,7 +794,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.101", + "syn 2.0.104", "syn-solidity", ] @@ -908,9 +908,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -923,37 +923,37 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1114,7 +1114,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1125,7 +1125,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1146,14 +1146,14 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -1184,9 +1184,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "basic-example" @@ -1284,9 +1284,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c79a94619fade3c0b887670333513a67ac28a6a7e653eb260bf0d4103db38d" +checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" dependencies = [ "cc", "glob", @@ -1296,18 +1296,18 @@ dependencies = [ [[package]] name = "branches" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3cb31f305a2591edaae2206f29e1e05b19ba48eba41042a18735bcc0efe165" +checksum = "a918aa7a861caeba57e502465c30e3a0d74ae02ee0b9db2933602fdb6a3a90e5" dependencies = [ - "rustc_version 0.2.3", + "rustc_version 0.4.1", ] [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-slice-cast" @@ -1347,24 +1347,24 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.23" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "clap" -version = "4.5.38" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -1372,9 +1372,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -1384,27 +1384,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "const-hex" @@ -1502,9 +1502,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" @@ -1571,7 +1571,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1591,7 +1591,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "unicode-xid", ] @@ -1624,7 +1624,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1635,9 +1635,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "e2e" @@ -1650,7 +1650,7 @@ dependencies = [ "regex", "stylus-sdk", "tokio", - "toml", + "toml 0.8.23", ] [[package]] @@ -1659,7 +1659,7 @@ version = "0.2.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1685,7 +1685,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1730,7 +1730,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1741,7 +1741,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1885,7 +1885,7 @@ dependencies = [ [[package]] name = "erc6909-example" -version = "0.2.0-rc.0" +version = "0.3.0-alpha.1" dependencies = [ "alloy", "alloy-primitives", @@ -1898,7 +1898,7 @@ dependencies = [ [[package]] name = "erc6909-supply-example" -version = "0.2.0-rc.0" +version = "0.3.0-alpha.1" dependencies = [ "alloy", "alloy-primitives", @@ -1919,7 +1919,7 @@ dependencies = [ "e2e", "eyre", "openzeppelin-stylus", - "rand 0.9.1", + "rand 0.9.2", "stylus-sdk", "tokio", ] @@ -1933,7 +1933,7 @@ dependencies = [ "e2e", "eyre", "openzeppelin-stylus", - "rand 0.9.1", + "rand 0.9.2", "stylus-sdk", "tokio", ] @@ -1961,7 +1961,7 @@ dependencies = [ "e2e", "eyre", "openzeppelin-stylus", - "rand 0.9.1", + "rand 0.9.2", "stylus-sdk", "tokio", ] @@ -1981,12 +1981,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2147,7 +2147,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2205,7 +2205,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -2257,9 +2257,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -2275,9 +2275,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2380,17 +2380,21 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -2522,33 +2526,54 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "serde", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2647,9 +2672,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libm" @@ -2671,9 +2696,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2691,7 +2716,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -2700,14 +2725,14 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "merkle-proofs-example" @@ -2719,12 +2744,6 @@ dependencies = [ "stylus-sdk", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "mini-alloc" version = "0.9.0" @@ -2736,22 +2755,22 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -2781,7 +2800,7 @@ checksum = "596b892439b97b288be7c6bf616c62ea607fe74a7a9a41903f9d78b7b1274ac4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2877,9 +2896,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -2887,22 +2906,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2935,18 +2955,15 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2611b99ab098a31bdc8be48b4f1a285ca0ced28bd5b4f23e45efa8c63b09efa5" -dependencies = [ - "once_cell", -] +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags", "cfg-if", @@ -2965,7 +2982,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -2976,9 +2993,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.108" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -2995,7 +3012,7 @@ dependencies = [ "hex-literal", "num-traits", "proptest", - "rand 0.9.1", + "rand 0.9.2", "ruint", "tiny-keccak", "zeroize", @@ -3023,7 +3040,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3077,14 +3094,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -3092,9 +3109,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -3130,9 +3147,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror", @@ -3156,7 +3173,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3294,7 +3311,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3317,7 +3334,7 @@ dependencies = [ "bitflags", "lazy_static", "num-traits", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -3334,7 +3351,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3368,9 +3385,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -3392,9 +3409,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -3458,9 +3475,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] @@ -3496,29 +3513,25 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64", "bytes", "futures-core", - "futures-util", "http", "http-body", "http-body-util", "hyper", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", - "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -3526,12 +3539,12 @@ dependencies = [ "tokio", "tokio-native-tls", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] @@ -3604,9 +3617,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.14.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a46eb779843b2c4f21fac5773e25d6d5b7c8f0922876c91541790d2ca27eef" +checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" dependencies = [ "alloy-rlp", "arbitrary", @@ -3622,7 +3635,7 @@ dependencies = [ "primitive-types", "proptest", "rand 0.8.5", - "rand 0.9.1", + "rand 0.9.2", "rlp", "ruint-macro", "serde", @@ -3638,9 +3651,9 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -3657,15 +3670,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.3.3" @@ -3686,24 +3690,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", + "windows-sys 0.60.2", ] [[package]] @@ -3717,9 +3712,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rusty-fork" @@ -3835,22 +3830,13 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser 0.10.3", + "semver-parser", ] [[package]] @@ -3859,12 +3845,6 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "semver-parser" version = "0.10.3" @@ -3891,14 +3871,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -3908,9 +3888,18 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] @@ -3966,9 +3955,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -3985,30 +3974,27 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.9" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4047,24 +4033,23 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4095,7 +4080,7 @@ dependencies = [ "quote", "regex", "sha3", - "syn 2.0.101", + "syn 2.0.104", "syn-solidity", "trybuild", ] @@ -4168,9 +4153,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -4186,7 +4171,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4206,7 +4191,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4260,7 +4245,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4293,20 +4278,22 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4317,7 +4304,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4344,9 +4331,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -4357,44 +4344,83 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit", ] +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tower" @@ -4411,6 +4437,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -4436,20 +4480,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -4462,9 +4506,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.105" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9bf9513a2f4aeef5fdac8677d7d349c79fdbcc03b9c86da6e9d254f1e43be2" +checksum = "32e257d7246e7a9fd015fb0b28b330a8d4142151a33f03e6a497754f4b1f6a8e" dependencies = [ "glob", "serde", @@ -4472,7 +4516,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml", + "toml 0.9.5", ] [[package]] @@ -4625,9 +4669,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -4660,7 +4704,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -4695,7 +4739,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4711,9 +4755,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" +checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" dependencies = [ "futures", "js-sys", @@ -4744,55 +4788,26 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings", - "windows-targets 0.53.0", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.53.3", ] [[package]] @@ -4813,10 +4828,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -4925,9 +4941,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -4976,28 +4992,28 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5017,7 +5033,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -5038,7 +5054,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5054,9 +5070,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -5071,5 +5087,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] From e05e7d0384c8b694541f44e58f9d4ac65a893674 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 7 Aug 2025 18:12:21 +0300 Subject: [PATCH 24/33] apply requested changes --- CHANGELOG.md | 2 + benches/src/main.rs | 3 +- .../token/erc6909/extensions/token_supply.rs | 21 +--- contracts/src/token/erc6909/mod.rs | 18 +-- docs/modules/ROOT/nav.adoc | 2 + docs/modules/ROOT/pages/erc6909-supply.adoc | 117 ++++++++++++++++++ docs/modules/ROOT/pages/erc6909.adoc | 11 ++ examples/erc6909-supply/Cargo.toml | 2 +- examples/erc6909/Cargo.toml | 2 +- 9 files changed, 149 insertions(+), 29 deletions(-) create mode 100644 docs/modules/ROOT/pages/erc6909-supply.adoc create mode 100644 docs/modules/ROOT/pages/erc6909.adoc diff --git a/CHANGELOG.md b/CHANGELOG.md index 3006a90c6..240e62bdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `SafeErc20` now implements: `try_safe_transfer`, `try_safe_transfer_from`, `transfer_and_call_relaxed`, `transfer_from_and_call_relaxed` and `approve_and_call_relaxed`. #765 - Add bidirectional conversions between `ruint::Uint` and crypto library `Uint` types behind `ruint` feature toggle. #758 - Add bidirectional conversions between `Uint` and `u8`, `u16`, `u32`, `u64`, `u128` types. #764 +- Add ERC-6909 Token. #778 +- Add ERC-6909 Supply Extension. # 779 ### Changed (Breaking) diff --git a/benches/src/main.rs b/benches/src/main.rs index 8733f5c74..0dd65d6f5 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,5 +1,5 @@ use benches::{ - access_control, erc1155, erc1155_metadata_uri, erc20, erc6909, erc721, + access_control, erc1155, erc1155_metadata_uri, erc20, erc6909, erc6909_supply, erc721, merkle_proofs, ownable, pedersen, poseidon, poseidon_asm_sol, poseidon_sol, report::BenchmarkReport, }; @@ -17,6 +17,7 @@ async fn main() -> eyre::Result<()> { erc1155::bench().boxed(), erc1155_metadata_uri::bench().boxed(), erc6909::bench().boxed(), + erc6909_supply::bench().boxed(), pedersen::bench().boxed(), poseidon_sol::bench().boxed(), poseidon_asm_sol::bench().boxed(), diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index 69e59c3a4..904d9ba21 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -2,7 +2,6 @@ //! Tracks the total supply of each token id individually. use alloc::{vec, vec::Vec}; -use core::ops::{Deref, DerefMut}; use alloy_primitives::{Address, FixedBytes, U256}; use openzeppelin_stylus_proc::interface_id; @@ -29,20 +28,6 @@ pub struct Erc6909TokenSupply { pub(crate) total_supplies: StorageMap, } -impl Deref for Erc6909TokenSupply { - type Target = Erc6909; - - fn deref(&self) -> &Self::Target { - &self.erc6909 - } -} - -impl DerefMut for Erc6909TokenSupply { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.erc6909 - } -} - /// Required interface of a [`Erc6909TokenSupply`] contract. #[interface_id] pub trait IErc6909TokenSupply: IErc165 { @@ -132,6 +117,7 @@ impl Erc6909TokenSupply { /// mechanism. /// /// Re-export of [`Erc6909::_mint`]. + #[allow(clippy::missing_errors_doc)] pub fn _mint( &mut self, to: Address, @@ -151,6 +137,7 @@ impl Erc6909TokenSupply { /// Relies on the `_update` mechanism. /// /// Re-export of [`Erc6909::_burn`]. + #[allow(clippy::missing_errors_doc)] pub fn _burn( &mut self, from: Address, @@ -174,6 +161,7 @@ impl Erc6909TokenSupply { /// Relies on the `_update` mechanism. /// /// Re-export of [`Erc6909::_transfer`]. + #[allow(clippy::missing_errors_doc)] fn _transfer( &mut self, from: Address, @@ -252,7 +240,6 @@ impl IErc165 for Erc6909TokenSupply { fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { ::interface_id() == interface_id || self.erc6909.supports_interface(interface_id) - || ::interface_id() == interface_id } } @@ -265,7 +252,7 @@ mod tests { }; use super::*; - use crate::token::erc6909::{ERC6909InvalidReceiver, ERC6909InvalidSender}; + use crate::token::erc6909::{ERC6909InvalidSender}; unsafe impl TopLevelStorage for Erc6909TokenSupply {} diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs index c6a0e2941..89bbea7fd 100644 --- a/contracts/src/token/erc6909/mod.rs +++ b/contracts/src/token/erc6909/mod.rs @@ -179,6 +179,7 @@ pub trait IErc6909: IErc165 { /// Must return true. /// /// # Arguments + /// /// * `&mut self` - Write access to the contract's state. /// * `spender` - Account that will spend the tokens. /// * `id` - Token id as a number. @@ -188,6 +189,7 @@ pub trait IErc6909: IErc165 { /// /// * [`Error::InvalidSpender`] - If the `spender` address is /// [`Address::ZERO`]. + /// /// # Events /// /// * [`Approval`]. @@ -204,6 +206,7 @@ pub trait IErc6909: IErc165 { /// Must return true. /// /// # Arguments + /// /// * `&mut self` - Write access to the contract's state. /// * `spender` - Account that will spend the tokens. /// * `approved` - Flag that determines whether or not permission will be @@ -591,7 +594,7 @@ impl Erc6909 { id: U256, amount: U256, ) -> Result<(), Error> { - let current_allowance = self.allowances.get(owner).get(spender).get(id); + let current_allowance = self.allowance(owner, spender, id); if current_allowance < U256::MAX { if current_allowance < amount { return Err(Error::InsufficientAllowance( @@ -603,13 +606,11 @@ impl Erc6909 { }, )); } - self._approve( - owner, - spender, - id, - current_allowance - amount, - false, - )?; + + self.allowances.setter(owner) + .setter(spender) + .setter(id) + .set(current_allowance - amount); } Ok(()) } @@ -1099,7 +1100,6 @@ mod tests { fn set_operator_errors_invalid_spender( contract: Contract, alice: Address, - bob: Address, ) { let err = contract .sender(alice) diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 4ce942940..47fe718c8 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -13,6 +13,8 @@ *** xref:erc721.adoc#erc721-token-extensions[Extensions] ** xref:erc1155.adoc[ERC-1155] *** xref:erc1155.adoc#erc1155-token-extensions[Extensions] +** xref:erc6909.adoc[ERC-6909] +*** xref:erc6909.adoc#erc6909-token-extensions[Extensions] * xref:proxy.adoc[Proxy Patterns] ** xref:erc1967.adoc[ERC-1967 Proxy] diff --git a/docs/modules/ROOT/pages/erc6909-supply.adoc b/docs/modules/ROOT/pages/erc6909-supply.adoc new file mode 100644 index 000000000..eccadb52d --- /dev/null +++ b/docs/modules/ROOT/pages/erc6909-supply.adoc @@ -0,0 +1,117 @@ += ERC-6909 Supply + +The OpenZeppelin xref:erc6909.adoc[ERC-6909] Token Supply extension that adds tracking of total supply per token id. +Useful for scenarios where Fungible and Non-fungible tokens have to be clearly identified. + +[[usage]] +== Usage + +In order to make an xref:erc6909.adoc[ERC-6909] token with https://docs.rs/openzeppelin-stylus/0.3.0-alpha.1/openzeppelin_stylus/token/erc6909/extensions/supply/index.html[Supply] flavour, +you need to reexport all the supply-related functions. + +[source,rust] +---- +use openzeppelin_stylus::{ + token::erc6909::{ + self, + extensions::{Erc6909TokenSupply, IErc6909TokenSupply}, + IErc6909, + }, + utils::introspection::erc165::IErc165, +}; + +#[entrypoint] +#[storage] +struct Erc6909TokenSupplyExample { + erc6909_token_supply: Erc6909TokenSupply, +} + +#[public] +#[implements(IErc6909, IErc6909TokenSupply, IErc165)] +impl Erc6909TokenSupplyExample { + fn mint( + &mut self, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909_token_supply._mint(to, id, amount) + } + + fn burn( + &mut self, + from: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909_token_supply._burn(from, id, amount) + } +} + +#[public] +impl IErc6909TokenSupply for Erc6909TokenSupplyExample { + fn total_supply(&self, id: U256) -> U256 { + self.erc6909_token_supply.total_supply(id) + } +} + +#[public] +impl IErc6909 for Erc6909TokenSupplyExample { + type Error = erc6909::Error; + + fn balance_of(&self, owner: Address, id: U256) -> U256 { + self.erc6909_token_supply.balance_of(owner, id) + } + + fn allowance(&self, owner: Address, spender: Address, id: U256) -> U256 { + self.erc6909_token_supply.allowance(owner, spender, id) + } + + fn is_operator(&self, owner: Address, spender: Address) -> bool { + self.erc6909_token_supply.is_operator(owner, spender) + } + + fn approve( + &mut self, + spender: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909_token_supply.approve(spender, id, amount) + } + + fn set_operator( + &mut self, + spender: Address, + approved: bool, + ) -> Result { + self.erc6909_token_supply.set_operator(spender, approved) + } + + fn transfer( + &mut self, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909_token_supply.transfer(receiver, id, amount) + } + + fn transfer_from( + &mut self, + sender: Address, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909_token_supply.transfer_from(sender, receiver, id, amount) + } +} + +#[public] +impl IErc165 for Erc6909TokenSupplyExample { + fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + self.erc6909_token_supply.supports_interface(interface_id) + } +} +---- diff --git a/docs/modules/ROOT/pages/erc6909.adoc b/docs/modules/ROOT/pages/erc6909.adoc new file mode 100644 index 000000000..651abe92c --- /dev/null +++ b/docs/modules/ROOT/pages/erc6909.adoc @@ -0,0 +1,11 @@ += ERC-6909 +link:https://eips.ethereum.org/EIPS/eip-6909[ERC-6909 Minimal Multi-Token Interface] + +This set of interfaces and contracts are all related to ERC-6909 Minimal Multi-Token Interface for managing multiple tokens by their id in a single contract. + +[[erc6909-token-extensions]] +== Extensions + +Additionally, there is one custom extension: + +* xref:erc6909-supply.adoc[ERC-6909 Supply]: Extension of the ERC-6909 standard that adds total supply functionality for each token id. diff --git a/examples/erc6909-supply/Cargo.toml b/examples/erc6909-supply/Cargo.toml index 4d36339d0..47198a150 100644 --- a/examples/erc6909-supply/Cargo.toml +++ b/examples/erc6909-supply/Cargo.toml @@ -26,4 +26,4 @@ crate-type = ["lib", "cdylib"] [[bin]] name = "erc6909-supply-example" -path = "src/main.rs" \ No newline at end of file +path = "src/main.rs" diff --git a/examples/erc6909/Cargo.toml b/examples/erc6909/Cargo.toml index 06001f7e2..8795026b2 100644 --- a/examples/erc6909/Cargo.toml +++ b/examples/erc6909/Cargo.toml @@ -26,4 +26,4 @@ crate-type = ["lib", "cdylib"] [[bin]] name = "erc6909-example" -path = "src/main.rs" \ No newline at end of file +path = "src/main.rs" From 25220f185d8e0dc2de4aa91a4f99a0e3be31a7b9 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 7 Aug 2025 18:17:59 +0300 Subject: [PATCH 25/33] fmt --- Cargo.lock | 4 ++-- benches/src/main.rs | 6 +++--- contracts/src/token/erc6909/extensions/token_supply.rs | 2 +- contracts/src/token/erc6909/mod.rs | 9 +++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9be6cc2d5..c49966995 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1898,7 +1898,7 @@ dependencies = [ [[package]] name = "erc6909-example" -version = "0.3.0-alpha.1" +version = "0.3.0-rc.1" dependencies = [ "alloy", "alloy-primitives", @@ -1911,7 +1911,7 @@ dependencies = [ [[package]] name = "erc6909-supply-example" -version = "0.3.0-alpha.1" +version = "0.3.0-rc.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/benches/src/main.rs b/benches/src/main.rs index 0dd65d6f5..0c89495b1 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,7 +1,7 @@ use benches::{ - access_control, erc1155, erc1155_metadata_uri, erc20, erc6909, erc6909_supply, erc721, - merkle_proofs, ownable, pedersen, poseidon, poseidon_asm_sol, poseidon_sol, - report::BenchmarkReport, + access_control, erc1155, erc1155_metadata_uri, erc20, erc6909, + erc6909_supply, erc721, merkle_proofs, ownable, pedersen, poseidon, + poseidon_asm_sol, poseidon_sol, report::BenchmarkReport, }; use futures::FutureExt; use itertools::Itertools; diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index 904d9ba21..12401a567 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -252,7 +252,7 @@ mod tests { }; use super::*; - use crate::token::erc6909::{ERC6909InvalidSender}; + use crate::token::erc6909::ERC6909InvalidSender; unsafe impl TopLevelStorage for Erc6909TokenSupply {} diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs index 89bbea7fd..0ef6368a9 100644 --- a/contracts/src/token/erc6909/mod.rs +++ b/contracts/src/token/erc6909/mod.rs @@ -179,7 +179,7 @@ pub trait IErc6909: IErc165 { /// Must return true. /// /// # Arguments - /// + /// /// * `&mut self` - Write access to the contract's state. /// * `spender` - Account that will spend the tokens. /// * `id` - Token id as a number. @@ -189,7 +189,7 @@ pub trait IErc6909: IErc165 { /// /// * [`Error::InvalidSpender`] - If the `spender` address is /// [`Address::ZERO`]. - /// + /// /// # Events /// /// * [`Approval`]. @@ -206,7 +206,7 @@ pub trait IErc6909: IErc165 { /// Must return true. /// /// # Arguments - /// + /// /// * `&mut self` - Write access to the contract's state. /// * `spender` - Account that will spend the tokens. /// * `approved` - Flag that determines whether or not permission will be @@ -607,7 +607,8 @@ impl Erc6909 { )); } - self.allowances.setter(owner) + self.allowances + .setter(owner) .setter(spender) .setter(id) .set(current_allowance - amount); From 0b6266fcd9be84b864247a823f557d0eb5551725 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 7 Aug 2025 18:24:09 +0300 Subject: [PATCH 26/33] change FixedBytes<4> to B32 --- contracts/src/token/erc6909/extensions/token_supply.rs | 8 ++++---- contracts/src/token/erc6909/mod.rs | 8 ++++---- docs/modules/ROOT/pages/erc6909-supply.adoc | 2 +- examples/erc6909-supply/src/lib.rs | 4 ++-- examples/erc6909/src/lib.rs | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index 12401a567..11dcb4673 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -3,7 +3,7 @@ use alloc::{vec, vec::Vec}; -use alloy_primitives::{Address, FixedBytes, U256}; +use alloy_primitives::{aliases::B32, Address, U256}; use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ msg, @@ -237,7 +237,7 @@ impl Erc6909TokenSupply { #[public] impl IErc165 for Erc6909TokenSupply { - fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + fn supports_interface(&self, interface_id: B32) -> bool { ::interface_id() == interface_id || self.erc6909.supports_interface(interface_id) } @@ -247,7 +247,7 @@ impl IErc165 for Erc6909TokenSupply { mod tests { use motsu::prelude::*; use stylus_sdk::{ - alloy_primitives::{fixed_bytes, uint, Address, FixedBytes, U256}, + alloy_primitives::{aliases::B32, fixed_bytes, uint, Address, U256}, prelude::*, }; @@ -393,7 +393,7 @@ mod tests { fn interface_id() { let actual = ::interface_id(); - let expected: FixedBytes<4> = fixed_bytes!("0xbd85b039"); + let expected: B32 = fixed_bytes!("0xbd85b039"); assert_eq!(actual, expected); } diff --git a/contracts/src/token/erc6909/mod.rs b/contracts/src/token/erc6909/mod.rs index 0ef6368a9..a661ac5f9 100644 --- a/contracts/src/token/erc6909/mod.rs +++ b/contracts/src/token/erc6909/mod.rs @@ -2,7 +2,7 @@ use alloc::{vec, vec::Vec}; -use alloy_primitives::{Address, FixedBytes, U256}; +use alloy_primitives::{aliases::B32, Address, U256}; use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ call::MethodError, @@ -692,7 +692,7 @@ impl Erc6909 { #[public] impl IErc165 for Erc6909 { - fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + fn supports_interface(&self, interface_id: B32) -> bool { ::interface_id() == interface_id || ::interface_id() == interface_id } @@ -700,7 +700,7 @@ impl IErc165 for Erc6909 { #[cfg(test)] mod tests { - use alloy_primitives::{uint, Address, FixedBytes, U256}; + use alloy_primitives::{aliases::B32, uint, Address, U256}; use motsu::prelude::*; use super::{ @@ -1127,7 +1127,7 @@ mod tests { #[motsu::test] fn interface_id() { let actual = ::interface_id(); - let expected: FixedBytes<4> = 0x0f632fb3.into(); + let expected: B32 = 0x0f632fb3.into(); assert_eq!(actual, expected); } diff --git a/docs/modules/ROOT/pages/erc6909-supply.adoc b/docs/modules/ROOT/pages/erc6909-supply.adoc index eccadb52d..3b7de66fd 100644 --- a/docs/modules/ROOT/pages/erc6909-supply.adoc +++ b/docs/modules/ROOT/pages/erc6909-supply.adoc @@ -110,7 +110,7 @@ impl IErc6909 for Erc6909TokenSupplyExample { #[public] impl IErc165 for Erc6909TokenSupplyExample { - fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + fn supports_interface(&self, interface_id: B32) -> bool { self.erc6909_token_supply.supports_interface(interface_id) } } diff --git a/examples/erc6909-supply/src/lib.rs b/examples/erc6909-supply/src/lib.rs index 4bc871a97..a6af4c6ca 100644 --- a/examples/erc6909-supply/src/lib.rs +++ b/examples/erc6909-supply/src/lib.rs @@ -4,7 +4,7 @@ extern crate alloc; use alloc::vec::Vec; -use alloy_primitives::{Address, FixedBytes, U256}; +use alloy_primitives::{aliases::B32, Address, U256}; use openzeppelin_stylus::{ token::erc6909::{ self, @@ -105,7 +105,7 @@ impl IErc6909TokenSupply for Erc6909TokenSupplyExample { #[public] impl IErc165 for Erc6909TokenSupplyExample { - fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + fn supports_interface(&self, interface_id: B32) -> bool { self.erc6909_token_supply.supports_interface(interface_id) } } diff --git a/examples/erc6909/src/lib.rs b/examples/erc6909/src/lib.rs index c6e7bf681..a0eaabb4b 100644 --- a/examples/erc6909/src/lib.rs +++ b/examples/erc6909/src/lib.rs @@ -9,7 +9,7 @@ use openzeppelin_stylus::{ utils::introspection::erc165::IErc165, }; use stylus_sdk::{ - alloy_primitives::{Address, FixedBytes, U256}, + alloy_primitives::{aliases::B32, Address, U256}, prelude::*, }; @@ -96,7 +96,7 @@ impl IErc6909 for Erc6909Example { #[public] impl IErc165 for Erc6909Example { - fn supports_interface(&self, interface_id: FixedBytes<4>) -> bool { + fn supports_interface(&self, interface_id: B32) -> bool { self.erc6909.supports_interface(interface_id) } } From d80ea4acd48eb2f214e6e3f838a986851914da87 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Thu, 7 Aug 2025 18:28:14 +0300 Subject: [PATCH 27/33] fix ancora --- docs/modules/ROOT/pages/erc6909.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc6909.adoc b/docs/modules/ROOT/pages/erc6909.adoc index 651abe92c..465051d59 100644 --- a/docs/modules/ROOT/pages/erc6909.adoc +++ b/docs/modules/ROOT/pages/erc6909.adoc @@ -1,7 +1,6 @@ = ERC-6909 -link:https://eips.ethereum.org/EIPS/eip-6909[ERC-6909 Minimal Multi-Token Interface] -This set of interfaces and contracts are all related to ERC-6909 Minimal Multi-Token Interface for managing multiple tokens by their id in a single contract. +This set of interfaces and contracts are all related to https://eips.ethereum.org/EIPS/eip-6909[ERC-6909 Minimal Multi-Token Interface] for managing multiple tokens by their id in a single contract. [[erc6909-token-extensions]] == Extensions From 2bd8e3a2b5b662f53d5e944ffd64053d6e2745ac Mon Sep 17 00:00:00 2001 From: onurinanc Date: Wed, 13 Aug 2025 14:51:17 +0300 Subject: [PATCH 28/33] add a test case covering ERC6909InvalidReceiver --- contracts/src/token/erc6909/extensions/token_supply.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index 11dcb4673..80b36fdf9 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -252,7 +252,7 @@ mod tests { }; use super::*; - use crate::token::erc6909::ERC6909InvalidSender; + use crate::token::erc6909::{ERC6909InvalidReceiver, ERC6909InvalidSender}; unsafe impl TopLevelStorage for Erc6909TokenSupply {} @@ -319,7 +319,9 @@ mod tests { ._mint(invalid_receiver, id, ten) .motsu_unwrap_err(); - assert!(matches!(err, Error::InvalidReceiver(_))); + assert!( + matches!(err, Error::InvalidReceiver(ERC6909InvalidReceiver { receiver }) if receiver == invalid_receiver) + ); } #[motsu::test] From 7f5926c9df19bdd39fd33b96b0464c45b37fb591 Mon Sep 17 00:00:00 2001 From: onurinanc Date: Wed, 13 Aug 2025 14:53:21 +0300 Subject: [PATCH 29/33] fix CHANGELOG --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6028e312..585f95b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,8 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `SafeErc20` now implements: `try_safe_transfer`, `try_safe_transfer_from`, `transfer_and_call_relaxed`, `transfer_from_and_call_relaxed` and `approve_and_call_relaxed`. #765 - Add bidirectional conversions between `ruint::Uint` and crypto library `Uint` types behind `ruint` feature toggle. #758 - Add bidirectional conversions between `Uint` and `u8`, `u16`, `u32`, `u64`, `u128` types. #764 -- Add ERC-6909 Token. #778 -- Add ERC-6909 Supply Extension. # 779 +- Add `Erc6909` contract and `Erc6909TokenSupply` extension. #777 - Add EDDSA (Ed25519) signature scheme. #757 ### Changed (Breaking) From 13bbd20dfd62fa8e93ac8b9ddd3c18a0d375cf3e Mon Sep 17 00:00:00 2001 From: onurinanc Date: Wed, 13 Aug 2025 15:53:29 +0300 Subject: [PATCH 30/33] update ERC-6909 ancora --- docs/modules/ROOT/pages/erc6909.adoc | 125 ++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/erc6909.adoc b/docs/modules/ROOT/pages/erc6909.adoc index 465051d59..b2b54825f 100644 --- a/docs/modules/ROOT/pages/erc6909.adoc +++ b/docs/modules/ROOT/pages/erc6909.adoc @@ -1,6 +1,129 @@ = ERC-6909 -This set of interfaces and contracts are all related to https://eips.ethereum.org/EIPS/eip-6909[ERC-6909 Minimal Multi-Token Interface] for managing multiple tokens by their id in a single contract. +ERC-6909 is a multi-token standard for managing multiple tokens by their ID within a single contract, +allowing each user to hold balances of different tokens efficiently without deploying separate contracts for each token type. + +ERC-6909 natively supports multi-token actions using its built-in mint and burn functions, while ERC-20 requires distinct approve and transfer calls for token interactions. + +Additionally, ERC-6909 offers a simplified alternative to the ERC-1155 token standard. +While ERC-1155 introduced a multi-token interface capable of managing both fungible and non-fungible assets (such as ERC-20 and ERC-721) within a single smart contract, ERC-6909 streamlines this approach for greater efficiency by +overcoming the limitations of ERC-1155 by removing contract-level callbacks and batch transfers, and by replacing the single-operator permission model with a hybrid allowance–operator system that enables more fine-grained token management. + +The OpenZeppelin Stylus Contracts provides a complete implementation of the ERC-6909 standard. +On the https://docs.rs/openzeppelin-stylus/0.3.0-rc.1/openzeppelin_stylus/token/erc6909/struct.Erc6909.html[`API reference`] you'll find detailed information on their properties and usage. + +[[constructing-an-erc6909-token-contract]] +== Constructing an ERC-6909 Token Contract + +We can easily create our own ERC-6909 token contract, which can be used as a `VaultManager` that brings all token operations into one interface, +removing the need for multiple approvals or tracking allowances across contracts. +This lets you focus on your application’s core logic while it handles token management. + +Here's what our `VaultManager` token might look like. + +[source,rust] +---- +use openzeppelin_stylus::{ + token::erc6909::{self, Erc6909, IErc6909}, + utils::introspection::erc165::IErc165, +}; + +use stylus_sdk::{ + alloy_primitives::{aliases::B32, Address, U256}, + prelude::*, +}; + +#[entrypoint] +#[storage] +struct VaultManager { + erc6909: Erc6909, +} + +#[public] +#[implements(IErc6909, IErc165)] +impl VaultManager { + fn mint( + &mut self, + to: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909._mint(to, id, amount) + } + + fn burn( + &mut self, + from: Address, + id: U256, + amount: U256, + ) -> Result<(), erc6909::Error> { + self.erc6909._burn(from, id, amount) + } +} + +#[public] +impl IErc6909 for VaultManager { + type Error = erc6909::Error; + + fn balance_of(&self, owner: Address, id: U256) -> U256 { + self.erc6909.balance_of(owner, id) + } + + fn allowance(&self, owner: Address, spender: Address, id: U256) -> U256 { + self.erc6909.allowance(owner, spender, id) + } + + fn is_operator(&self, owner: Address, spender: Address) -> bool { + self.erc6909.is_operator(owner, spender) + } + + fn approve( + &mut self, + spender: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909.approve(spender, id, amount) + } + + fn set_operator( + &mut self, + spender: Address, + approved: bool, + ) -> Result { + self.erc6909.set_operator(spender, approved) + } + + fn transfer( + &mut self, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909.transfer(receiver, id, amount) + } + + fn transfer_from( + &mut self, + sender: Address, + receiver: Address, + id: U256, + amount: U256, + ) -> Result { + self.erc6909.transfer_from(sender, receiver, id, amount) + } +} + +#[public] +impl IErc165 for VaultManager { + fn supports_interface(&self, interface_id: B32) -> bool { + self.erc6909.supports_interface(interface_id) + } +} + +---- + +This set of interfaces and contracts are all related to https://eips.ethereum.org/EIPS/eip-6909[ERC-6909 Minimal Multi-Token Interface]. [[erc6909-token-extensions]] == Extensions From a50996182318632d081decbcc9abe3d77a2c5f7e Mon Sep 17 00:00:00 2001 From: onurinanc Date: Wed, 13 Aug 2025 15:53:59 +0300 Subject: [PATCH 31/33] fmt --- .lia.cache | Bin 0 -> 118918 bytes .../token/erc6909/extensions/token_supply.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 .lia.cache diff --git a/.lia.cache b/.lia.cache new file mode 100644 index 0000000000000000000000000000000000000000..53cd8c87f23b50958cde236a867e1f651041da50 GIT binary patch literal 118918 zcmeHw37i$hwfEea9&lk_6cH5=M8FL@-&~Ch#EirjB$Tz)Tq&DT%Xar#2DmDp6L6Zt-9~++n1i^-kCc+J-=VUt?KIT zs&oEz&Z$#XrzgxmV^Ce)*msWYJpJ-Zue<%%b#-;yA@xBTLuuKvyT^}j827dOEnD`B zrsjOUDL;XJHMw72Co0S52OV?eVtvY)xO*(pa!TQ+;9hxDGd*iLGOk1(j@MAB*VW$U z^^#x9o2hsc&Cu^H_~lK@Ry5-w_$QBhyN+0zs3qJeZ4P&n8_^lB@%!y{54L2ZKuZRc zXo>gI#lKOS`~tK%3TX|cFir3VC2Afdp>A@Cw;`U&CEWAg+7=-ksxSro_6dE4Ypa^* z@(!8-X^DW1U48Y<)VfXOH+~J&QtrSo&2atgEW?*}wR)V>-YsV$2`%?U1%4S< zm@W;&b#$-)M_+$iyZ1?5UERg#+AK$VamW_0Mp`1}bFuaFcL%GVJ<=mMmb_?BcfM%qh zhuS5f%+Yo+NSRYn=u_sb0A)nDh$jz568^a#yJ(J{qNrLf7*mOe@_n=p z-*BMa&WM|3m*m;U*hRsPpt68e-U7MA11?OflcsDuu7lD_C^#1sbYoB$eJ&BT1g_?F z+HR=|?E8SLV}U7;QUtE>T*3;TNwjAnDcPXXC8o+-p_jNJ0){@uSmrwGd4=9!yTa%g zp*hZsk_-@JdP(}|^b%g?O`=NqeD6u!RrJ>FiOvi}nn5Yt89c+Dixx^>@SM*493bkp z>rsYQqgquS+IWBW8x*1-dEL`>b@e?c6}Qy4V5pfwOCn1N@=DRh(k)d?TS!$ItMXf~ zIJJn|Fp8PfPA5v%tQpCfHG@}!P;AZ6_?PSv`c}GhTtN#?u^e4GLo>~Shx0AVc(}xJ zRP#HM!LO^WnBRof3KitVlr=%5WD(Qt9^xqt0e#hj$g_bmuK#5#mGJKZ=OaP%zfvkD ze885DXMg!P$BWVKGK>&%OEu+#5>m>N@IfeQd{Bm-ptKY1O;UknHZ^a7BP$bsvCGoM zrIw@2m3Eu3`Z~?x5?X7_pq%#~%8c-pM~qFv4*y1OHf5!0&BkOA`mZ}KN)O&c-utUB z#+r)$@+*FX2Ve$M{72*X&_|z)$$3;3aaP26GP8?^AhDTo<_kS1%$Twxj8LL3{lUAN z64D_Vv}QTRsO3>+)|ZSxv%Z86WP!>f`86lGEZ=#$W%(ite;Tc5i?>8C(L@i~Z@Vnz znk+||8{9+qnaebbOFjeX8E9T4=X*NVI#x5inq0Od=RYXo{9lRl>uLe-d(ACJR2Y=O}JJqvxziC=14q{yHWiGFc!BwX7^qM35LjB0)otY9gmgv)8p4 z5tz9b)PlTh_J*_^5ue+06cKCaIR__{5vNR@^lPyT>T{40dq6&HOeriQGDNfmBA|}m zG=7Wj@IEAd@4>iXl?cX&hFfJ76&PPr#X}MTW8lvkVgdt(fM~`dvjOEHv%y&stUo)D z!B}zS0bsmf{8nJRLHz!(1Ta3fiV6&pAl}yrbx8NG?%hpi5+6fiY>QdMDoUBe2v`-Z zU{3-7&l_JA0Iv~WN0b0SNh4xHLdnqW^)q7vAd?KDGO9C`1OOf@0>HBbfY(X@;15<& zB>>Gcyw4I!fIU7x_*|6$Z$knM$LRkvrLY9Z0MPaTP#w&sDNF_YpOEdtZu(wKwr8ed z3{z3~B5YBLC1A)}1(a+?tqh$fQ6FDNu120^ks1!WaREmP?04%lElZZGpc*I_v_;(1}u5 zI%IHY3pl8jiSjyE3*_Kf*>)EoE;^1SzB&LvmwqCEROWO6_7>t#x<&NH{o z0c6$*RI)w@z!8@sg;()7T-HhV3o6EQ~Nu3#+7@Z)CFZd~+?)PGvdO5HYBSKluIP@VBG=^|;J$;1 z*U+|dkw4R{CZX6=iSFrCRe}r0X)UT`Vny0`y4^3?^E6u;?!J82 zVJ-6eCgX&@cLnm^J=o2=`LuIZ)h=~G3lk$m7h^ADXNr+U7~sSRbHIrau6m4-Osam< zvh>WVQunAQ*L+Kh zSAi(7>~ke7dw||=k-UZq=p~A9^ap#OsH8RtBaM}5MKc+MATS*gC?Lq3uLisj7>6rC zjE^X-8&bp`Vybx?IT~&1z~d+=Rd6N%Ka{fGj!JhCJ2nnw2&p;;!QC*FYHk=3{ATGB z=y!zCET+Retyo&7k@w0sRlF;Tlm?!TR>vL)j zL|w4`2Z!mHZ|?`9YGKT`_9C_TO+KRGC?k!Duucb}Hkv*xCL%IfP=ni71QBi}L}lI@ zASCgDsM+Isza}!GzP%rass-(!GJ-4w8MSO8tiusGXsQ_d+XNV^?4=5fbvh8$^M^0S z1O^O&$cad?+2AY*ViwC7x9Tm%T0tlS=ZparY(*`2j@mpzI-zir5Vj0-5(18-Qh9(R zFBreIi*Jx1sQQ7ZMLRalP^+T7ABd_2i6LgR%p^!`upJtnVf zKqeWgxd#do;ISeJ@GMDysvn3Ny#3kNssw272cl|0H3TI4U_VZA;Aj)b@F}X4;|4_Y-!z<)O`R-hHz& z+mYNlI^$1jM2h7w`oMy3;YeCRMj?($Td6oCL0&M%Z=;uAlplz8{M%ZR5l^dbCwy> zDDz{>2wd)#EH2z<7x;D5-=LXB`xA7xf)O=h^mCU#D%wU#g?J=WU^TmF@=eRjS$sjI zpiy`8`3dKJxt_{_*Rekh+pp@Mp_BoGx6X)gs6H@=i%nj$p&Ig>3vQ-Ca0|C24n#gJ z1CcKUBKc$Hy`~$O>k$KwKKIdZ9HrJ{IBa6YE`|t}19_KHk$7NwL<SHB1}vQ@H2j{*GN3v zYxOAepk`DlJ#JYMInTK)J;ezOsod->j}RErG^%d$gkJS^G#VOn-31QiBnBQ3#hn*QW<0b`UTq*p#)V=`eLG-so@RCi>bl#f}RENZ>{4;g^M=| zjW@Al19(r|S5W3x!4nSR9*K#8C+juuC@m&=JzriTEa$g-=c`e|cjzFN`NBm+?kh`O zX29TlJ?^2+nId)|`h_ekuWoaKfkyUSc8%{;`GWEXtX!lCRu#o!ktgDyy=3VTFIh&a z{=x2~Ywx;i{QOTeOP8=~OT70~TAzirY72#mP)v8ZgQjdx3v~Dm73X`U+40u5>bR^YbTcpx}HJ;QU=( zop%AH44fc6alkx_X$B{2VxC~quyf|#78q)cxj_MQ4>~s*rs8Q@`)s4w>%nL(gr!AX z2t-f{Vp@NPt_B`r)z!L`cjOly^3=YH_E&)Rmw@#1DP?HS&>loPi`~rt_7K`h8k1Cp zXn=&TX|V75>CyWu*e?axuYt{h&oMNV{TxGO=Tn*8QM+~rLwu9c5Z|f*;t^Y*MZzXD zJ2w4iFZ+CRYe!q+F%4r_4Kz0EcCZ|%;*Hh}{oYf5iwQZ&a&&2`WbkXc7OVYuxYbg# zmK>RI;oiOHvW+59DZRzmIS3D^M$x%$XHjcI=G}@_< z;bSdJu&}dr^xJ}R@ifc9gb`;^&jeV<$P;jjEv~DR+Fx8R)I@w6dAspwvd{RL38F1c z-}NSoqEZ9VmfD=UG*p+mp~^(&Z@{i&w51U}2RAB)YarTEty7ssPJ(DlI}PgDS&3W& z(U#hr$SPcm1qBf|LA0g)-hNv{S=DE&j;hziBaqN7+S3Pwec}Fa zP|4V?2CDV6UEq6c*-;kBanM;EdvDvb1epKjvaC63bJcOiloK^bU46Fd*a1KnV?l^8 z-k7W8s^d0;KAkFRqdr@8%s@u#7>uz>t~zeH@`M8vW7(=>2f$b@tU3<7?oo@Q`zE}9 z^ueVMC{@PQ4ZEW!&69X-fK7=0nhmU)cw3oiqCCf4qpXPS5O`5hW=Al~wYm~XdGGdv1*RuQyZ0%D!YMGYf7Ip!% zi<5q-sm6EP75I;}=XTk8Efe+^ZK|oOhS((5?0r;brLV*Shg;~tOZz>%l|`n096av< ze&0_i%yU$RwnejrgVGyNVMCPA8j!UUMH@*Geqqrv$nha1IPM&2KF9a4mjs?U#mbCa zc=`-iM(mhvIRU^N&2oM3G?!x@pKV#rEp~S@>n^j4^wg{D8fCs`8F}arEX%j8kQ}Pr zspUc=CXetw3CD@UOkm~s2grk_W#UZGk}oaPZhFaESLyWjDe$%@_=B*|ke0~>kw+nj581C5P8GK%0R8x9jhk3KNlAUXYQX3m(2Q_A31uTA(p7USK;@|-h z8@K#FG5EN}ztn9axfj4-Iwp^3m$Zqd9#J(4~2`0cqc)x5F zbtq$_NVE6nwez|6Pv5eGfj9`1>jfS^L@CT?f6x{T<4j1ae!`G7wH#MSgTe5f$oNzS zKaac!O4-VTi^5=HXi!$eXVUETk0YxO+hOH2mDTTo#lykgPbh_1oXKj^#1*!^@WhqI zJ7qF4-FuR8Qk|b>HxAbKD~~ciG7xZUyofJ+CX|qlV!LW z7DGY1f~W@05$M@@8R?Z$s=%QoROOkkk;FQz=C&`g1M_2$&Mq4*WFGyQO|gIqYU}?X z-ht%sMdh`_`U>7*P)F6SgC54nVC z=7V>HjB9O8e+wYvAfRG@N*OX#l2id1Dr+7!=E+!p8d(;$6&5NKETae|-B937EGJbA zB?Yc`$|!91ocNB#@EQx)oC?U`;6%fvVc0-maEGKkQhBJ&EMp^IjC@br) zm^E|L4b|8o9L)rVj-V9AQD*Fbg%vS&;;1yn(JKa+Hyq;$NAFtkM}(uzT8yK{9JXT= znnHc)N&7R7BH;qeMSQdZ&ssQRH$6^Mzm3YH3`a5>Y<;7d!SkPn61SktYVu7h{u5X( z7DkJvU5&X;#o*~d;MqPYc+A4i=j(>vsfUK@_XLJch8((xQdkycb`r!ubtmeKp~nm? zzjZt(0aO4j@0}J4An*UX$EhN#M*)J4l~-i2txEi@W$)VbnHx+}1ve3(It75bj8X=w z>KRHpO;J|}SZo$|QcwYNB(4HB62(br_Et0WaDnZ4aSQ#+Dw-)yGrGoHk1AlR%W1Qw zD7JP2w!Q&eMTBI{j3=xfAn@Pg{rPr(@?cBRh^a;RX|AZ zfv0{u04I`6b0}q6uX=|cmF3aP&lM!pQg3E`uQCU@(jfWm7=??o!RFakI= z*6s0>!f67e(_BF53Q8HA+7?ct49)!Ei6*!u2A6+G1mM9jU0mSBC|(Tun835D1bAA@ zGIy(OV?O@rGYYM7fYvtwtBWaxp@k0cJ~woyfx1JjR}&LOaMNoC#TZ0Uw%W!FCIhH& zwJna~iw&*66hUh>L2KJEw4xjblgr7aa)PO>KWg*qFEs_Y0bqr7mxjTVGFYj^s=`uT zq-h=-OXScDiYZ#+S05EF_>;WGT+V8m$wV&Aa=Yl^IVfDrJ?H*2E>poni@DQ*4uq;{ zI3SD=G@oBBi$+C)2N$*9+CYr9Y*o$)p2Af*rBv84AQz0qWfYn7gsTz5NY1(U&|k@FrrTxY^v*| ztR!soSzw!tb9k*18q5wWgP!|0^$+E~0#?QVCmT}=VJFDoE)d0$s!83>DQgOV|G0+Ig1+>jk_yG)G!ODU2ArH`6&4 z(K&IvIk81bjOlmF5EHQyUv9eO?;VH;tjlWsw^brqTsx}ekRk;%(eb(|-ugra; z4)e;N>Z$I&jtKa`^H=IGiD?9JwAKI2TdH^(1w7Ge>DiRR8V$|KZ0w5I*uvPL$3=JM zC&iOK&~2*L^J(TgZWSQy{?)+C6-WaCq=Nw>@-0sf11U3&DrFj#2U3DfLCJuWz!35k z+NhDQ+9{zSzzt(2!wq@%+tw|V)TC}T2tkbO_SKYqROS$NNaoO#Y(`k-pjDajs)+K6 z;~-+}B$&}B!%n0@6e*Wz9=7EFjtHJ4I|;Zsk5Y!6w)F-H2G+JDT4AJ=hES}9(Sc)^ z*hw9%pjfz z<(84h->KP(aQqLp%TD3>0rJ=&87ceyMd+=+d)ddz+o!MI26fW7^D{gwvZ&wTk3}VDN72)l|1n5wvFy(RL&R;m` z#-A#mp8=oK5qF-r+#ZB9n)$eiU=2zByog}uH+2J<7#iGMA3yH=gs0E`iL&@Tuy{Cz z@+Xv{4RqE5Xo9*gG(I&4UvovaO&)#dNllYY+(h~Qnq^A_;o7KdE2qVfLqAY7%8G2i zILh|4GFEm{Z(mnNcTKk(E5{m;)#M~ZL!)gkG}1@(BO1y**8AYgdj4df;?TDCAE3CgN_$Yr!?+7@%%`gO{vr1M4Mdk zoNb=Vadx3)dA{ad9|LEQP>Hyf(PT|I>=I0%cQv`CtsxbroL zj8@xP!62zh-1)8gpLM$m6vRJCVcdCwO^qp_IJ*MX1`(OFZnEWzV%buMf@;Q{U$SI7 zQ&JQ!JbiKJ+bO21w9`~H*qUOLb!L>UOWgSl9_oLR$|k~5`r^*FQ9e9cLY#jeo>2Y=eyoD@kffSoq(Ss@(EsZ918_=T z-1!>RdU0G%N!q@Lf6!jX z4^wE3gW^kH-1&B@_+mq=Hsj9kaA2b;z;wo)uR&Nv`VwMeDMrX&HJR2u?)-Z5{{F5C z9$L($I4|jnJ70r3ZQT*l=5goi*DN!CQaa<#cU0J@km9`|x`!ls^M2VqXD;sim|wlB z{-Kn{o$shhsAX6w3bQcx`Sngz#gops^EHTyIIf=9R!R(uHyJ>p;YO;{zPR)46i(H34z?6vWsfMmDAKqwL^u%#ugYO!LYc!ztO{GQFf(;0WZ1|cL23{G)^ zfu0lW7>c)eS~=YKVR}0+IqMyjKf4RsqtMzxzM$DUi|`cfKR&wPZj_UY` zXs3iIc6j>Y&etGr;<$y9n$)cZAtBBZ>QK%V+E}TlaiiJ)Q1FTd}SB4N<%1CVBoGx-n5fs>5MzyPW4wC z;3&%N)1-mM&)u@Ks;iX7ov%Tts7k3)goT#L8N-Ld;escxxmB%~bjF>pK@C((aEPT| zF1X;JJp?3mXVlfzA&^4dGD>TWeabOU1F=Mi85`gqXjQ014uwiZeI+~zej(~9$3Ugh zf%Ka+rkGGfZM-Z+b@3j{EFMIN0UU? zqf0m*Y=8M>n2=I0$G$cC3hm{6=;d7WkhFU5a4*p+G=iGv)pO`r%5u-G#g*;lUd6pc zSXu3*>j!m1`$s$;>us-}bgM%w&AKzukwKKI-x-cv;|)d&eZF`jk@dzDF>mkUZcVdp z5lf75fT4AaxRkqOlP8r+zd^^&L8nGg3U^F6f`2Q7#psUhR@5;JpF&g~;CC!IF)2o6 zzSGD-odXa&4IS%AsU|uX9E;u*NRr}c`}x2aL*?UN-sC%h&YX&l;8Q!f5}(?s^mvNq zl!IVFXC|+`Gu^Jb_$lqoU4R{?jJZZiVa`;#Gr^Vu%f1KOijqU@6jrF1IC>c5L=*ilT1?b0KNVicc(GME^qFjnaG*k_r z>gSCM zo`Ae}67|?gW60zp%LO8Nnd(-jeW+Hxe$K^<;}OOu0(ikgmrJ}23&?`9Lb8AzL9%RZ zStiR4MhGU$7|Rh^U@|Z@OOR!cQe@eO9vmRc!N_}uFj;~qWzv||B733~S%@$_I?dfv z(Yp$WxejO{C3a32F>Oj1W<--W8oe#(J}GTq2Jz8^Chx!^8X>-Uvo{+wGAmM)d5A{& z17DkHChlqw;|37od`e+rfN~602Lj0kEK42&)fzrZ4l*wb$T71NIgTqL2R^dX?9F3x zIE0iMN%{Tm`Qt*x#xFpQV?l~~N@d7lSa$RwtODli!^SOuxY5h&MAotcnk`idc%uX>hMqNH?trX(EVeL78n)CT98Q% z99jCCSzPlHy^)3!ZcM>9vyOjc*Q=H1j{~(m@SToyeKSiqerh4d7q~3rVX?85xw_PH z#NsP0BOGs6-^`ly<$v$5yiMyjv)XVx0~|3z_=}YGBfyFsLkh_^4UQ!=X$Y9DrH{JpEER%xw}px7jzdF6{Y=N%6FP zGpqfxUT0e@4HZ8$Qw~fG-E=uLOAK7$c~+mgO`lb9{wTyb{CV~FP|C!)sL86@-0{SD zOW2)f6wm~Ei&=x$SH`CGn^|o*lEJwc1~+QOu*V|!TjU({2QG1U_=MLsQ0}Jnn^`r_ zUEy2gbSep3yM4LfIb~})znLXWtxe-P89z&EU0)0zdeo=SC`Vs{e%%tf^$kj4{fgI% zVTk#7!9oTuSkv5u2kT8bVQTyD&$i(}U8^l^gwv^_ncC*^kjpXyURO)%gjJvXMmXI8 zzL_O6ajft$RMmxG-^>z^k{9vKET+BpZM_x4ho<5k@S9nk&dmQ-@j`1fyW{lS07_xW z0A%tq73kDp?h)Vj(M_cT4@0iXtIoXQNTeO;ZedDeeWpOZ=$?G6(+Mm62V3Mx0|!s!V`C46~%i3wT;tm2YNk{*5TpK)rd|jkD&x%aN=OdYpuxqm=1jN`3)NsR1=}#(*4b>w1NuaEJkc4F_Qj8 zeiU}Hut!2>bd9+l#k+Lv?@Jds|LQA)o`+fqBiDmR<_xj#u6)XwK-C1 z(K7U5U(DB&RP_X>bbm9eMzmfWms3*r0N~V?-^|+V#}8bt(kcDl%xXt)5@Xwh2}?>0 zewhiv53Vyqfah(KFe-~aCIPc5EQ?yrGI!gdrZ*4Q!^HLJ|7KQ=*idmCP_d!)mm+Ab zCTMLNhE`*)wBulMIm5j8RBF&@FRK6#9R0N^!1RAJt43fI=}U->rPlmr*05QhPE?(i z{%>a0h)!E~guGECZQdto^S7|H@jkbTx-o&)P8xITmlHNcC*Xs!#@{#A_kn5VPfGtc zvpN!NR7gRq$ZkLEmWkfHUrsOiSlNHUx70t>0lt~lk(5x&uww2-4H4JnmpKh|RNo(S zbNW!@WJ;NaJC^}nA8I0w`H&8!;HIdQx>u@Mtv`rR_b zBw4U1Bc!Ku2=!IlCn((z3G8Zmt0U`H@uW}z8< zWV5hLG!sWOANPw!v|cuQP#+T1jAa+KN<*le#pAq+Gf!G*C&>_FGB*|SCr>FXgIN5q zAj<3>F-DUfn~&oXY6BeEf>&sIrt2AJo_?sRs}AtZED$2{ z{iCPWOZvZ=RU;axmf#Rey^LBp{{{ugkx(yZLABgLDXd=5HFeDt#8WB$pa5$j`aXyr z7LuD_4`TB@72(%_ym)CyvGTJ}wzWN+^f2YtDG=Ppy#xJIn zfi9h(gN$g*;Tv``U}Ud6O2}v1nCmNsHzTZ9Irt4$_$?#&*>{>4Q^D_o=|{El`G&9l z@v?#pMssd90Jexy2C@zdGR9a>(kvp@d>Kc=P1S&UuVZ0sgYT_+RWSx3mOB&px}8#n zv2vS*kxbFKh>Dqw8)GDE{2ULPA+&fKF95H{up1F1pC2L7tdC(Ukb2a1DoT|lrpe)Ag~u1 zz`otV5Z|OU#J4Jd__A_BIHB3G&z~>WsPlF6@f}c{c&9H5*WDV%CUh`aKjPIxZ`7_r z^F25y#27;zWP2d1W+T4WrLNC_-Q;TrRvcvgjQY+k`^gaDX3&S0ms3jI3^sic@!lFD zE$dGWb@p3fJE$4E-TAnFNTl2Pu_ohVQ>cpK+92-lc*HMf7%Y$W6hds(y6uq8r4+wucm+9^ts%YWx4q6tVQ>3i DAL^BG literal 0 HcmV?d00001 diff --git a/contracts/src/token/erc6909/extensions/token_supply.rs b/contracts/src/token/erc6909/extensions/token_supply.rs index 80b36fdf9..b2d72afa2 100644 --- a/contracts/src/token/erc6909/extensions/token_supply.rs +++ b/contracts/src/token/erc6909/extensions/token_supply.rs @@ -321,7 +321,7 @@ mod tests { assert!( matches!(err, Error::InvalidReceiver(ERC6909InvalidReceiver { receiver }) if receiver == invalid_receiver) - ); + ); } #[motsu::test] From 1faf35e6cb30de1ce546d4887de760291b45c65a Mon Sep 17 00:00:00 2001 From: onurinanc Date: Wed, 13 Aug 2025 15:56:10 +0300 Subject: [PATCH 32/33] remove unnecessary files --- .lia.cache | Bin 118918 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .lia.cache diff --git a/.lia.cache b/.lia.cache deleted file mode 100644 index 53cd8c87f23b50958cde236a867e1f651041da50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118918 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 DAL^BG From 2801d86f63f27b9be2777fdf004651ea037a07be Mon Sep 17 00:00:00 2001 From: onurinanc Date: Wed, 13 Aug 2025 15:58:21 +0300 Subject: [PATCH 33/33] fix erc6909 ancora --- docs/modules/ROOT/pages/erc6909.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/erc6909.adoc b/docs/modules/ROOT/pages/erc6909.adoc index b2b54825f..7659ee86b 100644 --- a/docs/modules/ROOT/pages/erc6909.adoc +++ b/docs/modules/ROOT/pages/erc6909.adoc @@ -6,8 +6,7 @@ allowing each user to hold balances of different tokens efficiently without depl ERC-6909 natively supports multi-token actions using its built-in mint and burn functions, while ERC-20 requires distinct approve and transfer calls for token interactions. Additionally, ERC-6909 offers a simplified alternative to the ERC-1155 token standard. -While ERC-1155 introduced a multi-token interface capable of managing both fungible and non-fungible assets (such as ERC-20 and ERC-721) within a single smart contract, ERC-6909 streamlines this approach for greater efficiency by -overcoming the limitations of ERC-1155 by removing contract-level callbacks and batch transfers, and by replacing the single-operator permission model with a hybrid allowance–operator system that enables more fine-grained token management. +While ERC-1155 introduced a multi-token interface capable of managing both fungible and non-fungible assets (such as ERC-20 and ERC-721) within a single smart contract, ERC-6909 streamlines this approach for greater efficiency by overcoming the limitations of ERC-1155 by removing contract-level callbacks and batch transfers, and by replacing the single-operator permission model with a hybrid allowance–operator system that enables more fine-grained token management. The OpenZeppelin Stylus Contracts provides a complete implementation of the ERC-6909 standard. On the https://docs.rs/openzeppelin-stylus/0.3.0-rc.1/openzeppelin_stylus/token/erc6909/struct.Erc6909.html[`API reference`] you'll find detailed information on their properties and usage.