argent錢包軟件轉賬免手續費之謎——轉賬流程

終於到了要解析其轉賬的流程了。不過這裏我不用自己的賬號來做,因爲我暫時不想轉出來,那就用我好友的案例來分析吧。

下面是他自己的錢包最終受到0.01ETH轉賬的截圖

仔細看這筆交易的詳情

首先這個From地址是一個外部賬戶,這個To地址是TransferManager合約地址,而合約裏的from是他的錢包合約地址,to就是他的另一個以太坊錢包地址。

從這裏可以看出是通過外部賬號From調用TransferManager合約的某個接口來實現從錢包合約地址把ETH轉到自己指定的錢包地址的。

這下就很清楚了。當我在手機APP上填寫好轉賬信息,並點擊轉賬按鈕之後,實際上不是我直接控制我的錢包合約把ETH從錢包合約轉到我指定的以太坊錢包地址。而是間接的會觸發一箇中心化的服務,這個服務會調用外部賬號執行TransferManager合約的某個接口來完成我的這筆轉賬請求。

那調用的是TransferManager的哪個接口呢?

看來還得去Event log裏找。根據區塊高度,在TransferManager合約的Event log裏找到以下內容

下面看看這個execute函數的源碼:

    function execute(
        BaseWallet _wallet,
        bytes calldata _data,
        uint256 _nonce,
        bytes calldata _signatures,
        uint256 _gasPrice,
        uint256 _gasLimit
    )
        external
        returns (bool success)
    {
        uint startGas = gasleft();
        bytes32 signHash = getSignHash(address(this), address(_wallet), 0, _data, _nonce, _gasPrice, _gasLimit);
        require(checkAndUpdateUniqueness(_wallet, _nonce, signHash), "RM: Duplicate request");
        require(verifyData(address(_wallet), _data), "RM: the wallet authorized is different then the target of the relayed data");
        uint256 requiredSignatures = getRequiredSignatures(_wallet, _data);
        if ((requiredSignatures * 65) == _signatures.length) {
            if (verifyRefund(_wallet, _gasLimit, _gasPrice, requiredSignatures)) {
                if (requiredSignatures == 0 || validateSignatures(_wallet, _data, signHash, _signatures)) {
                    // solium-disable-next-line security/no-call-value
                    (success,) = address(this).call(_data);
                    refund(_wallet, startGas - gasleft(), _gasPrice, _gasLimit, requiredSignatures, msg.sender);
                }
            }
        }
        emit TransactionExecuted(address(_wallet), success, signHash);
    }

看到這裏只有一個  emit TransactionExecuted(address(_wallet), success, signHash);

那另外一個Transfer事件日誌是在哪裏?

仔細研究了一番,只有一行代碼有可能

(success,) = address(this).call(_data);

但是這裏的data是直接從鏈下傳進來的,現在無從得知。不過可以肯定的是,調用這個代碼會往Event log寫入Transfer日誌。搜遍源碼只有一個函數:

contract BaseTransfer is BaseModule {

...

function doTransfer(BaseWallet _wallet, address _token, address _to, uint256 _value, bytes memory _data) internal {
        if (_token == ETH_TOKEN) {
            invokeWallet(address(_wallet), _to, _value, EMPTY_BYTES);
        } else {
            bytes memory methodData = abi.encodeWithSignature("transfer(address,uint256)", _to, _value);
            invokeWallet(address(_wallet), _token, 0, methodData);
        }
        emit Transfer(address(_wallet), _token, _value, _to, _data);
    }

...

}

這裏核心代碼就幾行

if (_token == ETH_TOKEN) {
            invokeWallet(address(_wallet), _to, _value, EMPTY_BYTES);
} 

這裏的EMPTY_BYTES就是空字符串,定義如下:

bytes constant internal EMPTY_BYTES = "";

繼續往下dig

function invokeWallet(address _wallet, address _to, uint256 _value, bytes memory _data) internal returns (bytes memory _res) {
        bool success;
        // solium-disable-next-line security/no-call-value
        (success, _res) = _wallet.call(abi.encodeWithSignature("invoke(address,uint256,bytes)", _to, _value, _data));
        if (success && _res.length > 0) { //_res is empty if _wallet is an "old" BaseWallet that can't return output values
            (_res) = abi.decode(_res, (bytes));
        } else if (_res.length > 0) {
            // solium-disable-next-line security/no-inline-assembly
            assembly {
                returndatacopy(0, 0, returndatasize)
                revert(0, returndatasize)
            }
        } else if (!success) {
            revert("BM: wallet invoke reverted");
        }
    }

看到下面這樣代碼

_wallet.call(abi.encodeWithSignature("invoke(address,uint256,bytes)", _to, _value, _data));

繼續往下dig

function invoke(address _target, uint _value, bytes calldata _data) external moduleOnly returns (bytes memory _result) {
        bool success;
        // solium-disable-next-line security/no-call-value
        (success, _result) = _target.call.value(_value)(_data);
        if (!success) {
            // solium-disable-next-line security/no-inline-assembly
            assembly {
                returndatacopy(0, 0, returndatasize)
                revert(0, returndatasize)
            }
        }
        emit Invoked(msg.sender, _target, _value, _data);
    }

終於到頭了,看到這句

(success, _result) = _target.call.value(_value)(_data);

這其實就是把_value這麼多ETH轉向_target這個地址。不過有點讓我很疑惑。看下圖

本來這裏是空字符串的,按照道理來說應該是全零啊,爲什麼會有個莫名其妙的0x40在這裏?

 

(全文完)

參考資料:

https://medium.com/daox/three-methods-to-transfer-funds-in-ethereum-by-means-of-solidity-5719944ed6e9

https://stackoverflow.com/questions/50762019/solidity-tx-destination-call-valuetx-valuetx-data

https://solidity.readthedocs.io/en/latest/control-structures.html#external-function-calls

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章