Почему не удается вернуть деньги с DAO на ETH?

У пользователя thadao.slack.com возникли проблемы с выводом возмещения средств через контактное лицо DAO Withdrawal.

Я проверил WithdrawDAOконтакт и заметил, что есть много других неудачных транзакций:введите описание изображения здесь

Почему не удается вернуть пользователю The DAO в ETH?

В этом случае пользователь использовал кошелек Jaxx для возврата средств.

Смотрите также:

Ответы (1)

Резюме

Проблема. Похоже, что в некоторых кошельках возникают ошибки округления.

Решение . Решение состоит в том, чтобы утвердить сумму, превышающую баланс в The DAO. Пользователь должен связаться с Jaxx, если эта проблема возникает в рамках функций кошелька Jaxx.

В качестве альтернативы пользователь может выполнить вывод, используя один из 4 методов, описанных в разделе Как мне конвертировать мои токены DAO в эфиры, используя контракт на вывод средств после хард-форка?

Обратите внимание , что большинство неудачных транзакций DAO -> ETH связаны с нулевым балансом DAO в DAO. Это может быть связано с тем, что пользователь уже осуществил возврат средств DAO -> ETH или выполняет возврат средств на неправильные учетные записи.



Подробности

Контракт WithdrawDAOполагается на то, что пользователь выполняет DAO, approve(...)чтобы позволить WithdrawDAOконтракту передавать токены со счета пользователя на себя в обмен на возврат эквивалентной суммы на счет пользователя. Из-за ошибки округления где-то в процессе утверждается сумма меньше остатка, и это вызывает ошибку в процессе вывода средств.


Подтверждение ошибки округления

Я проверил один из неудачных выводов с сообщением об ошибке, Warning! Error encountered during contract execution [Bad jump destination]как показано ниже:введите описание изображения здесь

Я запустил следующий скрипт geth console, и он подтвердил, что в утвержденной для перевода сумме и остатке не хватает 341 вей.

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
> 

В коде ниже видно, что WithdrawDAO.withdraw()звонки TheDAO.transferFrom(...)с полным балансом, но TheDAO.transferFrom(...)есть следующие проверки:

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

Что не позволит снять баланс, превышающий утвержденную сумму.

Вот еще одна неудачная транзакция :

> 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

И еще одна неудачная транзакция :

> 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

И еще одно :

> 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

Некоторые пользователи не одобрили перевод до вызова вывода средств :

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

А у некоторых есть нулевые остатки для утверждения или перевода :

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


Контракт WithdrawDAO_

Ниже приведен фрагмент контракта WithdrawDAO, показывающий withdraw()метод:

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

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


The DAOДоговор

Ниже приведены фрагменты из контакта DAO , показывающие только соответствующие классы и методы:

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