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 layersThe two built-in layers
| Layer | Who signs / pays fees | How it reaches the network |
|---|---|---|
Direct (AccountExecution, mode: "account") | the user's account (viem); it pays the fees | direct call to the network |
API (ApiExecution, mode: "api") | typed signed request to the DataPrism API; a relayer pays, reimbursed in DPC | delegated 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.
| Input | Own fields |
|---|---|
CreatePrismInput | fundAmount: bigint, salt: Bytes32, relayerFundAmount?: bigint |
CreatePrismWithDataInput | fundAmount: bigint, salt: Bytes32, dataIds: Bytes32[], payloads: Payload[], relayerFundAmount?: bigint |
DeletePrismInput | cloudId |
WriteDataInput / ModifyDataInput | cloudId, dataId, payload |
DeleteDataInput | cloudId, dataId |
BatchWriteDataInput / BatchModifyDataInput | cloudId, dataIds[], payloads[] |
BatchDeleteDataInput | cloudId, dataIds[] |
ExecutionResult & .wait()
interface ExecutionResult {
mode: ExecutionMode;
txHash?: Hex; // direct: available immediately
requestId?: string; // api: available immediately
wait(options?: { intervalMs?; timeoutMs? }): Promise<ExecutionReceipt>;
}- Direct:
txHashis available right away;wait()awaits the network confirmation. - API:
requestIdis available right away;wait()polls the API until the relayer succeeds, then returns thetxHash. Only the API layer honorsintervalMs. createPrismandcreatePrismWithDatareturn aCreatePrismExecutionResultwhose deterministiccloudIdis available immediately in both modes (computed locally viacomputeCloudIdin API mode). Theirwait()methods confirm settlement and resolve toCreatePrismReceipt { txHash, cloudId }with the same ID.
const created = await client.execution.createPrism({ fundAmount: 10n, salt });
const { cloudId } = created;
const { txHash } = await created.wait();createPrismWithDatacreates a prism and writes an initial batch of entries in a single atomic operation, one signature, equivalent tocreatePrismthenbatchWriteData. In direct mode,fundAmountmust 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 nonzerorelayerFee. This is what backsPrismManager.create(), which provisions the prism and writes the standard slots in one atomiccreatePrismWithDatatransaction.
const created = await client.execution.createPrismWithData({
fundAmount: 20n,
salt,
dataIds,
payloads,
});
const { cloudId, txHash } = await created.wait();Fees
| Fee | Unit | Paid by | When |
|---|---|---|---|
| Network fees | native currency | the user's account | direct mode |
relayerFee | DPC | prism balance, to the relayer | api mode |
| Platform fee | DPC | prism balance | every 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.
| Method | Request type signed |
|---|---|
signCreatePrism | CreatePrism |
signCreatePrismWithData | CreatePrismWithData (dataIdsHash, payloadsHash) |
signWriteData / signModifyData / signDeleteData | WriteData / ModifyData / DeleteData |
signDeletePrism | DeletePrism |
signBatchWriteData / signBatchModifyData / signBatchDeleteData | batch 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[]): Bytes32Low-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 anApiRequest(requestId,status). Also:getRequest,listRequests,waitForRequest, andextractCloudId(req). Statuses:pending → processing → success | failed.

