diff --git a/api/ordinals.ts b/api/ordinals.ts index ff7a19e3..19314aed 100644 --- a/api/ordinals.ts +++ b/api/ordinals.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios, { isAxiosError } from 'axios'; import EsploraApiProvider from '../api/esplora/esploraAPiProvider'; import XordApiProvider from '../api/ordinals/provider'; import { INSCRIPTION_REQUESTS_SERVICE_URL, ORDINALS_URL, XVERSE_API_BASE_URL, XVERSE_INSCRIBE_URL } from '../constant'; @@ -273,6 +273,26 @@ export const getUtxoOrdinalBundle = async ( return response.data; }; +export const getUtxoOrdinalBundleIfFound = async ( + network: NetworkType, + txid: string, + vout: number, +): Promise => { + try { + const data = await getUtxoOrdinalBundle(network, txid, vout); + return data; + } catch (e) { + // we don't reject on 404s because if the UTXO is not found, + // it is likely this is a UTXO from an unpublished txn. + // this is required for gamma.io purchase flow + if (!isAxiosError(e) || e.response?.status !== 404) { + // rethrow error if response was not 404 + throw e; + } + return undefined; + } +}; + export const mapRareSatsAPIResponseToBundle = (apiBundle: UtxoOrdinalBundle): Bundle => { const generalBundleInfo = { txid: apiBundle.txid, diff --git a/package-lock.json b/package-lock.json index 9ed8408f..6d5f279e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@stacks/transactions": "4.3.5", "@stacks/wallet-sdk": "^5.0.2", "@zondax/ledger-stacks": "^1.0.4", - "axios": "0.27.2", + "axios": "1.4.0", "base64url": "^3.0.1", "bip32": "^4.0.0", "bip39": "3.0.3", @@ -1831,12 +1831,13 @@ } }, "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/balanced-match": { @@ -5720,6 +5721,11 @@ "node": ">=0.4.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", diff --git a/package.json b/package.json index 5691fa69..c631c14f 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@stacks/transactions": "4.3.5", "@stacks/wallet-sdk": "^5.0.2", "@zondax/ledger-stacks": "^1.0.4", - "axios": "0.27.2", + "axios": "1.4.0", "base64url": "^3.0.1", "bip32": "^4.0.0", "bip39": "3.0.3", diff --git a/tests/api/ordinals.test.ts b/tests/api/ordinals.test.ts index a9f0365d..b8878e97 100644 --- a/tests/api/ordinals.test.ts +++ b/tests/api/ordinals.test.ts @@ -1,6 +1,43 @@ -import { mapRareSatsAPIResponseToBundle } from '../../api/ordinals'; +import { AxiosError, AxiosResponse } from 'axios'; +import { getUtxoOrdinalBundleIfFound, mapRareSatsAPIResponseToBundle } from '../../api/ordinals'; import { Bundle, UtxoOrdinalBundle } from '../../types/api/xverse/ordinals'; -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi, afterEach } from 'vitest'; + +const mocked = vi.hoisted(() => ({ + get: vi.fn(), +})); +vi.mock('axios', async () => ({ + ...(await vi.importActual('axios')), + default: { + get: mocked.get, + }, +})); + +describe('getUtxoOrdinalBundleIfFound', () => { + afterEach(() => { + vi.resetAllMocks(); + }); + + it('rejects if API returns 500', () => { + mocked.get.mockRejectedValueOnce( + new AxiosError('server error', undefined, undefined, undefined, { status: 500 } as AxiosResponse), + ); + + expect(getUtxoOrdinalBundleIfFound('Testnet', '', 0)).rejects.toEqual( + expect.objectContaining({ message: 'server error' }), + ); + expect(mocked.get).toHaveBeenCalledOnce(); + }); + + it('resolves undefined if API returns 404', () => { + mocked.get.mockRejectedValueOnce( + new AxiosError('not found', undefined, undefined, undefined, { status: 404 } as AxiosResponse), + ); + + expect(getUtxoOrdinalBundleIfFound('Testnet', '', 0)).resolves.toEqual(undefined); + expect(mocked.get).toHaveBeenCalledOnce(); + }); +}); describe('rareSats', () => { describe('mapRareSatsAPIResponseToRareSats', () => { diff --git a/types/error.ts b/types/error.ts index c7f04a72..3fdd79cb 100644 --- a/types/error.ts +++ b/types/error.ts @@ -20,7 +20,7 @@ export class ResponseError extends Error { export type ApiResponseErrorParams = { status: number; data: unknown; - headers: Record; + headers: unknown; }; export class ApiResponseError extends Error implements ApiResponseErrorParams { public status;