FOUR SDK / V1.0.0

Transport, storage and adapter contracts

ts
import { FourBTDClient, InMemoryTransport, type Transport } from '@fourbtd/sdk';

const delegate = new InMemoryTransport();

const transport: Transport = {
  execute: (request) => delegate.execute(request),
};

const sdk = new FourBTDClient({
  network: 'local',
  transport,
});

Custom Transport

Transport.execute(TransportRequest) → Promise<unknown> receives:

  • Operation

  • Validated input

  • Network

  • Detached context

  • Request ID

  • Idempotency key

  • Expected version

  • AbortSignal

Return raw data matching operations[operation].output, not the HTTP envelope.

Throw typed SDK errors for known failures.

An injected transport takes precedence over built-in transport selection. Configure its storage and identity adapter directly.

In-Memory Transport

ts
new InMemoryTransport({
  storage?,
  clock?,
  idGenerator?,
  identityAdapter?,
});

InMemoryTransport uses MemoryStorage and LocalIdentityAdapter by default.

Storage.transaction(work) must:

  • Atomically serialize work against a StoreState

  • Roll back on failure

  • Detach returned results

The built-in store keeps data and idempotency records in memory without eviction. Share the store to preserve state across multiple clients within the same process.

Durable storage is an application-level injection.

Use a separate transport and store for each network and tenant.

A signer performs an external side effect and must enforce checkpoint-ID idempotency independently of storage rollback.

Identity Adapter

ts
IdentityAdapter.create(input, network)
  → { wallet, publicKey, confirmation }

IdentityAdapter.publish(checkpoint, signal?)
  → Checkpoint

IdentityAdapter.verify(checkpoint, signal?)
  → { valid, reason? }

The local transport owns identity storage, state hashing, and checkpoint history.

An identity adapter must not change checkpoint identity during publication.

HTTP backends own their identity implementation. Passing an identityAdapter cannot inject wallet functionality into a remote server.

Injectable Dependencies

Clock provides:

ts
now(): Date

sleep(
  ms: number,
  signal?: AbortSignal,
): Promise<void>

IdGenerator(kind) must produce unique, non-empty IDs.

random supplies retry jitter.

These dependencies, along with logger and lifecycle hooks, can be injected through ClientOptions for deterministic and reproducible application behavior.