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

@xenea-dacs/sdk

v0.2.0

Published

Xenea Decentralized Autonomous Contents Storage (DACS) client SDK

Readme

@xenea-dacs/sdk

Version: v0.2.0 — Last updated: 2026-04-03

JavaScript/TypeScript SDK for Xenea DACS — Decentralized Autonomous Contents Storage on the Xenea blockchain.

Upload files to IPFS and register their CIDs on-chain in one step.

Installation

npm install @xenea-dacs/sdk
# or
yarn add @xenea-dacs/sdk
# or
bun add @xenea-dacs/sdk

Getting Started

You'll need an API key. Contact the Xenea team to register your contract and receive one.


Core Client

Works in both browser and Node.js.

import { DACSClient } from '@xenea-dacs/sdk';

const client = new DACSClient({
  endpoint: 'https://ipfs.xenea.io',
  apiKey: 'your-api-key',
  timeoutMs: 30_000, // optional, default 30000ms
});

Upload

const file = new File(['hello world'], 'hello.txt', { type: 'text/plain' });
const result = await client.upload(file);

console.log(result.cid);  // QmXxx...
console.log(result.name); // hello.txt
console.log(result.size); // 11

Download

const blob = await client.download('QmXxx...');

List files

Returns files uploaded under your API key, sorted newest first.

const files = await client.list();
// [{ cid, filename, size, uploadedAt }, ...]

Health check

const health = await client.health();
// { status: 'ok', ipfs: { connected: true }, xenea: { connected: true, chainId: '0x448' } }

Quota

Returns storage and daily request usage for the current API key.

const quota = await client.quota();
// {
//   contractAddress: '0x...',
//   storage: { usedBytes, usedGB, limitGB, remaining },
//   requests: { today, limitDay },
//   fileSizeLimitMB: 100,
// }

Error handling

All methods throw an Error with the server's error message on non-2xx responses.

| HTTP Status | Meaning | |---|---| | 401 | Missing or invalid API key | | 403 | File belongs to a different contract | | 413 | File too large or storage quota exceeded | | 429 | Daily request limit exceeded | | 500 | Internal server / IPFS error |

try {
  const result = await client.upload(file);
} catch (err) {
  console.error(err.message); // e.g. "Quota exceeded: daily request limit reached"
}

Framework-Agnostic: uploadAndRegister()

Works in any environment — Node.js scripts, Vue, Svelte, Angular, Deno, and more. Uses viem WalletClient + PublicClient directly (no React required).

import { DACSClient, uploadAndRegister } from '@xenea-dacs/sdk';
import { createWalletClient, createPublicClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

const gatewayUrl  = process.env.DACS_ENDPOINT;          // e.g. https://ipfs.xenea.io
const apiKey      = process.env.DACS_API_KEY;
const rpcUrl      = process.env.XENEA_RPC_URL;          // e.g. https://rpc-ubusuna.xeneascan.com
const privateKey  = process.env.DEPLOYER_PRIVATE_KEY as `0x${string}`;
const CONTRACT    = '0x56Cf33F7DDE7727FdBe43ECcf7AEbC4d08a59987';

const client      = new DACSClient({ endpoint: gatewayUrl, apiKey });
const account     = privateKeyToAccount(privateKey);
const walletClient = createWalletClient({ account, transport: http(rpcUrl) });
const publicClient = createPublicClient({ transport: http(rpcUrl) });

const file   = new Blob(['hello world'], { type: 'text/plain' });
const result = await uploadAndRegister(client, walletClient, publicClient, CONTRACT, file, 'hello.txt');

console.log(result.cid);              // QmXxx...
console.log(result.txHash);           // 0x...  (null if already registered)
console.log(result.alreadyRegistered); // false (true = file was already safely stored)

Returns:

| Field | Type | Description | |---|---|---| | cid | string | IPFS CID of the uploaded file | | txHash | Hash \| null | On-chain transaction hash. null if already registered | | receipt | TransactionReceipt \| null | Receipt once confirmed. null if already registered | | alreadyRegistered | boolean | true means file was already safely stored — treated as success | | uploadResult | UploadResult | Full IPFS upload response |

React/wagmi users: Use useDACSUpload from @xenea-dacs/sdk/wagmi instead.


React Hooks

import { useDACSClient, useUpload, useFiles, useDownload, useHealth } from '@xenea-dacs/sdk/react';

useUpload

function UploadButton() {
  const client = useDACSClient({ endpoint: 'https://ipfs.xenea.io', apiKey: 'your-api-key' });
  const { upload, uploading, result, error } = useUpload(client);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) upload(file);
  };

  return (
    <div>
      <input type="file" onChange={handleChange} disabled={uploading} />
      {uploading && <p>Uploading...</p>}
      {result && <p>CID: {result.cid}</p>}
      {error && <p>Error: {error}</p>}
    </div>
  );
}

useFiles

function FileList() {
  const client = useDACSClient({ endpoint: 'https://ipfs.xenea.io', apiKey: 'your-api-key' });
  const { files, loading, refresh } = useFiles(client);

  return (
    <ul>
      {files.map(f => <li key={f.cid}>{f.filename} — {f.cid}</li>)}
    </ul>
  );
}

useDownload

function DownloadButton({ cid, filename }: { cid: string; filename: string }) {
  const client = useDACSClient({ endpoint: 'https://ipfs.xenea.io', apiKey: 'your-api-key' });
  const { download, downloading } = useDownload(client);

  return (
    <button onClick={() => download(cid, filename)} disabled={downloading}>
      {downloading ? 'Downloading...' : 'Download'}
    </button>
  );
}

useHealth

Polls /health every 30 seconds by default.

function StatusBadge() {
  const client = useDACSClient({ endpoint: 'https://ipfs.xenea.io', apiKey: 'your-api-key' });
  const { health } = useHealth(client);

  return <span>{health?.status === 'ok' ? '✅ Online' : '⚠️ Degraded'}</span>;
}

Gas Fees

| Operation | Gas Required | Notes | |---|---|---| | client.upload() | No | IPFS storage is off-chain | | register() on-chain | Yes | Requires TXENE on testnet | | getEntry(), isRegistered(), getUploadsByAddress() | No | Read-only view calls |

Uploading a file to IPFS via the SDK is free. Registering a CID on-chain via useDACSUpload or calling register() directly requires a small amount of TXENE to pay for the transaction.


wagmi Integration (React + on-chain registration)

Requires wagmi >= 2 and viem >= 2.

import { useDACSUpload, useDACSEntry, useDACSUploads, DACS_REGISTRY_ABI } from '@xenea-dacs/sdk/wagmi';

useDACSUpload

Uploads to IPFS and registers the CID on-chain in a single call.

const CONTRACT = '0x56Cf33F7DDE7727FdBe43ECcf7AEbC4d08a59987';

function UploadAndRegister() {
  const client = useDACSClient({ endpoint: 'https://ipfs.xenea.io', apiKey: 'your-api-key' });
  const { upload, step, cid, txHash, error } = useDACSUpload(client, CONTRACT);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) upload(file);
  };

  return (
    <div>
      <input type="file" onChange={handleChange} disabled={step !== 'idle'} />
      {step === 'uploading'   && <p>Uploading to IPFS...</p>}
      {step === 'registering' && <p>Sending transaction...</p>}
      {step === 'confirming'  && <p>Waiting for confirmation...</p>}
      {step === 'done'        && <p>Done! CID: {cid}</p>}
      {step === 'error'       && <p>Error: {error}</p>}
    </div>
  );
}

Steps: idleuploadingregisteringconfirmingdone

useDACSEntry

Read a single on-chain entry by CID.

const { entry } = useDACSEntry(CONTRACT, 'QmXxx...');
// entry: { cid, filename, size, uploadedAt, uploader }

useDACSUploads

Get all CIDs registered by a wallet address.

const { cids } = useDACSUploads(CONTRACT, '0xYourAddress');

DACS_REGISTRY_ABI

The full ABI for the DACSRegistry contract, exported for direct use with viem or wagmi.

import { DACS_REGISTRY_ABI } from '@xenea-dacs/sdk/wagmi';

Xenea Chain Config

import { defineChain } from 'viem';

export const xenea = defineChain({
  id: 1096,
  name: 'Xenea Testnet',
  nativeCurrency: { name: 'TXENE', symbol: 'TXENE', decimals: 18 },
  rpcUrls: { default: { http: ['https://rpc-ubusuna.xeneascan.com'] } },
});

Limits (Testnet)

| Resource | Default Limit | |---|---| | Storage per contract | 200 GB | | Max file size | 100 MB | | Requests per day | 1,000 |

These are the default limits for the testnet. All limits are tunable server-side via environment variables.

Contract Addresses

| Network | Contract | Address | |---|---|---| | Xenea Testnet (Chain ID 1096) | DACSRegistry | 0x56Cf33F7DDE7727FdBe43ECcf7AEbC4d08a59987 |

API Endpoint

| Environment | URL | |---|---| | Testnet | https://ipfs.xenea.io |