diff --git a/src/util.ts b/src/util.ts index 9c5f72e..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 ethers.utils.formatUnits(totalFromAmountBN, 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 c84509e..2b86c95 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, stringToFixed } from '../../src/util' + +const mockFetch = jest.fn() describe('util', () => { describe('normalizePath', () => { @@ -14,4 +16,69 @@ 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) + }) + }) + + 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) + }) + }) })