MegaBeastV3

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

// File: contracts/BaseRelayRecipient.sol


pragma solidity ^0.8.0;

abstract contract BaseRelayRecipient {

    address public trustedForwarder;

    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == trustedForwarder;
    }

    function versionRecipient() external view virtual returns (string memory) {
        return "3.0.0-beta.10";
    }

    function _setTrustedForwarder(address _trustedForwarder) internal virtual;

    function _msgSender() internal view virtual returns (address) {
        if (isTrustedForwarder(msg.sender)) {
            assembly {
                return(add(calldataload(sub(calldatasize(), 20)), 12), 20)
            }
        } else {
            return msg.sender;
        }
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        if (isTrustedForwarder(msg.sender)) {
            return msg.data[:msg.data.length - 20];
        } else {
            return msg.data;
        }
    }
}
// File: contracts/ECDSA.sol


// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions are designed to operate on message hashes that are signed using the personal_sign
 * method. To verify a message signed witheth_sign, calculate the hash of the message
 * using {toEthSignedMessageHash}.
 *
 * See {recover}.
 */
library ECDSA {
    /**
     * @dev Recovers the address that signed a hash with a standard EIP-2098 signature.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the hash of the message signed
            // v, r, s are components of the signature
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return ecrecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 s;
            // ecrecover takes the signature parameters, and the hash of the message signed
            // r, s are components of the signature
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x00))
            }
            return ecrecover(hash, 27, r, s);
        } else {
            revert("ECDSA: invalid signature length");
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This produces hash corresponding
     * to the one signed with the JSON-RPC method `eth_sign`.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:
32", hash));
    }
}
// File: contracts/MegaBeastV3.sol


pragma solidity ^0.8.20;

/**
 * @title MegaBeastV3 (Corrected)
 * @notice A corrected and secured version of the arbitrage contract.
 * @dev Fixes critical flaws in slippage protection and profit calculation.
 */



// Interfaces (condensed for brevity)
interface IERC20 {
    function balanceOf(address owner) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
}

interface ILendingPool {
    function flashLoan(address receiver, address[] calldata assets, uint256[] calldata amounts, uint256[] calldata modes, address onBehalfOf, bytes calldata params, uint16 referralCode) external;
}

interface IFlashLoanReceiver {
    function executeOperation(address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, bytes calldata params) external returns (bool);
}

// CORRECTED: SwapStep now includes the outputToken to accurately measure swap results.
struct SwapStep {
    address router;         // DEX router for this step
    address outputToken;    // The token this swap should produce
    uint256 minAmountOut;   // The minimum amount of outputToken to receive
    bytes callData;         // The swap calldata for the router
}

contract MegaBeastV3 is BaseRelayRecipient, IFlashLoanReceiver {
    using ECDSA for bytes32;

    address public owner;
    ILendingPool public immutable LENDING_POOL;
    address public profitToken;

    address[] public sweepTokens;
    mapping(uint256 => bool) public usedNonces;

    uint256 public minProfitWei;
    bool public paused;

    uint256 private _status = 1;

    // Modifiers
    modifier onlyOwner() {
        require(_msgSender() == owner, "Not owner");
        _;
    }
    modifier nonReentrant() {
        require(_status == 1, "Reentrant call");
        _status = 2;
        _;
        _status = 1;
    }
    modifier whenNotPaused() {
        require(!paused, "Paused");
        _;
    }

    // Events
    event SweptToken(address indexed token, uint256 amount, address indexed relayer, uint256 feePaid);
    event ArbitrageExecuted(address indexed profitToken, uint256 profit);
    event ArbitrageStarted(address[] assets, uint256[] amounts);
    event Paused();
    event Unpaused();

    constructor(
        address lendingPoolAddress,
        address trustedForwarder,
        address initialProfitToken,
        uint256 _minProfitWei
    ) {
        require(lendingPoolAddress != address(0) && trustedForwarder != address(0) && initialProfitToken != address(0), "Zero address");
        owner = msg.sender;
        LENDING_POOL = ILendingPool(lendingPoolAddress);
        profitToken = initialProfitToken;
        minProfitWei = _minProfitWei;
        _setTrustedForwarder(trustedForwarder);
    }

    // --- FIXED: IMPLEMENTATION FOR BaseRelayRecipient ---
    function _setTrustedForwarder(address _trustedForwarder) internal override {
        trustedForwarder = _trustedForwarder;
    }

    /*** Flashloan Arbitrage ***/
    function startArbitrage(
        address[] calldata assetsToBorrow,
        uint256[] calldata amountsToBorrow,
        bytes calldata swapStepsEncoded
    ) external onlyOwner whenNotPaused {
        require(assetsToBorrow.length > 0 && assetsToBorrow.length == amountsToBorrow.length, "Invalid input");
        emit ArbitrageStarted(assetsToBorrow, amountsToBorrow);

        uint256[] memory modes = new uint256[](assetsToBorrow.length);
        LENDING_POOL.flashLoan(address(this), assetsToBorrow, amountsToBorrow, modes, address(this), swapStepsEncoded, 0);
    }

    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address,
        bytes calldata params
    ) external override nonReentrant returns (bool) {
        require(msg.sender == address(LENDING_POOL), "Unauthorized");

        uint256 initialProfitTokenBalance = IERC20(profitToken).balanceOf(address(this));
        SwapStep[] memory steps = abi.decode(params, (SwapStep[]));

        for (uint i = 0; i < steps.length; i++) {
            uint256 balanceBefore = IERC20(steps[i].outputToken).balanceOf(address(this));
            (bool success, ) = steps[i].router.call(steps[i].callData);
            require(success, "DEX swap failed");
            uint256 balanceAfter = IERC20(steps[i].outputToken).balanceOf(address(this));
            require(balanceAfter > balanceBefore, "No output from swap");
            uint256 amountOut = balanceAfter - balanceBefore;
            require(amountOut >= steps[i].minAmountOut, "Slippage exceeded");
        }

        for (uint i = 0; i < assets.length; i++) {
            uint256 debt = amounts[i] + premiums[i];
            require(IERC20(assets[i]).balanceOf(address(this)) >= debt, "Insufficient balance to repay");
            IERC20(assets[i]).approve(address(LENDING_POOL), debt);
        }

        uint256 finalProfitTokenBalance = IERC20(profitToken).balanceOf(address(this));
        if (finalProfitTokenBalance > initialProfitTokenBalance) {
            uint256 profit = finalProfitTokenBalance - initialProfitTokenBalance;
            require(profit >= minProfitWei, "Profit below threshold");
            IERC20(profitToken).transfer(owner, profit);
            emit ArbitrageExecuted(profitToken, profit);
        } else {
            require(finalProfitTokenBalance >= initialProfitTokenBalance, "Profit token loss");
        }
        return true;
    }

    /*** Gasless Sweeps ***/
    function sweepTokensGasless(uint256 nonce, uint16 feeBps, bytes calldata ownerSignature) external whenNotPaused {
        require(!usedNonces[nonce], "Nonce used");
        require(feeBps <= 1000, "Fee too high");

        bytes32 hash = keccak256(abi.encodePacked(nonce, feeBps, address(this)));
        address signer = hash.toEthSignedMessageHash().recover(ownerSignature);
        require(signer == owner, "Invalid signature");

        usedNonces[nonce] = true;

        for (uint i = 0; i < sweepTokens.length; i++) {
            address token = sweepTokens[i];
            uint256 balance = IERC20(token).balanceOf(address(this));
            if (balance == 0) continue;
            uint256 fee = (balance * feeBps) / 10000;
            uint256 net = balance - fee;
            if (fee > 0) IERC20(token).transfer(_msgSender(), fee);
            if (net > 0) IERC20(token).transfer(owner, net);
            emit SweptToken(token, balance, _msgSender(), fee);
        }
    }

    /*** Owner Controls ***/
    function setSweepTokens(address[] calldata tokens) external onlyOwner {
        sweepTokens = tokens;
    }
    function setProfitToken(address newProfitToken) external onlyOwner {
        require(newProfitToken != address(0), "Zero address");
        profitToken = newProfitToken;
    }
    function setMinProfit(uint256 minWei) external onlyOwner {
        minProfitWei = minWei;
    }
    function pause() external onlyOwner {
        paused = true;
        emit Paused();
    }
    function unpause() external onlyOwner {
        paused = false;
        emit Unpaused();
    }
    function withdraw(address token, uint256 amount) external onlyOwner {
        uint256 bal = IERC20(token).balanceOf(address(this));
        uint256 toSend = (amount == 0 || amount > bal) ? bal : amount;
        if (toSend > 0) IERC20(token).transfer(owner, toSend);
    }
    function withdrawEther() external onlyOwner {
        uint256 bal = address(this).balance;
        if (bal > 0) payable(owner).transfer(bal);
    }

    receive() external payable {}
}

Tags:
Multisig, Swap, Upgradeable, Multi-Signature|addr:0xac58385134bebf2c4fb0a06eea692480ab0ca254|verified:true|block:23401496|tx:0xe27478e3ef25fdc4d368ef2b128ca0bdcdcf869f559ad4d26a4d5ecad549e095|first_check:1758368180

Submitted on: 2025-09-20 13:36:21

Comments

Log in to comment.

No comments yet.