A method of depositing ERC20 tokens with fewer transactions and less risk.
A user approves the wrapped deposit token to spend tokens on their behalf. Applications needing to accept deposits implement a function like the following:
function acceptERC20Deposit(address depositor, address token, uint amount) external returns (bool);
Applications then have the user send a transaction to the deposit wrapper like this:
// `to` is the application address
depositERC20(address to, address token, uint amount);
The user must first approve an application to spend tokens on their behalf. Then the user sends a second transaction asking the application to send tokens to itself from the user. This is necessary so the application can perform deposit logic, like updating the deposit balance for the user.
The user approves the wrapped deposit contract to spend tokens on their behalf. Then the user asks the wrapped deposit contract to deposit to an application on their behalf. Only the wrapped deposit contract needs to be approved to spend tokens. The wrapped deposit contract can send tokens to any number of application contracts.
Users only need to approve a token to be spent once.
Users only need to approve one contract to spend their tokens. They don't need to trust application contracts with token spend approvals.
The wrapped deposit contract is short and simple (less than 50 lines of code) and therefore less likely to contain exploits.
The wrapped deposit contract offers a simple way for application programmers to handle deposits of Ether and tokens without needing to interface with the token contract.
To accept deposits an application contract should implement the DepositReceiver
interface. This consists of a single function acceptDeposit(depositor, token, amount)
. This function should revert if the deposit is not accepted. If the function does not revert the funds will either be transferred, or the transaction will revert. An example implementation is shown below:
contract MyApplication implements DepositReceiver {
address constant ALLOWED_DEPOSIT_TOKEN;
address constant WRAPPER;
mapping (address => uint) balanceOf;
function acceptERC20Deposit(address depositor, address token, uint amount) public returns (bool) {
// Make sure it's the wrapper contract calling this function
require(msg.sender == WRAPPER, "Unauthorized");
// This application allows only 1 token to be deposited
if (token == ALLOWED_DEPOSIT_TOKEN) {
balanceOf[depositor] += amount;
return true;
}
return false;
}
}