ETH Price: $1,958.10 (+0.53%)

Transaction Decoder

Block:
23732291 at Nov-05-2025 09:44:59 AM +UTC
Transaction Fee:
0.0000128278878433 ETH $0.03
Gas Used:
40,340 Gas / 0.317994245 Gwei

Emitted Events:

416 MAYAChain_Router.Deposit( to=0x7c1a6a91c515028ba978ed270f32691a7a8469bc, asset=0x00000000...000000000, amount=60451000000000000, memo==:b:bc1qza5095nt3tp584rgyzkhzg7us00k99k565vrhe:0/1/1:ej:75 )

Account State Difference:

  Address   Before After State Difference Code
(Titan Builder)
3.630627541155753169 Eth3.630627582085322269 Eth0.0000000409295691
0x7c1a6a91...a7a8469BC 401.312646573687554822 Eth401.373097573687554822 Eth0.060451
0xcfeEc7a7...a7Fc767D2
0.209311804361471173 Eth
Nonce: 2
0.148847976473627873 Eth
Nonce: 3
0.0604638278878433

Execution Trace

ETH 0.060451 MAYAChain_Router.depositWithExpiry( vault=0x7c1a6a91C515028BA978ED270F32691a7a8469BC, asset=0x0000000000000000000000000000000000000000, amount=60451000000000000, memo==:b:bc1qza5095nt3tp584rgyzkhzg7us00k99k565vrhe:0/1/1:ej:75, expiration=1762339463 )
  • ETH 0.060451 0x7c1a6a91c515028ba978ed270f32691a7a8469bc.CALL( )
    // SPDX-License-Identifier: MIT
    // -------------------
    // Router Version: 4.0
    // -------------------
    pragma solidity 0.8.13;
    
    // ERC20 Interface
    interface iERC20 {
        function balanceOf(address) external view returns (uint256);
    }
    // ROUTER Interface
    interface iROUTER {
        function depositWithExpiry(address, address, uint, string calldata, uint) external;
    }
    
    // MAYAChain_Router is managed by MAYAChain Vaults
    contract MAYAChain_Router {
        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);
    
        // Emitted for all outgoing transferAndCalls, the vault dictates who sent it, memo used to track.
        event TransferOutAndCall(address indexed vault, address target, uint amount, address finalAsset, address to, uint256 amountOutMin, 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() {
            _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, "MAYAChain_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 {
                require(msg.value == 0, "unexpected eth");  // protect user from accidentally locking up eth
                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);
        }
    
        // Any vault calls to transferAndCall on a target contract that conforms with "swapOut(address,address,uint256)"
        // Example Memo: "~1b3:ETH.0xFinalToken:0xTo:"
        // Target is fuzzy-matched to the last three digits of whitelisted aggregators
        // FinalToken, To, amountOutMin come from originating memo
        // Memo passed in here is the "OUT:HASH" type
        function transferOutAndCall(address payable target, address finalToken, address to, uint256 amountOutMin, string memory memo) public payable nonReentrant {
            uint256 _safeAmount = msg.value;
            (bool erc20Success, ) = target.call{value:_safeAmount}(abi.encodeWithSignature("swapOut(address,address,uint256)", finalToken, to, amountOutMin));
            if (!erc20Success) {
                bool ethSuccess = payable(to).send(_safeAmount); // If can't swap, just send the recipient the ETH
                if (!ethSuccess) {
                    payable(address(msg.sender)).transfer(_safeAmount); // For failure, bounce back to Yggdrasil & continue.
                }
            }
            emit TransferOutAndCall(msg.sender, target, _safeAmount, finalToken, to, amountOutMin, 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
        }
    }