contract ExploitCompVAkroSwap is OwnableUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable {
    using SafeERC20 for IERC20;
    using SafeMathUpgradeable for uint256;

    event Swapped(address indexed receiver, uint256 amount);

    uint256 public minAmountToClaim = 0;
    bytes32[] public merkleRoots;
    mapping (address => uint256) public swapped;
    address public vAkro;

    modifier enough(uint256 _claimAmount) {
        require(_claimAmount > 0 && _claimAmount >= minAmountToClaim, "Insufficient vAkro amount");
        _;
    }

    /**
     * @param _vAkro Vested Akro address
     * if_succeeds {:msg "you must specify vAkro"} vAkro != address(0);
     */
    function initialize(address _vAkro) virtual public initializer {
        __Ownable_init();
        __Pausable_init();
        __ReentrancyGuard_init();

        vAkro = _vAkro;
    }

    /**
     * @notice Sets the minimum amount of  which can be swapped. 0 by default
     * @param _minAmount Minimum amount in wei (the least decimals)
     * if_succeeds {:msg "onlyOwner"} old(msg.sender == owner());
     */
    function setMinClaimAmount(uint256 _minAmount) external onlyOwner {
        minAmountToClaim = _minAmount;
    }

    /**
     * @notice Sets the Merkle roots
     * @param _merkleRoots Array of hashes
     * if_succeeds {:msg "merkle root not updated"} merkleRoots.length == _merkleRoots.length;
     * if_succeeds {:msg "onlyOwner"} old(msg.sender == owner());
     */
    function setMerkleRoots(bytes32[] memory _merkleRoots) external onlyOwner {
        require(_merkleRoots.length > 0, "Incorrect data");
        if (merkleRoots.length > 0) {
            delete merkleRoots;
        }
        merkleRoots = new bytes32[](_merkleRoots.length);
        merkleRoots = _merkleRoots;
    }

    /**
     * @notice Allows to swap vAkro
     * @param _merkleRootIndex Index of a merkle root to be used for calculations
     * @param _amountAllowedToClaim Maximum vAkro allowed for a user to swap
     * @param _merkleProofs Array of consiquent merkle hashes
     * if_succeeds {:msg "wrong _merkleRootIndex"} _merkleRootIndex >= 0 && _merkleRootIndex < merkleRoots.length;
     * if_succeeds {:msg "wrong _amountAllowedToClaim"} _amountAllowedToClaim > 0;
     * if_succeeds {:msg "wrong _merkleProofs"} _merkleProofs.length > 0;
     * if_succeeds {:msg "vAkro not received"} old(IERC20(vAkro).balanceOf(address(this))) - (_amountAllowedToClaim - old(swapped[_msgSender()])) == IERC20(vAkro).balanceOf(address(this));
     * if_succeeds {:msg "vAkro not received"} old(IERC20(vAkro).balanceOf(_msgSender())) + (_amountAllowedToClaim - old(swapped[_msgSender()])) == IERC20(vAkro).balanceOf(_msgSender());
     * if_succeeds {:msg "no data about receiving a swap is saved"} old(swapped[_msgSender()]) + (_amountAllowedToClaim - old(swapped[_msgSender()])) == swapped[_msgSender()];
     */
    function swap(
        uint256 _merkleRootIndex,
        uint256 _amountAllowedToClaim,
        bytes32[] memory _merkleProofs
    )
    external nonReentrant whenNotPaused enough(_amountAllowedToClaim)
    {
        require(verifyMerkleProofs(_msgSender(), _merkleRootIndex, _amountAllowedToClaim, _merkleProofs), "Merkle proofs not verified");
        uint256 availableAmount = _amountAllowedToClaim.sub(swapped[_msgSender()]);
        require(availableAmount != 0 && availableAmount >= minAmountToClaim, "Not enough tokens");
        swapped[_msgSender()] = swapped[_msgSender()].add(availableAmount);
        IERC20Mintable(vAkro).mint(address(this), availableAmount);
        IERC20(vAkro).safeTransfer(_msgSender(), availableAmount);
        emit Swapped(_msgSender(), availableAmount);
    }

    /**
     * @notice Verifies merkle proofs of user to be elligible for swap
     * @param _account Address of a user
     * @param _merkleRootIndex Index of a merkle root to be used for calculations
     * @param _amountAllowedToClaim Maximum ADEL allowed for a user to swap
     * @param _merkleProofs Array of consiquent merkle hashes
     * if_succeeds {:msg "you must specify account"} _account != address(0);
     * if_succeeds {:msg "wrong merkleRootIndex"} _merkleRootIndex >= 0 && _merkleRootIndex < merkleRoots.length;
     * if-succeeds {:msg "wrong amountAllowedToClaim"} _amountAllowedToClaim > 0;
     */
    function verifyMerkleProofs(
        address _account,
        uint256 _merkleRootIndex,
        uint256 _amountAllowedToClaim,
        bytes32[] memory _merkleProofs) virtual public view returns(bool)
    {
        require(_merkleProofs.length > 0, "No Merkle proofs");
        require(_merkleRootIndex < merkleRoots.length, "Merkle roots are not set");

        bytes32 node = keccak256(abi.encodePacked(_account, _amountAllowedToClaim));
        return MerkleProofUpgradeable.verify(_merkleProofs, merkleRoots[_merkleRootIndex], node);
    }

    /**
     * @notice Called by the owner to pause, deny swap
     * if_succeeds {:msg "not paused"} paused() == true;
     * if_succeeds {:msg "onlyOwner"} old(msg.sender == owner());
     */
    function pause() onlyOwner whenNotPaused external {
        _pause();
    }

    /**
     * @notice Called by the owner to unpause, allow swap
     * if_succeeds {:msg "paused"} paused() == false;
     * if_succeeds {:msg "onlyOwner"} old(msg.sender == owner());
     */
    function unpause() onlyOwner whenPaused external {
        _unpause();
    }
}
function initialize(address _vAkro) virtual public initializer {
    __Ownable_init();
    __Pausable_init();
    __ReentrancyGuard_init();

    vAkro = _vAkro;
}

/**
 * @param _vAkro Vested Akro address
 * if_succeeds {:msg "you must specify vAkro"} vAkro != address(0);
 */