ETH Price: $2,656.22 (+4.65%)

Transaction Decoder

Block:
13528000 at Nov-01-2021 12:33:53 AM +UTC
Transaction Fee:
0.01712004 ETH $45.47
Gas Used:
40,762 Gas / 420 Gwei

Emitted Events:

34 THORChain_Router.Deposit( to=0xf56cba49337a624e94042e325ad6bc864436e370, asset=0x00000000...000000000, amount=172060000000000000, memo==:ETH.XRUNE-71C:0x0c43301d63f83bc40c5f81179f386b9c1e1a6b80:64417852111 )

Account State Difference:

  Address   Before After State Difference Code
0x0C43301d...C1e1a6b80
0.196 Eth
Nonce: 0
0.00681996 Eth
Nonce: 1
0.18918004
(F2Pool Old)
5,464.600963958119041768 Eth5,464.611232979534440876 Eth0.010269021415399108
0xf56cBa49...64436E370 4,221.165103648883261265 Eth4,221.337163648883261265 Eth0.17206

Execution Trace

ETH 0.17206 THORChain_Router.deposit( vault=0xf56cBa49337A624E94042e325Ad6Bc864436E370, asset=0x0000000000000000000000000000000000000000, amount=172060000000000000, memo==:ETH.XRUNE-71C:0x0c43301d63f83bc40c5f81179f386b9c1e1a6b80:64417852111 )
  • ETH 0.17206 0xf56cba49337a624e94042e325ad6bc864436e370.CALL( )
    // SPDX-License-Identifier: UNLICENSED
    // -------------------
    // Router Version: 2.0
    // -------------------
    pragma solidity 0.8.3;
    
    // ERC20 Interface
    interface iERC20 {
        function balanceOf(address) external view returns (uint256);
        function burn(uint) external;
    }
    // RUNE Interface
    interface iRUNE {
        function transferTo(address, uint) external returns (bool);
    }
    // ROUTER Interface
    interface iROUTER {
        function depositWithExpiry(address, address, uint, string calldata, uint) external;
    }
    
    // THORChain_Router is managed by THORChain Vaults
    contract THORChain_Router {
        address public RUNE;
    
        struct Coin {
            address asset;
            uint amount;
        }
    
        // Vault allowance for each asset
        mapping(address => mapping(address => uint)) public vaultAllowance;
    
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
    
        // Emitted for all deposits, the memo distinguishes for swap, add, remove, donate etc
        event Deposit(address indexed to, address indexed asset, uint amount, string memo);
    
        // Emitted for all outgoing transfers, the vault dictates who sent it, memo used to track.
        event TransferOut(address indexed vault, address indexed to, address asset, uint amount, string memo);
    
        // Changes the spend allowance between vaults
        event TransferAllowance(address indexed oldVault, address indexed newVault, address asset, uint amount, string memo);
    
        // Specifically used to batch send the entire vault assets
        event VaultTransfer(address indexed oldVault, address indexed newVault, Coin[] coins, string memo);
    
        modifier nonReentrant() {
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            _status = _ENTERED;
            _;
            _status = _NOT_ENTERED;
        }
    
        constructor(address rune) {
            RUNE = rune;
            _status = _NOT_ENTERED;
        }
    
        // Deposit with Expiry (preferred)
        function depositWithExpiry(address payable vault, address asset, uint amount, string memory memo, uint expiration) external payable {
            require(block.timestamp < expiration, "THORChain_Router: expired");
            deposit(vault, asset, amount, memo);
        }
    
        // Deposit an asset with a memo. ETH is forwarded, ERC-20 stays in ROUTER
        function deposit(address payable vault, address asset, uint amount, string memory memo) public payable nonReentrant{
            uint safeAmount;
            if(asset == address(0)){
                safeAmount = msg.value;
                (bool success,) = vault.call{value:safeAmount}("");
                require(success);
            } else if(asset == RUNE) {
                safeAmount = amount;
                iRUNE(RUNE).transferTo(address(this), amount);
                iERC20(RUNE).burn(amount);
            } else {
                safeAmount = safeTransferFrom(asset, amount); // Transfer asset
                vaultAllowance[vault][asset] += safeAmount; // Credit to chosen vault
            }
            emit Deposit(vault, asset, safeAmount, memo);
        }
    
        //############################## ALLOWANCE TRANSFERS ##############################
    
        // Use for "moving" assets between vaults (asgard<>ygg), as well "churning" to a new Asgard
        function transferAllowance(address router, address newVault, address asset, uint amount, string memory memo) external {
            if (router == address(this)){
                _adjustAllowances(newVault, asset, amount);
                emit TransferAllowance(msg.sender, newVault, asset, amount, memo);
            } else {
                _routerDeposit(router, newVault, asset, amount, memo);
            }
        }
    
        //############################## ASSET TRANSFERS ##############################
    
        // Any vault calls to transfer any asset to any recipient.
        function transferOut(address payable to, address asset, uint amount, string memory memo) public payable nonReentrant {
            uint safeAmount; bool success;
            if(asset == address(0)){
                safeAmount = msg.value;
                (success,) = to.call{value:msg.value}(""); // Send ETH
            } else {
                vaultAllowance[msg.sender][asset] -= amount; // Reduce allowance
                (success,) = asset.call(abi.encodeWithSignature("transfer(address,uint256)" , to, amount));
                safeAmount = amount;
            }
            require(success);
            emit TransferOut(msg.sender, to, asset, safeAmount, memo);
        }
    
        // Batch Transfer
        function batchTransferOut(address[] memory recipients, Coin[] memory coins, string[] memory memos) external payable {
            require((recipients.length == coins.length) && (coins.length == memos.length));
            for(uint i = 0; i < coins.length; i++){
                transferOut(payable(recipients[i]), coins[i].asset, coins[i].amount, memos[i]);
            }
        }
    
        //############################## VAULT MANAGEMENT ##############################
    
        // A vault can call to "return" all assets to an asgard, including ETH. 
        function returnVaultAssets(address router, address payable asgard, Coin[] memory coins, string memory memo) external payable {
            if (router == address(this)){
                for(uint i = 0; i < coins.length; i++){
                    _adjustAllowances(asgard, coins[i].asset, coins[i].amount);
                }
                emit VaultTransfer(msg.sender, asgard, coins, memo); // Does not include ETH.           
            } else {
                for(uint i = 0; i < coins.length; i++){
                    _routerDeposit(router, asgard, coins[i].asset, coins[i].amount, memo);
                }
            }
            (bool success,) = asgard.call{value:msg.value}(""); //ETH amount needs to be parsed from tx.
            require(success);
        }
    
        //############################## HELPERS ##############################
    
        // Safe transferFrom in case asset charges transfer fees
        function safeTransferFrom(address _asset, uint _amount) internal returns(uint amount) {
            uint _startBal = iERC20(_asset).balanceOf(address(this));
            (bool success,) = _asset.call(abi.encodeWithSignature("transferFrom(address,address,uint256)", msg.sender, address(this), _amount));
            require(success);
            return (iERC20(_asset).balanceOf(address(this)) - _startBal);
        }
    
        // Decrements and Increments Allowances between two vaults
        function _adjustAllowances(address _newVault, address _asset, uint _amount) internal {
            vaultAllowance[msg.sender][_asset] -= _amount;
            vaultAllowance[_newVault][_asset] += _amount;
        }
    
        // Adjust allowance and forwards funds to new router, credits allowance to desired vault
        function _routerDeposit(address _router, address _vault, address _asset, uint _amount, string memory _memo) internal {
            vaultAllowance[msg.sender][_asset] -= _amount;
            (bool success,) = _asset.call(abi.encodeWithSignature("approve(address,uint256)", _router, _amount)); // Approve to transfer
            require(success);
            iROUTER(_router).depositWithExpiry(_vault, _asset, _amount, _memo, type(uint).max); // Transfer by depositing
        }
    }