-- | Amount sum type

{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE RecordWildCards #-}

module Blockfrost.Types.Shared.Amount
  where

import Blockfrost.Types.Shared.Ada (Lovelaces)
import Data.Aeson
  ( FromJSON (..)
  , ToJSON (..)
  , Value (Object)
  , object
  , withObject
  , (.:)
  , (.=)
  )
import GHC.Generics
import qualified Money
import Servant.Docs (ToSample (..), samples)
import qualified Text.Read

-- | Amount, which is either `AdaAmount Lovelaces` representing
-- amount of lovelaces or `AssetAmount SomeDiscrete` for asset amounts,
-- identified by concatenation of asset policy ID
-- and hex-encoded asset_name
data Amount =
    AdaAmount Lovelaces
  | AssetAmount Money.SomeDiscrete
  deriving (Amount -> Amount -> Bool
(Amount -> Amount -> Bool)
-> (Amount -> Amount -> Bool) -> Eq Amount
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Amount -> Amount -> Bool
== :: Amount -> Amount -> Bool
$c/= :: Amount -> Amount -> Bool
/= :: Amount -> Amount -> Bool
Eq, Int -> Amount -> ShowS
[Amount] -> ShowS
Amount -> String
(Int -> Amount -> ShowS)
-> (Amount -> String) -> ([Amount] -> ShowS) -> Show Amount
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Amount -> ShowS
showsPrec :: Int -> Amount -> ShowS
$cshow :: Amount -> String
show :: Amount -> String
$cshowList :: [Amount] -> ShowS
showList :: [Amount] -> ShowS
Show, Eq Amount
Eq Amount =>
(Amount -> Amount -> Ordering)
-> (Amount -> Amount -> Bool)
-> (Amount -> Amount -> Bool)
-> (Amount -> Amount -> Bool)
-> (Amount -> Amount -> Bool)
-> (Amount -> Amount -> Amount)
-> (Amount -> Amount -> Amount)
-> Ord Amount
Amount -> Amount -> Bool
Amount -> Amount -> Ordering
Amount -> Amount -> Amount
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Amount -> Amount -> Ordering
compare :: Amount -> Amount -> Ordering
$c< :: Amount -> Amount -> Bool
< :: Amount -> Amount -> Bool
$c<= :: Amount -> Amount -> Bool
<= :: Amount -> Amount -> Bool
$c> :: Amount -> Amount -> Bool
> :: Amount -> Amount -> Bool
$c>= :: Amount -> Amount -> Bool
>= :: Amount -> Amount -> Bool
$cmax :: Amount -> Amount -> Amount
max :: Amount -> Amount -> Amount
$cmin :: Amount -> Amount -> Amount
min :: Amount -> Amount -> Amount
Ord, (forall x. Amount -> Rep Amount x)
-> (forall x. Rep Amount x -> Amount) -> Generic Amount
forall x. Rep Amount x -> Amount
forall x. Amount -> Rep Amount x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Amount -> Rep Amount x
from :: forall x. Amount -> Rep Amount x
$cto :: forall x. Rep Amount x -> Amount
to :: forall x. Rep Amount x -> Amount
Generic)

-- | SomeDiscrete values always use scale of 1
unitScale :: Money.Scale
unitScale :: Scale
unitScale = case Rational -> Maybe Scale
Money.scaleFromRational Rational
1 of
  Just Scale
s -> Scale
s
  Maybe Scale
Nothing -> String -> Scale
forall a. HasCallStack => String -> a
error String
"Money.scaleFromRational impossible"

instance ToJSON Money.SomeDiscrete where
  toJSON :: SomeDiscrete -> Value
toJSON SomeDiscrete
sd =
    [Pair] -> Value
object [ Key
"unit" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= SomeDiscrete -> Text
Money.someDiscreteCurrency SomeDiscrete
sd
           , Key
"quantity" Key -> String -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Integer -> String
forall a. Show a => a -> String
show (SomeDiscrete -> Integer
Money.someDiscreteAmount SomeDiscrete
sd)
           ]

instance FromJSON Money.SomeDiscrete where
  parseJSON :: Value -> Parser SomeDiscrete
parseJSON = String
-> (Object -> Parser SomeDiscrete) -> Value -> Parser SomeDiscrete
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"amount" ((Object -> Parser SomeDiscrete) -> Value -> Parser SomeDiscrete)
-> (Object -> Parser SomeDiscrete) -> Value -> Parser SomeDiscrete
forall a b. (a -> b) -> a -> b
$ \Object
o -> do
    Text
u <- Object
o Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"unit"
    (String
strQuant :: String) <- Object
o Object -> Key -> Parser String
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"quantity"
    case String -> Maybe Integer
forall a. Read a => String -> Maybe a
Text.Read.readMaybe String
strQuant of
      Maybe Integer
Nothing    -> String -> Parser SomeDiscrete
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Unable to read quantity as Integer"
      Just Integer
quant -> SomeDiscrete -> Parser SomeDiscrete
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SomeDiscrete -> Parser SomeDiscrete)
-> SomeDiscrete -> Parser SomeDiscrete
forall a b. (a -> b) -> a -> b
$ Text -> Scale -> Integer -> SomeDiscrete
Money.mkSomeDiscrete Text
u Scale
unitScale Integer
quant

