Skip to content

Document withdrawal endpoints and guide - #430

Merged
ted-palmer merged 2 commits into
mainfrom
ted/int2-838-withdrawal-docs
Aug 28, 2026
Merged

Document withdrawal endpoints and guide #430
ted-palmer merged 2 commits into
mainfrom
ted/int2-838-withdrawal-docs

Conversation

@ted-palmer

Copy link
Copy Markdown
Collaborator

No description provided.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CdjhFUoc96H4BT53YwUm8E
@mintlify

mintlify Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
unevenlabs 🟢 Ready View Preview Aug 26, 2026, 7:46 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 2m 13s —— View job


Code review in progress

  • Read the PR diff
  • Check against AGENTS.md style guide
  • Verify docs.json nav wiring
  • Post review
    · branch ted/int2-838-withdrawal-docs

@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown

Greptile Summary

This change adds public withdrawal endpoint references, navigation entries, and a programmatic withdrawal guide.

A P1 compliance issue remains in the existing review thread: the guide presents screening-flagged deposits as eligible for self-service recovery despite the documented BLOCKED_WALLET handling requiring compliance review.

T-Rex validation blocked

Live verification of a BLOCKED_WALLET withdrawal requires a real blocked-wallet request fixture, an API key for request lookup, and the depositor's signing capability. Those items were unavailable, so the authenticated eligibility and execution path could not be exercised.

Confidence Score: 3/5

Not merge-safe until the withdrawal guidance distinguishes ordinary recoverable deposits from deposits requiring compliance review.

The score is 3 because one outstanding P1 security finding remains.

Files Needing Attention: references/protocol/guides/withdrawals.mdx

T-Rex T-Rex Logs

What T-Rex did

  • Attempted to validate the live Relay API contract, but the validation could not proceed due to missing test access, including no real BLOCKED_WALLET fixture, no request-lookup API key, and no depositor signature.
  • Noted that unauthenticated request lookups require an x-api-key, which blocks the documented eligibility and signed withdrawal path.
  • Created the Relay BLOCKED_WALLET validation script to capture the blocked-path scenario for later testing.
  • Inspected and compared the Before-change withdrawal validation output with the Current withdrawal validation output to verify the blocker and confirm that the API-key requirement remains in effect.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (2): Last reviewed commit: "Merge branch 'main' into ted/int2-838-wi..." | Re-trigger Greptile


## Overview

Sometimes a deposit can't be filled: it was flagged by screening, sent in the wrong currency or on the wrong chain, or the order failed after the deposit landed. Those funds sit in the [Depository](/references/protocol/components/depository) until the original depositor reclaims them. [relay.link/withdraw](https://relay.link/withdraw) is a thin frontend over three public API endpoints, and your app can drive the same flow directly.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Compliance-blocked deposits are presented as recoverable

The overview says that a deposit flagged by screening can be reclaimed through these public endpoints, and the eligibility instructions later treat protocol.isWithdrawable as the recovery criterion without excluding BLOCKED_WALLET. This selects a blocked-wallet request for attestation and withdrawal even though the execution-errors reference says those funds are subject to compliance review and are not automatically refunded. Explicitly exclude BLOCKED_WALLET from this flow and direct integrators to the required review or support process regardless of isWithdrawable.

Artifacts

PR source diff showing the new withdrawal guide eligibility language

  • Captured `git diff HEAD^ HEAD` for the two requested documentation paths, showing the guide was introduced with screened deposits and isWithdrawable recovery guidance; the takeaway is that the candidate language is in this PR.

Focused validation script source

  • Captured the numbered Python validation script that reads the two documentation files and exercises the reported eligibility decision; the takeaway is that the execution checks only the requested contradiction.

Executed BLOCKED_WALLET eligibility validation output

  • Captured execution of the focused validation with a BLOCKED_WALLET request and isWithdrawable true, which selected the public flow and reported the compliance-review contradiction; the takeaway is that the defect is confirmed.

View artifacts

T-Rex Ran code and verified through T-Rex

@scout-relayprotocol scout-relayprotocol Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two documentation issues to address:

  • P2 — XRP support is omitted (references/protocol/guides/withdrawals.mdx:29, 120–125, 169). The withdrawal API and worker support XRP user withdrawals and auto-broadcast them like TON, but XRP is absent from both the supported-chain list and signing/broadcast guidance. Either document the XRP signing flow and note solver broadcasting, or explicitly reject XRP in the API.
  • P2 — Not every failed job should be retried (references/protocol/guides/withdrawals.mdx:163). The guide says to restart after any failure, but terminal reasons include blocked_wallet and chain_disabled; repeatedly preparing cannot resolve those. Recommend inspecting reason, retrying only recoverable failures, and directing terminal cases to Support.

Mintlify deployment and link validation pass. The failed Claude check was infrastructure-only: it exhausted its turn limit without producing findings.

@ted-palmer
ted-palmer requested a review from ipeleg August 27, 2026 20:34
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error after 3m 43s —— View job


Code review

  • Read the PR diff
  • Check against AGENTS.md style guide
  • Verify docs.json nav wiring
  • Post review

Nice guide — the flow is genuinely hard to get right and the "ready is not done" framing plus the nonce-expiry warning are the two things integrators most often miss. All internal links resolve and docs.json parses and wires correctly.

Scope note: I have no network access in this job, so everything below is verified against files in this repo. I could not fetch https://api.relay.link/documentation/json to confirm the three openapi: operations exist in the spec, nor confirm runtime behaviour. Worth eyeballing the preview to confirm the endpoint bodies actually render rather than showing an empty stub.


P1 — Screening-flagged deposits are presented as self-serve recoverable

