0

I would like to write a factory script that can initialize another parameterized script address by sending it an NFT. The factory validator gets the parameters of the target script as redeemer and has to check if the NFT is actually sent to correct script address. Testing this with EmulatorTrace gives me an error that lets me doubt if calculating/validating a parameterized address is possible on-chain.

Writing it in Plutus looks like this:

{-# INLINABLE validateCreate #-}
validateCreate :: Factory
               -> [TargetParams] -- Datum: List of already created scripts
               -> TargetParams   -- Redeemer: parameters for new script to be created
               -> ScriptContext
               -> Bool
validateCreate factory existingScriptParams newScritpParam ctx =
    someOtherValidationLogic &&
    Constraints.checkTxConstraint ctx
      (MustPayToOtherScript (Scripts.validatorHash $ targetInst newScritpParam)
        calculateDatumLogic nftValue)
  where
    someOtherValidationLogic :: Bool
    calculateDatumLogic :: TargetDatum
    nftValue :: Value

With the targetInst function looking pretty standard:

{-# INLINABLE targetInst #-}
targetInst :: TargetParams -> Scripts.TypedValidator Target
targetInst p = Scripts.mkTypedValidator @Target
    ($$(PlutusTx.compile [|| mkTargetValidator ||])
    `PlutusTx.applyCode` PlutusTx.liftCode p)
    $$(PlutusTx.compile [|| wrap ||])
  where
    wrap = Scripts.wrapValidator @Integer @TargetAction

This loads in cabal repl without problems. But when executing the EmulatorTrace, it yields the following error:

*** Exception: Error: Unsupported feature: Type constructor: GHC.Prim.ByteArray#
Context: Compiling definition of: Target.targetInst
Context: Compiling definition of: Factory.validateCreate
Context: Compiling definition of: Factory.mkFactoryValidator
Context: Compiling expr at "ProjectName-0.1.0.0-inplace:Offchain:(84,9)-(84,52)"

I do not know what is going on in the template Haskell of targetInst. It looks to me like the calculation of a script address with the parameters given in the redeemer is not possible in the on-chain validator.

This raises a couple of questions for me:

  • Is it just a stupid mistake on my side?
  • Is there another way to calculate or validate script addresses with parameters given at runtime?
  • Would another architecture be better for what I am trying to accomplish? My goal is to separate different instances of my target script while ensuring that there is only one for each set of parameters.

I think this question is related to this one. But there were no helpful answers and I would like to give a more detailed description.

Thanks for your help.

1 Answer 1

1

You are asking a couple different questions here.

This loads in cabal repl without problems. But when executing the EmulatorTrace, it yields the following error:

*** Exception: Error: Unsupported feature: Type constructor: GHC.Prim.ByteArray#

Does your Target script include ByteArray? I believe this is saying it's not INLINABLE, and thus can't be compiled into Plutus. GHC wouldn't see any issues with it, that's why cabal repl doesn't complain.


Would another architecture be better for what I am trying to accomplish? My goal is to separate different instances of my target script while ensuring that there is only one for each set of parameters.

Generally the way these sort of access NFT validators would be written is the NFT isn't sent to some target address, but rather must be included in the txn (and returned to the validator in the same txn with a new datum). The target script can then just check that it's included in the txn.

You say you want "a factory script that can initialize another parameterized script". What is to stop you from sending any arbitrary token to "initialize" instead of your desired NFT. There are no constraints on Datum creation for a script (anyone can initialize a script) there are just constraints on who can spend the Datum UTxO with a Redeemer.

I'm not sure what behavior you want from your Target scripts, but as a rule of thumb I'd recommend:

  • Add constraints to the Factory script to ensure the NFT is returned to itself with a new Datum rather than sent to some param address
  • Use the params to ensure the rest of the txn is valid to spend some Datum at the Target address
  • Then have Target just check that the NFT is included in the txn--if the NFT is spent, then that means Targets requirements are met

Your Answer

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

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