Skip to main content

Sealance Merkle Tree

Overview

A utility class for constructing Merkle exclusion proofs for compliant stablecoin programs following the Sealance architecture. This class provides methods for building Merkle trees from Aleo addresses and generating proofs that can be used in Aleo transactions.

Kind: global class

Constructor

SealanceMerkleTree

Create a new SealanceMerkleTree instance for constructing Merkle exclusion proofs.

new SealanceMerkleTree()

Example

import { SealanceMerkleTree } from "@provablehq/sdk/mainnet.js";

// Create a new SealanceMerkleTree instance
const sealance = new SealanceMerkleTree();

// Generate a complete exclusion proof
const blockedAddresses = [
"aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px",
"aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t",
];

// Generate leaves and build tree
const leaves = sealance.generateLeaves(blockedAddresses);
const tree = sealance.buildTree(leaves);

// Generate exclusion proof for an address not in the blocklist
const targetAddress = "aleo1kypwp5m7qtk9mwazgcpg0tq8aal23mnrvwfvug65qgcg9xvsrqgspyjm6n";
const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, targetAddress);
const proofLeft = sealance.getSiblingPath(tree, leftIdx, 15);
const proofRight = sealance.getSiblingPath(tree, rightIdx, 15);
const formattedProof = sealance.formatMerkleProof([proofLeft, proofRight]);

SealanceMerkleTree Methods

convertAddressToField

Converts an Aleo blockchain address to a field element. This function decodes a bech32m-encoded Aleo address and converts it to a field element represented as a BigInt.

convertAddressToField(address) ⇒ bigint
ParametersTypeDescription
addressstringThe Aleo blockchain address (e.g., "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px")
returnbigintA BigInt representing the field element

Example

const sealance = new SealanceMerkleTree();
const address = "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px";
const fieldValue = sealance.convertAddressToField(address);
console.log(fieldValue); // 123456789...n

hashTwoElements

Hashes two elements using the Poseidon4 hash function.

hashTwoElements(prefix, el1, el2)Field
ParametersTypeDescription
prefixstringPrefix for the hash (e.g., "0field" for nodes, "1field" for leaves)
el1stringFirst element to hash
el2stringSecond element to hash
returnFieldThe hash result as a Field

Example

const sealance = new SealanceMerkleTree();
const hash = sealance.hashTwoElements("1field", "123field", "456field");
console.log(hash.toString());

buildTree

Builds a Merkle tree from given leaves. The tree is built bottom-up, hashing pairs of elements at each level.

buildTree(leaves) ⇒ bigint[]
ParametersTypeDescription
leavesstring[]Array of leaf elements as field strings (must have even number of elements)
returnbigint[]Array representing the complete Merkle tree as BigInts

Example

const sealance = new SealanceMerkleTree();
const leaves = ["0field", "1field", "2field", "3field"];
const tree = sealance.buildTree(leaves);
const root = tree[tree.length - 1]; // Get the Merkle root
console.log("Merkle root:", root);

convertTreeToBigInt

Converts an array of decimal string representations of U256 numbers to an array of BigInts.

convertTreeToBigInt(tree) ⇒ bigint[]
ParametersTypeDescription
treestring[]Array of decimal string representations of U256 numbers
returnbigint[]Array of BigInts

Example

const sealance = new SealanceMerkleTree();
const treeStrings = [
"0",
"4328470178059738374782465505490977516512210899136548187530607227309847251692",
"1741259420362056497457198439964202806733137875365061915996980524089960046336"
];
const treeBigInts = sealance.convertTreeToBigInt(treeStrings);
console.log(treeBigInts);
// [0n, 4328470178059738374782465505490977516512210899136548187530607227309847251692n, ...]

generateLeaves

Converts Aleo addresses to field elements, sorts them, pads with zero fields, and returns an array ready for Merkle tree construction.

generateLeaves(addresses, maxTreeDepth) ⇒ string[]
ParametersTypeDescription
addressesstring[]Array of Aleo addresses
maxTreeDepthnumberOptional. Maximum depth of the Merkle tree (default: 15)
returnstring[]Array of field elements ready for Merkle tree construction

Example

const sealance = new SealanceMerkleTree();
const addresses = [
"aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px",
"aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t",
];
const leaves = sealance.generateLeaves(addresses, 15);
console.log(leaves);
// ["0field", "1295133970529764960316948294624974168921228814652993007266766481909235735940field", ...]

getLeafIndices

