@arkiv-network/sdk
Version:
TypeScript client library for Arkiv's blockchains interactions
328 lines (320 loc) • 13.3 kB
text/typescript
import type { Account, Chain, Client, Hex, PublicActions, Transport, WalletActions } from "viem"
import type {
ChangeOwnershipParameters,
ChangeOwnershipReturnType,
} from "../../actions/wallet/changeOwnership"
import { changeOwnership } from "../../actions/wallet/changeOwnership"
import {
type CreateEntityOptions,
type CreateEntityReturnType,
createEntity,
} from "../../actions/wallet/createEntity"
import type {
DeleteEntityParameters,
DeleteEntityReturnType,
} from "../../actions/wallet/deleteEntity"
import { deleteEntity } from "../../actions/wallet/deleteEntity"
import type {
ExecuteBatchParameters,
ExecuteBatchReturnType,
} from "../../actions/wallet/executeBatch"
import { executeBatch } from "../../actions/wallet/executeBatch"
import type {
ExtendEntityParameters,
ExtendEntityReturnType,
} from "../../actions/wallet/extendEntity"
import { extendEntity } from "../../actions/wallet/extendEntity"
import {
type PatchEntityChanges,
type PatchEntityReturnType,
patchEntity,
} from "../../actions/wallet/patchEntity"
import type { CreateEntityValues, Schema } from "../../schema"
import type { TxParams } from "../../types"
export type WalletArkivActions<
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
> = Pick<
PublicActions<transport, chain, account>,
"waitForTransactionReceipt" | "call" | "simulateContract" | "readContract" | "getBlockNumber"
> &
Pick<
WalletActions<chain, account>,
| "addChain"
| "sendCalls"
| "waitForCallsStatus"
| "sendTransaction"
| "sendRawTransaction"
| "signMessage"
| "signTransaction"
| "writeContract"
> & {
/**
* Creates a new entity through a schema: each column becomes an attribute, written with the
* type and indexed flag the schema declares.
*
* - JSON-RPC Methods: `eth_sendRawTransaction`
*
* @param schema - The schema the entity is written through, built with `defineSchema`.
* @param values - The entity's values, keyed by column. Required columns must be given;
* defaulted ones left out are written with their default. {@link CreateEntityValues}
* @param options - The entity's lifetime, plus its flags and salt. {@link CreateEntityOptions}
* @param txParams - Optional transaction parameters
* @returns The new entity's key, the transaction hash, and the block it expires at, as the
* engine recorded it. {@link CreateEntityReturnType}
*
* @throws {InvalidExpiryError} If the expiry exceeds a protocol bound or would leave the
* entity dead on arrival.
* @throws {SchemaMismatchError} If the values do not fit the schema.
* @throws {InvalidValueError} If a value does not fit its type.
* @throws {EntityTooLargeError} If the `bytes` values add up to more than `MAX_ENTITY_BYTES`.
* @throws {EntityMutationError} If the transaction fails or reverts.
*
* @example
* import { createWalletClient, ExpirationTime } from "@arkiv-network/sdk"
* import { addr, bytes, dec, defineSchema, str } from "@arkiv-network/sdk/schema"
* import { rabdi } from "@arkiv-network/sdk/chains"
* import { http } from "viem"
* import { privateKeyToAccount } from "viem/accounts"
*
* const Posts = defineSchema("posts", {
* author: addr().indexed(),
* category: str().indexed(),
* score: dec().indexed(),
* title: str(),
* body: bytes(),
* thumbnail: bytes().optional(),
* mimeType: str().default("text/markdown"),
* })
*
* const client = createWalletClient({
* account: privateKeyToAccount("0x..."),
* chain: rabdi({ accessKey: "..." }),
* transport: http(),
* })
* const { entityKey, txHash, expiresAt } = await client.createEntity(
* Posts,
* {
* author: addr(client.account.address),
* category: "docs",
* score: dec("4.1"),
* title: "Cells, not entities",
* body: new TextEncoder().encode("# Hello"),
* },
* { expiration: ExpirationTime.fromDays(30) },
* )
*/
createEntity: <S extends Schema>(
schema: S,
values: CreateEntityValues<S>,
options: CreateEntityOptions,
txParams?: TxParams,
) => Promise<CreateEntityReturnType>
/**
* Patches the entity with the given key through a schema: sets some columns, unsets optional
* ones, and leaves everything it does not name alone.
*
* - JSON-RPC Methods: `eth_sendRawTransaction`
*
* @param schema - The schema the changes are typed by.
* @param entityKey - The entity to patch.
* @param changes - The columns to set and the optional columns to unset. {@link PatchEntityChanges}
* @param txParams - Optional transaction parameters
* @returns The patched entity's key and the transaction hash. {@link PatchEntityReturnType}
*
* @throws {EmptyPatchError} If the patch has nothing to apply.
* @throws {ConflictingMutationError} If a name appears in both `set` and `unset`.
* @throws {SchemaMismatchError} If the changes do not fit the schema.
* @throws {InvalidValueError} If a value does not fit its type.
* @throws {EntityTooLargeError} If the `bytes` values in `set` add up to more than
* `MAX_ENTITY_BYTES`.
* @throws {EntityMutationError} If the transaction fails or reverts.
*
* @example
* // Posts is the schema from the createEntity example.
* // One column changes, an optional one goes away.
* const { txHash } = await client.patchEntity(Posts, entityKey, {
* set: { score: dec("4.5") },
* unset: ["thumbnail"],
* })
*/
patchEntity: <S extends Schema>(
schema: S,
entityKey: Hex,
changes: PatchEntityChanges<S>,
txParams?: TxParams,
) => Promise<PatchEntityReturnType>
/**
* Deletes the entity with the given key.
*
* - JSON-RPC Methods: `eth_sendRawTransaction`
*
* @param data - The entity deletion parameters
* @param txParams - Optional transaction parameters
* @returns The deleted entity's key and the transaction hash. {@link DeleteEntityReturnType}
*
* @example
* import { createWalletClient } from "@arkiv-network/sdk"
* import { rabdi } from "@arkiv-network/sdk/chains"
* import { http } from "viem"
* import { privateKeyToAccount } from "viem/accounts"
*
* const client = createWalletClient({
* account: privateKeyToAccount("0x..."),
* chain: rabdi({ accessKey: "..." }),
* transport: http(),
* })
* // entityKey is the bytes32 key returned by createEntity.
* const { txHash } = await client.deleteEntity({ entityKey })
*/
deleteEntity: (
data: DeleteEntityParameters,
txParams?: TxParams,
) => Promise<DeleteEntityReturnType>
/**
* Sets a new expiry on the entity with the given key.
*
* The new lifetime is resolved the same way a create's is: a duration counts from now rather
* than adding to what the entity has left, and `atBlock` / `atDate` pin an absolute deadline.
* The engine rejects an extension that would move the expiry earlier; one that resolves to the
* current expiry changes nothing.
*
* - JSON-RPC Methods: `eth_sendRawTransaction`
*
* @param data - The entity key and its new lifetime
* @param txParams - Optional transaction parameters
* @returns The entity's key, the transaction hash, and the block it now expires at, as the
* engine recorded it. {@link ExtendEntityReturnType}
*
* @throws {InvalidExpiryError} If the expiry is malformed, exceeds a protocol bound, or would
* leave the entity dead on arrival.
*
* @example
* import { createWalletClient, ExpirationTime } from "@arkiv-network/sdk"
* import { rabdi } from "@arkiv-network/sdk/chains"
* import { http } from "viem"
* import { privateKeyToAccount } from "viem/accounts"
*
* const client = createWalletClient({
* account: privateKeyToAccount("0x..."),
* chain: rabdi({ accessKey: "..." }),
* transport: http(),
* })
* const { txHash, expiresAt } = await client.extendEntity({
* entityKey,
* expiration: ExpirationTime.fromDays(30),
* })
*/
extendEntity: (
data: ExtendEntityParameters,
txParams?: TxParams,
) => Promise<ExtendEntityReturnType>
/**
* Hands the entity with the given key to a new owner.
*
* - JSON-RPC Methods: `eth_sendRawTransaction`
*
* @param data - The ownership change parameters
* @param txParams - Optional transaction parameters
* @returns The entity's key and the transaction hash. {@link ChangeOwnershipReturnType}
*/
changeOwnership: (
data: ChangeOwnershipParameters,
txParams?: TxParams,
) => Promise<ChangeOwnershipReturnType>
/**
* Applies a batch of entity operations — creates, patches, deletes, extensions and ownership
* transfers — in one transaction.
*
* - JSON-RPC Methods: `eth_sendRawTransaction`
*
* Every operation lands in one transaction, so the whole batch applies or none of it does.
* At least one operation is required. Each create and patch names its own schema and is typed
* by it, so one batch can write entities of several schemas.
*
* @param data - The batch parameters (creates, patches, deletes, extensions, ownershipChanges)
* @param txParams - Optional transaction parameters
* @returns The transaction hash, plus the keys touched by each kind of operation.
* {@link ExecuteBatchReturnType}
*
* @throws {Error} If the batch has no operations.
* @throws {InvalidExpiryError} If an expiry exceeds a protocol bound or would leave the entity
* dead on arrival.
* @throws {SchemaMismatchError} If a create's or patch's values do not fit its schema.
* @throws {InvalidValueError} If a value does not fit its type.
* @throws {EmptyPatchError} If a patch has nothing to apply.
* @throws {ConflictingMutationError} If a patch names a column in both `set` and `unset`.
* @throws {EntityTooLargeError} If the `bytes` values of one create or patch add up to more
* than `MAX_ENTITY_BYTES`.
* @throws {EntityMutationError} If the transaction fails or reverts.
*
* @example
* import { createWalletClient, ExpirationTime } from "@arkiv-network/sdk"
* import { dec } from "@arkiv-network/sdk/schema"
* import { rabdi } from "@arkiv-network/sdk/chains"
* import { http } from "viem"
* import { privateKeyToAccount } from "viem/accounts"
*
* const client = createWalletClient({
* account: privateKeyToAccount("0x..."),
* chain: rabdi({ accessKey: "..." }),
* transport: http(),
* })
* // Posts is the schema from the createEntity example.
* const { txHash, createdEntities } = await client.executeBatch({
* creates: [{
* schema: Posts,
* values: { author, category: "docs", score: dec("4.1"), title: "Hi", body: new Uint8Array() },
* expiration: ExpirationTime.fromDays(30),
* }],
* patches: [{ schema: Posts, entityKey: keyToRevise, set: { category: "archive" } }],
* deletes: [{ entityKey: staleKey }],
* extensions: [{
* entityKey: keyToKeepAlive,
* expiration: ExpirationTime.atBlock(1_200_000n),
* }],
* ownershipChanges: [{ entityKey: keyToHandOver, newOwner }],
* })
*/
executeBatch: <
const TCreates extends readonly Schema[] = [],
const TPatches extends readonly Schema[] = [],
>(
data: ExecuteBatchParameters<TCreates, TPatches>,
txParams?: TxParams,
) => Promise<ExecuteBatchReturnType>
}
export function walletArkivActions<
transport extends Transport = Transport,
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
>(client: Client<transport, chain, account>) {
return {
createEntity: <S extends Schema>(
schema: S,
values: CreateEntityValues<S>,
options: CreateEntityOptions,
txParams?: TxParams,
) => createEntity(client, schema, values, options, txParams),
patchEntity: <S extends Schema>(
schema: S,
entityKey: Hex,
changes: PatchEntityChanges<S>,
txParams?: TxParams,
) => patchEntity(client, schema, entityKey, changes, txParams),
deleteEntity: (data: DeleteEntityParameters, txParams?: TxParams) =>
deleteEntity(client, data, txParams),
extendEntity: (data: ExtendEntityParameters, txParams?: TxParams) =>
extendEntity(client, data, txParams),
changeOwnership: (data: ChangeOwnershipParameters, txParams?: TxParams) =>
changeOwnership(client, data, txParams),
executeBatch: <
const TCreates extends readonly Schema[] = [],
const TPatches extends readonly Schema[] = [],
>(
data: ExecuteBatchParameters<TCreates, TPatches>,
txParams?: TxParams,
) => executeBatch(client, data, txParams),
}
}