Skip to content

Commit 629023e

Browse files
authored
Merge pull request #77 from mlabs-haskell/t4/metadata-2
Add metadata support
2 parents 9ca5939 + 36b8fdc commit 629023e

File tree

14 files changed

+170
-40
lines changed

14 files changed

+170
-40
lines changed

bot-plutus-interface.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ library
9797
, cardano-api
9898
, cardano-crypto
9999
, cardano-ledger-alonzo
100+
, cardano-prelude
100101
, cardano-slotting
101102
, containers
102103
, data-default
@@ -136,6 +137,7 @@ library
136137
, text ^>=1.2.4.0
137138
, transformers
138139
, transformers-either
140+
, transformers-except
139141
, unordered-containers
140142
, uuid
141143
, wai

examples/plutus-game/app/Main.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ main = do
6969
, pcLogLevel = Debug
7070
, pcProtocolParamsFile = "./protocol.json"
7171
, pcEnableTxEndpoint = True
72+
, pcMetadataDir = "./metadata"
7273
, pcCollectStats = False
7374
}
7475
BotPlutusInterface.runPAB @GameContracts pabConf

examples/plutus-nft/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
scripts
22
signing-keys
33
txs
4+
metadata

examples/plutus-nft/app/Main.hs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,12 @@ import BotPlutusInterface.Types (
1313
endpointsToSchemas,
1414
)
1515
import Cardano.Api (NetworkId (Testnet), NetworkMagic (..))
16-
import Cardano.PlutusExample.NFT (
17-
NFTSchema,
18-
mintNft,
19-
)
16+
import Cardano.PlutusExample.NFT
2017
import Data.Aeson qualified as JSON
2118
import Data.Aeson.TH (defaultOptions, deriveJSON)
2219
import Data.ByteString.Lazy qualified as LazyByteString
2320
import Data.Default (def)
2421
import Data.Maybe (fromMaybe)
25-
import Ledger.Value (TokenName)
2622
import Playground.Types (FunctionSchema)
2723
import Schema (FormSchema)
2824
import Servant.Client.Core (BaseUrl (BaseUrl), Scheme (Http))
@@ -37,11 +33,11 @@ instance HasDefinitions MintNFTContracts where
3733

3834
getContract :: (MintNFTContracts -> SomeBuiltin)
3935
getContract = \case
40-
MintNFT tokenName ->
36+
MintNFT p ->
4137
SomeBuiltin $
42-
mintNft tokenName
38+
mintNft p
4339

44-
newtype MintNFTContracts = MintNFT TokenName
40+
newtype MintNFTContracts = MintNFT MintParams
4541
deriving stock (Show)
4642

4743
$(deriveJSON defaultOptions ''MintNFTContracts)
@@ -65,10 +61,11 @@ main = do
6561
, pcScriptFileDir = "./scripts"
6662
, pcSigningKeyFileDir = "./signing-keys"
6763
, pcTxFileDir = "./txs"
68-
, pcDryRun = True
64+
, pcDryRun = False
6965
, pcLogLevel = Debug
7066
, pcProtocolParamsFile = "./protocol.json"
7167
, pcEnableTxEndpoint = True
68+
, pcMetadataDir = "./metadata"
7269
, pcCollectStats = False
7370
}
7471
BotPlutusInterface.runPAB @MintNFTContracts pabConf

examples/plutus-nft/plutus-nft.cabal

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,12 @@ library
7676
build-depends:
7777
, aeson ^>=1.5.0.0
7878
, attoparsec >=0.13.2.2
79+
, base16-bytestring
7980
, bytestring ^>=0.10.12.0
8081
, cardano-api
8182
, cardano-crypto
8283
, cardano-ledger-alonzo
84+
, cardano-prelude
8385
, containers
8486
, data-default
8587
, data-default-class
@@ -129,11 +131,13 @@ executable plutus-nft-pab
129131
, bot-plutus-interface
130132
, bytestring
131133
, cardano-api
134+
, containers
132135
, data-default
133136
, playground-common
134137
, plutus-ledger
135138
, plutus-nft
136139
, servant-client-core
140+
, text
137141

138142
main-is: Main.hs
139143
hs-source-dirs: app

