Skip to content

Commit 62591d8

Browse files
authored
Merge branch 'master' into pxrl/bigintBuffer
2 parents 9aa1a9c + 7e57c14 commit 62591d8

17 files changed

Lines changed: 198 additions & 413 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,6 @@ yarn-error.log
4444
yarn-debug.log*
4545
gasReporterOutput.json
4646
logs
47+
48+
# AI
49+
.claude

e2e/priceClient.e2e.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import assert from "assert";
2-
import axios from "axios";
32
import dotenv from "dotenv";
43
import winston from "winston";
54
import { Logger, msToS, PriceCache, PriceClient, PriceFeedAdapter, TokenPrice } from "../src/priceClient/priceClient";
@@ -104,20 +103,22 @@ describe("PriceClient: BaseHTTPAdapter", function () {
104103
const baseAdapter = new TestBaseHTTPAdapter(name, "127.0.0.1", { timeout: 1, retries });
105104
expect(baseAdapter.nRetries).to.be.eq(0);
106105

107-
// Instantiate callback for HTTP response != 2xx.
108-
const interceptor = axios.interceptors.response.use(
109-
undefined, // HTTP 2xx.
110-
function (error) {
111-
const result = retries && baseAdapter.nRetries === retries ? Promise.resolve({}) : Promise.reject(error);
112-
return result;
106+
// Mock fetch to succeed on the final retry.
107+
const originalFetch = globalThis.fetch;
108+
globalThis.fetch = (...args: Parameters<typeof fetch>) => {
109+
if (retries && baseAdapter.nRetries === retries) {
110+
return new Response(JSON.stringify({}), { status: 200 });
113111
}
114-
);
115-
116-
const response = baseAdapter._query("", { retries });
117-
await assertPromisePasses(response);
118-
axios.interceptors.response.eject(interceptor); // Cleanup ASAP.
112+
return originalFetch(...args);
113+
};
119114

120-
expect(baseAdapter.nRetries).to.be.eq(retries);
115+
try {
116+
const response = baseAdapter._query("", { retries });
117+
await assertPromisePasses(response);
118+
expect(baseAdapter.nRetries).to.be.eq(retries);
119+
} finally {
120+
globalThis.fetch = originalFetch;
121+
}
121122
}
122123
});
123124
});

