From c01794e79716f749ec0f273bf58ce71ec22904e8 Mon Sep 17 00:00:00 2001 From: "lightwalker.eth" <126201998+lightwalker-eth@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:17:52 +0400 Subject: [PATCH] Create buildPrice utility function --- src/currency.test.ts | 4 ++-- src/currency.ts | 2 +- src/price.test.ts | 44 +++++++++++++++++++++++++++++++++++++++++++- src/price.ts | 42 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/currency.test.ts b/src/currency.test.ts index 52c8b1a..f7e78ad 100644 --- a/src/currency.test.ts +++ b/src/currency.test.ts @@ -137,13 +137,13 @@ describe("parseStringToCurrency() function", () => { it("should throw an error for invalid currency string", () => { expect(() => parseStringToCurrency("invalid")).toThrow( - "Input string is not a valid currency" + "Cannot convert: \"invalid\" to a recognized Currency" ); }); it("should throw an error for empty string", () => { expect(() => parseStringToCurrency("")).toThrow( - "Input string is not a valid currency" + "Cannot convert: \"\" to a recognized Currency" ); }); }); diff --git a/src/currency.ts b/src/currency.ts index 15855a8..6a1b169 100644 --- a/src/currency.ts +++ b/src/currency.ts @@ -170,7 +170,7 @@ export const parseStringToCurrency = (str: string): Currency => { const curatedStr = str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); const currency = Currency[curatedStr as keyof typeof Currency]; - if (!currency) throw new Error("Input string is not a valid currency"); + if (!currency) throw new Error(`Cannot convert: "${str}" to a recognized Currency`); return currency; }; diff --git a/src/price.test.ts b/src/price.test.ts index ba05a64..ded7119 100644 --- a/src/price.test.ts +++ b/src/price.test.ts @@ -1,8 +1,50 @@ import { describe, it, expect } from "vitest"; -import { Price, convertCurrencyWithRates, formattedPrice } from "./price"; +import { Price, buildPrice, convertCurrencyWithRates, formattedPrice } from "./price"; import { Currency, PriceCurrencyFormat } from "./currency"; +describe("buildPrice() function", () => { + + it("Build from string values", () => { + const value = "100"; + const currency = "USD"; + + const result = buildPrice(value, currency); + + expect(result).toStrictEqual({ + value: 100n, + currency: Currency.Usd, + }); + }); + + it("Build from non-string values", () => { + const value = 100n; + const currency = Currency.Usd; + + const result = buildPrice(value, currency); + + expect(result).toStrictEqual({ + value: 100n, + currency: Currency.Usd, + }); + }); + + it("Invalid value", () => { + const value = "abc"; + const currency = "USD"; + + expect(() => buildPrice(value, currency)).toThrow(`Cannot convert string: ${value} to BigInt`); + }); + + it("Invalid currency", () => { + const value = "100"; + const currency = "invalid"; + + expect(() => buildPrice(value, currency)).toThrow(`Cannot convert: "${currency}" to a recognized Currency`); + }); + +}); + enum CurrencyTestScenario { "UNDERFLOW", "MIN", diff --git a/src/price.ts b/src/price.ts index b154cc1..75ac307 100644 --- a/src/price.ts +++ b/src/price.ts @@ -1,8 +1,17 @@ -import { Currency, PriceCurrencyFormat } from "./currency"; -import { approxScaleBigInt } from "./number"; +import { Currency, PriceCurrencyFormat, parseStringToCurrency } from "./currency"; +import { approxScaleBigInt, stringToBigInt } from "./number"; export interface Price { + + // TODO: consider adding a constraint where value is never negative + /** + * The value of the price. This is a BigInt to avoid floating point math issues when working with prices. + * For example, a price of 1.23 USD would be represented as 123n with a currency of USD. + * Note that the value is always in the smallest unit of the currency (e.g. cents for USD, wei for ETH). + * See the CurrencyConfig for the related currency for the number of decimals to use when converting the value to a human-readable format. + */ value: bigint; + currency: Currency; } @@ -11,6 +20,35 @@ export interface Price { // { ETH: 1737.16, DAI: 0.99999703, USDC: 1, WETH: 1737.16, USD: 1 } export interface ExchangeRates extends Partial> {} +/** + * Builds a Price object. + * @param value the value of the price. This is a BigInt to avoid floating point math issues when working with prices. + * For example, a price of 1.23 USD would be represented as 123n with a currency of USD. + * Note that the value is always in the smallest unit of the currency (e.g. cents for USD, wei for ETH). + * See the CurrencyConfig for the related currency for the number of decimals to use when converting the value to a human-readable format. + * @param currency + * @returns + */ +export const buildPrice = (value: bigint | string, currency: Currency | string): Price => { + + let priceValue : bigint; + let priceCurrency : Currency; + + if (typeof value === "string") { + priceValue = stringToBigInt(value) + } else { + priceValue = value; + } + + if (typeof currency === "string") { + priceCurrency = parseStringToCurrency(currency); + } else { + priceCurrency = currency; + } + + return { value: priceValue, currency: priceCurrency }; +} + export const priceAsNumber = (price: Price): number => { return ( Number(price.value) /