| title | M12 - (Non/)Fungible Tokens |
|---|---|
| author | Walker Leite |
In this module we'll learn about Fungible and Non Fungible Tokens and we'll build a minting solution
To run this presentation type (you will need nix):
../../slide README.md- LovelaceAcademy Discord
- StackExchange (:bulb: use the tag lovelace-academy)
- Plutonomicon Discord
We already know Datums and Redeemers:
- Datums are data attached to the transaction that are public in the blockchain (can be inspected) they are normally used to represent "contract state";
- Redeemers are data sent to the validator to decide upon its spending according to the "contract state";
validator :: MyDatum -> MyRedeemer -> ScriptContext -> BoolBut what if you want to inject information in the contract that should not be public in the blockchain?
validator :: ContractParams -> MyDatum -> MyRedeemer -> ScriptContext -> Boolvalidator is a function, you can just add as many arguments you need, if the validator has more than 3 arguments we say it's a parameterized contract.
What happens behind the scene is that the contract hash will be computed as soon as we apply all missing parameters.
-- ...
data CorrectAnswer = CorrectAnswer Integer
unstableMakeIsData ''CorrectAnswer
data Password = Password Integer
{-# INLINEABLE validator #-}
validator :: CorrectAnswer -> BuiltinData -> Password -> BuiltinData -> ()
validator answer _ (Password n) _ | n == answer = ()
validator _ _ _ = _shouldFail
validator' :: Validator
validator' = Validator $ fromCompiledCode $$(compile [|| wrap ||])
where
wrap = validator . unsafeFromBuiltinData-- ... on CTL
import Contract.Scripts (PlutusScript, ApplyArgsError, Validator, applyArgs)
-- ...
data CorrectAnswer = CorrectAnswer BigInt
instance ToData CorrectAnswer where
toData (CorrectAnswer answer) = Constr zero
[ toData answer
]
-- ..
applyError :: ApplyArgsError -> Error
applyError err = error $ "Failed to apply arguments to the script: " <> show err
-- ..
applyScript :: CorrectAnswer -> PlutusScript -> Either Error PlutusScript
applyScript answer ps = applyError `lmap` (applyArgs ps [toData answer])
validator :: CorrectAnswer -> Either Error Validator
validator answer = wrap <$> (parseScript >>= applyScript answer)Up so far we've used only Ada (lovelace) token. What if we need to use our own custom native token?
newtype Value = Value { getValue :: Map.Map CurrencySymbol (Map.Map TokenName Integer) }
newtype CurrencySymbol = CurrencySymbol { unCurrencySymbol :: BuiltinByteString }
newtype TokenName = TokenName { unTokenName :: BuiltinByteString }
newtype AssetClass = AssetClass { unAssetClass :: (CurrencySymbol, TokenName) }The convention is to use the script hash hex that have minted the token as CurrencySymbol. TokenName is free to be used in any way (normally we use it as the token symbol (eg: Ada) or a name + unique id. In the case of Ada, its CurrencySymbol is an empty string, given Ada is the only asset that have been minted in the genesis block (there is no minting script for Ada).
Value is also a monoid, you can append together many different Values to produce different combinations.
A minting policy is just a script that validates the mint transaction. The validator knows if it's a mint transaction through ScriptContext.
data ScriptContext = ScriptContext { scriptContextTxInfo :: TxInfo
, scriptContextPurpose :: ScriptPurpose
}
data ScriptPurpose = Minting CurrencySymbol
| Spending TxOutRef
| Rewarding StakingCredential
| Certifying DCertSo far we've seen fungible native tokens (eg: Ada). We also can have non-fungible native tokens (or unique tokens of a max quantity of 1). It works in the same way as in Values, but the quantity is always 1.
