ETH Price: $1,879.30 (-4.75%)

Contract Diff Checker

Contract Name:
Forwarder

Contract Source Code:

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/**
 * Contract that exposes the needed ERC20 token functions
 */
interface ERC20Interface {
    // Send _value amount of tokens to address _to
    function transfer(address _to, uint256 _value) external returns (bool);

    // Get the account balance of another account with address _owner
    function balanceOf(address _owner) external view returns (uint256 balance);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "./ERC20Interface.sol";

/**
 * Contract that will forward any incoming Ether and tokens to a designated destination address
 */
contract Forwarder {
    uint256 private constant _UNLOCKED = 1;
    uint256 private constant _LOCKED = 2;
    uint256 private lock;

    // Address to which any funds sent to this contract will be forwarded
    address public ownerAddress;
    // Address that can trigger flush
    address public tokenFlusherAddress;

    event ForwardedDeposit(address indexed from, address indexed to, address indexed forwardedTo, uint256 _amount);
    event TokensFlushed(address forwarderAddress, uint value, address tokenContractAddress);
    event OwnerChanged(address indexed oldOwner, address indexed newOwner);
    event TokenFlusherChanged(address indexed oldFlusher, address indexed newFlusher);

    error InvalidOwner();
    error InvalidFlusher();
    error NotAuthorized();
    error TransferFailed(string transferType);
    error InsufficientBalance();
    error NewValueMustBeDifferent();
    error ReentrantCallDetected();

    /**
     * Create the contract, and sets the owner address and token flusher address
     */
    constructor(address ownerAddress_, address tokenFlusherAddress_) {
        if (ownerAddress_ == address(0)) {
            revert InvalidOwner();
        }
        if (tokenFlusherAddress_ == address(0)) {
            revert InvalidFlusher();
        }
        ownerAddress = ownerAddress_;
        tokenFlusherAddress = tokenFlusherAddress_;
        lock = _UNLOCKED;
    }

    /**
     * Default function; Gets called when Ether is deposited, and forwards it to the owner address
     */
    receive() external payable nonReentrant {
        (bool success,) = ownerAddress.call{value: msg.value}("");
        if (!success) {
            revert TransferFailed("ETH forward");
        }
        emit ForwardedDeposit(msg.sender, address(this), ownerAddress, msg.value);
    }

    /**
     * It is possible that funds were sent to this address before the contract was deployed.
     * We can flush those funds to the parent address.
     */
    function flush() public nonReentrant {
        uint256 balance = address(this).balance;
        if (balance == 0) {
            revert InsufficientBalance();
        }
        (bool success,) = ownerAddress.call{value: balance}("");
        if (!success) {
            revert TransferFailed("ETH flush");
        }
    }

    /**
     * Execute a token transfer of the full balance from the forwarder token to the parent address
     * @param tokenContractAddress the address of the ERC20 token contract
     */
    function flushTokens(address tokenContractAddress) public onlyOwnerOrTokenFlusher {
        ERC20Interface instance = ERC20Interface(tokenContractAddress);
        uint256 forwarderBalance = instance.balanceOf(address(this));
        if (forwarderBalance == 0) {
            revert InsufficientBalance();
        }

        // Use low-level call to handle tokens that don't return a value
        (bool success, bytes memory data) = address(instance).call(
            abi.encodeWithSelector(instance.transfer.selector, ownerAddress, forwarderBalance)
        );

        if (!success || (data.length != 0 && !abi.decode(data, (bool)))) {
            revert TransferFailed("Token flush");
        }

        emit TokensFlushed(address(this), forwarderBalance, tokenContractAddress);
    }

    modifier onlyOwnerOrTokenFlusher {
        if (msg.sender != ownerAddress && msg.sender != tokenFlusherAddress) {
            revert NotAuthorized();
        }
        _;
    }

    modifier onlyOwner {
        if (msg.sender != ownerAddress) {
            revert NotAuthorized();
        }
        _;
    }

    function changeOwner(address newOwner) public onlyOwner {
        if (newOwner == address(0)) {
            revert InvalidOwner();
        }
        if (newOwner == ownerAddress) {
            revert NewValueMustBeDifferent();
        }

        address oldOwner = ownerAddress;
        ownerAddress = newOwner;
        emit OwnerChanged(oldOwner, newOwner);
    }

    function changeTokenFlusher(address newFlusher) public onlyOwner {
        if (newFlusher == address(0)) {
            revert InvalidFlusher();
        }
        if (newFlusher == tokenFlusherAddress) {
            revert NewValueMustBeDifferent();
        }

        address oldFlusher = tokenFlusherAddress;
        tokenFlusherAddress = newFlusher;
        emit TokenFlusherChanged(oldFlusher, newFlusher);
    }

    modifier nonReentrant() {
        if (lock == _LOCKED) {
            revert ReentrantCallDetected();
        }
        lock = _LOCKED;
        _;
        lock = _UNLOCKED;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):