Skip to main content
The utils module provides essential utility functions for working with Snapshot X governance contracts, including encoding utilities, byte manipulation, merkle tree generation, and blockchain-specific helpers.

Module Structure

The utils module is organized into several submodules:
import { 
  bytes,
  encoding,
  merkle,
  splitUint256,
  intsSequence,
  starknetEnums,
  strategies,
  storageProofs,
  execution
} from '@snapshot-labs/sx';

Encoding Utilities

The encoding submodule provides functions for encoding data for smart contract interactions.

Execution Hash

Compute execution hashes for proposals with L1 execution via the Zodiac Module:
import { encoding } from '@snapshot-labs/sx';

const txs = [
  {
    to: '0x1234...',
    value: 0,
    data: '0x',
    operation: 0,
    salt: 0n
  }
];

const { executionHash, txHashes } = encoding.createExecutionHash(
  txs,
  '0xVerifyingContract...',
  1 // chainId
);

Slot Key Calculation

Compute EVM storage slot keys for mapping values:
import { encoding } from '@snapshot-labs/sx';

// Get the slot key for balances[address]
const slotKey = encoding.getSlotKey(
  '0x1234...', // mapping key (e.g., user address)
  '0x00'       // slot index of the mapping
);
The slot index can be found from Solidity compiler artifacts and indicates where the mapping is defined in the contract storage layout.

Padding Utilities

Pad hex strings to 32 bytes:
import { encoding } from '@snapshot-labs/sx';

// Pad left with zeros
const paddedLeft = encoding.hexPadLeft('0x123');
// Result: '0x0000000000000000000000000000000000000000000000000000000000000123'

// Pad right with zeros
const paddedRight = encoding.hexPadRight('0x123');
// Result: '0x0123000000000000000000000000000000000000000000000000000000000000'

Signature Extraction

Extract r, s, v values from Ethereum signatures:
import { encoding } from '@snapshot-labs/sx';

const signature = '0x...';
const { r, s, v } = encoding.getRSVFromSig(signature);

// r and s are SplitUint256 instances
// v is a hex string ('0x1b' or '0x1c')

Bytes Utilities

Convert between hex strings and byte arrays:
import { bytes } from '@snapshot-labs/sx';

// Hex to bytes
const byteArray = bytes.hexToBytes('0x1234');
// Result: [18, 52]

// Bytes to hex
const hexString = bytes.bytesToHex([18, 52]);
// Result: '0x1234'

Split Uint256

Handle 256-bit integers by splitting them into low and high 128-bit components:
import { splitUint256 } from '@snapshot-labs/sx';

// From a BigInt
const split = splitUint256.SplitUint256.fromUint(123456789n);
console.log(split.low, split.high);

// From a hex string
const split2 = splitUint256.SplitUint256.fromHex('0x123abc');

// Convert back to BigInt
const original = split.toUint();

// Convert to hex
const hex = split.toHex();
The SplitUint256 class is particularly useful for Starknet interactions, where 256-bit values are represented as two 128-bit felts.

Merkle Tree

Generate merkle trees and proofs for whitelist-based voting strategies:
import { merkle } from '@snapshot-labs/sx';

// Generate tree from whitelist entries
const entries = [
  '0x1234...:1000',  // address:votingPower
  '0x5678...:2000'
];

const tree = await merkle.generateMerkleTree(entries);
const root = tree[0]; // Merkle root

// Generate proof for a specific entry
const proof = merkle.generateMerkleProof(tree, 0); // index 0
The generateMerkleTree function runs asynchronously and yields to the event loop to avoid blocking on large trees.

Address Types

The merkle tree supports different address types:
import { merkle } from '@snapshot-labs/sx';

enum AddressType {
  STARKNET,  // Starknet addresses
  ETHEREUM,  // Ethereum addresses (42 chars)
  CUSTOM     // Custom address types
}

Storage Proofs

Utilities for generating and verifying storage proofs for cross-chain voting strategies.
import { storageProofs } from '@snapshot-labs/sx';

// Storage proof utilities are available for advanced cross-chain strategies

Execution Utilities

Helpers for managing proposal execution:
import { execution } from '@snapshot-labs/sx';

// Execution utilities for handling proposal execution flow

Best Practices

Use Type Safety

Import specific submodules to benefit from TypeScript type checking

Validate Inputs

Always validate addresses and hex strings before encoding

Handle Errors

Wrap encoding operations in try-catch blocks for production code

Optimize Performance

Use async merkle tree generation for large whitelists

Authenticators

Learn about authentication methods

Strategies

Explore voting strategies

Executors

Understand execution strategies

Build docs developers (and LLMs) love