Mesh LogoMesh

Aiken Hello World

Aiken is a functional programming language for Cardano smart contract development. It prioritizes on-chain execution and offers a user-friendly approach for building secure and efficient smart contracts.

This tutorial walks you through writing a smart contract in Aiken and creating two transactions to lock and unlock assets on the Cardano blockchain.

Try the live demo. View the code on the GitHub repository.

System setup

Set up your system to compile Aiken smart contracts. Skip this section if you have already set up your system or do not wish to compile the contract.

Check the installation instructions on the Aiken website for more information.

Using aikup (on Linux & MacOS only)

Linux and MacOS users can use the utility tool to download and manage Aiken's pre-compiled executables.

Install the Aiken CLI:

$ curl -sSfL https://install.aiken-lang.org | bash
$ aikup

From sources (all platforms)

Aiken is written in Rust. Install Rust and Cargo to compile the smart contract. Install Rust via the Rust website.

Install Cargo, the Rust package manager, via the Cargo website.

Verify installation:

$ rustc --version
$ cargo --version

Install the Aiken CLI:

$ cargo install aiken

Check your installation

Verify the Aiken CLI installation:

$ aiken -V

If issues arise, check the Aiken website.

Writing a smart contract with Aiken

Write a smart contract in Aiken and create two transactions to lock and unlock assets.

Read more about this example on the Aiken website.

Create a new project

Create a new project. Refer to this guide for creating a new Next.js project.

Create a new Aiken project within this project folder:

$ aiken meshjs/hello_world
$ cd hello_world
$ aiken check

Run aiken check to verify your project.

Write the smart contract

Create validators/hello_world.ak:

use aiken/hash.{Blake2b_224, Hash}
use aiken/list
use aiken/transaction.{ScriptContext}
use aiken/transaction/credential.{VerificationKey}

type Datum {
  owner: Hash<Blake2b_224, VerificationKey>,
}

type Redeemer {
  msg: ByteArray,
}

validator {
  fn hello_world(datum: Datum, redeemer: Redeemer, context: ScriptContext) -> Bool {
    let must_say_hello =
      redeemer.msg == "Hello, World!"

    let must_be_signed =
      list.has(context.transaction.extra_signatories, datum.owner)

    must_say_hello && must_be_signed
  }
}

This validator checks that the redeemer message is "Hello, World!" and that the transaction is signed by the datum owner. Returns true if both conditions are met; otherwise false.

Compile the smart contract:

$ aiken build

This generates plutus.json in the root folder. This file is a CIP-0057 Plutus blueprint, describing your on-chain contract and its binary interface.

Creating locking transaction

Preparing the frontend

Prepare the frontend to allow users to lock and unlock assets.

Install cbor:

$ npm install cbor

Create a data folder and copy plutus.json into it.

Open pages/index.tsx and import the packages:

import {
  resolvePlutusScriptAddress,
  Transaction,
  KoiosProvider,
  resolveDataHash,
  resolvePaymentKeyHash,
} from "@meshsdk/core";
import type { PlutusScript, Data } from "@meshsdk/core";
import { CardanoWallet, useWallet } from "@meshsdk/react";

import plutusScript from "../data/plutus.json";
import cbor from "cbor";

Importing the contract

Import the contract:

const script: PlutusScript = {
  code: cbor
    .encode(Buffer.from(plutusScript.validators[0].compiledCode, "hex"))
    .toString("hex"),
  version: "V2",
};
const scriptAddress = resolvePlutusScriptAddress(script, 0);

Use plutus.json to create the script and resolvePlutusScriptAddress to resolve the address.

We encode the compiled validator code with cbor because the validator uses a flat format, unlike the format expected by cardano-cli and the serialization library.

Locking assets

Create the transaction to lock assets:

const hash = resolvePaymentKeyHash((await wallet.getUsedAddresses())[0]);
const datum: Data = {
  alternative: 0,
  fields: [hash],
};

const tx = new Transaction({ initiator: wallet }).sendLovelace(
  {
    address: scriptAddress,
    datum: { value: datum },
  },
  "5000000"
);

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

This transaction locks assets. resolvePaymentKeyHash resolves the wallet's key hash. sendLovelace sends lovelace to the script address.

The contract requires the owner's address in the datum. We build, sign, and submit the transaction.

Unlocking assets

Create the transaction to unlock assets.

Retrieve the UTXO of the locked assets:

async function _getAssetUtxo({ scriptAddress, asset, datum }) {
  const utxos = await koios.fetchAddressUTxOs(scriptAddress, asset);

  const dataHash = resolveDataHash(datum);

  let utxo = utxos.find((utxo: any) => {
    return utxo.output.dataHash == dataHash;
  });

  return utxo;
}

Create the unlock transaction:

const scriptAddress = resolvePlutusScriptAddress(script, 0);

const address = (await wallet.getUsedAddresses())[0];
const hash = resolvePaymentKeyHash(address);
const datum: Data = {
  alternative: 0,
  fields: [hash],
};

const assetUtxo = await _getAssetUtxo({
  scriptAddress: scriptAddress,
  asset: "lovelace",
  datum: datum,
});

const redeemer = { data: { alternative: 0, fields: ['Hello, World!'] } };

// create the unlock asset transaction
const tx = new Transaction({ initiator: wallet })
  .redeemValue({
    value: assetUtxo,
    script: script,
    datum: datum,
    redeemer: redeemer,
  })
  .sendValue(address, assetUtxo)
  .setRequiredSigners([address]);

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);

This transaction unlocks assets. resolvePlutusScriptAddress resolves the script address. resolvePaymentKeyHash resolves the wallet's key hash.

_getAssetUtxo retrieves the locked asset UTXO. redeemValue redeems the assets. sendValue sends assets to the owner. setRequiredSigners sets the required signers.

The validator requires "Hello, World!" as the redeemer message. We build, sign, and submit the transaction.

Check the full code on GitHub.