|
1 | 1 | {-# OPTIONS_GHC -fno-specialise #-}
|
2 | 2 |
|
| 3 | +-- | |
| 4 | +-- Module : TrustlessSidechain.Utils |
| 5 | +-- Description : Utility functions for Plutus scripts and test logic |
| 6 | +-- |
| 7 | +-- This module provides utility functions for working with Plutus data structures. |
3 | 8 | module TrustlessSidechain.Utils (
|
4 | 9 | fromSingleton,
|
5 | 10 | fromSingletonData,
|
@@ -30,59 +35,115 @@ import PlutusLedgerApi.V2 (
|
30 | 35 | import PlutusTx.AssocMap qualified as Map
|
31 | 36 | import PlutusTx.Data.List qualified as List
|
32 | 37 |
|
33 |
| --- | Unwrap a singleton list, or produce an error if not possible. |
| 38 | +-- | Unwrap a singleton list, or fail with a custom error. |
| 39 | +fromSingleton :: |
| 40 | + -- | Error message used if the list is not a singleton. |
| 41 | + BuiltinString -> |
| 42 | + -- | Input list |
| 43 | + [a] -> |
| 44 | + -- | The only element in the list (or error) |
| 45 | + a |
34 | 46 | {-# INLINEABLE fromSingleton #-}
|
35 |
| -fromSingleton :: BuiltinString -> [a] -> a |
36 | 47 | fromSingleton _ [x] = x
|
37 | 48 | fromSingleton msg _ = traceError msg
|
38 | 49 |
|
39 |
| --- | Unwrap a singleton list, or produce an error if not possible. |
| 50 | +-- | Unwrap a singleton Plutus list, or fail with a custom error. |
| 51 | +fromSingletonData :: |
| 52 | + (UnsafeFromData a) => |
| 53 | + -- \| Error message used if the list is not a singleton. |
| 54 | + BuiltinString -> |
| 55 | + -- | Plutus list |
| 56 | + List.List a -> |
| 57 | + -- | The only element in the list (or error) |
| 58 | + a |
40 | 59 | {-# INLINEABLE fromSingletonData #-}
|
41 |
| -fromSingletonData :: (UnsafeFromData a) => BuiltinString -> List.List a -> a |
42 | 60 | fromSingletonData msg list = case List.uncons list of
|
43 | 61 | Just (x, rest) | List.null rest -> x
|
44 | 62 | _ -> traceError msg
|
45 | 63 |
|
46 |
| --- | Unwrap a Just ctor, or produce an error if not possible. |
| 64 | +-- | Unwrap a 'Maybe' value, or fail with a custom error. |
| 65 | +fromJust :: |
| 66 | + -- | Error message used if the value is 'Nothing' |
| 67 | + BuiltinString -> |
| 68 | + -- | Input optional value |
| 69 | + Maybe a -> |
| 70 | + -- | The contained value (or error) |
| 71 | + a |
47 | 72 | {-# INLINEABLE fromJust #-}
|
48 |
| -fromJust :: forall a. BuiltinString -> Maybe a -> a |
49 | 73 | fromJust err m =
|
50 | 74 | case m of
|
51 | 75 | Just d -> d
|
52 | 76 | Nothing -> traceError err
|
53 | 77 |
|
54 |
| --- | Get amount of given currency in a value, ignoring token names. |
| 78 | +-- | Get the total amount of a given currency symbol in a value, ignoring token names. |
| 79 | +currencySymbolValueOf :: |
| 80 | + -- | Value to inspect |
| 81 | + Value -> |
| 82 | + -- | Currency symbol to extract total quantity for |
| 83 | + CurrencySymbol -> |
| 84 | + -- | Total quantity of that currency symbol |
| 85 | + Integer |
55 | 86 | {-# INLINEABLE currencySymbolValueOf #-}
|
56 |
| -currencySymbolValueOf :: Value -> CurrencySymbol -> Integer |
57 | 87 | currencySymbolValueOf v c = maybe 0 sum $ Map.lookup c $ getValue v
|
58 | 88 |
|
59 |
| --- | Check that exactly on specified asset was minted by a transaction. Note |
60 |
| --- that transaction is also allowed to mint/burn tokens of the same |
61 |
| --- 'CurrencySymbol', but with different 'TokenName's. |
| 89 | +-- | Check that exactly one token of the specified currency and token name was minted. |
| 90 | +oneTokenMinted :: |
| 91 | + -- | Transaction info |
| 92 | + DataV2.TxInfo -> |
| 93 | + -- | Currency symbol |
| 94 | + DataV1.CurrencySymbol -> |
| 95 | + -- | Token name |
| 96 | + DataV1.TokenName -> |
| 97 | + -- | True if exactly one token was minted |
| 98 | + Bool |
62 | 99 | {-# INLINEABLE oneTokenMinted #-}
|
63 |
| -oneTokenMinted :: DataV2.TxInfo -> DataV1.CurrencySymbol -> DataV1.TokenName -> Bool |
64 | 100 | oneTokenMinted txInfo cs tn =
|
65 | 101 | DataV1.valueOf (DataV2.txInfoMint txInfo) cs tn == 1
|
66 | 102 |
|
67 |
| --- | Check that exactly one specified asset was burned by a transaction. Note |
68 |
| --- that transaction is also allowed to burn tokens of the same 'CurrencySymbol', |
69 |
| --- but with different 'TokenName's. |
| 103 | +-- | Check that exactly one token of the specified currency and token name was burned. |
| 104 | +oneTokenBurned :: |
| 105 | + -- | The 'txInfoMint' field of the transaction, treated as a 'Value' |
| 106 | + Value -> |
| 107 | + -- | Currency symbol |
| 108 | + CurrencySymbol -> |
| 109 | + -- | Token name |
| 110 | + TokenName -> |
| 111 | + -- | True if exactly one token was burned |
| 112 | + Bool |
70 | 113 | {-# INLINEABLE oneTokenBurned #-}
|
71 |
| -oneTokenBurned :: Value -> CurrencySymbol -> TokenName -> Bool |
72 | 114 | oneTokenBurned txInfoMint cs tn =
|
73 | 115 | valueOf txInfoMint cs tn == -1
|
74 | 116 |
|
75 |
| -scriptToPlutusScript :: SerialisedScript -> PlutusScript PlutusScriptV2 |
| 117 | +-- | Convert a serialized Plutus script to a 'PlutusScript' suitable for submission via the Cardano API. |
| 118 | +scriptToPlutusScript :: |
| 119 | + -- | Serialized script (from 'PlutusTx.compile') |
| 120 | + SerialisedScript -> |
| 121 | + -- | Wrapped script for use in transactions |
| 122 | + PlutusScript PlutusScriptV2 |
76 | 123 | scriptToPlutusScript =
|
77 | 124 | PlutusScriptSerialised @PlutusScriptV2
|
78 | 125 |
|
| 126 | +-- | Get all outputs that pay to the specified address. |
| 127 | +getOutputsAt :: |
| 128 | + -- | Transaction info |
| 129 | + DataV2.TxInfo -> |
| 130 | + -- | Address to filter for |
| 131 | + DataV2.Address -> |
| 132 | + -- | Outputs paying to that address |
| 133 | + List.List DataV2.TxOut |
79 | 134 | {-# INLINEABLE getOutputsAt #-}
|
80 |
| -getOutputsAt :: DataV2.TxInfo -> DataV2.Address -> List.List DataV2.TxOut |
81 | 135 | getOutputsAt txInfo address =
|
82 | 136 | ((== address) . DataV2.txOutAddress) `List.filter` DataV2.txInfoOutputs txInfo
|
83 | 137 |
|
| 138 | +-- | Get all resolved inputs that come from the specified address. |
| 139 | +getInputsAt :: |
| 140 | + -- | Transaction info |
| 141 | + DataV2.TxInfo -> |
| 142 | + -- | Address to filter for |
| 143 | + DataV2.Address -> |
| 144 | + -- | Inputs that were spent from that address |
| 145 | + List.List DataV2.TxOut |
84 | 146 | {-# INLINEABLE getInputsAt #-}
|
85 |
| -getInputsAt :: DataV2.TxInfo -> DataV2.Address -> List.List DataV2.TxOut |
86 | 147 | getInputsAt txInfo address =
|
87 | 148 | DataV2.txInInfoResolved
|
88 | 149 | `List.map` List.filter ((== address) . DataV2.txOutAddress . DataV2.txInInfoResolved) (DataV2.txInfoInputs txInfo)
|
0 commit comments