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
- SealanceMerkleTree
- constructor
- instance
- .convertAddressToField(address) ⇒
bigint - .hashTwoElements(prefix, el1, el2) ⇒
Field - .buildTree(leaves) ⇒
bigint[] - .convertTreeToBigInt(tree) ⇒
bigint[] - .generateLeaves(addresses, maxTreeDepth) ⇒
string[] - .getLeafIndices(merkleTree, address) ⇒
[number, number] - .getSiblingPath(tree, leafIndex, depth) ⇒
object - .formatMerkleProof(proof) ⇒
string
- .convertAddressToField(address) ⇒
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
| Parameters | Type | Description |
|---|---|---|
| address | string | The Aleo blockchain address (e.g., "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px") |
| return | bigint | A 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
| Parameters | Type | Description |
|---|---|---|
| prefix | string | Prefix for the hash (e.g., "0field" for nodes, "1field" for leaves) |
| el1 | string | First element to hash |
| el2 | string | Second element to hash |
| return | Field | The 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[]
| Parameters | Type | Description |
|---|---|---|
| leaves | string[] | Array of leaf elements as field strings (must have even number of elements) |
| return | bigint[] | 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[]
| Parameters | Type | Description |
|---|---|---|
| tree | string[] | Array of decimal string representations of U256 numbers |
| return | bigint[] | 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[]
| Parameters | Type | Description |
|---|---|---|
| addresses | string[] | Array of Aleo addresses |
| maxTreeDepth | number | Optional. Maximum depth of the Merkle tree (default: 15) |
| return | string[] | 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]
| Parameters | Type | Description |
|---|---|---|
| merkleTree | bigint[] | The complete Merkle tree as array of BigInts |
| address | string | The 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 }
| Parameters | Type | Description |
|---|---|---|
| tree | bigint[] | The complete Merkle tree |
| leafIndex | number | Index of the leaf for which to generate the proof |
| depth | number | Maximum depth of the tree |
| return | object | Object 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
| Parameters | Type | Description |
|---|---|---|
| proof | { siblings: bigint[], leaf_index: number }[] | An array of two sibling path objects |
| return | string | String 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