3

This doesn't work

mkPolicy oref () ctx = traceIfFalse "UTxO not consumed"   hasUTxO           &&
                       traceIfFalse "wrong amount minted" checkMintedAmount
  where
    info :: TxInfo
    info = scriptContextTxInfo ctx

    hasUTxO :: Bool
    hasUTxO = any (\i -> txInInfoOutRef i == oref) $ txInfoInputs info

    checkMintedAmount :: Bool
    checkMintedAmount = case flattenValue (txInfoMint info) of
        [(_, tn, amt)]  -> tn == "" && amt == 1
        _               -> False

and throws

GHC Core to PLC plugin: E042:Error: Unsupported feature: Type constructor: GHC.Prim.Char#
Context: Compiling type: GHC.Prim.Char#
Context: Compiling data constructor type: GHC.Types.C#
Context: Compiling type: GHC.Types.Char
Context: Compiling type: GHC.Base.String
Context: Compiling type: GHC.Base.String
                         -> Data.ByteString.Internal.ByteString
Context: Compiling definition of: Plutus.V1.Ledger.Value.$fIsStringTokenName1
Context: Compiling expr: Plutus.V1.Ledger.Value.$fIsStringTokenName1
Context: Compiling expr: Plutus.V1.Ledger.Value.$fIsStringTokenName1
                           ((GHC.Types.[] @ GHC.Types.Char)
                            `cast` (Sym (Nth:2
                                             (((<GHC.Base.String>_R
                                                ->_R Sym (PlutusTx.Builtins.Internal.N:BuiltinByteString[0]) ; Sym (Plutus.V1.Ledger.Value.N:TokenName[0])) ; Sym (Data.String.N:IsString[0]) <Plutus.V1.Ledger.Value.TokenName>_N) ; Data.String.N:IsString[0] <Plutus.V1.Ledger.Value.TokenName>_N))
                                    :: GHC.Types.Coercible GHC.Base.String GHC.Base.String))
