// File: lib/geb/src/shared/BasicTokenAdapters.sol



/// BasicTokenAdapters.sol

// Copyright (C) 2018 Rain <rainbreak@riseup.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

pragma solidity 0.6.7;

abstract contract CollateralLike {
    function decimals() virtual public view returns (uint256);
    function transfer(address,uint256) virtual public returns (bool);
    function transferFrom(address,address,uint256) virtual public returns (bool);
}

abstract contract DSTokenLike {
    function mint(address,uint256) virtual external;
    function burn(address,uint256) virtual external;
}

abstract contract SAFEEngineLike {
    function modifyCollateralBalance(bytes32,address,int256) virtual external;
    function transferInternalCoins(address,address,uint256) virtual external;
}

abstract contract MultiSAFEEngineLike {
    function modifyCollateralBalance(bytes32,bytes32,address,int256) virtual external;
    function transferInternalCoins(bytes32,address,address,uint256) virtual external;
}

/*
    Here we provide *adapters* to connect the SAFEEngine to arbitrary external
    token implementations, creating a bounded context for the SAFEEngine. The
    adapters here are provided as working examples:
      - `BasicCollateralJoin`: For well behaved ERC20 tokens, with simple transfer semantics.
      - `ETHJoin`: For native Ether.
      - `CoinJoin`: For connecting internal coin balances to an external
                   `Coin` implementation.
    In practice, adapter implementations will be varied and specific to
    individual collateral types, accounting for different transfer
    semantics and token standards.
    Adapters need to implement two basic methods:
      - `join`: enter collateral into the system
      - `exit`: remove collateral from the system
*/

