1

I ran the code from this plutus playground tutorial in the online playground https://playground.plutus.iohkdev.io/doc/plutus/tutorials/basic-apps.html#

I'm getting this error when entering a number for the recipient wallet:

"... Contract instance stopped with error: "EndpointDecodeContractError {eeEndpointDescription = EndpointDescription {getEndpointDescription = "unlock"}, eeEndpointValue = EndpointValue {unEndpointValue = Object (fromList [("recipient1Wallet",String "1"),("recipient2Wallet",String "2"),("totalAda",Object (fromList [("getLovelace",Number 5000000.0)]))])}, eeErrorMessage = "Error in $.recipient1Wallet: parsing Wallet.Emulator.Wallet.Wallet(Wallet) failed, expected Object, but encountered String"}" ]"

I also don't see the same fields in the simulation as the screenshot from the tutorial.

This is screenshot from the tutorial simulation enter image description here

This is what is what my simulation looks like: enter image description here

There's no "getWallet" text above the wallet fields.

I copied the code directly from the tutorial:

import Playground.Contract
import Control.Monad (void)
import Data.Aeson (FromJSON, ToJSON)
import Data.Text qualified as T
import GHC.Generics (Generic)
import Ledger (Ada, PaymentPubKeyHash (unPaymentPubKeyHash), ScriptContext (ScriptContext, scriptContextTxInfo),
               valuePaidTo)
import Ledger.Ada qualified as Ada
import Ledger.Constraints qualified as Constraints
import Ledger.Typed.Scripts qualified as Scripts
import Plutus.Contract (Contract, Endpoint, Promise, collectFromScript, endpoint, logInfo, selectList,
                        submitTxConstraints, submitTxConstraintsSpending, type (.\/), utxosAt)
import PlutusTx qualified
import PlutusTx.Prelude (Bool, Semigroup ((<>)), ($), (&&), (-), (.), (>=))
import Prelude qualified as Haskell
import Schema (ToSchema)
import Wallet.Emulator.Wallet (Wallet, mockWalletPaymentPubKeyHash)


--  Define data types
data SplitData =
    SplitData
        { recipient1 :: PaymentPubKeyHash -- ^ First recipient of the funds
        , recipient2 :: PaymentPubKeyHash -- ^ Second recipient of the funds
        , amount     :: Ada -- ^ How much Ada we want to lock
        }
    deriving stock (Haskell.Show, Generic)

-- For a 'real' application use 'makeIsDataIndexed' to ensure the output is stable over time
PlutusTx.unstableMakeIsData ''SplitData
PlutusTx.makeLift ''SplitData
----------------------------------------------------------------------------------------------------------------



-- Validator used off chain.  Takes context 
validateSplit :: SplitData -> () -> ScriptContext -> Bool
validateSplit SplitData{recipient1, recipient2, amount} _ ScriptContext{scriptContextTxInfo} =
    let half = Ada.divide amount 2 in
    Ada.fromValue (valuePaidTo scriptContextTxInfo (unPaymentPubKeyHash recipient1)) >= half &&
    Ada.fromValue (valuePaidTo scriptContextTxInfo (unPaymentPubKeyHash recipient2)) >= (amount - half)


--boilerplate to compile the validator
data Split
instance Scripts.ValidatorTypes Split where
    type instance RedeemerType Split = ()
    type instance DatumType Split = SplitData

splitValidator :: Scripts.TypedValidator Split
splitValidator = Scripts.mkTypedValidator @Split
    $$(PlutusTx.compile [|| validateSplit ||])
    $$(PlutusTx.compile [|| wrap ||]) where
        wrap = Scripts.wrapValidator @SplitData @()
-----------------------------------------------------------


-- Arguments for endpoints
data LockArgs =
        LockArgs
            { recipient1Wallet :: Wallet
            , recipient2Wallet :: Wallet
            , totalAda         :: Ada
            }
    deriving stock (Haskell.Show, Generic)
    deriving anyclass (ToJSON, FromJSON, ToSchema)
