Skip to content

fix(backend): Improve exchange rate handling#3944

Merged
takundachirema merged 13 commits into
mainfrom
takunda/rop-52
Jun 30, 2026
Merged

fix(backend): Improve exchange rate handling#3944
takundachirema merged 13 commits into
mainfrom
takunda/rop-52

Conversation

@takundachirema

@takundachirema takundachirema commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Changes proposed in this pull request

  • Removed logic in the assets service that checks for exchange rate URL config
  • Added rates errors.ts and updated rates service.ts to use them
    • The error codes will distinguish between missing exchange rate URL and network or other errors when fetching exchange rates
  • Updated ilp payment-methods service so that it does not throw when sender and receiver wallet asset codes are the same
  • Added error handling for rate errors in quote service.ts and ilp payment handler service.ts
    • Instead of throwing a 500 error with no details, a detailed message will be shown with the cause of quote error being the exchange rate service
  • Added error handling for rate errors in local payment handler service.ts
    • Check for RatesError type in local payment handler when doing currency conversion

Context

Fixes ROP-52

Checklist

  • Related issues linked using fixes #number
  • Tests added/updated
  • Make sure that all checks pass
  • Bruno collection updated (if necessary)
  • Documentation issue created with user-docs label (if necessary)
  • OpenAPI specs updated (if necessary)

…e URL config

update the tests

updated getExchangeRatesUrl method in rates service so that it throws an error if exchange rate url is not set
added rate related error codes for quote errors

added rate related error codes to payment handler errors.ts
renamed the error code of rates to CouldNotGetRates in payment handler errors.ts

refactored fetchNewRatesAndCache in rates service.ts to be clearer
added tests to check for returned error codes from the rates service.ts

added CouldNotGetRates to quote errors.ts for exchange rate errors when calling the quote service.ts
…rrency conversion

added test case for local payment handler to check correct error handling for missing exchange rate url

added tests for quote service to check for proper error handling in case of rates services error

updated ilp services tests to check for RatesErrors when they are thrown
@netlify

netlify Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploy Preview for brilliant-pasca-3e80ec canceled.

Name Link
🔨 Latest commit 40e48d5
🔍 Latest deploy log https://app.netlify.com/projects/brilliant-pasca-3e80ec/deploys/6a438fcb8f11930009c47e7f

@github-actions github-actions Bot added type: tests Testing related pkg: backend Changes in the backend package. type: source Changes business logic labels Jun 23, 2026
…map to quote errors.ts codes

updated from CouldNotGetRates to CouldNotFetchRates in quote errors.ts for consistency
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

🚀 Performance Test Results

Test Configuration:

  • VUs: 4
  • Duration: 1m0s

Test Metrics:

  • Requests/s: 38.71
  • Iterations/s: 12.90
  • Failed Requests: 0.00% (0 of 2329)
📜 Logs

> performance@1.0.0 run-tests:testenv /home/runner/work/rafiki/rafiki/test/performance
> ./scripts/run-tests.sh -e test -k -q --vus 4 --duration 1m

Cloud Nine GraphQL API is up: http://localhost:3101/graphql
Cloud Nine Wallet Address is up: http://localhost:3100/
Happy Life Bank Address is up: http://localhost:4100/
cloud-nine-wallet-test-backend already set
cloud-nine-wallet-test-auth already set
happy-life-bank-test-backend already set
happy-life-bank-test-auth already set
     data_received..................: 840 kB 14 kB/s
     data_sent......................: 1.8 MB 30 kB/s
     http_req_blocked...............: avg=7.47µs   min=2.42µs   med=5.8µs    max=568.69µs p(90)=7.21µs   p(95)=7.93µs  
     http_req_connecting............: avg=390ns    min=0s       med=0s       max=249.55µs p(90)=0s       p(95)=0s      
     http_req_duration..............: avg=102.59ms min=10.09ms  med=82.75ms  max=485.52ms p(90)=181.96ms p(95)=206.89ms
       { expected_response:true }...: avg=102.59ms min=10.09ms  med=82.75ms  max=485.52ms p(90)=181.96ms p(95)=206.89ms
     http_req_failed................: 0.00%  ✓ 0         ✗ 2329
     http_req_receiving.............: avg=107.37µs min=29.8µs   med=93.81µs  max=1.87ms   p(90)=137.01µs p(95)=171.34µs
     http_req_sending...............: avg=39.65µs  min=9.45µs   med=28.41µs  max=2.11ms   p(90)=50.26µs  p(95)=69.26µs 
     http_req_tls_handshaking.......: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting...............: avg=102.44ms min=9.9ms    med=82.49ms  max=485.4ms  p(90)=181.86ms p(95)=206.79ms
     http_reqs......................: 2329   38.709074/s
     iteration_duration.............: avg=309.79ms min=214.99ms med=297.23ms max=985.1ms  p(90)=377.55ms p(95)=402.93ms
     iterations.....................: 776    12.897485/s
     vus............................: 4      min=4       max=4 
     vus_max........................: 4      min=4       max=4 

@takundachirema takundachirema requested a review from mkurapov June 23, 2026 12:07
Comment thread packages/backend/src/open_payments/quote/service.ts Outdated
Comment thread packages/backend/src/open_payments/payment/outgoing/errors.ts Outdated
…xchange rate error

updated tests in quote, ilp services to reflect the change

updated the rates errors to be targeted for the client
@takundachirema takundachirema requested a review from mkurapov June 29, 2026 12:54
})
})

test('succeeds when exchange rates service fails but receiver wallet asset code matches sender', async (): Promise<void> => {

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.

maybe this tests belongs in packages/backend/src/payment-method/ilp/service.test.ts test since that is where the actual logic lies

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done thanks, missed that.

Comment on lines +253 to +256
return new QuoteError(QuoteErrorCode.CouldNotFetchRates, {
description: err.description,
details: err.details
})

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.

Suggested change
return new QuoteError(QuoteErrorCode.CouldNotFetchRates, {
description: err.description,
details: err.details
})
return new QuoteError(QuoteErrorCode.CouldNotFetchRates)

from previous review, let's not return these details object back to the client. I think the only error we should do that for is the QuoteNonPositiveReceiveAmount as we are currently doing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done 👍

[QuoteErrorCode.InactiveWalletAddress]: 'inactive wallet address',
[QuoteErrorCode.NonPositiveReceiveAmount]: 'non-positive receive amount'
[QuoteErrorCode.NonPositiveReceiveAmount]: 'non-positive receive amount',
[QuoteErrorCode.CouldNotFetchRates]: 'could not fetch exchange rates'

@mkurapov mkurapov Jun 29, 2026

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.

Related to the comments from the previous review:
since this error message (and the outgoing payment one) is the one that gets sent to the end Open Payments client, we could say something like "could not convert between assets", since that is a bit more clearer IMO.

No changes required to the RatesErrors, they are good for internal logging 👍

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done 👍

@takundachirema takundachirema requested a review from mkurapov June 30, 2026 09:57
@takundachirema takundachirema merged commit ca2eff1 into main Jun 30, 2026
38 of 58 checks passed
@takundachirema takundachirema deleted the takunda/rop-52 branch June 30, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg: backend Changes in the backend package. type: source Changes business logic type: tests Testing related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants