// Contract: Swap
// Target function: function addTranche (uint256 total, uint256 available, uint256 lockDuration, uint16 price) public onlyAdmin returns (uint256)
// SPDX-License-Identifier: GPL-3.0 AND MIT
/** 
 *  SourceUnit: /home/dos/dev/clients/cointinum/vaultContract/contracts/Swap.sol
*/
            
////// SPDX-License-Identifier-FLATTEN-SUPPRESS-WARNING: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}




/** 
 *  SourceUnit: /home/dos/dev/clients/cointinum/vaultContract/contracts/Swap.sol
*/
            
////// SPDX-License-Identifier-FLATTEN-SUPPRESS-WARNING: MIT
pragma solidity ^0.8.17;
interface VaultInterface {
    // Events
    event Minted(address indexed minter, uint256 amount);
    event BRX_Supplied(address indexed depositer, uint256 amount, uint256 timestamp);
    event BRXPurchased(
        address indexed purchaser,
        uint256 usdcAmount,
        uint256 cmtAllotedAmount
    );
    event TokenWithdraw(address indexed user, bytes32 token, uint256 payment);
    function setPaymentToken(address token) external;
    function setPenaltyAmount(uint256 _amount) external;
    function setFundsAddress(address _fundsAddress) external;
    function supplyBRX (uint256 _amount) external;
    function removeBRX (uint256 _amount) external;
    function removePaymentTokens(uint256 _amount) external;
    function addPaymentTokens(uint256 _amount) external;
    function buyBRX(address _user, uint256 _usdcAmount, uint256 _brxAmount) external;
    function refund(address _buyer, uint256 _amount, uint256 _brxAdjustment) external;
    function withdraw(address _user, uint256 _amount, uint256 _usdcAdjustment) external;
    function penalty() external returns(uint256);
}




/** 
 *  SourceUnit: /home/dos/dev/clients/cointinum/vaultContract/contracts/Swap.sol
*/
            
////// SPDX-License-Identifier-FLATTEN-SUPPRESS-WARNING: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * ////IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}




/** 
 *  SourceUnit: /home/dos/dev/clients/cointinum/vaultContract/contracts/Swap.sol
*/
            
////// SPDX-License-Identifier-FLATTEN-SUPPRESS-WARNING: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

////import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}


/** 
 *  SourceUnit: /home/dos/dev/clients/cointinum/vaultContract/contracts/Swap.sol
*/

////// SPDX-License-Identifier-FLATTEN-SUPPRESS-WARNING: GPL-3.0
pragma solidity ^0.8.17;

/// @dev - Inherited contracts for security and token compliancy
////import "@openzeppelin/contracts/access/Ownable.sol";
////import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
////import "./interfaces/VaultInterface.sol";

/** 
    @title Swap smart contract for the BRX exchange
    @author The Cointinuum Development Team
    @notice BRX token to be swapped for USDC
    @notice BRX token supply with specific price to be provided (tranches)
    @notice BRX tranche 0 - 10,000,000 @ $0.45
    @notice BRX tranche 1 - 12,000,000 @ $1.41
    @notice BRX tranche 2 - 16,000,000 @ $4.43
    @dev number of decimals for price variable is 2 (price in cents)
    @dev interface to vault, call buyBRX
*/

interface IERC20Extended is IERC20{
    function decimals() external view returns(uint8);
}

contract Swap is Ownable {
    IERC20Extended public USDCtoken;
    IERC20Extended public BRXtoken;
    VaultInterface public Vault;
    
/**
    @notice Tokens to be supplied in tranches
    @dev Define the tranches
    @dev Define the current state of the tranche
    @dev Tranches are currently set up in test, should put in a config file
*/

    enum TrancheState {
        paused,
        active,
        completed,
        deleted
    }

    event MadeDeposit (address indexed customer, uint256 BRXqty, uint256 USDCqty, uint16 tranche);
    event MaxPerTranche (uint16 indexed trancheNumber, uint256 max);
    event AddTranche (uint256 indexed total, uint256 available, uint256 indexed lockDuration, uint256 indexed price);
    event ChangeTrancheState (uint16 indexed trancheNumber, TrancheState newstate);
    event SetMinPurchase (uint32 indexed _minPurchase);
    event ApproveRefund (address indexed _user, uint16 xactionIndex);
    event SetVaultAddress (address indexed _address);
    event AddWhiteList (address[] indexed accounts);
    event RemoveWhiteList (address[] indexed accounts);
    // @notice Struct for defining a tranche
    struct tranche {
        uint256 totalTokens;
        uint256 numberOfTokensAvailable;
    
        // @dev price denominated in USD (2 decimals)
        uint256 lockDuration;
        uint16 price;
        TrancheState state; 
    }

    // @notice Struct for refunds
    struct refundXaction {
        uint256 BRXtokens;
        uint256 USDCtokens;
        uint256 XactionTime;
        uint16 depositID; /// @dev index of depositXaction, for customer to find deposit (ID)
    }

    // @notice Struct for deposits
    struct depositXaction {
        uint256 BRXtokens;
        uint256 USDCtokens;
        uint256 XactionTime;
        uint256 lockDuration;
        uint16 price;
        uint16 tranche;
        bool refunded;
        bool claimed;
    }

    // @notice Mappings for deposits, refunds, checks and balances
    uint256 public maxBRXAllowed;
    uint32 public minPurchase;
    mapping(address => uint256) public totalBRXPurchased; 
    mapping(address => refundXaction[]) public RefundRecord;
    mapping(address => depositXaction[]) public DepositRecord;
    mapping(address => mapping(uint256 => bool)) public refundApprovals;
    mapping(address => bool) public whitelist;
    mapping(uint16 => uint256) public maxPerTranche;

    uint16 constant penaltyScale = 10000;

    // @notice tracks the total returned BRX
    uint256 public returnedBRX;
    // @notice tracks the total settled USDC
    uint256 public settledUSDC;

    /// @notice address to gate access to admin functions
    address adminAddress;

    /// @notice current active tranche
    uint16 public activeTranche;

    // @notice Array of tranches 
    tranche[] public tranches;

    function unsafe_increment(uint256 x) private pure returns (uint256) {
        unchecked {return x + 1;}
    }

    function unsafe_increment16(uint16 x) private pure returns (uint16) {
        unchecked {return x + 1;}
    }

/// @param trancheNumber - One based numbering of tranches as added
/// @param max - Max number of BRX tokens allowed per user to be purchased in a tranche
    function setMaxPerTranche (uint16 trancheNumber, uint256 max) public onlyAdmin {
        require(trancheNumber > 0, "cannot set max per tranche for tranche 0");
        require(tranches.length > (trancheNumber), "cannot set max per tranche for tranche not added");
        maxPerTranche[trancheNumber] = max;
        emit MaxPerTranche(trancheNumber, max);
    }