Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Writing & execution

Every write goes through the execution layer, a single interface, ExecutionLayer, with interchangeable implementations. The same application code works whether the user's account pays network fees itself (direct) or a relayer fronts them (API).

const r = await client.execution.writeData({ cloudId, dataId, payload });
await r.wait();   // identical on both layers

The two built-in layers

LayerWho signs / pays feesHow it reaches the network
Direct (AccountExecution, mode: "account")the user's account (viem); it pays the feesdirect call to the network
API (ApiExecution, mode: "api")typed signed request to the DataPrism API; a relayer pays, reimbursed in DPCdelegated execution

The client picks one from options.mode ("account" default, or "api"), or you inject a custom layer via options.execution.

import { accountExecution, apiExecution } from "@dataprism/sdk";
 
const account = accountExecution(network, accountClient);
const api    = apiExecution(network, accountClient, apiUrl);

The ExecutionLayer interface

interface ExecutionLayer {
  readonly mode: ExecutionMode;       // "account" | "api" | (custom string)
  readonly account: Address;          // signer address
 
  createPrism(input): Promise<CreatePrismExecutionResult>;
  createPrismWithData(input): Promise<CreatePrismExecutionResult>;
  deletePrism(input): Promise<ExecutionResult>;
 
  writeData(input): Promise<ExecutionResult>;
  modifyData(input): Promise<ExecutionResult>;
  modifyDataIfCurrent(input): Promise<ExecutionResult>;
  deleteData(input): Promise<ExecutionResult>;
 
  batchWriteData(input): Promise<ExecutionResult>;
  batchModifyData(input): Promise<ExecutionResult>;
  batchDeleteData(input): Promise<ExecutionResult>;
}

Inputs

Every input extends MetaOptions = { relayerFee?: bigint; timestamp?: bigint }. These are ignored in direct mode; in API mode they default to timestamp = now, relayerFee = 0n.

modifyDataIfCurrent additionally requires expectedLineageId and expectedVersion. It preserves modifyData's fresh-anchor behavior but reverts if another write changed the observed entry before execution.

InputOwn fields
CreatePrismInputfundAmount: bigint, salt: Bytes32, relayerFundAmount?: bigint
CreatePrismWithDataInputfundAmount: bigint, salt: Bytes32, dataIds: Bytes32[], payloads: Payload[], relayerFundAmount?: bigint
DeletePrismInputcloudId
WriteDataInput / ModifyDataInputcloudId, dataId, payload
DeleteDataInputcloudId, dataId
BatchWriteDataInput / BatchModifyDataInputcloudId, dataIds[], payloads[]
BatchDeleteDataInputcloudId, dataIds[]

ExecutionResult & .wait()

interface ExecutionResult {
  mode: ExecutionMode;
  txHash?: Hex;        // direct: available immediately
  requestId?: string;  // api: available immediately
  wait(options?: { intervalMs?; timeoutMs? }): Promise<ExecutionReceipt>;
}
  • Direct: txHash is available right away; wait() awaits the network confirmation.
  • API: requestId is available right away; wait() polls the API until the relayer succeeds, then returns the txHash. Only the API layer honors intervalMs.
  • createPrism and createPrismWithData return a CreatePrismExecutionResult whose deterministic cloudId is available immediately in both modes (computed locally via computeCloudId in API mode). Their wait() methods confirm settlement and resolve to CreatePrismReceipt { txHash, cloudId } with the same ID.
const created = await client.execution.createPrism({ fundAmount: 10n, salt });
const { cloudId } = created;
const { txHash } = await created.wait();
  • createPrismWithData creates a prism and writes an initial batch of entries in a single atomic operation, one signature, equivalent to createPrism then batchWriteData. In direct mode, fundAmount must cover the creation fee plus the per-entry data fees. In API mode, the combined initial funding (fundAmount + relayerFundAmount) must cover those fees and any nonzero relayerFee. This is what backs PrismManager.create(), which provisions the prism and writes the standard slots in one atomic createPrismWithData transaction.
const created = await client.execution.createPrismWithData({
  fundAmount: 20n,
  salt,
  dataIds,
  payloads,
});
const { cloudId, txHash } = await created.wait();

Fees

FeeUnitPaid byWhen
Network feesnative currencythe user's accountdirect mode
relayerFeeDPCprism balance, to the relayerapi mode
Platform feeDPCprism balanceevery write / modify / delete

Custom layers

ExecutionLayer is the call standard. For a custom path (a different relayer, a multisig, specific constraints), extend the abstract BaseExecutionLayer: it implements the remaining write methods by normalizing each into an ExecutionOperation (a discriminated union) and delegating to a single method you provide.

class MyExecution extends BaseExecutionLayer {
  readonly mode = "my-custom-mode";        // ExecutionMode accepts any string
  get account() { return this.signerAddress; }
  async createPrism(input) { /* … */ }
  protected async execute(op /* ExecutionOperation */) {
    switch (op.kind) {
      case "writeData":      /* … */ break;
      case "batchWriteData": /* … */ break;
      // deletePrism, modifyData, deleteData, batch*
    }
  }
}
 
const client = new DataPrismClient(network, { execution: new MyExecution() });

The ExecutionOperation kinds are: deletePrism, writeData, modifyData, deleteData, batchWriteData, batchModifyData, batchDeleteData (createPrism and createPrismWithData are implemented directly, not via execute).

Signing

The API path signs requests as typed structured data (EIP-712), through DataPrismSigner (exposed as client.signer). The request nonce is read from the network on every signature, so requests cannot be replayed.

MethodRequest type signed
signCreatePrismCreatePrism
signCreatePrismWithDataCreatePrismWithData (dataIdsHash, payloadsHash)
signWriteData / signModifyData / signDeleteDataWriteData / ModifyData / DeleteData
signDeletePrismDeletePrism
signBatchWriteData / signBatchModifyData / signBatchDeleteDatabatch variants (pre-hashed)

All return EIP712Signature = { v, r, s }. Batch types sign over pre-computed hashes; the SDK computes these for you via the exported helpers:

hashBytes32Array(values: Bytes32[]): Bytes32
hashPayloadsArray(payloads: Payload[]): Bytes32

Low-level writers (advanced)

The building blocks under the execution layer, exposed for advanced use and reachable via .raw on each layer:

  • NetworkWriter (direct): each write is simulated first (dry run), then sent. Beyond the interface it also offers direct-only extras: fundPrism, addWriter, updateWriterExpiration, removeWriter. Helper: createNetworkWriterFromPrivateKey(network, privateKey).
  • ApiWriter (api): signs the typed request and POSTs it to the API, returning an ApiRequest (requestId, status). Also: getRequest, listRequests, waitForRequest, and extractCloudId(req). Statuses: pending → processing → success | failed.
Copyright © 2026 DataPrism.