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
$ aikupFrom 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 --versionInstall the Aiken CLI:
$ cargo install aikenCheck your installation
Verify the Aiken CLI installation:
$ aiken -VIf 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 checkRun 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 buildThis 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 cborCreate 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.