ERC-1363 is an ERC-20 extension that adds callback hooks to token transfers and approvals. It solves the fundamental two-step problem with ERC-20: the approve + transferFrom dance that requires two transactions, confuses users, and creates a window where tokens can be lost if the receiving contract doesn’t pull them.

With ERC-1363, a token holder can transfer tokens and notify the recipient in a single transaction. The standard was formalized as EIP-1363 and has an OpenZeppelin implementation.

The approve-and-transfer problem#

The standard ERC-20 workflow for interacting with a smart contract looks like this:

  1. User calls approve(spender, amount) on the token contract, granting the spender an allowance.
  2. User calls a function on the spender contract (e.g. stake(amount)).
  3. The spender contract internally calls transferFrom(user, self, amount) to pull the tokens.

This is two transactions, two gas payments, and a race condition (the allowance sits open between steps 1 and 2). If the user calls transfer directly instead of approve, the receiving contract has no way to know tokens arrived – they’re effectively stuck.

ERC-1363 collapses this into one step.

The interface#

ERC-1363 adds four functions to the ERC-20 interface:

1
2
3
4
5
6
interface IERC1363 is IERC20 {
    function transferAndCall(address to, uint256 amount) external returns (bool);
    function transferAndCall(address to, uint256 amount, bytes calldata data) external returns (bool);
    function approveAndCall(address spender, uint256 amount) external returns (bool);
    function approveAndCall(address spender, uint256 amount, bytes calldata data) external returns (bool);
}

And two receiver interfaces that contracts must implement to accept these calls:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
interface IERC1363Receiver {
    function onTransferReceived(
        address operator,
        address from,
        uint256 amount,
        bytes calldata data
    ) external returns (bytes4);
}

interface IERC1363Spender {
    function onApprovalReceived(
        address owner,
        uint256 amount,
        bytes calldata data
    ) external returns (bytes4);
}

The receiver returns a magic value (bytes4 selector) to confirm it handled the callback – the same pattern used by ERC-721’s onERC721Received. If the receiver doesn’t return the correct value, the transaction reverts.

How it works#

transferAndCall: Transfers tokens to the recipient, then calls onTransferReceived on the recipient. The recipient can decode the data parameter to determine what action to take (e.g. stake, deposit, purchase).

approveAndCall: Sets an allowance for the spender, then calls onApprovalReceived on the spender. The spender can immediately pull the tokens within the same transaction.

Both paths are atomic – if the callback reverts, the transfer or approval reverts too.

Concrete example#

A staking contract using ERC-1363:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import "@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol";
import "@openzeppelin/contracts/interfaces/IERC1363Receiver.sol";

contract StakingPool is IERC1363Receiver {
    mapping(address => uint256) public staked;

    function onTransferReceived(
        address,          // operator
        address from,
        uint256 amount,
        bytes calldata    // data
    ) external returns (bytes4) {
        staked[from] += amount;
        return IERC1363Receiver.onTransferReceived.selector;
    }
}

The user calls token.transferAndCall(stakingPool, amount) – one transaction, tokens are transferred and staked.

Standard Mechanism Receiver interface Notable adoption
ERC-1363 transferAndCall + approveAndCall IERC1363Receiver, IERC1363Spender OpenZeppelin, various DeFi tokens
ERC-677 transferAndCall only onTokenTransfer Chainlink LINK
ERC-777 Hooks via ERC-1820 registry IERC777Recipient Deprecated due to reentrancy risks

ERC-1363 is the most complete of the three: it supports both transfer and approval callbacks, uses the battle-tested magic-value pattern, and doesn’t require an external registry like ERC-777. ERC-777 was deprecated by OpenZeppelin in v5 due to persistent reentrancy vulnerabilities stemming from its pre-transfer hooks.

Limitations#

  • Receiver must opt in – contracts that don’t implement IERC1363Receiver will cause transferAndCall to revert. Plain transfer still works (it’s just ERC-20), but then you lose the callback.
  • Not widely adopted yet – most existing DeFi contracts expect the traditional approve-then-call pattern. A token being ERC-1363 doesn’t help unless the receiving protocol supports the callback.
  • Extra interface surface – contracts accepting ERC-1363 tokens need to implement and audit the receiver interface, adding code and attack surface.