pragma solidity ^0.4.24;

 /* 
  *  Handles mapping and calculation of fees for exchanges.
  */

contract DSAuthority {
    function canCall(
        address src, address dst, bytes4 sig
    ) public view returns (bool);
}

contract DSAuthEvents {
    event LogSetAuthority (address indexed authority);
    event LogSetOwner     (address indexed owner);
}

contract DSAuth is DSAuthEvents {
    DSAuthority  public  authority;
    address      public  owner;

    constructor() public {
        owner = msg.sender;
        emit LogSetOwner(msg.sender);
    }

    function setOwner(address owner_)
        public
        auth
    {
        owner = owner_;
        emit LogSetOwner(owner);
    }

    function setAuthority(DSAuthority authority_)
        public
        auth
    {
        authority = authority_;
        emit LogSetAuthority(authority);
    }

    modifier auth {
        require(isAuthorized(msg.sender, msg.sig));
        _;
    }

    function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
        if (src == address(this)) {
            return true;
        } else if (src == owner) {
            return true;
        } else if (authority == DSAuthority(0)) {
            return false;
        } else {
            return authority.canCall(src, this, sig);
        }
    }
}

contract DSMath {
    function add(uint x, uint y) internal pure returns (uint z) {
        require((z = x + y) >= x);
    }
    function sub(uint x, uint y) internal pure returns (uint z) {
        require((z = x - y) <= x);
    }
    function mul(uint x, uint y) internal pure returns (uint z) {
        require(y == 0 || (z = x * y) / y == x);
    }
    function min(uint x, uint y) internal pure returns (uint z) {
        return x <= y ? x : y;
    }
    function max(uint x, uint y) internal pure returns (uint z) {
        return x >= y ? x : y;
    }
    function imin(int x, int y) internal pure returns (int z) {
        return x <= y ? x : y;
    }
    function imax(int x, int y) internal pure returns (int z) {
        return x >= y ? x : y;
    }

    uint constant WAD = 10 ** 18;
    uint constant RAY = 10 ** 27;

    function wmul(uint x, uint y) internal pure returns (uint z) {
        z = add(mul(x, y), WAD / 2) / WAD;
    }
    function rmul(uint x, uint y) internal pure returns (uint z) {
        z = add(mul(x, y), RAY / 2) / RAY;
    }
    function wdiv(uint x, uint y) internal pure returns (uint z) {
        z = add(mul(x, WAD), y / 2) / y;
    }
    function rdiv(uint x, uint y) internal pure returns (uint z) {
        z = add(mul(x, RAY), y / 2) / y;
    }

    function rpow(uint x, uint n) internal pure returns (uint z) {
        z = n % 2 != 0 ? x : RAY;

        for (n /= 2; n != 0; n /= 2) {
            x = rmul(x, x);

            if (n % 2 != 0) {
                z = rmul(z, x);
            }
        }
    }
}

contract FeeAuthority is DSMath, DSAuth {
        
    mapping (address => uint) tokenRates;
    uint defaultFeePercentage;

    constructor () public {
        defaultFeePercentage = 0.02 ether;
    }

    function setDefaultFee (uint newFeeWad) public auth {
        require(newFeeWad < 0.1 ether); /* require <10% fee */
        defaultFeePercentage = newFeeWad;
    }
    
    function setFee (address token, uint newFeeWad) public auth {
        /* set the new fee for a token. auth modifier ensures only owner can call. */
        require(newFeeWad < 0.1 ether); /* require <10% fee */
        tokenRates[token] = newFeeWad;
    }
    
    function rateOf (address token) internal view returns (uint) {
        if (tokenRates[token] == 0) {
        /* use default fee rate if the token's fee rate is not specified */
            return defaultFeePercentage;
        } else {
            return tokenRates[token];
        }
    }    
    
    function takeFee (uint amt, address token) public view returns (uint fee, uint remaining) {
        /* shave the fee off of an amount */
        fee = wmul(amt, rateOf(token));
        remaining = sub(amt, fee);
    }
}