The wrapped deposit contract handles deposits of assets (Ether, ERC-20, ERC-721) on behalf of a user. A user must only approve a spend limit once and then an asset may be deposited to any number of different applications that support deposits from the contract.
Motivation
The current user flow for depositing assets in dapps is unnecessarily expensive and insecure. To deposit an ERC-20 asset a user must either:
send an approve transaction for the exact amount being sent, before making a deposit, and then repeat this process for every subsequent deposit.
send an approve transaction for an infinite spend amount before making deposits.
The first option is inconvenient, and expensive. The second option is insecure. Further, explaining approvals to new or non-technical users is confusing. This has to be done in every dapp that supports ERC20 deposits.
Specification
The wrapped deposit contract SHOULD be deployed at an identifiable address (e.g. 0x1111119a9e30bceadf9f939390293ffacef93fe9). The contract MUST be non-upgradable with no ability for state variables to be changed.
The wrapped deposit contract MUST have the following public functions:
Each of these functions MUST revert if to is an address with a zero code size. Each function MUST attempt to call a method on the to address confirming that it is willing and able to accept the deposit. If this function call does not return a true value execution MUST revert. If the asset transfer is not successful execution MUST revert.
The following interfaces SHOULD exist for contracts wishing to accept deposits:
A receiving contract MAY implement any of these functions as desired. If a given function is not implemented deposits MUST not be sent for that asset type.
Rationale
Having a single contract that processes all token transfers allows users to submit a single approval per token to deposit to any number of contracts. The user does not have to trust receiving contracts with token spend approvals and receiving contracts have their complexity reduced by not having to implement token transfers themselves.
User experience is improved because a simple global dapp can be implemented with the messaging: “enable token for use in other apps”.
Backwards Compatibility
This EIP is not backward compatible. Any contract planning to use this deposit system must implement specific functions to accept deposits. Existing contracts that are upgradeable can add support for this EIP retroactively by implementing one or more accept deposit functions.
Upgraded contracts can allow deposits using both the old system (approving the contract itself) and the proposed deposit system to preserve existing approvals. New users should be prompted to use the proposed deposit system.
Reference Implementation
pragmasolidity^0.7.0;interfaceERC20Receiver{functionacceptERC20Deposit(addressdepositor,addresstoken,uintamount)externalreturns(bool);}interfaceERC721Receiver{functionacceptERC721Deposit(addressdepositor,addresstoken,uinttokenId)externalreturns(bool);}interfaceERC1155Receiver{functionacceptERC1155Deposit(addressdepositor,addresstoken,uinttokenId,uintvalue,bytescalldatadata)externalreturns(bool);functionacceptERC1155BatchDeposit(addressdepositor,addresstoken,uint[]calldatatokenIds,uint[]calldatavalues,bytescalldatadata)externalreturns(bool);}interfaceEtherReceiver{functionacceptEtherDeposit(addressdepositor,uintamount)externalreturns(bool);}interfaceIERC20{functiontransferFrom(addresssender,addressrecipient,uintamount)externalreturns(bool);}interfaceIERC721{functiontransferFrom(address_from,address_to,uint256_tokenId)externalpayable;functionsafeTransferFrom(address_from,address_to,uint256_tokenId,bytesmemorydata)externalpayable;}interfaceIERC1155{functionsafeTransferFrom(address_from,address_to,uint_id,uint_value,bytescalldata_data)external;functionsafeBatchTransferFrom(address_from,address_to,uint256[]calldata_ids,uint256[]calldata_values,bytescalldata_data)external;}contractWrappedDeposit{functiondepositERC20(addressto,addresstoken,uintamount)public{_assertContract(to);require(ERC20Receiver(to).acceptERC20Deposit(msg.sender,token,amount));bytesmemorydata=abi.encodeWithSelector(IERC20(token).transferFrom.selector,msg.sender,to,amount);(boolsuccess,bytesmemoryreturndata)=token.call(data);require(success);// backward compat for tokens incorrectly implementing the transfer functionif(returndata.length>0){require(abi.decode(returndata,(bool)),"ERC20 operation did not succeed");}}functiondepositERC721(addressto,addresstoken,uinttokenId)public{_assertContract(to);require(ERC721Receiver(to).acceptERC721Deposit(msg.sender,token,tokenId));IERC721(token).transferFrom(msg.sender,to,tokenId);}functionsafeDepositERC721(addressto,addresstoken,uinttokenId,bytesmemorydata)public{_assertContract(to);require(ERC721Receiver(to).acceptERC721Deposit(msg.sender,token,tokenId));IERC721(token).safeTransferFrom(msg.sender,to,tokenId,data);}functionsafeDepositERC1155(addressto,addresstoken,uinttokenId,uintvalue,bytescalldatadata)public{_assertContract(to);require(ERC1155Receiver(to).acceptERC1155Deposit(msg.sender,to,tokenId,value,data));IERC1155(token).safeTransferFrom(msg.sender,to,tokenId,value,data);}functionbatchDepositERC1155(addressto,addresstoken,uint[]calldatatokenIds,uint[]calldatavalues,bytescalldatadata)public{_assertContract(to);require(ERC1155Receiver(to).acceptERC1155BatchDeposit(msg.sender,to,tokenIds,values,data));IERC1155(token).safeBatchTransferFrom(msg.sender,to,tokenIds,values,data);}functiondepositEther(addressto)publicpayable{_assertContract(to);require(EtherReceiver(to).acceptEtherDeposit(msg.sender,msg.value));(boolsuccess,)=to.call{value:msg.value}('');require(success,"nonpayable");}function_assertContract(addressc)privateview{uintsize;assembly{size:=extcodesize(c)}require(size>0,"noncontract");}}
Security Considerations
The wrapped deposit implementation should be as small as possible to reduce the risk of bugs. The contract should be small enough that an engineer can read and understand it in a few minutes.
Receiving contracts MUST verify that msg.sender is equal to the wrapped deposit contract. Failing to do so allows anyone to simulate deposits.