Sign In

@trustless-work/escrow

Package Overview
Dependencies
Maintainers
2
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@trustless-work/escrow

This SDK is a React/TypeScript client for Trustless Work.

Source
npmnpm
Version
5.0.0-beta.0
Version published
Weekly downloads
212
-15.87%
Maintainers
2
Weekly downloads
 
Created
Source

CLR-S (2)

Trustless Work React Library

@trustless-work/escrow v5 — React/TypeScript client for Trustless Work Core API v2 escrows.

SurfaceWhat it does
REST operateBuild unsigned XDR → you sign → sendTransaction
REST readsGET /escrows* (list, detail, events, milestones, financial)
GraphQL readsPOST /graphql (escrow / escrows)

Identity is always contractId (Soroban C…). Types: single-release | multi-release.

Auth, users, platforms, wallets, admin, and access grants are out of scope for this package.

Installation

npm install @trustless-work/escrow@5
# or
yarn add @trustless-work/escrow@5
# or
pnpm add @trustless-work/escrow@5

Peer dependencies: react and react-dom >=18 <20.

If you publish/install under an npm dist-tag (e.g. beta), use that tag instead of @5 so latest can stay on the audited Core v1 line.

Quick Start

"use client";

import {
  development,
  TrustlessWorkConfig,
} from "@trustless-work/escrow";

export function TrustlessWorkProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const apiKey = process.env.NEXT_PUBLIC_API_KEY || "";

  return (
    <TrustlessWorkConfig baseURL={development} apiKey={apiKey}>
      {children}
    </TrustlessWorkConfig>
  );
}

Optional wallet-session auth and default platform header:

<TrustlessWorkConfig
  baseURL={development}
  apiKey={apiKey}
  getAccessToken={() => sessionToken}
  defaultHeaders={{ "X-TW-Platform": platformId }}
>
  {children}
</TrustlessWorkConfig>
PropRole
baseURLCore API host (development / mainNet helpers, or any URL string)
apiKeyx-api-key header
getAccessTokenOptional Bearer token getter (re-read per request)
defaultHeadersMerged into every request (e.g. X-TW-Platform)

Without React

import { TrustlessWorkClient, development } from "@trustless-work/escrow";

const client = new TrustlessWorkClient({
  baseURL: development,
  apiKey: process.env.API_KEY,
});

await client.rest.listEscrows({ scope: "mine", limit: 20 });
await client.graphql.getEscrow({ contractId });

Package entry points

ImportContains
@trustless-work/escrowConfig, client, REST + GraphQL hooks, types, errors
@trustless-work/escrow/restEscrowRestService + REST hooks only
@trustless-work/escrow/graphqlGraphQL service, documents, GraphQL hooks
@trustless-work/escrow/hooksAll hooks (REST + GraphQL)
@trustless-work/escrow/hooks/restREST hooks only
@trustless-work/escrow/hooks/graphqlGraphQL hooks only
@trustless-work/escrow/typesPayloads, responses, read-model, entities
import { useEscrowRest, useEscrowGraphql } from "@trustless-work/escrow";
import { useGraphqlGetEscrow } from "@trustless-work/escrow/hooks/graphql";

const rest = useEscrowRest();       // operate + GET /escrows*
const graphql = useEscrowGraphql(); // POST /graphql
const { getEscrow } = useGraphqlGetEscrow();

Build → sign → send

Every mutate hook returns an unsigned transaction. You sign with the wallet, then submit.

import {
  useDeployEscrow,
  useSendTransaction,
  toTrustlessWorkError,
  TrustlessWorkApiError,
  formatApiErrorMessage,
} from "@trustless-work/escrow";
import type { DeploySingleReleaseEscrowPayload } from "@trustless-work/escrow/types";

const { deployEscrow } = useDeployEscrow();
const { sendTransaction } = useSendTransaction();

