Skip to content

Commit

Permalink
Merge pull request #2 from wowica/add-ppp-mint-example
Browse files Browse the repository at this point in the history
Added PPP example
  • Loading branch information
caike authored Jul 12, 2024
2 parents f1d9db9 + 5be7f5c commit afbee63
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 1 deletion.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ This is a slightly modified version of the [Gift Card](https://aiken-lang.org/ex

The application runs using Lucid emulator and requires [Deno](https://docs.deno.com/runtime/manual/getting_started/installation) to be installed.

To run the application, run the following command:
To run the application, run the following command:

`deno run -A run_emulation.ts`

The first time it runs, Deno will first download all dependencies. It does not, however, communicate with a real Cardano network nor does it depend on any funds.

## Plutus Pioneer NFT example

An updated example from Plutus Pionner Program's Week09 lesson is included in [/validators/ppp_mint.ak](validators/ppp_mint.ak). Run it with the following command:

`deno run -A run_ppp_emulation.ts`
25 changes: 25 additions & 0 deletions plutus.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,31 @@
],
"compiledCode": "5901320100003232323232323223223222253330083232533300d3010002132533300b3370e6eb4c034009200113371e0020122940dd718058008b180700099299980499b8748008c028dd50008a5eb7bdb1804dd5980718059baa001323300100132330010013756601e602060206020602060186ea8c03cc030dd50019129998070008a5eb7bdb1804c8c8c8c94ccc03ccdc8a45000021533300f3371e91010000210031005133013337606ea4008dd3000998030030019bab3010003375c601c0046024004602000244a66601a002298103d87a8000132323232533300e337220140042a66601c66e3c0280084cdd2a4000660246e980052f5c02980103d87a80001330060060033756601e0066eb8c034008c044008c03c00452613656375c0026eb80055cd2ab9d5573caae7d5d02ba157441",
"hash": "39faa048196bb6b30f50815475e9d16b22e7a0ef6de5935b408ca617"
},
{
"title": "ppp_mint.nft_policy",
"redeemer": {
"title": "_r",
"schema": {
"$ref": "#/definitions/Data"
}
},
"parameters": [
{
"title": "utxo_ref",
"schema": {
"$ref": "#/definitions/aiken~1transaction~1OutputReference"
}
},
{
"title": "token_name",
"schema": {
"$ref": "#/definitions/ByteArray"
}
}
],
"compiledCode": "59018f0100003232323232323222322253330063253330073370e900018041baa001132323232533300e301100213232533300d323300100100622533301200114a0264a66602066ebcc054c048dd5180a8010078a5113300300300130150011533300d3370e9001000899b8f00200a14a02940dd698070011bae300c00116300f00132533300a3370e900118059baa00114bd6f7b63009bab300f300c375400264646600200264660020026eacc044c048c048c048c048014894ccc04000452f5bded8c0264646464a66602266e45220100002153330113371e91010000210031005133015337606ea4008dd3000998030030019bab3012003375c60200046028004602400244a66601e002298103d87a800013232323253330103372200e0042a66602066e3c01c0084cdd2a4000660286e980052f5c02980103d87a8000133006006003375660220066eb8c03c008c04c008c044004dd7180718059baa0033758601a00260126ea8c030c024dd50010b1805980618041baa00114984d958dd7000ab9a5573aaae7955cfaba05742ae881",
"hash": "876ec4f00341ad45919865def59a436aa176e3432957327231ae974e"
}
],
"definitions": {
Expand Down
109 changes: 109 additions & 0 deletions run_ppp_emulation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// $ deno run -A run_ppp_emulation.ts
import {
Address,
applyDoubleCborEncoding,
applyParamsToScript,
Constr,
Data,
Emulator,
fromText,
generatePrivateKey,
Lucid,
OutRef,
PrivateKey,
UTxO,
} from "https://deno.land/x/lucid/mod.ts";

// Imports blueprint generated from `aiken build`
// Blueprints CIP: https://cips.cardano.org/cips/cip57/
import blueprint from "./plutus.json" assert { type: "json" };

// Define wallets, balances and Custom network

const privateKeyAlice = generatePrivateKey();

const addressAlice = await (await Lucid.new(undefined, "Custom"))
.selectWalletFromPrivateKey(privateKeyAlice)
.wallet.address();

const emulator = new Emulator([
{
address: addressAlice,
assets: { lovelace: 100_000_000n },
},
]);

const lucid = await Lucid.new(emulator);

const nft = blueprint.validators.find((v) => v.title === "ppp_mint.nft_policy");

const tokenName = "JUNGLE-GIFT-CARD";

let policyId: string;
let nftMintingPolicy: string;
let assetName: string;

const applyParamsToContract = (outputReference: OutRef) => {
nftMintingPolicy = applyParamsToScript(nft?.compiledCode, [
outputReference,
fromText(tokenName),
]);

policyId = lucid.utils.validatorToScriptHash({
type: "PlutusV2",
script: nftMintingPolicy,
});

assetName = `${policyId}${fromText(tokenName)}`;
};

const mintNFT = async (
minterPrivateKey: PrivateKey,
receiverAddress: Address,
) => {
const owner: Lucid = lucid.selectWalletFromPrivateKey(minterPrivateKey);
const [utxo] = await owner.wallet.getUtxos();

const outRef: OutRef = new Constr(0, [
new Constr(0, [utxo.txHash]),
BigInt(utxo.outputIndex),
]);

applyParamsToContract(outRef);

const mintingPolicy = {
type: "PlutusV2",
script: applyDoubleCborEncoding(nftMintingPolicy),
};

// Change this to > 1n and script will error
const mintAmount = 1n;

const txHash = await owner
.newTx()
.collectFrom([utxo])
// use the nft validator
.attachMintingPolicy(mintingPolicy)
// mint 1 of the asset
.mintAssets({ [assetName]: mintAmount }, Data.void())
.payToAddress(receiverAddress, { [assetName]: mintAmount })
.complete()
.then((tx) => tx.sign().complete())
.then((tx) => tx.submit())
.catch((e) => console.log(e));

console.log(txHash);
};

console.log("Alice's balance before mint");
console.log(await getBalanceAtAddress(addressAlice));

await mintNFT(privateKeyAlice, addressAlice);
emulator.awaitBlock(4);

console.log("Alice's balance after mint");
console.log(await getBalanceAtAddress(addressAlice));

function getBalanceAtAddress(address: Address): Promise<UTxO> {
return lucid.utxosAt(address);
}
26 changes: 26 additions & 0 deletions validators/ppp_mint.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use aiken/dict.{to_pairs}
use aiken/list
use aiken/transaction.{Mint, OutputReference, ScriptContext, Transaction}
use aiken/transaction/value.{from_minted_value, tokens}

// NFT policy (same as week05's one)
validator(utxo_ref: OutputReference, token_name: ByteArray) {
fn nft_policy(_r: Data, ctx: ScriptContext) -> Bool {
// Pattern-match to get policy_id and Fail if it's not a minting transaction
expect Mint(policy_id) = ctx.purpose
// Pattern-match to get the transaction inputs and minted values
let ScriptContext { transaction: Transaction { inputs, mint, .. }, .. } =
ctx
// Pattern-match to get the minted tokens and Fail if it's not a single asset minted
let asset_pairs =
mint |> from_minted_value() |> tokens(policy_id) |> to_pairs()

expect [Pair(asset_name, amount)] = asset_pairs

// Check if the transaction consumes the utxo_ref passed as parameter
let is_ouput_consumed =
list.any(inputs, fn(input) { input.output_reference == utxo_ref })
// Final checks
is_ouput_consumed? && (1 == amount)? && (asset_name == token_name)?
}
}

0 comments on commit afbee63

Please sign in to comment.