From e00910dd641e9edebb311e2df003bf348a5acf00 Mon Sep 17 00:00:00 2001 From: pilot <8565879+odcey@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:52:50 +0200 Subject: [PATCH] fix: check token price array & add number validation --- src/index.spec.ts | 5 ++--- src/index.ts | 29 ++++++++++++++++++-------- src/utils/numbers.spec.ts | 43 +++++++++++++++++++++++++++++++++++++++ src/utils/numbers.ts | 26 +++++++++++++++++++++++ 4 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 src/utils/numbers.spec.ts create mode 100644 src/utils/numbers.ts diff --git a/src/index.spec.ts b/src/index.spec.ts index 6ef18b3..e556b4a 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -1,4 +1,5 @@ import { Squid } from "./index"; +import { isValidNumber } from "./utils/numbers"; let squid: Squid; const testIntegratorId = "test-api"; @@ -53,9 +54,7 @@ describe("Squid", () => { chainId: "1", }); - expect(tokensWithPrice.every(tokenPrice => typeof tokenPrice.usdPrice === "number")).toBe( - true, - ); + expect(tokensWithPrice.every(tokenPrice => isValidNumber(tokenPrice.usdPrice))).toBe(true); }); }); diff --git a/src/index.ts b/src/index.ts index 3e97626..a67a257 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,25 +1,26 @@ /* eslint-disable no-case-declarations */ import { ChainType, + CosmosAddress, + CosmosBalance, + EvmWallet, RouteRequest, RouteResponse, + SquidData, StatusResponse, - EvmWallet, Token, TokenBalance, - CosmosAddress, - CosmosBalance, - SquidData, } from "./types"; import HttpAdapter from "./adapter/HttpAdapter"; -import { Config, GetStatus, ExecuteRoute, TransactionResponses } from "./types"; +import { Config, ExecuteRoute, GetStatus, TransactionResponses } from "./types"; +import { CosmosHandler, EvmHandler } from "./handlers"; import { TokensChains } from "./utils/TokensChains"; -import { EvmHandler, CosmosHandler } from "./handlers"; -import { getChainRpcUrls, getEvmTokensForChainIds } from "./utils/evm"; import { getCosmosChainsForChainIds } from "./utils/cosmos"; +import { getChainRpcUrls, getEvmTokensForChainIds } from "./utils/evm"; +import { isValidNumber } from "./utils/numbers"; const baseUrl = "https://testnet.api.squidrouter.com/"; @@ -221,7 +222,17 @@ export class Squid extends TokensChains { params: { address: tokenAddress, chainId, usdPrice: true }, }); - return response.data.token.usdPrice; + const token = response.data.tokens.find( + (t: Token) => t.address.toLowerCase() === tokenAddress.toLowerCase(), + ); + + if (!token || !isValidNumber(token.usdPrice)) { + throw new Error( + `Valid token price not found for address ${tokenAddress} on chain ${chainId}`, + ); + } + + return Number(token.usdPrice); } /** @@ -239,7 +250,7 @@ export class Squid extends TokensChains { }, }); - return response.data.tokens; + return response.data.tokens.filter((token: Token) => isValidNumber(token.usdPrice)); } public async getFromAmount({ diff --git a/src/utils/numbers.spec.ts b/src/utils/numbers.spec.ts new file mode 100644 index 0000000..2755bee --- /dev/null +++ b/src/utils/numbers.spec.ts @@ -0,0 +1,43 @@ +// ... existing imports ... + +import { isValidNumber } from "./numbers"; + +describe("isValidNumber", () => { + it("should return true for valid numbers", () => { + expect(isValidNumber(0)).toBe(true); + expect(isValidNumber(1)).toBe(true); + expect(isValidNumber(1.5)).toBe(true); + expect(isValidNumber("0")).toBe(true); + expect(isValidNumber("1")).toBe(true); + expect(isValidNumber("1.5")).toBe(true); + expect(isValidNumber("0x0")).toBe(true); + expect(isValidNumber("0x1")).toBe(true); + expect(isValidNumber("0xF")).toBe(true); + expect(isValidNumber("0x1A")).toBe(true); + }); + + it("should return false for invalid numbers", () => { + expect(isValidNumber(-1)).toBe(false); + expect(isValidNumber("-1")).toBe(false); + expect(isValidNumber("abc")).toBe(false); + expect(isValidNumber("")).toBe(false); + expect(isValidNumber(null)).toBe(false); + expect(isValidNumber(undefined)).toBe(false); + expect(isValidNumber(NaN)).toBe(false); + expect(isValidNumber(Infinity)).toBe(false); + expect(isValidNumber({})).toBe(false); + }); + + it("should handle large numbers correctly", () => { + expect(isValidNumber(Number.MAX_SAFE_INTEGER)).toBe(true); + expect(isValidNumber(Number.MAX_SAFE_INTEGER.toString())).toBe(true); + expect(isValidNumber("0x" + Number.MAX_SAFE_INTEGER.toString(16))).toBe(true); + }); + + it("should handle edge cases", () => { + expect(isValidNumber("0x0")).toBe(true); + expect(isValidNumber("0x00")).toBe(true); + expect(isValidNumber("0x")).toBe(false); + expect(isValidNumber("0xG")).toBe(false); + }); +}); diff --git a/src/utils/numbers.ts b/src/utils/numbers.ts new file mode 100644 index 0000000..6c36b74 --- /dev/null +++ b/src/utils/numbers.ts @@ -0,0 +1,26 @@ +import { getNumber, isHexString } from "ethers"; + +/** + * Checks if a value is a valid number. + * Handles both numeric and string inputs, including hex strings. + * + * @param {any} value - The value to check. + * @returns {boolean} True if the value is a valid, non-negative, finite number; false otherwise. + */ +export const isValidNumber = (value: any): boolean => { + try { + if (value === null || value === undefined || value === "") { + return false; + } + + // Handle hex strings + if (typeof value === "string" && isHexString(value)) { + value = getNumber(value); + } + + const num = Number(value); + return !isNaN(num) && isFinite(num) && num >= 0; + } catch { + return false; + } +};