module Data.Bitcoin.PaymentChannel.Internal.Bitcoin.Amount where import qualified Data.Serialize as Ser import qualified Data.Serialize.Put as SerPut import qualified Data.Serialize.Get as SerGet 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 (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 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 Ser.Serialize BitcoinAmount where put = SerPut.putWord64le . fromIntegral . toInteger get = BitcoinAmount . fromIntegral <$> SerGet.getWord64le