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