My favourite money library is safe- money in Haskell. It basically catches 99% of the most common money bugs at compile time https://ren.zone/articles/safe-money
I wonder if it might be even better for the ExchangeRate constructor to take two different Dense values, something like:
exchangeRate (1 :: Dense "EUR") (12 % 10 Dense "USD")
Then the type system could automatically prevent you from making an error by accidentally entering the reciprocal exchange rate from what is expected.