From b7add122fa60162efd7d3b8f45c581581eeacee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Ram=C3=ADrez?= Date: Mon, 4 Nov 2024 09:11:15 -0300 Subject: [PATCH 1/2] fix: use normalized decimal count for fromAmount calculation --- src/util.ts | 2 +- test/unit/util.test.ts | 56 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/util.ts b/src/util.ts index 9c5f72e..3134336 100644 --- a/src/util.ts +++ b/src/util.ts @@ -742,7 +742,7 @@ export async function prepareXchainFromAmountCalculation({ const slippageFractionBN = ethers.BigNumber.from(Math.floor(slippagePercentage * 1000)) const slippageBN = fromAmountBN.mul(slippageFractionBN).div(100000) // 1000 * 100 (10e5) const totalFromAmountBN = fromAmountBN.add(slippageBN) - return ethers.utils.formatUnits(totalFromAmountBN, fromToken.decimals) + return Number(ethers.utils.formatUnits(totalFromAmountBN, normalizedDecimalCount)).toFixed(fromToken.decimals) } catch (error) { console.error('Failed to calculate fromAmount:', error) return null diff --git a/test/unit/util.test.ts b/test/unit/util.test.ts index c84509e..0b3fc8f 100644 --- a/test/unit/util.test.ts +++ b/test/unit/util.test.ts @@ -1,4 +1,6 @@ -import { normalizePath } from '../../src/util' +import { normalizePath, prepareXchainFromAmountCalculation } from '../../src/util' + +const mockFetch = jest.fn() describe('util', () => { describe('normalizePath', () => { @@ -14,4 +16,56 @@ describe('util', () => { expect(normalizePath(url)).toBe(normalizedUrl) }) }) + + describe('prepareXchainFromAmountCalculation', () => { + beforeEach(() => { + jest.spyOn(global, 'fetch').mockImplementation(mockFetch) + }) + + afterEach(() => { + jest.restoreAllMocks() + jest.clearAllMocks() + }) + it('should return price with correct decimals', async () => { + const fromToken = { + chainId: '534352', + address: '0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4', + decimals: 6, + } + const toToken = { + chainId: '8453', + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + decimals: 18, + } + const fromTokenPrice = 0.999842 + const toTokenPrice = 2453.66 + const toAmount = '0.00408' + const expectedFromAmount = '10.042552' + const slippagePercentage = 0.3 + mockFetch.mockImplementation((url: string) => { + const tokenAddress = new URL(url).searchParams.get('tokenAddress') + let price: number + if (tokenAddress === fromToken.address) { + price = fromTokenPrice + } else if (tokenAddress === toToken.address) { + price = toTokenPrice + } else { + price = 0 + } + return Promise.resolve({ + json: () => Promise.resolve({ price }), + }) + }) + + const fromAmount = await prepareXchainFromAmountCalculation({ + fromToken, + toToken, + toAmount, + slippagePercentage, + }) + + expect(mockFetch).toHaveBeenCalledTimes(2) + expect(fromAmount).toBe(expectedFromAmount) + }) + }) }) From a3f52056e7c9be6a7e38d3b38d9ac023dd627cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Ram=C3=ADrez?= Date: Mon, 4 Nov 2024 11:34:11 -0300 Subject: [PATCH 2/2] refactor: fix decimals in estimateFromAmount with regex --- src/util.ts | 12 +++++++++++- test/unit/util.test.ts | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/util.ts b/src/util.ts index 3134336..7639d27 100644 --- a/src/util.ts +++ b/src/util.ts @@ -742,7 +742,8 @@ export async function prepareXchainFromAmountCalculation({ const slippageFractionBN = ethers.BigNumber.from(Math.floor(slippagePercentage * 1000)) const slippageBN = fromAmountBN.mul(slippageFractionBN).div(100000) // 1000 * 100 (10e5) const totalFromAmountBN = fromAmountBN.add(slippageBN) - return Number(ethers.utils.formatUnits(totalFromAmountBN, normalizedDecimalCount)).toFixed(fromToken.decimals) + const amount = ethers.utils.formatUnits(totalFromAmountBN, normalizedDecimalCount) + return stringToFixed(amount, fromToken.decimals) } catch (error) { console.error('Failed to calculate fromAmount:', error) return null @@ -759,3 +760,12 @@ export function normalizePath(url: string): string { return url.replace(/\/+/g, '/') } } + +export function stringToFixed(numStr: string, precision: number = 0): string { + const match = numStr.match(/^(-?\d+)\.(?\d*)$/) + if (!match) return numStr + + const [_, intPart, decimals] = match + const rounded = decimals.padEnd(precision, '0').slice(0, precision) + return rounded ? `${intPart}.${rounded}` : intPart +} diff --git a/test/unit/util.test.ts b/test/unit/util.test.ts index 0b3fc8f..2b86c95 100644 --- a/test/unit/util.test.ts +++ b/test/unit/util.test.ts @@ -1,4 +1,4 @@ -import { normalizePath, prepareXchainFromAmountCalculation } from '../../src/util' +import { normalizePath, prepareXchainFromAmountCalculation, stringToFixed } from '../../src/util' const mockFetch = jest.fn() @@ -68,4 +68,17 @@ describe('util', () => { expect(fromAmount).toBe(expectedFromAmount) }) }) + + describe('stringToFixed', () => { + it.each([ + ['0.001', '0.00', 2], + ['0.01', '0.01', 2], + ['0.1', '0.10', 2], + ['1', '1', 2], + ['10.1', '10.10', 2], + ['10.123456', '10.123', 3], + ])('should convert %s to %s with precision %s', (numStr, expected, precision) => { + expect(stringToFixed(numStr, precision)).toBe(expected) + }) + }) })