user1983: 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

user4880: 
So why must I also invoke the grab endpoint for Wallet 2 (trace12)? What
changes if instead it's invoked for Wallet 3 (trace13)?

You must invoke it in order to receive funds that are locked in the contract (via give endpoint).
If you invoke grab endpoint on Wallet3, you would no receive anything from the script, because wallet3 is not beneficiary of this vesting contract. You can see that in the logs you posted for trace12 where wallet2 has more ada than in the beginning (102996448) and for trace13 where wallet3 has default initial amount of ADA 100000000, but there are funds locked on Vesting script

Script 3f3c25dfdfad56d6f08d6d3e8bec53ab14d1950be944bc0ed26957c6:
{, ""}: 3000000

which can only be unlocked/claimed (grab-ed) by wallet2 who is beneficiary of contract (defined as param thePkh in give endpoint)

Why is that? And what does it mean to invoke an endpoint for a wallet altogether?

I've explained why above. And for what does it mean to invoke endpoint for a wallet. It just means that you call that contract's offchain endpoint, which creates transaction for that contract/script with specific params and constraints, signed with that wallet's private key.

1983: <p>As per entire code below, <code>give</code> <strong>always</strong> mandates that <code>Wallet 2</code> be given:</p>
<pre><code>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 &lt;- submitTxConstraints (typedValidator param) tx
</code></pre>
<p>So why must I <strong>also</strong> invoke the <code>grab</code> endpoint for <code>Wallet 2</code> (<code>trace12</code>)? What changes if instead it's invoked for <code>Wallet 3</code> (<code>trace13</code>)?</p>
<pre><code>trace12 #######################################
...
Final balances
Wallet 1: 
    {, &quot;&quot;}: 96999990
Wallet 2: 
    {, &quot;&quot;}: 102996448
Wallet 3: 
    {, &quot;&quot;}: 100000000
Wallet 4: 
    {, &quot;&quot;}: 100000000
Wallet 5: 
    {, &quot;&quot;}: 100000000
Wallet 6: 
    {, &quot;&quot;}: 100000000
Wallet 7: 
    {, &quot;&quot;}: 100000000
Wallet 8: 
    {, &quot;&quot;}: 100000000
Wallet 9: 
    {, &quot;&quot;}: 100000000
Wallet 10: 
    {, &quot;&quot;}: 100000000
</code></pre>
<p>Note that if <code>Wallet 3</code> instead is invoked for <code>grab</code>, then the result is different:</p>
<pre><code>trace13 #######################################
...
Final balances
Wallet 1: 
    {, &quot;&quot;}: 96999990
Wallet 2: 
    {, &quot;&quot;}: 100000000
Wallet 3: 
    {, &quot;&quot;}: 100000000
Wallet 4: 
    {, &quot;&quot;}: 100000000
Wallet 5: 
    {, &quot;&quot;}: 100000000
Wallet 6: 
    {, &quot;&quot;}: 100000000
Wallet 7: 
    {, &quot;&quot;}: 100000000
Wallet 8: 
    {, &quot;&quot;}: 100000000
Wallet 9: 
    {, &quot;&quot;}: 100000000
Wallet 10: 
    {, &quot;&quot;}: 100000000
Script 3f3c25dfdfad56d6f08d6d3e8bec53ab14d1950be944bc0ed26957c6: 
    {, &quot;&quot;}: 3000000
