bitcoin-payment-channel-0.2.3.0: src/Data/Bitcoin/PaymentChannel/Internal/BitcoinAmount.hs
module Data.Bitcoin.PaymentChannel.Internal.BitcoinAmount where
import qualified Data.Serialize as Ser
import qualified Data.Serialize.Put as SerPut
import qualified Data.Serialize.Get as SerGet
import qualified Data.Binary as Bin
import qualified Data.Binary.Put as BinPut
import qualified Data.Binary.Get as BinGet
import Data.Word
-- |Represents a bitcoin amount as number of satoshis.
-- 1 satoshi = 1e-8 bitcoin.
-- Only natural numbers (>= 0) can be represented ('fromInteger' caps to a 'Word64').
-- It is thus not possible to construct a negative BitcoinAmount which, when added to
-- another BitcoinAmount, subtracts from its value. So we avoid the problem of being
-- able to invert subtraction/addition by supplying a negative value as one of the
-- arguments.
newtype BitcoinAmount = BitcoinAmount Integer
deriving (Eq, Ord)
instance Show BitcoinAmount where
show amount = show (toInteger amount) ++ " satoshi"
instance Num BitcoinAmount where
-- We leave multiplication of two money amounts as undefined
(BitcoinAmount a1) * (BitcoinAmount a2) = BitcoinAmount (fromIntegral . capToWord64 $ a1*a2)
(BitcoinAmount a1) + (BitcoinAmount a2) = BitcoinAmount (fromIntegral . capToWord64 $ a1+a2)
(BitcoinAmount a1) - (BitcoinAmount a2) = BitcoinAmount (fromIntegral . capToWord64 $ a1-a2)
abs = id -- Always positive
signum (BitcoinAmount 0) = BitcoinAmount 0
signum (BitcoinAmount _) = BitcoinAmount 1
fromInteger = BitcoinAmount . fromIntegral . capToWord64
instance Enum BitcoinAmount where
toEnum = BitcoinAmount . fromIntegral . capToWord64 . fromIntegral
fromEnum (BitcoinAmount amount) = fromIntegral amount
instance Real BitcoinAmount where
toRational (BitcoinAmount amount) = toRational amount
instance Integral BitcoinAmount where
toInteger (BitcoinAmount int) = int
-- Dividing one money amounts by another doesn't make sense either
quotRem (BitcoinAmount _) (BitcoinAmount _) =
error "Division of two BitcoinAmounts is undefined"
-- | Convert to 'Word64', with zero as floor, (maxBound :: Word64) as ceiling
capToWord64 :: Integer -> Word64
capToWord64 i = fromIntegral $
max 0 cappedValue
where
cappedValue = min i $ fromIntegral (maxBound :: Word64)
instance Bin.Binary BitcoinAmount where
put = BinPut.putWord64le . fromIntegral . toInteger
get = BitcoinAmount . fromIntegral <$> BinGet.getWord64le
instance Ser.Serialize BitcoinAmount where
put = SerPut.putWord64le . fromIntegral . toInteger
get = BitcoinAmount . fromIntegral <$> SerGet.getWord64le