Finds the leaf indices for a non-inclusion proof of an address. Returns the indices of the two adjacent leaves that surround the target address.

getLeafIndices(merkleTree, address)[number, number]
ParametersTypeDescription
merkleTreebigint[]The complete Merkle tree as array of BigInts
addressstringThe Aleo address for which to find indices
return[number, number]Tuple of [leftLeafIndex, rightLeafIndex]

Example

const sealance = new SealanceMerkleTree();
const addresses = [
"aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px",
"aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t",
];
const leaves = sealance.generateLeaves(addresses);
const tree = sealance.buildTree(leaves);

// Find indices for an address not in the blocklist
const targetAddress = "aleo1kypwp5m7qtk9mwazgcpg0tq8aal23mnrvwfvug65qgcg9xvsrqgspyjm6n";
const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, targetAddress);
console.log(`Left index: ${leftIdx}, Right index: ${rightIdx}`);

getSiblingPath

Generates the sibling path (Merkle proof) for a given leaf index.

getSiblingPath(tree, leafIndex, depth){ siblings: bigint[], leaf_index: number }
ParametersTypeDescription
treebigint[]The complete Merkle tree
leafIndexnumberIndex of the leaf for which to generate the proof
depthnumberMaximum depth of the tree
returnobjectObject containing siblings array and leaf_index

Example

const sealance = new SealanceMerkleTree();
const addresses = [
"aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px",
"aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t",
];
const leaves = sealance.generateLeaves(addresses);
const tree = sealance.buildTree(leaves);
const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, "aleo1...");

const proof = sealance.getSiblingPath(tree, leftIdx, 15);
console.log(proof);
// { siblings: [0n, 1n, ...], leaf_index: 0 }

formatMerkleProof

Generates a formatted exclusion proof suitable for Aleo transactions.

formatMerkleProof(proof) ⇒ string
ParametersTypeDescription
proof{ siblings: bigint[], leaf_index: number }[]An array of two sibling path objects
returnstringString representation of the exclusion proof formatted for Aleo

Example

const sealance = new SealanceMerkleTree();
const addresses = [
"aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px",
"aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t",
];
const leaves = sealance.generateLeaves(addresses);
const tree = sealance.buildTree(leaves);

const targetAddress = "aleo1kypwp5m7qtk9mwazgcpg0tq8aal23mnrvwfvug65qgcg9xvsrqgspyjm6n";
const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, targetAddress);
const proofLeft = sealance.getSiblingPath(tree, leftIdx, 15);
const proofRight = sealance.getSiblingPath(tree, rightIdx, 15);

const formattedProof = sealance.formatMerkleProof([proofLeft, proofRight]);
console.log(formattedProof);
// "[{siblings: [0field, 1field, ...], leaf_index: 0u32}, {siblings: [0field, 2field, ...], leaf_index: 1u32}]"

Complete Example

Here is a complete example showing how to generate an exclusion proof for a compliant stablecoin transaction:

import { SealanceMerkleTree } from "@provablehq/sdk/mainnet.js";

// Initialize the Sealance Merkle Tree
const sealance = new SealanceMerkleTree();

// Define blocked/sanctioned addresses
const blockedAddresses = [
"aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px",
"aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t",
"aleo1y9mnptjymqaz63l72yp5erer7s82a4q3p0s4kzxw6tqfnp0gfyfsh2n8c8",
];

// Step 1: Generate leaves from blocked addresses
const leaves = sealance.generateLeaves(blockedAddresses, 15);

// Step 2: Build the Merkle tree
const tree = sealance.buildTree(leaves);

// Step 3: Get the Merkle root (for on-chain verification)
const root = tree[tree.length - 1];
console.log("Merkle root:", root.toString() + "field");

// Step 4: Generate exclusion proof for a non-blocked address
const userAddress = "aleo1kypwp5m7qtk9mwazgcpg0tq8aal23mnrvwfvug65qgcg9xvsrqgspyjm6n";
const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, userAddress);

// Step 5: Generate sibling paths for both adjacent leaves
const proofLeft = sealance.getSiblingPath(tree, leftIdx, 15);
const proofRight = sealance.getSiblingPath(tree, rightIdx, 15);

// Step 6: Format the proof for use in Aleo transactions
const exclusionProof = sealance.formatMerkleProof([proofLeft, proofRight]);
console.log("Exclusion proof:", exclusionProof);

// The exclusion proof can now be used as an input to a compliant stablecoin program
// that verifies the sender is not on the blocklist