contract BasicCollateralJoin {
    // --- Auth ---
    mapping (address => uint256) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call an authed function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "BasicCollateralJoin/account-not-authorized");
        _;
    }

    // SAFE database
    SAFEEngineLike  public safeEngine;
    // Collateral type name
    bytes32        public collateralType;
    // Actual collateral token contract
    CollateralLike public collateral;
    // How many decimals the collateral token has
    uint256        public decimals;
    // Whether this adapter contract is enabled or not
    uint256        public contractEnabled;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DisableContract();
    event Join(address sender, address account, uint256 wad);
    event Exit(address sender, address account, uint256 wad);

    constructor(address safeEngine_, bytes32 collateralType_, address collateral_) public {
        authorizedAccounts[msg.sender] = 1;
        contractEnabled = 1;
        safeEngine      = SAFEEngineLike(safeEngine_);
        collateralType  = collateralType_;
        collateral      = CollateralLike(collateral_);
        decimals        = collateral.decimals();
        require(decimals == 18, "BasicCollateralJoin/non-18-decimals");
        emit AddAuthorization(msg.sender);
    }
    /**
     * @notice Disable this contract
     */
    function disableContract() external isAuthorized {
        contractEnabled = 0;
        emit DisableContract();
    }
    /**
    * @notice Join collateral in the system
    * @dev This function locks collateral in the adapter and creates a 'representation' of
    *      the locked collateral inside the system. This adapter assumes that the collateral
    *      has 18 decimals
    * @param account Account from which we transferFrom collateral and add it in the system
    * @param wad Amount of collateral to transfer in the system (represented as a number with 18 decimals)
    **/
    function join(address account, uint256 wad) external {
        require(contractEnabled == 1, "BasicCollateralJoin/contract-not-enabled");
        require(int256(wad) >= 0, "BasicCollateralJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, account, int256(wad));
        require(collateral.transferFrom(msg.sender, address(this), wad), "BasicCollateralJoin/failed-transfer");
        emit Join(msg.sender, account, wad);
    }
    /**
    * @notice Exit collateral from the system
    * @dev This function destroys the collateral representation from inside the system
    *      and exits the collateral from this adapter. The adapter assumes that the collateral
    *      has 18 decimals
    * @param account Account to which we transfer the collateral
    * @param wad Amount of collateral to transfer to 'account' (represented as a number with 18 decimals)
    **/
    function exit(address account, uint256 wad) external {
        require(wad <= 2 ** 255, "BasicCollateralJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, msg.sender, -int256(wad));
        require(collateral.transfer(account, wad), "BasicCollateralJoin/failed-transfer");
        emit Exit(msg.sender, account, wad);
    }
}

contract MultiBasicCollateralJoin {
    // --- Auth ---
    mapping (address => uint256) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call an authed function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "MultiBasicCollateralJoin/account-not-authorized");
        _;
    }

    // SAFE database
    MultiSAFEEngineLike  public safeEngine;
    // Collateral type name
    bytes32              public collateralType;
    // Actual collateral token contract
    CollateralLike       public collateral;
    // How many decimals the collateral token has
    uint256              public decimals;
    // Whether this adapter contract is enabled or not
    uint256              public contractEnabled;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DisableContract();
    event Join(address sender, address account, uint256 wad);
    event Exit(address sender, address account, uint256 wad);

    constructor(address safeEngine_, bytes32 collateralType_, address collateral_) public {
        authorizedAccounts[msg.sender] = 1;
        contractEnabled = 1;
        safeEngine      = MultiSAFEEngineLike(safeEngine_);
        collateralType  = collateralType_;
        collateral      = CollateralLike(collateral_);
        decimals        = collateral.decimals();
        require(decimals == 18, "MultiBasicCollateralJoin/non-18-decimals");
        emit AddAuthorization(msg.sender);
    }
    /**
     * @notice Disable this contract
     */
    function disableContract() external isAuthorized {
        contractEnabled = 0;
        emit DisableContract();
    }
    /**
    * @notice Join collateral in the system
    * @dev This function locks collateral in the adapter and creates a 'representation' of
    *      the locked collateral inside the system. This adapter assumes that the collateral
    *      has 18 decimals
    * @param account Account from which we transferFrom collateral and add it in the system
    * @param wad Amount of collateral to transfer in the system (represented as a number with 18 decimals)
    **/
    function join(address account, uint256 wad) external {
        require(contractEnabled == 1, "MultiBasicCollateralJoin/contract-not-enabled");
        require(int256(wad) >= 0, "MultiBasicCollateralJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, collateralType, account, int256(wad));
        require(collateral.transferFrom(msg.sender, address(this), wad), "MultiBasicCollateralJoin/failed-transfer");
        emit Join(msg.sender, account, wad);
    }
    /**
    * @notice Exit collateral from the system
    * @dev This function destroys the collateral representation from inside the system
    *      and exits the collateral from this adapter. The adapter assumes that the collateral
    *      has 18 decimals
    * @param account Account to which we transfer the collateral
    * @param wad Amount of collateral to transfer to 'account' (represented as a number with 18 decimals)
    **/
    function exit(address account, uint256 wad) external {
        require(wad <= 2 ** 255, "MultiBasicCollateralJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, collateralType, msg.sender, -int256(wad));
        require(collateral.transfer(account, wad), "MultiBasicCollateralJoin/failed-transfer");
        emit Exit(msg.sender, account, wad);
    }
}

contract MultiSubCollateralJoin {
    // --- Auth ---
    mapping (address => uint256) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call an authed function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "MultiSubCollateralJoin/account-not-authorized");
        _;
    }

    // Base collateral type
    bytes32                     public collateralType;
    // SAFE database
    MultiSAFEEngineLike         public safeEngine;
    // How many decimals the sub-collateral tokens have
    uint256                     public decimals;
    // Whether this adapter contract is enabled or not
    uint256                     public contractEnabled;

    // Sub-collateral names and token contracts
    mapping(bytes32 => address) public subCollaterals;
    // Whether a token contract has already been onboarded
    mapping(address => uint256) public tokenOnboarded;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DisableContract();
    event Join(bytes32 subCollateral, address sender, address account, uint256 wad);
    event Exit(bytes32 subCollateral, address sender, address account, uint256 wad);
    event AddSubCollateral(bytes32 subCollateral, address token);

    constructor(address safeEngine_, bytes32 collateralType_) public {
        authorizedAccounts[msg.sender] = 1;
        contractEnabled = 1;
        safeEngine      = MultiSAFEEngineLike(safeEngine_);
        collateralType  = collateralType_;
        decimals        = 18;
        emit AddAuthorization(msg.sender);
    }
    /**
     * @notice Add a subcollateral
     * @param subCollateral Sub-collateral name
     * @param token Address of the collateral token contract
     */
    function addSubCollateral(bytes32 subCollateral, address token) external isAuthorized {
        require(tokenOnboarded[token] == 0, "MultiSubCollateralJoin/token-already-onboarded");
        require(subCollaterals[subCollateral] == address(0), "MultiSubCollateralJoin/subcollateral-already-onboarded");
        require(CollateralLike(token).decimals() == decimals, "MultiSubCollateralJoin/invalid-decimal-number");

        tokenOnboarded[token]         = 1;
        subCollaterals[subCollateral] = token;

        emit AddSubCollateral(subCollateral, token);
    }
    /**
     * @notice Disable this contract
     */
    function disableContract() external isAuthorized {
        contractEnabled = 0;
        emit DisableContract();
    }
    /**
    * @notice Join sub-collateral in the system
    * @dev This function locks sub-collateral in the adapter and creates a 'representation' of
    *      the locked collateral inside the system. This adapter assumes that the collateral
    *      has 18 decimals
    * @param subCollateral The sub-collateral to join
    * @param account Account from which we transferFrom collateral and add it in the system
    * @param wad Amount of collateral to transfer in the system (represented as a number with 18 decimals)
    **/
    function join(bytes32 subCollateral, address account, uint256 wad) external {
        require(contractEnabled == 1, "MultiSubCollateralJoin/contract-not-enabled");
        require(subCollaterals[subCollateral] != address(0), "MultiSubCollateralJoin/subcollateral-not-onboarded");
        require(int256(wad) >= 0, "MultiSubCollateralJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, subCollateral, account, int256(wad));
        require(CollateralLike(subCollaterals[subCollateral]).transferFrom(msg.sender, address(this), wad), "MultiSubCollateralJoin/failed-transfer");
        emit Join(subCollateral, msg.sender, account, wad);
    }
    /**
    * @notice Exit collateral from the system
    * @dev This function destroys the collateral representation from inside the system
    *      and exits the collateral from this adapter. The adapter assumes that the collateral
    *      has 18 decimals
    * @param subCollateral The sub-collateral to exit
    * @param account Account to which we transfer the collateral
    * @param wad Amount of collateral to transfer to 'account' (represented as a number with 18 decimals)
    **/
    function exit(bytes32 subCollateral, address account, uint256 wad) external {
        require(wad <= 2 ** 255, "MultiSubCollateralJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, subCollateral, msg.sender, -int256(wad));
        require(CollateralLike(subCollaterals[subCollateral]).transfer(account, wad), "MultiSubCollateralJoin/failed-transfer");
        emit Exit(subCollateral, msg.sender, account, wad);
    }
}

contract ETHJoin {
    // --- Auth ---
    mapping (address => uint256) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call a restricted function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "ETHJoin/account-not-authorized");
        _;
    }

    // SAFE database
    SAFEEngineLike public safeEngine;
    // Collateral type name
    bytes32       public collateralType;
    // Whether this contract is enabled or not
    uint256       public contractEnabled;
    // Number of decimals ETH has
    uint256       public decimals;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DisableContract();
    event Join(address sender, address account, uint256 wad);
    event Exit(address sender, address account, uint256 wad);

    constructor(address safeEngine_, bytes32 collateralType_) public {
        authorizedAccounts[msg.sender] = 1;
        contractEnabled                = 1;
        safeEngine                     = SAFEEngineLike(safeEngine_);
        collateralType                 = collateralType_;
        decimals                       = 18;
        emit AddAuthorization(msg.sender);
    }
    /**
     * @notice Disable this contract
     */
    function disableContract() external isAuthorized {
        contractEnabled = 0;
        emit DisableContract();
    }
    /**
    * @notice Join ETH in the system
    * @param account Account that will receive the ETH representation inside the system
    **/
    function join(address account) external payable {
        require(contractEnabled == 1, "ETHJoin/contract-not-enabled");
        require(int256(msg.value) >= 0, "ETHJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, account, int256(msg.value));
        emit Join(msg.sender, account, msg.value);
    }
    /**
    * @notice Exit ETH from the system
    * @param account Account that will receive the ETH representation inside the system
    **/
    function exit(address payable account, uint256 wad) external {
        require(int256(wad) >= 0, "ETHJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, msg.sender, -int256(wad));
        emit Exit(msg.sender, account, wad);
        account.transfer(wad);
    }
}

contract MultiETHJoin {
    // --- Auth ---
    mapping (address => uint256) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call a restricted function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "MultiETHJoin/account-not-authorized");
        _;
    }

    // SAFE database
    MultiSAFEEngineLike public safeEngine;
    // Collateral type name
    bytes32             public collateralType;
    // Whether this contract is enabled or not
    uint256             public contractEnabled;
    // Number of decimals ETH has
    uint256             public decimals;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DisableContract();
    event Join(address sender, address account, uint256 wad);
    event Exit(address sender, address account, uint256 wad);

    constructor(address safeEngine_, bytes32 collateralType_) public {
        authorizedAccounts[msg.sender] = 1;
        contractEnabled                = 1;
        safeEngine                     = MultiSAFEEngineLike(safeEngine_);
        collateralType                 = collateralType_;
        decimals                       = 18;
        emit AddAuthorization(msg.sender);
    }
    /**
     * @notice Disable this contract
     */
    function disableContract() external isAuthorized {
        contractEnabled = 0;
        emit DisableContract();
    }
    /**
    * @notice Join ETH in the system
    * @param account Account that will receive the ETH representation inside the system
    **/
    function join(address account) external payable {
        require(contractEnabled == 1, "MultiETHJoin/contract-not-enabled");
        require(int256(msg.value) >= 0, "MultiETHJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, collateralType, account, int256(msg.value));
        emit Join(msg.sender, account, msg.value);
    }
    /**
    * @notice Exit ETH from the system
    * @param account Account that will receive the ETH representation inside the system
    **/
    function exit(address payable account, uint256 wad) external {
        require(int256(wad) >= 0, "MultiETHJoin/overflow");
        safeEngine.modifyCollateralBalance(collateralType, collateralType, msg.sender, -int256(wad));
        emit Exit(msg.sender, account, wad);
        account.transfer(wad);
    }
}

