-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: gas info #38
Conversation
ZKS-78 ETH gas info
AC:
Gas Price in Ether |
const [ethTransferGasCost, erc20TransferGasCost, gasPrice] = await Promise.all([ | ||
// Estimate gas for an ETH transfer. | ||
this.evmProviderService.estimateGas({ | ||
account: vitalikAddress, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty original lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments!
gasPriceInGwei: Number(formatGwei(gasPrice)), | ||
ethPrice: ethPriceInUsd, | ||
ethTransferGas: Number(ethTransferGasCost), | ||
erc20TransferGas: Number(erc20TransferGasCost), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
gasPriceInGwei: Number(formatGwei(gasPrice)), | ||
ethPrice: ethPriceInUsd, | ||
ethTransferGas: Number(ethTransferGasCost), | ||
erc20TransferGas: Number(erc20TransferGasCost), |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
export type GasInfo = { | ||
gasPriceInGwei: number; | ||
ethPrice?: number; // USD | ||
ethTransferGas: number; // units of gas | ||
erc20TransferGas: number; // units of gas | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | |
}; |
There was a problem hiding this comment.
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?
ethPrice: ethPriceInUsd?.toString(), | ||
ethTransferGas: ethTransferGasCost.toString(), | ||
erc20TransferGas: erc20TransferGasCost.toString(), |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
🤖 Linear
Closes ZKS-78
Description
Implement
ethGasInfo
method on L1 Metrics service:Returns: