All skills

Guide for writing TypeScript code with AlgoKit Utils (`@algorandfoundation/algokit-utils`). Use this skill whenever the user is building on Algorand with TypeScript — client setup, account management, payments, asset operations, atomic transaction groups, smart contract deployment and interaction (AppFactory, AppClient, ARC-56/ARC-32 specs), raw app calls, key registration, network management, testing with algorandFixture, error handling, and the low-level crypto primitives under `@algorandfoundation/algokit-utils/crypto` (Ed25519 keygen/signing/verification, SHA-512/256 `hash`, Peikert xHD BIP44 wallets, wrapped-secret patterns). Trigger on imports from `@algorandfoundation/algokit-utils` (incl. `/crypto`, `/testing`, `/transact` subpaths), references to `AlgorandClient`, `AppFactory`, `AppClient`, `AlgoAmount`, `algorandFixture`, `ed25519Generator`, `peikertXHdWalletGenerator`, `hash`, `WrappedEd25519Seed`, or `RawEd25519Signer`. Also on any TypeScript or JavaScript code that builds on Algorand.

Use this Skill: https://skilld.dev/gh/algorand-devrel/algorand-agent-skills/algokit-utils-ts

This session only. Nothing lands on disk.

referencesed25519.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Ed25519 keys, signing, and verification

All snippets below import from @algorandfoundation/algokit-utils/crypto, the subpath that re-exports @algorandfoundation/algokit-crypto. This is the low-level cryptographic layer under AccountManager — reach for it when you are wiring up a custom signer, implementing protocol helpers, or verifying signatures produced by algokit-utils.

Generate a random Ed25519 keypair

Create a fresh Ed25519 keypair along with a raw signer function bound to it.

import { ed25519Generator } from "@algorandfoundation/algokit-utils/crypto";

const { ed25519Pubkey, ed25519SecretKey, rawEd25519Signer } = ed25519Generator();

console.log(ed25519Pubkey.length); // 32
console.log(ed25519SecretKey.length); // 32

What just happened: ed25519Generator() drew a cryptographically random 32-byte secret key, derived its 32-byte public key, and returned both alongside a RawEd25519Signer closure that already captures the secret key. Any call to rawEd25519Signer(bytes) will produce an Ed25519 signature over those bytes. The default export is currently an alias for nobleEd25519Generator (backed by @noble/ed25519) — import nobleEd25519Generator directly if you want to pin the backend explicitly.

Generate a deterministic Ed25519 keypair from a seed

Pass a 32-byte seed to produce the same keypair every time.

import { ed25519Generator } from "@algorandfoundation/algokit-utils/crypto";

const seed = new Uint8Array(32);
seed.set([1, 2, 3, 4, 5]);

const { ed25519Pubkey, ed25519SecretKey } = ed25519Generator(seed);

What just happened: When you supply a seed, @noble/ed25519 uses it directly as the Ed25519 secret key — no extra hashing — so the resulting keypair is fully deterministic in the seed. This is useful for tests and for reproducing keys from a known source. Do not reuse a seed across unrelated identities.

Sign bytes with the generated signer

Call the returned rawEd25519Signer to produce a signature over arbitrary bytes.

import { ed25519Generator } from "@algorandfoundation/algokit-utils/crypto";

const { rawEd25519Signer } = ed25519Generator();

const message = new TextEncoder().encode("Hello, Algorand!");
const signature = await rawEd25519Signer(message);

console.log(signature.length); // 64

What just happened: rawEd25519Signer implements the RawEd25519Signer contract — an async function taking raw bytes and returning a 64-byte Ed25519 signature. It signs exactly the bytes you pass in, with no added domain separator. When signing Algorand transactions, logic sigs, or program data, let AccountManager or generateAddressWithSigners handle the Algorand prefix (TX, Program, ProgData, MX) for you — the raw signer is deliberately agnostic to domain separation.

Build a custom RawEd25519Signer

Implement the RawEd25519Signer type to plug any external signing source into AlgoKit.

import type { RawEd25519Signer } from "@algorandfoundation/algokit-utils/crypto";

const customSigner: RawEd25519Signer = async (bytesToSign) => {
  // Forward the bytes to a hardware wallet, KMS, HSM, or browser extension
  const response = await fetch("https://my-signer.example.com/sign", {
    method: "POST",
    body: bytesToSign,
  });
  return new Uint8Array(await response.arrayBuffer());
};

What just happened: RawEd25519Signer is simply (bytesToSign: Uint8Array) => Promise<Uint8Array>. Anything that satisfies that shape is accepted throughout the AlgoKit stack — AccountManager.setSigner and generateAddressWithSigners combine it with a public key (via Ed25519SigningKey) to build full Algorand signers. The signer is expected to produce a raw 64-byte Ed25519 signature over the bytes it receives; any domain separation must be applied by the caller before invocation.

Construct an Ed25519SigningKey manually

Assemble an Ed25519SigningKey from an existing public key and signer.

import type {
  Ed25519SigningKey,
  RawEd25519Signer,
} from "@algorandfoundation/algokit-utils/crypto";

declare const pubkey: Uint8Array; // 32 bytes from your key store
declare const rawSigner: RawEd25519Signer; // e.g. a hardware wallet wrapper

const signingKey: Ed25519SigningKey = {
  ed25519Pubkey: pubkey,
  rawEd25519Signer: rawSigner,
};

What just happened: Ed25519SigningKey is a minimal record — just the 32-byte public key and a RawEd25519Signer. Every AlgoKit helper that accepts a "signing key" takes this shape, which is why third-party key sources (hardware wallets, KMS, browser extensions) can be integrated without any extra adapter layer: expose a public key and a sign function, wrap them in an object literal, and pass it on.

Verify an Ed25519 signature

Check whether a 64-byte signature is valid for the given message and public key.

import {
  ed25519Generator,
  ed25519Verifier,
} from "@algorandfoundation/algokit-utils/crypto";

const { ed25519Pubkey, rawEd25519Signer } = ed25519Generator();

const message = new TextEncoder().encode("Hello, Algorand!");
const signature = await rawEd25519Signer(message);

const isValid = await ed25519Verifier(signature, message, ed25519Pubkey);
console.log(isValid); // true

What just happened: ed25519Verifier(signature, message, pubkey) returns a Promise<boolean>. Internally it delegates to @noble/ed25519's verifyAsync and returns true only when the signature is a valid Ed25519 signature for exactly the message bytes you pass in under the given public key. Flipping a single bit in any argument causes it to return false rather than throw.

Verify a signature produced by the Algorand signing path

When the bytes you want to check came from a higher-level Algorand signer, verify against the same domain-prefixed bytes the signer saw.

import {
  ed25519Generator,
  ed25519Verifier,
} from "@algorandfoundation/algokit-utils/crypto";
import {
  bytesForSigning,
  generateAddressWithSigners,
} from "@algorandfoundation/algokit-utils/transact";

const generated = ed25519Generator();
const { addr, mxBytesSigner } = generateAddressWithSigners(generated);

const message = new TextEncoder().encode("Hello, Algorand!");
const signature = await mxBytesSigner(message);

// The signer hashed `MX` || message — feed the verifier the same bytes
const signedBytes = bytesForSigning.mxBytes(message);
const isValid = await ed25519Verifier(signature, signedBytes, addr.publicKey);
// The "raw" message would not verify, because mxBytesSigner adds a domain prefix
const isRawValid = await ed25519Verifier(signature, message, addr.publicKey);

console.log(isValid, isRawValid); // true false

What just happened: Every Algorand signer — transaction, logic sig, program data, mx bytes — applies a domain-separation prefix before signing. When verifying a signature that came out of one of those higher-level signers, reproduce the exact bytes that were signed with the matching bytesForSigning.* helper. Verifying against the raw user message will return false.

Use the pinned Noble backend explicitly

Import the noble-prefixed variants when you want a guaranteed implementation.

import {
  nobleEd25519Generator,
  nobleEd25519Verifier,
  type RawEd25519Signer,
  type RawEd25519Verifier,
} from "@algorandfoundation/algokit-utils/crypto";

const { ed25519Pubkey, rawEd25519Signer } = nobleEd25519Generator();
const signer: RawEd25519Signer = rawEd25519Signer;
const verify: RawEd25519Verifier = nobleEd25519Verifier;

What just happened: nobleEd25519Generator and nobleEd25519Verifier are the current concrete backings for ed25519Generator and ed25519Verifier. The package documents that the default exports may change in future versions — if your code depends on specific behaviour of the Noble implementation (constant-time guarantees, exact error messages, etc.) import the noble* symbol directly so future default-repointing cannot break you.

Source: SKILL.md on GitHub

1 alert1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    The skill provides a comprehensive guide for using the AlgoKit Utils TypeScript library. It follows security best practices, such as recommending environment variables for secrets, and uses standard testing mnemonics and placeholder domains in its code examples. No malicious patterns or security risks were detected.

  • Socket1mo

    No alerts

  • Snyk1mo

    Risk: HIGH · 2 issues

Signed by skilld at 4235fb8. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 weeks ago.

Activeupdated 6 months ago

README badge

README badge for algorand-devrel/algorand-agent-skills/algokit-utils-ts