... (a lot of lines skipped here)
Context: Compiling definition of: Week05.Homework2.mkPolicy
Context: Compiling expr: Week05.Homework2.mkPolicy
Context: Compiling expr: Week05.Homework2.mkPolicy oref'_axTu
Context: Compiling expr: PlutusTx.Base.$
                           @ (() -> Plutus.V1.Ledger.Contexts.ScriptContext -> GHC.Types.Bool)
                           @ Ledger.Typed.Scripts.MonetaryPolicies.WrappedMintingPolicyType
                           (Ledger.Typed.Scripts.MonetaryPolicies.wrapMintingPolicy
                              @ () PlutusTx.IsData.Instances.$fUnsafeFromData())
                           (Week05.Homework2.mkPolicy oref'_axTu)
Context: Compiling expr: \ (oref'_axTu :: Plutus.V1.Ledger.Tx.TxOutRef) ->
                           PlutusTx.Base.$
                             @ (() -> Plutus.V1.Ledger.Contexts.ScriptContext -> GHC.Types.Bool)
                             @ Ledger.Typed.Scripts.MonetaryPolicies.WrappedMintingPolicyType
                             (Ledger.Typed.Scripts.MonetaryPolicies.wrapMintingPolicy
                                @ () PlutusTx.IsData.Instances.$fUnsafeFromData())
                             (Week05.Homework2.mkPolicy oref'_axTu)
Context: Compiling expr at "plutus-pioneer-program-week05-0.1.0.0-inplace:Week05.Homework2:(55,8)-(55,85)"

but this works

mkPolicy oref () ctx = traceIfFalse "UTxO not consumed"   hasUTxO           &&
                       traceIfFalse "wrong amount minted" checkMintedAmount
  where
    info :: TxInfo
    info = scriptContextTxInfo ctx

    emptyToken :: TokenName
    emptyToken = TokenName ""

    hasUTxO :: Bool
    hasUTxO = any (\i -> txInInfoOutRef i == oref) $ txInfoInputs info

    checkMintedAmount :: Bool
    checkMintedAmount = case flattenValue (txInfoMint info) of
        [(_, tn, amt)]  -> tn == emptyToken && amt == 1
        _               -> False

I assumed that {-# LANGUAGE OverloadedStrings #-} would work as TokenName is just a newtype for PlutusTx.Builtins.Internal.BuiltinByteString. From the repl

Prelude Week05.Homework2> :t Plutus.V1.Ledger.Value.TokenName
Plutus.V1.Ledger.Value.TokenName
  :: PlutusTx.Builtins.Internal.BuiltinByteString
     -> Plutus.V1.Ledger.Value.TokenName
Prelude Week05.Homework2> :i Plutus.V1.Ledger.Value.TokenName
type Plutus.V1.Ledger.Value.TokenName :: *
newtype Plutus.V1.Ledger.Value.TokenName
  = Plutus.V1.Ledger.Value.TokenName {Plutus.V1.Ledger.Value.unTokenName :: PlutusTx.Builtins.Internal.BuiltinByteString}
    -- Defined in `Plutus.V1.Ledger.Value'
instance Eq Plutus.V1.Ledger.Value.TokenName
  -- Defined in `Plutus.V1.Ledger.Value'
instance Ord Plutus.V1.Ledger.Value.TokenName
  -- Defined in `Plutus.V1.Ledger.Value'
instance Show Plutus.V1.Ledger.Value.TokenName
  -- Defined in `Plutus.V1.Ledger.Value'

Edit #1

On reading this doc I realised that maybe TokenName is not an instance of IsString. I tried to implement it using:

instance IsString TokenName where
        fromString = unTokenName

but I got this error

src/Week05/Homework2.hs:32:10: error:
    Duplicate instance declarations:
      instance IsString TokenName
        -- Defined at src/Week05/Homework2.hs:32:10
      instance IsString TokenName -- Defined in `Plutus.V1.Ledger.Value'
   |
32 | instance IsString TokenName where
   |          ^^^^^^^^^^^^^^^^^^

Edit #2

The complete Homework file is here.

1

2 Answers 2

3

After digging into Plutus source, I think I've found why tn == "" crashes at runtime.

The problem might be that the instance of TokenName IsString doesn't use the INLINABLE pragma in the function fromString. Due to this, Plutus core crashes. (As a reminder, every function in order to compiles to Plutus core must have the INLINABLE pragma).

[source: Plutus.V1.Ledger.Value]

instance IsString TokenName where
    fromString = fromText . Text.pack

On other hand, for example traceIfFalse "UTxO not consumed" hasUTxO is resolved properly because the function fromString in IsString BuiltinString uses the INLINABLE mechanism.

[source: PlutusTx.Builtins.Class]

-- We can't put this in `Builtins.hs`, since that force `O0` deliberately, which prevents
-- the unfoldings from going in. So we just stick it here. Fiddly.
instance IsString BuiltinString where
    -- Try and make sure the dictionary selector goes away, it's simpler to match on
    -- the application of 'stringToBuiltinString'
    {-# INLINE fromString #-}
    -- See Note [noinline hack]
    fromString = Magic.noinline stringToBuiltinString

{-# INLINABLE stringToBuiltinString #-}
stringToBuiltinString :: Haskell.String -> BuiltinString
-- To explain why the obfuscatedId is here
-- See Note [noinline hack]
stringToBuiltinString str = obfuscatedId (BuiltinString $ pack str)

Why don't Plutus team use INLINABLE mechanism in the function fromString for TokenName? I don't know, it might be a bug.

EDIT 1

I've raised an issue on Plutus repo.

https://github.com/input-output-hk/plutus/issues/4404

EDIT 2

The issue is (see Plutus team's answer https://github.com/input-output-hk/plutus/issues/4404)

Unfortunately, due to the technical reasons plutus plugin doesn't support OverloadedStrings with newtypes.

Notice the same thing happens with PubKeyHash (it's a newtype)

0

The problem is you don't have IsString in scope. If you add import Data.String should work. Here you can see it in the repl.

enter image description here

EDIT 1

( First part of the answer referred to how to bring into scope IsString for TokenName in the repl. )

Additionally, I've tried to compile your code with the imports and pragmas that you can see in the following code snippet and the result of compiling has been successful. Probably, you have something wrong with imports.

{-# LANGUAGE NoImplicitPrelude  #-}
{-# LANGUAGE OverloadedStrings  #-}

module Plutus.Contracts.Currency( mkPolicy ) where

import Ledger.Value ( flattenValue )
import Ledger.Contexts
    ( ScriptContext(scriptContextTxInfo),
      TxOutRef,
      TxInfo(txInfoInputs, txInfoMint),
      TxInInfo(txInInfoOutRef) )
import PlutusTx.Prelude
    ( Bool(False), Eq((==)), ($), (&&), any, traceIfFalse )

mkPolicy :: TxOutRef -> () -> ScriptContext -> Bool
mkPolicy oref () ctx = traceIfFalse "UTxO not consumed"   hasUTxO           &&
                       traceIfFalse "wrong amount minted" checkMintedAmount
  where
    info :: TxInfo
    info = scriptContextTxInfo ctx

    hasUTxO :: Bool
    hasUTxO = any (\i -> txInInfoOutRef i == oref) $ txInfoInputs info

    checkMintedAmount :: Bool
    checkMintedAmount = case flattenValue (txInfoMint info) of
        [(_, tn, amt)]  -> tn == "" && amt == 1
        _               -> False    
6
  • But in week01 here there was no such import yet it still overloaded the string. Is there a special case for BuiltinByteString? I remember this because I didn't about the Overloaded String pragma ({-# LANGUAGE OverloadedStrings #-}).
    – Roofi
    Feb 10, 2022 at 11:23
  • @Roofi, you are right, maybe for BuiltinByteString is different. Just curious, Does it work your code by importing Data.String?
    – kindofdev
    Feb 10, 2022 at 11:51
  • Sorry, it doesn't. At line 30 I changed import Data.String( IsString(..) ) to import Data.String and then tried the first code block in my question. Also in my previous comment, I meant "didn't know about" instead of "didn't about".
    – Roofi
    Feb 10, 2022 at 12:08
  • @Roofi, I've just edited my answer. Please, take a look.
    – kindofdev
    Feb 10, 2022 at 12:34
  • 1
    I think the code you provided may not raise that error as the error stems from the usage of wrapMintingPolicy. I inferred this by looking at the last line of the error block. I just edited my question to add a link to the complete code.
    – Roofi
    Feb 10, 2022 at 14:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.