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

Embed calculator #931

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
279 changes: 279 additions & 0 deletions components/calculator/ChainParametersForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
import type { ReactElement } from "react";
import { useState } from "react";
import { TextInput, SelectInput } from "./Inputs";
import { ResultsParams, ResultsTable } from "./ResultsTable";
import {
displayL1BaseFeeScalar,
displayL1BlobBaseFeeScalar,
calculateOverallL1DataAndStateCostsMargin,
calculateModeledDAPlusStateRevenueOnL2,
calculateTotalL1StateProposalCostsInETH,
determineDAInUse,
calculateImpliedDataGasFeePerTxUsingBlobs,
calculateImpliedDataGasFeePerTxUsingL1Calldata,
calculateImpliedDataGasFeePerTxUsingAltDAPlasmaMode,
resultsFeeScalarsAssumed,
impliedDataGasFee
} from "@/utils/calculator-helpers";
import { Loader } from "./Loader";

type ComparableTransactionType = "Base" | "Zora" | "Mint" | "Mode";
type DataAvailabilityType = "Ethereum" | "AltDA Plasma Mode";

export function ChainParametersForm(): ReactElement {
const [transactionsPerDay, setTransactionsPerDay] = useState(500000);
const [comparableTransactionType, setComparableTransactionType] =
useState<ComparableTransactionType>("General OP Mainnet");
Comment on lines +25 to +26
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inconsistent default value for 'comparableTransactionType'

The default value "General OP Mainnet" assigned to comparableTransactionType is not included in the defined ComparableTransactionType type. This will cause a type error.

Apply this diff to include "General OP Mainnet" in the ComparableTransactionType type:

 type ComparableTransactionType = 
+  "General OP Mainnet" |
   "Base" | 
   "Zora" | 
   "Mint" | 
   "Mode";

Committable suggestion was skipped due to low confidence.

const [dataAvailabilityType, setDataAvailabilityType] = useState<DataAvailabilityType>("Ethereum");
const [isFaultProofEnabled, setIsFaultProofEnabled] = useState("yes");
const [targetDataFeeMargin, setTargetDataFeeMargin] = useState(5);
const [maxBlobsPerL1Transaction, setMaxBlobsPerL1Transaction] = useState(5);
const [maxChannelDuration, setMaxChannelDuration] = useState(5);
const [outputRootPostFrequency, setOutputRootPostFrequency] = useState(1 );
const [isIncludeOutputRootCosts, setIsIncludeOutputRootCosts] = useState("yes");
const [resultsParams, setResultsParams] = useState<ResultsParams>({});
const [isLoading, setIsLoading] = useState(false);
const [showResult, setShowResult] = useState(false);

const comparableTransactionTypeOptions = [
"General OP Mainnet",
"Base",
"Zora",
"Mint",
"Mode",
];
const dataAvailabilityTypeOptions = ["Ethereum", "AltDA Plasma Mode"];
const booleanOptions = ["Yes", "No"];

const handleSubmit = async (e: any) => {
e.preventDefault();
setIsLoading(true);
setShowResult(false)

//e37
const l1BlobBaseFeeScalar = await displayL1BlobBaseFeeScalar(
stringToBoolean(isIncludeOutputRootCosts),
stringToBoolean(isFaultProofEnabled),
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);

//e38
const l1BaseFeeScalar = await displayL1BaseFeeScalar(
isIncludeOutputRootCosts,
isFaultProofEnabled,
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
targetDataFeeMargin,
dataAvailabilityType
);
Comment on lines +66 to +75
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Convert string values to booleans in 'displayL1BaseFeeScalar' call

In the call to displayL1BaseFeeScalar, the parameters isIncludeOutputRootCosts and isFaultProofEnabled are passed as strings ("Yes"/"No") instead of booleans. This may lead to unexpected behavior if the function expects boolean values.

Apply this diff to convert the string values to booleans:

 const l1BaseFeeScalar = await displayL1BaseFeeScalar(
-  isIncludeOutputRootCosts,
-  isFaultProofEnabled,
+  stringToBoolean(isIncludeOutputRootCosts),
+  stringToBoolean(isFaultProofEnabled),
   outputRootPostFrequency,
   transactionsPerDay,
   maxChannelDuration,
   comparableTransactionType,
   targetDataFeeMargin,
   dataAvailabilityType
 );

Committable suggestion was skipped due to low confidence.


// e58
const overallL1DataAndStateCostsMargin =
await calculateOverallL1DataAndStateCostsMargin(
transactionsPerDay,
comparableTransactionType,
l1BlobBaseFeeScalar,
l1BaseFeeScalar,
dataAvailabilityType,
maxChannelDuration,
outputRootPostFrequency,
isFaultProofEnabled
);

//e56
const totalL1StateProposalCostsInETH =
await calculateTotalL1StateProposalCostsInETH(
outputRootPostFrequency,
isFaultProofEnabled
);
Comment on lines +91 to +95
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Convert 'isFaultProofEnabled' to boolean in 'calculateTotalL1StateProposalCostsInETH'

The parameter isFaultProofEnabled is passed as a string, but the function may expect a boolean value. Consistently convert string values to booleans when passing parameters.

Apply this diff:

 const totalL1StateProposalCostsInETH =
   await calculateTotalL1StateProposalCostsInETH(
     outputRootPostFrequency,
-    isFaultProofEnabled
+    stringToBoolean(isFaultProofEnabled)
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const totalL1StateProposalCostsInETH =
await calculateTotalL1StateProposalCostsInETH(
outputRootPostFrequency,
isFaultProofEnabled
);
const totalL1StateProposalCostsInETH =
await calculateTotalL1StateProposalCostsInETH(
outputRootPostFrequency,
stringToBoolean(isFaultProofEnabled)
);


// e118
const modeledDAPlusStateRevenueOnL2 =
await calculateModeledDAPlusStateRevenueOnL2(
transactionsPerDay,
comparableTransactionType,
l1BlobBaseFeeScalar,
l1BaseFeeScalar
);

// e64
const impliedDataGasFeePerTxUsingBlobs =
await calculateImpliedDataGasFeePerTxUsingBlobs(
isIncludeOutputRootCosts,
isFaultProofEnabled,
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);

// e67
const impliedDataGasFeePerTxUsingL1Calldata =
await calculateImpliedDataGasFeePerTxUsingL1Calldata(
isIncludeOutputRootCosts,
isFaultProofEnabled,
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);
Comment on lines +120 to +130
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Convert string values to booleans in 'calculateImpliedDataGasFeePerTxUsingL1Calldata'

The parameters isIncludeOutputRootCosts and isFaultProofEnabled should be converted to booleans before being passed to the function.

Apply this diff:

 const impliedDataGasFeePerTxUsingL1Calldata =
   await calculateImpliedDataGasFeePerTxUsingL1Calldata(
-    isIncludeOutputRootCosts,
-    isFaultProofEnabled,
+    stringToBoolean(isIncludeOutputRootCosts),
+    stringToBoolean(isFaultProofEnabled),
     outputRootPostFrequency,
     transactionsPerDay,
     maxChannelDuration,
     comparableTransactionType,
     dataAvailabilityType,
     targetDataFeeMargin
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const impliedDataGasFeePerTxUsingL1Calldata =
await calculateImpliedDataGasFeePerTxUsingL1Calldata(
isIncludeOutputRootCosts,
isFaultProofEnabled,
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);
const impliedDataGasFeePerTxUsingL1Calldata =
await calculateImpliedDataGasFeePerTxUsingL1Calldata(
stringToBoolean(isIncludeOutputRootCosts),
stringToBoolean(isFaultProofEnabled),
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);


// e66
const impliedDataGasFeePerTxUsingAltDAPlasmaMode =
await calculateImpliedDataGasFeePerTxUsingAltDAPlasmaMode(
isIncludeOutputRootCosts,
isFaultProofEnabled,
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);
Comment on lines +133 to +143
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Convert string values to booleans in 'calculateImpliedDataGasFeePerTxUsingAltDAPlasmaMode'

Consistently convert isIncludeOutputRootCosts and isFaultProofEnabled to booleans when invoking this function.

Apply this diff:

 const impliedDataGasFeePerTxUsingAltDAPlasmaMode =
   await calculateImpliedDataGasFeePerTxUsingAltDAPlasmaMode(
-    isIncludeOutputRootCosts,
-    isFaultProofEnabled,
+    stringToBoolean(isIncludeOutputRootCosts),
+    stringToBoolean(isFaultProofEnabled),
     outputRootPostFrequency,
     transactionsPerDay,
     maxChannelDuration,
     comparableTransactionType,
     dataAvailabilityType,
     targetDataFeeMargin
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const impliedDataGasFeePerTxUsingAltDAPlasmaMode =
await calculateImpliedDataGasFeePerTxUsingAltDAPlasmaMode(
isIncludeOutputRootCosts,
isFaultProofEnabled,
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);
const impliedDataGasFeePerTxUsingAltDAPlasmaMode =
await calculateImpliedDataGasFeePerTxUsingAltDAPlasmaMode(
stringToBoolean(isIncludeOutputRootCosts),
stringToBoolean(isFaultProofEnabled),
outputRootPostFrequency,
transactionsPerDay,
maxChannelDuration,
comparableTransactionType,
dataAvailabilityType,
targetDataFeeMargin
);


const dataAvailabilityInUse = determineDAInUse(dataAvailabilityType);

const assumedFeeScalarMessage = resultsFeeScalarsAssumed(
comparableTransactionType, // e15
transactionsPerDay, // e14
dataAvailabilityType, // E16
targetDataFeeMargin, // E18
isIncludeOutputRootCosts, // E24
maxChannelDuration // E22
);
const impliedDataGasFeeMessage = await impliedDataGasFee(dataAvailabilityType)

const data = {
dataAvailabilityType, // e16
l1BlobBaseFeeScalar, // e37
l1BaseFeeScalar, // e38
overallL1DataAndStateCostsMargin, // e58
totalL1StateProposalCostsInETH, // e56
modeledDAPlusStateRevenueOnL2, // e118
dataAvailabilityInUse, // F35
impliedDataGasFeePerTxUsingBlobs, // e64
impliedDataGasFeePerTxUsingL1Calldata, // e67
impliedDataGasFeePerTxUsingAltDAPlasmaMode, // e66
assumedFeeScalarMessage,
impliedDataGasFeeMessage,
};
setResultsParams(data);
setIsLoading(false);
setShowResult(true)
};

const stringToBoolean = (value: string): boolean => {
return value === "yes" || value === "Yes" ? true : false;
}
Comment on lines +176 to +178
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify 'stringToBoolean' function and handle case sensitivity

The stringToBoolean function can be simplified by removing the unnecessary ternary operator and making the comparison case-insensitive.

Apply this diff to simplify the function:

 const stringToBoolean = (value: string): boolean => {
-  return value === "yes" || value === "Yes" ? true : false;
+  return value.toLowerCase() === "yes";
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const stringToBoolean = (value: string): boolean => {
return value === "yes" || value === "Yes" ? true : false;
}
const stringToBoolean = (value: string): boolean => {
return value.toLowerCase() === "yes";
}
🧰 Tools
🪛 Biome

[error] 177-177: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)


return (
<div>
<form className="calculator-form" onSubmit={handleSubmit}>
<div className="calculator-chain-inputs">
<h2 className="calculator-heading_sub">Chain Inputs</h2>
<TextInput
otherProps={{ value: transactionsPerDay }}
onInputChange={setTransactionsPerDay}
labelClass="calculator-label"
description="Txs / Day, excluding Internal system transactions"
type="number"
className="calculator-input mt-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Transactions per Day"
/>

<SelectInput
labelClass="calculator-label"
data={comparableTransactionTypeOptions}
onSelect={setComparableTransactionType}
description="What are the transaction types are similar to"
className="calculator-select t-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Comparable Transaction Type"
/>

<SelectInput
labelClass="calculator-label"
data={dataAvailabilityTypeOptions}
onSelect={setDataAvailabilityType}
description="Ethereum (Blobs or Calldata) or AltDA Plasma Mode (Alt-DA)"
className="calculator-select t-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Data Availability Type"
/>

<SelectInput
labelClass="calculator-label"
data={booleanOptions}
onSelect={setIsFaultProofEnabled}
description="Are Fault Proofs enabled on the chain? (Note: Ethereum DA Only)"
className="calculator-select t-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Fault Proofs Enabled"
/>

<TextInput
otherProps={{ value: targetDataFeeMargin }}
onInputChange={setTargetDataFeeMargin}
description="Buffer charged on top of L1 & Blob Data costs"
labelClass="calculator-label"
type="number"
className="calculator-input mt-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Target Data Fee Margin"
/>
</div>
<div className="calculator-advanced-inputs">
<h2 className="calculator-heading_sub">Advanced Inputs</h2>
<TextInput
otherProps={{ value: maxBlobsPerL1Transaction }}
onInputChange={setMaxBlobsPerL1Transaction}
labelClass="calculator-label"
description="Maximum amount of blobs submitted per L1 Transaction (max: 6)"
type="number"
className="calculator-input mt-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Max # of Blobs per L1 Transaction"
/>

<TextInput
description="Max hours are we willing to wait between batch submissions"
otherProps={{ value: maxChannelDuration }}
onInputChange={setMaxChannelDuration}
labelClass="calculator-label"
type="number"
className="calculator-input mt-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="op-batcher Max Channel Duration (hours)"
/>
<TextInput
description="Hours between each state proposals on L1 (0 if chain doesn't pay)"
otherProps={{ value: outputRootPostFrequency }}
onInputChange={setOutputRootPostFrequency}
labelClass="calculator-label"
type="number"
className="calculator-input mt-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Output Root Post Frequency (hours)"
/>
<SelectInput
description="Is the cost of state/output proposals passed on to L2 users?"
labelClass="calculator-label"
data={booleanOptions}
onSelect={setIsIncludeOutputRootCosts}
className="calculator-select mt-1 sm:text-lg py-1 px-2 sm:py-2 sm:px-4"
label="Include Root Costs in User Fees?"
/>
</div>
<button className="calculator-button" type="submit">
Calculate
</button>
</form>
{isLoading && <Loader />}
{!isLoading && showResult && <ResultsTable data={resultsParams} />}
</div>
);
}
31 changes: 31 additions & 0 deletions components/calculator/Inputs/CheckboxInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";

type Props = {
otherProps?: any;
className?: string;
label?: string;
description?:string;
handleToggle: (e: any) => void;
};
krofax marked this conversation as resolved.
Show resolved Hide resolved

export const CheckboxInput: React.FC<Props> = React.memo(
({ otherProps, label, description, className, handleToggle }) => {
function onCheckboxChange(e: any) {
const isChecked = e.target.checked;
handleToggle(isChecked);
}
return (
<div className="flex items-center gap-2">
{label && <label className="text-sm">{label}</label>}
<p className="text-xs my-1">{description}</p>
<input
{...otherProps}
type="checkbox"
onChange={onCheckboxChange}
className={`${className} meta-checkbox accent-custom-puple toggle bg-custom-puple`}
/>
</div>
);
}
);
krofax marked this conversation as resolved.
Show resolved Hide resolved
CheckboxInput.displayName = "CheckboxInput";
51 changes: 51 additions & 0 deletions components/calculator/Inputs/SelectInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";

type Props = {
className?: string;
label?: string;
data: any[];
otherProps?: any;
description?: string;
labelClass?: string;
onSelect?: (e: any) => void;
};
Comment on lines +1 to +11
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve type safety by refining prop types

While the Props type definition is comprehensive, there are a few areas where we can improve type safety:

  1. The data prop is typed as any[]. Consider using a more specific type that reflects the expected structure of the data.
  2. The otherProps is typed as any. It would be better to explicitly define the expected properties or use a more specific type.
  3. The onSelect prop is typed as (e: any) => void, but it's called with a string value in the component. Update this to match the actual usage.

Consider updating the Props type as follows:

type Props = {
  className?: string;
  label?: string;
  data: string[]; // Assuming the data array contains strings
  otherProps?: React.SelectHTMLAttributes<HTMLSelectElement>;
  description?: string;
  labelClass?: string;
  onSelect?: (value: string) => void;
};

This will provide better type safety and make the component's API more clear to other developers.


export const SelectInput: React.FC<Props> = React.memo(
({ className, labelClass, description, otherProps, label, data, onSelect }) => {
const handleSelect = (e: any) => {
const value = e.target.value
onSelect(value)
}
Comment on lines +15 to +18
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add safety check for onSelect callback

The handleSelect function correctly extracts the selected value from the event. However, it doesn't check if onSelect is defined before calling it.

Add a safety check to prevent potential runtime errors:

const handleSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
  const value = e.target.value;
  if (onSelect) {
    onSelect(value);
  }
};

This change ensures that the code won't throw an error if onSelect is not provided.

return (
<div className="flex flex-col ">
{label && (
<label className={`font-semibold text-sm md:text-xl ${labelClass}`}>
{label}
</label>
)}
<p className="text-xs my-1 calcularor-label_description">
{description}
</p>
<div className="grid">
<select
{...otherProps}
onChange={handleSelect}
className={`${className} appearance-none row-start-1 col-start-1 focus:bg-background bg-transparent py-1 px-2 border w-full rounded-lg border-custom-puple`}
>
{data.map((selectValue, index) => (
<option
key={index}
className="cursor-pointer "
value={selectValue}
>
{selectValue}
</option>
))}
</select>
{/* <IoIosArrowDown className="place-self-end mb-3 sm:mx-4 row-start-1 col-start-1 pointer-events-none " /> */}
</div>
</div>
Comment on lines +19 to +47
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve render logic and clean up commented code

The component's render logic is well-structured, with good use of conditional rendering for the label. However, there are a few areas for improvement:

  1. The className for the select element is quite long. Consider extracting it to a constant or using a CSS module for better maintainability.
  2. There's a commented-out icon component at the end. If this is no longer needed, it should be removed.
  3. The description is always rendered, even if it's not provided.

Consider these improvements:

  1. Extract the select element's className:

    const selectClassName = `appearance-none row-start-1 col-start-1 focus:bg-background bg-transparent py-1 px-2 border w-full rounded-lg border-custom-puple ${className}`;
  2. Remove the commented-out icon component if it's no longer needed.

  3. Add conditional rendering for the description:

    {description && (
      <p className="text-xs my-1 calcularor-label_description">
        {description}
      </p>
    )}
  4. Consider using React.ChangeEvent<HTMLSelectElement> for the handleSelect function parameter type.

These changes will improve code cleanliness and prevent rendering empty elements unnecessarily.

);
}
);
SelectInput.displayName = "SelectInput";
Loading
Loading