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

import "Ownable.sol";
import "IERC20.sol";

import "CollectibleFees.sol";
import "ISCMinter.sol";
import "IGeminonOracle.sol";

import "TimeLocks.sol";
import "TradePausable.sol";


contract SCMinterMigration is Ownable, TradePausable, TimeLocks, CollectibleFees {
    
    uint64 public oracleAge;

    bool public isMigrationRequested;
    uint64 public timestampMigrationRequest;
    address public migrationMinter;



    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // +                         MINTER MIGRATION                           +
    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    /// @dev Register a request to migrate the minter.
    /// Begins a timelock of 7 days before enabling the migration.
    /// requestAddressChange() had to be made in this contract and in the
    /// oracle contract 7 days before this request.
    function requestMigration(address newMinter) external onlyOwner {
        require(changeRequests[address(this)].changeRequested); // dev: Not requested
        require(block.timestamp - changeRequests[address(this)].timestampRequest > 7 days); // dev: Time elapsed
        require(changeRequests[address(this)].newAddressRequested == newMinter); // dev: Address not zero
        require(oracleGeminon != address(0)); // dev: oracle is not set

        changeRequests[address(this)].changeRequested = false;
        changeRequests[address(this)].newAddressRequested = address(this);
        changeRequests[address(this)].timestampRequest = type(uint64).max;
        
        isMigrationRequested = true;
        migrationMinter = newMinter;
        timestampMigrationRequest = uint64(block.timestamp);

        IGeminonOracle(oracleGeminon).requestMigrateMinter(newMinter);
        _pauseMint();
    }

    /// @dev Transfer all GEX in the minter to the new minter.
    /// Removes the minter from the Geminon Oracle.
    function migrateMinter() external onlyOwner whenMintPaused {
        require(isMigrationRequested); // dev: migration not requested
        require(oracleGeminon != address(0)); // dev: oracle is not set
        require(IGeminonOracle(oracleGeminon).isMigratingMinter()); // dev: migration not requested
        require(block.timestamp - timestampMigrationRequest > 15 days); // dev: timelock
        
        uint256 amountGEX = IERC20(GEX).balanceOf(address(this)) - _balanceFees;
        
        isMigrationRequested = false;

        IERC20(GEX).approve(migrationMinter, amountGEX);

        ISCMinter(migrationMinter).receiveMigration(amountGEX);
        
        IGeminonOracle(oracleGeminon).setMinterMigrationDone();
    }

    /// @dev Receive the funds of the previous minter that is migrating.
    function receiveMigration(uint256 amountGEX) external {
        require(oracleGeminon != address(0)); // dev: oracle is not set
        require(IGeminonOracle(oracleGeminon).scMinter() == msg.sender); // dev: sender is not pool
        require(IGeminonOracle(oracleGeminon).isMigratingMinter()); // dev: migration not requested

        require(IERC20(GEX).transferFrom(msg.sender, address(this), amountGEX));
    }
}
[{"inputs":[{"internalType":"address","name":"gex","type":"address"},{"internalType":"address","name":"usdi","type":"address"},{"internalType":"address","name":"oracle","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"MintPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"MintUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"RedeemPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"RedeemUnpaused","type":"event"},{"inputs":[],"name":"GEX","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDI","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"stablecoin","type":"address"}],"name":"addReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addReservesGEX","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"swapFee","type":"uint32"}],"name":"addStablecoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountGEX","type":"uint256"},{"internalType":"uint256","name":"baseFee","type":"uint256"}],"name":"amountFeeGEX","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stable","type":"address"},{"internalType":"uint256","name":"amountStable","type":"uint256"},{"internalType":"uint256","name":"usdPrice","type":"uint256"}],"name":"amountFeeMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stable","type":"address"},{"internalType":"uint256","name":"amountStable","type":"uint256"},{"internalType":"uint256","name":"usdPrice","type":"uint256"}],"name":"amountFeeRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stableIn","type":"address"},{"internalType":"address","name":"stableOut","type":"address"},{"internalType":"uint256","name":"usdPriceIn","type":"uint256"},{"internalType":"uint256","name":"usdPriceOut","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"amountFeeSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoin","type":"address"},{"internalType":"uint256","name":"amountGEX","type":"uint256"}],"name":"amountMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoin","type":"address"},{"internalType":"uint256","name":"amountStablecoin","type":"uint256"}],"name":"amountRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoin","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"amountUSDI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"applyOracleChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"arbitrageur","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseMintFee","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseRedeemFee","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"baseSwapFees","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"stablecoin","type":"address"}],"name":"burnReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelChangeRequests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"changeRequests","outputs":[{"internalType":"bool","name":"changeRequested","type":"bool"},{"internalType":"uint64","name":"timestampRequest","type":"uint64"},{"internalType":"address","name":"newAddressRequested","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountGEX","type":"uint256"},{"internalType":"uint256","name":"baseFee","type":"uint256"}],"name":"feeGEX","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stable","type":"address"},{"internalType":"uint256","name":"amountStable","type":"uint256"}],"name":"feeStablecoinMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stable","type":"address"},{"internalType":"uint256","name":"amountStable","type":"uint256"}],"name":"feeStablecoinRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stableIn","type":"address"},{"internalType":"address","name":"stableOut","type":"address"},{"internalType":"uint256","name":"usdPriceIn","type":"uint256"},{"internalType":"uint256","name":"usdPriceOut","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"feeSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalanceGEX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"inGEXAmount","type":"uint256"},{"internalType":"address","name":"stablecoin","type":"address"}],"name":"getMintInfo","outputs":[{"internalType":"uint256","name":"gexPriceUSD","type":"uint256"},{"internalType":"uint256","name":"stablecoinPriceUSD","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"outStablecoinAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"inStablecoinAmount","type":"uint256"},{"internalType":"address","name":"stablecoin","type":"address"}],"name":"getRedeemInfo","outputs":[{"internalType":"uint256","name":"stablecoinPriceUSD","type":"uint256"},{"internalType":"uint256","name":"gexPriceUSD","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"outGEXAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoin","type":"address"}],"name":"getSafeMintRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoin","type":"address"}],"name":"getSafeRedeemRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"inAmount","type":"uint256"},{"internalType":"address","name":"stableIn","type":"address"},{"internalType":"address","name":"stableOut","type":"address"}],"name":"getStableSwapInfo","outputs":[{"internalType":"uint256","name":"inStablecoinPriceUSD","type":"uint256"},{"internalType":"uint256","name":"outStablecoinPriceUSD","type":"uint256"},{"internalType":"uint256","name":"quoteS2S1","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"internalType":"uint256","name":"outStablecoinAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTVC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMigrationRequested","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMintPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRedeemPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrateMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"migrationMinter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoin","type":"address"},{"internalType":"uint256","name":"amountInGEX","type":"uint256"}],"name":"mintStablecoin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mintedTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleAge","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleGeminon","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"priceRecordsLongs","outputs":[{"internalType":"uint64","name":"lastTimestamp","type":"uint64"},{"internalType":"uint32","name":"weightedPrice","type":"uint32"},{"internalType":"uint256","name":"volume","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"priceRecordsShorts","outputs":[{"internalType":"uint64","name":"lastTimestamp","type":"uint64"},{"internalType":"uint32","name":"weightedPrice","type":"uint32"},{"internalType":"uint256","name":"volume","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountGEX","type":"uint256"}],"name":"receiveMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stablecoin","type":"address"},{"internalType":"uint256","name":"amountInStablecoin","type":"uint256"}],"name":"redeemStablecoin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"removeStablecoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newContract","type":"address"}],"name":"requestAddAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"actualContract","type":"address"},{"internalType":"address","name":"newContract","type":"address"}],"name":"requestAddressChange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestBailoutFromPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMinter","type":"address"}],"name":"requestMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldContract","type":"address"}],"name":"requestRemoveAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"arbitrageur_","type":"address"}],"name":"setArbitrageur","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"feesCollector","type":"address"}],"name":"setCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setMintFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setRedeemFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stable","type":"address"},{"internalType":"uint32","name":"value","type":"uint32"}],"name":"setSwapFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stableIn","type":"address"},{"internalType":"address","name":"stableOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"stableSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stablecoins","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timestampMigrationRequest","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"validTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]