Skip to main content

ERC-20 Approve Module

@perfect-abstractions/compose/token/ERC20/Approve/ERC20ApproveMod.sol

Helper functions for setting ERC-20 allowances and accessing ERC-20 storage

Key Features
  • approve always sets the allowance of msg.sender. It cannot approve on behalf of another owner.
  • No access control check is performed, and none is needed because the caller only spends their own allowance.
  • Emits Approval on every successful call.
Module Usage

Use helper functions from Compose using your own custom facets. See Facets & Modules for more information.

Storage

State Variables

PropertyTypeDescriptionSTORAGE_POSITIONbytes32ERC-20 storage position within the diamond (Value: keccak256("erc20"))

ERC20Storage

Definition
/** @custom:storage-location erc8042:erc20 */
struct ERC20Storage {
mapping(address owner => uint256 balance) balanceOf;
uint256 totalSupply;
mapping(address owner => mapping(address spender => uint256 allowance)) allowance;
}

Functions

getStorage

Returns a pointer to the ERC20Storage struct.

function getStorage() pure returns (ERC20Storage storage s);

Returns:

PropertyTypeDescriptionsERC20Storage storageThe struct in storage.

approve

Sets how many of the caller's tokens _spender may move. The stored allowance becomes exactly _value, overwriting any previous allowance for that spender.

Inside a diamond, your facet runs through delegatecall, so msg.sender here is the account that called the diamond, not the diamond itself.

function approve(address _spender, uint256 _value) returns (bool);

Parameters:

PropertyTypeDescription_spenderaddressThe address allowed to spend the caller's tokens. Cannot be address(0)._valueuint256The new allowance. 0 revokes it. type(uint256).max grants an unlimited allowance that is never decremented.

Returns:

PropertyTypeDescription-boolAlways true. Failures revert instead of returning false.

Reverts:

PropertyTypeDescriptionERC20InvalidSpendererror_spender is address(0).

Events

Emitted on every successful approve, including when _value is 0 or unchanged.

Signature:
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
Parameters:
PropertyTypeDescription_owneraddressThe address granting the allowance. Always msg.sender._spenderaddressThe address receiving the allowance._valueuint256The new allowance.

Errors

Thrown by approve when _spender is the zero address.

Signature:
error ERC20InvalidSpender(address _spender);
Parameters:
PropertyTypeDescription_spenderaddressThe rejected spender. Always address(0).

Best Practices

Integration Notes

Import the module under a namespace and call it from your facet:

Calling approve from a custom facet
import "src/token/ERC20/Approve/ERC20ApproveMod.sol" as ERC20ApproveMod;

contract MyFacet {
function approveSpender(address _spender, uint256 _value) external returns (bool) {
return ERC20ApproveMod.approve(_spender, _value);
}
}

approve cannot set an allowance for any owner other than msg.sender. If your facet authorizes an owner some other way, such as a signature, write getStorage().allowance[_owner][_spender] directly and emit Approval yourself. ERC20PermitFacet does exactly this.

ERC20Storage lives at keccak256("erc20") inside the diamond. That is the same slot ERC20DataFacet, ERC20TransferFacet, and the other ERC-20 contracts use, so an allowance set here is what allowance() returns and what transferFrom spends.

Last updated:

Newsletter

Get notified about releases, feature announcements, and technical deep-dives on building smart contracts with Compose.

No spam. Unsubscribe anytime.