--  defines the endpoints
type SplitSchema =
        Endpoint "lock" LockArgs
        .\/ Endpoint "unlock" LockArgs
------------------------------------------------------------------------


-- Endpoints
lock :: Promise () SplitSchema T.Text ()
lock = endpoint @"lock" (lockFunds . mkSplitData)

unlock :: Promise () SplitSchema T.Text ()
unlock = endpoint @"unlock" (unlockFunds . mkSplitData)
------------------------------------------------------------------------



---   turn the two Wallet values into their public key hashes so that you can get the SplitData value from the input that was supplied by the user.
mkSplitData :: LockArgs -> SplitData
mkSplitData LockArgs{recipient1Wallet, recipient2Wallet, totalAda} =
    SplitData
        { recipient1 = mockWalletPaymentPubKeyHash recipient1Wallet
        , recipient2 = mockWalletPaymentPubKeyHash recipient2Wallet
        , amount = totalAda
        }
---------------------------------------------------------------------------------

-------------  Endpoint Functions ----------------------------------------------------
--  Lock function
lockFunds :: SplitData -> Contract () SplitSchema T.Text ()
lockFunds s@SplitData{amount} = do
    logInfo $ "Locking " <> Haskell.show amount
    let tx = Constraints.mustPayToTheScript s (Ada.toValue amount)  -- tx is transaction
    void $ submitTxConstraints splitValidator tx

-- unlock function
unlockFunds :: SplitData -> Contract () SplitSchema T.Text ()
unlockFunds SplitData{recipient1, recipient2, amount} = do
    let contractAddress = Scripts.validatorAddress splitValidator
    utxos <- utxosAt contractAddress
    let half = Ada.divide amount 2
        tx =
            collectFromScript utxos ()  -- collectFromScript takes the script outputs in unspentOutputs and adds them as input to the transaction, using the unit () as the redeemer.
            <> Constraints.mustPayToPubKey recipient1 (Ada.toValue half)
            <> Constraints.mustPayToPubKey recipient2 (Ada.toValue $ amount - half)
    void $ submitTxConstraintsSpending splitValidator utxos tx
---------------------------------------------------------------------------------




endpoints :: Contract () SplitSchema T.Text ()
endpoints = selectList [lock, unlock]


mkSchemaDefinitions ''SplitSchema
$(mkKnownCurrencies [])

1 Answer 1

0

Using the WalletNumber import and a few other changes in the code below fixed it for me, if anybody runs into the same issue. Surprising that the tutorial on the plutus playground website doesn't work with the plutus playground.

import Playground.Contract
import Control.Monad (void)
import Data.Aeson (FromJSON, ToJSON)
import Data.Text qualified as T
import GHC.Generics (Generic)
import Ledger (Ada, PaymentPubKeyHash (unPaymentPubKeyHash), ScriptContext (ScriptContext, scriptContextTxInfo),
               valuePaidTo)
import Ledger.Ada qualified as Ada
import Ledger.Constraints qualified as Constraints
import Ledger.Typed.Scripts qualified as Scripts
import Plutus.Contract (Contract, Endpoint, Promise, collectFromScript, endpoint, logInfo, selectList,
                        submitTxConstraints, submitTxConstraintsSpending, type (.\/), utxosAt)
import PlutusTx qualified
import PlutusTx.Prelude (Bool, Semigroup ((<>)), ($), (&&), (-), (.), (>=))
import Prelude qualified as Haskell
import Schema (ToSchema)
import Wallet.Emulator.Wallet (Wallet, mockWalletPaymentPubKeyHash)
import Wallet.Emulator.Wallet (fromWalletNumber)
import Ledger.CardanoWallet (WalletNumber (..))

--  Define data types
data SplitData =
    SplitData
        { recipient1 :: PaymentPubKeyHash -- ^ First recipient of the funds
        , recipient2 :: PaymentPubKeyHash -- ^ Second recipient of the funds
        , amount     :: Ada -- ^ How much Ada we want to lock
        }
    deriving stock (Haskell.Show, Generic)

