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

feat: gas info #38

Merged
merged 4 commits into from
Aug 7, 2024
Merged

feat: gas info #38

merged 4 commits into from
Aug 7, 2024

Conversation

0xnigir1
Copy link
Collaborator

@0xnigir1 0xnigir1 commented Aug 5, 2024

🤖 Linear

Closes ZKS-78

Description

Implement ethGasInfo method on L1 Metrics service:

Returns:

type GasInfo = {
    gasPrice: bigint; //wei
    ethPrice?: number; // USD
    ethTransferGas: bigint; // units of gas
    erc20TransferGas: bigint; // units of gas
};

@0xnigir1 0xnigir1 requested review from 0xkenj1 and 0xyaco August 5, 2024 14:50
Copy link

linear bot commented Aug 5, 2024

ZKS-78 ETH gas info

  1. Get the following metrics by simulating txs:
    1. ETH transfer cost
    2. ERC-20 transfer cost
  2. Get gasPrice by calling eth_getGasPrice

AC:

  • getGasInfo successfully implemented on L1MetricsService
  • Unit tests

Gas Price in Ether
Gas Price in USD

const [ethTransferGasCost, erc20TransferGasCost, gasPrice] = await Promise.all([
// Estimate gas for an ETH transfer.
this.evmProviderService.estimateGas({
account: vitalikAddress,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Out of curiosity, is using vitalikAddress some kind of witty convention that everyone uses to estimate gas or does it have a special meaning behind?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

just picked vitalik address because it was the first address that came to my mind tbh

Copy link
Collaborator

Choose a reason for hiding this comment

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

Pretty original lol

Copy link
Collaborator

@0xyaco 0xyaco left a comment

Choose a reason for hiding this comment

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

Some comments!

Comment on lines 103 to 106
gasPriceInGwei: Number(formatGwei(gasPrice)),
ethPrice: ethPriceInUsd,
ethTransferGas: Number(ethTransferGasCost),
erc20TransferGas: Number(erc20TransferGasCost),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are these Number(...) safe to cast from BigInt to Number? If there's no assurance that, let's say, ethTransferGasCost < Number.MAX_SAFE_INTEGER, we might want to handle that situation.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The safest way to handle this i think it is using strings

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it's safe since usually these operations cost between 21.000 and 45.000 gas units
i should add a comment to clarify this 🫡


/**
* Calculates the transaction value in USD based on the gas used, gas price, and ether price.
* Formula: (txGas * gasPriceInWei/1e18) * etherPrice
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice comment 😍

* @returns The transaction value in USD.
*/
private transactionInUsd(txGas: bigint, gasPriceInWei: bigint, etherPrice: number): number {
return Number(formatUnits(txGas * gasPriceInWei, ETHER_DECIMALS)) * etherPrice;
Copy link
Collaborator

Choose a reason for hiding this comment

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

As a nice rule of thumb, whenever I'm working with values that represent money, I tend to avoid Number as a numerical structure due to the floating numbers precision issues in JavaScript (or any language tbh), ie

> 0.3 - 0.1
0.19999999999999998

Imagine this operation being repeated a million times, you'll start losing or adding cents out of nowhere.

Not sure how you are going to use this method as it's a private method and I don't see it being it used anywhere, but in case this method is a public method (or generates a value that will be used in a return value of a public method), I'd use some kind of BigDecimal solution.

Here is a nice explanation on the Ethers page. They explain it so much better than myself.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice. Lets use bignumber.js if we need some specific calculation and strings to return end user values.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i think this depends on the case, here we're just showing information to the end user and i don't think is too relevant full precision. usd has no more than 2 decimals (1 cent is the smallest unit if im not wrong), so there will be a rounding anyways

however you're right and in cases where precision is crucial we should never cast an use a lib like bignumber.js

also, my bad but this function i ended up not using it since money math is left to the frontend so i will delete it

Comment on lines 103 to 106
gasPriceInGwei: Number(formatGwei(gasPrice)),
ethPrice: ethPriceInUsd,
ethTransferGas: Number(ethTransferGasCost),
erc20TransferGas: Number(erc20TransferGasCost),
Copy link
Collaborator

Choose a reason for hiding this comment

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

The safest way to handle this i think it is using strings

* @returns The transaction value in USD.
*/
private transactionInUsd(txGas: bigint, gasPriceInWei: bigint, etherPrice: number): number {
return Number(formatUnits(txGas * gasPriceInWei, ETHER_DECIMALS)) * etherPrice;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice. Lets use bignumber.js if we need some specific calculation and strings to return end user values.

Comment on lines 1 to 6
export type GasInfo = {
gasPriceInGwei: number;
ethPrice?: number; // USD
ethTransferGas: number; // units of gas
erc20TransferGas: number; // units of gas
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
export type GasInfo = {
gasPriceInGwei: number;
ethPrice?: number; // USD
ethTransferGas: number; // units of gas
erc20TransferGas: number; // units of gas
};
export type GasInfo = {
gasPriceInGwei: string;
ethPrice?: string; // USD
ethTransferGas: string; // units of gas
erc20TransferGas: string; // units of gas
};

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i thought about this but wasn't sure if was necessary. should we always return strings when handling prices?

@0xnigir1 0xnigir1 requested review from 0xyaco and 0xkenj1 August 6, 2024 00:25
Comment on lines 96 to 98
ethPrice: ethPriceInUsd?.toString(),
ethTransferGas: ethTransferGasCost.toString(),
erc20TransferGas: erc20TransferGasCost.toString(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

@0xkenj1 we just had a conversation with @0xnigir1 about this and talked about calling the .toString() method on the controller, separating:

  • Business logic on the service side by letting the service methods return the "raw" values
  • Handling the serialization (aka calling .toString()) on the controller side.

What do you think?

This way we avoid leaking controller's responsibilities into services, which might cause a bigint → string → bigint situation if some other part of the application wants to consume this service and use the raw bigint values.

Copy link
Collaborator

Choose a reason for hiding this comment

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

i am in. Yea, makes totally sense since metrics module will not interact with end users, our entry point for endusers will be the api , so makes totally sense to make serialization in there.

good catch @0xyaco

@0xnigir1 0xnigir1 requested a review from 0xyaco August 6, 2024 20:00
@0xnigir1 0xnigir1 merged commit 512da31 into dev Aug 7, 2024
6 checks passed
@0xnigir1 0xnigir1 deleted the feat/gas-info branch August 7, 2024 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants