Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add token amount formatter #543

Merged
merged 2 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions src/swap/components/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useCallback, useMemo, useState } from 'react';
import { cn } from '../../utils/cn';
import { SwapContext } from '../context';
import { getSwapQuote } from '../core/getSwapQuote';
import { isSwapError } from '../utils';
import { formatTokenAmount, isSwapError } from '../utils';
import type { SwapError, SwapReact } from '../types';
import type { Token } from '../../token';

Expand All @@ -12,9 +12,9 @@ export function Swap({ address, children, onError }: SwapReact) {
const [toAmount, setToAmount] = useState('');
const [toToken, setToToken] = useState<Token>();

const handleToAmountChange = useCallback(
const handleFromAmountChange = useCallback(
async (amount: string) => {
setToAmount(amount);
setFromAmount(amount);
const hasRequiredFields = fromToken && toToken && amount;
if (!hasRequiredFields) {
return;
Expand All @@ -24,23 +24,27 @@ export function Swap({ address, children, onError }: SwapReact) {
from: fromToken,
to: toToken,
amount,
amountReference: 'to',
amountReference: 'from',
});
if (isSwapError(response)) {
onError?.(response);
return;
}
setFromAmount(response?.fromAmount);
const formattedAmount = formatTokenAmount(
response?.toAmount,
response?.to?.decimals,
);
setToAmount(formattedAmount);
} catch (error) {
onError?.(error as SwapError);
}
},
[fromToken, toToken, setFromAmount, setToAmount],
);

const handleFromAmountChange = useCallback(
const handleToAmountChange = useCallback(
async (amount: string) => {
setFromAmount(amount);
setToAmount(amount);
const hasRequiredFields = fromToken && toToken && amount;
if (!hasRequiredFields) {
return;
Expand All @@ -50,13 +54,17 @@ export function Swap({ address, children, onError }: SwapReact) {
from: fromToken,
to: toToken,
amount,
amountReference: 'from',
amountReference: 'to',
});
if (isSwapError(response)) {
onError?.(response);
return;
}
setToAmount(response?.toAmount);
const formattedAmount = formatTokenAmount(
response.fromAmount,
response?.from?.decimals,
);
setFromAmount(formattedAmount);
} catch (error) {
onError?.(error as SwapError);
}
Expand Down
46 changes: 45 additions & 1 deletion src/swap/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isValidAmount, isSwapError } from './utils'; // Adjust the import path as needed
import { formatTokenAmount, isSwapError, isValidAmount } from './utils'; // Adjust the import path as needed

describe('isValidAmount', () => {
it('should return true for an empty string', () => {
Expand Down Expand Up @@ -69,3 +69,47 @@ describe('isSwapError function', () => {
expect(isSwapError(response)).toBe(false);
});
});

describe('formatTokenAmount', () => {
test('formats amount correctly with 18 decimals', () => {
const amount = '100000000000000000';
const decimals = 18;
const formattedAmount = formatTokenAmount(amount, decimals);
expect(formattedAmount).toBe('0.1');
});

test('formats amount correctly with different decimals', () => {
const amount = '1000000000';
const decimals = 9;
const formattedAmount = formatTokenAmount(amount, decimals);
expect(formattedAmount).toBe('1');
});

test('rounds to a maximum of 11 significant digits', () => {
const amount = '16732157880511600003860';
const decimals = 18;
const formattedAmount = formatTokenAmount(amount, decimals);
expect(formattedAmount).toBe('16732.157881');
});

test('handles very small amounts correctly', () => {
const amount = '1';
const decimals = 18;
const formattedAmount = formatTokenAmount(amount, decimals);
expect(formattedAmount).toBe('1e-18');
});

test('handles zero amount correctly', () => {
const amount = '0';
const decimals = 18;
const formattedAmount = formatTokenAmount(amount, decimals);
expect(formattedAmount).toBe('0');
});

test('handles large amounts correctly', () => {
const amount = '1000000000000000000000000000';
const decimals = 18;
const formattedAmount = formatTokenAmount(amount, decimals);
expect(formattedAmount).toBe('1000000000');
});
});
8 changes: 8 additions & 0 deletions src/swap/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ export function isSwapError(response: unknown): response is SwapError {
response !== null && typeof response === 'object' && 'error' in response
);
}

export function formatTokenAmount(amount: string, decimals: number) {
// Convert the string amount to a number using decimals value
const numberAmount = Number(amount) / Math.pow(10, decimals);
// Round to a maximum of 11 significant digits
const roundedAmount = Number(numberAmount.toPrecision(11));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i round to 11 digits here so that the amount will fit in the input but let me know if there is another standard we should follow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this, but I would like the team feedback on what's best. In particular from Jordan Frankfurt and Conner.

return roundedAmount.toString();
}
Loading