Skip to content

Commit

Permalink
refactor permit
Browse files Browse the repository at this point in the history
  • Loading branch information
0xNeshi committed Dec 11, 2024
1 parent 49ff333 commit 8d47251
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 154 deletions.
2 changes: 1 addition & 1 deletion contracts/src/token/erc20/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
170 changes: 21 additions & 149 deletions contracts/src/token/erc20/extensions/permit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -68,12 +69,8 @@ pub enum Error {
sol_storage! {
/// State of a Permit Contract.
pub struct Erc20Permit<T: IEip712 + StorageType>{
/// ERC-20 contract.
Erc20 erc20;

/// Nonces contract.
Nonces nonces;

/// EIP-712 contract. Must implement [`IEip712`] trait.
T eip712;
}
Expand All @@ -84,18 +81,16 @@ sol_storage! {
/// BorrowMut<Self>)`. Should be fixed in the future by the Stylus team.
unsafe impl<T: IEip712 + StorageType> TopLevelStorage for Erc20Permit<T> {}

#[public]
impl<T: IEip712 + StorageType> Erc20Permit<T> {
/// 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<T: IEip712 + StorageType> {
/// 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

Check failure on line 95 in contracts/src/token/erc20/extensions/permit.rs

View workflow job for this annotation

GitHub Actions / nightly / doc

unresolved link to `Self::permit`
/// [`Self::permit`], as defined by EIP712.
Expand All @@ -104,11 +99,22 @@ impl<T: IEip712 + StorageType> Erc20Permit<T> {
///
/// * `&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]

Check warning on line 105 in contracts/src/token/erc20/extensions/permit.rs

View workflow job for this annotation

GitHub Actions / nightly / doc

unexpected `cfg` condition value: `export-abi`

Check warning on line 105 in contracts/src/token/erc20/extensions/permit.rs

View workflow job for this annotation

GitHub Actions / ubuntu / beta

unexpected `cfg` condition value: `export-abi`

Check warning on line 105 in contracts/src/token/erc20/extensions/permit.rs

View workflow job for this annotation

GitHub Actions / ubuntu / beta

unexpected `cfg` condition value: `export-abi`
impl<T: IEip712 + StorageType> IErc20Permit<T> for Erc20Permit<T> {
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<T: IEip712 + StorageType> Erc20Permit<T> {
/// Sets `value` as the allowance of `spender` over `owner`'s tokens,
/// given `owner`'s signed approval.
///
Expand Down Expand Up @@ -158,6 +164,7 @@ impl<T: IEip712 + StorageType> Erc20Permit<T> {
v: u8,
r: B256,
s: B256,
erc20: &mut Erc20,
) -> Result<(), Error> {
if U256::from(block::timestamp()) > deadline {
return Err(ERC2612ExpiredSignature { deadline }.into());
Expand All @@ -180,143 +187,8 @@ impl<T: IEip712 + StorageType> Erc20Permit<T> {
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<bool, crate::token::erc20::Error> {
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:
/// <https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729>
///
/// # 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<bool, crate::token::erc20::Error> {
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<bool, crate::token::erc20::Error> {
self.erc20.transfer_from(from, to, value)
}
}
33 changes: 29 additions & 4 deletions examples/erc20-permit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Eip712> erc20_permit;
}
Expand All @@ -25,15 +28,37 @@ impl IEip712 for Eip712 {
}

#[public]
#[inherit(Erc20Permit<Eip712>)]
#[inherit(Erc20, Erc20Permit<Eip712>)]
impl Erc20PermitExample {
// Add token minting feature.
pub fn mint(
&mut self,
account: Address,
value: U256,
) -> Result<(), Vec<u8>> {
self.erc20_permit.erc20._mint(account, value)?;
self.erc20._mint(account, value)?;
Ok(())
}

pub fn permit(

Check warning on line 43 in examples/erc20-permit/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] examples/erc20-permit/src/lib.rs#L43

warning: this function has too many arguments (8/7) --> examples/erc20-permit/src/lib.rs:43:5 | 43 | / pub fn permit( 44 | | &mut self, 45 | | owner: Address, 46 | | spender: Address, ... | 51 | | s: B256, 52 | | ) -> Result<(), Vec<u8>> { | |____________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments = note: `#[warn(clippy::too_many_arguments)]` on by default
Raw output
examples/erc20-permit/src/lib.rs:43:5:w:warning: this function has too many arguments (8/7)
  --> examples/erc20-permit/src/lib.rs:43:5
   |
43 | /     pub fn permit(
44 | |         &mut self,
45 | |         owner: Address,
46 | |         spender: Address,
...  |
51 | |         s: B256,
52 | |     ) -> Result<(), Vec<u8>> {
   | |____________________________^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
   = note: `#[warn(clippy::too_many_arguments)]` on by default


__END__
&mut self,
owner: Address,
spender: Address,
value: U256,
deadline: U256,
v: u8,
r: B256,
s: B256,
) -> Result<(), Vec<u8>> {
Ok(self.erc20_permit.permit(
owner,
spender,
value,
deadline,
v,
r,
s,
&mut self.erc20,
)?)
}
}

0 comments on commit 8d47251

Please sign in to comment.