abstract contract OracleCommon is IOracle, ICHIModuleCommon {

    uint256 constant NORMAL = 18;
    bytes32 constant public override MODULE_TYPE = keccak256(abi.encodePacked("ICHI V1 Oracle Implementation"));
    address public override indexToken;

    event OracleDeployed(address sender, string description, address indexToken);
    event OracleInitialized(address sender, address baseToken, address indexToken);
    
    /**
     @notice records the oracle description and the index that will be used for all quotes
     @dev oneToken implementations can share oracles
     @param oneTokenFactory_ oneTokenFactory to bind to
     @param description_ all modules have a description. No processing or validation
     @param indexToken_ every oracle has an index token for reporting the value of a base token
     */
    constructor(address oneTokenFactory_, string memory description_, address indexToken_) 
        ICHIModuleCommon(oneTokenFactory_, ModuleType.Oracle, description_) 
    { 
        require(indexToken_ != NULL_ADDRESS, "OracleCommon: indexToken cannot be empty");
        indexToken = indexToken_;
        emit OracleDeployed(msg.sender, description_, indexToken_);
    }

    /**
     @notice oneTokens can share Oracles. Oracles must be re-initializable. They are initialized from the Factory.
     @param baseToken oracles _can be_ multi-tenant with separately initialized baseTokens
     */
    function init(address baseToken) external onlyModuleOrFactory virtual override {
        emit OracleInitialized(msg.sender, baseToken, indexToken);
    }

    /**
     @notice converts normalized precision 18 amounts to token native precision amounts, truncates low-order values
     @param token ERC20 token contract
     @param amountNormal quantity in precision-18
     @param amountTokens quantity scaled to token decimals()
     */    
    function normalizedToTokens(address token, uint256 amountNormal) public view override returns(uint256 amountTokens) {
        IERC20Extended t = IERC20Extended(token);
        uint256 nativeDecimals = t.decimals();
        require(nativeDecimals <= 18, "OracleCommon: unsupported token precision (greater than 18)");
        if(nativeDecimals == NORMAL) return amountNormal;
        return amountNormal / ( 10 ** (NORMAL - nativeDecimals));
    }

    /**
     @notice converts token native precision amounts to normalized precision 18 amounts
     @param token ERC20 token contract
     @param amountTokens quantity scaled to token decimals
     @param amountNormal quantity in precision-18
     */  
    function tokensToNormalized(address token, uint256 amountTokens) public view override returns(uint256 amountNormal) {
        IERC20Extended t = IERC20Extended(token);
        uint256 nativeDecimals = t.decimals();
        require(nativeDecimals <= 18, "OracleCommon: unsupported token precision (greater than 18)");
        if(nativeDecimals == NORMAL) return amountTokens;
        return amountTokens * ( 10 ** (NORMAL - nativeDecimals));
    }

}
function tokensToNormalized(address token, uint256 amountTokens) public view override returns(uint256 amountNormal) {
    IERC20Extended t = IERC20Extended(token);
    uint256 nativeDecimals = t.decimals();
    require(nativeDecimals <= 18, "OracleCommon: unsupported token precision (greater than 18)");
    if(nativeDecimals == NORMAL) return amountTokens;
    return amountTokens * ( 10 ** (NORMAL - nativeDecimals));
}

/**
 @notice converts token native precision amounts to normalized precision 18 amounts
 @param token ERC20 token contract
 @param amountTokens quantity scaled to token decimals
 @param amountNormal quantity in precision-18
 */