examples/plutus-nft/src/Cardano/PlutusExample/NFT.hs

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,55 @@
1+
{-# LANGUAGE RecordWildCards #-}
12
{-# LANGUAGE TemplateHaskell #-}
23

34
module Cardano.PlutusExample.NFT where
45

56
import Cardano.Api.Shelley (PlutusScript (..), PlutusScriptV1)
67
import Codec.Serialise (serialise)
7-
import Control.Monad hiding (fmap)
8+
import Control.Monad (void)
9+
import Data.Aeson.TH (defaultOptions, deriveJSON)
810
import Data.ByteString.Lazy qualified as LBS
911
import Data.ByteString.Short qualified as SBS
1012
import Data.Map qualified as Map
1113
import Data.Monoid (Last (Last))
1214
import Data.Text (Text)
1315
import Data.Text qualified as Text
1416
import Data.Void (Void)
15-
import Ledger hiding (singleton)
17+
import Ledger (
18+
CurrencySymbol,
19+
PaymentPubKeyHash,
20+
Script,
21+
ScriptContext (scriptContextTxInfo),
22+
TokenName,
23+
TxInInfo (txInInfoOutRef),
24+
TxInfo (txInfoInputs, txInfoMint),
25+
TxOutRef,
26+
mkMintingPolicyScript,
27+
ownCurrencySymbol,
28+
pubKeyHashAddress,
29+
scriptCurrencySymbol,
30+
)
31+
import Ledger.Address (StakePubKeyHash)
1632
import Ledger.Constraints as Constraints
33+
import Ledger.Constraints.Metadata (
34+
NftMetadata (NftMetadata),
35+
NftMetadataToken (NftMetadataToken),
36+
TxMetadata (TxMetadata),
37+
nmtDescription,
38+
nmtFiles,
39+
nmtImage,
40+
nmtMediaType,
41+
nmtName,
42+
nmtOtherFields,
43+
)
1744
import Ledger.Typed.Scripts (wrapMintingPolicy)
18-
import Ledger.Value as Value
45+
import Ledger.Value (flattenValue, singleton)
1946
import Plutus.Contract (Contract, Endpoint, submitTxConstraintsWith, tell, utxosAt)
2047
import Plutus.Contract qualified as Contract
2148
import Plutus.V1.Ledger.Scripts qualified as Scripts
2249
import PlutusTx qualified
23-
import PlutusTx.Prelude hiding (Semigroup (..), unless)
50+
import PlutusTx.Prelude
2451
import Text.Printf (printf)
25-
import Prelude (Semigroup (..), String, show)
52+
import Prelude qualified as Hask
2653

2754
{-# INLINEABLE mkPolicy #-}
2855
mkPolicy :: TxOutRef -> TokenName -> BuiltinData -> ScriptContext -> Bool
@@ -63,18 +90,52 @@ curSymbol oref tn = scriptCurrencySymbol $ policy oref tn
6390
type NFTSchema =
6491
Endpoint "mint" TokenName
6592

66-
mintNft :: TokenName -> Contract (Last Text) NFTSchema Text ()
67-
mintNft tn = do
93+
data MintParams = MintParams
94+
{ mpName :: Text
95+
, mpDescription :: Maybe Text
96+
, mpImage :: Text
97+
, mpTokenName :: TokenName
98+
, mpPubKeyHash :: PaymentPubKeyHash
99+
, mpStakeHash :: StakePubKeyHash
100+
}
101+
deriving stock (Hask.Show)
102+
103+
$(deriveJSON defaultOptions ''MintParams)
104+
105+
mintNft :: MintParams -> Contract (Last Text) NFTSchema Text ()
106+
mintNft MintParams {..} = do
68107
pkh <- Contract.ownPaymentPubKeyHash
69108
utxos <- utxosAt (pubKeyHashAddress pkh Nothing)
70-
tell $ Last $ Just "Contract started with "
71109
case Map.keys utxos of
72-
[] -> Contract.logError @String "no utxo found"
110+
[] -> Contract.logError @Hask.String "no utxo found"
73111
oref : _ -> do
74-
tell $ Last $ Just $ "Using oref:" <> Text.pack (show oref)
75-
let val = Value.singleton (curSymbol oref tn) tn 1
76-
lookups = Constraints.mintingPolicy (policy oref tn) <> Constraints.unspentOutputs utxos
77-
tx = Constraints.mustMintValue val <> Constraints.mustSpendPubKeyOutput oref
112+
tell $ Last $ Just $ "Using oref:" Hask.<> Text.pack (Hask.show oref)
113+
let cs = curSymbol oref mpTokenName
114+
val = singleton cs mpTokenName 1
115+
meta =
116+
NftMetadata $
117+
Map.singleton cs $
118+
Map.singleton mpTokenName $
119+
NftMetadataToken
120+
{ nmtName = mpName
121+
, nmtImage = mpImage
122+
, nmtMediaType = Hask.pure "image/png"
123+
, nmtDescription = mpDescription
124+
, nmtFiles = Hask.mempty
125+
, nmtOtherFields = Hask.mempty
126+
}
127+
lookups =
128+
Hask.mconcat
129+
[ Constraints.mintingPolicy (policy oref mpTokenName)
130+
, Constraints.unspentOutputs utxos
131+
]
132+
tx =
133+
Hask.mconcat
134+
[ Constraints.mustMintValue val
135+
, Constraints.mustSpendPubKeyOutput oref
136+
, Constraints.mustPayToPubKeyAddress mpPubKeyHash mpStakeHash val
137+
, Constraints.mustIncludeMetadata $ TxMetadata (Just meta) Hask.mempty
138+
]
78139
void $ submitTxConstraintsWith @Void lookups tx
79-
Contract.logInfo @String $ printf "forged %s" (show val)
140+
Contract.logInfo @Hask.String $ printf "forged %s" (Hask.show val)
80141
tell $ Last $ Just "Finished"

examples/plutus-transfer/app/Main.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ main = do
6868
, pcLogLevel = Debug
6969
, pcProtocolParamsFile = "./protocol.json"
7070
, pcEnableTxEndpoint = True
71+
, pcMetadataDir = "./metadata"
7172
, pcCollectStats = False
7273
}
7374
BotPlutusInterface.runPAB @TransferContracts pabConf

flake.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,17 @@
8181
"github:input-output-hk/ouroboros-network/d2d219a86cda42787325bb8c20539a75c2667132";
8282
flake = false;
8383
};
84+
# Patched plutus for metadata support. We need this until `plutus-apps` will update `plutus`,
85+
# rewrite of `plutus-ledger-constraints`, and possibly some bpi adjustments afterwards.
86+
# tldr: Dependency hell
8487
plutus = {
8588
url =
86-
"github:input-output-hk/plutus/cc72a56eafb02333c96f662581b57504f8f8992f";
89+
"github:mlabs-haskell/plutus/1a3c3a761cf048371c52a34b004f8b3fcf0dab43";
8790
flake = false;
8891
};
8992
plutus-apps = {
9093
url =
91-
"github:input-output-hk/plutus-apps/7f543e21d4945a2024e46c572303b9c1684a5832";
94+
"github:mlabs-haskell/plutus-apps/82c0725c4d05398ae76d71927cc60aa23db1a11d";
9295
flake = false;
9396
};
9497
purescript-bridge = {

src/BotPlutusInterface/CardanoCLI.hs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import BotPlutusInterface.Effects (PABEffect, ShellArgs (..), callCommand)
1818
import BotPlutusInterface.Files (
1919
DummyPrivKey (FromSKey, FromVKey),
2020
datumJsonFilePath,
21+
metadataFilePath,
2122
policyScriptFilePath,
2223
redeemerJsonFilePath,
2324
signingKeyFilePath,
@@ -93,7 +94,7 @@ import Plutus.V1.Ledger.Api (
9394
ExMemory (..),
9495
TokenName (..),
9596
)
96-
import PlutusTx.Builtins (fromBuiltin)
97+
import PlutusTx.Builtins (BuiltinByteString, fromBuiltin)
9798
import Prelude
9899

99100
-- | Getting information of the latest block
@@ -215,6 +216,7 @@ buildTx pabConf privKeys txBudget tx = do
215216
, txOutOpts pabConf (txData tx) (txOutputs tx)
216217
, mints
217218
, validRangeOpts (txValidRange tx)
219+
, metadataOpts pabConf (txMetadata tx)
218220
, requiredSigners
219221
, ["--fee", showText . getLovelace . fromValue $ txFee tx]
220222
, mconcat
@@ -422,3 +424,8 @@ showText = Text.pack . show
422424
-- toWalletKey :: Wallet -> Text
423425
-- toWalletKey =
424426
-- decodeUtf8 . convertToBase Base16 . hash @ByteString @Blake2b_160 . unXPub . walletXPub
427+
428+
metadataOpts :: PABConfig -> Maybe BuiltinByteString -> [Text]
429+
metadataOpts _ Nothing = mempty
430+
metadataOpts pabConf (Just meta) =
431+
["--metadata-json-file", metadataFilePath pabConf meta]

0 commit comments

Comments
 (0)