diff --git a/.changeset/mighty-pans-carry.md b/.changeset/mighty-pans-carry.md new file mode 100644 index 00000000..407e577f --- /dev/null +++ b/.changeset/mighty-pans-carry.md @@ -0,0 +1,5 @@ +--- +'@interledger/open-payments': patch +--- + +Add `estimatedExchangeRate` property to `Quote`. diff --git a/openapi/resource-server.yaml b/openapi/resource-server.yaml index 0bf91e9f..9cfdd002 100644 --- a/openapi/resource-server.yaml +++ b/openapi/resource-server.yaml @@ -1193,6 +1193,10 @@ components: description: "The total amount that should be deducted from the sender's account when the corresponding outgoing payment has been paid. " method: $ref: '#/components/schemas/payment-method' + estimatedExchangeRate: + type: number + description: 'Estimated probed exchange rate over the path (ILP specific).' + readOnly: true expiresAt: type: string description: The date and time when the calculated `debitAmount` is no longer valid. diff --git a/packages/open-payments/src/client/quote.test.ts b/packages/open-payments/src/client/quote.test.ts index 12391e4f..dd1786bd 100644 --- a/packages/open-payments/src/client/quote.test.ts +++ b/packages/open-payments/src/client/quote.test.ts @@ -36,6 +36,24 @@ describe('quote', (): void => { describe('getQuote', (): void => { test('returns the quote if it passes open api validation', async (): Promise => { + const scope = nock(baseUrl).get(`/quotes/${quote.id}`).reply(200, quote) + const result = await getQuote( + deps, + { + url: `${baseUrl}/quotes/${quote.id}`, + accessToken + }, + openApiValidators.successfulValidator + ) + expect(result).toEqual(quote) + expect(result.estimatedExchangeRate).toBeUndefined() + scope.done() + }) + + test('returns the quote with the estimated exchange rate if it passes open api validation', async (): Promise => { + const quote = mockQuote({ + estimatedExchangeRate: 120 + }) const scope = nock(baseUrl).get(`/quotes/${quote.id}`).reply(200, quote) const result = await getQuote( deps, @@ -79,6 +97,7 @@ describe('quote', (): void => { { receiver: quote.receiver, method: 'ilp', walletAddress } ) expect(result).toEqual(quote) + expect(result.estimatedExchangeRate).toBeUndefined() scope.done() }) @@ -97,6 +116,25 @@ describe('quote', (): void => { ).rejects.toThrowError() scope.done() }) + + test('returns estimated exchange rate if amount can not be sent over the path', async (): Promise => { + const quote = mockQuote({ + receiveAmount: { value: '0', assetCode: 'ZAR', assetScale: 2 }, + estimatedExchangeRate: 120 + }) + const scope = nock(baseUrl).post(`/quotes`).reply(200, quote) + const result = await createQuote( + deps, + { + url: baseUrl, + accessToken + }, + openApiValidators.successfulValidator, + { receiver: quote.receiver, method: 'ilp', walletAddress } + ) + expect(result).toEqual(quote) + scope.done() + }) }) describe('routes', (): void => { diff --git a/packages/open-payments/src/openapi/generated/resource-server-types.ts b/packages/open-payments/src/openapi/generated/resource-server-types.ts index 1402d0b3..720600fc 100644 --- a/packages/open-payments/src/openapi/generated/resource-server-types.ts +++ b/packages/open-payments/src/openapi/generated/resource-server-types.ts @@ -257,6 +257,8 @@ export interface components { /** @description The total amount that should be deducted from the sender's account when the corresponding outgoing payment has been paid. */ debitAmount: external["schemas.yaml"]["components"]["schemas"]["amount"]; method: components["schemas"]["payment-method"]; + /** @description Estimated probed exchange rate over the path (ILP specific). */ + estimatedExchangeRate?: number; /** @description The date and time when the calculated `debitAmount` is no longer valid. */ expiresAt?: string; /**