Skip to content

Commit

Permalink
fix: number parsing (#2329)
Browse files Browse the repository at this point in the history
  • Loading branch information
namgold authored Oct 26, 2023
1 parent 2d9e046 commit 8fc5993
Show file tree
Hide file tree
Showing 15 changed files with 94 additions and 72 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2

- name: Set up Node.js 18.15.0
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 18.15.0
node-version: 20.9.0
registry-url: 'https://npm.pkg.github.com'
scope: '@kybernetwork'

Expand Down Expand Up @@ -147,10 +147,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2

- name: Set up Node.js 18.15.0
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 18.15.0
node-version: 20.9.0
registry-url: 'https://npm.pkg.github.com'
scope: '@kybernetwork'

Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v3

- name: Set up Node.js 18.15.0
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18.15.0
node-version: 20.9.0
registry-url: 'https://npm.pkg.github.com'
scope: '@kybernetwork'

Expand All @@ -84,9 +84,9 @@ jobs:

- name: Install linux deps
run: |
sudo apt-get install --no-install-recommends -y \
fluxbox \
xvfb
sudo apt-get install --no-install-recommends -y \
fluxbox \
xvfb
- name: Yarn Build
env:
Expand Down Expand Up @@ -118,14 +118,14 @@ jobs:
yarn preview &
yarn test-e2e -c baseUrl='http://127.0.0.1:4173/' -e grepTags=smoke,NETWORK=Ethereum
env:
DISPLAY: :0.0
DISPLAY: :0.0

- name: Archive e2e artifacts
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8
if: always()
with:
name: e2e-artifacts
path: |
cypress/videos
cypress/screenshots
name: e2e-artifacts
path: |
cypress/videos
cypress/screenshots
continue-on-error: true
8 changes: 4 additions & 4 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2

- name: Set up Node.js 18.15.0
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 18.15.0
node-version: 20.9.0
registry-url: 'https://npm.pkg.github.com'
scope: '@kybernetwork'

Expand Down Expand Up @@ -144,10 +144,10 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v2

- name: Set up Node.js 18.15.0
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 18.15.0
node-version: 20.9.0
registry-url: 'https://npm.pkg.github.com'
scope: '@kybernetwork'

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v2

- name: Set up Node.js 18.15.0
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: 18.15.0
node-version: 20.9.0
registry-url: 'https://npm.pkg.github.com'
scope: '@kybernetwork'

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
- name: Trigger Code Checkout
uses: actions/checkout@v3

- name: Set up Node.js 18.15.0
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18.15.0
node-version: 20.9.0
registry-url: 'https://npm.pkg.github.com'
scope: '@kybernetwork'

Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"homepage": "/",
"license": "GPL-3.0-or-later",
"engines": {
"node": "~18.15.0",
"node": "~20.9.0",
"yarn": ">=1.22"
},
"packageManager": "yarn@1.22.19",
Expand Down Expand Up @@ -158,7 +158,7 @@
"@types/dompurify": "^3.0.3",
"@types/mixpanel-browser": "^2.38.0",
"@types/multicodec": "^1.0.0",
"@types/node": "^13.13.52",
"@types/node": "^20.8.8",
"@types/numeral": "^2.0.0",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.8",
Expand Down Expand Up @@ -208,4 +208,4 @@
"@lingui/core": "3.14.0",
"@lingui/conf": "3.16.0"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ export const TokenInfo = ({
</Text>{' '}
<Text as="span" color={+priceChange > 0 ? theme.apr : theme.red}>
({+priceChange > 0 && '+'}
{formatDisplayNumber(+priceChange / 100, { style: 'percent', fractionDigits: 2, allowNegative: true })})
{formatDisplayNumber(+priceChange / 100, {
style: 'percent',
fractionDigits: 2,
allowDisplayNegative: true,
})}
)
</Text>
</Text>
)}
Expand Down
3 changes: 2 additions & 1 deletion src/components/ProAmm/CandleStickChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ const CandleStickChart = ({
borderColor: 'rgba(197, 203, 206, 0.8)',
},
localization: {
priceFormatter: (val: number) => formatDisplayNumber(val, { significantDigits: 6, allowNegative: true }),
priceFormatter: (val: number) =>
formatDisplayNumber(val, { significantDigits: 6, allowDisplayNegative: true }),
},
})

