--- eip: 7914 title: An Interface for Transfer From Native description: This proposal introduces a method for approved spenders to execute native token transfers on behalf of users. author: Sara Reynolds (@snreynolds), Eric Zhong (@zhongeric), Daniel Gretzke (@gretzke), Diana Kocsis (@dianakocsis), Mark Toda (@marktoda), and Ryan McPeck (@McOso) discussions-to: https://ethereum-magicians.org/t/erc-7914-an-interface-for-transfer-from-native/24523 status: Draft type: Standards Track category: ERC created: 2025-03-21 --- ## Abstract This proposal introduces a method for native transfers that can originate from an approved spender on a smart contract wallet or a [ERC-7702](./eip-7702.md) delegated EOA account. For wallets that can execute code, other contracts can be authorized to pull native tokens via a new callback mechanism. ## Motivation Because Native transfers must be initiated by the calling account, spenders cannot pull Native tokens without executing code on a user's wallet. This presents a problem in intent-based systems, where transactions are expected to be sent on behalf of users rather than initiated by the users themselves. With smart contract wallets and ERC-7702 delegated EOAs, wallets that support code execution enable a way for spenders to request Native transfers. This EIP defines an interface for both the approval and transfer mechanisms on the account, making authorization-based native transfers possible without requiring the user to initiate the transaction. ## Specification The contract code MUST implement the following function: ```solidity function transferFromNative(address from, address recipient, uint256 amount) public returns (bool); ``` - `from` : The account that the ETH transfer should originate from. If this function is implemented on the spender’s contract account, then it should check `from` is set to `address(this)`. If `from` is not `address(this)` the contract COULD forward the call. Note that specifically parameterizing a `from` address allows for flexibility, and other types of integrations with this standard. - `recipient`: The recipient of the Native transfer. - `amount`: The amount of Native to transfer. SHOULD early return with true when amount == 0 and skip any event emissions, otherwise anyone can execute any fallback function on any contract on behalf of the user without explicit approval. MUST verify whether `msg.sender` (the caller) is authorized to execute the Native transfer. MUST handle the Native transfer. Can revert or return false on failure. MUST emit the following event on success: ```solidity event TransferFromNative(address indexed from, address indexed to, uint256 value); ``` ### Persistent Approval An approval system similar to the [ERC-20](./eip-20.md) `approve` mechanism MUST be implemented to allow users to specify trusted accounts that can perform Native transfers on their behalf. To modify this approval, the following function MUST be implemented. ```solidity function approveNative(address spender, uint256 amount) external returns (bool); ``` - `spender`: The address being authorized. - `amount`: The maximum Native amount the `spender` can transfer. This function SHOULD only be callable by the owner of the account. The following event MUST be emitted when approvals are updated: ```solidity event ApproveNative(address indexed owner, address indexed spender, uint256 value); ``` To read the persistent allowance for a given spender, the contract MUST implement the following view function: ```solidity function nativeAllowance(address spender) public view returns (uint256 allowance) ``` ### Optional Transient Approval For cases where approval should only persist within a single transaction, a transient approval mechanism MAY be used. This allows a user to grant temporary approval to a `spender` for a single transaction, without modifying persistent storage. The recommended function for transient approvals is: ```solidity function approveNativeTransient(address spender, uint256 amount) external returns (bool); ``` - `spender`: The address being authorized. - `amount`: The Native amount the `spender` is allowed to transfer within the scope of the transaction. A corresponding event SHOULD be emitted: ```solidity event ApproveNativeTransient(address indexed owner, address indexed spender, uint256 value); ``` A corresponding transfer function should be used to spend the transient amount approved: ```solidity function transferFromNativeTransient(address from,address recipient,uint256 amount) external returns (bool); ``` Note that a spender could be approved for funds BOTH transiently and persistently and that users of both of these approval mechanisms should be aware that the total sum is approved. Similar to the persistent approval mechanism, a view function to read the transient allowance given a spender can be implemented: ```solidity function transientNativeAllowance(address spender) public view returns (uint256) ``` The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. ## Rationale - Enables seamless ETH transfers without requiring users to submit raw transactions themselves. - Leverages the capabilities introduced by ERC-7702, which allows EOAs to execute code. - Adopts a standardized approval mechanism similar to ERC-20 for developer experience. - Outlines both persistent and transient approval mechanisms to minimize hanging approvals for tighter security on the account. ## Backwards Compatibility - This proposal requires ERC-7702 for EOAs and does not interfere with existing transaction models for EOAs without account code. - Compatible with [ERC-4337](./eip-4337.md) accounts. ## Reference Implementation An example implementation is shown below: ```solidity contract DelegatedNativeTransfer { event ApproveNative(address indexed owner, address indexed spender, uint256 value); mapping(address => uint256) public allowance; function approveNative(address spender, uint256 amount) external returns (bool) { if (msg.sender != address(this)) revert Unauthorized(); allowance[spender] = amount; emit ApproveNative(msg.sender, spender, amount); return true; } function transferFromNative(address from, address recipient, uint256 amount) external returns (bool) { if (amount == 0) return true; // early return for amount == 0 if (from != address(this)) revert IncorrectSpender(); if (allowance[msg.sender] < amount) revert AllowanceExceeded(); allowance[msg.sender] -= amount; (bool success,) = payable(recipient).call{value: amount}(""); if (success) { emit TransferFromNative(address(this), recipient, amount); return true; } return false; } } ``` ## Security Considerations - Authorization checks must be robust to prevent unauthorized Native transfers. - The implementation should early return in the transfer function when the amount specified is zero, so as to prevent any caller from triggering the fallback function on any contract. - Contracts integrating with this callback should be aware that the account may perform arbitrary actions within the callback. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).