{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE FlexibleInstances        #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeFamilies             #-}
{-# LANGUAGE TypeOperators            #-}

module PlutusTx.Builtins.HasBuiltin where

import Prelude

import PlutusCore.Crypto.BLS12_381.G1 qualified as BLS12_381.G1 (Element)
import PlutusCore.Crypto.BLS12_381.G2 qualified as BLS12_381.G2 (Element)
import PlutusCore.Crypto.BLS12_381.Pairing qualified as BLS12_381.Pairing (MlResult)
import PlutusCore.Data (Data)
import PlutusCore.Default qualified as PLC
import PlutusTx.Builtins.Internal

import Data.ByteString (ByteString)
import Data.Kind qualified as GHC
import Data.Text (Text)

{- Note [useToOpaque and useFromOpaque]
It used to be possible to use 'toBuiltin'/'fromBuiltin' within a smart contract, but this is no
longer the case, hence we throw a compilation error suggesting to use 'toOpaque'/'fromOpaque'
instead.
-}

useToOpaque :: a -> a
useToOpaque :: forall a. a -> a
useToOpaque a
x = a
x
{-# OPAQUE useToOpaque #-}

useFromOpaque :: a -> a
useFromOpaque :: forall a. a -> a
useFromOpaque a
x = a
x
{-# OPAQUE useFromOpaque #-}

-- Also see Note [Built-in types and their Haskell counterparts].
-- | A class for converting values of Haskell-defined built-in types to their Plutus Tx
-- counterparts.
type HasToBuiltin :: GHC.Type -> GHC.Constraint
class PLC.DefaultUni `PLC.Contains` a => HasToBuiltin a where
    type ToBuiltin a
    toBuiltin :: a -> ToBuiltin a

-- Also see Note [Built-in types and their Haskell counterparts].
-- | A class for converting values of Plutus Tx built-in types to their Haskell-defined
-- counterparts.
type HasFromBuiltin :: GHC.Type -> GHC.Constraint
class HasToBuiltin (FromBuiltin arep) => HasFromBuiltin arep where
    type FromBuiltin arep
    fromBuiltin :: arep -> FromBuiltin arep

instance HasToBuiltin Integer where
    type ToBuiltin Integer = BuiltinInteger
    toBuiltin :: Integer -> ToBuiltin Integer
toBuiltin = (Integer -> Integer) -> Integer -> Integer
forall a. a -> a
useToOpaque Integer -> Integer
forall a. a -> a
id
instance HasFromBuiltin BuiltinInteger where
    type FromBuiltin BuiltinInteger = Integer
    fromBuiltin :: Integer -> FromBuiltin Integer
fromBuiltin = (Integer -> Integer) -> Integer -> Integer
forall a. a -> a
useFromOpaque Integer -> Integer
forall a. a -> a
id

instance HasToBuiltin ByteString where
    type ToBuiltin ByteString = BuiltinByteString
    toBuiltin :: ByteString -> ToBuiltin ByteString
toBuiltin = (ByteString -> BuiltinByteString)
-> ByteString -> BuiltinByteString
forall a. a -> a
useToOpaque ByteString -> BuiltinByteString
BuiltinByteString
instance HasFromBuiltin BuiltinByteString where
    type FromBuiltin BuiltinByteString = ByteString
    fromBuiltin :: BuiltinByteString -> FromBuiltin BuiltinByteString
fromBuiltin = (BuiltinByteString -> FromBuiltin BuiltinByteString)
-> BuiltinByteString -> FromBuiltin BuiltinByteString
forall a. a -> a
useFromOpaque ((BuiltinByteString -> FromBuiltin BuiltinByteString)
 -> BuiltinByteString -> FromBuiltin BuiltinByteString)
-> (BuiltinByteString -> FromBuiltin BuiltinByteString)
-> BuiltinByteString
-> FromBuiltin BuiltinByteString
forall a b. (a -> b) -> a -> b
$ \(BuiltinByteString ByteString
b) -> ByteString
FromBuiltin BuiltinByteString
b

instance HasToBuiltin Text where
    type ToBuiltin Text = BuiltinString
    toBuiltin :: Text -> ToBuiltin Text
toBuiltin = (Text -> BuiltinString) -> Text -> BuiltinString
forall a. a -> a
useToOpaque Text -> BuiltinString
BuiltinString
instance HasFromBuiltin BuiltinString where
    type FromBuiltin BuiltinString = Text
    fromBuiltin :: BuiltinString -> FromBuiltin BuiltinString
fromBuiltin (BuiltinString Text
t) = Text
FromBuiltin BuiltinString
t

instance HasToBuiltin () where
    type ToBuiltin () = BuiltinUnit
    toBuiltin :: () -> ToBuiltin ()
toBuiltin = (() -> BuiltinUnit) -> () -> BuiltinUnit
forall a. a -> a
useToOpaque () -> BuiltinUnit
BuiltinUnit
instance HasFromBuiltin BuiltinUnit where
    type FromBuiltin BuiltinUnit = ()
    fromBuiltin :: BuiltinUnit -> FromBuiltin BuiltinUnit
fromBuiltin (BuiltinUnit ()
u) = ()
FromBuiltin BuiltinUnit
u

instance HasToBuiltin Bool where
    type ToBuiltin Bool = BuiltinBool
    toBuiltin :: Bool -> ToBuiltin Bool
toBuiltin = (Bool -> BuiltinBool) -> Bool -> BuiltinBool
forall a. a -> a
useToOpaque Bool -> BuiltinBool
BuiltinBool
instance HasFromBuiltin BuiltinBool where
    type FromBuiltin BuiltinBool = Bool
    fromBuiltin :: BuiltinBool -> FromBuiltin BuiltinBool
fromBuiltin (BuiltinBool Bool
b) = Bool
FromBuiltin BuiltinBool
b

instance HasToBuiltin a => HasToBuiltin [a] where
    type ToBuiltin [a] = BuiltinList (ToBuiltin a)
    toBuiltin :: [a] -> ToBuiltin [a]
toBuiltin = ([ToBuiltin a] -> BuiltinList (ToBuiltin a))
-> [ToBuiltin a] -> BuiltinList (ToBuiltin a)
forall a. a -> a
useToOpaque [ToBuiltin a] -> BuiltinList (ToBuiltin a)
forall a. [a] -> BuiltinList a
BuiltinList ([ToBuiltin a] -> BuiltinList (ToBuiltin a))
-> ([a] -> [ToBuiltin a]) -> [a] -> BuiltinList (ToBuiltin a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> ToBuiltin a) -> [a] -> [ToBuiltin a]
forall a b. (a -> b) -> [a] -> [b]
map a -> ToBuiltin a
forall a. HasToBuiltin a => a -> ToBuiltin a
toBuiltin
instance HasFromBuiltin a => HasFromBuiltin (BuiltinList a) where
    type FromBuiltin (BuiltinList a) = [FromBuiltin a]
    fromBuiltin :: BuiltinList a -> FromBuiltin (BuiltinList a)
fromBuiltin (BuiltinList [a]
xs) = (a -> FromBuiltin a) -> [a] -> [FromBuiltin a]
forall a b. (a -> b) -> [a] -> [b]
map a -> FromBuiltin a
forall arep. HasFromBuiltin arep => arep -> FromBuiltin arep
fromBuiltin [a]
xs

instance (HasToBuiltin a, HasToBuiltin b) => HasToBuiltin (a, b) where
    type ToBuiltin (a, b) = BuiltinPair (ToBuiltin a) (ToBuiltin b)
    toBuiltin :: (a, b) -> ToBuiltin (a, b)
toBuiltin (a
x, b
y) = (ToBuiltin a, ToBuiltin b)
-> BuiltinPair (ToBuiltin a) (ToBuiltin b)
forall a b. (a, b) -> BuiltinPair a b
BuiltinPair (a -> ToBuiltin a
forall a. HasToBuiltin a => a -> ToBuiltin a
toBuiltin a
x, b -> ToBuiltin b
forall a. HasToBuiltin a => a -> ToBuiltin a
toBuiltin b
y)
instance (HasFromBuiltin a, HasFromBuiltin b) => HasFromBuiltin (BuiltinPair a b) where
    type FromBuiltin (BuiltinPair a b) = (FromBuiltin a, FromBuiltin b)
    fromBuiltin :: BuiltinPair a b -> FromBuiltin (BuiltinPair a b)
fromBuiltin (BuiltinPair (a
x, b
y)) = (a -> FromBuiltin a
forall arep. HasFromBuiltin arep => arep -> FromBuiltin arep
fromBuiltin a
x, b -> FromBuiltin b
forall arep. HasFromBuiltin arep => arep -> FromBuiltin arep
fromBuiltin b
y)

instance HasToBuiltin Data where
    type ToBuiltin Data = BuiltinData
    toBuiltin :: Data -> ToBuiltin Data
toBuiltin = (Data -> BuiltinData) -> Data -> BuiltinData
forall a. a -> a
useToOpaque Data -> BuiltinData
BuiltinData
instance HasFromBuiltin BuiltinData where
    type FromBuiltin BuiltinData = Data
    fromBuiltin :: BuiltinData -> FromBuiltin BuiltinData
fromBuiltin (BuiltinData Data
t) = Data
FromBuiltin BuiltinData
t

instance HasToBuiltin BLS12_381.G1.Element where
    type ToBuiltin BLS12_381.G1.Element = BuiltinBLS12_381_G1_Element
    toBuiltin :: Element -> ToBuiltin Element
toBuiltin = (Element -> BuiltinBLS12_381_G1_Element)
-> Element -> BuiltinBLS12_381_G1_Element
forall a. a -> a
useToOpaque Element -> BuiltinBLS12_381_G1_Element
BuiltinBLS12_381_G1_Element
instance HasFromBuiltin BuiltinBLS12_381_G1_Element where
    type FromBuiltin BuiltinBLS12_381_G1_Element = BLS12_381.G1.Element
    fromBuiltin :: BuiltinBLS12_381_G1_Element
-> FromBuiltin BuiltinBLS12_381_G1_Element
fromBuiltin (BuiltinBLS12_381_G1_Element Element
a) = Element
FromBuiltin BuiltinBLS12_381_G1_Element
a

instance HasToBuiltin BLS12_381.G2.Element where
    type ToBuiltin BLS12_381.G2.Element = BuiltinBLS12_381_G2_Element
    toBuiltin :: Element -> ToBuiltin Element
toBuiltin = (Element -> BuiltinBLS12_381_G2_Element)
-> Element -> BuiltinBLS12_381_G2_Element
forall a. a -> a
useToOpaque Element -> BuiltinBLS12_381_G2_Element
BuiltinBLS12_381_G2_Element
instance HasFromBuiltin BuiltinBLS12_381_G2_Element where
    type FromBuiltin BuiltinBLS12_381_G2_Element = BLS12_381.G2.Element
    fromBuiltin :: BuiltinBLS12_381_G2_Element
-> FromBuiltin BuiltinBLS12_381_G2_Element
fromBuiltin (BuiltinBLS12_381_G2_Element Element
a) = Element
FromBuiltin BuiltinBLS12_381_G2_Element
a

instance HasToBuiltin BLS12_381.Pairing.MlResult where
    type ToBuiltin BLS12_381.Pairing.MlResult = BuiltinBLS12_381_MlResult
    toBuiltin :: MlResult -> ToBuiltin MlResult
toBuiltin = (MlResult -> BuiltinBLS12_381_MlResult)
-> MlResult -> BuiltinBLS12_381_MlResult
forall a. a -> a
useToOpaque MlResult -> BuiltinBLS12_381_MlResult
BuiltinBLS12_381_MlResult
instance HasFromBuiltin BuiltinBLS12_381_MlResult where
    type FromBuiltin BuiltinBLS12_381_MlResult = BLS12_381.Pairing.MlResult
    fromBuiltin :: BuiltinBLS12_381_MlResult -> FromBuiltin BuiltinBLS12_381_MlResult
fromBuiltin (BuiltinBLS12_381_MlResult MlResult
a) = MlResult
FromBuiltin BuiltinBLS12_381_MlResult
a