package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@across-protocol/sdk",
33
"author": "UMA Team",
4-
"version": "4.3.136",
4+
"version": "4.3.138",
55
"license": "AGPL-3.0",
66
"homepage": "https://docs.across.to/reference/sdk",
77
"repository": {
@@ -115,7 +115,6 @@
115115
"@eth-optimism/sdk": "^3.3.1",
116116
"@ethersproject/bignumber": "^5.7.0",
117117
"@nktkas/hyperliquid": "^0.25.9",
118-
"@pinata/sdk": "^2.1.0",
119118
"@solana-program/compute-budget": "^0.11.0",
120119
"@solana-program/system": "^0.10.0",
121120
"@solana-program/token": "^0.9.0",
@@ -124,11 +123,10 @@
124123
"@uma/contracts-node": "^0.4.0",
125124
"arweave": "^1.14.4",
126125
"async": "^3.2.5",
127-
"axios": "^0.27.2",
128126
"bs58": "^6.0.0",
129127
"decimal.js": "^10.3.1",
130128
"ethers": "^5.7.2",
131-
"lodash": "^4.17.23",
129+
"lodash": "^4.18.1",
132130
"lodash.get": "^4.4.2",
133131
"superstruct": "^0.15.4",
134132
"tslib": "^2.6.2",

src/addressAggregator/adapters/abstract.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import axios from "axios";
21
import { AdapterOptions, AddressListAdapter } from "../types";
3-
import { Logger } from "../../utils";
2+
import { Logger, fetchWithTimeout } from "../../utils";
43

54
const { ACROSS_USER_AGENT = "across-protocol" } = process.env;
65

@@ -28,18 +27,13 @@ export abstract class AbstractAdapter implements AddressListAdapter {
2827
}
2928

3029
protected async fetch(name: string, url: string, timeout = 2000, retries = 1): Promise<unknown> {
31-
const args = {
32-
headers: { "User-Agent": ACROSS_USER_AGENT },
33-
timeout,
34-
};
35-
3630
const errs: string[] = [];
3731
let tries = 0;
3832
do {
3933
try {
40-
return (await axios(url, args)).data;
34+
return await fetchWithTimeout(url, {}, { "User-Agent": ACROSS_USER_AGENT }, timeout);
4135
} catch (err) {
42-
const errMsg = axios.isAxiosError(err) || err instanceof Error ? err.message : "unknown error";
36+
const errMsg = err instanceof Error ? err.message : "unknown error";
4337
errs.push(errMsg);
4438
if (++tries <= retries) await this.sleep(Math.pow(1.5, tries) * 1000); // simple backoff
4539
}

src/apiClient/productionClient.ts

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
1-
import axios from "axios";
21
import AbstractApiClient from "./abstractClient";
3-
import { BigNumber, parseEther } from "../utils";
2+
import { BigNumber, fetchWithTimeout, parseEther } from "../utils";
43
import {
54
CoingeckoDataReturnType,
65
SuggestedFeeReturnType,
76
BridgeLimitsReturnType,
87
AcrossBridgeStatisticsType,
98
} from "./types";
109

10+
type SuggestedFeesApiResponse = {
11+
relayFeePct: string;
12+
relayFeeTotal: string;
13+
capitalFeePct: string;
14+
capitalFeeTotal: string;
15+
relayGasFeePct: string;
16+
relayGasFeeTotal: string;
17+
isAmountTooLow: boolean;
18+
timestamp: string;
19+
quoteBlock: string;
20+
};
21+
1122
/**
1223
* An implementation of AbstractApiClient that uses the production API.
1324
* @class
@@ -20,13 +31,10 @@ export default class ProductionApiClient extends AbstractApiClient {
2031
}
2132

2233
public async getCoinGeckoData(l1Token: string, baseCurrency: string): Promise<CoingeckoDataReturnType> {
23-
const response = await axios.get(`${this.getServerlessApiUrl()}/api/coingecko`, {
24-
params: {
25-
l1Token,
26-
baseCurrency,
27-
},
34+
const result = await fetchWithTimeout<{ price: string }>(`${this.getServerlessApiUrl()}/api/coingecko`, {
35+
l1Token,
36+
baseCurrency,
2837
});
29-
const result = response.data;
3038
const price = baseCurrency === "usd" ? parseEther(String(result.price)) : BigNumber.from(result.price);
3139
return {
3240
price,
@@ -38,16 +46,16 @@ export default class ProductionApiClient extends AbstractApiClient {
3846
toChainid: number,
3947
fromChainid: number
4048
): Promise<SuggestedFeeReturnType> {
41-
const response = await axios.get(`${this.getServerlessApiUrl()}/api/suggested-fees`, {
42-
params: {
49+
const result = await fetchWithTimeout<SuggestedFeesApiResponse>(
50+
`${this.getServerlessApiUrl()}/api/suggested-fees`,
51+
{
4352
token: originToken,
4453
destinationChainId: toChainid,
4554
originChainId: fromChainid,
4655
amount: amount.toString(),
4756
skipAmountLimit: true,
48-
},
49-
});
50-
const result = response.data;
57+
}
58+
);
5159
const relayFeePct = BigNumber.from(result["relayFeePct"]);
5260
const relayFeeTotal = BigNumber.from(result["relayFeeTotal"]);
5361

@@ -85,13 +93,15 @@ export default class ProductionApiClient extends AbstractApiClient {
8593
fromChainId: string | number,
8694
toChainId: string | number
8795
): Promise<BridgeLimitsReturnType> {
88-
const { data } = await axios.get<BridgeLimitsReturnType>(
89-
`${this.getServerlessApiUrl()}/api/limits?token=${token}&originChainId=${fromChainId}&destinationChainId=${toChainId}`
90-
);
96+
const data = await fetchWithTimeout<BridgeLimitsReturnType>(`${this.getServerlessApiUrl()}/api/limits`, {
97+
token,
98+
originChainId: fromChainId,
99+
destinationChainId: toChainId,
100+
});
91101
return data;
92102
}
93103
public async getAcrossStats(): Promise<AcrossBridgeStatisticsType> {
94-
const axiosResponse = await axios.get<AcrossBridgeStatisticsType>(`${this.getScraperApiUrl()}/deposits/stats`);
95-
return axiosResponse.data;
104+
const data = await fetchWithTimeout<AcrossBridgeStatisticsType>(`${this.getScraperApiUrl()}/deposits/stats`);
105+
return data;
96106
}
97107
}

src/caching/Arweave/ArweaveClient.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import Arweave from "arweave";
22
import { JWKInterface } from "arweave/node/lib/wallet";
3-
import axios from "axios";
3+
44
import { Struct, create } from "superstruct";
55
import winston from "winston";
66
import { ARWEAVE_TAG_APP_NAME, ARWEAVE_TAG_APP_VERSION, DEFAULT_ARWEAVE_STORAGE_ADDRESS } from "../../constants";
7-
import { BigNumber, delay, isDefined, jsonReplacerWithBigNumbers, toBN } from "../../utils";
7+
import { BigNumber, delay, fetchWithTimeout, isDefined, jsonReplacerWithBigNumbers, toBN } from "../../utils";
88

99
export class ArweaveClient {
1010
private client: Arweave;
@@ -103,8 +103,7 @@ export class ArweaveClient {
103103
// that the Arweave SDK's `getData` method is too slow and does not provide a way to set a timeout.
104104
// Therefore, something that could take milliseconds to complete could take tens of minutes.
105105
const request = async () => {
106-
const { data: requestData } = await axios.get(transactionUrl);
107-
return requestData;
106+
return await fetchWithTimeout(transactionUrl);
108107
};
109108
const data = await this._retryRequest(request, 0);
110109
try {

src/caching/IPFS/PinataIPFSClient.ts

Lines changed: 0 additions & 92 deletions
This file was deleted.

src/caching/IPFS/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/caching/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
export * from "./IPFS";
21
export * from "./Arweave";
32
export * from "./Memory";

src/coingecko/Coingecko.ts

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import axios, { AxiosError } from "axios";
21
import assert from "assert";
3-
import get from "lodash.get";
4-
import { getCoingeckoTokenIdByAddress, retry } from "../utils";
2+
import { fetchWithTimeout, getCoingeckoTokenIdByAddress, retry } from "../utils";
53
import { Logger } from "../relayFeeCalculator";
64

75
export function msToS(ms: number) {
@@ -386,19 +384,19 @@ export class Coingecko {
386384

387385
// If no pro api key, only send basic request:
388386
if (this.apiKey === undefined) {
389-
return (await this._callBasic(path)) as T;
387+
return await this._callBasic<T>(path);
390388
}
391389

392390
// If pro api key, try basic and use pro as fallback.
393391
try {
394-
return (await this._callBasic(path, this.basicApiTimeout)) as T;
392+
return await this._callBasic<T>(path, this.basicApiTimeout);
395393
} catch (err) {
396394
this.logger.debug({
397395
at: "sdk/coingecko",
398396
message: `Basic CG url request failed, falling back to CG PRO host ${proHost}`,
399-
errMessage: (err as AxiosError).message,
397+
errMessage: (err as Error).message,
400398
});
401-
return await this._callPro(path);
399+
return await this._callPro<T>(path);
402400
}
403401
};
404402

@@ -502,29 +500,15 @@ export class Coingecko {
502500
}
503501
}
504502

505-
private async _callBasic(path: string, timeout?: number) {
503+
private async _callBasic<T>(path: string, timeout?: number): Promise<T> {
506504
const url = `${this.host}/${path}`;
507505

508-
try {
509-
// Don't use timeout if there is no pro API to fallback to.
510-
const result = await axios(url, { timeout });
511-
return result.data;
512-
} catch (err) {
513-
const msg = get(err, "response.data.error", get(err, "response.statusText", (err as AxiosError).message));
514-
throw new Error(msg);
515-
}
506+
// Don't use timeout if there is no pro API to fallback to.
507+
return await fetchWithTimeout<T>(url, {}, {}, timeout);
516508
}
517509

518-
private async _callPro(path: string) {
510+
private async _callPro<T>(path: string): Promise<T> {
519511
const url = `${this.proHost}/${path}`;
520-
521-
try {
522-
// Don't use timeout if there is no pro API to fallback to.
523-
const result = await axios(url, { params: { x_cg_pro_api_key: this.apiKey } });
524-
return result.data;
525-
} catch (err) {
526-
const msg = get(err, "response.data.error", get(err, "response.statusText", (err as AxiosError).message));
527-
throw new Error(msg);
528-
}
512+
return await fetchWithTimeout<T>(url, { x_cg_pro_api_key: this.apiKey });
529513
}
530514
}

0 commit comments

Comments
 (0)