Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor permit #449

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 @@
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,31 +81,40 @@
/// 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.
///
/// # Arguments
///
/// * `&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`

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

View workflow job for this annotation

GitHub Actions / clippy

[clippy] contracts/src/token/erc20/extensions/permit.rs#L105

warning: unexpected `cfg` condition value: `export-abi` --> contracts/src/token/erc20/extensions/permit.rs:105:1 | 105 | #[public] | ^^^^^^^^^ | = note: expected values for `feature` are: `std` = help: consider adding `export-abi` as a feature in `Cargo.toml` = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration = note: this warning originates in the attribute macro `public` (in Nightly builds, run with -Z macro-backtrace for more info)
Raw output
contracts/src/token/erc20/extensions/permit.rs:105:1:w:warning: unexpected `cfg` condition value: `export-abi`
   --> contracts/src/token/erc20/extensions/permit.rs:105:1
    |
105 | #[public]
    | ^^^^^^^^^
    |
    = note: expected values for `feature` are: `std`
    = help: consider adding `export-abi` as a feature in `Cargo.toml`
    = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
    = note: this warning originates in the attribute macro `public` (in Nightly builds, run with -Z macro-backtrace for more info)


__END__
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 @@
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 @@
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)
}
}
34 changes: 30 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,38 @@ 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(())
}

#[allow(clippy::too_many_arguments)]
pub fn permit(
&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,
)?)
}
}
Loading