¿Por qué fallan mis reembolsos de DAO a ETH?

Un usuario en thadao.slack.com estaba teniendo problemas para retirar sus reembolsos del contacto de retiro de DAO.

Revisé el WithdrawDAOcontacto y noté que hay muchas otras transacciones que fallan:ingrese la descripción de la imagen aquí

¿Por qué falla el reembolso de DAO a ETH del usuario?

En este caso, el usuario estaba usando la billetera Jaxx para ejecutar los reembolsos.

Ver también:

Respuestas (1)

Resumen

El problema : parece haber algún problema con ciertas billeteras que tienen errores de redondeo.

La solución : la solución es aprobar una cantidad mayor que el saldo en The DAO. El usuario debe hacer un seguimiento con Jaxx si este problema ocurre dentro de la funcionalidad de la billetera Jaxx.

Alternativamente, el usuario puede realizar el retiro utilizando uno de los 4 métodos establecidos en ¿Cómo convierto mis tokens The DAO en éteres usando el contrato de retiro después de la bifurcación dura?

Tenga en cuenta que la mayoría de las transacciones DAO -> ETH que fallan se deben a que hay un saldo de 0 DAO en The DAO. Esto podría deberse a que el usuario ya ejecutó sus reembolsos DAO -> ETH, o está realizando los reembolsos en las cuentas incorrectas.



Detalles

El WithdrawDAOcontrato depende de que el usuario ejecute los DAO approve(...)para permitir que el WithdrawDAOcontrato transfiera los tokens de la cuenta del usuario a sí mismo a cambio de reembolsar el monto equivalente a la cuenta del usuario. Debido a un error de redondeo en alguna parte del proceso, se está aprobando una cantidad inferior al saldo, y esto provoca un error en el proceso de retiro.


Confirmando el error de redondeo

Verifiqué uno de los retiros fallidos con el mensaje de error Warning! Error encountered during contract execution [Bad jump destination]que se muestra a continuación:ingrese la descripción de la imagen aquí

Ejecuté el siguiente script geth consoley confirmó que había un déficit de 341 wei en la cantidad aprobada para la transferencia y el saldo.

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
> 

En el código a continuación, puede ver que WithdrawDAO.withdraw()llama TheDAO.transferFrom(...)con el saldo completo, pero TheDAO.transferFrom(...)tiene los siguientes controles:

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

Lo cual no permitirá un retiro del saldo siendo mayor al monto aprobado.

Aquí hay otra transacción fallida :

> 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

Y otra transacción fallida :

> 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

Y uno más :

> 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

Algunos usuarios no han aprobado la transferencia antes de solicitar el retiro :

> 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
> 

Y algunos tienen saldos cero para aprobar o transferir :

> 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
> 


el WithdrawDAOcontrato

A continuación se muestra un fragmento del contrato WithdrawDAO que muestra el withdraw()método:

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

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


The DAOContrato

Los siguientes son fragmentos de The DAO contact que muestran solo las clases y métodos relevantes:

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;
        }
    }
    ...
}