-- For a 'real' application use 'makeIsDataIndexed' to ensure the output is stable over time
PlutusTx.unstableMakeIsData ''SplitData
PlutusTx.makeLift ''SplitData
----------------------------------------------------------------------------------------------------------------



-- Validator used off chain.  Takes context 
validateSplit :: SplitData -> () -> ScriptContext -> Bool
validateSplit SplitData{recipient1, recipient2, amount} _ ScriptContext{scriptContextTxInfo} =
    let half = Ada.divide amount 2 in
    Ada.fromValue (valuePaidTo scriptContextTxInfo (unPaymentPubKeyHash recipient1)) >= half &&
    Ada.fromValue (valuePaidTo scriptContextTxInfo (unPaymentPubKeyHash recipient2)) >= (amount - half)


--boilerplate to compile the validator
data Split
instance Scripts.ValidatorTypes Split where
    type instance RedeemerType Split = ()
    type instance DatumType Split = SplitData

splitValidator :: Scripts.TypedValidator Split
splitValidator = Scripts.mkTypedValidator @Split
    $$(PlutusTx.compile [|| validateSplit ||])
    $$(PlutusTx.compile [|| wrap ||]) where
        wrap = Scripts.wrapValidator @SplitData @()
-----------------------------------------------------------


-- Arguments for endpoints
data LockArgs =
        LockArgs
            { recipient1Wallet :: WalletNumber
            , recipient2Wallet :: WalletNumber
            , totalAda         :: Ada
            }
    deriving stock (Haskell.Show, Generic)
    deriving anyclass (ToJSON, FromJSON, ToSchema)
--  defines the endpoints
type SplitSchema =
        Endpoint "lock" LockArgs
        .\/ Endpoint "unlock" LockArgs
------------------------------------------------------------------------


-- Endpoints
lock :: Promise () SplitSchema T.Text ()
lock = endpoint @"lock" (lockFunds . mkSplitData)

unlock :: Promise () SplitSchema T.Text ()
unlock = endpoint @"unlock" (unlockFunds . mkSplitData)
------------------------------------------------------------------------



---   turn the two Wallet values into their public key hashes so that you can get the SplitData value from the input that was supplied by the user.
mkSplitData :: LockArgs -> SplitData
mkSplitData LockArgs{recipient1Wallet, recipient2Wallet, totalAda} =
    SplitData
        { recipient1 = mockWalletPaymentPubKeyHash (fromWalletNumber recipient1Wallet)
        , recipient2 = mockWalletPaymentPubKeyHash (fromWalletNumber recipient2Wallet)
        , amount = totalAda
        }
---------------------------------------------------------------------------------

-------------  Endpoint Functions ----------------------------------------------------
--  Lock function
lockFunds :: SplitData -> Contract () SplitSchema T.Text ()
lockFunds s@SplitData{amount} = do
    logInfo $ "Locking " <> Haskell.show amount
    let tx = Constraints.mustPayToTheScript s (Ada.toValue amount)  -- tx is transaction
    void $ submitTxConstraints splitValidator tx

-- unlock function
unlockFunds :: SplitData -> Contract () SplitSchema T.Text ()
unlockFunds SplitData{recipient1, recipient2, amount} = do
    let contractAddress = Scripts.validatorAddress splitValidator
    utxos <- utxosAt contractAddress
    let half = Ada.divide amount 2
        tx =
            collectFromScript utxos ()  -- collectFromScript takes the script outputs in unspentOutputs and adds them as input to the transaction, using the unit () as the redeemer.
            <> Constraints.mustPayToPubKey recipient1 (Ada.toValue half)
            <> Constraints.mustPayToPubKey recipient2 (Ada.toValue $ amount - half)
    void $ submitTxConstraintsSpending splitValidator utxos tx
---------------------------------------------------------------------------------




endpoints :: Contract () SplitSchema T.Text ()
endpoints = selectList [lock, unlock]


mkSchemaDefinitions ''SplitSchema
$(mkKnownCurrencies [])

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.