references/protocol/guides/withdrawals.mdx:11 opens with "it was flagged by screening" as a case these endpoints reclaim, and :47 gives protocol.isWithdrawable === true as the sole eligibility test. But references/api/api_core_concepts/execution-errors.mdx:25 says BLOCKED_WALLET funds "are not automatically refunded and are subject to compliance review", and security/unsupported-exchanges.mdx:30 reinforces that blocked deposits can't be completed.

Two pages in this repo now give opposite instructions for the same request. Either drop the screening case from the overview and have Step 1 require a failReason check that routes BLOCKED_WALLET to support regardless of isWithdrawable, or — if the API really does allow it — update execution-errors.mdx:25 so there's one answer. Greptile flagged the same thing; I'm re-raising it because it's the one finding reproducible entirely from the repo. Fix this →

P1 — The execute example sends additionalData: {}, which the guide's own signing rules say breaks the signature

  • :87 — prepare returns { "nonce": …, "amount": …, "additionalData": { … } }, "pass it through untouched"
  • :110 / :116additionalData is inside the signed digest; "changing any value … produces an invalid signature"
  • :144 — the execute example sends "additionalData": {}

The example is a base withdrawal whose prepare response was shown as carrying a populated additionalData, so a reader who copies Step 5 verbatim submits a payload that doesn't match what they signed. Either echo the prepare value in the example ("additionalData": <value from prepare>) or omit the key entirely and add a line saying it's omitted when prepare didn't return one. Fix this →

P2 — TON contradicts "pass additionalData through untouched"

:127 tells TON integrators to set additionalData["ton-vm"] = { timestamp, domain } on the execute call, which is by definition mutating the object that :87 and :110 say to forward unchanged. The sentence does say it isn't part of the signed digest, but it never says what execute should carry: prepare's additionalData merged with ton-vm, or ton-vm alone. Spell out the merge and note that the server verifies the signature against the prepare-returned subset.

P2 — failed is documented as an unconditional retry

:163 — "See reason, then restart from the prepare step." The row tells the reader to inspect reason and then ignores it. If any reason is terminal (Scout suggests blocked_wallet and chain_disabled; I can't verify the enum from here), a naive integrator loops prepare→sign→execute forever. Split the row: retry recoverable reasons, route terminal ones to support, and cap the retries either way.

P2 — Owner-vs-recipient broadcast is stated three ways

:160 says broadcast "from the owner wallet", :169 says "the owner wallet must sign and broadcast on-chain, paying its own gas", and then :171 ends with "If the owner is on a different VM than the withdrawal chain, the returned transaction is built for the recipient to broadcast instead." The exception is buried as the last sentence of the section, after the reader has been told twice it's the owner. Lead Step 7 with "whichever wallet the returned transaction is built for" and put the cross-VM rule up front.

P2 — The supported-chain list reads exhaustive but doesn't cover every documented chain

:29 — "EVM chains, Hyperliquid, Solana, Tron, and TON. Bitcoin and Lighter withdrawals are rejected with a 400." Eclipse is listed as a supported chain in use-cases/bridging.mdx:12 and use-cases/cross-chain-swaps.mdx:14 but is neither an EVM chain nor Solana, so an Eclipse integrator can't tell which bucket they're in. Scout raised XRP separately — for what it's worth, XRP appears nowhere in these docs outside changelog.mdx, so if the API supports it this list is the place that has to say so. Consider framing by VM family with an explicit "anything not listed is rejected with a 400" so the list degrades gracefully.

P2 — execution-errors.mdx should link to the new guide

That page is where an integrator lands when a request fails, and several of its reasons (ORPHANED_DEPOSIT_REFUND, DEPOSIT_CONFIRMATION_TIMEOUT, AMOUNT_TOO_LOW_TO_REFUND, the protocol-deadline cases) leave funds sitting in the Depository. Per AGENTS.md §2.6 this is exactly a link-on-first-mention case, and right now nothing in the docs points at the recovery flow except the guide itself.

P2 — Placement: this is an API integration guide living in the Protocol tab

Every call in it goes to api.relay.link over curl, which is the shape of api_guides/bitcoin.mdx, hyperliquid-support.mdx, and lighter-support.mdx — all of which sit under Chain Support Guides in the API Reference tab, next to the endpoints they document. As placed, the three endpoint stubs each link across tabs into Protocol → Guides, whose siblings (for-solvers, for-apps, third-party-oracle) are about settlement internals rather than REST flows.

Related: sidebarTitle: "Withdrawals" sits directly under for-solvers.mdx, whose §4 is a Withdrawal section describing an entirely different flow (solver hub balances via the Oracle). Two adjacent nav entries meaning different things. sidebarTitle: "User Withdrawals" would disambiguate cheaply if the page stays put.

Nits

  • snippets/BuildWithAI.mdx is used on exactly one page. AGENTS.md §2.5 reserves snippets for content appearing on ≥2 pages — fine if more pages are coming, otherwise inline it.
  • The three endpoint stubs open with a bare markdown link line above the <Note>. §3.3 says the optional body is a <Note>/<Info>; folding the link into the callout matches the existing stubs.
  • Frontmatter description says "depository funds" lowercase while the body capitalizes Depository (§5).
  • :43 uses GET /requests/v3?user={wallet}&status=failure — I couldn't verify those query params against the spec offline; worth a quick check since it's the entry point to the whole flow.

Not changed: review only, no commits pushed.
· branch ted/int2-838-withdrawal-docs

@ted-palmer
ted-palmer merged commit 25b9ddf into main Aug 28, 2026
4 of 5 checks passed
@ted-palmer
ted-palmer deleted the ted/int2-838-withdrawal-docs branch August 28, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants