This may look duplicate but I can't find a proper answer. I aim to develop a contract where contract owner can set recipients and the number of tokens they can withdraw. Once they are set, the recipient can call a function with metamask bearing the gas price and redeem the tokens.
- Have deployed the below contract via Remix (Token owner is not same the contract owner)
- When tried to execute the method
setBalances
with only 2 items in each array, that resulted in errortransact to WithDraw.setBalances errored: Error: gas required exceeds allowance or always failing transaction
- Have changed the gas limit to
900000000000
but still the same result. - Just for check, called
redeem
which also yield the same errortransact to WithDraw.redeem errored: Error: gas required exceeds allowance or always failing transaction
Smart Contract Code
pragma solidity ^0.4.18;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred (address indexed _from, address indexed _to);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public{
owner = msg.sender;
OwnershipTransferred(address(0), owner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
owner = newOwner;
OwnershipTransferred(owner,newOwner);
}
}
/**
* @title Token
* @dev API interface for interacting with the Token contract
*/
interface Token {
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function balanceOf(address _owner) constant external returns (uint256 balance);
}
contract WithDraw is Ownable {
Token token;
mapping(address => uint256) public redeemBalanceOf;
event BalanceSet(address indexed beneficiary, uint256 value);
event Redeemed(address indexed beneficiary, uint256 value);
function WithDraw() public {
address _tokenAddr = MY_TOKEN_ADDR;
token = Token(_tokenAddr);
}
function setBalances(address[] dests, uint256[] values) onlyOwner public {
uint256 i = 0;
while (i < dests.length) {
if(dests[i] == address(0)) continue;
uint256 toSend = values[i] * 10**18;
redeemBalanceOf[dests[i]] += toSend;
i++;
BalanceSet(dests[i],values[i]);
}
}
function redeem(uint256 quantity) external{
uint256 baseUnits = quantity * 10**18;
require(redeemBalanceOf[msg.sender]>=baseUnits);
redeemBalanceOf[msg.sender] -= baseUnits;
token.transferFrom(owner,msg.sender,baseUnits);
Redeemed(msg.sender,quantity);
}
}
What is the cause of the error? Should the contract owner same as token contract owner?
EDIT1
Based on @smarx reply, have changed the setBalances
method like below.
function setBalances(address[] dests, uint256[] values) onlyOwner public {
for (uint256 i = 0; i < dests.length; i++) {
if(dests[i] != address(0))
{
uint256 toSend = values[i] * 10**18;
redeemBalanceOf[dests[i]] += toSend;
BalanceSet(dests[i],values[i]);
}
i++;
}
}
Now, when I pass two items in the array to set value, am seeing event log for only the first item, and the second one got missed. Confused!. Whats wrong with the for loop?
Apart from the loop issue, calling the redeem
function also throws the same error transact to WithDraw.redeem errored: Error: gas required exceeds allowance or always failing transaction
even when passing less quantity than what is already assigned. Is it needed to call the approve
method before token transfer in that method?
EDIT2
Couldn't the issue with for loop. Modified it back to while back with approve function as below.This time setBalances
worked properly, the balances were showing properly.
function setBalances(address[] dests, uint256[] values) onlyOwner public {
uint256 i = 0;
while (i < dests.length){
if(dests[i] != address(0))
{
uint256 toSend = values[i] * 10**18;
redeemBalanceOf[dests[i]] += toSend;
token.approve(dests[i], toSend);
BalanceSet(dests[i],values[i]);
}
i++;
}
}
But still, the redeem
is throwing that same error. Any help is highly appreciated.
EDIT 3
Have made the changes like below. And this time have transferred enough number of tokens to this contract address
function setBalances(address[] dests, uint256[] values) onlyOwner public {
uint256 i = 0;
while (i < dests.length){
if(dests[i] != address(0))
{
uint256 toSend = values[i] * 10**18;
redeemBalanceOf[dests[i]] += toSend;
BalanceSet(dests[i],values[i]);
}
i++;
}
}
function redeem(uint256 quantity) external{
uint256 baseUnits = quantity * 10**18;
uint256 tokensAvailable = token.balanceOf(this);
require(redeemBalanceOf[msg.sender]>=baseUnits);
require( tokensAvailable >= baseUnits);
redeemBalanceOf[msg.sender] -= baseUnits;
token.transfer(msg.sender,baseUnits);
Redeemed(msg.sender,quantity);
}
But still getting the same issue. Am surely missing something, don't know what is it. Please help.
if (dest[i] == address(0)) continue;
? That will cause an infinite loop if it ever happens.redeem
callstoken.transferFrom(owner, ...)
. Perhapsowner
hasn'tapprove
d a sufficient number of tokens for that to work? (Try commenting out that line to see if it's the source of the revert.)setBalances
also callstoken.approve
. I think there's some confusion over where the tokens are. It can't make sense to have both of those lines.owner
that would approve the tokens to be withdrawn on the redeem function. Is there something missing?