pragma solidity ^0.8.0;





/** PriceFeed REF
 * https://docs.chain.link/docs/data-feeds/price-feeds/#solidity
 *
 * Network: Mainnet
 * Aggregator: ETH/USD
 * Address: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
 *
 * Network: Goerli
 * Aggregator: ETH/USD
 * Address: 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
 */

interface IDignityToken is IERC20Metadata {
    function mint(address, uint256) external;
}

contract DignityTokenSwap is AccessControl {
    bytes32 public constant OPS_ROLE = keccak256("OPS_ROLE");

    using Strings for uint256;
    AggregatorV3Interface internal priceFeed;
    IDignityToken public dignityToken;

    // price for StableCoin (in dollar)
    mapping(address => uint256) public tokensMap;
    // price for ETH (in dollar)
    uint256 public coinPrice;

    address public dignityWallet;
    address public dignityWalletToken;

    bool public paused = false;

    constructor(
        address _dignityToken,
        address _stable,
        uint256 _cost,
        address _dignityWallet,
        address _ethUsdAddress
    ) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

        dignityToken = IDignityToken(_dignityToken);
        coinPrice = 10;
        dignityWallet = _dignityWallet;
        dignityWalletToken = _dignityWallet;
        addTokenToSupportList(IERC20(_stable), _cost);
        priceFeed = AggregatorV3Interface(_ethUsdAddress);
    }

    function addTokenToSupportList(IERC20 _paytoken, uint256 _payCostPerDT)
        public
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        tokensMap[address(_paytoken)] = _payCostPerDT;
    }

    function setPausedState(bool _state) external onlyRole(DEFAULT_ADMIN_ROLE) {
        paused = _state;
    }

    function setDignityWallet(address _addr)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        dignityWallet = _addr;
    }

    function setDignityWalletToken(address _addr)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        dignityWalletToken = _addr;
    }

    /**
     *  INPUTS :
     *      PARAMETERS :
     *      GLOBALS :
     *          uint    msg.value               amount of eth to swap for dignity token
     */
    function swap() public payable {
        require(!paused, "Swap paused");
        require(msg.value > 0, "Invalid amount");

        int256 etherPriceUSD = getLatestPrice(); // Goerli  eg. 130962000000
        uint8 etherPriceDecimals = priceFeed.decimals(); // uint8 etherPriceDecimals = 8;
        require(etherPriceUSD > 0, "Invalid USD price");
        require(etherPriceDecimals > 0, "Invalid price decimals");

        uint8 etherDecimals = 18;
        uint8 dtDecimals = dignityToken.decimals();
        require(dtDecimals > 0, "Invalid dignity token decimals");

        uint256 costUSD = (msg.value * uint256(etherPriceUSD)) /
            (10**etherPriceDecimals);
        uint256 amount = costUSD / coinPrice;
        uint256 dtAmount = amount / (10**(etherDecimals - dtDecimals));
        require(dtAmount > 0, "Invalid dignity token swap");
        dignityToken.mint(msg.sender, dtAmount);
    }

    /**
     *  INPUTS :
     *      PARAMETERS :
     *          address _token                  stable coin contract address
     *          uint256 _amount                 amount of stable coin (in token's decimal)
     */
    function swapToken(address _token, uint256 _amount) public {
        require(!paused, "Swap paused");
        require(tokensMap[_token] > 0, "Token not supported!");

        IERC20Metadata paytoken = IERC20Metadata(_token);
        uint256 cost = tokensMap[_token]; // cost (in token's decimal)

        uint8 dtDecimals = dignityToken.decimals();
        uint256 dtAmount = (_amount * 10**dtDecimals) / cost;
        require(dtAmount > 0, "Invalid cost");

        paytoken.transferFrom(msg.sender, address(this), _amount);
        dignityToken.mint(msg.sender, dtAmount);
    }

    function getLatestPrice() public view returns (int256) {
        (
            ,
            /*uint80 roundID*/
            int256 price, /*uint startedAt*/ /*uint timeStamp*/ /*uint80 answeredInRound*/
            ,
            ,

        ) = priceFeed.latestRoundData();
        return price;
    }

    function withdraw() public onlyRole(OPS_ROLE) {
        uint256 balance = address(this).balance;
        payable(dignityWallet).transfer(balance);
    }

    function withdrawToken(address _token) public onlyRole(OPS_ROLE) {
        require(tokensMap[_token] > 0, "Token not supported!");

        IERC20 paytoken = IERC20(_token);
        paytoken.transfer(
            dignityWalletToken,
            paytoken.balanceOf(address(this))
        );
    }
}
[{"inputs":[{"internalType":"address","name":"_dignityToken","type":"address"},{"internalType":"address","name":"_stable","type":"address"},{"internalType":"uint256","name":"_cost","type":"uint256"},{"internalType":"address","name":"_dignityWallet","type":"address"},{"internalType":"address","name":"_ethUsdAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPS_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_paytoken","type":"address"},{"internalType":"uint256","name":"_payCostPerDT","type":"uint256"}],"name":"addTokenToSupportList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"coinPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dignityToken","outputs":[{"internalType":"contract IDignityToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dignityWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dignityWalletToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLatestPrice","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setDignityWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"setDignityWalletToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_state","type":"bool"}],"name":"setPausedState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swap","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"swapToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokensMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]