</code></pre>
<p>Why is that? And what does it mean to invoke an endpoint for a wallet altogether?</p>
<pre><code>{-# 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 ( (&lt;&gt;), 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 -&gt; () -&gt; () -&gt; ScriptContext -&gt; Bool
mkValidator param () () ctx = 
    traceIfFalse &quot;beneficiary's signature missing&quot; 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 -&gt; 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 -&gt; Validator
validator = Scripts.validatorScript . typedValidator


{-# INLINABLE valHash #-}
valHash :: PayParam -&gt; Ledger.ValidatorHash
valHash = Scripts.validatorHash . typedValidator


{-# INLINABLE scrAddress #-}
scrAddress :: PayParam -&gt; 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 &lt;- submitTxConstraints (typedValidator param) tx

    -- also works fine
    -- let tx = mustPayToOtherScript (valHash param) unitDatum theVal
    -- ledgerTx &lt;- submitTx tx

    void $ awaitTxConfirmed $ txId ledgerTx
    logInfo @H.String &quot;\ngive&quot;


grab :: Contract () PaySchema Text () 
grab = do      
    let param = PayParam thePkh theVal

    utxos &lt;- utxoAt $ scrAddress param
    let lookups = Constraints.unspentOutputs utxos &lt;&gt;
                  Constraints.otherScript (validator param) &lt;&gt;
                  Constraints.typedValidatorLookups (typedValidator param)
   
    let orefs = fst &lt;$&gt; Map.toList utxos

    let tx :: TxConstraints () ()
        tx = PlutusTx.Prelude.mconcat [mustSpendScriptOutput oref unitRedeemer | oref &lt;- orefs]    

    ledgerTx &lt;- submitTxConstraintsWith @Lock lookups tx
    awaitTxConfirmed $ txId ledgerTx     
    logInfo @H.String &quot;\ngrab&quot;      


type PaySchema =
    Endpoint &quot;give&quot; ()
        .\/ 
    Endpoint &quot;grab&quot; ()


endpoints :: Contract () PaySchema Text ()
endpoints = 
    forever
        $ handleError logError
        $ awaitPromise
        $ give' `select` grab' 
            where
                give' = endpoint @&quot;give&quot; $ const give 
                grab' = endpoint @&quot;grab&quot; $ const grab


trace12 :: H.IO ()
trace12 = Emu.runEmulatorTraceIO $ do
    h1 &lt;- Emu.activateContractWallet (Wallet 1) endpoints
    h2 &lt;- Emu.activateContractWallet (Wallet 2) endpoints
    void $ Emu.waitNSlots 5
    Emu.callEndpoint @&quot;give&quot; h1 ()
    void $ Emu.waitUntilSlot 10    
    Emu.callEndpoint @&quot;grab&quot; h2 ()
    void $ Emu.waitUntilSlot 15


trace13 :: H.IO ()
trace13 = Emu.runEmulatorTraceIO $ do
    h1 &lt;- Emu.activateContractWallet (Wallet 1) endpoints
    h3 &lt;- Emu.activateContractWallet (Wallet 3) endpoints
    void $ Emu.waitNSlots 5
    Emu.callEndpoint @&quot;give&quot; h1 ()
    void $ Emu.waitUntilSlot 10    
    Emu.callEndpoint @&quot;grab&quot; h3 ()
    void $ Emu.waitUntilSlot 15
</code></pre>
<p><strong>plutus commit: <em>plutus-starter-devcontainer/v1.0.7</em></strong></p>
 4880: <blockquote>
<p>So why must I also invoke the grab endpoint for Wallet 2 (trace12)? What
changes if instead it's invoked for Wallet 3 (trace13)?</p>
</blockquote>
<p>You must invoke it in order to receive funds that are locked in the contract (via give endpoint).
If you invoke grab endpoint on Wallet3, you would no receive anything from the script, because wallet3 is not beneficiary of this vesting contract. You can see that in the logs you posted for trace12 where wallet2 has more ada than in the beginning (102996448) and for trace13 where wallet3 has default initial amount of ADA 100000000, but there are funds locked on Vesting script</p>
<blockquote>
<p>Script 3f3c25dfdfad56d6f08d6d3e8bec53ab14d1950be944bc0ed26957c6:
{, &quot;&quot;}: 3000000</p>
</blockquote>
<p>which can only be unlocked/claimed (grab-ed) by wallet2 who is beneficiary of contract (defined as param thePkh in give endpoint)</p>
<blockquote>
<p>Why is that? And what does it mean to invoke an endpoint for a wallet altogether?</p>
</blockquote>
<p>I've explained why above. And for what does it mean to invoke endpoint for a wallet. It just means that you call that contract's offchain endpoint, which creates transaction for that contract/script with specific params and constraints, signed with that wallet's private key.</p>
