3

A user on thadao.slack.com was having trouble withdrawing their refunds from The DAO Withdrawal contact.

I checked the WithdrawDAO contact and noticed that there are many other transactions failing: enter image description here

Why is the user's The DAO to ETH refund failing?

In this case, the user was using the Jaxx wallet to execute the refunds.

See also:

1 Answer 1

2

Summary

The Issue - There seems to be some issue with certain wallets having rounding errors.

The Solution - The solution is to approve an amount larger than the balance in The DAO. The user with have to follow up with Jaxx if this issue is occurring within the Jaxx wallet functionality.

Alternatively, the user can perform the withdrawal using one of the 4 methods set out in How do I convert my The DAO tokens into ethers using the withdrawal contract after the hard fork?

Note that most of the failing DAO -> ETH transactions are due to there being a 0 DAO balance in The DAO. This could be because the user have already executed their DAO -> ETH refunds, or are performing the refunds on the wrong accounts.



Details

The WithdrawDAO contract relies on the user executing The DAO's approve(...) to allow the WithdrawDAO contract to transfer the tokens from the user's account to itself in return to refunding the equivalent amount to the user's account. Due to a rounding error somewhere in the process, an amount lower than the balance is being approved, and this causes an error in the withdrawal process.


Confirming The Rounding Error

I checked one of the failed withdrawals with there error message Warning! Error encountered during contract execution [Bad jump destination] as shown below: enter image description here

I ran the following script in geth console and it confirmed that there was a shortfall of 341 wei in the amount approved for transfer and the balance.

var theDAOABIFragment = [{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"type":"function"}, {"type":"function","outputs":[{"type":"uint256","name":"balance"}],"name":"balanceOf","inputs":[{"type":"address","name":"_owner"}],"constant":true}];
undefined
> var theDAOAddress = "0xbb9bc244d798123fde783fcc1c72d3bb8c189413"
undefined
> var theDAO = web3.eth.contract(theDAOABIFragment).at(theDAOAddress);
undefined
> var owner = "0x2de4452025f0f2c92f0cde55c0990e44abdc55b5".toLowerCase();
undefined
> var spender = "0xbf4ed7b27f1d666546e30d74d50d173d20bca754".toLowerCase();
undefined
> var amount = theDAO.allowance(owner, spender);
undefined
> var balance = theDAO.balanceOf(owner);
undefined
> var difference = amount.minus(balance);
undefined
> amount;
8333333333333332992
> balance;
8333333333333333333
> difference;
-341
> 

In the code below, you can see that WithdrawDAO.withdraw() calls TheDAO.transferFrom(...) with the full balance, but TheDAO.transferFrom(...) has the following checks:

balances[_from] >= _amount
&& allowed[_from][msg.sender] >= _amount

Which will not allow a withdrawal of the balance being more than the approved amount.

Here's another failing transaction:

> var owner = "0x1062eecd8d3ce44a469eddb82f309971dd02ec92".toLowerCase();
undefined
> var amount = theDAO.allowance(owner, spender);
undefined
> var balance = theDAO.balanceOf(owner);
undefined
> var difference = amount.minus(balance);
undefined
> amount;
41338832000199999488
> balance;
41338832000200000000
> difference;
-512

And another failing transaction:

> var owner = "0xe69619509a867775bf2c8b96408a82157fda695d".toLowerCase();
undefined
undefined
> var amount = theDAO.allowance(owner, spender);
undefined
> var balance = theDAO.balanceOf(owner);
undefined
> var difference = amount.minus(balance);
undefined
> amount;
2051130434782608640
> balance;
2051130434782608695
> difference;
-55

And one more:

> var owner = "0x34657ab7e8a352e7c0a08c9a14a7f07a15ae98ce".toLowerCase();
undefined
> var amount = theDAO.allowance(owner, spender);
undefined
> var balance = theDAO.balanceOf(owner);
undefined
> var difference = amount.minus(balance);
undefined
> amount;
20682758620689653760
> balance;
20682758620689655172
> difference;
-1412

Some users have not approved the transfer before calling the withdraw:

> var owner = "0x98ba5387be9f93d777b52aef0d9c579851ee8142".toLowerCase();
> var amount = theDAO.allowance(owner, spender);
undefined
> var balance = theDAO.balanceOf(owner);
undefined
> var difference = amount.minus(balance);
undefined
> amount;
0
> balance;
39485714285714
> difference;
-39485714285714
> 

And some have zero balances to approve or transfer:

> var owner = "0x9c0af3a6f4a2266b3e2cf1cf81d30258e3862481".toLowerCase();
undefined
> var amount = theDAO.allowance(owner, spender);
undefined
> var balance = theDAO.balanceOf(owner);
undefined
> var difference = amount.minus(balance);
undefined
> amount;
0
> balance;
0
> difference;
0
> 


The WithdrawDAO Contract

Following is a fragment of the WithdrawDAO contract showing the withdraw() method:

contract WithdrawDAO {
    ...    
    function withdraw(){
        uint balance = mainDAO.balanceOf(msg.sender);

        if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
            throw;
    }
    ...    
}


The DAO Contract

Following are fragments from The DAO contact showing only the relevant classes and methods:

contract TokenInterface {
    mapping (address => uint256) balances;
    mapping (address => mapping (address => uint256)) allowed;
    ...
    /// @param _owner The address from which the balance will be retrieved
    /// @return The balance
    function balanceOf(address _owner) constant returns (uint256 balance);
    ...
    /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it
    /// is approved by `_from`
    /// @param _from The address of the origin of the transfer
    /// @param _to The address of the recipient
    /// @param _amount The amount of tokens to be transferred
    /// @return Whether the transfer was successful or not
    function transferFrom(address _from, address _to, uint256 _amount) returns (bool success);

    /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
    /// its behalf
    /// @param _spender The address of the account able to transfer the tokens
    /// @param _amount The amount of tokens to be approved for transfer
    /// @return Whether the approval was successful or not
    function approve(address _spender, uint256 _amount) returns (bool success);

    /// @param _owner The address of the account owning tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @return Amount of remaining tokens of _owner that _spender is allowed
    /// to spend
    function allowance(
        address _owner,
        address _spender
    ) constant returns (uint256 remaining);
    ...
}

contract Token is TokenInterface {
    ...
    function balanceOf(address _owner) constant returns (uint256 balance) {
        return balances[_owner];
    }

    function transferFrom(
        address _from,
        address _to,
        uint256 _amount
    ) noEther returns (bool success) {

        if (balances[_from] >= _amount
            && allowed[_from][msg.sender] >= _amount
            && _amount > 0) {

            balances[_to] += _amount;
            balances[_from] -= _amount;
            allowed[_from][msg.sender] -= _amount;
            Transfer(_from, _to, _amount);
            return true;
        } else {
            return false;
        }
    }

    function approve(address _spender, uint256 _amount) returns (bool success) {
        allowed[msg.sender][_spender] = _amount;
        Approval(msg.sender, _spender, _amount);
        return true;
    }

    function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }
}
...

// The DAO contract itself
contract DAO is DAOInterface, Token, TokenCreation {
    ...
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        if (isFueled
            && now > closingTime
            && !isBlocked(_from)
            && transferPaidOut(_from, _to, _value)
            && super.transferFrom(_from, _to, _value)) {

            return true;
        } else {
            throw;
        }
    }
    ...
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.