In ERC20 we approve a user to spend our token and then they can spend as much allowed using transferFrom, but if the allowance limit exceed then I have to approve again, is there any way to allow them to spend all tokens? I mean have to approve a account only once
[Ethereum] How to approve user to spend all the ERC20 tokens
erc-20erc-20-approvetokenstransfertransferfrom
Related Solutions
This is non-trivial, although possible.
Architecture with a separate account isn't the problem per se. (The DAO had it to separate income from current holdings, as I understand it.) The main issue is that you can't simply iterate through every token holder, or you'll hit the block gas limit after enough token holders. (The question you linked to is about this.)
The pattern more commonly used, withdrawals, is where the user specifically asks for money, and at that time the contract pays them. While this makes sense in most situations, such as an auction contract where the users have individual balances, what does it mean if rewards accumulate to tokens? Is the reward calculated when the user withdraws, based on the time since last withdrawal? Then an attacker withdraws, sends tokens to another address, and repeats.
I am not sure exactly how the DAO avoided this, as I did not understand the math involved. Which leads to the following point.
Security-wise, there's all sorts of small mistakes that can lead to disaster. Notably, The DAO heist itself was due one such small mistake in this category... in withdrawRewardFor()
as it happened.
The DAO had expert programmers and a paid security audit, and they still missed this. I would be incredibly cautious about this entire area. I'm not saying it's impossible, just risky.
Escrowing ERC20 tokens:
Your escrow contract needs to know when it received new tokens, from whom, and how many.
This is achieved by having the "seller" create two transactions:
- Approval: First transaction calls token contract
// tracker_0x_address is the address of the ERC20 contract they want to deposit tokens from ( ContractA ) // spender is your deployed escrow contract address ERC20(tracker_0x_address).approve(address spender, uint tokens)
- Deposit: Second transaction calls a method in the escrow contract
mapping ( address => uint256 ) public balances; deposit(uint tokens) { // add the deposited tokens into existing balance balances[msg.sender]+= tokens; // transfer the tokens from the sender to this contract ERC20(tracker_0x_address).transferFrom(msg.sender, address(this), tokens); }
This updates the sender's balance, and then transfers the tokens from sender to escrow (contractB).
Releasing escrowed tokens:
All your smart contract has to do is call the
ERC20(tracker_0x_address).transfer(msg.sender, balances[msg.sender]);
on the token tracker address in order to transfer those tokens to an address.
If you're doing multiple tokens and multiple users in one contract you're going to have to implement a second layer mapping for the balances as well as support for a tracker variable in the deposit method.
Code Example
// ---------------------------------------------------------------------------- // ERC Token Standard #20 Interface // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md // ---------------------------------------------------------------------------- pragma solidity ^0.4.17; contract ERC20 { function totalSupply() public constant returns (uint); function balanceOf(address tokenOwner) public constant returns (uint balance); function allowance(address tokenOwner, address spender) public constant returns (uint remaining); function transfer(address to, uint tokens) public returns (bool success); function approve(address spender, uint tokens) public returns (bool success); function transferFrom(address from, address to, uint tokens) public returns (bool success); event Transfer(address indexed from, address indexed to, uint tokens); event Approval(address indexed tokenOwner, address indexed spender, uint tokens); } contract contractB { address tracker_0x_address = 0xd26114cd6EE289AccF82350c8d8487fedB8A0C07; // ContractA Address mapping ( address => uint256 ) public balances; function deposit(uint tokens) public { // add the deposited tokens into existing balance balances[msg.sender]+= tokens; // transfer the tokens from the sender to this contract ERC20(tracker_0x_address).transferFrom(msg.sender, address(this), tokens); } function returnTokens() public { uint256 amount = balances[msg.sender]; balances[msg.sender] = 0; ERC20(tracker_0x_address).transfer(msg.sender, amount); } }
Best Answer
What is usually done (you can see such cases on decentralized exchanges) is to approve a very large number of tokens (greater than the total supply). A library such as bignumber.js can help you to work with this kind of numbers in javascript.
Note that this approach may involve security concerns. If the smart contract has a weakness and is "hacked", the attacker could potentially steal all of the users' tokens. One can reduce this risk in their dApp by implementing a "disapprove" feature which is an approval of 0. Indeed, each call of the
approve
method overrides the previous value.