Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
update
  • Loading branch information
Amxx authored and cairoeth committed Jul 16, 2024
commit 76f17453c17fe4e80a9dd068e622e00050a5f0ba
24 changes: 12 additions & 12 deletions contracts/mocks/token/ERC20GetterHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@ import {IERC20} from "../../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../../token/ERC20/extensions/IERC20Metadata.sol";

contract ERC20GetterHelper {
event erc20totalSupply(IERC20 token, uint256 totalSupply);
event erc20balanceOf(IERC20 token, address account, uint256 balanceOf);
event erc20allowance(IERC20 token, address owner, address spender, uint256 allowance);
event erc20name(IERC20Metadata token, string name);
event erc20symbol(IERC20Metadata token, string symbol);
event erc20decimals(IERC20Metadata token, uint8 decimals);
event ERC20totalSupply(IERC20 token, uint256 totalSupply);
event ERC20balanceOf(IERC20 token, address account, uint256 balanceOf);
event ERC20allowance(IERC20 token, address owner, address spender, uint256 allowance);
event ERC20name(IERC20Metadata token, string name);
event ERC20symbol(IERC20Metadata token, string symbol);
event ERC20decimals(IERC20Metadata token, uint8 decimals);

function totalSupply(IERC20 token) external {
emit erc20totalSupply(token, token.totalSupply());
emit ERC20totalSupply(token, token.totalSupply());
}

function balanceOf(IERC20 token, address account) external {
emit erc20balanceOf(token, account, token.balanceOf(account));
emit ERC20balanceOf(token, account, token.balanceOf(account));
}

function allowance(IERC20 token, address owner, address spender) external {
emit erc20allowance(token, owner, spender, token.allowance(owner, spender));
emit ERC20allowance(token, owner, spender, token.allowance(owner, spender));
}

function name(IERC20Metadata token) external {
emit erc20name(token, token.name());
emit ERC20name(token, token.name());
}

function symbol(IERC20Metadata token) external {
emit erc20symbol(token, token.symbol());
emit ERC20symbol(token, token.symbol());
}

function decimals(IERC20Metadata token) external {
emit erc20decimals(token, token.decimals());
emit ERC20decimals(token, token.decimals());
}
}
50 changes: 37 additions & 13 deletions contracts/token/ERC20/extensions/draft-ERC20TemporaryApproval.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ abstract contract ERC20TemporaryApproval is ERC20, IERC7674 {
using StorageSlot for StorageSlot.Uint256SlotType;

// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20TemporaryApproval")) - 1)) & ~bytes32(uint256(0xff))
// solhint-disable-next-line const-name-snakecase
bytes32 private constant ERC20TemporaryApprovalStorageLocation =
0x0fd66af0be6cb88466bb5c49c7ea8fbb4acdc82057e863d0a17fddeaaf18fe00;

Expand All @@ -29,54 +30,77 @@ abstract contract ERC20TemporaryApproval is ERC20, IERC7674 {
function allowance(address owner, address spender) public view virtual override returns (uint256) {
(bool success, uint256 amount) = Math.tryAdd(
super.allowance(owner, spender),
_loadTemporaryAllowance(owner, spender)
_temporaryAllowance(owner, spender)
);
return success ? amount : type(uint256).max;
}

/**
* @dev Internal getter for the current temporary allowance that `spender` has over `owner` tokens.
*/
function _temporaryAllowance(address owner, address spender) internal view virtual returns (uint256) {
return ERC20TemporaryApprovalStorageLocation.deriveMapping(owner).deriveMapping(spender).asUint256().tload();
}

/**
* @dev Alternative to {approve} that sets a `value` amount of tokens as the temporary allowance of `spender` over
* the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Requirements:
* - `spender` cannot be the zero address.
*
* Does NOT emit an {Approval} event.
*/
function temporaryApprove(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_storeTemporaryAllowance(owner, spender, value);
_temporaryApprove(_msgSender(), spender, value);
return true;
}

/**
* @dev Sets `value` as the temporary allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `temporaryApprove`, and can be used to e.g. set automatic allowances
* for certain subsystems, etc.
*
* Requirements:
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Does NOT emit an {Approval} event.
*/
function _temporaryApprove(address owner, address spender, uint256 value) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
ERC20TemporaryApprovalStorageLocation.deriveMapping(owner).deriveMapping(spender).asUint256().tstore(value);
}

/**
* @dev {_spendAllowance} override that consumes the temporary allowance (if any) before eventually falling back
* to consumming the persistent allowance.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual override {
unchecked {
// load transient allowance
uint256 currentTemporaryAllowance = _loadTemporaryAllowance(owner, spender);
uint256 currentTemporaryAllowance = _temporaryAllowance(owner, spender);
// if there is temporary allowance
if (currentTemporaryAllowance > 0) {
// if infinite, do nothing
if (currentTemporaryAllowance == type(uint256).max) return;
// check how much of the value is covered by the transient allowance
uint256 spendTemporaryAllowance = Math.min(currentTemporaryAllowance, value);
// decrease transient allowance accordingly
_storeTemporaryAllowance(owner, spender, currentTemporaryAllowance - spendTemporaryAllowance);
_temporaryApprove(owner, spender, currentTemporaryAllowance - spendTemporaryAllowance);
// update value necessary
value -= spendTemporaryAllowance;
}
// reduce any remaining value from the persistent allowance
super._spendAllowance(owner, spender, value);
}
}

function _loadTemporaryAllowance(address owner, address spender) private view returns (uint256) {
return ERC20TemporaryApprovalStorageLocation.deriveMapping(owner).deriveMapping(spender).asUint256().tload();
}

function _storeTemporaryAllowance(address owner, address spender, uint256 value) private {
ERC20TemporaryApprovalStorageLocation.deriveMapping(owner).deriveMapping(spender).asUint256().tstore(value);
}
}
Loading