Can somebody explain these lines and why (both) Constraints.typedValidatorLookups... and Constraints.otherScript... are used? Still struggling to get my head around lookups, so maybe that is why I'm somehow failing to grasp what exactly the purpose of these lines is and why for example, just the second is not enough by itself?

let lookups = Constraints.unspentOutputs (Map.singleton oref o)     <>
Constraints.typedValidatorLookups (typedValidator oracle) <>
Constraints.otherScript (oracleValidator oracle)```

(Core.hs : lines 169, 170)

2 Answers 2


This one is a bit technical it has to do with looking up and updating the constraints of an already existing transaction (that belongs to the script address). See Case ii) to skip my synopsis.

Just a little synopsis for future readers: The Core.hs script is an Oracle, that has to update its datum regarding Ada price. The lines in which lookups occur are in the function updateOracle that has the purpose of building a transaction and then submitting it. To accomplish this the constraints need to refer to typed script inputs and outputs. In updateOracle we dealt with two cases

    case m of
        Nothing -> do
            ledgerTx <- submitTxConstraints (typedOracleValidator oracle) c
        Just (oref, o,  _) -> do
            let lookups = Constraints.unspentOutputs (Map.singleton oref o)     <>
                          Constraints.typedValidatorLookups (typedOracleValidator oracle) <>
                          Constraints.otherScript (oracleValidator oracle)
                tx      = c <> Constraints.mustSpendScriptOutput oref (Redeemer $ PlutusTx.toBuiltinData Update)
            ledgerTx <- submitTxConstraintsWith @Oracling lookups tx
  • i) Create the Oracle (if it doesn't exists) at the same time of submission and add datum.

  • ii) Just update the datum of an existing Oracle

Case i) uses submitTxConstraints which takes the constraints as inputs in the form of a script instance (with typed validator hash & the compiled program) and the locked value with the script.

Case ii) on the other hand, uses submitTxConstraintsWith which takes the updated constraints as ScriptLookup type rather than a script instance. To your question, we need to semigroup:

  • Constraints.typedValidatorLookups (this gets the previous parameters of the ScriptLookup)
  • Constraints.unspentOutputs (updates slTxOutputs from ScriptLookup, which are the unspent outputs that the script wants to spend)
  • Constraints.otherScript (updates slOtherScripts from ScriptLookup, which are validators of the script other than our script)

The reason why Constraints.typedValidatorLookups alone is not enough is because we would not be updating anything without semigrouping ScriptLookup element by element as defined in OffChain.hs)

  • What do you mean by "the previous parameters of the ScriptLookup"? Isn't the point of these lines precisely to construct the ScriptLookup?
    – ZachSweet
    Commented Aug 11, 2021 at 15:04
  • No, that is handled when case m of -> Nothing for creating the Oracle for the first time. After that we do have initial ScripLookup parameters.
    – Igodlab
    Commented Aug 11, 2021 at 16:44
  • Wait, so the Scriptlookup "state" is persistent while the (off-chain) contract is running in the wallet? Is that correct? I had understood that we were building everything up from scratch each time for each new transaction, on each pass. And if that is so, then where exacly is this Scriptlookup stored / why is it not lost once we leave the updateOracle function? As far as I can make out it is not stored on the blockchain... (btw thanks for your answers so far!)
    – ZachSweet
    Commented Aug 11, 2021 at 19:42
  • Yes, we build it from scratch the first time, ScriptLookup gives instructions to the wallet on how to create the transaction, and since transactions are unique it lives in the blockchain. Thus, we can access and update it latter on. Updates are only possible through new transactions, these are also build every time but they access to the previous state of the lookups and <> UTXOs out and validators.
    – Igodlab
    Commented Aug 12, 2021 at 18:59

I share your confusion around this. I think a lot of it is due to the flux in the codebase.

On the other hand, I don't think it is anything complicated. The main purpose of Lookups, and thus also those two lines is to supply information needed for the transaction.

I noted that Constraints.otherScript seems to be consistently used where a transaction consumes uTxOs and needs to supply the validator script.

Have a look at Lars explanation in the Iteration #2 Lecture #6 video at 00:50h. In it he mentions that two scripts need to be supplied: one for the input uTxO (consume) and one for output uTxO. This explains why there are two entries, and my guess is that otherScript entry is to be able to consume a script uTxO.

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.