contract CoinJoin {
    // --- Auth ---
    mapping (address => uint256) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call an authed function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "CoinJoin/account-not-authorized");
        _;
    }

    // SAFE database
    SAFEEngineLike public safeEngine;
    // Coin created by the system; this is the external, ERC-20 representation, not the internal 'coinBalance'
    DSTokenLike    public systemCoin;
    // Whether this contract is enabled or not
    uint256        public contractEnabled;
    // Number of decimals the system coin has
    uint256        public decimals;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DisableContract();
    event Join(address sender, address account, uint256 wad);
    event Exit(address sender, address account, uint256 wad);

    constructor(address safeEngine_, address systemCoin_) public {
        authorizedAccounts[msg.sender] = 1;
        contractEnabled                = 1;
        safeEngine                     = SAFEEngineLike(safeEngine_);
        systemCoin                     = DSTokenLike(systemCoin_);
        decimals                       = 18;
        emit AddAuthorization(msg.sender);
    }
    /**
     * @notice Disable this contract
     */
    function disableContract() external isAuthorized {
        contractEnabled = 0;
        emit DisableContract();
    }
    uint256 constant RAY = 10 ** 27;
    function multiply(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require(y == 0 || (z = x * y) / y == x, "CoinJoin/mul-overflow");
    }
    /**
    * @notice Join system coins in the system
    * @dev Exited coins have 18 decimals but inside the system they have 45 (rad) decimals.
           When we join, the amount (wad) is multiplied by 10**27 (ray)
    * @param account Account that will receive the joined coins
    * @param wad Amount of external coins to join (18 decimal number)
    **/
    function join(address account, uint256 wad) external {
        safeEngine.transferInternalCoins(address(this), account, multiply(RAY, wad));
        systemCoin.burn(msg.sender, wad);
        emit Join(msg.sender, account, wad);
    }
    /**
    * @notice Exit system coins from the system and inside 'Coin.sol'
    * @dev Inside the system, coins have 45 (rad) decimals but outside of it they have 18 decimals (wad).
           When we exit, we specify a wad amount of coins and then the contract automatically multiplies
           wad by 10**27 to move the correct 45 decimal coin amount to this adapter
    * @param account Account that will receive the exited coins
    * @param wad Amount of internal coins to join (18 decimal number that will be multiplied by ray)
    **/
    function exit(address account, uint256 wad) external {
        require(contractEnabled == 1, "CoinJoin/contract-not-enabled");
        safeEngine.transferInternalCoins(msg.sender, address(this), multiply(RAY, wad));
        systemCoin.mint(account, wad);
        emit Exit(msg.sender, account, wad);
    }
}