const onSubmit = async (payload: DeploySingleReleaseEscrowPayload) => {
  try {
    const { unsignedXdr, contractId } = await deployEscrow(
      payload,
      "single-release",
      // optional attribution → X-TW-Platform / X-TW-Subject:
      // { platformId, subjectId }
    );

    const signedXdr = await signWithWallet(unsignedXdr);
    const result = await sendTransaction(signedXdr);
    // result.txHash, result.ledger, result.contractId?, result.escrow?, result.code?

    if (result.code === "STELLAR_TX_SUBMITTED_INDEXER_LAGGING") {
      // Tx is on-chain; poll GET /escrows/:contractId until the indexer catches up
    }
  } catch (error: unknown) {
    const normalized = toTrustlessWorkError(error);
    if (normalized instanceof TrustlessWorkApiError) {
      console.error(formatApiErrorMessage(normalized), normalized.code);
    }
    throw error;
  }
};

Deploy trustline shape: { contractId, symbol } (Soroban SAC C… + asset code).

Operate responses:

StepTypeShape
Deploy buildDeployEscrowResponse{ unsignedXdr, txHash, contractId }
Other buildsBuildTransactionResponse{ unsignedXdr, txHash }
SubmitSendTransactionResponse{ txHash, ledger, contractId?, escrow?, code?, message? }

code may be STELLAR_TX_SUBMITTED or STELLAR_TX_SUBMITTED_INDEXER_LAGGING.

Operate hooks (REST only)

Import from @trustless-work/escrow, @trustless-work/escrow/hooks, or @trustless-work/escrow/hooks/rest.

HookAction
useDeployEscrowCreate escrow (unsignedXdr + predicted contractId)
useFundEscrowFund
useUpdateEscrowUpdate properties
useChangeMilestoneStatusStatus / evidence (batch)
useApproveMilestonesApprove (batch)
useApproveAndReleaseMilestonesApprove + release (multi-release)
useManageMilestonesAdd / edit milestones
useReleaseFundsRelease
useStartDisputeStart dispute
useResolveDisputeResolve dispute (distributions)
useWithdrawRemainingFundsWithdraw remaining
useSendTransactionSubmit signed XDR (POST /stellar/send-transaction)

Most operate methods take (payload, type) where type is "single-release" | "multi-release". Multi-only helpers (approveAndReleaseMilestones, etc.) omit the type argument.

Reads — REST

HookEndpoint
useListEscrowsGET /escrows
useGetEscrowGET /escrows/:contractId
useGetEscrowDetailsGET /escrows/details?contractIds=
useListEscrowEventsGET /escrows/:contractId/events
useGetEscrowMilestonesGET /escrows/:contractId/milestones
useGetEscrowsMilestonesGET /escrows/milestones?contractIds=
useGetEscrowsFinancialGET /escrows/financial?contractIds=
import { useListEscrows, useGetEscrow } from "@trustless-work/escrow/hooks/rest";
import { useQuery } from "@tanstack/react-query";

const { listEscrows } = useListEscrows();
const { getEscrow } = useGetEscrow();

useQuery({
  queryKey: ["escrows", "mine"],
  queryFn: () => listEscrows({ scope: "mine", limit: 20 }),
});

useQuery({
  queryKey: ["escrow", contractId],
  queryFn: () => getEscrow(contractId),
  enabled: !!contractId,
});

ListEscrowsParams (filters)

scope, status, contractType, engagementId, contractIds, participant, role, platformId, subjectId, createdAfter, createdBefore, limit, cursor, sort, order.

  • Use scope: "mine" | "all" for segmentation (not per-escrow access grants).
  • List/events return a keyset page: { data, hasMore, nextCursor }.

Read-model shapes

TypeNotes
EscrowSummaryList/detail row: contractId, type, status, balance, asset, camelCased snapshot
EscrowDetail{ escrow, events, deposits }
EscrowFinancialBatch financial: deposited / released / pending / balance
EscrowEventIndexed event (kind, topics, payload, …) — no UUID event id
EscrowDepositDeposit row — no UUID deposit id

There is no UUID id / escrowId. Amounts on reads are human decimal strings (e.g. "250.5") — do not divide by 1e7. Build/operate payloads still use human numbers.

Reads — GraphQL

