Skip to content

Instantly share code, notes, and snippets.

@bwbush
Created October 5, 2023 23:48
Show Gist options
  • Save bwbush/c1e8f42f7ed48d4ad82991f5ec40b96f to your computer and use it in GitHub Desktop.
Save bwbush/c1e8f42f7ed48d4ad82991f5ec40b96f to your computer and use it in GitHub Desktop.
Minting Cardano Native Tokens with Marlowe Tools
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "a8b9d3ec-9b61-4c11-8465-504673116f9d",
"metadata": {},
"source": [
"# Minting Tokens for Marlowe Contracts\n",
"\n",
"***Before running this notebook, you might want to use Jupyter's \"clear output\" function to erase the results of the previous execution of this notebook. That will make more apparent what has been executed in the current session.***\n",
"\n",
"This notebook demonstrates how to mint Cardano native tokens using Marlowe's CLI tool and how to use such tokens in a Marlowe contract. (Note that Marlowe contracts themselves will not mint tokens, but they can use tokens minted elsewhere.)\n",
"\n",
"[A video works through this Jupyter notebook.](https://youtu.be/S0MOipqXpmQ)\n",
"\n",
"You can ask questions about Marlowe in [the #ask-marlowe channel on the IOG Discord](https://discord.com/channels/826816523368005654/936295815926927390) or post problems with this lesson to [the issues list for the Marlowe Starter Kit github repository](https://github.com/input-output-hk/marlowe-starter-kit/issues)."
]
},
{
"cell_type": "markdown",
"id": "f128e27a-afe6-4d03-8c46-ef6e5753e4a8",
"metadata": {},
"source": [
"## Preliminaries\n",
"\n",
"See [Preliminaries](../../docs/preliminaries.md) for information on setting up one's environment for using this tutorial.\n",
"\n",
"The first step is to check we have all the required tools and environment variables available to the notebook. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "8b94d1f5",
"metadata": {
"tags": [],
"vscode": {
"languageId": "shellscript"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"########################\n",
"## Check CLI commands ##\n",
"########################\n",
"\n",
"The following required programs are available in the shell:\n",
" * jq\n",
" * json2yaml\n",
" * marlowe-cli\n",
" * marlowe-runtime-cli\n",
" * cardano-cli\n",
" * cardano-address\n",
" * cardano-wallet\n",
"\n",
"#########################\n",
"## Check required envs ##\n",
"#########################\n",
"\n",
"The following environment variables are available in the shell:\n",
" * CARDANO_NODE_SOCKET_PATH = /extra/iohk/networks/mainnet/node.socket\n",
" * MARLOWE_RT_HOST = 192.168.0.12\n",
" * MARLOWE_RT_PORT = 53700\n",
" * MARLOWE_RT_WEBSERVER_HOST = 192.168.0.12\n",
" * MARLOWE_RT_WEBSERVER_PORT = 53780\n",
" * MARLOWE_RT_WEBSERVER_URL = http://192.168.0.12:53780\n",
" * FAUCET_ADDR = addr1qy9prvx8ufwutkwxx9cmmuuajaqmjqwujqlp9d8pvg6gupcvluken35ncjnu0puetf5jvttedkze02d5kf890kquh60sut9jg7\n",
" * FAUCET_SKEY = ../../keys/faucet.skey\n",
"\n",
"###################\n",
"## Check Network ##\n",
"###################\n",
"\n",
"CARDANO_SCAN_URL = https://cardanoscan.io\n",
"MARLOWE_SCAN_URL = https://mainnet.marlowescan.com\n"
]
}
],
"source": [
"export CARDANO_NODE_SOCKET_PATH=/extra/iohk/networks/mainnet/node.socket\n",
"export MARLOWE_RT_HOST=192.168.0.12\n",
"export MARLOWE_RT_PORT=53700\n",
"export MARLOWE_RT_WEBSERVER_HOST=192.168.0.12\n",
"export MARLOWE_RT_WEBSERVER_PORT=53780\n",
"FAUCET_ADDR=$(cat ../../keys/faucet.address)\n",
"FAUCET_SKEY=../../keys/faucet.skey\n",
"\n",
"export SCRIPTS=../../scripts\n",
"export KEYS=../../keys\n",
"source $SCRIPTS/check-tools-and-env.sh"
]
},
{
"cell_type": "markdown",
"id": "833a75c2",
"metadata": {},
"source": [
"Make sure you've also [setup and funded](../../setup/01-setup-keys.ipynb) the different parties\n",
"- Token distributor\n",
" - `$KEYS/lender.address`: Cardano address for the token distributor\n",
" - `$KEYS/lender.skey`: location of signing key file for the token distributor"
]
},
{
"cell_type": "markdown",
"id": "1f209e22-2e7c-444c-a72b-aa88d676e0f8",
"metadata": {
"tags": [],
"vscode": {
"languageId": "shellscript"
}
},
"source": [
"### Token distributor address and funds\n",
"\n",
"Check that an address and key has been created for the token distributor. If not, see \"Creating Addresses and Signing Keys\" in [Setup Keys](../../setup/01-setup-keys.ipynb#Creating-Addresses-and-Signing-Keys)."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "38d0bad1-3b9e-4d80-9aec-a4357c8dc79a",
"metadata": {
"tags": [],
"vscode": {
"languageId": "shellscript"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TOKEN_DISTRIBUTOR_ADDR = addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\n"
]
}
],
"source": [
"TOKEN_DISTRIBUTOR_SKEY=$KEYS/lender.skey\n",
"TOKEN_DISTRIBUTOR_ADDR=$(cat $KEYS/lender.address)\n",
"echo \"TOKEN_DISTRIBUTOR_ADDR = $TOKEN_DISTRIBUTOR_ADDR\""
]
},
{
"cell_type": "markdown",
"id": "d23a2695-2d68-4fa3-80cb-3afe5afe2196",
"metadata": {},
"source": [
"One can view the address on a Cardano explorer. It sometimes takes thirty seconds or so for a transaction to be visible in an explorer."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "b1d78493-a1a1-4739-b911-02b901c73bda",
"metadata": {
"tags": [],
"vscode": {
"languageId": "shellscript"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://cardanoscan.io/address/addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\n"
]
}
],
"source": [
"$SCRIPTS/cardano-scan-address.sh $TOKEN_DISTRIBUTOR_ADDR"
]
},
{
"cell_type": "markdown",
"id": "78033ed1-1127-4935-ad6b-9656a1f8a50e",
"metadata": {
"tags": []
},
"source": [
"## Design the tokens\n",
"\n",
"Now we create [CIP-25](https://cips.cardano.org/cips/cip25/) metadata for the tokens that we will mint. This has metadata key `721` and consists of the following:\n",
"\n",
"- JSON describing the token.\n",
"- Optionally, links to external resources such as an image for the token.\n",
"\n",
"The metadata is stored on the blockchain in the transaction that mints the token, but the images are stored off of the blockchain and just referenced by the on-chain metadata.\n",
"\n",
"We will create a token with asset name `M4B` and associate with it metadata with a description and image."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "20c1db7b-854a-4ff7-aac3-833510bd1f50",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"TOKEN_NAME=M4B\n",
"METADATA_FILE=marlowe-babbage.json\n",
"IMAGE_IPFS=QmZqCCHLqQcHXftNarCwKpRHbzF4mvNeQRVpzk2bdue5bw"
]
},
{
"cell_type": "markdown",
"id": "3a94f714-00fa-484b-b6fc-faea80b361d1",
"metadata": {},
"source": [
"We're using IPFS to store the image, but the image can be hosted at any URI. Many [IPFS pinning services](https://sourceforge.net/software/ipfs-pinning/) are available. Let's fetch the image, even though the image file is note required for the minting process."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "9e0a0dde-466e-4a61-ba18-5956304f44e5",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"IMAGE_URL=https://ipfs.io/ipfs/\"$IMAGE_IPFS\"\n",
"IMAGE_FILE=marlowe-babbage.png\n",
"curl -sS \"$IMAGE_URL\" -o \"$IMAGE_FILE\""
]
},
{
"cell_type": "markdown",
"id": "f22ec76f-ad1d-4881-b53f-52bf96350786",
"metadata": {},
"source": [
"Here is the image itself:\n",
"\n",
"![Marlowe in the Babbage Era](marlowe-babbage.png)"
]
},
{
"cell_type": "markdown",
"id": "37e1c9bd-daee-4c72-b187-46a7b294e4be",
"metadata": {},
"source": [
"Here is the metadata for the token that we will create."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "24690bf4-3a5f-44be-83cf-875ad580e394",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;39m{\n",
" \u001b[0m\u001b[34;1m\"M4B\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n",
" \u001b[0m\u001b[34;1m\"description\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Marlowe smart contracts in the Babbage Era\"\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"image\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"ipfs://QmZqCCHLqQcHXftNarCwKpRHbzF4mvNeQRVpzk2bdue5bw\"\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"mediaType\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"image/gif\"\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"name\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Marlowe in the Babbage Era\"\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"url\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://marlowe.iohk.io/\"\u001b[0m\u001b[1;39m\n",
" \u001b[1;39m}\u001b[0m\u001b[1;39m\n",
"\u001b[1;39m}\u001b[0m\n"
]
}
],
"source": [
"yaml2json << EOI > \"$METADATA_FILE\"\n",
"$TOKEN_NAME:\n",
" description: Marlowe smart contracts in the Babbage Era\n",
" image: ipfs://$IMAGE_IPFS\n",
" mediaType: image/gif\n",
" name: Marlowe in the Babbage Era\n",
" url: https://marlowe.iohk.io/\n",
"EOI\n",
"jq . marlowe-babbage.json"
]
},
{
"cell_type": "markdown",
"id": "7767b836-f4c5-45d6-ad5e-201583dc6486",
"metadata": {},
"source": [
"The above only contains metadata for a single token, but the JSON can have entries for many tokens.\n",
"\n",
"The `M4B` key is the asset name for the token on the blockchain. The nested fields describe that token.\n",
"\n",
"- `name` is the human-friendly name for the token.\n",
"- `image` is a URI referencing the location of the image for the token.\n",
"- `mediaType` is the MIME type for the image.\n",
"- `description` is text describing the token.\n",
"- `url` is a URL to find more information related to the token.\n",
"\n",
"The [CIP25 standard](https://cips.cardano.org/cips/cip25/#generalstructure) provides information on general structure of the metadata."
]
},
{
"cell_type": "markdown",
"id": "58a513a2-7c34-4f4d-b32b-e48356090a2a",
"metadata": {},
"source": [
"## Mint the tokens\n",
"\n",
"The `marlowe-cli` tool supports minting tokens according to the CIP25 standard."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "0e4e6324-ae63-4e18-84b7-0978a27668d7",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Usage: marlowe-cli util mint (--mainnet | --testnet-magic INTEGER) \n",
" [--socket-path SOCKET_FILE]\n",
" --issuer ADDRESS:SIGNING_FILE \n",
" [(--token-provider ADDRESS:SIGNING_FILE)] \n",
" [--metadata-file JSON_FILE] [--count INTEGER] \n",
" [--expires SLOT_NO] --out-file FILE \n",
" [--submit SECONDS] TOKEN_NAME:ADDRESS\n",
"\n",
" Mint native tokens.\n",
"\n",
"Available options:\n",
" --mainnet Execute on mainnet.\n",
" --testnet-magic INTEGER Network magic. Defaults to the CARDANO_TESTNET_MAGIC\n",
" environment variable's value.\n",
" --socket-path SOCKET_FILE\n",
" Location of the cardano-node socket file. Defaults to\n",
" the CARDANO_NODE_SOCKET_PATH environment variable's\n",
" value.\n",
" --issuer ADDRESS:SIGNING_FILE\n",
" Issuer wallet info\n",
" --token-provider ADDRESS:SIGNING_FILE\n",
" Additional tokens owners info.\n",
" --metadata-file JSON_FILE\n",
" The CIP-25 metadata, with keys for each token name.\n",
" --count INTEGER The number of each token to mint.\n",
" --expires SLOT_NO The slot number after which minting is no longer\n",
" possible.\n",
" --out-file FILE Output file for transaction body.\n",
" --submit SECONDS Also submit the transaction, and wait for\n",
" confirmation.\n",
" TOKEN_NAME:ADDRESS The name of the token.\n",
" -h,--help Show this help text\n"
]
}
],
"source": [
"marlowe-cli util mint --help"
]
},
{
"cell_type": "markdown",
"id": "f13f4932-9422-43d0-81d5-d9e34353d44b",
"metadata": {},
"source": [
"The `marlowe-cli util mint` command uses a Cardano Simple Script V2 minting policy. If the `--expires` option is specified, then the minting policy is \"locked\" after a specified slot, so that no more tokens with that policy can ever be minted at or after that slot number; if not, then there is no time limit on minting new tokens or burning old ones."
]
},
{
"cell_type": "markdown",
"id": "6659ce7b-8d06-4522-b50d-cf4196deadae",
"metadata": {},
"source": [
"First find the slot number for the tip of the blockchain."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "b9c903d1-94a1-4e80-a758-db88c3397443",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The tip is at slot number 104965842.\n"
]
}
],
"source": [
"TIP=$(cardano-cli query tip --mainnet | jq -r .slot)\n",
"echo \"The tip is at slot number $TIP.\""
]
},
{
"cell_type": "markdown",
"id": "9c4298e4-d754-4330-8407-49173899ce20",
"metadata": {},
"source": [
"Let's set the expiration slot to five hours into the future. That gives us some time to burn and recreate the tokens if we've made typographical or other errors in the metadata."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "ab4b3d83-0f4a-4f9f-a6a7-bfddf00399ea",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"EXPIRES = 105037842\n"
]
}
],
"source": [
"SECONDS=1\n",
"MINUTES=$((60 * SECONDS))\n",
"HOURS=$((60 * MINUTES))\n",
"EXPIRES=$((TIP + 5 * HOURS))\n",
"echo \"EXPIRES = $EXPIRES\""
]
},
{
"cell_type": "markdown",
"id": "a05f52d5-d779-435d-9477-f86144f85f30",
"metadata": {},
"source": [
"We'll mint six tokens."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "16bc1649-f66e-454b-86d3-d35b1f95b4bf",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"TOKEN_COUNT=6"
]
},
{
"cell_type": "markdown",
"id": "21f58136-cc3a-4bd2-bd4b-d6ca35df4baa",
"metadata": {},
"source": [
"We provide the token distributor's key information, the metadata file, the count of tokens, the expiration slot, and the destinations. In return, the tool prints the policy ID for the minting script."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "b685429c-b294-47a6-9f87-6891e1022cf1",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TOKEN_POLICY = e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\n"
]
}
],
"source": [
"TOKEN_POLICY=$(\n",
"marlowe-cli util mint \\\n",
" --mainnet \\\n",
" --issuer \"$TOKEN_DISTRIBUTOR_ADDR:$TOKEN_DISTRIBUTOR_SKEY\" \\\n",
" --metadata-file \"$METADATA_FILE\" \\\n",
" --count \"$TOKEN_COUNT\" \\\n",
" --expires \"$EXPIRES\" \\\n",
" --out-file /dev/null \\\n",
" --submit 600s \\\n",
" \"$TOKEN_NAME:$TOKEN_DISTRIBUTOR_ADDR\" \\\n",
" 2> /dev/null \\\n",
")\n",
"echo \"TOKEN_POLICY = $TOKEN_POLICY\""
]
},
{
"cell_type": "markdown",
"id": "99a58746-2009-45b4-b1c4-d1f38a31bbe7",
"metadata": {},
"source": [
"We can view the minted tokens on an explorer:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "6d9912f1-43c4-431a-8ad5-abeb0abb7b03",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://pool.pm/policy/e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\n"
]
}
],
"source": [
"echo \"https://pool.pm/policy/$TOKEN_POLICY\""
]
},
{
"cell_type": "markdown",
"id": "e15ac188-f1d5-44c5-80c6-8bcb90cbe3a6",
"metadata": {},
"source": [
"## Optional: Burning tokens\n",
"\n",
"Marlowe CLI also provides a command for un-minting (i.e., \"burning\") previously minted tokens."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "5458ac5d-7b0f-4260-a40e-59c0e4ca1bdb",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Usage: marlowe-cli util burn (--mainnet | --testnet-magic INTEGER) \n",
" [--socket-path SOCKET_FILE]\n",
" --issuer ADDRESS:SIGNING_FILE \n",
" [(--token-provider ADDRESS:SIGNING_FILE)] \n",
" [--metadata-file JSON_FILE] --out-file FILE \n",
" [--expires SLOT_NO] [--submit SECONDS]\n",
"\n",
" Burn native tokens.\n",
"\n",
"Available options:\n",
" --mainnet Execute on mainnet.\n",
" --testnet-magic INTEGER Network magic. Defaults to the CARDANO_TESTNET_MAGIC\n",
" environment variable's value.\n",
" --socket-path SOCKET_FILE\n",
" Location of the cardano-node socket file. Defaults to\n",
" the CARDANO_NODE_SOCKET_PATH environment variable's\n",
" value.\n",
" --issuer ADDRESS:SIGNING_FILE\n",
" Issuer wallet info\n",
" --token-provider ADDRESS:SIGNING_FILE\n",
" Additional tokens owners info.\n",
" --metadata-file JSON_FILE\n",
" The CIP-25 metadata, with keys for each token name.\n",
" --out-file FILE Output file for transaction body.\n",
" --expires SLOT_NO The slot number after which miniting is no longer\n",
" possible.\n",
" --submit SECONDS Also submit the transaction, and wait for\n",
" confirmation.\n",
" -h,--help Show this help text\n"
]
}
],
"source": [
"marlowe-cli util burn --help"
]
},
{
"cell_type": "markdown",
"id": "3375d5fa-353d-47f1-a559-bf0a30439251",
"metadata": {
"tags": []
},
"source": [
"## Optional: Details of the minting script\n",
"\n",
"The Marlowe CLI uses a Simple Script V2 as its minting policy. Let's manually derive that policy and see that it has the same policy ID as the tool reported.\n",
"\n",
"The JSON for the policy script can be used with `cardano-cli`, uploaded to explorers like CardanoScan, etc.\n",
"\n",
"First, we need the public-key hash (PKH) of the signing key used to mint the tokens. In our case this is the token"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "f0ba87db-a6bb-44fc-bdaf-5debd09ec307",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TOKEN_DISTRIBUTOR_PKH = 6992a73cadec1a81cc403b003d53ebe828e511888520712a69f59d00\n"
]
}
],
"source": [
"TOKEN_DISTRIBUTOR_PKH=$(\n",
"cardano-cli key verification-key --signing-key-file \"$TOKEN_DISTRIBUTOR_SKEY\" --verification-key-file /dev/stdout \\\n",
"| cardano-cli address key-hash --verification-key-file /dev/stdin \\\n",
")\n",
"echo \"TOKEN_DISTRIBUTOR_PKH = $TOKEN_DISTRIBUTOR_PKH\""
]
},
{
"cell_type": "markdown",
"id": "985d4921-3f64-44aa-a69c-505cbd30b996",
"metadata": {},
"source": [
"### Minting policy with an expiration slot\n",
"\n",
"For a minting policy with an expiration slot, Marlowe CLI generates a monetary policy of the following form:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "77a9a473-9d74-464f-8587-0c2b84d0425c",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;39m{\n",
" \u001b[0m\u001b[34;1m\"scripts\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n",
" \u001b[1;39m{\n",
" \u001b[0m\u001b[34;1m\"keyHash\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"6992a73cadec1a81cc403b003d53ebe828e511888520712a69f59d00\"\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"sig\"\u001b[0m\u001b[1;39m\n",
" \u001b[1;39m}\u001b[0m\u001b[1;39m,\n",
" \u001b[1;39m{\n",
" \u001b[0m\u001b[34;1m\"slot\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39m105037842\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"before\"\u001b[0m\u001b[1;39m\n",
" \u001b[1;39m}\u001b[0m\u001b[1;39m\n",
" \u001b[1;39m]\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"all\"\u001b[0m\u001b[1;39m\n",
"\u001b[1;39m}\u001b[0m\n"
]
}
],
"source": [
"yaml2json << EOI > policy-$EXPIRES.json\n",
"type: all\n",
"scripts:\n",
"- type: sig\n",
" keyHash: $TOKEN_DISTRIBUTOR_PKH\n",
"- type: before\n",
" slot: $EXPIRES\n",
"EOI\n",
"jq . policy-$EXPIRES.json"
]
},
{
"cell_type": "markdown",
"id": "2fe1cb9a-bbcc-4b6e-a1ca-a010494f19ee",
"metadata": {},
"source": [
"We can verify that this policy corresponds to the policy ID that we used to mint the tokens."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "3a3b371d-dda1-4912-99e9-6f8dbfcde951",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\n"
]
}
],
"source": [
"cardano-cli transaction policyid --script-file \"policy-$EXPIRES.json\""
]
},
{
"cell_type": "markdown",
"id": "9fb81560-84d7-41e2-b6e4-14d04b6926de",
"metadata": {},
"source": [
"### Minting policy without an expiration slot\n",
"\n",
"The minting policy generated by Marlowe CLI is simplier if there is not expiration slot."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "f52d5f60-3973-4301-926d-3cd65c85ef8f",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;39m{\n",
" \u001b[0m\u001b[34;1m\"keyHash\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"6992a73cadec1a81cc403b003d53ebe828e511888520712a69f59d00\"\u001b[0m\u001b[1;39m,\n",
" \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"sig\"\u001b[0m\u001b[1;39m\n",
"\u001b[1;39m}\u001b[0m\n"
]
}
],
"source": [
"yaml2json << EOI > policy-noexpires.json\n",
"type: sig\n",
"keyHash: $TOKEN_DISTRIBUTOR_PKH\n",
"EOI\n",
"jq . policy-noexpires.json"
]
},
{
"cell_type": "markdown",
"id": "5e1a32fb-fa0b-43b7-bcae-a710c3f35b86",
"metadata": {},
"source": [
"Once again, we can verify the policy ID of such."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "1fd3b992-1c6f-4d6e-8b1c-f1fccbf884b9",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"816517c6580cd2358687bdae570e169c871fa511fbb0fdc428400f11\n"
]
}
],
"source": [
"cardano-cli transaction policyid --script-file policy-noexpires.json"
]
},
{
"cell_type": "markdown",
"id": "484a9c4a-7ce1-49a2-9550-6d882b9b9e41",
"metadata": {},
"source": [
"## Example Marlowe contract: a small airdrop to Ada Handle holders\n",
"\n",
"The tokens that we've minted can be used in ordinary transactions or in Marlowe contracts. Here we'll use them in a small airdrop to six holders of [Ada Handle](https://mint.handle.me/) tokens.\n",
"\n",
"This example illustrates how a well-known monetary policy like that for [Ada Handles](https://mint.handle.me/) or [Ada Domains](https://www.adadomains.io/) can be used as the roles currency symbol for a Marlowe contract. This can be convenient because Marlowe's payout validator provides *an on-chain guarantee* that funds will be delivered to the holder of the role token: that is, no off-chain services are needed to locate the address of the handle's or domain's holder.\n",
"\n",
"The below contract accepts a deposit of the six tokens we just created and then distribute one each to the holders of six Ada Handles:\n",
"- `$e.cary`\n",
"- `$f.beaumont`\n",
"- `$j.lumley`\n",
"- `$j.webster`\n",
"- `$m.herbert`\n",
"- `$w.shakespeare`\n",
"\n",
"![Small airdrop contract in Marlowe](contract.png)\n",
"\n",
"Note the presence of the two `Notify` cases in the contract. These are necessary to break up the payments into three transactions. Attempting to perform them all in the same transaction as the deposit would result in Plutus execution costs being exceeded."
]
},
{
"cell_type": "markdown",
"id": "451dee28-dada-4919-99e2-0c8e7d94f423",
"metadata": {},
"source": [
"### Design the contract\n",
"\n",
"First, set the deadline to deposit the tokens twenty minutes into the future, expressed in the POSIX milliseconds."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "4ddf7055-c26c-40ef-827b-c1369e43b182",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"DEPOSIT_DEADLINE = 1696533999000 POSIX milliseconds\n"
]
}
],
"source": [
"DEPOSIT_DEADLINE=$((1000 * $(date -u -d \"$(date) + 20 minutes\" +%s)))\n",
"echo \"DEPOSIT_DEADLINE = $DEPOSIT_DEADLINE POSIX milliseconds\""
]
},
{
"cell_type": "markdown",
"id": "5c3d9b73-3735-43eb-ac04-3e70efce856e",
"metadata": {},
"source": [
"Write the contract to a file. (We could have just created the contract in [Marlowe Playground](https://play.marlowe.iohk.io/) and then downloaded the JSON.) *Note that the role names do not contain the `$` that prefixes an Ada Handle; similarly, they would not include the `.ada` suffix of an Ada Domain.*"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "dfea90ee-62a5-4c97-99dd-b9256a464f9c",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{\"timeout\":1696533999000,\"timeout_continuation\":\"close\",\"when\":[{\"case\":{\"deposits\":6,\"into_account\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"},\"of_token\":{\"currency_symbol\":\"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\",\"token_name\":\"M4B\"},\"party\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"}},\"then\":{\"from_account\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"},\"pay\":1,\"then\":{\"from_account\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"},\"pay\":1,\"then\":{\"timeout\":1696533999000,\"timeout_continuation\":\"close\",\"when\":[{\"case\":{\"notify_if\":true},\"then\":{\"from_account\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"},\"pay\":1,\"then\":{\"from_account\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"},\"pay\":1,\"then\":{\"timeout\":1696533999000,\"timeout_continuation\":\"close\",\"when\":[{\"case\":{\"notify_if\":true},\"then\":{\"from_account\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"},\"pay\":1,\"then\":{\"from_account\":{\"address\":\"addr1q95e9feu4hkp4qwvgqasq02na05z3eg33zzjquf2d86e6qzznwng4gtlladnxm7d486psa003jy6dv230t82rvv3pflqeuzzt2\"},\"pay\":1,\"then\":\"close\",\"to\":{\"party\":{\"role_token\":\"w.shakespeare\"}},\"token\":{\"currency_symbol\":\"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\",\"token_name\":\"M4B\"}},\"to\":{\"party\":{\"role_token\":\"m.herbert\"}},\"token\":{\"currency_symbol\":\"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\",\"token_name\":\"M4B\"}}}]},\"to\":{\"party\":{\"role_token\":\"j.webster\"}},\"token\":{\"currency_symbol\":\"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\",\"token_name\":\"M4B\"}},\"to\":{\"party\":{\"role_token\":\"j.lumley\"}},\"token\":{\"currency_symbol\":\"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\",\"token_name\":\"M4B\"}}}]},\"to\":{\"party\":{\"role_token\":\"f.beaumont\"}},\"token\":{\"currency_symbol\":\"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\",\"token_name\":\"M4B\"}},\"to\":{\"party\":{\"role_token\":\"e.cary\"}},\"token\":{\"currency_symbol\":\"e127582f4a9f49744f61051cfe91cf505d85a731a376ff4331fbf144\",\"token_name\":\"M4B\"}}}]}\n"
]
}
],
"source": [
"yaml2json << EOI > contract.json\n",
"when:\n",
"- case:\n",
" deposits: 6\n",
" into_account:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" of_token:\n",
" currency_symbol: $TOKEN_POLICY\n",
" token_name: $TOKEN_NAME\n",
" party:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" then:\n",
" from_account:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" pay: 1\n",
" token:\n",
" currency_symbol: $TOKEN_POLICY\n",
" token_name: $TOKEN_NAME\n",
" to:\n",
" party:\n",
" role_token: e.cary\n",
" then:\n",
" from_account:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" pay: 1\n",
" token:\n",
" currency_symbol: $TOKEN_POLICY\n",
" token_name: $TOKEN_NAME\n",
" to:\n",
" party:\n",
" role_token: f.beaumont\n",
" then:\n",
" when:\n",
" - case:\n",
" notify_if: true\n",
" then:\n",
" from_account:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" pay: 1\n",
" token:\n",
" currency_symbol: $TOKEN_POLICY\n",
" token_name: $TOKEN_NAME\n",
" to:\n",
" party:\n",
" role_token: j.lumley\n",
" then:\n",
" from_account:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" pay: 1\n",
" token:\n",
" currency_symbol: $TOKEN_POLICY\n",
" token_name: $TOKEN_NAME\n",
" to:\n",
" party:\n",
" role_token: j.webster\n",
" then:\n",
" when:\n",
" - case:\n",
" notify_if: true\n",
" then:\n",
" from_account:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" token:\n",
" currency_symbol: $TOKEN_POLICY\n",
" token_name: $TOKEN_NAME\n",
" to:\n",
" party:\n",
" role_token: m.herbert\n",
" pay: 1\n",
" then:\n",
" from_account:\n",
" address: $TOKEN_DISTRIBUTOR_ADDR\n",
" token:\n",
" currency_symbol: $TOKEN_POLICY\n",
" token_name: $TOKEN_NAME\n",
" pay: 1\n",
" to:\n",
" party:\n",
" role_token: w.shakespeare\n",
" then: close\n",
" timeout: $DEPOSIT_DEADLINE\n",
" timeout_continuation: close\n",
" timeout: $DEPOSIT_DEADLINE\n",
" timeout_continuation: close\n",
"timeout: $DEPOSIT_DEADLINE\n",
"timeout_continuation: close\n",
"EOI\n",
"cat contract.json"
]
},
{
"cell_type": "markdown",
"id": "404aa363-89cd-4dd1-9021-141909da6e8a",
"metadata": {},
"source": [
"### Transaction 1. Create the contract on the blockchain\n",
"\n",
"We'll be sending the tokens to holders of [ADA Handles](https://mint.handle.me/), so we need to use the Ada Handles policy ID as the Marlowe roles currency symbol."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "21338a1d-01d1-40f7-844d-67df449fd095",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"ADA_HANDLES_POLICY=f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a"
]
},
{
"cell_type": "markdown",
"id": "5c87123a-1b7d-45d8-a1a9-125fb898145f",
"metadata": {},
"source": [
"Now have Marlowe Runtime build the transaction to create the Marlowe contract. See [Lesson 1](../01-runtime-cli) for a more detailed exposition."
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "e6ffeca5-4676-49d8-aba2-1a2bb15d9dda",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Safety analysis found no errors in the contract.\n",
"CONTRACT_ID = 6532b1d45856c1daadf999beb1636f15aeb25a1abdfb0e3a00213ab2ba3e3fa0#1\n"
]
}
],
"source": [
"CONTRACT_ID=$(\n",
"marlowe-runtime-cli create \\\n",
" --core-file contract.json \\\n",
" --role-token-policy-id \"$ADA_HANDLES_POLICY\" \\\n",
" --min-utxo \"$((2 * 1000000))\" \\\n",
" --change-address \"$TOKEN_DISTRIBUTOR_ADDR\" \\\n",
" --manual-sign tx-1.unsigned \\\n",
"| jq -r 'fromjson | .contractId' \\\n",
")\n",
"echo \"CONTRACT_ID = $CONTRACT_ID\""
]
},
{
"cell_type": "markdown",
"id": "230537e2-fea7-43a5-9bb3-87b89eea8926",
"metadata": {},
"source": [
"*Always check that the contract has no safety errors before submitting the transaction that creates it.* See [Lesson 7](../07-safety) for a detailed discussion of the safety checks that Marlowe Runtime does and why they are important.\n",
"\n",
"Since the contract is safe, we sign and submit the transaction."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "ec1d9ce8-61ef-4d49-903e-91be1af4ad01",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TxId \"6532b1d45856c1daadf999beb1636f15aeb25a1abdfb0e3a00213ab2ba3e3fa0\"\n"
]
}
],
"source": [
"marlowe-cli transaction submit \\\n",
" --mainnet \\\n",
" --required-signer \"$TOKEN_DISTRIBUTOR_SKEY\" \\\n",
" --tx-body-file tx-1.unsigned \\\n",
" --timeout 600s"
]
},
{
"cell_type": "markdown",
"id": "4d0129b6-e097-4142-830f-d57b2e6458d2",
"metadata": {},
"source": [
"After the transaction is confirmed, we can view the contract on MarloweScan:"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "f9ef36fa-d40b-4741-94a7-9405c41c557b",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://mainnet.marlowescan.com/contractView?tab=info&contractId=6532b1d45856c1daadf999beb1636f15aeb25a1abdfb0e3a00213ab2ba3e3fa0%231\n"
]
}
],
"source": [
"echo \"$MARLOWE_SCAN_URL/contractView?tab=info&contractId=${CONTRACT_ID/\\#/%23}\""
]
},
{
"cell_type": "markdown",
"id": "76d514be-0897-40e0-81b5-f52296af46c7",
"metadata": {},
"source": [
"### Transaction 2. Deposit the tokens and make the first two airdrops\n",
"\n",
"First build the transaction that will deposit the tokens in the contract and make the payments to `$e.cary` and `$f.beaumont`."
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "0617e819-2a7f-4c6c-9b81-cb3809a545c8",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TX_2 = 7275f02ba1245ec89a12b2221406b6d5d38b3a7bfe74b5536bb242cc6ac944a0\n"
]
}
],
"source": [
"TX_2=$(\n",
"marlowe-runtime-cli deposit \\\n",
" --contract \"$CONTRACT_ID\" \\\n",
" --from-party \"$TOKEN_DISTRIBUTOR_ADDR\" \\\n",
" --to-party \"$TOKEN_DISTRIBUTOR_ADDR\" \\\n",
" --currency \"$TOKEN_POLICY\" \\\n",
" --token-name \"$TOKEN_NAME\" \\\n",
" --quantity \"$TOKEN_COUNT\" \\\n",
" --change-address \"$TOKEN_DISTRIBUTOR_ADDR\" \\\n",
" --manual-sign tx-2.unsigned \\\n",
"| jq -r 'fromjson | .txId' \\\n",
")\n",
"echo \"TX_2 = $TX_2\""
]
},
{
"cell_type": "markdown",
"id": "d135bbac-bbb1-441c-b7ce-b5d242f18e78",
"metadata": {},
"source": [
"Now sign and submit it."
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "bddfde1c-1d2f-4de8-8ad7-a39819c4957b",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TxId \"7275f02ba1245ec89a12b2221406b6d5d38b3a7bfe74b5536bb242cc6ac944a0\"\n"
]
}
],
"source": [
"marlowe-cli transaction submit \\\n",
" --mainnet \\\n",
" --required-signer \"$TOKEN_DISTRIBUTOR_SKEY\" \\\n",
" --tx-body-file tx-2.unsigned \\\n",
" --timeout 600s"
]
},
{
"cell_type": "markdown",
"id": "d10120d2-ea02-4c2a-816d-07c39a96f99b",
"metadata": {
"tags": []
},
"source": [
"After the transaction is confirmed, we can view it on MarloweScan."
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "c35692a1-bebc-4d79-b1ea-24eb42dd8bcf",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://mainnet.marlowescan.com/contractView?tab=tx&contractId=6532b1d45856c1daadf999beb1636f15aeb25a1abdfb0e3a00213ab2ba3e3fa0%231&transactionId=7275f02ba1245ec89a12b2221406b6d5d38b3a7bfe74b5536bb242cc6ac944a0\n"
]
}
],
"source": [
"echo \"$MARLOWE_SCAN_URL/contractView?tab=tx&contractId=${CONTRACT_ID/\\#/%23}&transactionId=$TX_2\""
]
},
{
"cell_type": "markdown",
"id": "4fb27dd1-3b9c-4c03-a8c2-b2cf68e3b82d",
"metadata": {},
"source": [
"### Transaction 3. Notify the contract to make the next two airdrops\n",
"\n",
"Now build the transaction that will make the payments to `$j.lumley` and `$j.webster`."
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "a40ad6e1-fca4-454a-955e-d8be569553d2",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TX_3 = eeab084afa3abc3ab8167784122cf2f0e536e656cad767bf95d98d13b6c2af08\n"
]
}
],
"source": [
"TX_3=$(\n",
"marlowe-runtime-cli notify \\\n",
" --contract \"$CONTRACT_ID\" \\\n",
" --change-address \"$TOKEN_DISTRIBUTOR_ADDR\" \\\n",
" --manual-sign tx-3.unsigned \\\n",
"| jq -r 'fromjson | .txId' \\\n",
")\n",
"echo \"TX_3 = $TX_3\""
]
},
{
"cell_type": "markdown",
"id": "0f670b9c-905d-49d8-bcc8-2e3b6c6c218c",
"metadata": {},
"source": [
"Now sign and submit it."
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "1433ddc5-b354-4cb0-89af-1393b64ed65f",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TxId \"eeab084afa3abc3ab8167784122cf2f0e536e656cad767bf95d98d13b6c2af08\"\n"
]
}
],
"source": [
"marlowe-cli transaction submit \\\n",
" --mainnet \\\n",
" --required-signer \"$TOKEN_DISTRIBUTOR_SKEY\" \\\n",
" --tx-body-file tx-3.unsigned \\\n",
" --timeout 600s"
]
},
{
"cell_type": "markdown",
"id": "e1d907e6-aaae-4aaa-8042-0802c5a77ceb",
"metadata": {
"tags": []
},
"source": [
"After the transaction is confirmed, we can view it on MarloweScan."
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "49ba2b42-de18-4e35-a379-a3e8f9864f75",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://mainnet.marlowescan.com/contractView?tab=tx&contractId=6532b1d45856c1daadf999beb1636f15aeb25a1abdfb0e3a00213ab2ba3e3fa0%231&transactionId=eeab084afa3abc3ab8167784122cf2f0e536e656cad767bf95d98d13b6c2af08\n"
]
}
],
"source": [
"echo \"$MARLOWE_SCAN_URL/contractView?tab=tx&contractId=${CONTRACT_ID/\\#/%23}&transactionId=$TX_3\""
]
},
{
"cell_type": "markdown",
"id": "b11068d6-36db-436a-97b9-c46f04c51244",
"metadata": {},
"source": [
"### Transaction 4. Notify the contract to make the last two airdrops\n",
"\n",
"Now build the transaction that will make the payments to `$m.herbert` and `$w.shakespeare`."
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "93dd9d6f-9586-4e56-b732-4d2dea1e2c20",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TX_4 = ea070fbc7b2744bd13204dbb927e41507157d4e7bcc2d77cb3166fd7791cf42b\n"
]
}
],
"source": [
"TX_4=$(\n",
"marlowe-runtime-cli notify \\\n",
" --contract \"$CONTRACT_ID\" \\\n",
" --change-address \"$TOKEN_DISTRIBUTOR_ADDR\" \\\n",
" --manual-sign tx-4.unsigned \\\n",
"| jq -r 'fromjson | .txId' \\\n",
")\n",
"echo \"TX_4 = $TX_4\""
]
},
{
"cell_type": "markdown",
"id": "c7744f3a-3d6d-4082-9665-90e70ce76e4c",
"metadata": {},
"source": [
"Now sign and submit it."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "99a9dd28-5f40-4cbc-a089-db70f44f354b",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TxId \"ea070fbc7b2744bd13204dbb927e41507157d4e7bcc2d77cb3166fd7791cf42b\"\n"
]
}
],
"source": [
"marlowe-cli transaction submit \\\n",
" --mainnet \\\n",
" --required-signer \"$TOKEN_DISTRIBUTOR_SKEY\" \\\n",
" --tx-body-file tx-4.unsigned \\\n",
" --timeout 600s"
]
},
{
"cell_type": "markdown",
"id": "02e8ead2-d01c-4f69-afa7-d81190746152",
"metadata": {
"tags": []
},
"source": [
"After the transaction is confirmed, we can view it on MarloweScan."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "281b5401-a41d-42f9-b133-76f1035892eb",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"https://mainnet.marlowescan.com/contractView?tab=tx&contractId=6532b1d45856c1daadf999beb1636f15aeb25a1abdfb0e3a00213ab2ba3e3fa0%231&transactionId=ea070fbc7b2744bd13204dbb927e41507157d4e7bcc2d77cb3166fd7791cf42b\n"
]
}
],
"source": [
"echo \"$MARLOWE_SCAN_URL/contractView?tab=tx&contractId=${CONTRACT_ID/\\#/%23}&transactionId=$TX_4\""
]
},
{
"cell_type": "markdown",
"id": "be18f7f8-50f6-4534-b78c-54e651e02645",
"metadata": {},
"source": [
"### Recipients withdraw the tokens from the Marlowe role-payout address\n",
"\n",
"The six payments are waiting at Marlowe's role-payout address for the holders of each Ada Handle to withdraw them. Currently there are several methods to withdraw such payments:\n",
"\n",
"1. Connect one's CIP30 wallet to the Marlowe Payouts dapp, which will list all of the payouts held for the benefit the the wallet's owner.\n",
"2. Use the withdraw endpoint of the Marlowe Runtime REST API.\n",
"3. Use the withdraw command of the Marlowe Runtime CLI.\n",
"4. Use the withdraw command of the Marlowe CLI.\n",
"5. Craft a withdrawal transaction using Cardano CLI."
]
},
{
"cell_type": "markdown",
"id": "4f15d5d6-bd03-482d-b4c1-47d6969338f3",
"metadata": {
"tags": []
},
"source": [
"## Optional: Use cases involving native tokens and Marlowe contracts\n",
"\n",
"Although Marlowe Runtime does not yet provide support for minting native tokens in conjunction with a Marlowe contract, this can be accomplished using `cardano-cli transaction build`.\n",
"\n",
"1. Tokens can be minted in the same transaction that creates the Marlowe contract by running a Simple or Plutus minting script in that transaction.\n",
"2. Tokens can be minted in the same transaction as a Marlowe `Deposit`by running a Simple or Plutus minting script in that transaction.\n",
"\n",
"Note that running a Simple script in the same transaction as Marlowe is inexpensive, but less powerful than running a Plutus script. The Plutus script can enforce stronger assurances about the transaction, such as ensuring that the minting *only* can take place at a particular stage of the Marlowe contract."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Bash with Marlowe Tools",
"language": "bash",
"name": "bash-minimal"
},
"language_info": {
"codemirror_mode": "shell",
"file_extension": ".sh",
"mimetype": "text/x-sh",
"name": "bash"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment