Plutus Development - Q&A

Hello everyone.

I am a mobile developer and I am very interested in Cardano, so I am now learning how to develop smart contracts in Plutus. I am currently enrolled in the udemy course but the Q&A section there doesn’t seem active, so I am hoping we can have a more active discussion here.

2 Likes

I will start with my own question:

I am having issues using the Ada datatype. Here is a very basic example that doesn’t work. I noticed that it works fine if I replace the Ada with Value as the parameter for the lock method. Not sure what is happening here.

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE TemplateHaskell     #-}
{-# LANGUAGE DeriveGeneric       #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# OPTIONS_GHC -O0 #-}
module Tutorial.ValidatorScripts where
 
import qualified Language.PlutusTx            as PlutusTx
import qualified Ledger.Interval              as Interval
import           Ledger.Slot                  (SlotRange)
import qualified Ledger.Slot                  as Slot
import qualified Language.PlutusTx.Prelude    as P
import           Ledger                     as L
import qualified Ledger.Ada.TH                as Ada
import           Ledger.Ada                   (Ada)
import           Ledger.Validation           as V
import           Playground.Contract
import           Wallet                       as W
 
 
data TxText  = TxText (P.SizedByteString 32)
PlutusTx.makeLift ''TxText
 
payload :: TxText -> DataScript 
payload text = DataScript $ L.lifted text
 
validator :: ValidatorScript 
validator = ValidatorScript $$(L.compileScript [||
        \(ds :: ()) (rs :: ()) (_ :: PendingTx) -> ()
    ||])
 
address :: Address
address =   L.scriptAddress validator
 
lock :: MonadWallet m => Ada -> m ()
lock value = do 
    _ <- if value <= 0
    then throwOtherError "Must contribute a positive value" 
    else pure ()
   
    let ds = payload $ TxText "Test"
        range = W.defaultSlotRange
        ammount = $$(Ada.toValue) value
        
    payToScript_ range address ammount ds
    logMsg "Submitted contribution"
    
 
$(mkFunctions ['lock])

Working:

lock :: MonadWallet m => Value -> m ()
lock value = do 
   
    let ds = payload $ TxText "Test"
        range = W.defaultSlotRange
        ammount = value
        
    payToScript_ range address ammount ds
    logMsg "Submitted contribution"
1 Like

I also enrolled to the training and honestly… that’s a very bad training… poor content, poor audio, poor video… I am just wondering if they really want to engage people on this… for me it was a very bad training.

2 Likes

Hi @adapt, I tried your example and I get a NegativeValueError with the lock endpoint that uses Ada.

I think that’s a bug - the Playground has a different understanding of what Ada is, so when you set up a simulation that forges for example 10 “Playground-Ada”, and use the lock endpoint to lock 5 “Backend-Ada” then the transaction will fail because the wallet actually has 0 “Backend-Ada” at that time.I’ve created a github issue to track this: https://github.com/input-output-hk/plutus/issues/1005

5 Likes

@Jann_Mueller Perfect, thanks! I was not aware of that difference between Playground-Ada and Backend-Ada. That actually helps me understand the issues I’ve been having.

Thanks for your help! :cardano:

4 Likes

Since a wallet needs to call startWatching to keep track of the outputs for an address, collectFromScript (as far as I understand) will only collect funds from transactions that happened after startWatching.

Is there a way to collect funds from transactions that happened before the startWatching call?

As an example lets think of a smart contract that collects funds from multiple users and pays the total to a random user. This random user should be able to collect all the funds, even if they happened before he entered the game.

Thanks for your help!

This isn’t possible in the current wallet API, to avoid forcing the wallet (which might be running in a resource constrained environment) to either go back through past transactions on the chain, or to keep track of the entire UTXO set.

But this question has come up a few times, and the final word on it hasn’t been spoken yet. It’s not impossible that you will be able to look at earlier transactions through the wallet API at some point.

PS @adapt You can just post any future questions about Plutus in the Plutus forum: https://forum.cardano.org/c/developers/plutus instead of this thread - then they will be easier to find and the discussion can be more focused.

1 Like

Thanks for the quick answer @Jann_Mueller :+1: will do.

Don’t know if you’ve seen it but this person has a YouTube channel on Plutus lessons:

2 Likes

@Donnybaseball Yes I found @PathToPlutus on this forum and I am already subscribed! Great channel.

1 Like

How could you alter the crowdfunding contract example so that the funds could be paid to a different wallet than the campaignOwner?

I tried just creating the contract (run scheduleCollection) as wallet 1 and set wallet 3 to be the campaignOwner however this doesn’t work.

I’ve tried other ways and made some changes below to show my thinking, last two lines of scheduleCollection is incorrect but just trying to show an incorrect solution. Added a second wallet argument to scheduleCollection and added the campaignPayee to the Campaign.

At the end of scheduleCollection what I want to do is take the inputs of the contract and output them to campaignPayee who is not the person setting up the contract

-- added campaignPayee to pay to instead of campaignOwner
data Campaign = Campaign
    { campaignDeadline           :: Slot
    , campaignTarget             :: Value
    , campaignCollectionDeadline :: Slot
    , campaignOwner              :: PubKey
    , campaignPayee              :: PubKey
    } deriving (Generic, ToJSON, FromJSON, ToSchema)

mkCampaign :: Slot -> Value -> Slot -> Wallet -> Wallet -> Campaign
mkCampaign ddl target collectionDdl ownerWallet payeeWallet =
    Campaign
        { campaignDeadline = ddl
        , campaignTarget   = target
        , campaignCollectionDeadline = collectionDdl
        , campaignOwner = EM.walletPubKey ownerWallet
        , campaignPayee = EM.walletPubKey payeeWallet
        }

scheduleCollection :: MonadWallet m => Slot -> Value -> Slot -> Wallet -> Wallet -> m ()
scheduleCollection deadline target collectionDeadline ownerWallet payeeWallet = do
    let cmp = mkCampaign deadline target collectionDeadline ownerWallet payeeWallet
    register (collectFundsTrigger cmp) (EventHandler (\_ -> do
        logMsg "Collecting funds"
        let redeemerScript = Ledger.RedeemerScript (Ledger.lifted Collect)
            range = collectionRange cmp
        
        -- trying to do something like this, i.e. take all of the outputs and pay them to payeeWallet
        utxos <- W.outputsAt (campaignAddress cmp)
        W.payToPublicKey_ W.always utxos (EM.walletPubKey payeeWallet) ))

thanks

To preface - it appears the official example in the playground as well as the accompanying tutorial has changed. Still, since I spent some time obsessing over this it would still be nice if someone could explain the thought process behind this.


In the Plutus book, Chapter 10 (about the vesting scheme).

I can’t wrap my head around why redeemerScript1 is needed at all - no where in the book does it say that on-chain redeemers are applied to anything. Why define a redeemer that is being applied to a data script?