All of the pieces have been built and now it is time for us to put them all together to create a fully working smart contract.
Our ValidatorScript has two inputs we are interested in. submittedPIN
and myPIN
.
1 2 3 | myFirstValidator :: ValidatorScript myFirstValidator = ValidatorScript (fromCompiledCode $$(PlutusTx.compile [|| \(submittedPIN :: Int) (myPIN :: Int) (p :: PendingTx') -> () ||])) |
The submittedPIN
is the RedeemerScript which is sent with our withdrawADA
call, while myPIN
is the DataScript that is attached to the ada sent with our depositADA
function. What we need to do now is to create a check so that if the submittedPIN
is equal to myPIN
then the smart contract successfully executes. Otherwise, we need to throw an error that the two are not equal, thus stopping the withdrawADA
call from succeeding.
1 2 3 4 5 6 7 | myFirstValidator :: ValidatorScript myFirstValidator = ValidatorScript (fromCompiledCode $$(PlutusTx.compile [|| \(submittedPIN :: Int) (myPIN :: Int) (p :: PendingTx') -> if ... then ... else ... ||])) |
We first begin with creating an if then else
. Our expression after the if
needs to evaluate to a Bool
, thus this is where we will do the equality check between the two PIN numbers.
1 2 3 4 5 6 7 | myFirstValidator :: ValidatorScript myFirstValidator = ValidatorScript (fromCompiledCode $$(PlutusTx.compile [|| \(submittedPIN :: Int) (myPIN :: Int) (p :: PendingTx') -> if submittedPIN == myPIN then ... else ... ||])) |
If the two PIN numbers are equal, then we wish for the smart contract to finish executing successfully. We return unit, ()
, to represent this.
1 2 3 4 5 6 7 | myFirstValidator :: ValidatorScript myFirstValidator = ValidatorScript (fromCompiledCode $$(PlutusTx.compile [|| \(submittedPIN :: Int) (myPIN :: Int) (p :: PendingTx') -> if submittedPIN == myPIN then () else ... ||])) |
Otherwise if the two PIN numbers are not equal we will throw an error. However before we proceed to write the error code, we need but one more import.
1 | import qualified Language.PlutusTx.Prelude as P |
This is the standard Prelude/standard library for Plutus which holds many of the basic functions you will need when writing smart contracts. In our case we need it to throw the error if our equality check evaluates to false. (the else
case)
1 2 3 4 5 6 7 | myFirstValidator :: ValidatorScript myFirstValidator = ValidatorScript (fromCompiledCode $$(PlutusTx.compile [|| \(submittedPIN :: Int) (myPIN :: Int) (p :: PendingTx') -> if submittedPIN == myPIN then () else $$(P.error) () ||])) |
When using Plutus functions from the Prelude, or when using any other external Plutus functions that are not defined in your ValidatorScript, you will need to use the splicing operator $$()
. As our error function is P.error
, we will need to call it using $$(P.error)
. It takes a single input, and in our case we simply provide it with unit to throw a default error.
However, before we finish up, let’s add an error message to make this more user-friendly.
1 2 3 4 5 6 7 | myFirstValidator :: ValidatorScript myFirstValidator = ValidatorScript (fromCompiledCode $$(PlutusTx.compile [|| \(submittedPIN :: Int) (myPIN :: Int) (p :: PendingTx') -> if submittedPIN == myPIN then () else $$(P.error) ($$(P.traceH) "Please supply the correct PIN number to withdraw ada." ()) ||])) |
Instead of feeding our error function ()
, we provide it with the result of another function, P.traceH
. P.traceH
allows us to supply an error message which tags along with the error to be reported in the log.
Now whenever a withdrawADA
call is used with the incorrect PIN number, an error similar to the following will be displayed.
Validation failed for transaction: nxhdGHgYPxhwGOcYdxiIGOQYlhi3CBhlGOQYphizGGAYRhg0GGoY4BjtChg8GB0YvBgYGMIY0RjLGNEYfxh8/w==
Ledger.Index.ScriptFailure ["Please supply the correct PIN number to withdraw ada."]
And with that, you have finished writing your very first smart contract! Congratulations!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | module MyFirstPlutusSmartContract where import qualified Language.PlutusTx as PlutusTx import qualified Language.PlutusTx.Prelude as P import Ledger import Wallet import Ledger.Validation import Playground.Contract myFirstValidator :: ValidatorScript myFirstValidator = ValidatorScript (fromCompiledCode $$(PlutusTx.compile [|| \(submittedPIN :: Int) (myPIN :: Int) (p :: PendingTx') -> if submittedPIN == myPIN then () else $$(P.error) ($$(P.traceH) "Please supply the correct PIN number to withdraw ada." ()) ||])) smartContractAddress :: Address' smartContractAddress = scriptAddress myFirstValidator watchSmartContract :: MockWallet () watchSmartContract = startWatching smartContractAddress depositADA :: Int -> Value -> MockWallet () depositADA pin val = payToScript_ smartContractAddress val (DataScript (lifted pin)) withdrawADA :: Int -> MockWallet () withdrawADA pin = collectFromScript myFirstValidator (RedeemerScript (lifted pin)) $(mkFunction 'watchSmartContract) $(mkFunction 'depositADA) $(mkFunction 'withdrawADA) |
Type this code into the Plutus Playground editor, compile, and experiment with the smart contract that you have just finished making. If you aren’t the experimenting type of person, here are the Action Sequences to display how the contract works.
Success:
watchSmartContract
.watchSmartContract
.depositADA
with PIN number 1234 and deposits 5 ada.withdrawADA
with PIN number 1234.Failure:
watchSmartContract
.watchSmartContract
.depositADA
with PIN number 3421 and deposits 5 ada.withdrawADA
with PIN number 1234.