From 453d9400c27027af0c87c8d89d62ad6f10058c7f Mon Sep 17 00:00:00 2001 From: armandocodecr Date: Fri, 11 Sep 2026 10:31:56 -0600 Subject: [PATCH 1/6] document the Core API v2 surface Co-Authored-By: Claude Opus 5 --- README.md | 2 +- SKILL.md | 7 +- skills/api/core-concepts.md | 2 +- skills/api/multi-release-escrow.md | 2 +- skills/api/single-release-escrow.md | 2 +- skills/api/types.md | 2 +- skills/api/v2/core-concepts.md | 229 +++++++++++++++++++++++++++ skills/api/v2/multi-release.md | 218 ++++++++++++++++++++++++++ skills/api/v2/single-release.md | 235 ++++++++++++++++++++++++++++ skills/blocks/components.md | 2 +- skills/blocks/hooks.md | 2 +- skills/blocks/introduction.md | 2 +- skills/blocks/providers.md | 2 +- skills/blocks/vibe-coding.md | 2 +- skills/protocol/v2.md | 2 +- skills/react-sdk/hooks-reference.md | 2 +- skills/react-sdk/react-sdk.md | 2 +- skills/react-sdk/vibe-coding.md | 2 +- 18 files changed, 702 insertions(+), 15 deletions(-) create mode 100644 skills/api/v2/core-concepts.md create mode 100644 skills/api/v2/multi-release.md create mode 100644 skills/api/v2/single-release.md diff --git a/README.md b/README.md index c37a8bd..93b458a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Trustless Work Development Skill -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](skills/api/v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](constitution.md). [View on skills.sh](https://www.skills.sh/trustless-work/trustlesswork-skill) diff --git a/SKILL.md b/SKILL.md index 0a84209..2600d79 100644 --- a/SKILL.md +++ b/SKILL.md @@ -36,7 +36,7 @@ npx skills add trustless-work/trustlesswork-skill Trustless Work has two protocol versions, and they are not interchangeable. - **V1 is production and the default.** It is the only version deployed on mainnet. Build every normal integration from [skills/protocol/v1.md](skills/protocol/v1.md). -- **V2 is beta and testnet-only.** It is deployed on testnet, so you can build on it there — see [skills/protocol/v2.md](skills/protocol/v2.md) — but never on mainnet while it is beta, and never as the recommended production path. Say it is beta every time. +- **V2 is beta and testnet-only.** It is deployed on testnet, so you can build on it there — contract semantics in [skills/protocol/v2.md](skills/protocol/v2.md), REST endpoints in [skills/api/v2/](skills/api/v2/core-concepts.md) — but never on mainnet while it is beta, and never as the recommended production path. Say it is beta every time. - **Explaining V2 is always in scope.** Answer any V2 question; the restriction is which network you target, not whether the user said "beta". - **Never mix them.** Roles, payload shapes, approval semantics and lifecycle rules belong to one version at a time. - **If the version is ambiguous, use V1** and state which version you are describing. @@ -96,6 +96,11 @@ Load these on demand — only when the task requires them: - Read **[skills/api/multi-release-escrow.md](skills/api/multi-release-escrow.md)** when implementing any multi-release endpoint. - Read **[skills/api/trustlines.md](skills/api/trustlines.md)** when trustline errors occur, when setting up new accounts, or when the user asks about tokens/assets. +### REST API — V2 (beta, testnet only) +- Read **[skills/api/v2/core-concepts.md](skills/api/v2/core-concepts.md)** first for the v2 role model, trustline forms, type rules and what changed from V1. +- Read **[skills/api/v2/single-release.md](skills/api/v2/single-release.md)** when implementing any single-release v2 endpoint. +- Read **[skills/api/v2/multi-release.md](skills/api/v2/multi-release.md)** when implementing any multi-release v2 endpoint. + ### React SDK - Read **[skills/react-sdk/react-sdk.md](skills/react-sdk/react-sdk.md)** for SDK setup, provider config, and hook overview. - Read **[skills/react-sdk/hooks-reference.md](skills/react-sdk/hooks-reference.md)** for complete parameter docs and examples of any specific hook (`useInitializeEscrow`, `useFundEscrow`, `useApproveMilestone`, `useReleaseFunds`, `useStartDispute`, `useResolveDispute`, `useUpdateEscrow`, `useChangeMilestoneStatus`, `useWithdrawRemainingFunds`, `useSendTransaction`). diff --git a/skills/api/core-concepts.md b/skills/api/core-concepts.md index afb0e0a..e05c4b8 100644 --- a/skills/api/core-concepts.md +++ b/skills/api/core-concepts.md @@ -1,6 +1,6 @@ # Core Concepts -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). ## Overview diff --git a/skills/api/multi-release-escrow.md b/skills/api/multi-release-escrow.md index c9cd845..92025a0 100644 --- a/skills/api/multi-release-escrow.md +++ b/skills/api/multi-release-escrow.md @@ -1,6 +1,6 @@ # Multi-Release Escrow -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Multi-release escrows release funds incrementally as each milestone is completed and approved. diff --git a/skills/api/single-release-escrow.md b/skills/api/single-release-escrow.md index 3fbc3dc..bb78f35 100644 --- a/skills/api/single-release-escrow.md +++ b/skills/api/single-release-escrow.md @@ -1,6 +1,6 @@ # Single-Release Escrow -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Single-release escrows release all funds in one payment after all milestones are completed and approved. diff --git a/skills/api/types.md b/skills/api/types.md index 6adef8a..f970b2e 100644 --- a/skills/api/types.md +++ b/skills/api/types.md @@ -1,6 +1,6 @@ # TypeScript Types Reference -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Complete TypeScript type definitions for all Trustless Work API payloads, responses, and errors. diff --git a/skills/api/v2/core-concepts.md b/skills/api/v2/core-concepts.md new file mode 100644 index 0000000..c16f9c9 --- /dev/null +++ b/skills/api/v2/core-concepts.md @@ -0,0 +1,229 @@ +# Core API v2 — Concepts + +> **Protocol version: V2 (BETA).** V2 runs on its own host, `https://beta.api.trustlesswork.com`, against testnet. It is **not** on the V1 hosts and not on mainnet. For production use [V1](../core-concepts.md). See [constitution.md](../../../constitution.md) for the version-selection rule. + +Everything here is verified against the Core API source. Shared shapes and rules live in this file; the per-endpoint reference is in [single-release.md](single-release.md) and [multi-release.md](multi-release.md). + +--- + +## Routes are version-scoped + +Every v2 operation lives under a versioned prefix: + +``` +/escrow/single-release/v2/... +/escrow/multi-release/v2/... +``` + +There is no unversioned v2 route. Sending a v2 payload to a v1 route, or the reverse, fails. + +## Base URL + +``` +https://beta.api.trustlesswork.com +``` + +V2 has its **own host**. It is not served from the V1 hosts (`api.trustlesswork.com` for mainnet, `dev.api.trustlesswork.com` for testnet) — pointing a V2 integration at either of those fails. The beta host settles on Stellar testnet. + +## Authentication + +`x-api-key` on **every** request, including reads. Never `Authorization: Bearer`. + +## The write pattern + +Every write endpoint **builds** a transaction; it does not execute one. The response is: + +```json +{ "unsignedXdr": "AAAAAgAAAAB...", "txHash": "..." } +``` + +The field is **`unsignedXdr`**, not `unsignedTransaction`. `deploy` returns the same shape plus the predicted `contractId`: + +```json +{ "unsignedXdr": "...", "txHash": "...", "contractId": "C..." } +``` + +Three steps, always: + +1. **Build** — call the endpoint, receive the unsigned XDR. +2. **Sign** — sign client-side with the wallet the operation requires. Each endpoint below names that signer. +3. **Submit** — `POST /stellar/send-transaction` with the signed XDR. + +> The submit route differs from V1, which documents `/helper/send-transaction`. In the v2 API there is no `helper` controller. + +Nothing reaches the chain until step 3. + +The submit response carries a machine-readable `code`: + +| Code | Meaning | +| --- | --- | +| `STELLAR_TX_SUBMITTED` | Submitted and indexed. | +| `STELLAR_TX_SUBMITTED_INDEXER_LAGGING` | **Submitted successfully**, but the read model has not caught up. Not an error — do not retry the transaction. Re-read after a moment. | + +It also returns `txHash` and `ledger`, plus `contractId` and the `escrow` object when the transaction was a deploy. + +--- + +## Shared objects + +### `roles` + +The v2 role model. Most roles are **arrays** of up to 5 unique addresses; `platform`, `admin` and `receiver` are single addresses. + +```jsonc +{ + "approvers": ["G..."], // 1-5, unique. Approve milestones + "serviceProviders": ["G..."], // 1-5, unique. Change milestone status + "releaseSigners": ["G..."], // 1-5, unique. Release funds + "disputeResolvers": ["G..."], // 1-5, unique. Resolve disputes + "platform": "G...", // single. Receives the platform fee + "admin": "G...", // single. update, manage-milestones, extend-ttl + "receiver": "G...", // single. SINGLE-RELEASE ONLY + "observers": [] // optional, read-only, no on-chain authority +} +``` + +**Multi-release has no `receiver` in `roles`** — each milestone carries its own. + +Composition rules the API and contract reject: + +- Duplicates inside a role array, or more than 5 entries. +- `disputeResolvers` overlapping any other role, including `platform` and any milestone receiver. +- `admin` overlapping any other role, including a milestone receiver. + +`admin` and `platform` **may** be the same address: that pair is a capability distinction, not an address-separation rule. + +### `trustline` + +v2 accepts **either** form. This differs from v1, which takes the issuer address only. + +```jsonc +// Form A — Soroban token contract +{ "contractId": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA" } + +// Form B — issuer + symbol, resolved to the contract by the API +{ "symbol": "USDC", "address": "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN" } +``` + +Supply `contractId`, or supply both `symbol` and `address`. When `contractId` is present the other two are ignored. + +### `milestone` + +**Single-release** — a tracking unit. It holds no funds and no receiver. + +```jsonc +{ + "description": "Phase 1 — UI design delivery", // required + "status": "pending", // optional, defaults to "pending" + "approvalsTarget": 1 // optional, defaults to 1 +} +``` + +**Multi-release** — independently funded and released, so it adds its own amount and receiver. + +```jsonc +{ + "description": "Phase 1 — UI design delivery", // required + "amount": 500, // required, > 0 + "receiver": "G...", // required + "status": "pending", // optional + "approvalsTarget": 1 // optional, defaults to 1 +} +``` + +`approvalsTarget` must be `> 0` and `<= roles.approvers.length`. It is how many **distinct** approvers must approve before the milestone counts as approved. A target of 1 behaves like v1's boolean, but the field is always a threshold. + +--- + +## Type rules + +| Field | Type | Note | +| --- | --- | --- | +| `amount` (operate payloads) | `number` | Human-readable decimals (`1000`, not `"1000"`). | +| `amount` (read responses) | `string` | Reads return **decimal strings**. Do not assume the type is symmetric with what you send. | +| `platformFee` | `number` | **Percent**, integer 0–100 (`1` means 1%). Scaled to basis points on-chain. | +| `milestoneIndexes` | `number[]` | **Numbers, not strings.** v1's `milestoneIndex` was a string; v2 takes an array of numbers. | +| `approvalsTarget` | `number` | Integer ≥ 1. | +| `ledgersToExtend` | `number` | Integer. | +| `receiverMemo` | `number` | Optional, u32 on-chain. | +| Addresses | `string` | `G…` accounts, `C…` contracts. | + +## Field limits at deploy + +| Field | Limit | +| --- | --- | +| `engagementId` | 100 chars | +| `title` | 200 chars | +| `description` | 2000 chars | +| `milestones` | max 50 entries | + +> These differ from v1, which caps `title` at 100 and `description` at 500. Do not reuse v1 limits. + +--- + +## What changed from v1 + +| | v1 | v2 | +| --- | --- | --- | +| Route prefix | unversioned | `/v2/` required | +| Roles | one address each | arrays of up to 5, plus `admin` and `observers` | +| Approval | boolean per milestone | threshold (`approvalsTarget`) | +| Milestone index | `"0"` string, one at a time | `[0, 1]` numbers, batched | +| Trustline | issuer address only | `contractId` **or** issuer + symbol | +| Milestones at deploy | at least 1 required | **optional** — deploy empty, add later | +| Batch operations | none | approve, approve-and-release, dispute, release | +| Escrow update | `platformAddress` | `admin`, via `PUT /update` | + +--- + +## Endpoint index + +Both variants expose the same 14 operations. Names differ only for disputes. + +| Operation | Method | Path suffix | Signer | +| --- | --- | --- | --- | +| Deploy | POST | `/deploy` | `signer` | +| Fund | POST | `/fund` | `signer` (any depositor) | +| Update properties | PUT | `/update` | `admin` | +| Manage milestones | POST | `/manage-milestones` | `admin` | +| Change milestone status | POST | `/change-milestone-status` | one of `serviceProviders` | +| Approve milestones | POST | `/approve-milestones` | one of `approvers` | +| Approve and release | POST | `/approve-and-release-milestones` | approver + release signer | +| Release funds | POST | `/release-funds` | one of `releaseSigners` | +| Raise dispute | POST | `/dispute` (single) · `/dispute-milestones` (multi) | see endpoint | +| Resolve dispute | POST | `/resolve-dispute` | one of `disputeResolvers` | +| Withdraw remaining | POST | `/withdraw-remaining-funds` | one of `disputeResolvers` | +| Extend TTL | POST | `/extend-ttl` | `admin` | +| Get escrow | GET | `/:contractId` | — | +| Get balances | GET | `/escrow-balances` | — | + +--- + +## Two read surfaces — do not confuse them + +Reads exist in two places, and **both SDKs use the second one**. + +**1. On the v2 transaction controllers** — version- and type-scoped: + +``` +GET /escrow/{single-release|multi-release}/v2/:contractId +GET /escrow/{single-release|multi-release}/v2/escrow-balances +``` + +**2. The read model** — a separate controller that is **neither version-scoped nor type-scoped**: + +``` +GET /escrows # keyset list, filterable +GET /escrows/:contractId # one escrow +GET /escrows/:contractId/events # event history +GET /escrows/:contractId/milestones # milestones for one escrow +GET /escrows/details # batch detail lookup +GET /escrows/financial # batch financial summary +GET /escrows/milestones # batch milestones +``` + +There is no `/v2/` in these paths. `@trustless-work/escrow` 5.x and `@trustless-work/escrow-js` route every read method here — `getEscrow`, `listEscrows`, `getEscrowDetails`, `listEscrowEvents`, `getEscrowMilestones`, `getEscrowsMilestones`, `getEscrowsFinancial`. + +So if you are reading through an SDK you are on surface 2, and adding `/v2/` to those paths is wrong. Surface 1 is reached only by calling the transaction controller directly. + +**Read amounts come back as decimal strings**, unlike the numbers you send in operate payloads. diff --git a/skills/api/v2/multi-release.md b/skills/api/v2/multi-release.md new file mode 100644 index 0000000..cec51c8 --- /dev/null +++ b/skills/api/v2/multi-release.md @@ -0,0 +1,218 @@ +# Core API v2 — Multi-Release Escrow + +> **Protocol version: V2 (BETA), testnet only.** Read [core-concepts.md](core-concepts.md) first for the shared `roles`, `trustline` and `milestone` shapes, the type rules, and the build/sign/submit pattern. + +Base path: `/escrow/multi-release/v2` + +Multi-release pays out **per milestone**. Each milestone carries its own `amount` and `receiver`, and is approved, released and disputed independently. + +## The three differences from single-release + +1. **No top-level `amount`** on the escrow, and **no `receiver` in `roles`**. Both live on each milestone. +2. **Operations take `milestoneIndexes`.** Release and dispute are scoped to the milestones you name, not the whole escrow. +3. **Disputes are per milestone**, so the endpoint is `/dispute-milestones`, not `/dispute`. + +Everything else — auth, the build/sign/submit pattern, role composition rules, trustline forms, field limits — matches [core-concepts.md](core-concepts.md). + +--- + +## Deploy + +`POST /escrow/multi-release/v2/deploy` — signed by `signer` + +```jsonc +{ + "signer": "G...", + "engagementId": "ENG-12345", + "title": "Grant programme — cohort 3", + "description": "...", + "roles": { /* no `receiver` here */ }, + "platformFee": 1, // no top-level `amount` + "trustline": { "symbol": "USDC", "address": "G..." }, + "milestones": [ // OPTIONAL — may be empty or absent + { "description": "Tranche 1", "amount": 500, "receiver": "G...", "approvalsTarget": 1 }, + { "description": "Tranche 2", "amount": 500, "receiver": "G...", "approvalsTarget": 2 } + ], + "receiverMemo": 0 +} +``` + +Each milestone needs `description`, `amount` (> 0) and `receiver`. Max 50. + +## Fund + +`POST /escrow/multi-release/v2/fund` — signed by `signer` + +```jsonc +{ "contractId": "C...", "signer": "G...", "amount": 1000 } +``` + +Fund the escrow as a whole. The canonical target is the sum of the milestone amounts. + +## Update properties + +`PUT /escrow/multi-release/v2/update` — signed by `admin` + +```jsonc +{ + "contractId": "C...", + "admin": "G...", + "escrow": { + "engagementId": "ENG-12345", + "title": "...", + "description": "...", + "platformFee": 1, + "receiverMemo": 0, + "roles": { /* full roles object, no receiver */ }, + "milestones": [ /* full milestones array */ ], + "trustline": { /* trustline object */ } + } +} +``` + +Complete desired state, not a patch. `admin` only, and only while the balance is zero. + +## Manage milestones + +`POST /escrow/multi-release/v2/manage-milestones` — signed by `admin` + +```jsonc +{ + "contractId": "C...", + "admin": "G...", + "newMilestones": [ + { "description": "Tranche 3", "amount": 250, "receiver": "G...", "approvalsTarget": 1 } + ], + "milestoneUpdates": [ + { "index": 0, "newDescription": "Tranche 1 — revised", "newAmount": 600 } + ] +} +``` + +Multi-release updates can change a milestone's **amount** as well as its description — single-release cannot, since the amount lives on the escrow. Amount changes are rejected once the contract holds funds. + +A new milestone's `receiver` must not be the `admin` or any `disputeResolver`. The contract rejects that configuration. + +## Change milestone status + +`POST /escrow/multi-release/v2/change-milestone-status` — signed by one of `serviceProviders` + +```jsonc +{ + "contractId": "C...", + "serviceProvider": "G...", + "updates": [ { "index": 0, "newStatus": "completed", "newEvidence": "https://..." } ] +} +``` + +## Approve milestones + +`POST /escrow/multi-release/v2/approve-milestones` — signed by one of `approvers` + +```jsonc +{ "contractId": "C...", "approver": "G...", "milestoneIndexes": [0, 1] } +``` + +One approver's vote on each listed milestone; approved once distinct approvals reach that milestone's `approvalsTarget`. + +## Approve and release + +`POST /escrow/multi-release/v2/approve-and-release-milestones` — signed by an address holding both roles + +```jsonc +{ "contractId": "C...", "signer": "G...", "milestoneIndexes": [0] } +``` + +## Release funds + +`POST /escrow/multi-release/v2/release-funds` — signed by one of `releaseSigners` + +```jsonc +{ "contractId": "C...", "releaseSigner": "G...", "milestoneIndexes": [0, 1] } +``` + +Releases **only** the named milestones — unlike single-release, which pays the whole escrow at once and requires every milestone approved. Each listed milestone must be approved, not already released, and not disputed. Each pays its own `amount` minus fees to its own `receiver`. + +## Raise a dispute + +`POST /escrow/multi-release/v2/dispute-milestones` — signed by `signer` + +```jsonc +{ + "contractId": "C...", + "signer": "G...", + "milestoneIndexes": [1], + "reason": "Tranche 2 deliverable incomplete" +} +``` + +Disputes specific milestones, leaving the rest of the escrow operative. Approvers, service providers, release signers, the platform and that milestone's receiver may raise one; a `disputeResolver` is rejected. + +## Resolve a dispute + +`POST /escrow/multi-release/v2/resolve-dispute` — signed by one of `disputeResolvers` + +```jsonc +{ + "contractId": "C...", + "disputeResolver": "G...", + "milestoneIndexes": [1], + "distributions": [ + { "address": "G...", "amount": 300 }, + { "address": "G...", "amount": 200 } + ] +} +``` + +Scoped to the named milestones. Distributions must be positive, must not exceed those milestones' amounts, and must not exceed the contract balance. They do **not** have to equal the full escrow balance — that rule is single-release only. Resolution is terminal. + +## Withdraw remaining funds + +`POST /escrow/multi-release/v2/withdraw-remaining-funds` — signed by one of `disputeResolvers` + +```jsonc +{ + "contractId": "C...", + "disputeResolver": "G...", + "distributions": [ { "address": "G...", "amount": 25 } ] +} +``` + +Requires **every** milestone to be terminal — released or its dispute resolved. Fee-bearing, same as single-release. + +## Extend TTL + +`POST /escrow/multi-release/v2/extend-ttl` — signed by `admin` + +```jsonc +{ "contractId": "C...", "admin": "G...", "ledgersToExtend": 100000 } +``` + +--- + +## Reads + +`GET /escrow/multi-release/v2/:contractId` + +Same shape as single-release except: no top-level `amount`, no `roles.receiver`, and each milestone carries `amount`, `receiver`, `released` and its own `dispute` object. + +```jsonc +{ + "type": "multi-release", + "contractId": "C...", + "milestones": [ + { + "description": "Tranche 1", + "amount": 500, + "receiver": "G...", + "status": "completed", + "evidence": "https://...", + "approvals": { "target": 2, "approvalCount": 2, "approvedBy": ["G...", "G..."] }, + "released": true, + "dispute": { "isDisputed": false, "reason": "", "resolved": false } + } + ] +} +``` + +`GET /escrow/multi-release/v2/escrow-balances?addresses=C...&addresses=C...` diff --git a/skills/api/v2/single-release.md b/skills/api/v2/single-release.md new file mode 100644 index 0000000..b09b759 --- /dev/null +++ b/skills/api/v2/single-release.md @@ -0,0 +1,235 @@ +# Core API v2 — Single-Release Escrow + +> **Protocol version: V2 (BETA), testnet only.** Read [core-concepts.md](core-concepts.md) first for the shared `roles`, `trustline` and `milestone` shapes, the type rules, and the build/sign/submit pattern. + +Base path: `/escrow/single-release/v2` + +Single-release pays out **once**, to a single `roles.receiver`, after the required milestones are approved. Every write endpoint returns `{ "unsignedTransaction": "..." }`. + +--- + +## Deploy + +`POST /escrow/single-release/v2/deploy` — signed by `signer` + +```jsonc +{ + "signer": "G...", // signs the deploy; gains no escrow role + "engagementId": "ENG-12345", // your reference, max 100 chars, NOT globally unique + "title": "Website redesign — Q2 2026", // max 200 + "description": "...", // max 2000 + "roles": { /* see core-concepts */ }, + "amount": 1000, // number, > 0. Total escrow amount + "platformFee": 1, // percent, 0-100 integer + "trustline": { "symbol": "USDC", "address": "G..." }, + "milestones": [ // OPTIONAL in v2 — may be empty or absent + { "description": "Phase 1", "approvalsTarget": 1 } + ], + "receiverMemo": 0 // optional +} +``` + +Deploying with no milestones is valid; add them later with `manage-milestones`. Max 50 when present. + +## Fund + +`POST /escrow/single-release/v2/fund` — signed by `signer` + +```jsonc +{ "contractId": "C...", "signer": "G...", "amount": 1000 } +``` + +Any address holding the asset may fund. Funding grants no role. The signer needs the asset's trustline. + +## Update properties + +`PUT /escrow/single-release/v2/update` — signed by `admin` + +```jsonc +{ + "contractId": "C...", + "admin": "G...", + "escrow": { + "engagementId": "ENG-12345", + "title": "...", + "description": "...", + "amount": 1000, + "platformFee": 1, + "receiverMemo": 0, + "roles": { /* full roles object */ }, + "milestones": [ /* full milestones array */ ], + "trustline": { /* trustline object */ } + } +} +``` + +The `escrow` object is the **complete** desired state, not a patch. Only `admin` may call it, and only while the contract balance is zero — once funded, properties and roles are frozen. + +## Manage milestones + +`POST /escrow/single-release/v2/manage-milestones` — signed by `admin` + +```jsonc +{ + "contractId": "C...", + "admin": "G...", + "newMilestones": [ + { "description": "Phase 2", "approvalsTarget": 2 } + ], + "milestoneUpdates": [ + { "index": 0, "newDescription": "Phase 1 — revised" } + ] +} +``` + +Appends new milestones and edits existing descriptions. The receiver of a new milestone must not be the `admin` or any `disputeResolver`. + +## Change milestone status + +`POST /escrow/single-release/v2/change-milestone-status` — signed by one of `serviceProviders` + +```jsonc +{ + "contractId": "C...", + "serviceProvider": "G...", + "updates": [ + { "index": 0, "newStatus": "completed", "newEvidence": "https://..." } + ] +} +``` + +Batched. Status is free text and moves no funds; convention is `pending → in_progress → completed`. `newEvidence` is optional. + +## Approve milestones + +`POST /escrow/single-release/v2/approve-milestones` — signed by one of `approvers` + +```jsonc +{ + "contractId": "C...", + "approver": "G...", + "milestoneIndexes": [0, 1] +} +``` + +Casts **one** approver's vote on each listed milestone. A milestone counts as approved once distinct approvals reach its `approvalsTarget`. An approver cannot vote twice, and approval is irreversible. + +## Approve and release + +`POST /escrow/single-release/v2/approve-and-release-milestones` — signed by an address that is **both** an approver and a release signer + +```jsonc +{ + "contractId": "C...", + "signer": "G...", + "milestoneIndexes": [0, 1] +} +``` + +Collapses approval and release into one transaction. Use it only when a single wallet legitimately holds both roles. + +## Release funds + +`POST /escrow/single-release/v2/release-funds` — signed by one of `releaseSigners` + +```jsonc +{ "contractId": "C...", "releaseSigner": "G..." } +``` + +Requires **all** milestones approved and no active dispute. Pays the configured `amount` minus the platform fee and the Trustless Work protocol fee to `roles.receiver`. The contract balance must cover it. + +## Raise a dispute + +`POST /escrow/single-release/v2/dispute` — signed by `signer` + +```jsonc +{ "contractId": "C...", "signer": "G...", "reason": "Deliverable does not match scope" } +``` + +Disputes the **whole escrow**. `reason` is required in v2 (v1 had none). Approvers, service providers, release signers, the platform and the receiver may raise one; a `disputeResolver` is rejected. + +## Resolve a dispute + +`POST /escrow/single-release/v2/resolve-dispute` — signed by one of `disputeResolvers` + +```jsonc +{ + "contractId": "C...", + "disputeResolver": "G...", + "distributions": [ + { "address": "G...", "amount": 600 }, + { "address": "G...", "amount": 400 } + ] +} +``` + +Max 50 entries, every amount positive. Every recipient needs the asset's trustline. Resolution is terminal — a dispute cannot be reopened. + +## Withdraw remaining funds + +`POST /escrow/single-release/v2/withdraw-remaining-funds` — signed by one of `disputeResolvers` + +```jsonc +{ + "contractId": "C...", + "disputeResolver": "G...", + "distributions": [ { "address": "G...", "amount": 25 } ] +} +``` + +Sweeps leftover balance — overfunding, stray transfers, rounding dust. In v2 the escrow must be genuinely terminal: `released` or the dispute `resolved`. An open dispute does **not** qualify (it did in v1). + +It is fee-bearing: each recipient's amount is reduced pro rata by the platform and protocol fees, and recipients receive the net. + +## Extend TTL + +`POST /escrow/single-release/v2/extend-ttl` — signed by `admin` + +```jsonc +{ "contractId": "C...", "admin": "G...", "ledgersToExtend": 100000 } +``` + +Extends Soroban storage so the escrow does not expire and get archived. + +--- + +## Reads + +`GET /escrow/single-release/v2/:contractId` + +Returns the escrow. Note the v2 shape: no `flags` object, `approvals` instead of a boolean, role arrays. + +```jsonc +{ + "type": "single-release", + "contractId": "C...", + "contractBaseId": "C...", + "engagementId": "ENG-12345", + "title": "...", + "description": "...", + "amount": 1000, + "balance": 1000, + "platformFee": 1, + "receiverMemo": 0, + "trustline": { "address": "G...", "contractId": "C...", "symbol": "USDC" }, + "roles": { + "approvers": ["G..."], "serviceProviders": ["G..."], "platform": "G...", + "releaseSigners": ["G..."], "disputeResolvers": ["G..."], + "receiver": "G...", "admin": "G...", "observers": [] + }, + "milestones": [ + { + "description": "Phase 1", + "status": "completed", + "evidence": "https://...", + "approvals": { "target": 2, "approvalCount": 1, "approvedBy": ["G..."] } + } + ], + "dispute": { "isDisputed": false, "reason": "", "resolved": false }, + "released": false +} +``` + +`GET /escrow/single-release/v2/escrow-balances?addresses=C...&addresses=C...` + +Batch balance lookup for several escrow contracts. diff --git a/skills/blocks/components.md b/skills/blocks/components.md index b769e46..f827777 100644 --- a/skills/blocks/components.md +++ b/skills/blocks/components.md @@ -1,6 +1,6 @@ # Blocks Components -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Pre-built UI components for escrow management interfaces. All components can be installed via CLI and are fully customizable. diff --git a/skills/blocks/hooks.md b/skills/blocks/hooks.md index 4393f17..07a47dd 100644 --- a/skills/blocks/hooks.md +++ b/skills/blocks/hooks.md @@ -1,6 +1,6 @@ # Blocks TanStack Query Hooks -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). TanStack Query hooks for fetching and mutating escrows. diff --git a/skills/blocks/introduction.md b/skills/blocks/introduction.md index 48c0261..e0d6bc9 100644 --- a/skills/blocks/introduction.md +++ b/skills/blocks/introduction.md @@ -1,6 +1,6 @@ # Blocks SDK Introduction -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). The Trustless Work Blocks SDK (`@trustless-work/blocks`) provides pre-built UI components, providers, and TanStack Query hooks for quickly building escrow management interfaces. diff --git a/skills/blocks/providers.md b/skills/blocks/providers.md index d3f9647..7872c51 100644 --- a/skills/blocks/providers.md +++ b/skills/blocks/providers.md @@ -1,6 +1,6 @@ # Blocks Providers -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Providers for escrow state, wallet context, dialogs, and amount formatting. **Provider order is critical** — do not reorder. diff --git a/skills/blocks/vibe-coding.md b/skills/blocks/vibe-coding.md index a73cc29..604a25d 100644 --- a/skills/blocks/vibe-coding.md +++ b/skills/blocks/vibe-coding.md @@ -1,6 +1,6 @@ # Vibe Coding Guide -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Single-file AI context guide for Trustless Work Escrow Blocks with Next.js. Built for agent workflows. diff --git a/skills/protocol/v2.md b/skills/protocol/v2.md index c82eb83..12b4605 100644 --- a/skills/protocol/v2.md +++ b/skills/protocol/v2.md @@ -2,7 +2,7 @@ > **BETA — deployed on testnet only.** V2 runs on testnet and you can build against it there. It is **not deployed on mainnet**: never target mainnet with V2 and never recommend it as the production path while it is beta. V1 remains the production default. Explaining V2 is always in scope, but **label every V2 answer as beta**, and never mix its roles, fields or lifecycle rules into a V1 answer. -Read [constitution.md](../../constitution.md) first for the universal laws and the tag meanings. For the production profile see [v1.md](v1.md). +Read [constitution.md](../../constitution.md) first for the universal laws and the tag meanings. For the production profile see [v1.md](v1.md). For the v2 REST endpoints and payloads see [../api/v2/core-concepts.md](../api/v2/core-concepts.md). **Deployment:** testnet only. V2 is live on testnet and absent from mainnet. diff --git a/skills/react-sdk/hooks-reference.md b/skills/react-sdk/hooks-reference.md index 550e466..f07534c 100644 --- a/skills/react-sdk/hooks-reference.md +++ b/skills/react-sdk/hooks-reference.md @@ -1,6 +1,6 @@ # React SDK Hooks Reference -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Complete reference for all Trustless Work React SDK hooks with detailed usage examples. diff --git a/skills/react-sdk/react-sdk.md b/skills/react-sdk/react-sdk.md index 2320223..e52cb5e 100644 --- a/skills/react-sdk/react-sdk.md +++ b/skills/react-sdk/react-sdk.md @@ -1,6 +1,6 @@ # React SDK -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). The Trustless Work React SDK (`@trustless-work/escrow`) provides custom hooks for integrating escrow functionality into React/Next.js applications. It uses Axios for HTTP requests. diff --git a/skills/react-sdk/vibe-coding.md b/skills/react-sdk/vibe-coding.md index 935ec4f..74226da 100644 --- a/skills/react-sdk/vibe-coding.md +++ b/skills/react-sdk/vibe-coding.md @@ -1,6 +1,6 @@ # Vibe Coding Guide -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 API, React SDK and Blocks surfaces are **not** documented in this skill yet; for the contract-level differences between V1 and V2 see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). In all of your chat prompts, you should provide the agent with specific document references, including the global content rules and the complete code project. ***GPT 5 gives the best result*** From 3afcba20d9af0e8bddba5f03b458b2b5287b4117 Mon Sep 17 00:00:00 2001 From: armandocodecr Date: Fri, 11 Sep 2026 10:32:49 -0600 Subject: [PATCH 2/6] document the React and JavaScript SDKs for v2 Co-Authored-By: Claude Opus 5 --- README.md | 2 +- SKILL.md | 8 +- skills/api/core-concepts.md | 2 +- skills/api/multi-release-escrow.md | 2 +- skills/api/single-release-escrow.md | 2 +- skills/api/types.md | 2 +- skills/blocks/components.md | 2 +- skills/blocks/hooks.md | 2 +- skills/blocks/introduction.md | 2 +- skills/blocks/providers.md | 2 +- skills/blocks/vibe-coding.md | 2 +- skills/js-sdk/js-sdk.md | 174 +++++++++++++++++++++++++ skills/react-sdk/hooks-reference.md | 2 +- skills/react-sdk/react-sdk.md | 2 +- skills/react-sdk/v2/react-sdk.md | 194 ++++++++++++++++++++++++++++ skills/react-sdk/vibe-coding.md | 2 +- 16 files changed, 388 insertions(+), 14 deletions(-) create mode 100644 skills/js-sdk/js-sdk.md create mode 100644 skills/react-sdk/v2/react-sdk.md diff --git a/README.md b/README.md index 93b458a..e60d323 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Trustless Work Development Skill -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](skills/api/v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](skills/api/v2/); the V2 React SDK is documented in [skills/react-sdk/v2/](skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](constitution.md). [View on skills.sh](https://www.skills.sh/trustless-work/trustlesswork-skill) diff --git a/SKILL.md b/SKILL.md index 2600d79..c95f53e 100644 --- a/SKILL.md +++ b/SKILL.md @@ -36,7 +36,7 @@ npx skills add trustless-work/trustlesswork-skill Trustless Work has two protocol versions, and they are not interchangeable. - **V1 is production and the default.** It is the only version deployed on mainnet. Build every normal integration from [skills/protocol/v1.md](skills/protocol/v1.md). -- **V2 is beta and testnet-only.** It is deployed on testnet, so you can build on it there — contract semantics in [skills/protocol/v2.md](skills/protocol/v2.md), REST endpoints in [skills/api/v2/](skills/api/v2/core-concepts.md) — but never on mainnet while it is beta, and never as the recommended production path. Say it is beta every time. +- **V2 is beta and testnet-only.** It is deployed on testnet, so you can build on it there — contract semantics in [skills/protocol/v2.md](skills/protocol/v2.md), REST endpoints in [skills/api/v2/](skills/api/v2/core-concepts.md), React SDK in [skills/react-sdk/v2/](skills/react-sdk/v2/react-sdk.md), JS SDK in [skills/js-sdk/](skills/js-sdk/js-sdk.md) — but never on mainnet while it is beta, and never as the recommended production path. Say it is beta every time. - **Explaining V2 is always in scope.** Answer any V2 question; the restriction is which network you target, not whether the user said "beta". - **Never mix them.** Roles, payload shapes, approval semantics and lifecycle rules belong to one version at a time. - **If the version is ambiguous, use V1** and state which version you are describing. @@ -101,6 +101,12 @@ Load these on demand — only when the task requires them: - Read **[skills/api/v2/single-release.md](skills/api/v2/single-release.md)** when implementing any single-release v2 endpoint. - Read **[skills/api/v2/multi-release.md](skills/api/v2/multi-release.md)** when implementing any multi-release v2 endpoint. +### React SDK — V2 (beta, testnet only) +- Read **[skills/react-sdk/v2/react-sdk.md](skills/react-sdk/v2/react-sdk.md)** when integrating `@trustless-work/escrow` 5.x: provider setup, the build/sign/submit loop, every hook, and the new GraphQL hooks. + +### JavaScript SDK — V2 (beta) +- Read **[skills/js-sdk/js-sdk.md](skills/js-sdk/js-sdk.md)** when the integration is **not** React — Node, NestJS, Angular, browser, scripts. `@trustless-work/escrow-js` is framework-agnostic, zero-dependency, and V2-only. + ### React SDK - Read **[skills/react-sdk/react-sdk.md](skills/react-sdk/react-sdk.md)** for SDK setup, provider config, and hook overview. - Read **[skills/react-sdk/hooks-reference.md](skills/react-sdk/hooks-reference.md)** for complete parameter docs and examples of any specific hook (`useInitializeEscrow`, `useFundEscrow`, `useApproveMilestone`, `useReleaseFunds`, `useStartDispute`, `useResolveDispute`, `useUpdateEscrow`, `useChangeMilestoneStatus`, `useWithdrawRemainingFunds`, `useSendTransaction`). diff --git a/skills/api/core-concepts.md b/skills/api/core-concepts.md index e05c4b8..c58ebf9 100644 --- a/skills/api/core-concepts.md +++ b/skills/api/core-concepts.md @@ -1,6 +1,6 @@ # Core Concepts -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). ## Overview diff --git a/skills/api/multi-release-escrow.md b/skills/api/multi-release-escrow.md index 92025a0..78fae83 100644 --- a/skills/api/multi-release-escrow.md +++ b/skills/api/multi-release-escrow.md @@ -1,6 +1,6 @@ # Multi-Release Escrow -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Multi-release escrows release funds incrementally as each milestone is completed and approved. diff --git a/skills/api/single-release-escrow.md b/skills/api/single-release-escrow.md index bb78f35..19e2657 100644 --- a/skills/api/single-release-escrow.md +++ b/skills/api/single-release-escrow.md @@ -1,6 +1,6 @@ # Single-Release Escrow -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Single-release escrows release all funds in one payment after all milestones are completed and approved. diff --git a/skills/api/types.md b/skills/api/types.md index f970b2e..f6435c6 100644 --- a/skills/api/types.md +++ b/skills/api/types.md @@ -1,6 +1,6 @@ # TypeScript Types Reference -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented separately in [skills/api/v2/](v2/); the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Complete TypeScript type definitions for all Trustless Work API payloads, responses, and errors. diff --git a/skills/blocks/components.md b/skills/blocks/components.md index f827777..009192b 100644 --- a/skills/blocks/components.md +++ b/skills/blocks/components.md @@ -1,6 +1,6 @@ # Blocks Components -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Pre-built UI components for escrow management interfaces. All components can be installed via CLI and are fully customizable. diff --git a/skills/blocks/hooks.md b/skills/blocks/hooks.md index 07a47dd..4dd5daa 100644 --- a/skills/blocks/hooks.md +++ b/skills/blocks/hooks.md @@ -1,6 +1,6 @@ # Blocks TanStack Query Hooks -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). TanStack Query hooks for fetching and mutating escrows. diff --git a/skills/blocks/introduction.md b/skills/blocks/introduction.md index e0d6bc9..ac8a16e 100644 --- a/skills/blocks/introduction.md +++ b/skills/blocks/introduction.md @@ -1,6 +1,6 @@ # Blocks SDK Introduction -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). The Trustless Work Blocks SDK (`@trustless-work/blocks`) provides pre-built UI components, providers, and TanStack Query hooks for quickly building escrow management interfaces. diff --git a/skills/blocks/providers.md b/skills/blocks/providers.md index 7872c51..e35600b 100644 --- a/skills/blocks/providers.md +++ b/skills/blocks/providers.md @@ -1,6 +1,6 @@ # Blocks Providers -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Providers for escrow state, wallet context, dialogs, and amount formatting. **Provider order is critical** — do not reorder. diff --git a/skills/blocks/vibe-coding.md b/skills/blocks/vibe-coding.md index 604a25d..b07cc71 100644 --- a/skills/blocks/vibe-coding.md +++ b/skills/blocks/vibe-coding.md @@ -1,6 +1,6 @@ # Vibe Coding Guide -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Single-file AI context guide for Trustless Work Escrow Blocks with Next.js. Built for agent workflows. diff --git a/skills/js-sdk/js-sdk.md b/skills/js-sdk/js-sdk.md new file mode 100644 index 0000000..5e543c5 --- /dev/null +++ b/skills/js-sdk/js-sdk.md @@ -0,0 +1,174 @@ +# JavaScript SDK v2 — `@trustless-work/escrow-js` + +> **Protocol version: V2 (BETA).** Framework-agnostic client over Core API v2, served from `https://beta.api.trustlesswork.com`. There is no V1 equivalent of this package. For React use [@trustless-work/escrow 5.x](../react-sdk/v2/react-sdk.md); for the REST endpoints and payload shapes see [skills/api/v2/](../api/v2/core-concepts.md). + +Use this SDK outside React: Node services, NestJS, Angular, plain browser, scripts, tests, edge runtimes. + +--- + +## Install + +```bash +npm i @trustless-work/escrow-js@beta +``` + +Install under the **`beta`** dist-tag while Core v2 is in beta. + +Requires **Node 18+** for global `fetch`. **Zero runtime dependencies** — no Axios, no React. + +## Create a client + +```ts +import { TrustlessWorkClient, development } from "@trustless-work/escrow-js"; + +const client = new TrustlessWorkClient({ + baseURL: development, + apiKey: process.env.API_KEY, +}); + +await client.rest.listEscrows({ scope: "mine", limit: 20 }); +await client.graphql.getEscrow({ contractId: "C..." }); +``` + +This instance pattern is the one to use with dependency injection — NestJS providers, Angular services — and anywhere you need more than one configuration. + +There is also a module-level default: `configureTrustlessWork(...)` once at startup, then `getTrustlessWorkClient()`, `escrowRest()` and `escrowGraphql()` anywhere. Prefer the explicit instance in server code; the default is convenient for scripts. + +### Options + +| Option | Type | Purpose | +| --- | --- | --- | +| `baseURL` | `string` | API host. | +| `apiKey` | `string` | Sent as `x-api-key`. Required for writes. | +| `getAccessToken` | `() => string \| Promise` | Wallet-session auth. **May be async here**, unlike the React SDK. | +| `defaultHeaders` | `Record` | Extra headers on every request. | + +> **`mainNet` and `development` both resolve to the beta host.** The package exports both names, but while V2 is beta they point at the same URL. `mainNet` does not mean production — V2 is not deployed to mainnet. + +--- + +## The signing loop + +Identical to the React SDK, minus the hooks. + +```ts +// 1. Build +const { unsignedXdr } = await client.rest.fundEscrow( + { contractId, signer, amount: 1000 }, + "single-release", +); + +// 2. Sign with the wallet the operation requires (your wallet layer) +const signedXdr = await signWithWallet(unsignedXdr); + +// 3. Submit +const result = await client.rest.sendTransaction(signedXdr); +``` + +The build response is `{ unsignedXdr, txHash }`. **`deployEscrow` adds the predicted `contractId`**, so you know the escrow's identity before submitting: + +```ts +const { unsignedXdr, txHash, contractId } = await client.rest.deployEscrow( + payload, + "single-release", +); +``` + +### Reading the submit result + +```ts +type SendTransactionResponse = { + txHash: string; + ledger: number; + contractId?: string; // present on deploy confirmation + escrow?: Escrow; // present on deploy confirmation + code?: SendTransactionCode; + message?: string; +}; +``` + +| `code` | Meaning | +| --- | --- | +| `STELLAR_TX_SUBMITTED` | Submitted and indexed. | +| `STELLAR_TX_SUBMITTED_INDEXER_LAGGING` | **The transaction succeeded.** Only the read model is behind. Do not retry — re-read after a moment. | + +Treating the lagging code as a failure and resubmitting is the most expensive mistake available here. + +--- + +## Methods + +Same surface as the React SDK, reached through `client.rest` and `client.graphql` instead of hooks. + +### Operate — build transactions + +`deployEscrow(payload, type, attribution?)` · `fundEscrow(payload, type)` · `updateEscrow(payload, type)` · `manageMilestones(payload, type)` · `changeMilestoneStatus(payload, type)` · `approveMilestones(payload, type)` · `approveAndReleaseMilestones(payload)` · `releaseFunds(payload, type)` · `releaseMilestones(payload)` · `startDispute(payload, type)` · `disputeMilestones(payload)` · `resolveDispute(payload, type)` · `withdrawRemainingFunds(payload, type)` + +`releaseMilestones` and `disputeMilestones` are multi-release only and take no `type`. + +### Submit + +`sendTransaction(signedXdr)` + +### REST reads + +`listEscrows(params?)` · `getEscrow(contractId)` · `getEscrowDetails(contractIds)` · `listEscrowEvents(contractId, params?)` · `getEscrowMilestones(contractId)` · `getEscrowsMilestones(contractIds)` · `getEscrowsFinancial(contractIds)` + +Batch methods accept a `string[]` of contract IDs. Use them rather than looping single reads. + +> These hit the **read model** at `/escrows/...`, which is neither version-scoped nor type-scoped — there is no `/v2/` in those paths. The v2 transaction controllers expose their own `GET /escrow/{type}/v2/:contractId`, but the SDK does not use it. Read amounts come back as **decimal strings**. + +### GraphQL reads + +`client.graphql.getEscrow(variables)` · `client.graphql.listEscrows(variables)` + +`listEscrows` filters on `scope` (`"mine"` | `"all"`), `status`, `contractType`, `engagementId`, `contractIds`, `participant`, `role`, `platformId`, `subjectId`, `createdAfter`/`createdBefore`. Paginate with `limit` + `cursor` (keyset, not offsets); sort by `createdAt` or `updatedAt`. + +--- + +## Errors + +| Class | When | +| --- | --- | +| `TrustlessWorkApiError` | The API answered with a Problem Details body. Carries the status and machine-readable detail. | +| `TrustlessWorkNetworkError` | The request never got a usable response — DNS, timeout, offline. | + +Use `parseProblemDetailsFromResponse` when handling a raw `Response` yourself. The React SDK's Axios-specific `parseProblemDetailsFromAxiosError` does not exist here. + +--- + +## Coming from the React SDK + +Payloads, responses, error codes and the Core v2 contract are **identical**. Only the React layer is removed. + +| React SDK | JS SDK | +| --- | --- | +| `TrustlessWorkConfig` | `new TrustlessWorkClient(...)` or `configureTrustlessWork(...)` | +| `useTrustlessWorkClient()` | your client instance, or `getTrustlessWorkClient()` | +| `useEscrowRest()` | `client.rest` or `escrowRest()` | +| `useEscrowGraphql()` | `client.graphql` or `escrowGraphql()` | +| `useDeployEscrow().deployEscrow` | `client.rest.deployEscrow` | +| `useSendTransaction().sendTransaction` | `client.rest.sendTransaction` | +| `useGraphqlListEscrows().listEscrows` | `client.graphql.listEscrows` | + +Every other hook follows the same rule: drop `use`, call the method on `client.rest`. + +### Transport differences + +| | React SDK | JS SDK | +| --- | --- | --- | +| HTTP | Axios | Native `fetch` | +| Runtime deps | `axios` + React peers | none | +| Token getter | sync only | sync **or** async | +| Non-Problem errors | raw Axios error | `TrustlessWorkNetworkError` | + +--- + +## Common mistakes + +* **Destructuring `unsignedTransaction`.** The field is `unsignedXdr`. +* **Retrying on `STELLAR_TX_SUBMITTED_INDEXER_LAGGING`.** The transaction already landed. +* **Assuming `mainNet` is production.** Both host constants point at the beta host today. +* **Expecting read amounts to be numbers.** Operate payloads take numbers; read responses return decimal strings. +* **Installing `latest`.** Use the `@beta` dist-tag while Core v2 is beta. +* **Running on Node < 18.** There is no `fetch` polyfill in the package. diff --git a/skills/react-sdk/hooks-reference.md b/skills/react-sdk/hooks-reference.md index f07534c..734a4b8 100644 --- a/skills/react-sdk/hooks-reference.md +++ b/skills/react-sdk/hooks-reference.md @@ -1,6 +1,6 @@ # React SDK Hooks Reference -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). Complete reference for all Trustless Work React SDK hooks with detailed usage examples. diff --git a/skills/react-sdk/react-sdk.md b/skills/react-sdk/react-sdk.md index e52cb5e..f8737e1 100644 --- a/skills/react-sdk/react-sdk.md +++ b/skills/react-sdk/react-sdk.md @@ -1,6 +1,6 @@ # React SDK -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). The Trustless Work React SDK (`@trustless-work/escrow`) provides custom hooks for integrating escrow functionality into React/Next.js applications. It uses Axios for HTTP requests. diff --git a/skills/react-sdk/v2/react-sdk.md b/skills/react-sdk/v2/react-sdk.md new file mode 100644 index 0000000..737eab8 --- /dev/null +++ b/skills/react-sdk/v2/react-sdk.md @@ -0,0 +1,194 @@ +# React SDK v2 — `@trustless-work/escrow` 5.x + +> **Protocol version: V2 (BETA), testnet only.** SDK 5.x targets the **v2** Core API exclusively — every transaction hook builds `/escrow/{type}/v2/...` routes. For V1 integrations use SDK 3.x and the [V1 React SDK reference](../react-sdk.md). + +The REST endpoints and payload shapes this SDK wraps are documented in [skills/api/v2/core-concepts.md](../../api/v2/core-concepts.md). This file covers the React surface: setup, hooks, and the signing loop. + +--- + +## Install + +```bash +npm install @trustless-work/escrow@5 +``` + +Peer requirement: a wallet integration that can sign a Stellar XDR — commonly `@creit.tech/stellar-wallets-kit`. + +## Provider setup + +Wrap your app once. The provider is a client component, so in Next.js App Router it needs `"use client"` in the file that renders it. + +```tsx +"use client"; + +import { TrustlessWorkConfig } from "@trustless-work/escrow"; + +export function Providers({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} +``` + +### Configuration options + +| Prop | Type | Purpose | +| --- | --- | --- | +| `baseURL` | `string` | API host. V2 lives on `https://beta.api.trustlesswork.com`, not on the V1 hosts. | +| `apiKey` | `string` | Sent as `x-api-key`. | +| `getAccessToken` | `() => string \| undefined \| null` | Alternative to a static key — called per request, for session-based auth. | +| `defaultHeaders` | `Record` | Extra headers on every request. | + +`apiKey` and `getAccessToken` can coexist; the getter is re-read on each call, so it suits rotating tokens. + +> SDK 5.x does **not** require a `QueryClientProvider`. It has no TanStack Query dependency — hooks return plain async functions, so you own the caching strategy. + +--- + +## The signing loop + +Every transaction hook **builds** a transaction. It never submits one. The shape is always the same: + +```tsx +"use client"; + +import { useFundEscrow, useSendTransaction } from "@trustless-work/escrow"; +import { signTransaction } from "@/lib/wallet"; + +export function FundButton({ contractId, signer }: Props) { + const { fundEscrow } = useFundEscrow(); + const { sendTransaction } = useSendTransaction(); + + async function onFund() { + // 1. Build — the field is `unsignedXdr`, not `unsignedTransaction` + const { unsignedXdr } = await fundEscrow( + { contractId, signer, amount: 1000 }, + "single-release", + ); + + // 2. Sign with the wallet the operation requires + const signedXdr = await signTransaction(unsignedXdr); + + // 3. Submit + const result = await sendTransaction(signedXdr); + // result.code === "STELLAR_TX_SUBMITTED_INDEXER_LAGGING" means it worked + // but the read model is behind — do not retry the transaction. + } + + return ; +} +``` + +Skipping step 3 means nothing reaches the chain. This is the single most common integration mistake. + +### The `type` argument + +Most transaction hooks take the escrow type as a second argument: + +```ts +type EscrowType = "single-release" | "multi-release"; +``` + +It must match the payload type you pass. A multi-release payload with `"single-release"` hits the wrong route and fails. + +A few hooks are multi-release only and take no type, because the operation does not exist for single-release: `releaseMilestones` and `disputeMilestones`. + +--- + +## Transaction hooks + +| Hook | Method | Signed by | Notes | +| --- | --- | --- | --- | +| `useDeployEscrow` | `deployEscrow(payload, type, attribution?)` | `signer` | Accepts optional attribution headers | +| `useFundEscrow` | `fundEscrow(payload, type)` | any depositor | | +| `useUpdateEscrow` | `updateEscrow(payload, type)` | `admin` | Zero balance only | +| `useManageMilestones` | `manageMilestones(payload, type)` | `admin` | Add or edit milestones | +| `useChangeMilestoneStatus` | `changeMilestoneStatus(payload, type)` | a service provider | Batched `updates` | +| `useApproveMilestones` | `approveMilestones(payload, type)` | an approver | One vote per listed milestone | +| `useApproveAndReleaseMilestones` | `approveAndReleaseMilestones(payload)` | approver + release signer | | +| `useReleaseFunds` | `releaseFunds(payload, type)` · `releaseMilestones(payload)` | a release signer | The second is multi-release only | +| `useStartDispute` | `startDispute(payload, type)` · `disputeMilestones(payload)` | see below | `disputeMilestones` is multi-release only | +| `useResolveDispute` | `resolveDispute(payload, type)` | a dispute resolver | | +| `useWithdrawRemainingFunds` | `withdrawRemainingFunds(payload, type)` | a dispute resolver | Terminal escrows only | +| `useSendTransaction` | `sendTransaction(signedXdr)` | — | `POST /stellar/send-transaction` | + +> **The submit route changed.** V1 documents `POST /helper/send-transaction`. SDK 5.x posts to `/stellar/send-transaction`. Use `useSendTransaction` and you never have to hardcode it. + +Dispute permissions follow the contract: approvers, service providers, release signers, the platform and the receiver may open one. A dispute resolver may not — the contract rejects it. + +## Read hooks + +These query the indexer read model, not the chain directly. + +> These hit the **read model** at `/escrows/...`, which is neither version-scoped nor type-scoped — there is no `/v2/` in those paths. The v2 transaction controllers expose their own `GET /escrow/{type}/v2/:contractId`, but the SDK does not use it. Read amounts come back as **decimal strings**. + +| Hook | Method | Returns | +| --- | --- | --- | +| `useGetEscrow` | `getEscrow(contractId)` | One escrow | +| `useListEscrows` | `listEscrows(params?)` | Keyset-paginated list | +| `useGetEscrowDetails` | `getEscrowDetails(contractIds)` | Batch detail lookup | +| `useGetEscrowMilestones` | `getEscrowMilestones(contractId)` | Milestones for one escrow | +| `useGetEscrowsMilestones` | `getEscrowsMilestones(contractIds)` | Milestones for many | +| `useGetEscrowsFinancial` | `getEscrowsFinancial(contractIds)` | Financial summary for many | +| `useListEscrowEvents` | `listEscrowEvents(contractId, params?)` | Event history | + +Batch hooks accept either a `string[]` of contract IDs or a params object — use the batch form rather than looping single reads. + +## GraphQL hooks + +New in 5.x. Same read model, one round trip, and you choose the fields. + +| Hook | Method | REST twin | +| --- | --- | --- | +| `useGraphqlGetEscrow` | `getEscrow(variables)` | `GET /escrows/:id` | +| `useGraphqlListEscrows` | `listEscrows(variables?)` | `GET /escrows` | + +```tsx +const { listEscrows } = useGraphqlListEscrows(); +const page = await listEscrows({ scope: "mine", limit: 20, sort: "createdAt", order: "desc" }); +``` + +`listEscrows` filters on `scope` (`"mine"` or `"all"`), `status`, `contractType`, `engagementId`, `contractIds`, `participant`, `role`, `platformId`, `subjectId` and a `createdAfter`/`createdBefore` range. Paginate with `limit` plus `cursor` — it is keyset pagination, not offsets. Sort by `createdAt` or `updatedAt`. + +The exported documents `GRAPHQL_GET_ESCROW` and `GRAPHQL_LIST_ESCROWS` are available if you prefer your own client. Errors surface as `GraphqlRequestError`. + +Prefer GraphQL when a screen needs several related pieces of one escrow; prefer the REST batch reads when you need the same shape across many escrows. + +## Attribution headers + +`deployEscrow` accepts an optional third argument that tags the escrow with the platform and end user that created it: + +```ts +await deployEscrow(payload, "single-release", { + platformId: "my-platform", // X-TW-Platform + subjectId: "user-123", // X-TW-Subject +}); +``` + +Useful when one API key serves several platforms or tenants. + +--- + +## Direct service access + +The hooks are thin wrappers. Outside React — a server action, a script, a test — use the services directly: + +```ts +import { TrustlessWorkClient, EscrowRestService } from "@trustless-work/escrow"; +``` + +`useTrustlessWorkClient`, `useEscrowRest` and `useEscrowGraphql` expose the same objects inside React. + +## Common mistakes + +* **Forgetting `sendTransaction`.** The hook returns `unsignedXdr`; nothing happens until you sign and submit. +* **Mismatching `type` and payload.** `"single-release"` needs a `SingleRelease*Payload`. +* **Signing with the wrong wallet.** Each operation names its required signer; the contract rejects anything else. +* **Reusing V1 payload shapes.** Roles are arrays in v2, `milestoneIndexes` is `number[]`, and milestone approval is a threshold. See [skills/api/v2/core-concepts.md](../../api/v2/core-concepts.md). +* **Pointing `baseURL` at a V1 host.** V2 is served only from `https://beta.api.trustlesswork.com`; neither `api.trustlesswork.com` nor `dev.api.trustlesswork.com` serves it. +* **Expecting a `QueryClientProvider` requirement.** 5.x does not use TanStack Query; that was the 3.x pattern. diff --git a/skills/react-sdk/vibe-coding.md b/skills/react-sdk/vibe-coding.md index 74226da..c797119 100644 --- a/skills/react-sdk/vibe-coding.md +++ b/skills/react-sdk/vibe-coding.md @@ -1,6 +1,6 @@ # Vibe Coding Guide -> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK and Blocks surfaces are **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). +> **Protocol version: V1.** This file documents the production V1 integration — the only version deployed on mainnet. The V2 Core API is documented in [skills/api/v2/](../api/v2/), but the V2 React SDK is documented in [skills/react-sdk/v2/](../../skills/react-sdk/v2/react-sdk.md); the V2 Blocks surface is **not** documented yet. For the contract-level differences see [constitution.md](../../constitution.md). In all of your chat prompts, you should provide the agent with specific document references, including the global content rules and the complete code project. ***GPT 5 gives the best result*** From d369f531796e1b4692173a9700a1e36e7abb8e2a Mon Sep 17 00:00:00 2001 From: armandocodecr Date: Fri, 11 Sep 2026 11:42:30 -0600 Subject: [PATCH 3/6] Correct v2 endpoint docs against contract source audit - resolve-dispute: distributions must equal the exact target (full balance on single-release; combined amount of the named milestones on multi) - withdraw-remaining-funds: full sweep on both types, not partial - update-escrow: milestones in the payload are ignored; lock is the cumulative funded amount (FundedAmount), not a zero live balance - multi manage-milestones: all edits frozen once funded, not just amounts - single release: zero-milestone escrows cannot release Co-Authored-By: Claude Fable 5 --- skills/api/v2/multi-release.md | 8 ++++---- skills/api/v2/single-release.md | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/skills/api/v2/multi-release.md b/skills/api/v2/multi-release.md index cec51c8..b23ccef 100644 --- a/skills/api/v2/multi-release.md +++ b/skills/api/v2/multi-release.md @@ -70,7 +70,7 @@ Fund the escrow as a whole. The canonical target is the sum of the milestone amo } ``` -Complete desired state, not a patch. `admin` only, and only while the balance is zero. +Complete desired state for the escrow's **properties and roles** — **`milestones` in this payload is ignored**; the contract preserves the existing ones. Use `manage-milestones` for milestones. `admin` only, rejected while any milestone is disputed, and only **before the first `fund` call** — the lock is the cumulative funded amount, which never decreases. ## Manage milestones @@ -89,7 +89,7 @@ Complete desired state, not a patch. `admin` only, and only while the balance is } ``` -Multi-release updates can change a milestone's **amount** as well as its description — single-release cannot, since the amount lives on the escrow. Amount changes are rejected once the contract holds funds. +Multi-release updates can change a milestone's **amount** as well as its description — single-release cannot, since the amount lives on the escrow. **Every** milestone edit (description or amount) is rejected once the escrow has been funded; appending new milestones stays allowed until the escrow is released, disputed or resolved. A new milestone's `receiver` must not be the `admin` or any `disputeResolver`. The contract rejects that configuration. @@ -164,7 +164,7 @@ Disputes specific milestones, leaving the rest of the escrow operative. Approver } ``` -Scoped to the named milestones. Distributions must be positive, must not exceed those milestones' amounts, and must not exceed the contract balance. They do **not** have to equal the full escrow balance — that rule is single-release only. Resolution is terminal. +Scoped to the named milestones, which must each be disputed and not already resolved (duplicates in `milestoneIndexes` are rejected). Distributions: max 50 entries, every amount positive, and the total must equal the **combined amount of the named milestones exactly** (`DistributionsMustEqualEscrowBalance` otherwise) — not less, not more. The contract balance must cover that total. It does not have to equal the full escrow balance, since resolution is per milestone. Resolution is terminal for those milestones. ## Withdraw remaining funds @@ -178,7 +178,7 @@ Scoped to the named milestones. Distributions must be positive, must not exceed } ``` -Requires **every** milestone to be terminal — released or its dispute resolved. Fee-bearing, same as single-release. +Requires **every** milestone to be terminal — released or its dispute resolved. The distributions must sum to the **entire remaining balance**: a full sweep, not a partial withdrawal. Fee-bearing, same as single-release. ## Extend TTL diff --git a/skills/api/v2/single-release.md b/skills/api/v2/single-release.md index b09b759..fc985bd 100644 --- a/skills/api/v2/single-release.md +++ b/skills/api/v2/single-release.md @@ -63,7 +63,9 @@ Any address holding the asset may fund. Funding grants no role. The signer needs } ``` -The `escrow` object is the **complete** desired state, not a patch. Only `admin` may call it, and only while the contract balance is zero — once funded, properties and roles are frozen. +The `escrow` object is the complete desired state for the escrow's **properties and roles** — but **`milestones` in this payload is ignored**: the contract preserves the existing milestones (and the dispute/released state). Change milestones through `manage-milestones` instead. + +Only `admin` may call it, it is rejected while a dispute is open, and it only works **before the first `fund` call**: the lock is the cumulative funded amount, which never decreases, so releasing funds does not make the escrow editable again. ## Manage milestones @@ -136,7 +138,7 @@ Collapses approval and release into one transaction. Use it only when a single w { "contractId": "C...", "releaseSigner": "G..." } ``` -Requires **all** milestones approved and no active dispute. Pays the configured `amount` minus the platform fee and the Trustless Work protocol fee to `roles.receiver`. The contract balance must cover it. +Requires **all** milestones approved and no active dispute. Pays the configured `amount` minus the platform fee and the Trustless Work protocol fee to `roles.receiver`. The contract balance must cover it. An escrow with **zero milestones cannot release** (`NoMilestoneDefined`) — add milestones first. ## Raise a dispute @@ -163,7 +165,7 @@ Disputes the **whole escrow**. `reason` is required in v2 (v1 had none). Approve } ``` -Max 50 entries, every amount positive. Every recipient needs the asset's trustline. Resolution is terminal — a dispute cannot be reopened. +Max 50 entries, every amount positive, and the total must equal the **entire current contract balance exactly** (`DistributionsMustEqualEscrowBalance` otherwise). The escrow must be disputed. Every recipient needs the asset's trustline. Resolution is terminal — a dispute cannot be reopened. ## Withdraw remaining funds @@ -177,7 +179,7 @@ Max 50 entries, every amount positive. Every recipient needs the asset's trustli } ``` -Sweeps leftover balance — overfunding, stray transfers, rounding dust. In v2 the escrow must be genuinely terminal: `released` or the dispute `resolved`. An open dispute does **not** qualify (it did in v1). +Sweeps leftover balance — overfunding, stray transfers, rounding dust. In v2 the escrow must be genuinely terminal: `released` or the dispute `resolved`. An open dispute does **not** qualify (it did in v1). The distributions must sum to the **entire remaining balance** — it is a full sweep, not a partial withdrawal, which v1 allowed. It is fee-bearing: each recipient's amount is reduced pro rata by the platform and protocol fees, and recipients receive the net. From 29d3aef6f24c35ceee6ff8c322f2ae0473937f63 Mon Sep 17 00:00:00 2001 From: armandocodecr Date: Fri, 11 Sep 2026 11:42:49 -0600 Subject: [PATCH 4/6] Correct useUpdateEscrow mutability note to FundedAmount semantics The update lock is the cumulative funded amount, which never decreases, not a zero live balance. Co-Authored-By: Claude Fable 5 --- skills/react-sdk/v2/react-sdk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/react-sdk/v2/react-sdk.md b/skills/react-sdk/v2/react-sdk.md index 737eab8..ea14f7a 100644 --- a/skills/react-sdk/v2/react-sdk.md +++ b/skills/react-sdk/v2/react-sdk.md @@ -106,7 +106,7 @@ A few hooks are multi-release only and take no type, because the operation does | --- | --- | --- | --- | | `useDeployEscrow` | `deployEscrow(payload, type, attribution?)` | `signer` | Accepts optional attribution headers | | `useFundEscrow` | `fundEscrow(payload, type)` | any depositor | | -| `useUpdateEscrow` | `updateEscrow(payload, type)` | `admin` | Zero balance only | +| `useUpdateEscrow` | `updateEscrow(payload, type)` | `admin` | Only before the first fund (lock is cumulative `FundedAmount`, never resets) | | `useManageMilestones` | `manageMilestones(payload, type)` | `admin` | Add or edit milestones | | `useChangeMilestoneStatus` | `changeMilestoneStatus(payload, type)` | a service provider | Batched `updates` | | `useApproveMilestones` | `approveMilestones(payload, type)` | an approver | One vote per listed milestone | From 3b174232ae34a6839ba1125f8bfb70dc96b14d4f Mon Sep 17 00:00:00 2001 From: armandocodecr Date: Fri, 11 Sep 2026 12:32:08 -0600 Subject: [PATCH 5/6] Correct v2 API docs against core API source audit - fix stale unsignedTransaction field name in single-release intro - drop milestone-receiver rule from single-release manage-milestones (single-release milestones have no receiver; the rule is multi-only) - split read amount types by surface: read model returns decimal strings, versioned v2 reads return numbers - submit response: code is absent on successful factory deploys, which return contractId + escrow instead - update: the API requires escrow.milestones (1-50) even though the contract ignores it - add DTO limits: dispute reason 500, newStatus 50, newEvidence 500, newDescription 500, escrow-balances max 20 addresses Co-Authored-By: Claude Fable 5 --- skills/api/v2/core-concepts.md | 15 +++++++++------ skills/api/v2/multi-release.md | 10 ++++++---- skills/api/v2/single-release.md | 12 ++++++------ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/skills/api/v2/core-concepts.md b/skills/api/v2/core-concepts.md index c16f9c9..bdcaf90 100644 --- a/skills/api/v2/core-concepts.md +++ b/skills/api/v2/core-concepts.md @@ -53,14 +53,17 @@ Three steps, always: Nothing reaches the chain until step 3. -The submit response carries a machine-readable `code`: +The submit response always carries `txHash` and `ledger`. The rest depends on what was submitted: + +- **Successful factory deploy** — `contractId` plus the initial `escrow` snapshot. **No `code` field.** +- **Everything else** — a machine-readable `code`: | Code | Meaning | | --- | --- | -| `STELLAR_TX_SUBMITTED` | Submitted and indexed. | -| `STELLAR_TX_SUBMITTED_INDEXER_LAGGING` | **Submitted successfully**, but the read model has not caught up. Not an error — do not retry the transaction. Re-read after a moment. | +| `STELLAR_TX_SUBMITTED` | Plain (non-deploy) transaction submitted successfully. | +| `STELLAR_TX_SUBMITTED_INDEXER_LAGGING` | Deploy **submitted successfully**, but the contract's return value was not indexed in time. Not an error — do not retry. Fetch the contract via `getTransaction(txHash)` or re-read shortly. | -It also returns `txHash` and `ledger`, plus `contractId` and the `escrow` object when the transaction was a deploy. +Branch on `code` when present, or on the presence of `contractId`, to know which variant you received. `message` is human-readable and unstable — never branch on it. --- @@ -140,7 +143,7 @@ Supply `contractId`, or supply both `symbol` and `address`. When `contractId` is | Field | Type | Note | | --- | --- | --- | | `amount` (operate payloads) | `number` | Human-readable decimals (`1000`, not `"1000"`). | -| `amount` (read responses) | `string` | Reads return **decimal strings**. Do not assume the type is symmetric with what you send. | +| `amount` (read responses) | surface-dependent | The read model (`/escrows/...`) returns **decimal strings**; the versioned `GET /escrow/.../v2/:contractId` returns **numbers**. See “Two read surfaces”. | | `platformFee` | `number` | **Percent**, integer 0–100 (`1` means 1%). Scaled to basis points on-chain. | | `milestoneIndexes` | `number[]` | **Numbers, not strings.** v1's `milestoneIndex` was a string; v2 takes an array of numbers. | | `approvalsTarget` | `number` | Integer ≥ 1. | @@ -226,4 +229,4 @@ There is no `/v2/` in these paths. `@trustless-work/escrow` 5.x and `@trustless- So if you are reading through an SDK you are on surface 2, and adding `/v2/` to those paths is wrong. Surface 1 is reached only by calling the transaction controller directly. -**Read amounts come back as decimal strings**, unlike the numbers you send in operate payloads. +**Amount types differ by surface**: the read model (surface 2) returns amounts and balances as **decimal strings**, unlike the numbers you send in operate payloads; the versioned reads (surface 1) return them as numbers. diff --git a/skills/api/v2/multi-release.md b/skills/api/v2/multi-release.md index b23ccef..0339311 100644 --- a/skills/api/v2/multi-release.md +++ b/skills/api/v2/multi-release.md @@ -70,7 +70,7 @@ Fund the escrow as a whole. The canonical target is the sum of the milestone amo } ``` -Complete desired state for the escrow's **properties and roles** — **`milestones` in this payload is ignored**; the contract preserves the existing ones. Use `manage-milestones` for milestones. `admin` only, rejected while any milestone is disputed, and only **before the first `fund` call** — the lock is the cumulative funded amount, which never decreases. +Complete desired state for the escrow's **properties and roles** — **`milestones` in this payload is ignored**; the contract preserves the existing ones. Use `manage-milestones` for milestones — but the API still **requires** the array (1–50 entries): send the existing milestones back, omitting the field fails validation. `admin` only, rejected while any milestone is disputed, and only **before the first `fund` call** — the lock is the cumulative funded amount, which never decreases. ## Manage milestones @@ -89,7 +89,7 @@ Complete desired state for the escrow's **properties and roles** — **`mileston } ``` -Multi-release updates can change a milestone's **amount** as well as its description — single-release cannot, since the amount lives on the escrow. **Every** milestone edit (description or amount) is rejected once the escrow has been funded; appending new milestones stays allowed until the escrow is released, disputed or resolved. +Multi-release updates can change a milestone's **amount** as well as its description (`newDescription` max 500 chars) — single-release cannot, since the amount lives on the escrow. **Every** milestone edit (description or amount) is rejected once the escrow has been funded; appending new milestones stays allowed until the escrow is released, disputed or resolved. A new milestone's `receiver` must not be the `admin` or any `disputeResolver`. The contract rejects that configuration. @@ -105,6 +105,8 @@ A new milestone's `receiver` must not be the `admin` or any `disputeResolver`. T } ``` +Same rules as single-release: `newStatus` max 50 chars, `newEvidence` optional, max 500. + ## Approve milestones `POST /escrow/multi-release/v2/approve-milestones` — signed by one of `approvers` @@ -146,7 +148,7 @@ Releases **only** the named milestones — unlike single-release, which pays the } ``` -Disputes specific milestones, leaving the rest of the escrow operative. Approvers, service providers, release signers, the platform and that milestone's receiver may raise one; a `disputeResolver` is rejected. +Disputes specific milestones, leaving the rest of the escrow operative. `reason` is required, max 500 chars. Approvers, service providers, release signers, the platform and that milestone's receiver may raise one; a `disputeResolver` is rejected. ## Resolve a dispute @@ -215,4 +217,4 @@ Same shape as single-release except: no top-level `amount`, no `roles.receiver`, } ``` -`GET /escrow/multi-release/v2/escrow-balances?addresses=C...&addresses=C...` +`GET /escrow/multi-release/v2/escrow-balances?addresses=C...&addresses=C...` — max 20 addresses per call. diff --git a/skills/api/v2/single-release.md b/skills/api/v2/single-release.md index fc985bd..e9b9afb 100644 --- a/skills/api/v2/single-release.md +++ b/skills/api/v2/single-release.md @@ -4,7 +4,7 @@ Base path: `/escrow/single-release/v2` -Single-release pays out **once**, to a single `roles.receiver`, after the required milestones are approved. Every write endpoint returns `{ "unsignedTransaction": "..." }`. +Single-release pays out **once**, to a single `roles.receiver`, after the required milestones are approved. Every write endpoint returns `{ "unsignedXdr": "...", "txHash": "..." }` — the build/sign/submit pattern in core-concepts. --- @@ -63,7 +63,7 @@ Any address holding the asset may fund. Funding grants no role. The signer needs } ``` -The `escrow` object is the complete desired state for the escrow's **properties and roles** — but **`milestones` in this payload is ignored**: the contract preserves the existing milestones (and the dispute/released state). Change milestones through `manage-milestones` instead. +The `escrow` object is the complete desired state for the escrow's **properties and roles** — but **`milestones` in this payload is ignored**: the contract preserves the existing milestones (and the dispute/released state). Change milestones through `manage-milestones` instead. The API still **requires** the array (1–50 entries) even though the contract ignores it — send the existing milestones back; omitting the field fails validation before reaching the contract. Only `admin` may call it, it is rejected while a dispute is open, and it only works **before the first `fund` call**: the lock is the cumulative funded amount, which never decreases, so releasing funds does not make the escrow editable again. @@ -84,7 +84,7 @@ Only `admin` may call it, it is rejected while a dispute is open, and it only wo } ``` -Appends new milestones and edits existing descriptions. The receiver of a new milestone must not be the `admin` or any `disputeResolver`. +Appends new milestones and edits existing descriptions (`newDescription` max 500 chars). ## Change milestone status @@ -100,7 +100,7 @@ Appends new milestones and edits existing descriptions. The receiver of a new mi } ``` -Batched. Status is free text and moves no funds; convention is `pending → in_progress → completed`. `newEvidence` is optional. +Batched. Status is free text (max 50 chars) and moves no funds; convention is `pending → in_progress → completed`. `newEvidence` is optional, max 500 chars. ## Approve milestones @@ -148,7 +148,7 @@ Requires **all** milestones approved and no active dispute. Pays the configured { "contractId": "C...", "signer": "G...", "reason": "Deliverable does not match scope" } ``` -Disputes the **whole escrow**. `reason` is required in v2 (v1 had none). Approvers, service providers, release signers, the platform and the receiver may raise one; a `disputeResolver` is rejected. +Disputes the **whole escrow**. `reason` is required in v2 (v1 had none), max 500 chars. Approvers, service providers, release signers, the platform and the receiver may raise one; a `disputeResolver` is rejected. ## Resolve a dispute @@ -234,4 +234,4 @@ Returns the escrow. Note the v2 shape: no `flags` object, `approvals` instead of `GET /escrow/single-release/v2/escrow-balances?addresses=C...&addresses=C...` -Batch balance lookup for several escrow contracts. +Batch balance lookup — max 20 contract addresses per call. From 301b56689de70056acbc8be0ead431bd2de829c7 Mon Sep 17 00:00:00 2001 From: armandocodecr Date: Fri, 11 Sep 2026 13:45:44 -0600 Subject: [PATCH 6/6] Correct SDK v2 docs against SDK source audit - approveAndReleaseMilestones is multi-release only in both SDKs (hardcoded to the multi route, no type argument); the API does expose the single-release route, the SDKs just do not wrap it - extend-ttl has no SDK method in either package; call the API route - React package also exports mainNet/development constants that point at the beta backend's internal URL: warn against reading them as environments and prefer an explicit baseURL Co-Authored-By: Claude Fable 5 --- skills/js-sdk/js-sdk.md | 4 +++- skills/react-sdk/v2/react-sdk.md | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/skills/js-sdk/js-sdk.md b/skills/js-sdk/js-sdk.md index 5e543c5..df1a280 100644 --- a/skills/js-sdk/js-sdk.md +++ b/skills/js-sdk/js-sdk.md @@ -104,7 +104,9 @@ Same surface as the React SDK, reached through `client.rest` and `client.graphql `deployEscrow(payload, type, attribution?)` · `fundEscrow(payload, type)` · `updateEscrow(payload, type)` · `manageMilestones(payload, type)` · `changeMilestoneStatus(payload, type)` · `approveMilestones(payload, type)` · `approveAndReleaseMilestones(payload)` · `releaseFunds(payload, type)` · `releaseMilestones(payload)` · `startDispute(payload, type)` · `disputeMilestones(payload)` · `resolveDispute(payload, type)` · `withdrawRemainingFunds(payload, type)` -`releaseMilestones` and `disputeMilestones` are multi-release only and take no `type`. +`releaseMilestones`, `disputeMilestones` and `approveAndReleaseMilestones` are multi-release only and take no `type`. For the first two the operation does not exist on single-release; approve-and-release **does** exist there in the API (`POST /escrow/single-release/v2/approve-and-release-milestones`) — the SDK just does not wrap it, so call that route directly. + +`extend-ttl` is not wrapped either — call `POST /escrow/{type}/v2/extend-ttl` directly. ### Submit diff --git a/skills/react-sdk/v2/react-sdk.md b/skills/react-sdk/v2/react-sdk.md index ea14f7a..832ab4e 100644 --- a/skills/react-sdk/v2/react-sdk.md +++ b/skills/react-sdk/v2/react-sdk.md @@ -46,6 +46,8 @@ export function Providers({ children }: { children: React.ReactNode }) { `apiKey` and `getAccessToken` can coexist; the getter is re-read on each call, so it suits rotating tokens. +> The package also exports `mainNet` and `development` host constants. Do **not** read them as environments: both point at the beta backend — through its internal hosting URL, not the branded host — and `mainNet` is not production (V2 is not on mainnet). Pass the explicit `baseURL` shown above instead. + > SDK 5.x does **not** require a `QueryClientProvider`. It has no TanStack Query dependency — hooks return plain async functions, so you own the caching strategy. --- @@ -96,7 +98,7 @@ type EscrowType = "single-release" | "multi-release"; It must match the payload type you pass. A multi-release payload with `"single-release"` hits the wrong route and fails. -A few hooks are multi-release only and take no type, because the operation does not exist for single-release: `releaseMilestones` and `disputeMilestones`. +A few methods are multi-release only and take no type: `releaseMilestones`, `disputeMilestones` and `approveAndReleaseMilestones`. For the first two the operation does not exist on single-release. Approve-and-release is different: the API **does** expose `POST /escrow/single-release/v2/approve-and-release-milestones` — the SDK just does not wrap it, so for single-release call that route directly. --- @@ -110,7 +112,7 @@ A few hooks are multi-release only and take no type, because the operation does | `useManageMilestones` | `manageMilestones(payload, type)` | `admin` | Add or edit milestones | | `useChangeMilestoneStatus` | `changeMilestoneStatus(payload, type)` | a service provider | Batched `updates` | | `useApproveMilestones` | `approveMilestones(payload, type)` | an approver | One vote per listed milestone | -| `useApproveAndReleaseMilestones` | `approveAndReleaseMilestones(payload)` | approver + release signer | | +| `useApproveAndReleaseMilestones` | `approveAndReleaseMilestones(payload)` | approver + release signer | **Multi-release only** in the SDK (see above) | | `useReleaseFunds` | `releaseFunds(payload, type)` · `releaseMilestones(payload)` | a release signer | The second is multi-release only | | `useStartDispute` | `startDispute(payload, type)` · `disputeMilestones(payload)` | see below | `disputeMilestones` is multi-release only | | `useResolveDispute` | `resolveDispute(payload, type)` | a dispute resolver | | @@ -121,6 +123,8 @@ A few hooks are multi-release only and take no type, because the operation does Dispute permissions follow the contract: approvers, service providers, release signers, the platform and the receiver may open one. A dispute resolver may not — the contract rejects it. +`extend-ttl` has **no hook** — neither SDK wraps it. When you need it, call `POST /escrow/{type}/v2/extend-ttl` directly. + ## Read hooks These query the indexer read model, not the chain directly.