Skip to main content

Using the Typescript SDK

The @repyh-labs/delta-signing package provides TypeScript utilities for serializing and signing delta network messages. It's designed for client applications that need to create signed debit allowances and token mints.

Installation

warning

This installation method isn't available yet. Contact us to get access to the package.

In your TypeScript project:

npm install @repyh-labs/delta-signing

Quickstart: Signing Messages

For most use cases, the following methods of the Signer class are all you need. They accept JSON, handle parsing and serialization internally, and return a signed message ready to submit to a domain.

Sign a debit allowance

import bs58 from "bs58"
import { Ed25519Signer, Signer } from "@repyh-labs/delta-signing"

// Create a `Signer` (more details and signers below)
const signer: Signer = new Ed25519Signer(bs58.decode("your-private-key-base58"))

const signedDebit = await signer.signedDebitAllowance({
credited: "8CYgy4ZoKgKezUKc7LXNH6yhuckuhgGKT3bkgziXeiAj,42",
new_nonce: 1,
debited_shard: 43,
allowances: { Native: { Fungible: 1000 } },
})

The result is a SignedMessage containing the original payload and a signature.

Sign a fungible token mint

For token mints, it's exactly the same flow:

const signedMint = await signer.signedFungibleMint({
operation: {
Create: {
metadata: { name: "My Token", symbol: "MTK" },
credited: [["8CYgy4ZoKgKezUKc7LXNH6yhuckuhgGKT3bkgziXeiAj,42", 1000]],
},
},
new_nonce: 1,
shard: 43,
})

Sign an NFT mint

const signedNft = await signer.signedNftMint({
operation: {
Create: {
/* ... */
},
},
})

See below for full JSON examples.

Advanced

This section covers parsing, serialization, low-level signing methods, and alternative signers like passkeys.

Parsing JSON into delta Types

The parse object provides functions to convert JSON objects into typed delta structures. This is handled automatically by the signing convenience methods, but can be useful for validation or custom workflows.

Debit Allowances

import { parse } from "@repyh-labs/delta-signing"

const debitAllowance = parse.debitAllowance({
credited: "8CYgy4ZoKgKezUKc7LXNH6yhuckuhgGKT3bkgziXeiAj,42",
new_nonce: 1,
debited_shard: 43,
allowances: {
// Native token
Native: { Fungible: 1000 },

// Fungible token identified by `ownerIdBase58,shard`
"J4jzCxV7Wjg677BaFjJTUVqpdWAr61KQKuTby46C5cxC,42": { Fungible: 500 },

// NFT collection identified by `nft:ownerIdBase58,shard`
"nft:H7kL9mNpQrStUvWxYz12AbCdEfGhIjKlMnOpQrStUvWx,42": {
NonFungible: [1, 2, 3],
},
},
})

The credited field uses the ownerIdBase58,shard format to represent a vault address: the base58-encoded owner ID followed by the shard number. The keys in allowances are token kinds, which have three variants:

  • Native for the native token,
  • ownerIdBase58,shard for fungible tokens (the address of the mint vault), and
  • nft:ownerIdBase58,shard for NFT collections.

Values specify amounts: { Fungible: amount } for fungible tokens or { NonFungible: [id1, id2, ...] } for NFTs.

Token Mints

Use parse.fungibleMint() and parse.nftMint() to parse token mint messages. Both support two operations:

  • Create - creates a new token with metadata and initial distribution
  • IncreaseSupply - mints additional tokens to specified recipients

Fungible mints include name and symbol metadata. NFT mints include collection-level metadata (name, description, attributes) and per-NFT metadata. See the Examples section for full mint examples.

Serializing to Bytes

The serialize() function converts parsed delta types to their binary representation:

import { parse, serialize } from "@repyh-labs/delta-signing"

const debitAllowance = parse.debitAllowance({
/* ... */
})
const bytes: Uint8Array = serialize(debitAllowance)

Low-Level Signing

For custom payloads, use signedMessage() of the Signer class with a custom serializer:

const signed = await signer.signedMessage(myPayload, (payload) =>
mySerialize(payload)
)

Or sign raw bytes directly:

const signature = await signer.signBytes(someBytes)

Ed25519 Signer

The Ed25519Signer uses Ed25519ph with the delta protocol's context string. Create one from a 32-byte private key:

import bs58 from "bs58"
import { Ed25519Signer } from "@repyh-labs/delta-signing"

const privateKey = bs58.decode("your-private-key-base58")
const signer = new Ed25519Signer(privateKey)

Passkey Signer

Passkeys provide a secure, user-friendly alternative to managing private keys. They use the WebAuthn standard and are supported in modern browsers. The delta protocol supports passkey signatures using the secp256r1 (P-256) curve.

Setting Up the Provider

The BrowserPasskeyProvider wraps the browser's WebAuthn API:

import { BrowserPasskeyProvider } from "@repyh-labs/delta-signing"

const provider = new BrowserPasskeyProvider({
rp: {
id: window.location.hostname,
name: "My delta App",
},
user: {
id: new TextEncoder().encode(userId),
name: userEmail,
displayName: userName,
},
})

The rp (relying party) identifies your application to the browser. The user object identifies the user creating the passkey.

Creating a Passkey Signer

For new users, create a passkey and signer together using DeltaPasskeySigner.create(). This prompts the user to create a new passkey:

import {
DeltaPasskeySigner,
BrowserPasskeyProvider,
} from "@repyh-labs/delta-signing"

// This triggers the browser's passkey creation dialog
const signer = await DeltaPasskeySigner.create(provider)

// Get the public key to store persistently
const publicKey = signer.getPublicKey() // Uint8Array (33 bytes, compressed)

Your application must store the public key persistently (e.g., in a database) alongside a user identifier like email or user ID. This allows you to look up the correct public key when a returning user wants to sign transactions.

For returning users, retrieve their stored public key and reconstruct the signer:

import {
DeltaPasskeySigner,
PasskeySigner,
BrowserPasskeyProvider,
} from "@repyh-labs/delta-signing"

// Look up the user's stored public key (e.g., by email or user ID)
const storedPublicKey = await db.getPublicKeyForUser(userEmail)

const signer = new DeltaPasskeySigner(
storedPublicKey,
new PasskeySigner(provider)
)

Each signing operation prompts the user to authenticate with their passkey (fingerprint, face recognition, or security key).

Provider Configuration Options

The BrowserPasskeyProvider accepts additional WebAuthn options like timeout and authenticatorSelection. See the MDN documentation for PublicKeyCredentialCreationOptions for all available options.

Examples

Full JSON examples of token mint payloads:

Fungible Mint
// Creating a new fungible token
const signedCreate = await signer.signedFungibleMint({
operation: {
Create: {
metadata: { name: "My Token", symbol: "MTK" },
credited: [
["8CYgy4ZoKgKezUKc7LXNH6yhuckuhgGKT3bkgziXeiAj,42", 1000],
["J4jzCxV7Wjg677BaFjJTUVqpdWAr61KQKuTby46C5cxC,42", 500],
],
},
},
new_nonce: 1,
shard: 43,
})

// Increasing supply of an existing token
const signedIncrease = await signer.signedFungibleMint({
operation: {
IncreaseSupply: {
credited: [["8CYgy4ZoKgKezUKc7LXNH6yhuckuhgGKT3bkgziXeiAj,42", 2000]],
},
},
new_nonce: 2,
shard: 43,
})
NFT Mint
const signedNft = await signer.signedNftMint({
operation: {
Create: {
collection: {
name: "My Collection",
description: "A collection of NFTs",
attributes: { category: "art" },
},
credited: [
[
"8CYgy4ZoKgKezUKc7LXNH6yhuckuhgGKT3bkgziXeiAj,42",
[
{
id: 1,
metadata: {
name: "NFT #1",
description: "First NFT",
attributes: {},
},
},
{
id: 2,
metadata: {
name: "NFT #2",
description: "Second NFT",
attributes: {},
},
},
],
],
],
},
},
new_nonce: 1,
shard: 43,
})

Next Steps

  • Learn about verifiables and how signed messages are processed by domains