HookQuery
useGraphqlGetEscrowescrow(contractId) + financial / deposits / events
useGraphqlListEscrowsescrows(...) (same filters as REST list)

Documents: GRAPHQL_GET_ESCROW, GRAPHQL_LIST_ESCROWS.

import { useGraphqlGetEscrow, useGraphqlListEscrows } from "@trustless-work/escrow/hooks/graphql";

const { getEscrow } = useGraphqlGetEscrow();
const { listEscrows } = useGraphqlListEscrows();

const detail = await getEscrow({
  contractId,
  eventsLimit: 20,
});

const page = await listEscrows({
  scope: "mine",
  limit: 20,
});

GraphQL wire types (GraphqlEscrow, GraphqlEscrowPage, …) live on @trustless-work/escrow / @trustless-work/escrow/graphql.

Errors

HTTP failures from Core are RFC 9457 Problem Details. The transport surfaces them as TrustlessWorkApiError.

import {
  toTrustlessWorkError,
  TrustlessWorkApiError,
  formatApiErrorMessage,
  ESCROW_ERROR_CODES,
} from "@trustless-work/escrow";

try {
  await fundEscrow(payload, "single-release");
} catch (error: unknown) {
  const err = toTrustlessWorkError(error);
  if (err instanceof TrustlessWorkApiError) {
    // err.code, err.status, err.detail, err.traceId, err.extensions
    toast.error(formatApiErrorMessage(err));
    if (err.code === ESCROW_ERROR_CODES.ESCROW_NOT_FOUND) { /* … */ }
  }
}

Also exported: parseProblemDetails, parseProblemDetailsFromAxiosError, isProblemDetails, isEscrowErrorCode.

Types

Import from @trustless-work/escrow/types (or the root package):

AreaExamples
Operate payloadsDeploySingleReleaseEscrowPayload, FundEscrowPayload, ApproveMilestonesPayload, AttributionHeaders, …
ResponsesDeployEscrowResponse, BuildTransactionResponse, SendTransactionResponse, ListEscrowsResponse, …
ReadsEscrowSummary, EscrowAsset, EscrowSnapshot, EscrowEvent, EscrowFinancial, KeysetPage, …
EntitiesEscrow, Roles, DeployTrustline, SingleReleaseMilestone, …
ErrorsApiProblemDetails, EscrowErrorCode, ESCROW_ERROR_CODES

Environment

development and mainNet currently point at:

https://trustless-core-production.up.railway.app

Pass any Core API baseURL string when you need another host. Get an API key from the Trustless Work dApp.

Migrating from v3 / v4 → v5

Breaking changes aligned with the Core v2 wire contract:

  • Removed legacy helpers: /helper/get-escrows-by-*, /helper/get-escrow-by-contract-ids, /helper/get-multiple-escrow-balance and their hooks.
  • Use useListEscrows / useGetEscrow / useGetEscrowDetails / financial & milestones hooks (or GraphQL equivalents).
  • Escrow identity is contractId only (no UUID).
  • Deploy response includes contractId: { unsignedXdr, txHash, contractId }.
  • Deploy trustline is { contractId, symbol } (Soroban SAC + asset code).
  • Use useDeployEscrow + Deploy*EscrowPayload (no Initialize* aliases).
  • Milestone ops are batch (useApproveMilestones, useChangeMilestoneStatus, useManageMilestones, …).
  • Prefer entry points @trustless-work/escrow/rest and @trustless-work/escrow/graphql instead of mixing surfaces.

Read-model notes

  • EscrowSummary includes root balance (string) and asset { name, address, contractId }.
  • Read amounts (balance, totalAmount, financial fields, snapshot amounts) are decimal strings.
  • createdByUserId / creatorAddress are not on reads (on-chain state is public).
  • Listing uses scope=mine|all — not per-escrow access grants.
  • After submit, handle STELLAR_TX_SUBMITTED_INDEXER_LAGGING and poll reads; balance is eventually consistent.

License

MIT License — see LICENSE file for details.

Keywords

react

FAQs

Package last updated on 16 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts