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
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:
Nativefor the native token,ownerIdBase58,shardfor fungible tokens (the address of the mint vault), andnft:ownerIdBase58,shardfor 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 distributionIncreaseSupply- 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