Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x0C43301d...C1e1a6b80 |
0.196 Eth
Nonce: 0
|
0.00681996 Eth
Nonce: 1
| 0.18918004 | ||
0x829BD824...93333A830
Miner
| (F2Pool Old) | 5,464.600963958119041768 Eth | 5,464.611232979534440876 Eth | 0.010269021415399108 | |
0xf56cBa49...64436E370 | 4,221.165103648883261265 Eth | 4,221.337163648883261265 Eth | 0.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( )
deposit[THORChain_Router (ln:68)]
call[THORChain_Router (ln:72)]
transferTo[THORChain_Router (ln:76)]
burn[THORChain_Router (ln:77)]
safeTransferFrom[THORChain_Router (ln:79)]
balanceOf[THORChain_Router (ln:144)]
call[THORChain_Router (ln:145)]
encodeWithSignature[THORChain_Router (ln:145)]
balanceOf[THORChain_Router (ln:147)]
Deposit[THORChain_Router (ln:82)]
// 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 } }