Skip to content

Instantly share code, notes, and snippets.

@leMedi
Created May 5, 2021 23:49
Show Gist options
  • Save leMedi/17546cd6d05c415144cc38ab6f1a61a9 to your computer and use it in GitHub Desktop.
Save leMedi/17546cd6d05c415144cc38ab6f1a61a9 to your computer and use it in GitHub Desktop.
Plutus Playground Smart Contract
import Control.Lens (makeClassyPrisms, prism', review)
import Control.Monad (void)
import Control.Monad.Error.Lens (catching, throwing, throwing_)
import Data.Text (Text)
import qualified Data.Text as T
import Language.Plutus.Contract (AsContractError (_ContractError), ContractError, HasAwaitSlot, logInfo,
mapError, select)
import Playground.Contract
import Prelude (Maybe (..), const, show, ($), (.), (<>), (>>), (>>=))
-- Demonstrates how to deal with errors in Plutus contracts. We define a custom
-- error type 'MyError' with three constructors and use
-- 'Control.Lens.makeClassyPrisms' to generate the 'AsMyError' class. We can
-- then use 'MyError' in our contracts with the combinators from
-- 'Control.Monad.Error.Lens'. The unit tests in 'Spec.ErrorHandling' show how
-- to write tests for error conditions.
type Schema =
BlockchainActions
.\/ Endpoint "throwError" Text
.\/ Endpoint "catchError" Text
.\/ Endpoint "catchContractError" ()
-- | 'MyError' has a constructor for each type of error that our contract
-- can throw. The 'AContractError' constructor wraps a 'ContractError'.
data MyError =
Error1 Text
| Error2
| AContractError ContractError
deriving Show
makeClassyPrisms ''MyError
instance AsContractError MyError where
-- 'ContractError' is another error type. It is defined in
-- 'Language.Plutus.Contract.Request'. By making 'MyError' an
-- instance of 'AsContractError' we can handle 'ContractError's
-- thrown by other contracts in our code (see 'catchContractError')
_ContractError = _AContractError
instance AsMyError Text where
_MyError = prism' (T.pack . show) (const Nothing)
-- | Throw an 'Error1', using 'Control.Monad.Error.Lens.throwing' and the
-- prism generated by 'makeClassyPrisms'
throw :: AsMyError e => Text -> Contract s e ()
throw e = do
logInfo @Text $ "throwError: " <> e
throwing _Error1 e
-- | Handle the error from 'throw' using 'Control.Monad.Error.Lens.catching'
throwAndCatch :: AsMyError e => Text -> Contract s e ()
throwAndCatch e =
let handleError1 :: Text -> Contract s e ()
handleError1 t = logInfo $ "handleError: " <> t
in catching _Error1 (throw e) handleError1
-- | Handle an error from 'awaitSlot' by wrapping it in the 'AContractError'
-- constructor
catchContractError :: (AsMyError e, HasAwaitSlot s) => Contract s e ()
catchContractError =
catching _AContractError
(void $ mapError (review _AContractError) $ awaitSlot 10)
(\_ -> throwing_ _Error2)
contract
:: ( AsMyError e
, AsContractError e
)
=> Contract Schema e ()
contract =
(endpoint @"throwError" >>= throw)
`select` (endpoint @"catchError" >>= throwAndCatch)
`select` (endpoint @"catchContractError" >> catchContractError)
endpoints :: (AsMyError e, AsContractError e) => Contract Schema e ()
endpoints = contract
mkSchemaDefinitions ''Schema
$(mkKnownCurrencies [])
[0,[{"simulationWallets":[{"simulatorWalletWallet":{"getWallet":1},"simulatorWalletBalance":{"getValue":[[{"unCurrencySymbol":""},[[{"unTokenName":""},100]]]]}},{"simulatorWalletWallet":{"getWallet":2},"simulatorWalletBalance":{"getValue":[[{"unCurrencySymbol":""},[[{"unTokenName":""},100]]]]}}],"simulationName":"Throw/Catch","simulationId":1,"simulationActions":[{"caller":{"getWallet":1},"argumentValues":{"endpointDescription":{"getEndpointDescription":"throwError"},"argument":{"contents":"Hello","tag":"FormStringF"}},"tag":"CallEndpoint"},{"caller":{"getWallet":2},"argumentValues":{"endpointDescription":{"getEndpointDescription":"catchError"},"argument":{"contents":"World","tag":"FormStringF"}},"tag":"CallEndpoint"}]}]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment