Transaction Hash:
Block:
13945737 at Jan-05-2022 01:36:02 PM +UTC
Transaction Fee:
0.01115828 ETH
$26.98
Gas Used:
39,851 Gas / 280 Gwei
Emitted Events:
0 |
THORChain_Router.TransferOut( vault=[Sender] 0x94724a455e7f18a966bd2076af23cbacf374b16d, to=0xea2cfc0456be11797bb41b2758a6238d8142dcb6, asset=0x00000000...000000000, amount=384397140000000000, memo=OUT:35E900370C0CD63385407603F1FD0AA34AD0B98294FCC85D6612577F624D6045 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x7F101fE4...353f2B90c
Miner
| (Flexpool.io) | 4,856.417994001481972665 Eth | 4,856.425532376076697786 Eth | 0.007538374594725121 | |
0x94724a45...cf374B16D |
47.505518827461980299 Eth
Nonce: 349
|
47.109963407461980299 Eth
Nonce: 350
| 0.39555542 | ||
0xEA2CFC04...d8142dCb6 | 0.01168957 Eth | 0.39608671 Eth | 0.38439714 |
Execution Trace
ETH 0.38439714
THORChain_Router.transferOut( to=0xEA2CFC0456BE11797bB41b2758A6238d8142dCb6, asset=0x0000000000000000000000000000000000000000, amount=384397140000000000, memo=OUT:35E900370C0CD63385407603F1FD0AA34AD0B98294FCC85D6612577F624D6045 )
- ETH 0.38439714
0xea2cfc0456be11797bb41b2758a6238d8142dcb6.CALL( )
transferOut[THORChain_Router (ln:101)]
send[THORChain_Router (ln:105)]
transfer[THORChain_Router (ln:107)]
payable[THORChain_Router (ln:107)]
call[THORChain_Router (ln:111)]
encodeWithSignature[THORChain_Router (ln:111)]
decode[THORChain_Router (ln:112)]
TransferOut[THORChain_Router (ln:115)]
// SPDX-License-Identifier: MIT // ------------------- // Router Version: 3.0 // ------------------- pragma solidity 0.8.10; // 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)) private _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.send(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 nonReentrant { 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. // Note: Contract recipients of ETH are only given 2300 Gas to complete execution. function transferOut(address payable to, address asset, uint amount, string memory memo) public payable nonReentrant { uint safeAmount; if(asset == address(0)){ safeAmount = msg.value; bool success = to.send(safeAmount); // Send ETH. if (!success) { payable(address(msg.sender)).transfer(safeAmount); //For failure, bounce back to Yggdrasil & continue. } } else { _vaultAllowance[msg.sender][asset] -= amount; // Reduce allowance (bool success, bytes memory data) = asset.call(abi.encodeWithSignature("transfer(address,uint256)" , to, amount)); require(success && (data.length == 0 || abi.decode(data, (bool)))); safeAmount = amount; } emit TransferOut(msg.sender, to, asset, safeAmount, memo); } //############################## 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 nonReentrant { 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.send(msg.value); require(success); } //############################## HELPERS ############################## function vaultAllowance(address vault, address token) public view returns(uint amount){ return _vaultAllowance[vault][token]; } // 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, bytes memory data) = _asset.call(abi.encodeWithSignature("transferFrom(address,address,uint256)", msg.sender, address(this), _amount)); require(success && (data.length == 0 || abi.decode(data, (bool)))); 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 } }