instance ToJSON Amount where
  toJSON :: Amount -> Value
toJSON (AdaAmount Lovelaces
lovelaces) =
    [Pair] -> Value
object [ Key
"unit" Key -> String -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (String
"lovelace" :: String)
           , Key
"quantity" Key -> Value -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Discrete' "ADA" '(1000000, 1) -> Value
forall a. ToJSON a => a -> Value
toJSON Discrete' "ADA" '(1000000, 1)
Lovelaces
lovelaces
           ]
  toJSON (AssetAmount SomeDiscrete
av) = SomeDiscrete -> Value
forall a. ToJSON a => a -> Value
toJSON SomeDiscrete
av

instance FromJSON Amount where
  parseJSON :: Value -> Parser Amount
parseJSON x :: Value
x@(Object Object
o) = do
    (String
u :: String) <- Object
o Object -> Key -> Parser String
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"unit"
    Value
v <- Object
o Object -> Key -> Parser Value
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"quantity"
    case String
u of
      String
"lovelace" -> Discrete' "ADA" '(1000000, 1) -> Amount
Lovelaces -> Amount
AdaAmount (Discrete' "ADA" '(1000000, 1) -> Amount)
-> Parser (Discrete' "ADA" '(1000000, 1)) -> Parser Amount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser (Discrete' "ADA" '(1000000, 1))
forall a. FromJSON a => Value -> Parser a
parseJSON Value
v
      String
_          -> SomeDiscrete -> Amount
AssetAmount (SomeDiscrete -> Amount) -> Parser SomeDiscrete -> Parser Amount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser SomeDiscrete
forall a. FromJSON a => Value -> Parser a
parseJSON Value
x
  parseJSON Value
other = String -> Parser Amount
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Parser Amount) -> String -> Parser Amount
forall a b. (a -> b) -> a -> b
$ String
"Amount expecting object, got" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Value -> String
forall a. Show a => a -> String
show Value
other

instance ToSample Amount where
  toSamples :: Proxy Amount -> [(Text, Amount)]
toSamples = [(Text, Amount)] -> Proxy Amount -> [(Text, Amount)]
forall a. a -> Proxy Amount -> a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([(Text, Amount)] -> Proxy Amount -> [(Text, Amount)])
-> [(Text, Amount)] -> Proxy Amount -> [(Text, Amount)]
forall a b. (a -> b) -> a -> b
$ [Amount] -> [(Text, Amount)]
forall a. [a] -> [(Text, a)]
samples
    [ Lovelaces -> Amount
AdaAmount Discrete' "ADA" '(1000000, 1)
Lovelaces
42000000
    , SomeDiscrete -> Amount
AssetAmount
        (SomeDiscrete -> Amount) -> SomeDiscrete -> Amount
forall a b. (a -> b) -> a -> b
$ Discrete'
  "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"
  '(1, 1)
-> SomeDiscrete
forall (currency :: Symbol) (scale :: (Natural, Natural)).
(KnownSymbol currency, GoodScale scale) =>
Discrete' currency scale -> SomeDiscrete
Money.toSomeDiscrete
          (Discrete'
  "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"
  '(1, 1)
12 :: Money.Discrete'
                    "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"
                    '(1,1))
    , SomeDiscrete -> Amount
AssetAmount
        (SomeDiscrete -> Amount) -> SomeDiscrete -> Amount
forall a b. (a -> b) -> a -> b
$ Discrete'
  "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"
  '(1, 1)
-> SomeDiscrete
forall (currency :: Symbol) (scale :: (Natural, Natural)).
(KnownSymbol currency, GoodScale scale) =>
Discrete' currency scale -> SomeDiscrete
Money.toSomeDiscrete
          (Discrete'
  "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"
  '(1, 1)
18605647 :: Money.Discrete'
                          "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"
                          '(1,1))
    ]

-- | Like @Amount@, extended with @decimals` and `has_nft_onchain_metadata`
data AmountExtended =
    AdaAmountExtended Lovelaces
  | AssetAmountExtended
    { AmountExtended -> Maybe Int
assetAmountExtendedDecimals              :: Maybe Int
    , AmountExtended -> Bool
assetAmountExtendedHasNftOnchainMetadata :: Bool
    , AmountExtended -> SomeDiscrete
assetAmountExtendedValue                 :: Money.SomeDiscrete
    }
  deriving (AmountExtended -> AmountExtended -> Bool
(AmountExtended -> AmountExtended -> Bool)
-> (AmountExtended -> AmountExtended -> Bool) -> Eq AmountExtended
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: AmountExtended -> AmountExtended -> Bool
== :: AmountExtended -> AmountExtended -> Bool
$c/= :: AmountExtended -> AmountExtended -> Bool
/= :: AmountExtended -> AmountExtended -> Bool
Eq, Int -> AmountExtended -> ShowS
[AmountExtended] -> ShowS
AmountExtended -> String
(Int -> AmountExtended -> ShowS)
-> (AmountExtended -> String)
-> ([AmountExtended] -> ShowS)
-> Show AmountExtended
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AmountExtended -> ShowS
showsPrec :: Int -> AmountExtended -> ShowS
$cshow :: AmountExtended -> String
show :: AmountExtended -> String
$cshowList :: [AmountExtended] -> ShowS
showList :: [AmountExtended] -> ShowS
Show, Eq AmountExtended
Eq AmountExtended =>
(AmountExtended -> AmountExtended -> Ordering)
-> (AmountExtended -> AmountExtended -> Bool)
-> (AmountExtended -> AmountExtended -> Bool)
-> (AmountExtended -> AmountExtended -> Bool)
-> (AmountExtended -> AmountExtended -> Bool)
-> (AmountExtended -> AmountExtended -> AmountExtended)
-> (AmountExtended -> AmountExtended -> AmountExtended)
-> Ord AmountExtended
AmountExtended -> AmountExtended -> Bool
AmountExtended -> AmountExtended -> Ordering
AmountExtended -> AmountExtended -> AmountExtended
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: AmountExtended -> AmountExtended -> Ordering
compare :: AmountExtended -> AmountExtended -> Ordering
$c< :: AmountExtended -> AmountExtended -> Bool
< :: AmountExtended -> AmountExtended -> Bool
$c<= :: AmountExtended -> AmountExtended -> Bool
<= :: AmountExtended -> AmountExtended -> Bool
$c> :: AmountExtended -> AmountExtended -> Bool
> :: AmountExtended -> AmountExtended -> Bool
$c>= :: AmountExtended -> AmountExtended -> Bool
>= :: AmountExtended -> AmountExtended -> Bool
$cmax :: AmountExtended -> AmountExtended -> AmountExtended
max :: AmountExtended -> AmountExtended -> AmountExtended
$cmin :: AmountExtended -> AmountExtended -> AmountExtended
min :: AmountExtended -> AmountExtended -> AmountExtended
Ord, (forall x. AmountExtended -> Rep AmountExtended x)
-> (forall x. Rep AmountExtended x -> AmountExtended)
-> Generic AmountExtended
forall x. Rep AmountExtended x -> AmountExtended
forall x. AmountExtended -> Rep AmountExtended x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AmountExtended -> Rep AmountExtended x
from :: forall x. AmountExtended -> Rep AmountExtended x
$cto :: forall x. Rep AmountExtended x -> AmountExtended
to :: forall x. Rep AmountExtended x -> AmountExtended
Generic)

instance ToJSON AmountExtended where
  toJSON :: AmountExtended -> Value
toJSON (AdaAmountExtended Lovelaces
lovelaces) =
    [Pair] -> Value
object [ Key
"unit" Key -> String -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (String
"lovelace" :: String)
           , Key
"quantity" Key -> Value -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Discrete' "ADA" '(1000000, 1) -> Value
forall a. ToJSON a => a -> Value
toJSON Discrete' "ADA" '(1000000, 1)
Lovelaces
lovelaces
           , Key
"decimals" Key -> Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= (Int
6 :: Int)
           , Key
"has_nft_onchain_metadata" Key -> Bool -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Bool
False
           ]
  toJSON (AssetAmountExtended {Bool
Maybe Int
SomeDiscrete
assetAmountExtendedDecimals :: AmountExtended -> Maybe Int
assetAmountExtendedHasNftOnchainMetadata :: AmountExtended -> Bool
assetAmountExtendedValue :: AmountExtended -> SomeDiscrete
assetAmountExtendedDecimals :: Maybe Int
assetAmountExtendedHasNftOnchainMetadata :: Bool
assetAmountExtendedValue :: SomeDiscrete
..}) =
    [Pair] -> Value
object [ Key
"unit" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= SomeDiscrete -> Text
Money.someDiscreteCurrency SomeDiscrete
assetAmountExtendedValue
           , Key
"quantity" Key -> String -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Integer -> String
forall a. Show a => a -> String
show (SomeDiscrete -> Integer
Money.someDiscreteAmount SomeDiscrete
assetAmountExtendedValue)
           , Key
"decimals" Key -> Maybe Int -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Maybe Int
assetAmountExtendedDecimals
           , Key
"has_nft_onchain_metadata" Key -> Bool -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Bool
assetAmountExtendedHasNftOnchainMetadata
           ]

instance FromJSON AmountExtended where
  parseJSON :: Value -> Parser AmountExtended
parseJSON x :: Value
x@(Object Object
o) = do
    (String
u :: String) <- Object
o Object -> Key -> Parser String
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"unit"
    Value
v <- Object
o Object -> Key -> Parser Value
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"quantity"
    Maybe Int
assetAmountExtendedDecimals
      <- Object
o Object -> Key -> Parser (Maybe Int)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"decimals"
    Bool
assetAmountExtendedHasNftOnchainMetadata
      <- Object
o Object -> Key -> Parser Bool
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"has_nft_onchain_metadata"
    case String
u of
      String
"lovelace" -> Discrete' "ADA" '(1000000, 1) -> AmountExtended
Lovelaces -> AmountExtended
AdaAmountExtended (Discrete' "ADA" '(1000000, 1) -> AmountExtended)
-> Parser (Discrete' "ADA" '(1000000, 1)) -> Parser AmountExtended
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser (Discrete' "ADA" '(1000000, 1))
forall a. FromJSON a => Value -> Parser a
parseJSON Value
v
      String
_          -> do
        SomeDiscrete
assetAmountExtendedValue <- Value -> Parser SomeDiscrete
forall a. FromJSON a => Value -> Parser a
parseJSON Value
x
        AmountExtended -> Parser AmountExtended
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AssetAmountExtended{Bool
Maybe Int
SomeDiscrete
assetAmountExtendedDecimals :: Maybe Int
assetAmountExtendedHasNftOnchainMetadata :: Bool
assetAmountExtendedValue :: SomeDiscrete
assetAmountExtendedDecimals :: Maybe Int
assetAmountExtendedHasNftOnchainMetadata :: Bool
assetAmountExtendedValue :: SomeDiscrete
..}

  parseJSON Value
other = String -> Parser AmountExtended
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Parser AmountExtended)
-> String -> Parser AmountExtended
forall a b. (a -> b) -> a -> b
$ String
"Amount expecting object, got" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Value -> String
forall a. Show a => a -> String
show Value
other

instance ToSample AmountExtended where
  toSamples :: Proxy AmountExtended -> [(Text, AmountExtended)]
toSamples = [(Text, AmountExtended)]
-> Proxy AmountExtended -> [(Text, AmountExtended)]
forall a. a -> Proxy AmountExtended -> a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([(Text, AmountExtended)]
 -> Proxy AmountExtended -> [(Text, AmountExtended)])
-> [(Text, AmountExtended)]
-> Proxy AmountExtended
-> [(Text, AmountExtended)]
forall a b. (a -> b) -> a -> b
$ [AmountExtended] -> [(Text, AmountExtended)]
forall a. [a] -> [(Text, a)]
samples
    [ Lovelaces -> AmountExtended
AdaAmountExtended Discrete' "ADA" '(1000000, 1)
Lovelaces
42000000
    , AssetAmountExtended
        { assetAmountExtendedDecimals :: Maybe Int
assetAmountExtendedDecimals              = Maybe Int
forall a. Maybe a
Nothing
        , assetAmountExtendedHasNftOnchainMetadata :: Bool
assetAmountExtendedHasNftOnchainMetadata = Bool
True
        , assetAmountExtendedValue :: SomeDiscrete
assetAmountExtendedValue =
            Discrete'
  "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"
  '(1, 1)
-> SomeDiscrete
forall (currency :: Symbol) (scale :: (Natural, Natural)).
(KnownSymbol currency, GoodScale scale) =>
Discrete' currency scale -> SomeDiscrete
Money.toSomeDiscrete
            (Discrete'
  "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"
  '(1, 1)
12 :: Money.Discrete'
                      "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"
                      '(1,1))
        }
    ]