@@ -62,6 +62,7 @@ const createConfig = (apiKey: string) => ({
62
62
} ) ;
63
63
64
64
export const fetchPrices = async ( coinIds : string [ ] , apiKey ?: string ) : Promise < CoinGeckoPrices > => {
65
+ const FIVE_MINUTES_IN_MS = 5 * 60 * 1000 ;
65
66
const [ baseUrl , config ] = apiKey
66
67
? [ COIN_GECKO_PRO_API_BASE_URL , createConfig ( apiKey ) ]
67
68
: [ COIN_GECKO_API_BASE_URL , undefined ] ;
@@ -70,13 +71,34 @@ export const fetchPrices = async (coinIds: string[], apiKey?: string): Promise<C
70
71
console . log ( `fetching ${ chunks . length } chunks of prices` ) ;
71
72
for ( const chunk of chunks ) {
72
73
console . log ( `fetching chunk of ${ chunk . length } prices` ) ;
74
+ let currentBackoff = COIN_GECKO_API_SLEEP_MS ;
73
75
const url = `${ baseUrl } /simple/price?ids=${ chunk . join ( ',' ) } &vs_currencies=usd` ;
74
- const response = await axios . get < CoinGeckoPrices > ( url , config ) ;
75
- console . log ( `fetched ${ Object . keys ( response . data ) . length } prices` ) ;
76
- prices = {
77
- ...prices ,
78
- ...response . data ,
79
- } ;
76
+ let done : boolean = false ;
77
+ while ( ! done ) {
78
+ try {
79
+ const response = await axios . get < CoinGeckoPrices > ( url , config ) ;
80
+ console . log ( `fetched ${ Object . keys ( response . data ) . length } prices` ) ;
81
+ prices = {
82
+ ...prices ,
83
+ ...response . data ,
84
+ } ;
85
+ done = true ;
86
+ } catch ( e ) {
87
+ console . error ( `failed to fetch prices: ${ e } ` ) ;
88
+ if ( isAxiosError ( e ) && e . response ?. status === 429 ) {
89
+ if ( currentBackoff > FIVE_MINUTES_IN_MS ) {
90
+ console . error ( 'Exceeded max backoff time querying CoinGecko API, giving up.' ) ;
91
+ throw e ;
92
+ }
93
+ console . log ( `backing off for ${ currentBackoff } ms` ) ;
94
+ await sleep ( currentBackoff ) ;
95
+ currentBackoff *= 2 ;
96
+ } else {
97
+ // Only want to retry on 429s
98
+ throw e ;
99
+ }
100
+ }
101
+ }
80
102
await sleep ( COIN_GECKO_API_SLEEP_MS ) ;
81
103
}
82
104
return prices ;
0 commit comments