ERC-677 is an ERC-20 extension that adds a single function – transferAndCall – enabling a token transfer and a callback to the recipient in one transaction. It was proposed in 2017 and is best known as the standard behind Chainlink’s LINK token.

Like ERC-1363, ERC-677 exists to solve the two-step approve + transferFrom problem inherent in ERC-20. Where they differ is scope: ERC-677 is a minimal extension (one function, one receiver interface), while ERC-1363 adds approval callbacks and uses the magic-value return pattern for safer recipient validation.

The interface#

1
2
3
4
5
6
7
interface IERC677 is IERC20 {
    function transferAndCall(
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (bool);
}

The receiving contract implements a single callback:

1
2
3
4
5
6
7
interface IERC677Receiver {
    function onTokenTransfer(
        address from,
        uint256 value,
        bytes calldata data
    ) external;
}

Note that onTokenTransfer returns void – unlike ERC-1363’s onTransferReceived, there is no magic return value to confirm the callback was handled. This makes ERC-677 simpler but slightly less safe: a contract that happens to have a fallback function could silently accept the call without actually processing the transfer.

How it works#

  1. The sender calls transferAndCall(recipient, amount, data) on the token contract.
  2. The token contract executes a standard ERC-20 transfer to the recipient.
  3. The token contract then calls onTokenTransfer(sender, amount, data) on the recipient.
  4. If the recipient reverts, the entire transaction (including the transfer) reverts.

The data parameter is an opaque bytes payload the sender can use to encode instructions for the recipient – e.g. which oracle job to request, which pool to deposit into, etc.

Concrete example: paying for an oracle request#

The canonical use case is Chainlink’s oracle network. A smart contract requests off-chain data by sending LINK to an oracle contract:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";

contract PriceConsumer {
    LinkTokenInterface internal immutable LINK;
    address internal immutable oracle;

    constructor(address linkToken, address oracleAddress) {
        LINK = LinkTokenInterface(linkToken);
        oracle = oracleAddress;
    }

    function requestPrice(bytes32 jobId, uint256 fee) external {
        // Encode the request parameters as the data payload
        bytes memory payload = abi.encode(jobId, address(this), this.fulfill.selector);
        LINK.transferAndCall(oracle, fee, payload);
    }

    function fulfill(bytes32 requestId, uint256 price) external {
        // Called by the oracle with the result
    }
}

One transaction transfers LINK and initiates the oracle request. Without transferAndCall, this would require an approve call followed by a separate request function that pulls LINK via transferFrom.

ERC-677 vs. ERC-1363#

Feature ERC-677 ERC-1363
transferAndCall Yes Yes
approveAndCall No Yes
Receiver return value None (void) Magic bytes4 selector
EIP status Draft / stagnant Final (EIP-1363)
Key adoption Chainlink LINK OpenZeppelin v5

ERC-1363 is the more complete and safer standard. New token projects should prefer it. ERC-677 remains relevant because LINK – one of the most widely held ERC-20 tokens – uses it, and the Chainlink ecosystem is built around it.

Relationship with ERC-667#

ERC-667 is sometimes confused with ERC-677. ERC-667 was a separate, earlier proposal with a similar transferAndCall concept, but it never gained meaningful adoption and has no notable implementations. In practice, references to “ERC-667” in the wild are almost always mistaken references to ERC-677.