Expand Down
6 changes: 5 additions & 1 deletion src/components/YieldPools/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,11 @@ const ListItem = ({ farm }: ListItemProps) => {
</RowBetween>
<RowBetween marginBottom="16px">
<Text fontSize="16px" color={theme.text} lineHeight="20px">
{formatDisplayNumber(userStakedBalanceUSD, { style: 'currency', significantDigits: 6, allowZero: false })}
{formatDisplayNumber(userStakedBalanceUSD, {
style: 'currency',
significantDigits: 6,
allowDisplayZero: false,
})}
</Text>
</RowBetween>
<RowBetween marginBottom="16px">
Expand Down
2 changes: 1 addition & 1 deletion src/components/swapv2/LimitOrder/useValidateInputError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const useValidateInputError = ({
const formatNum = formatDisplayNumber(remainBalance, {
style: 'decimal',
fractionDigits: 6,
allowNegative: true,
allowDisplayNegative: true,
})
return (
<Text sx={{ cursor: 'pointer' }}>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/MyEarnings/TotalEarningsAndChainSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const TotalEarningsAndChainSelect: React.FC<Props> = ({ totalEarningToday, total
style: 'percent',
fractionDigits: 2,
significantDigits: 6,
allowNegative: true,
allowDisplayNegative: true,
})
: ''

Expand Down
5 changes: 5 additions & 0 deletions src/types/intl.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare namespace Intl {
interface NumberFormatOptions {
roundingPriority?: 'auto' | 'morePrecision' | 'lessPrecision'
}
}
2 changes: 1 addition & 1 deletion src/utils/dmm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export const getMyLiquidity = (
(parseFloat(liquidityPosition.liquidityTokenBalance) * parseFloat(liquidityPosition.pool.reserveUSD)) /
parseFloat(liquidityPosition.pool.totalSupply)

return formatDisplayNumber(myLiquidity, { style: 'currency', significantDigits: 4, allowZero: false })
return formatDisplayNumber(myLiquidity, { style: 'currency', significantDigits: 4, allowDisplayZero: false })
}

function useFarmRewardsPerTimeUnit(farm?: Farm): RewardPerTimeUnit[] {
Expand Down
72 changes: 36 additions & 36 deletions src/utils/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,40 @@ const subscriptMap: { [key: string]: string } = {
}

const log10 = (n: Fraction): number => {
const parsedN = Number(n.toSignificant(30))
const parsedN = Number(n.toFixed(100))
return Math.log10(parsedN)
}
// - $ 123,456,222,333.44444 e+22 eur5
const regex = /^\s*?\+?(-)?\s*?(\$)?\s*?([\d,]+)(?:\.(\d+))?\s*?(?:e\+?(\-?\d+))?\s*?(%|\w+?)?\s*?$/
const parseNumPart = (str: string): [string, string, string, string, string, string] => {

const unitMapping: { [key: string]: string } = {
k: '3',
m: '6',
b: '9',
t: '12',
}
// - $ 123,456,222,333.44444K e+22 eur5
const regex = /^\s*?\+?(-)?\s*?(\$)?\s*?([\d,]+)(?:\.(\d+))?(\s*?(?:K|M|T))?(?:\s*?e\+?(\-?\d+))?\s*?(%|\w+?)?\s*?$/
const parseNumPart = (str: string): [string, string, string, string, string, string, string] => {
const parsedResult = regex.exec(str)
if (parsedResult) {
const [, negative, currency, integer, decimal, exponent, unit] = parsedResult
return [negative || '', currency || '', integer, decimal || '', exponent, unit || '']
const [, negative, currency, integer, decimal, exponentUnit, exponent, unit] = parsedResult
return [negative || '', currency || '', integer, decimal || '', exponentUnit || '', exponent || '', unit || '']
}
return ['', '', '0', '', '', ''] // [negative, currency, integer, decimal, exponent, unit]
return ['', '', '0', '', '', '', '']
}

const parseString = (value: string): Fraction => {
try {
const [negative, _currency, integer, decimal, e, _unit] = parseNumPart(value)
const exponent = Number(e || '0') - decimal.length
const [negative, _currency, integer, decimal, exponentUnit, e, _unit] = parseNumPart(value)
const trimedNumerator = (negative + integer.replace(/,/g, '') + decimal).replace(/^0+/, '').replace(/^-0+/, '-')
const exponent =
Number(e || '0') +
Number(exponentUnit ? unitMapping[exponentUnit.toLowerCase().trim()] ?? '0' : '0') -
decimal.length

if (exponent > 0) {
return new Fraction(negative + integer.replace(/,/g, '') + decimal + '0'.repeat(exponent), 1)
return new Fraction(trimedNumerator + '0'.repeat(exponent), 1)
}
return new Fraction(negative + integer.replace(/,/g, '') + decimal, 10 ** -exponent)
return new Fraction(trimedNumerator, '1' + '0'.repeat(-exponent))
} catch (e) {
return new Fraction(0, 1)
}
Expand All @@ -105,12 +117,12 @@ export const parseFraction = (value: FormatValue): Fraction => {
value instanceof Price
) {
const valueStr = (() => {
if (typeof value === 'string') return parseString(value).toFixed(18)
if (typeof value === 'string') return parseString(value).toFixed(100)
if (typeof value === 'number') return toString(value)
if (value instanceof BigNumber) return value.toString()
if (value instanceof CurrencyAmount) return value.toFixed(value.currency.decimals)
if (value instanceof Price) return value.toFixed(18)
if (value instanceof Percent) return value.divide(100).toFixed(18)
if (value instanceof Percent) return value.divide(100).toFixed(100)
return '0'
})()
return new Fraction(valueStr.replace('.', ''), '1' + '0'.repeat(valueStr.split('.')[1]?.length || 0))
Expand All @@ -131,8 +143,8 @@ type FormatOptions = {
fractionDigits?: number // usually for percent & currency styles
significantDigits?: number // usually for decimal style
fallback?: string
allowNegative?: boolean
allowZero?: boolean
allowDisplayNegative?: boolean
allowDisplayZero?: boolean
}
interface RequiredFraction extends FormatOptions {
fractionDigits: number // usually for percent & currency styles
Expand Down Expand Up @@ -163,8 +175,8 @@ export const formatDisplayNumber = (
significantDigits,
fractionDigits,
fallback = '--',
allowNegative = false,
allowZero = true,
allowDisplayNegative = false,
allowDisplayZero = true,
}: RequiredFraction | RequiredSignificant,
): string => {
const currency = style === 'currency' ? '$' : ''
Expand All @@ -173,20 +185,20 @@ export const formatDisplayNumber = (

if (value === undefined || value === null || Number.isNaN(value)) return fallbackResult
const parsedFraction = parseFraction(value)
if (!allowNegative && parsedFraction.lessThan(BIG_INT_ZERO)) return fallbackResult
if (!allowZero && parsedFraction.equalTo(BIG_INT_ZERO)) return fallbackResult
if (!allowDisplayNegative && parsedFraction.lessThan(BIG_INT_ZERO)) return fallbackResult
if (!allowDisplayZero && parsedFraction.equalTo(BIG_INT_ZERO)) return fallbackResult

const shownFraction = style === 'percent' ? parsedFraction.multiply(100) : parsedFraction
const absShownFraction = shownFraction.lessThan(0) ? shownFraction.multiply(-1) : shownFraction

if (absShownFraction.lessThan(BIG_INT_ONE) && !shownFraction.equalTo(BIG_INT_ZERO)) {
const decimal = shownFraction.toSignificant(Math.max(30, significantDigits || 0, fractionDigits || 0)).split('.')[1]
const decimal = shownFraction.toFixed(100).split('.')[1]
const negative = shownFraction.lessThan(BIG_INT_ZERO) ? '-' : ''
const numberOfLeadingZeros = -Math.floor(log10(absShownFraction) + 1)
const slicedDecimal = decimal
.replace(/^0+/, '')
.slice(0, fractionDigits)
.slice(0, significantDigits || 6)
.slice(0, fractionDigits ? fractionDigits : 30)
.slice(0, significantDigits ? significantDigits : 30)
.replace(/0+$/, '')

if (numberOfLeadingZeros > 3) {
Expand All @@ -213,20 +225,8 @@ export const formatDisplayNumber = (
maximumFractionDigits: fractionDigits,
minimumSignificantDigits: significantDigits ? 1 : undefined,
maximumSignificantDigits: significantDigits,
roundingPriority: fractionDigits && significantDigits ? 'lessPrecision' : undefined,
})

const result = formatter.format(
Number(parsedFraction.toSignificant(Math.max(30, significantDigits || 0, fractionDigits || 0))),
)

// Intl.NumberFormat does not handle maximumFractionDigits well when used along with maximumSignificantDigits
// It might return number with longer fraction digits than maximumFractionDigits
// Hence, we have to do an additional step that manually slice those oversize fraction digits
if (fractionDigits !== undefined) {
const [negative, currency, integer, decimal, _exponent, unit] = parseNumPart(result)
const trimedSlicedDecimal = decimal?.slice(0, fractionDigits).replace(/0+$/, '')
if (trimedSlicedDecimal) return negative + currency + integer + '.' + trimedSlicedDecimal + unit
return negative + currency + integer + unit
}
return result
return formatter.format(Number(parsedFraction.toFixed(100)))
}
17 changes: 12 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4784,11 +4784,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==

"@types/node@^13.13.52":
version "13.13.52"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.52.tgz#03c13be70b9031baaed79481c0c0cfb0045e53f7"
integrity sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==

"@types/node@^14.14.31":
version "14.18.42"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.42.tgz#fa39b2dc8e0eba61bdf51c66502f84e23b66e114"
Expand All @@ -4799,6 +4794,13 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.48.tgz#3bc872236cdb31cb51024d8875d655e25db489a4"
integrity sha512-mlaecDKQ7rIZrYD7iiKNdzFb6e/qD5I9U1rAhq+Fd+DWvYVs+G2kv74UFHmSOlg5+i/vF3XxuR522V4u8BqO+Q==

"@types/node@^20.8.8":
version "20.8.8"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.8.tgz#adee050b422061ad5255fc38ff71b2bb96ea2a0e"
integrity sha512-YRsdVxq6OaLfmR9Hy816IMp33xOBjfyOgUd77ehqg96CFywxAPbDbXvAsuN2KVg2HOT8Eh6uAfU+l4WffwPVrQ==
dependencies:
undici-types "~5.25.1"

"@types/normalize-package-data@^2.4.0":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
Expand Down Expand Up @@ -17088,6 +17090,11 @@ underscore@^1.13.6:
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441"
integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==

undici-types@~5.25.1:
version "5.25.3"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.25.3.tgz#e044115914c85f0bcbb229f346ab739f064998c3"
integrity sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==

unique-string@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-3.0.0.tgz#84a1c377aff5fd7a8bc6b55d8244b2bd90d75b9a"
Expand Down

0 comments on commit 8fc5993

Please sign in to comment.