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

Safemath pt 2 #5778

Merged
merged 3 commits into from
May 29, 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
270 changes: 202 additions & 68 deletions src/__swaps__/safe-math/SafeMath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,182 +40,316 @@ const formatResultWorklet = (result: bigint): string => {
return isNegative ? `-${formattedResult}` : formattedResult;
};

// Helper function to handle string and number input types
const toStringWorklet = (value: string | number): string => {
'worklet';
return typeof value === 'number' ? value.toString() : value;
};

// Sum function
export function sumWorklet(num1: string, num2: string): string {
export function sumWorklet(num1: string | number, num2: string | number): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
if (isZeroWorklet(num1)) {
return num2;
if (isZeroWorklet(num1Str)) {
return num2Str;
}

if (isZeroWorklet(num2)) {
return num1;
if (isZeroWorklet(num2Str)) {
return num1Str;
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = scaledBigInt1 + scaledBigInt2;
return formatResultWorklet(result);
}

// Subtract function
export function subWorklet(num1: string, num2: string): string {
export function subWorklet(num1: string | number, num2: string | number): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}

if (isZeroWorklet(num2)) {
return num1;
if (isZeroWorklet(num2Str)) {
return num1Str;
}

const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = scaledBigInt1 - scaledBigInt2;
return formatResultWorklet(result);
}

// Multiply function
export function mulWorklet(num1: string, num2: string): string {
export function mulWorklet(num1: string | number, num2: string | number): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
if (isZeroWorklet(num1) || isZeroWorklet(num2)) {
if (isZeroWorklet(num1Str) || isZeroWorklet(num2Str)) {
return '0';
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = (scaledBigInt1 * scaledBigInt2) / BigInt(10) ** BigInt(20);
return formatResultWorklet(result);
}

// Divide function
export function divWorklet(num1: string, num2: string): string {
export function divWorklet(num1: string | number, num2: string | number): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to make it "easy" to keep numbers in the codebase?
like we can make the user use string by not accepting number in the api or at least indicate something is up by making his code ugly having to parse before lol

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm just getting started to replace the unsafe Number usages inside the worklets and I'm not sure what I'm gonna find out so I'll keep it flexible like this to move faster but in the end I'd like to find all the occurrences and make it more stricter if possible

'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
if (isZeroWorklet(num2)) {
if (isZeroWorklet(num2Str)) {
throw new Error('Division by zero');
}
if (isZeroWorklet(num1)) {
if (isZeroWorklet(num1Str)) {
return '0';
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = (scaledBigInt1 * BigInt(10) ** BigInt(20)) / scaledBigInt2;
return formatResultWorklet(result);
}

// Modulus function
export function modWorklet(num1: string, num2: string): string {
export function modWorklet(num1: string | number, num2: string | number): string {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
if (isZeroWorklet(num2)) {
if (isZeroWorklet(num2Str)) {
throw new Error('Division by zero');
}
if (isZeroWorklet(num1)) {
if (isZeroWorklet(num1Str)) {
return '0';
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
const result = scaledBigInt1 % scaledBigInt2;
return formatResultWorklet(result);
}

// Power function
export function powWorklet(base: string, exponent: string): string {
export function powWorklet(base: string | number, exponent: string | number): string {
'worklet';
if (!isNumberStringWorklet(base) || !isNumberStringWorklet(exponent)) {
throw new Error('Arguments must be a numeric string');
const baseStr = toStringWorklet(base);
const exponentStr = toStringWorklet(exponent);

if (!isNumberStringWorklet(baseStr) || !isNumberStringWorklet(exponentStr)) {
throw new Error('Arguments must be a numeric string or number');
}
if (isZeroWorklet(base)) {
if (isZeroWorklet(baseStr)) {
return '0';
}
if (isZeroWorklet(exponent)) {
if (isZeroWorklet(exponentStr)) {
return '1';
}
const [bigIntBase, decimalPlaces] = removeDecimalWorklet(base);
const [bigIntBase, decimalPlaces] = removeDecimalWorklet(baseStr);
const scaledBigIntBase = scaleUpWorklet(bigIntBase, decimalPlaces);
const result = scaledBigIntBase ** BigInt(exponent) / BigInt(10) ** BigInt(20);
const result = scaledBigIntBase ** BigInt(exponentStr) / BigInt(10) ** BigInt(20);
return formatResultWorklet(result);
}

// Logarithm base 10 function
export function log10Worklet(num: string | number): string {
'worklet';
const numStr = toStringWorklet(num);

if (!isNumberStringWorklet(numStr)) {
throw new Error('Arguments must be a numeric string or number');
}
if (isZeroWorklet(numStr)) {
throw new Error('Argument must be greater than 0');
}

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);
}

// Equality function
export function equalWorklet(num1: string, num2: string): boolean {
export function equalWorklet(num1: string | number, num2: string | number): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 === scaledBigInt2;
}

// Greater than function
export function greaterThanWorklet(num1: string, num2: string): boolean {
export function greaterThanWorklet(num1: string | number, num2: string | number): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 > scaledBigInt2;
}

// Greater than or equal to function
export function greaterThanOrEqualToWorklet(num1: string, num2: string): boolean {
export function greaterThanOrEqualToWorklet(num1: string | number, num2: string | number): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 >= scaledBigInt2;
}

// Less than function
export function lessThanWorklet(num1: string, num2: string): boolean {
export function lessThanWorklet(num1: string | number, num2: string | number): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 < scaledBigInt2;
}

// Less than or equal to function
export function lessThanOrEqualToWorklet(num1: string, num2: string): boolean {
export function lessThanOrEqualToWorklet(num1: string | number, num2: string | number): boolean {
'worklet';
if (!isNumberStringWorklet(num1) || !isNumberStringWorklet(num2)) {
throw new Error('Arguments must be a numeric string');
const num1Str = toStringWorklet(num1);
const num2Str = toStringWorklet(num2);

if (!isNumberStringWorklet(num1Str) || !isNumberStringWorklet(num2Str)) {
throw new Error('Arguments must be a numeric string or number');
}
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2);
const [bigInt1, decimalPlaces1] = removeDecimalWorklet(num1Str);
const [bigInt2, decimalPlaces2] = removeDecimalWorklet(num2Str);
const scaledBigInt1 = scaleUpWorklet(bigInt1, decimalPlaces1);
const scaledBigInt2 = scaleUpWorklet(bigInt2, decimalPlaces2);
return scaledBigInt1 <= scaledBigInt2;
}

// toFixed function
export function toFixedWorklet(num: string | number, decimalPlaces: number): string {
'worklet';
const numStr = toStringWorklet(num);

if (!isNumberStringWorklet(numStr)) {
throw new Error('Argument must be a numeric string or number');
}

const [bigIntNum, numDecimalPlaces] = removeDecimalWorklet(numStr);
const scaledBigIntNum = scaleUpWorklet(bigIntNum, numDecimalPlaces);

const scaleFactor = BigInt(10) ** BigInt(20 - decimalPlaces);
const roundedBigInt = ((scaledBigIntNum + scaleFactor / BigInt(2)) / scaleFactor) * scaleFactor;

const resultStr = roundedBigInt.toString().padStart(20 + 1, '0'); // SCALE_FACTOR decimal places + at least 1 integer place
const integerPart = resultStr.slice(0, -20) || '0';
const fractionalPart = resultStr.slice(-20, -20 + decimalPlaces).padEnd(decimalPlaces, '0');

return `${integerPart}.${fractionalPart}`;
}

// Ceil function
export function ceilWorklet(num: string | number): string {
'worklet';
const numStr = toStringWorklet(num);

if (!isNumberStringWorklet(numStr)) {
throw new Error('Argument must be a numeric string or number');
}

const [bigIntNum, decimalPlaces] = removeDecimalWorklet(numStr);
const scaledBigIntNum = scaleUpWorklet(bigIntNum, decimalPlaces);

const scaleFactor = BigInt(10) ** BigInt(20);
const ceilBigInt = ((scaledBigIntNum + scaleFactor - BigInt(1)) / scaleFactor) * scaleFactor;

return formatResultWorklet(ceilBigInt);
}

// Floor function
export function floorWorklet(num: string | number): string {
'worklet';
const numStr = toStringWorklet(num);

if (!isNumberStringWorklet(numStr)) {
throw new Error('Argument must be a numeric string or number');
}

const [bigIntNum, decimalPlaces] = removeDecimalWorklet(numStr);
const scaledBigIntNum = scaleUpWorklet(bigIntNum, decimalPlaces);

const scaleFactor = BigInt(10) ** BigInt(20);
const floorBigInt = (scaledBigIntNum / scaleFactor) * scaleFactor;

return formatResultWorklet(floorBigInt);
}

// Round function
export function roundWorklet(num: string | number): string {
'worklet';
const numStr = toStringWorklet(num);

if (!isNumberStringWorklet(numStr)) {
throw new Error('Argument must be a numeric string or number');
}

const [bigIntNum, decimalPlaces] = removeDecimalWorklet(numStr);
const scaledBigIntNum = scaleUpWorklet(bigIntNum, decimalPlaces);

const scaleFactor = BigInt(10) ** BigInt(20);
const roundBigInt = ((scaledBigIntNum + scaleFactor / BigInt(2)) / scaleFactor) * scaleFactor;

return formatResultWorklet(roundBigInt);
}
Loading
Loading