contract MultiCoinJoin {
    // --- Auth ---
    mapping (address => uint256) public authorizedAccounts;
    /**
     * @notice Add auth to an account
     * @param account Account to add auth to
     */
    function addAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 1;
        emit AddAuthorization(account);
    }
    /**
     * @notice Remove auth from an account
     * @param account Account to remove auth from
     */
    function removeAuthorization(address account) external isAuthorized {
        authorizedAccounts[account] = 0;
        emit RemoveAuthorization(account);
    }
    /**
    * @notice Checks whether msg.sender can call an authed function
    **/
    modifier isAuthorized {
        require(authorizedAccounts[msg.sender] == 1, "MultiCoinJoin/account-not-authorized");
        _;
    }

    // Multi synth SAFE database
    MultiSAFEEngineLike public safeEngine;
    // Coin created by the system; this is the external, ERC-20 representation, not the internal 'coinBalance'
    DSTokenLike         public systemCoin;
    // Whether this contract is enabled or not
    uint256             public contractEnabled;
    // Number of decimals the system coin has
    uint256             public decimals;
    // The name of the synth
    bytes32             public coinName;

    // --- Events ---
    event AddAuthorization(address account);
    event RemoveAuthorization(address account);
    event DisableContract();
    event Join(address sender, address account, uint256 wad);
    event Exit(address sender, address account, uint256 wad);

    constructor(bytes32 coinName_, address safeEngine_, address systemCoin_) public {
        authorizedAccounts[msg.sender] = 1;
        contractEnabled                = 1;
        coinName                       = coinName_;
        safeEngine                     = MultiSAFEEngineLike(safeEngine_);
        systemCoin                     = DSTokenLike(systemCoin_);
        decimals                       = 18;
        emit AddAuthorization(msg.sender);
    }
    /**
     * @notice Disable this contract
     */
    function disableContract() external isAuthorized {
        contractEnabled = 0;
        emit DisableContract();
    }
    uint256 constant RAY = 10 ** 27;
    function multiply(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require(y == 0 || (z = x * y) / y == x, "MultiCoinJoin/mul-overflow");
    }
    /**
    * @notice Join system coins in the system
    * @dev Exited coins have 18 decimals but inside the system they have 45 (rad) decimals.
           When we join, the amount (wad) is multiplied by 10**27 (ray)
    * @param account Account that will receive the joined coins
    * @param wad Amount of external coins to join (18 decimal number)
    **/
    function join(address account, uint256 wad) external {
        safeEngine.transferInternalCoins(coinName, address(this), account, multiply(RAY, wad));
        systemCoin.burn(msg.sender, wad);
        emit Join(msg.sender, account, wad);
    }
    /**
    * @notice Exit system coins from the system and inside 'Coin.sol'
    * @dev Inside the system, coins have 45 (rad) decimals but outside of it they have 18 decimals (wad).
           When we exit, we specify a wad amount of coins and then the contract automatically multiplies
           wad by 10**27 to move the correct 45 decimal coin amount to this adapter
    * @param account Account that will receive the exited coins
    * @param wad Amount of internal coins to join (18 decimal number that will be multiplied by ray)
    **/
    function exit(address account, uint256 wad) external {
        require(contractEnabled == 1, "MultiCoinJoin/contract-not-enabled");
        safeEngine.transferInternalCoins(coinName, msg.sender, address(this), multiply(RAY, wad));
        systemCoin.mint(account, wad);
        emit Exit(msg.sender, account, wad);
    }
}