@@ -36,7 +36,6 @@ module PlutusLedgerApi.Test.ScriptContextBuilder.Builder
3636 , mkMintingScriptWithPurpose
3737 , addChangeOutput
3838 , signAndAddChangeOutput
39- , negateValue
4039 , mkAdaValue
4140 , mkTxOut
4241 , withTxOutReferenceScript
@@ -48,6 +47,10 @@ module PlutusLedgerApi.Test.ScriptContextBuilder.Builder
4847 , buildBalancedScriptContext
4948 , balanceWithChangeOutput
5049 , builderPlaceHolderTxOutRef
50+
51+ -- * Helpers
52+ , currencySymbolFromHex
53+ , singleCurrencySymbol
5154 )
5255where
5356
@@ -56,6 +59,7 @@ import Data.ByteString.Base16 qualified as Base16
5659import Data.ByteString.Char8 qualified as BS8
5760import Data.Function (on )
5861import Data.List qualified as List
62+ import Data.Maybe (isJust )
5963import Data.Ord (comparing )
6064import GHC.Generics (Generic )
6165import PlutusLedgerApi.Test.ScriptContextBuilder.Lenses
@@ -68,10 +72,11 @@ import PlutusLedgerApi.Test.ScriptContextBuilder.Lenses
6872 , txInfoReferenceInputsL
6973 , txInfoSignatoriesL
7074 )
75+ import PlutusLedgerApi.V1.Address (toPubKeyHash , toScriptHash )
7176import PlutusLedgerApi.V3
7277 ( Address (Address )
7378 , BuiltinData
74- , Credential (PubKeyCredential , ScriptCredential )
79+ , Credential (PubKeyCredential )
7580 , CurrencySymbol (CurrencySymbol )
7681 , Datum (Datum )
7782 , Lovelace (getLovelace )
@@ -100,27 +105,11 @@ import PlutusTx qualified
100105import PlutusTx.AssocMap qualified as Map
101106import PlutusTx.Builtins.Internal (BuiltinByteString (.. ))
102107import PlutusTx.Eq qualified
108+ import PlutusTx.Numeric qualified as PlutusTx
103109
104110instance PlutusTx.Eq. Eq ScriptPurpose where
105111 a == b = PlutusTx. toBuiltinData a == PlutusTx. toBuiltinData b
106112
107- -- | Convert a hex encoded Haskell 'String' to a 'CurrencySymbol'.
108- currencySymbolFromHex :: String -> CurrencySymbol
109- currencySymbolFromHex hexStr =
110- case Base16. decode (BS8. pack hexStr) of
111- Left err -> error $ " currencySymbolFromHex: invalid hex: " <> err
112- Right bs -> CurrencySymbol (BuiltinByteString bs)
113-
114- {-| Extract the single currency symbol from a 'Value'. Errors if the value
115- contains zero or more than one currency symbol. -}
116- singleCurrencySymbol :: Value -> CurrencySymbol
117- singleCurrencySymbol val = case Map. keys (getValue val) of
118- [cs] -> cs
119- keys ->
120- error $
121- " singleCurrencySymbol: expected exactly 1 currency symbol, got "
122- <> show (length keys)
123-
124113-- | Arguments for a unit test: a script context and additional parameters.
125114data UnitTestArgs = UnitTestArgs
126115 { utaScriptContext :: ScriptContext
@@ -332,42 +321,50 @@ mkMintingScriptWithPurpose mintValue redeemer =
332321 , txInfoTreasuryDonation = Nothing
333322 }
334323
335- -- | Negate all token quantities in a 'Value'.
336- negateValue :: Value -> Value
337- negateValue (Value val) = Value $ Map. mapWithKey (\ _ -> Map. mapWithKey (\ _ x -> negate x)) val
338-
339324-- | Compute and add a change output to the given public key hash.
340325addChangeOutput :: PubKeyHash -> ScriptContext -> ScriptContext
341326addChangeOutput signerPkh ctx =
342- let ti = ctx ^. scriptContextTxInfoL
343- totalInputValue = foldMap (txOutValue . txInInfoResolved) (ti ^. txInfoInputsL)
344- totalOutputValue = foldMap txOutValue (ti ^. txInfoOutputsL)
345- feeValue = mkAdaValue (ti ^. txInfoFeeL)
346- mintedValue = Value $ mintValueToMap (ti ^. txInfoMintL)
347- changeValue = mintedValue <> totalInputValue <> negateValue feeValue <> negateValue totalOutputValue
348- changeOutput = TxOut (Address (PubKeyCredential signerPkh) Nothing ) changeValue NoOutputDatum Nothing
327+ let info = ctx ^. scriptContextTxInfoL
328+ totalInputValue = foldMap (txOutValue . txInInfoResolved) (info ^. txInfoInputsL)
329+ totalOutputValue = foldMap txOutValue (info ^. txInfoOutputsL)
330+ feeValue = mkAdaValue (info ^. txInfoFeeL)
331+ mintedValue = Value (mintValueToMap (info ^. txInfoMintL))
332+ changeOutput =
333+ TxOut
334+ (Address (PubKeyCredential signerPkh) Nothing )
335+ ( mintedValue
336+ <> totalInputValue
337+ <> PlutusTx. negate feeValue
338+ <> PlutusTx. negate totalOutputValue
339+ )
340+ NoOutputDatum
341+ Nothing
349342 in ctx & scriptContextTxInfoL . txInfoOutputsL %~ (changeOutput : )
350343
351344-- | Balance the transaction by adding a change output to the first public key input.
352345balanceWithChangeOutput :: ScriptContext -> ScriptContext
353346balanceWithChangeOutput ctx =
354- let ti = ctx ^. scriptContextTxInfoL
355- resolvedInputs = map txInInfoResolved (ti ^. txInfoInputsL)
356- signerPkh = case filter (isPubKeyAddress . txOutAddress) resolvedInputs of
357- ( TxOut (Address (PubKeyCredential pkh) _) _ _ _ : _) -> pkh
347+ let info = ctx ^. scriptContextTxInfoL
348+ resolvedInputs = map txInInfoResolved (info ^. txInfoInputsL)
349+ signerPkh = case filter (isJust . toPubKeyHash . txOutAddress) resolvedInputs of
350+ TxOut (Address (PubKeyCredential pkh) _) _ _ _ : _ -> pkh
358351 _ -> PubKeyHash " deadbeef"
359352 -- \^ Fallback to default if no public key input is found
360- totalInputValue = foldMap (txOutValue . txInInfoResolved) (ti ^. txInfoInputsL)
361- totalOutputValue = foldMap txOutValue (ti ^. txInfoOutputsL)
362- feeValue = mkAdaValue (ti ^. txInfoFeeL)
363- mintedValue = Value $ mintValueToMap (ti ^. txInfoMintL)
364- changeValue = mintedValue <> totalInputValue <> negateValue feeValue <> negateValue totalOutputValue
365- changeOutput = TxOut (Address (PubKeyCredential signerPkh) Nothing ) changeValue NoOutputDatum Nothing
353+ totalInputValue = foldMap (txOutValue . txInInfoResolved) (info ^. txInfoInputsL)
354+ totalOutputValue = foldMap txOutValue (info ^. txInfoOutputsL)
355+ feeValue = mkAdaValue (info ^. txInfoFeeL)
356+ mintedValue = Value $ mintValueToMap (info ^. txInfoMintL)
357+ changeOutput =
358+ TxOut
359+ (Address (PubKeyCredential signerPkh) Nothing )
360+ ( mintedValue
361+ <> totalInputValue
362+ <> PlutusTx. negate feeValue
363+ <> PlutusTx. negate totalOutputValue
364+ )
365+ NoOutputDatum
366+ Nothing
366367 in ctx & scriptContextTxInfoL . txInfoOutputsL %~ (<> [changeOutput])
367- where
368- isPubKeyAddress :: Address -> Bool
369- isPubKeyAddress (Address (PubKeyCredential _) _) = True
370- isPubKeyAddress _ = False
371368
372369-- | Add a signatory to an existing 'ScriptContext'.
373370addSigner :: PubKeyHash -> ScriptContext -> ScriptContext
@@ -457,36 +454,27 @@ withOutput modify = ScriptContextBuilder \scb ->
457454
458455-- | Add a public-key input. Errors if the address is a script address.
459456withInput :: InputBuilder -> ScriptContextBuilder
460- withInput modify = ScriptContextBuilder \ scb ->
461- let newInput = mkInput modify
462- newInputAddress = txOutAddress $ txInInfoResolved newInput
463- in if isPubKeyAddress newInputAddress
464- then
465- scb {scbInputs = List. insertBy (comparing txInInfoOutRef) newInput (scbInputs scb)}
466- else
467- error " withInput: Input address is not a public key address"
457+ withInput inputBuilder = ScriptContextBuilder \ scb ->
458+ if isJust (toPubKeyHash (txOutAddress (txInInfoResolved newInput)))
459+ then scb {scbInputs = List. insertBy (comparing txInInfoOutRef) newInput (scbInputs scb)}
460+ else error " withInput: Input address is not a public key address"
468461 where
469- isPubKeyAddress :: Address -> Bool
470- isPubKeyAddress (Address (PubKeyCredential _) _) = True
471- isPubKeyAddress _ = False
462+ newInput :: TxInInfo
463+ newInput = mkInput inputBuilder
472464
473465-- | Add a script input with a redeemer. Errors if the address is not a script address.
474466withScriptInput :: BuiltinData -> InputBuilder -> ScriptContextBuilder
475467withScriptInput redeemer modify = ScriptContextBuilder \ scb ->
476468 let newInput = mkInput modify
477469 inputOutRef = txInInfoOutRef newInput
478470 newRedeemers = Map. insert (Spending inputOutRef) (Redeemer redeemer) (scbRedeemers scb)
479- in if isScriptAddress ( txOutAddress $ txInInfoResolved newInput)
471+ in if isJust (toScriptHash ( txOutAddress $ txInInfoResolved newInput) )
480472 then
481473 scb
482474 { scbInputs = List. insertBy (comparing txInInfoOutRef) newInput (scbInputs scb)
483475 , scbRedeemers = newRedeemers
484476 }
485477 else error " withScriptInput: Input address is not a script address"
486- where
487- isScriptAddress :: Address -> Bool
488- isScriptAddress (Address (ScriptCredential _) _) = True
489- isScriptAddress _ = False
490478
491479-- | Add a reference input (read-only, not spent).
492480withReferenceInput :: InputBuilder -> ScriptContextBuilder
@@ -630,3 +618,22 @@ buildBalancedScriptContext modify =
630618 }
631619 where
632620 finalState = runBuilder modify defaultScriptContextBuilderState
621+
622+ -- * Helpers
623+
624+ -- | Convert a hex encoded Haskell 'String' to a 'CurrencySymbol'.
625+ currencySymbolFromHex :: String -> CurrencySymbol
626+ currencySymbolFromHex hexStr =
627+ case Base16. decode (BS8. pack hexStr) of
628+ Left err -> error $ " currencySymbolFromHex: invalid hex: " <> err
629+ Right bs -> CurrencySymbol (BuiltinByteString bs)
630+
631+ {-| Extract the single currency symbol from a 'Value'. Errors if the value
632+ contains zero or more than one currency symbol. -}
633+ singleCurrencySymbol :: Value -> CurrencySymbol
634+ singleCurrencySymbol val = case Map. keys (getValue val) of
635+ [cs] -> cs
636+ keys ->
637+ error $
638+ " singleCurrencySymbol: expected exactly 1 currency symbol, got "
639+ <> show (length keys)
0 commit comments