From 8d47251ceb811f4056eed07267a2ef9687ea088c Mon Sep 17 00:00:00 2001 From: 0xNeshi Date: Wed, 11 Dec 2024 15:27:36 +0100 Subject: [PATCH 1/2] refactor permit --- contracts/src/token/erc20/extensions/mod.rs | 2 +- .../src/token/erc20/extensions/permit.rs | 170 +++--------------- examples/erc20-permit/src/lib.rs | 33 +++- 3 files changed, 51 insertions(+), 154 deletions(-) diff --git a/contracts/src/token/erc20/extensions/mod.rs b/contracts/src/token/erc20/extensions/mod.rs index beaa80ecb..eed27dd38 100644 --- a/contracts/src/token/erc20/extensions/mod.rs +++ b/contracts/src/token/erc20/extensions/mod.rs @@ -7,4 +7,4 @@ pub mod permit; pub use burnable::IErc20Burnable; pub use capped::Capped; pub use metadata::{Erc20Metadata, IErc20Metadata}; -pub use permit::Erc20Permit; +pub use permit::{Erc20Permit, IErc20Permit}; diff --git a/contracts/src/token/erc20/extensions/permit.rs b/contracts/src/token/erc20/extensions/permit.rs index 64af6492a..e517a7abf 100644 --- a/contracts/src/token/erc20/extensions/permit.rs +++ b/contracts/src/token/erc20/extensions/permit.rs @@ -14,6 +14,7 @@ use alloy_primitives::{b256, keccak256, Address, B256, U256}; use alloy_sol_types::{sol, SolType}; +use openzeppelin_stylus_proc::interface_id; use stylus_sdk::{ block, prelude::StorageType, @@ -22,7 +23,7 @@ use stylus_sdk::{ }; use crate::{ - token::erc20::{self, Erc20, IErc20}, + token::erc20::{self, Erc20}, utils::{ cryptography::{ecdsa, eip712::IEip712}, nonces::Nonces, @@ -68,12 +69,8 @@ pub enum Error { sol_storage! { /// State of a Permit Contract. pub struct Erc20Permit{ - /// ERC-20 contract. - Erc20 erc20; - /// Nonces contract. Nonces nonces; - /// EIP-712 contract. Must implement [`IEip712`] trait. T eip712; } @@ -84,18 +81,16 @@ sol_storage! { /// BorrowMut)`. Should be fixed in the future by the Stylus team. unsafe impl TopLevelStorage for Erc20Permit {} -#[public] -impl Erc20Permit { +/// Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in https://eips.ethereum.org/EIPS/eip-2612[ERC-2612]. +#[interface_id] +pub trait IErc20Permit { /// Returns the current nonce for `owner`. /// /// # Arguments /// /// * `&self` - Read access to the contract's state. /// * `owner` - The address for which to return the nonce. - #[must_use] - pub fn nonces(&self, owner: Address) -> U256 { - self.nonces.nonces(owner) - } + fn nonces(&self, owner: Address) -> U256; /// Returns the domain separator used in the encoding of the signature for /// [`Self::permit`], as defined by EIP712. @@ -104,11 +99,22 @@ impl Erc20Permit { /// /// * `&self` - Read access to the contract's state. #[selector(name = "DOMAIN_SEPARATOR")] - #[must_use] - pub fn domain_separator(&self) -> B256 { + fn domain_separator(&self) -> B256; +} + +#[public] +impl IErc20Permit for Erc20Permit { + fn nonces(&self, owner: Address) -> U256 { + self.nonces.nonces(owner) + } + + #[selector(name = "DOMAIN_SEPARATOR")] + fn domain_separator(&self) -> B256 { self.eip712.domain_separator_v4() } +} +impl Erc20Permit { /// Sets `value` as the allowance of `spender` over `owner`'s tokens, /// given `owner`'s signed approval. /// @@ -158,6 +164,7 @@ impl Erc20Permit { v: u8, r: B256, s: B256, + erc20: &mut Erc20, ) -> Result<(), Error> { if U256::from(block::timestamp()) > deadline { return Err(ERC2612ExpiredSignature { deadline }.into()); @@ -180,143 +187,8 @@ impl Erc20Permit { return Err(ERC2612InvalidSigner { signer, owner }.into()); } - self.erc20._approve(owner, spender, value, true)?; + erc20._approve(owner, spender, value, true)?; Ok(()) } - - /// Returns the number of tokens in existence. - /// - /// # Arguments - /// - /// * `&self` - Read access to the contract's state. - pub fn total_supply(&self) -> U256 { - self.erc20.total_supply() - } - - /// Returns the number of tokens owned by `account`. - /// - /// # Arguments - /// - /// * `&self` - Read access to the contract's state. - /// * `account` - Account to get balance from. - pub fn balance_of(&self, account: Address) -> U256 { - self.erc20.balance_of(account) - } - - /// Moves a `value` amount of tokens from the caller's account to `to`. - /// - /// Returns a boolean value indicating whether the operation succeeded. - /// - /// # Arguments - /// - /// * `&mut self` - Write access to the contract's state. - /// * `to` - Account to transfer tokens to. - /// * `value` - Number of tokens to transfer. - /// - /// # Errors - /// - /// * If the `to` address is `Address::ZERO`, then the error - /// [`crate::token::erc20::Error::InvalidReceiver`] is returned. - /// * If the caller doesn't have a balance of at least `value`, then the - /// error [`crate::token::erc20::Error::InsufficientBalance`] is returned. - /// - /// # Events - /// - /// Emits a [`crate::token::erc20::Transfer`] event. - pub fn transfer( - &mut self, - to: Address, - value: U256, - ) -> Result { - self.erc20.transfer(to, value) - } - - /// Returns the remaining number of tokens that `spender` will be allowed - /// to spend on behalf of `owner` through `transfer_from`. This is zero by - /// default. - /// - /// This value changes when `approve` or `transfer_from` are called. - /// - /// # Arguments - /// - /// * `&self` - Read access to the contract's state. - /// * `owner` - Account that owns the tokens. - /// * `spender` - Account that will spend the tokens. - pub fn allowance(&self, owner: Address, spender: Address) -> U256 { - self.erc20.allowance(owner, spender) - } - - /// Sets a `value` number of tokens as the allowance of `spender` over the - /// caller's tokens. - /// - /// Returns a boolean value indicating whether the operation succeeded. - /// - /// WARNING: Beware that changing an allowance with this method brings the - /// risk that someone may use both the old and the new allowance by - /// unfortunate transaction ordering. One possible solution to mitigate - /// this race condition is to first reduce the `spender`'s allowance to 0 - /// and set the desired value afterwards: - /// - /// - /// # Arguments - /// - /// * `&mut self` - Write access to the contract's state. - /// * `owner` - Account that owns the tokens. - /// * `spender` - Account that will spend the tokens. - /// * `value` - The number of tokens being allowed to transfer by `spender`. - /// - /// # Errors - /// - /// If the `spender` address is `Address::ZERO`, then the error - /// [`crate::token::erc20::Error::InvalidSpender`] is returned. - /// - /// # Events - /// - /// Emits an [`crate::token::erc20::Approval`] event. - pub fn approve( - &mut self, - spender: Address, - value: U256, - ) -> Result { - self.erc20.approve(spender, value) - } - - /// Moves a `value` number of tokens from `from` to `to` using the - /// allowance mechanism. `value` is then deducted from the caller's - /// allowance. - /// - /// Returns a boolean value indicating whether the operation succeeded. - /// - /// NOTE: If `value` is the maximum `U256::MAX`, the allowance is not - /// updated on `transfer_from`. This is semantically equivalent to - /// an infinite approval. - /// - /// # Arguments - /// - /// * `&mut self` - Write access to the contract's state. - /// * `from` - Account to transfer tokens from. - /// * `to` - Account to transfer tokens to. - /// * `value` - Number of tokens to transfer. - /// - /// # Errors - /// - /// * If the `from` address is `Address::ZERO`, then the error - /// [`crate::token::erc20::Error::InvalidSender`] is returned. - /// * If the `to` address is `Address::ZERO`, then the error - /// [`crate::token::erc20::Error::InvalidReceiver`] is returned. - /// * If not enough allowance is available, then the error - /// [`crate::token::erc20::Error::InsufficientAllowance`] is returned. - /// - /// # Events - /// - /// Emits a [`crate::token::erc20::Transfer`] event. - pub fn transfer_from( - &mut self, - from: Address, - to: Address, - value: U256, - ) -> Result { - self.erc20.transfer_from(from, to, value) - } } diff --git a/examples/erc20-permit/src/lib.rs b/examples/erc20-permit/src/lib.rs index b4e9ef425..0fb01f0ca 100644 --- a/examples/erc20-permit/src/lib.rs +++ b/examples/erc20-permit/src/lib.rs @@ -3,15 +3,18 @@ extern crate alloc; use alloc::vec::Vec; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, B256, U256}; use openzeppelin_stylus::{ - token::erc20::extensions::Erc20Permit, utils::cryptography::eip712::IEip712, + token::erc20::{extensions::Erc20Permit, Erc20}, + utils::cryptography::eip712::IEip712, }; use stylus_sdk::prelude::{entrypoint, public, sol_storage}; sol_storage! { #[entrypoint] struct Erc20PermitExample { + #[borrow] + Erc20 erc20; #[borrow] Erc20Permit erc20_permit; } @@ -25,7 +28,7 @@ impl IEip712 for Eip712 { } #[public] -#[inherit(Erc20Permit)] +#[inherit(Erc20, Erc20Permit)] impl Erc20PermitExample { // Add token minting feature. pub fn mint( @@ -33,7 +36,29 @@ impl Erc20PermitExample { account: Address, value: U256, ) -> Result<(), Vec> { - self.erc20_permit.erc20._mint(account, value)?; + self.erc20._mint(account, value)?; Ok(()) } + + pub fn permit( + &mut self, + owner: Address, + spender: Address, + value: U256, + deadline: U256, + v: u8, + r: B256, + s: B256, + ) -> Result<(), Vec> { + Ok(self.erc20_permit.permit( + owner, + spender, + value, + deadline, + v, + r, + s, + &mut self.erc20, + )?) + } } From a30c055068fbac7cf12f63a3c517273e191a05d2 Mon Sep 17 00:00:00 2001 From: 0xNeshi Date: Wed, 11 Dec 2024 15:29:34 +0100 Subject: [PATCH 2/2] add too many args attr --- examples/erc20-permit/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/erc20-permit/src/lib.rs b/examples/erc20-permit/src/lib.rs index 0fb01f0ca..6fc7b8350 100644 --- a/examples/erc20-permit/src/lib.rs +++ b/examples/erc20-permit/src/lib.rs @@ -40,6 +40,7 @@ impl Erc20PermitExample { Ok(()) } + #[allow(clippy::too_many_arguments)] pub fn permit( &mut self, owner: Address,