npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fizzyflow/endless-vector

v0.0.13

Published

Scalable append-only on-chain vector for Sui with Walrus blob offloading and Seal encryption

Readme

Endless Vector - JavaScript SDK

JavaScript/TypeScript SDK for the Endless Vector smart contract on Sui. Endless Vector is a scalable, append-only on-chain vector<vector<u8>> that grows beyond Sui object size limits by automatically splitting data into history segments. Items larger than ~120 KB are transparently stored as Walrus blobs. Optional Seal encryption protects all stored data with AES-256-GCM.

Installation

npm install @fizzyflow/endless-vector

Quick Start

import { EndlessVector } from '@fizzyflow/endless-vector';

// Create
const ev = await EndlessVector.create({
    suiClient: client,
    packageId: 'testnet', // or 'mainnet', or an explicit 0x... package ID
    signAndExecuteTransaction: async (tx) => {
        const result = await wallet.signAndExecuteTransaction({ transaction: tx });
        return result.digest;
    },
});

// Write
await ev.push(new Uint8Array([1, 2, 3]));

// Read
const item = await ev.at(0); // Uint8Array

Constructor

const ev = new EndlessVector(params);

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | suiClient | SuiGrpcClient | yes | Sui gRPC client instance | | id | string | yes | On-chain object ID of the vector | | packageId | string | no | Move package ID, or 'testnet'/'mainnet'. Enables writes | | signAndExecuteTransaction | function | no | Signs and submits a Transaction, returns its digest. Enables writes | | walrusClient | WalrusClient | no | @mysten/walrus client for blob read/write | | publisherUrl | string | no | Walrus publisher HTTP URL (fallback when no walrusClient) | | aggregatorUrl | string | no | Walrus aggregator HTTP URL (fallback when no walrusClient) | | senderAddress | string | no | Sender Sui address, required for Walrus blob writes | | sealClient | SealClient | no | @mysten/seal client for encryption/decryption | | sessionKey | SessionKey | no | Pre-built Seal SessionKey. Alternative to signer | | signer | Signer | no | Keypair or wallet signer used to auto-create a SessionKey when needed | | sealTtlMin | number | no | SessionKey TTL in minutes (default: 5) |

Providing only suiClient + id gives a read-only instance. Add packageId + signAndExecuteTransaction for writes. Add Walrus params for large items (>120 KB). Add Seal params for encryption.

EndlessVector.create(params)

Creates a new on-chain vector.

Accepts all constructor params above, plus:

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | array | Uint8Array | Uint8Array[] | no | Initial item(s) to push | | gasCoin | {objectId, digest, version} | no | Explicit gas coin for parallel creation | | options.timeout | number | no | Tx confirmation timeout, ms (default: 30000) | | options.pollIntervalMs | number | no | Tx poll interval, ms (default: 200) |

When sealClient is provided, the vector is created with Seal encryption enabled. A random AES-256-GCM key is generated, Seal-wrapped scoped to the new vector's object ID, and stored on-chain. Any initial array items are encrypted before storage.

const ev = await EndlessVector.create({
    suiClient: client,
    packageId: 'testnet',
    sealClient,
    signer: keypair,
    signAndExecuteTransaction: sign,
});
// All push() / at() calls now encrypt/decrypt transparently

Properties

| Property | Type | Description | |----------|------|-------------| | id | string | Object ID | | isWritable | boolean | Whether writes are enabled | | length | number | Total item count (append-only, never decreases) | | binaryLength | number | Total size of all items in bytes | | sealEncryptedKey | Uint8Array | null | Seal-wrapped AES key, or null if unencrypted | | seal | EndlessVectorSeal | Seal companion (always present; active only when sealClient was provided) | | walrus | EndlessVectorWalrus | Walrus companion (always present; active only when Walrus params were provided) | | historyItemsCount | number | History segments in the current object | | archiveItemsCount | number | Total archive entries ever created | | archivedAtLength | number | length at the time of the last archive | | archivedFromLength | number | Items before this index have been burned | | burnedArchiveCount | number | Burned archive count | | firstNotHistoryIndex | number | First index stored in the current object |

Methods

initialize()

Loads metadata from chain. Most read methods call this internally.

await ev.initialize();

reInitialize()

Marks the instance stale so the next operation re-fetches from chain.

ev.reInitialize();

isEncrypted()

Async. Returns true if the vector has a Seal encryption key on-chain. Calls initialize() internally.

if (await ev.isEncrypted()) { /* ... */ }

push(arr, params?)

Appends one or more Uint8Array items. Requires writable mode.

  • Items up to ~120 KB are stored on-chain as vector<u8>.
  • Larger items are stored as Walrus blobs (requires Walrus params).
  • On encrypted vectors, every item is AES-256-GCM encrypted before storage (28 bytes overhead per item).
await ev.push(new Uint8Array([1, 2, 3]));
await ev.push([chunk1, chunk2, chunk3]); // multiple items

getPushTransaction(arr, tx?, expectedLength?)

Returns a Transaction without executing it. Useful for batching multiple pushes.

If expectedLength is provided, an ensure_length check is prepended as the first PTB command — the whole transaction aborts atomically if the vector's current on-chain length doesn't match. This prevents duplicate pushes after a timeout-retry and blocks concurrent writers. push() passes this.length automatically; pass it explicitly when building PTBs manually.

const tx = new Transaction();
ev.getPushTransaction(data1, tx);
ev.getPushTransaction(data2, tx);
await signAndExecuteTransaction(tx);

// with consistency guard:
const tx2 = ev.getPushTransaction(data, null, ev.length);

at(index)

Reads the item at a zero-based index. On encrypted vectors, decrypts transparently.

const data = await ev.at(0); // Uint8Array

concat(other, params?)

Appends all items from another vector (or array of vectors) into this one. Sources are consumed (destroyed).

await ev.concat(otherVector);
await ev.concat([v2, v3]);
await ev.concat('0xOTHER_VECTOR_ID');

Restrictions: cannot concat vectors that have archived items or that are Seal-encrypted.

getConcatTransaction(other, tx?)

Returns a concat Transaction without executing.

archive(params?)

Sweeps current history segments into a new archive entry, freeing capacity for future pushes.

await ev.archive();

getArchiveTransaction(tx?)

Returns an archive Transaction without executing.

burnArchive(params?)

Permanently deletes the oldest archive entry. Items in the burned range become unreadable.

await ev.burnArchive();
// ev.archivedFromLength now advanced; at() throws for burned indices

getBurnArchiveTransaction(tx?)

Returns a burn-archive Transaction without executing.

Walrus Blob Storage

When Walrus params are configured, items larger than ~120 KB are automatically stored as Walrus blobs instead of on-chain vector<u8>. The SDK handles upload, certification, and read-back. On encrypted vectors, blobs contain ciphertext only.

const ev = new EndlessVector({
    suiClient: client,
    id: '0x...',
    packageId: 'testnet',
    walrusClient,                       // or aggregatorUrl + publisherUrl
    senderAddress: wallet.address,
    signAndExecuteTransaction: sign,
});

await ev.push(largeFile); // >120 KB → stored as Walrus blob
const data = await ev.at(0); // fetched from Walrus transparently

Blob lifetime: inspect & extend

Walrus blobs are backed by storage that expires at an end_epoch. The Walrus companion (ev.walrus) can report when a vector's blobs expire and renew them all in a single transaction — covering blobs in current items, history segments, and non-burned archive segments. These methods require walrusClient (to resolve the Walrus System object and current storage price).

minBlobEndEpoch()

Async. Reads the soonest-expiring blob's end_epoch across the whole vector, on-chain via simulation (no gas, works read-only). Returns null if the vector holds no blobs.

const minEpoch = await ev.walrus.minBlobEndEpoch(); // number | null

extendBlobsCostToEpoch(targetEndEpoch)

Async. Returns the exact WAL cost (in FROST, as a bigint) to bring every blob up to targetEndEpoch. Computed on-chain, so it accounts for every blob across all tiers — even ones not loaded in the SDK. 0n when nothing needs extending.

const cost = await ev.walrus.extendBlobsCostToEpoch(minEpoch + 10); // bigint (FROST)

extendBlobsToEpoch(targetEndEpoch, params?)

Async. Extends every blob whose storage ends before targetEndEpoch up to that epoch, in one transaction. Blobs already valid through the target (and expired blobs, which Walrus cannot extend) are skipped on-chain. Resolves to the new minimum blob end epoch. Requires writable mode and senderAddress.

The WAL payment is sourced automatically from the sender's balance for exactly the on-chain cost, and any leftover is returned to the sender. Pass walCoin to pay from a specific coin, or cost to skip the cost read.

const minEpoch = await ev.walrus.minBlobEndEpoch();
const newMin = await ev.walrus.extendBlobsToEpoch(minEpoch + 10);
// newMin === minEpoch + 10

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | cost | bigint | no | Precomputed cost in FROST; skips the on-chain cost read | | walCoin | TransactionObjectArgument | no | WAL coin to pay from; if omitted, one is sourced from the sender's balance | | timeout | number | no | Tx confirmation timeout, ms | | pollIntervalMs | number | no | Tx poll interval, ms |

getExtendBlobsToEpochTransaction(targetEndEpoch, params?)

Async. Returns the extend Transaction without executing, for batching. Accepts cost, walCoin, and txToAppendTo.

Seal Encryption

Seal provides end-to-end encryption for vector items. The access policy (seal_approve_endless_vector_owner) ensures only the vector owner can decrypt.

Creating an encrypted vector

const ev = await EndlessVector.create({
    suiClient: client,
    packageId: 'testnet',
    sealClient,
    signer: keypair,
    signAndExecuteTransaction: sign,
});
// AES key generated → Seal-wrapped → stored on-chain
// All push()/at() calls encrypt/decrypt transparently

Reading an encrypted vector

You can provide a signer and the SDK auto-creates a SessionKey:

const ev = new EndlessVector({
    suiClient: client,
    id: '0x...',
    sealClient,
    signer: keypair,          // SDK creates a 5-minute SessionKey automatically
});
const data = await ev.at(0); // decrypted

Or provide a pre-built sessionKey (useful in browser wallets where signing is interactive):

const ev = new EndlessVector({
    suiClient: client,
    id: '0x...',
    sealClient,
    sessionKey: mySessionKey, // created externally, e.g. via wallet adapter
});
const data = await ev.at(0); // decrypted using the provided SessionKey

You can also set the session key after construction:

ev.seal._sessionKey = mySessionKey;

How it works

  1. create() generates a random AES-256-GCM key
  2. The key is Seal-wrapped scoped to the vector's object ID and stored on-chain as seal_encrypted_key
  3. push() encrypts each item before storage (adds 28 bytes: 12B nonce + 16B GCM tag)
  4. at() unwraps the AES key via Seal (requires a valid SessionKey), then decrypts the item
  5. The unwrapped AES key is cached in memory for subsequent reads

Passing sealClient to an unencrypted vector is safe — push() and at() check for the on-chain sealEncryptedKey before attempting any encryption/decryption.

Examples

Archive and burn lifecycle

await ev.push(largeData);
await ev.archive();
await ev.initialize();

console.log(ev.archiveItemsCount);  // 1
console.log(ev.archivedAtLength);   // e.g. 5

const item = await ev.at(0); // still readable

await ev.burnArchive();
await ev.initialize();

console.log(ev.burnedArchiveCount);  // 1
console.log(ev.archivedFromLength);  // 5 — items 0..4 are gone
await ev.at(0); // throws

Parallel vector creation with gas coins

const vectors = await Promise.all(
    dataChunks.map((chunk, i) =>
        EndlessVector.create({
            suiClient: client,
            packageId: 'testnet',
            array: chunk,
            gasCoin: gasCoinRefs[i],
            signAndExecuteTransaction: sign,
        })
    )
);

Testing

pnpm test:base                   # core tests
pnpm test:seal                   # seal encryption tests
pnpm test:walrus-blobs           # walrus blob storage
pnpm test:walrus-blobs-sdk       # walrus blob storage (SDK client)
pnpm test:walrus-blobs-history   # blob storage across history segments
pnpm test:walrus-blobs-extend    # blob lifetime: min end epoch, cost, extend

Tests use vitest and require a local Sui validator with Walrus and Seal localnet services.

License

Apache-2.0

Links