Skip to content

Add settled transaction callback to execute()#946

Open
0xTxbi wants to merge 6 commits into
mainfrom
joseph/fe-7922-sdk-execute-method-returns-quoted-amount-instead-of-actual
Open

Add settled transaction callback to execute()#946
0xTxbi wants to merge 6 commits into
mainfrom
joseph/fe-7922-sdk-execute-method-returns-quoted-amount-instead-of-actual

Conversation

@0xTxbi
Copy link
Copy Markdown
Member

@0xTxbi 0xTxbi commented Feb 19, 2026

No description provided.

@0xTxbi 0xTxbi requested a review from pedromcunha February 19, 2026 10:08
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
relay-sdk-demo Ready Ready Preview, Comment May 5, 2026 7:06am

Request Review

Comment thread packages/sdk/src/actions/execute.ts Outdated
Comment thread packages/sdk/src/actions/execute.ts Outdated
Comment thread packages/sdk/src/actions/execute.ts Outdated
@0xTxbi 0xTxbi force-pushed the joseph/fe-7922-sdk-execute-method-returns-quoted-amount-instead-of-actual branch from 6096680 to 6f3d4c6 Compare May 5, 2026 06:54
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Claude finished @0xTxbi's task in 2m 11s —— View job


Code Review — 2 issue(s) found

  1. WARNING: Correctness — abortController is ignored by the metadata polling
    packages/sdk/src/actions/execute.ts:158-228 — Once executeSteps resolves, enrichExecutionWithRequestMetadata runs in the background but never inspects abortController.signal before firing onTransactionReceived and never breaks out of the polling loop in pollRequestMetadataById when aborted. If a consumer calls abortController.abort() after the execute promise settles (e.g. on unmount of the swap modal), the SDK will continue hitting /requests/v2 for up to maxAttempts and may still invoke onTransactionReceived long after teardown. Only the trailing onProgress call is gated by abortController.signal.aborted.

    Suggested fix: thread abortController.signal into the polling loop — bail out of the for loop when aborted, gate onTransactionReceived?.(transaction) on !abortController.signal.aborted, and pass signal through to requestApi (axios supports it via AxiosRequestConfig.signal) so the in-flight request itself is cancelled. Fix this →

  2. NOTE: Correctness — default maxAttempts can blow up with small pollingInterval
    packages/sdk/src/actions/execute.ts:236-238 — When the consumer sets pollingInterval low (say 100ms) but leaves maxPollingAttemptsBeforeTimeout unset, the fallback computes (2.5 * 60 * 1000) / 100 = 1500 attempts. That is consistent with the comment in client.ts ("API is polled every 5 seconds by default (default is 30 attempts)"), but coupling the two via a multiplication of user-tunable knobs is easy to misconfigure. Consider clamping or documenting that pollingInterval controls both step polling and metadata polling cadence here.

Reviewed 5 files, ~290 lines added. Checked correctness, abort/lifecycle handling, error handling, and repo guidelines. The two prior reviewer comments (void placement and "hydrate" wording → renamed to enrich) appear to have been addressed.
Branch

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Claude finished @0xTxbi's task in 1m 54s —— View job


📝 Docs Drift Check — Confidence: HIGH

What changed: execute() gains a new optional onTransactionReceived callback, and after execution settles the SDK polls /requests/v2 to surface the settled request metadata; onProgress is then re-fired once with details.currencyOut updated to the actual settled amount when it differs from the quoted amount.

Docs impact: The existing execute() reference page lists onProgress and other parameters but does not mention onTransactionReceived or the post-settlement re-emission of onProgress with the settled output amount.

Affected pages (paths inside relayprotocol/relay-docs):

  • references/relay-kit/sdk/actions/execute.mdx — update

Draft:

> Replace section: `## Parameters`

## Parameters

