@arkiv-network/sdk
v0.8.1
Published
TypeScript client library for Arkiv's blockchains interactions
Readme
arkivjs
A TypeScript client library for Arkiv's blockchains interactions. The Arkiv SDK builds strongly on the Viem library - it extends Viem's clients with Arkiv's chain-specific features.
viem is a peer dependency: install it alongside the SDK.
Installation
npm install @arkiv-network/sdk viem
# or
pnpm install @arkiv-network/sdk viem
# or
bun add @arkiv-network/sdk viem
# or
yarn add @arkiv-network/sdk viemUsage
Below is a tutorial to help you create simple scripts that use Arkiv to query and write data.
Prerequisites
For this tutorial, we recommend using Node.js version 22.10.0 or newer (see nodejs.org).
Alternatively, you can use bun, a JavaScript/TypeScript runtime and package manager that natively supports TypeScript without transpilation.
Project Setup
Create a new directory and navigate into it:
mkdir arkiv-sample
cd arkiv-sampleCreate an empty read_example.ts file:
touch read_example.tsInitialize a new JavaScript/TypeScript project:
npm initYou can accept all the default options by pressing Enter at each prompt.
After this step, a package.json file will be created with content similar to:
{
"name": "arkiv-sample",
"version": "1.0.0",
"description": "",
"license": "ISC",
"author": "",
"type": "commonjs",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}Modify the "main" entry to "read_example.ts" and set "type" to "module" so your package.json looks like this:
{
"name": "arkiv-sample",
"version": "1.0.0",
"description": "",
"license": "ISC",
"author": "",
"type": "module",
"main": "read_example.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}Install @arkiv-network/sdk along with its viem peer dependency using your preferred package manager:
npm install @arkiv-network/sdk viemThis command will update your package.json with a section like:
"dependencies": {
"@arkiv-network/sdk": "^0.6.0",
"viem": "^2.38.2"
}It will also create a node_modules directory with all dependencies installed.
Public Client Example (Query Data)
You can now use Arkiv's public client to query data. Paste the following in read_example.ts:
import { createPublicClient } from "@arkiv-network/sdk"
import { tiramisu } from "@arkiv-network/sdk/chains"
import { eq } from "@arkiv-network/sdk/query"
import { http } from "viem"
const publicClient = createPublicClient({
chain: tiramisu, // "tiramisu" is Arkiv's testnet
transport: http(),
});
// Get chain ID
const chainId = await publicClient.getChainId();
console.log('Chain ID:', chainId);
// Get entity by key
const entity = await publicClient.getEntity('0xcadb830a3414251d65e5c92cd28ecb648d9e73d85f2203eff631839d5421f9d7');
console.log('Entity:', entity);
// Build and execute a query using select()
const result = await publicClient
.select({ owner: true, attributes: true, payload: true })
.where(eq('category', 'documentation'))
.ownedBy('0x6186B0DbA9652262942d5A465d49686eb560834C')
.limit(10)
.fetch();
console.log('Found entities:', result.entities);
// Pagination
if (result.hasNextPage()) {
const nextPage = await result.next();
console.log('Next page:', nextPage.entities);
}
// Or walk every page at once
for await (const entity of publicClient
.select({ key: true })
.where(eq('category', 'documentation'))) {
console.log(entity.key);
}Selecting fields with select()
Pass nothing (or "*") to fetch everything, or pass an object to fetch only specific fields:
// All fields
await publicClient.select().where(eq("category", "docs")).fetch();
// Only the fields you need
await publicClient.select({ key: true, owner: true, payload: true }).where(eq("category", "docs")).fetch();Select only what you need. Every selected field is fetched over the network, so requesting data you won't use makes queries slower. Narrowing the selection keeps responses small and fast.
The result type is inferred from your selection: reading a field you didn't select is a compile error. The toText() / toJson() payload
helpers are available only when you select payload.
const [entity] = (
await publicClient.select({ owner: true, payload: true }).where(eq("category", "docs")).fetch()
).entities;
entity.owner; // ✅ Hex
entity.toJson(); // ✅ payload was selected
entity.creator; // ❌ compile error — not selectedFootgun: pass the selection inline. A selection stored in a variable widens its
truevalues toboolean, so the result type can't be narrowed (you get{}and a compile error on every field). If you need to reuse one, annotate itas const:const fields = { owner: true, payload: true } as const; await publicClient.select(fields).where(eq("category", "docs")).fetch();
Running the Example
You have several options to run your TypeScript sample:
With Node.js (using experimental TypeScript support):
node --experimental-strip-types read_example.tsWith Bun (native TypeScript support):
bun read_example.tsClassic Node.js (using transpilation to JavaScript):
- Install TypeScript if you haven't already:
npm install typescript - Initialize a TypeScript config with default settings:
This will create anpx tsc --inittsconfig.jsonfile. You do not need to change its contents. - Transpile your
.tsfiles into.js:
This creates anpx tsc --outDir distdistdirectory containingread_example.js(the transpiled code), along with corresponding type declaration and source map files. - Run the transpiled script:
node dist/read_example.js
- Install TypeScript if you haven't already:
Wallet Client Example (Create Entity)
Now let's add storage (write) functionality.
Create a file named write_example.ts with the following content:
import { createPublicClient, createWalletClient } from "@arkiv-network/sdk"
import { dec, i32 } from "@arkiv-network/sdk/attr"
import { tiramisu } from "@arkiv-network/sdk/chains"
import { ExpirationTime, jsonToPayload } from "@arkiv-network/sdk/utils"
import { http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
// Create a public client
const publicClient = createPublicClient({
chain: tiramisu, // tiramisu is the Arkiv testnet
transport: http(),
})
// Create a wallet client with an account
const client = createWalletClient({
chain: tiramisu,
transport: http(),
account: privateKeyToAccount('0x...'), // Replace with your private key
});
// Create an entity
const { entityKey, txHash } = await client.createEntity({
payload: jsonToPayload({
entity: {
entityType: 'document',
entityId: 'doc-123',
entityContent: "Hello from DevConnect Hackathon 2025! Arkiv chain wishes you all the best!"
},
}),
contentType: 'application/json',
// Attributes are keyed by name. Values carry their type: use the tagged constructors from
// "@arkiv-network/sdk/attr" (i32, u64, u256, dec, str, addr, key, bytes32, bool), or pass a bare
// boolean, number, bigint or string where the type is unambiguous.
attributes: {
category: 'documentation', // bare string -> str
version: i32(1),
score: dec('4.5'),
},
expires: ExpirationTime.fromDays(30), // Entity expires in 30 days
});
console.log('Created entity:', entityKey);
console.log('Transaction hash:', txHash);
const newEntity = await publicClient.getEntity(entityKey);
console.log('Entity:', newEntity);Now you can run it in the same way as in the previous example:
With Node.js (using experimental TypeScript support):
node --experimental-strip-types write_example.tsWith Bun (native TypeScript support):
bun write_example.tsClassic Node.js (using transpilation to JavaScript):
npx tsc --outDir dist node dist/write_example.js
Note:
You must provide your own private key with a minimum balance on the Arkiv L3 network.
You can generate a private key using any tool, for example: https://vanity-eth.tk/
Once you have a key, you can paste it into the example above and fund its address on the Arkiv Tiramisu testnet.
For quick testing, you may use this example key:
0x3d05798f7d11bb1c10b83fed8d3b4d76570c31cd66c8e0a8d8d991434c6d7a5eHowever, funds may not always be available for this key.
Sample code can also be found in the sample directory of this repository.
Package Distribution
This package supports multiple module formats for maximum compatibility:
- ES Modules (
dist/*.js) - For modernimportstatements - CommonJS (
dist/*.cjs) - For Node.jsrequire() - Type Declarations (
dist/*.d.tsanddist/*.d.cts) - Full TypeScript support
The build uses tsdown to generate both ESM and CommonJS formats with proper type declarations.
Runtime Support
Node.js (ESM):
import { createPublicClient } from '@arkiv-network/sdk'; // Uses compiled ESMNode.js (CommonJS):
const { createPublicClient } = require('@arkiv-network/sdk'); // Uses compiled CJSBun (TypeScript native):
import { createPublicClient } from '@arkiv-network/sdk'; // Uses *.ts directlyAll formats provide full type safety and IntelliSense support when using TypeScript.
Development
To install dependencies:
bun installTo build all outputs (ESM, CommonJS, and type declarations):
bun run buildFor more information about building this SDK refer to: BUILD.md
To run type checking:
bun run type-checkTo lint:
bun run lintFor more information about refer to: CONTRIBUTING.md
Verbose Logging
The SDK uses debug under the hood. Set the DEBUG environment variable to view verbose logs:
DEBUG=arkiv:* bun run your-scriptAdjust the namespace (for example, arkiv:rpc or arkiv:query) to target specific log sources. Unset DEBUG to silence debug output.
