Skip to content

Commit

Permalink
Math related fixes (#5837)
Browse files Browse the repository at this point in the history
* order of magnitude fn

* use orderOfMagnitude instead of log10

* fix div by 0 crash

* limit inputs to 17 digits

* fix max digits math

* fix magnitude

---------

Co-authored-by: Matthew Wall <[email protected]>
  • Loading branch information
brunobar79 and walmat authored Jun 13, 2024
1 parent aebb26d commit 1d2884c
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 25 deletions.
43 changes: 31 additions & 12 deletions src/__swaps__/safe-math/SafeMath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ const formatResultWorklet = (result: bigint): string => {
const toStringWorklet = (value: string | number): string => {
'worklet';
const ret = typeof value === 'number' ? value.toString() : value;

if (ret.includes('e') && !ret.includes('e-')) {
const [base, exponent] = ret.split('e');
const exp = Number(exponent);
return base.replace('.', '') + '0'.repeat(exp);
}

if (/^\d+\.$/.test(ret)) {
return ret.slice(0, -1);
}
Expand Down Expand Up @@ -195,23 +202,35 @@ export function modWorklet(num1: string | number, num2: string | number): string
return formatResultWorklet(result);
}

// Logarithm base 10 function
export function log10Worklet(num: string | number): string {
export function orderOfMagnitudeWorklet(num: string | number): number {
'worklet';
const numStr = toStringWorklet(num);
if (num === 0) {
return -Infinity; // log10(0) is -Infinity
}

if (!isNumberStringWorklet(numStr)) {
throw new Error('Arguments must be a numeric string or number');
// Convert the number to a string to handle large numbers and fractional parts
const numStr = num.toString();

// Split the number into integer and fractional parts
const [integerPart, fractionalPart] = numStr.split('.');

// Handle integer parts
if (BigInt(integerPart) !== 0n) {
return integerPart.length - 1;
}
if (isZeroWorklet(numStr)) {
throw new Error('Argument must be greater than 0');

// Handle fractional parts
if (fractionalPart) {
// Find the first non-zero digit in the fractional part
for (let i = 0; i < fractionalPart.length; i++) {
if (fractionalPart[i] !== '0') {
return -(i + 1);
}
}
}

const [bigIntNum, decimalPlaces] = removeDecimalWorklet(numStr);
const scaledBigIntNum = scaleUpWorklet(bigIntNum, decimalPlaces);
const result = Math.log10(Number(scaledBigIntNum)) - 20; // Adjust the scale factor for log10
const resultBigInt = BigInt(result * 10 ** 20);
return formatResultWorklet(resultBigInt);
// If the fractional part is all zeros, return a very negative number
return -Infinity;
}

// Equality function
Expand Down
29 changes: 20 additions & 9 deletions src/__swaps__/safe-math/__tests__/SafeMath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
greaterThanWorklet,
lessThanOrEqualToWorklet,
lessThanWorklet,
log10Worklet,
modWorklet,
mulWorklet,
orderOfMagnitudeWorklet,
powWorklet,
roundWorklet,
subWorklet,
Expand All @@ -26,13 +26,14 @@ const RESULTS = {
div: '325.56878986395199044836',
mod: '2172.345',
pow: '1546106588588.369025',
log10: '0.30102999566398124032',
toFixed: '1243425.35',
ceil: '1243426',
floor: '1243425',
toScaledInteger: '57464009350560633',
negativePow: '0.001',
negativeExp: '6.0895415516156',
orderOfMagnitude: '29',
divWithExp: '100000000000000000000000',
};

const VALUE_A = '1243425.345';
Expand All @@ -42,6 +43,9 @@ const VALUE_D = '1243425.745';
const VALUE_E = '0.057464009350560633';
const VALUE_F = '147887324';
const VALUE_G = '4.11769e-8';
const VALUE_H = '123456789012345678901234567890';
const VALUE_I = '6.25e+21';
const VALUE_K = '6.25';
const NEGATIVE_VALUE = '-2412.12';
const ZERO = '0';
const ONE = '1';
Expand Down Expand Up @@ -94,6 +98,7 @@ describe('SafeMath', () => {
expect(divWorklet(VALUE_A, VALUE_B)).toBe(RESULTS.div);
expect(divWorklet(Number(VALUE_A), VALUE_B)).toBe(RESULTS.div);
expect(divWorklet(VALUE_A, Number(VALUE_B))).toBe(RESULTS.div);
expect(divWorklet(VALUE_I, VALUE_K)).toBe(RESULTS.divWithExp);
});

test('modWorklet', () => {
Expand All @@ -118,13 +123,6 @@ describe('SafeMath', () => {
expect(powWorklet(TEN, Number(MINUS_3))).toBe(RESULTS.negativePow);
});

test('log10Worklet', () => {
expect(() => log10Worklet(NON_NUMERIC_STRING)).toThrow('Arguments must be a numeric string or number');
expect(() => log10Worklet(ZERO)).toThrow('Argument must be greater than 0');
expect(log10Worklet(VALUE_C)).toBe(RESULTS.log10);
expect(log10Worklet(Number(VALUE_C))).toBe(RESULTS.log10);
});

test('equalWorklet', () => {
expect(() => equalWorklet(NON_NUMERIC_STRING, VALUE_B)).toThrow('Arguments must be a numeric string or number');
expect(() => equalWorklet(VALUE_A, NON_NUMERIC_STRING)).toThrow('Arguments must be a numeric string or number');
Expand Down Expand Up @@ -205,6 +203,19 @@ describe('SafeMath', () => {
test('toScaledIntegerWorklet', () => {
expect(toScaledIntegerWorklet(VALUE_E, 18)).toBe(RESULTS.toScaledInteger);
});

test('orderOfMagnitude', () => {
expect(orderOfMagnitudeWorklet(VALUE_H)).toBe(Number(RESULTS.orderOfMagnitude));
expect(orderOfMagnitudeWorklet('12500')).toBe(4);
expect(orderOfMagnitudeWorklet('5000')).toBe(3);
expect(orderOfMagnitudeWorklet('500')).toBe(2);
expect(orderOfMagnitudeWorklet('50')).toBe(1);
expect(orderOfMagnitudeWorklet('97.29560620602980607032')).toBe(1);
expect(orderOfMagnitudeWorklet('0.6')).toBe(-1);
expect(orderOfMagnitudeWorklet('0.04219495')).toBe(-2);
expect(orderOfMagnitudeWorklet('0.00133253382018097672')).toBe(-3);
expect(orderOfMagnitudeWorklet('0.00064470276596066749')).toBe(-4);
});
});

describe('BigNumber', () => {
Expand Down
7 changes: 7 additions & 0 deletions src/__swaps__/screens/Swap/components/SwapNumberPad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ export const SwapNumberPad = () => {
const currentValue = SwapInputController.inputValues.value[inputKey].toString();
const newValue = currentValue === '0' ? `${number}` : `${currentValue}${number}`;

// For a uint256, the maximum value is:
// 2e256 − 1 =115792089237316195423570985008687907853269984665640564039457584007913129639935
// This value has 78 digits.
if (newValue.length > 78) {
return;
}

// Make the quote stale only when the number in the input actually changes
if (Number(newValue) !== 0 && !(currentValue.includes('.') && number === 0)) {
isQuoteStale.value = 1;
Expand Down
9 changes: 5 additions & 4 deletions src/__swaps__/utils/swaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ import {
divWorklet,
equalWorklet,
floorWorklet,
log10Worklet,
lessThanOrEqualToWorklet,
mulWorklet,
powWorklet,
roundWorklet,
toFixedWorklet,
greaterThanOrEqualToWorklet,
orderOfMagnitudeWorklet,
isNumberStringWorklet,
} from '../safe-math/SafeMath';

Expand Down Expand Up @@ -175,7 +175,7 @@ export const findNiceIncrement = (availableBalance: string | number | undefined)
const exactIncrement = divWorklet(availableBalance, 100);

// Calculate the order of magnitude of the exact increment
const orderOfMagnitude = floorWorklet(log10Worklet(exactIncrement));
const orderOfMagnitude = orderOfMagnitudeWorklet(exactIncrement);

const baseIncrement = powWorklet(10, orderOfMagnitude);

Expand Down Expand Up @@ -235,7 +235,7 @@ export function trimTrailingZeros(value: string) {
export function precisionBasedOffMagnitude(amount: number | string, isStablecoin = false): number {
'worklet';

const magnitude = -Number(floorWorklet(log10Worklet(amount)));
const magnitude = -orderOfMagnitudeWorklet(amount);
// don't let stablecoins go beneath 2nd order
if (magnitude < -2 && isStablecoin) {
return -STABLECOIN_MINIMUM_SIGNIFICANT_DECIMALS;
Expand Down Expand Up @@ -368,12 +368,13 @@ export function niceIncrementFormatter({
}
if (percentageToSwap === 0.5) {
const amount = mulWorklet(inputAssetBalance, 0.5);
const precisionAdjustment = precisionBasedOffMagnitude(amount, isStablecoin);
return valueBasedDecimalFormatter({
amount,
usdTokenPrice: inputAssetUsdPrice,
assetBalanceDisplay,
roundingMode: 'up',
precisionAdjustment: precisionBasedOffMagnitude(amount, isStablecoin),
precisionAdjustment,
isStablecoin,
});
}
Expand Down

0 comments on commit 1d2884c

Please sign in to comment.