As per entire code below, give
always mandates that Wallet 2
be given:
thePkh :: PubKeyHash
thePkh = pubKeyHash $ walletPubKey $ Wallet 2
theVal :: Value
theVal = ADA.toValue (ADA.Lovelace 3_000_000)
give :: Contract () PaySchema Text ()
give = do
let param = PayParam thePkh theVal
let tx = mustPayToTheScript () theVal
ledgerTx <- submitTxConstraints (typedValidator param) tx
So why must I also invoke the grab
endpoint for Wallet 2
(trace12
)? What changes if instead it's invoked for Wallet 3
(trace13
)?
trace12 #######################################
...
Final balances
Wallet 1:
{, ""}: 96999990
Wallet 2:
{, ""}: 102996448
Wallet 3:
{, ""}: 100000000
Wallet 4:
{, ""}: 100000000
Wallet 5:
{, ""}: 100000000
Wallet 6:
{, ""}: 100000000
Wallet 7:
{, ""}: 100000000
Wallet 8:
{, ""}: 100000000
Wallet 9:
{, ""}: 100000000
Wallet 10:
{, ""}: 100000000
Note that if Wallet 3
instead is invoked for grab
, then the result is different:
trace13 #######################################
...
Final balances
Wallet 1:
{, ""}: 96999990
Wallet 2:
{, ""}: 100000000
Wallet 3:
{, ""}: 100000000
Wallet 4:
{, ""}: 100000000
Wallet 5:
{, ""}: 100000000
Wallet 6:
{, ""}: 100000000
Wallet 7:
{, ""}: 100000000
Wallet 8:
{, ""}: 100000000
Wallet 9:
{, ""}: 100000000
Wallet 10:
{, ""}: 100000000
Script 3f3c25dfdfad56d6f08d6d3e8bec53ab14d1950be944bc0ed26957c6:
{, ""}: 3000000
Why is that? And what does it mean to invoke an endpoint for a wallet altogether?
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
module ASK (trace12, trace13 ) where
import Control.Monad hiding (fmap)
import Data.Map as Map
import Data.Monoid as Mnd ( (<>), Monoid(mconcat) )
import Data.Text (Text, unpack)
import Data.Void (Void)
import GHC.Generics (Generic)
import qualified Prelude as H--askell
import Plutus.Contract
import qualified Plutus.Trace.Emulator as Emu
import PlutusTx (Data (..))
import qualified PlutusTx
import PlutusTx.Prelude hiding (Semigroup(..), unless)
import Ledger hiding (singleton)
import Ledger.Constraints as Constraints
import qualified Ledger.Typed.Scripts as Scripts
import Ledger.Value (Value(Value), geq)
import Wallet.Emulator.Wallet (Wallet(Wallet), walletPubKey)
import qualified Plutus.V1.Ledger.Ada as ADA
import qualified Control.Monad.Freer.Extras as Extras
data PayParam = PayParam
{ _pkh :: PubKeyHash
, _val :: Value
} deriving H.Show
PlutusTx.makeLift ''PayParam
{-# INLINABLE mkValidator #-}
mkValidator :: PayParam -> () -> () -> ScriptContext -> Bool
mkValidator param () () ctx =
traceIfFalse "beneficiary's signature missing" signedByBeneficiary
where
info :: TxInfo
info = scriptContextTxInfo ctx
signedByBeneficiary :: Bool
signedByBeneficiary = txSignedBy info $ _pkh param
data Lock
instance Scripts.ValidatorTypes Lock where
type instance DatumType Lock = ()
type instance RedeemerType Lock = ()
typedValidator :: PayParam -> Scripts.TypedValidator Lock
typedValidator param = Scripts.mkTypedValidator @Lock
($$(PlutusTx.compile [|| mkValidator ||]) `PlutusTx.applyCode` PlutusTx.liftCode param)
$$(PlutusTx.compile [|| wrap ||])
where
wrap = Scripts.wrapValidator @() @()
{-# INLINABLE validator #-}
validator :: PayParam -> Validator
validator = Scripts.validatorScript . typedValidator
{-# INLINABLE valHash #-}
valHash :: PayParam -> Ledger.ValidatorHash
valHash = Scripts.validatorHash . typedValidator
{-# INLINABLE scrAddress #-}
scrAddress :: PayParam -> Ledger.Address
scrAddress = scriptAddress . validator
--------------------------
{-# INLINABLE thePkh #-}
thePkh :: PubKeyHash
thePkh = pubKeyHash $ walletPubKey $ Wallet 2
{-# INLINABLE theVal #-}
theVal :: Value
theVal = ADA.toValue (ADA.Lovelace 3_000_000)
give :: Contract () PaySchema Text ()
give = do
let param = PayParam thePkh theVal
let tx = mustPayToTheScript () theVal
ledgerTx <- submitTxConstraints (typedValidator param) tx
-- also works fine
-- let tx = mustPayToOtherScript (valHash param) unitDatum theVal
-- ledgerTx <- submitTx tx
void $ awaitTxConfirmed $ txId ledgerTx
logInfo @H.String "\ngive"
grab :: Contract () PaySchema Text ()
grab = do
let param = PayParam thePkh theVal
utxos <- utxoAt $ scrAddress param
let lookups = Constraints.unspentOutputs utxos <>
Constraints.otherScript (validator param) <>
Constraints.typedValidatorLookups (typedValidator param)
let orefs = fst <$> Map.toList utxos
let tx :: TxConstraints () ()
tx = PlutusTx.Prelude.mconcat [mustSpendScriptOutput oref unitRedeemer | oref <- orefs]
ledgerTx <- submitTxConstraintsWith @Lock lookups tx
awaitTxConfirmed $ txId ledgerTx
logInfo @H.String "\ngrab"
type PaySchema =
Endpoint "give" ()
.\/
Endpoint "grab" ()
endpoints :: Contract () PaySchema Text ()
endpoints =
forever
$ handleError logError
$ awaitPromise
$ give' `select` grab'
where
give' = endpoint @"give" $ const give
grab' = endpoint @"grab" $ const grab
trace12 :: H.IO ()
trace12 = Emu.runEmulatorTraceIO $ do
h1 <- Emu.activateContractWallet (Wallet 1) endpoints
h2 <- Emu.activateContractWallet (Wallet 2) endpoints
void $ Emu.waitNSlots 5
Emu.callEndpoint @"give" h1 ()
void $ Emu.waitUntilSlot 10
Emu.callEndpoint @"grab" h2 ()
void $ Emu.waitUntilSlot 15
trace13 :: H.IO ()
trace13 = Emu.runEmulatorTraceIO $ do
h1 <- Emu.activateContractWallet (Wallet 1) endpoints
h3 <- Emu.activateContractWallet (Wallet 3) endpoints
void $ Emu.waitNSlots 5
Emu.callEndpoint @"give" h1 ()
void $ Emu.waitUntilSlot 10
Emu.callEndpoint @"grab" h3 ()
void $ Emu.waitUntilSlot 15
plutus commit: plutus-starter-devcontainer/v1.0.7