| Property                     | Description                                                                                                                                                                                                                                            | Required |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| **quote**                    | A valid quote retrieved using the [getQuote](/references/relay-kit/sdk/actions/getQuote) action                                                                                                                                                        ||
| **wallet**                   | A valid WalletClient from viem or an adapted wallet generated from an adapter that meets this [interface](https://github.com/reservoirprotocol/relay-sdk/blob/main/packages/sdk/src/types/AdaptedWallet.ts).                                           ||
| **depositGasLimit**          | The gas limit used for the deposit transaction.                                                                                                                                                                                                        ||
| **onProgress**               | Callback to update UI state as execution progresses. Can also be used to get the transaction hash for a given step item. The following data points are returned: `steps`, `fees`, `breakdown`, `txHashes`, `currentStep`, `currentStepItem`, `details` ||
| **onTransactionReceived**    | Callback fired asynchronously after execution settles, once request metadata is available. Receives a `RelayTransaction` whose `data.metadata` contains the settled `currencyIn`, `currencyOut`, `sender`, and `recipient`.                            ||
| **disableCapabilitiesCheck** | Skip `wallet.getCapabilities` calls used for EIP-5792 atomic-batch detection and smart-wallet detection. Set this for wallets with a broken `getCapabilities` implementation that hangs or never resolves. When true, execution falls back to sequential transactions. ||
> Replace section: `## Example`

## Example

```typescript
import { getClient, Execute, getQuote } from "@relayprotocol/relay-sdk";
import { useWalletClient } from 'wagmi'

...

const wallet = useWalletClient()

const options = ... //define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet,
  onProgress: ({steps, fees, breakdown, currentStep, currentStepItem, txHashes, details}) => {
        //custom handling
  },
  onTransactionReceived: (transaction) => {
        // fired once the request has settled with finalized metadata
  },
})

```
> Replace section: `## Handling onProgress updates`

## Handling onProgress updates

The state of progress is updated as users are moved through the flow. The onProgress callback will fire every time there are changes to the status. Below is a breakdown of the data returned and how it may be useful in representing the status:

* __steps__: This is the full steps object as it's returned by the api and enhanced with some additional properties on the client. Notably the step status has been updated as well as any errors have been attached.
* __fees__: This is the full fees object as it's returned by the api
* __breakdown__: This is the full breakdown object as it's returned by the api
* __currentStep__: We've conveniently pinpointed the current step that's being processed and made it accessible in this callback.
* __currentStepItem__: Similarly to the step we've hoisted the current step item that is being processed so it's more easily accessible. A step can contain multiple items that are processed in parallel as a batch.
* __txHashes__: A full list of all the transaction hashes that have been processed so far during execution.
* __details__: A summary of the swap action being performed (`currencyIn`, `currencyOut`, swap rate, etc.). After the request settles, if the settled `currencyOut.amount` differs from the quoted amount, `onProgress` fires once more with `details.currencyOut` updated to reflect the actual settled amount.

Needs human input:

  • Whether the new RelayTransaction exported type warrants its own short reference entry, or if the in-table mention is sufficient. The page does not currently document any of the other callback payload types (ProgressData) inline, so following precedent the in-table mention is likely fine.

To publish as a draft PR in relayprotocol/relay-docs, reply on this PR with:

/publish-docs-pr

A separate workflow will open a draft PR attributed to you. Edit the draft freely before marking it ready for review.

@0xTxbi 0xTxbi changed the title Hydrate settled amounts + add onTransactionReceived Add settled transaction callback to execute() May 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Claude finished @0xTxbi's task in 2m 23s —— View job


Code Review — No issues found

Reviewed 5 files, ~290 lines added. Checked correctness, abort/lifecycle handling, error handling, and repo guidelines.

The two prior reviewer concerns and the prior automated-review findings have been addressed in commit 835ab2ae:

  • Abort threading: enrichExecutionWithRequestMetadata and pollRequestMetadataById now thread abortController.signal through the loop and into the axios request config (verified via test 3, which asserts config.signal is defined). The early if (signal.aborted) return undefined checks both before and after each await requestApi(...), plus the if (!transaction || abortController.signal.aborted) return guard before the onTransactionReceived?.(transaction) call, correctly prevent late callback firing after teardown.
  • Word choice / void: prior hydrateenrich rename and void removal are reflected in the current diff.
  • UI polling concern: confirmed by the author that the UI client polling stays — this SDK polling is only for initial enrichment.

Other things I checked (no issues)

  • waitForPollInterval correctly cleans up both the timeout (via clearTimeout on abort) and the abort listener ({ once: true } plus explicit removeEventListener on timeout completion). No leak.
  • onTransactionReceived is fired only once (early return on !nextCurrencyOut); onProgress re-fire is gated on amountChanged, so consumers won't see redundant identical updates.
  • The new RelayTransaction type is correctly derived from the OpenAPI paths schema.
  • Tests cover happy path, missing-metadata fallback, and abort-mid-poll — all three exercise the real code paths.

Minor NOTE (carry-over from prior automated review, non-blocking)

packages/sdk/src/actions/execute.ts:242-244 — When a consumer sets pollingInterval very low (e.g. 100ms) but leaves maxPollingAttemptsBeforeTimeout unset, the fallback (2.5 * 60 * 1000) / pollingInterval produces 1500 attempts. Consistent with client.ts precedent, but worth a docs note on the new metadata polling so integrators don't accidentally over-poll.


Branch

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Claude finished @0xTxbi's task in 1m 20s —— View job


📝 Docs Drift Check — Confidence: HIGH

What changed: execute() gains a new optional onTransactionReceived callback, and after execution settles the SDK polls /requests/v2 to surface settled request metadata; onProgress is then re-fired once with details.currencyOut updated to the actual settled amount when it differs from the quoted amount. The latest commit wires the existing abortController.signal through this enrichment path so polling and the trailing callbacks are cancellable, but this is internal — it does not change the public API.

Docs impact: The existing execute() reference page lists onProgress and other parameters but does not mention onTransactionReceived or the post-settlement re-emission of onProgress with the settled output amount.

Affected pages (paths inside relayprotocol/relay-docs):

  • references/relay-kit/sdk/actions/execute.mdx — update

Draft:

> Replace section: `## Parameters`

## Parameters

| Property                     | Description                                                                                                                                                                                                                                            | Required |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
| **quote**                    | A valid quote retrieved using the [getQuote](/references/relay-kit/sdk/actions/getQuote) action                                                                                                                                                        ||
| **wallet**                   | A valid WalletClient from viem or an adapted wallet generated from an adapter that meets this [interface](https://github.com/reservoirprotocol/relay-sdk/blob/main/packages/sdk/src/types/AdaptedWallet.ts).                                           ||
| **depositGasLimit**          | The gas limit used for the deposit transaction.                                                                                                                                                                                                        ||
| **onProgress**               | Callback to update UI state as execution progresses. Can also be used to get the transaction hash for a given step item. The following data points are returned: `steps`, `fees`, `breakdown`, `txHashes`, `currentStep`, `currentStepItem`, `details` ||
| **onTransactionReceived**    | Callback fired asynchronously after execution settles, once request metadata is available. Receives a `RelayTransaction` whose `data.metadata` contains the settled `currencyIn`, `currencyOut`, `sender`, and `recipient`.                            ||
| **disableCapabilitiesCheck** | Skip `wallet.getCapabilities` calls used for EIP-5792 atomic-batch detection and smart-wallet detection. Set this for wallets with a broken `getCapabilities` implementation that hangs or never resolves. When true, execution falls back to sequential transactions. ||
> Replace section: `## Example`

## Example

```typescript
import { getClient, Execute, getQuote } from "@relayprotocol/relay-sdk";
import { useWalletClient } from 'wagmi'

...

const wallet = useWalletClient()

const options = ... //define this based on getQuote options

const quote = await getClient().actions.getQuote(options)

getClient().actions.execute({
  quote,
  wallet,
  onProgress: ({steps, fees, breakdown, currentStep, currentStepItem, txHashes, details}) => {
        //custom handling
  },
  onTransactionReceived: (transaction) => {
        // fired once the request has settled with finalized metadata
  },
})

```
> Replace section: `## Handling onProgress updates`

## Handling onProgress updates

The state of progress is updated as users are moved through the flow. The onProgress callback will fire every time there are changes to the status. Below is a breakdown of the data returned and how it may be useful in representing the status:

* __steps__: This is the full steps object as it's returned by the api and enhanced with some additional properties on the client. Notably the step status has been updated as well as any errors have been attached.
* __fees__: This is the full fees object as it's returned by the api
* __breakdown__: This is the full breakdown object as it's returned by the api
* __currentStep__: We've conveniently pinpointed the current step that's being processed and made it accessible in this callback.
* __currentStepItem__: Similarly to the step we've hoisted the current step item that is being processed so it's more easily accessible. A step can contain multiple items that are processed in parallel as a batch.
* __txHashes__: A full list of all the transaction hashes that have been processed so far during execution.
* __details__: A summary of the swap action being performed (`currencyIn`, `currencyOut`, swap rate, etc.). After the request settles, if the settled `currencyOut.amount` differs from the quoted amount, `onProgress` fires once more with `details.currencyOut` updated to reflect the actual settled amount.

Needs human input:

  • Whether the new RelayTransaction exported type warrants its own short reference entry, or if the in-table mention is sufficient. The page does not currently document any of the other callback payload types (ProgressData) inline, so following precedent the in-table mention is likely fine.

To publish as a draft PR in relayprotocol/relay-docs, reply on this PR with:

/publish-docs-pr

A separate workflow will open a draft PR attributed to you. Edit the draft freely before marking it ready for review.

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