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.
npm install @fizzyflow/endless-vectorimport { 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); // Uint8Arrayconst 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.
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: 1000) |
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| 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 |
Loads metadata from chain. Most read methods call this internally.
await ev.initialize();Marks the instance stale so the next operation re-fetches from chain.
ev.reInitialize();Async. Returns true if the vector has a Seal encryption key on-chain. Calls initialize() internally.
if (await ev.isEncrypted()) { /* ... */ }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 itemsReturns a Transaction without executing it. Useful for batching multiple pushes.
const tx = new Transaction();
ev.getPushTransaction(data1, tx);
ev.getPushTransaction(data2, tx);
await signAndExecuteTransaction(tx);Reads the item at a zero-based index. On encrypted vectors, decrypts transparently.
const data = await ev.at(0); // Uint8ArrayAppends 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.
Returns a concat Transaction without executing.
Sweeps current history segments into a new archive entry, freeing capacity for future pushes.
await ev.archive();Returns an archive Transaction without executing.
Permanently deletes the oldest archive entry. Items in the burned range become unreadable.
await ev.burnArchive();
// ev.archivedFromLength now advanced; at() throws for burned indicesReturns a burn-archive Transaction without executing.
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 transparentlySeal provides end-to-end encryption for vector items. The access policy (seal_approve_endless_vector_owner) ensures only the vector owner can decrypt.
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 transparentlyYou 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); // decryptedOr 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 SessionKeyYou can also set the session key after construction:
ev.seal._sessionKey = mySessionKey;create()generates a random AES-256-GCM key- The key is Seal-wrapped scoped to the vector's object ID and stored on-chain as
seal_encrypted_key push()encrypts each item before storage (adds 28 bytes: 12B nonce + 16B GCM tag)at()unwraps the AES key via Seal (requires a valid SessionKey), then decrypts the item- 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.
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); // throwsconst vectors = await Promise.all(
dataChunks.map((chunk, i) =>
EndlessVector.create({
suiClient: client,
packageId: 'testnet',
array: chunk,
gasCoin: gasCoinRefs[i],
signAndExecuteTransaction: sign,
})
)
);pnpm test:base # core tests
pnpm test:seal # seal encryption testsTests use vitest and require seal_walrus_localnet running on a local Sui validator.
Apache-2.0