# ETH Compatible Gas Params
## Problem
We have a couple issues with our current gas system:
### 1) extra effort
developers have to take extra effort to get familiar with our gas system. Although we have detailed doc explaining it, people still get confused and asked many questions. We also need to spend extra time supporting them.
This is not a great dev UX. My personal thought is that even 90% compatibility still impacts a lot for UX and development efficiency.

### 2) inaccurate tx cost estimation
Total tx cost estimation is not accurate, it's off by 90%.
Now we basically have two types of params:
- `storageLimit=640` for normal tx, which correspond to **0.2** ACA tx cost estimation.
- `storageLimit=64000` for contract deployment and some heavy contract calls, which correspond to **20** ACA tx cost estimation.
However, normal contract call and contract deployment actually cost ~0.01 / ~1 ACA, which is huge off to the estimated value. We got asked **many many** times: "I have 10 ACA in my account, but why contract deployment still failed?"
### 3) validUntil expires
In the current encoding, `validUntil` is a absolute block number. Many devs have encountered the problem that they have a gas that worked before, but failed after (especially with truffle where they hard coded gas params in the config). A better encoding should dynamically calculate validUntil.
## Solution
The main underlying issue for the current implementation is that we need to hardcode a `storageLimit` manually, it's either a default 640, or a much bigger 64000. It requires dev to have some pre-knowledge of the tx, to choose what's the best storageLimit to use.
However, in a smooth flow, **devs don't have to know anything about the tx resources beforehand**. It's handled **automatically** by eth toolings when sending the tx.
We should be able to solve this problem by using the `estimateGas` to auto calculate tx-dependent values (`storageLimit`, `gasLimit`), and encode them into `gasLimit`. We can also encode `validUntil` into gasPrice, since `validUntil` doesn't need any context knowledge of the tx.
### Features
In order to design a gas system to solves the above issues, we need to ensure these 5 features:
- accurate tx cost estimation. `GasPrice * gasLimit` should be close to the actual cost.
- ETH tools compatible. Most existing ETH apps/codes should be able to switch to EVM+ without gas modification, and devs with ETH development experience should get up to speed with EVM+ with minimum effort.
- eth `gasPrice/gasLimit` should be able to decode to substrate `gasLimit/gasPrice/validUntil`, and vice versa.
- no validUntil issue mentioned above.
- eth GasPrice/gasLimit should be some intuitive number, for better UX.
### Idea and Assumptions
Since `estimateGas(tx) => gasLimit`, we should encode both substrate `gasLimit` and `gasPrice` to eth `gasLimit`. We can also encode `validUntil` to `gasPrice`.
The main idea is that **when decoding eth gasPrice/gasLimit to substrate params, the values don't need to be absolute accurate, it only needs to be close, and a little bigger than the estimated gas/storage for the tx to succeed.**
We will assume to following range for these variables:
- `vallidUntil`: [0, 99999999]
- `storageLimit`: [0, 2^20] = [0, 1048576]
- `gasLimit`: [30000 * 1, 30000 * 999] = [30000, 2997000]
- eth `gasLimit`: [21000, 9999999999]
- `estimatedTxCost`: [0.01 ACA, 99.9 ACA] = [1{16}0, 1{19}0]
- `gasPrice`: const 100 Gwei = [1{9}0]
### Decode Process
#### when there is no tip (default case)
Let's say eth gasLimit is encoded as `aaaabbbcc` and gasPrice is encoded as `100yyyyyyyyy`, which can be decoded to substrate gas params as following:
- `validUntil = yyyyyyyyy`
- `storageLimit = 2^min(21, cc)`
- `gasLimit = 30000 * bbb`
- `tip = extraCost / unitCostPerTip = (gasPrice - 1xxyyyyyyyyy) * gasLimit`
#### when there is tip
`gasLimit` won't be affected by tip, since tip is encoded into gasPrice `ab0yyyyyyyyy`. Let's say `ab0 = 120`, and `(120 / 100) - 1 = 0.2 = 20%`
- `tip = 20% of the original cost`
and in the eth toolings (truffle, metamask, etc..)
- `estimatedTxCost = aaaabbbcc * gasPrice ≈ aaaa{5}0 * 1{11}0 = aaaa{16}0`, which has a precision of a{16}0 = 0.01 ACA, and range of [0.01, 99] ACA
### Encode Process
first we need to calculate the actual estimated `txCost`, as well as substrate gas params `usedStorage`, `usedGas`.
```ts
const { usedStorage, usedGas } = substrateEstimateResource(tx)
const txCost = calculateTxCost(tx); // in eth 18 decimals
```
then encode these params to eth `gasLimit`
```
/* -------------------------------- */
/* ----- gasPrice: xxyyyyyyyy ----- */
/* -------------------------------- */
tip = 3%;
validUntil = 1234567 // dynamic
gasPrice
= 100 Gwei * (1 + tip) + validUntil
= 103000000000 + 1234567
= 103001234567 // xxyyyyyyyy
/* -------------------------------- */
/* ----- gasLimit: aaaaaabbcc ----- */
/* -------------------------------- */
bbbccMask = 100000;
ccMask = 100;
gasLimitChunk = 30000;
bbb = Math.ceil(usedGas / gasLimitChunk);
cc = Math.ceil(log2(usedStorage + 1));
aaaa = Math.ceil((txCost / gasPrice) / bbbccMask)
aaaa00000 = aaaa * bbbccMask
bbb00 = bb * ccMask
gasLimit = aaaa00000 + bbb00 + cc; // aaaaabbbcc
```
### Examples
| operation | storageUsed | gasUsed | gasPrice | gasLimit | gasPrice * gasLimit | actual cost |
|---------------------|-------------|---------|-------------------|----------|--------------------|-------------------|
| transfer ACA | 0 | 21000 | ~100 Gwei | 100100 | 0.01 | 0.002 |
| small contract call | 0 | 44763 | ~100 Gwei | 100200 | 0.01 | 0.003 |
| contract call | 64 | 260830 | ~100 Gwei | 400707 | 0.04 | 0.0329 |
| heavy contract call | 320 | 200358 | ~100 Gwei | 1100709 | 0.11 | 0.1 |
| super contract call | 11727 | 747877 | ~100 Gwei | 35402514 | 3.5402 | 3.53 |
| deploy contract | 11000 | 351991 | ~100 Gwei | 33401214 | 3.34 | 3.33 |
links for the above tx:
- https://blockscout.acala.network/tx/0x32e6a4bdbeaea2ea09370dc06d42ea346903e4b3a816434d03ba629d0e8e7d49
- https://blockscout.acala.network/tx/0xd727baaa33a5334d4e3a6cfe3dec6e089acfc9db1517d119458168f314a95d64
- https://blockscout.acala.network/tx/0xed4543dcff7e36b25510895eb6509569d30671e073f10e55384e01feeaa99d0d
- https://blockscout.acala.network/tx/0x9f9fd19dc05577f13ad436eee0e8f75df37c6741a8a06f162f09e648784e15bb
- https://blockscout.acala.network/tx/0xe7b71fccc7dc65927fe481327f72a3040aa7b51e1c8f62580dca25c5c40a094f
- https://blockscout.acala.network/tx/0x828b8421ccda2571144a93e426f1ed1f7fb5daf31e85714dc1f921ac9fa877aa
## Conclusion
The above proposal should solve all [problems](#Problem) for the current gas system, and satisfy all [features](#Features) required for the new one.
- `GasPrice * gasLimit` is close to the actual cost.
- ETH tools compatible. For any tx, eth tools will usually call `gasPrice()` then `estimateGas(tx)` internally, and return valid eth gas params.
- eth `gasPrice/gasLimit` is able to decode to substrate `gasLimit/gasPrice/validUntil`, and vice versa. Note that after decoding eth `gasLimit` to substrate paras, the `storageLimit` and `gasLimit` will be slightly larger than the original, which is fine and can make sure tx pass.
- validUntil is dynamically calculated everytime `gasPrice()` is called
- eth GasPrice/gasLimit is intuitive, gasPrice = 100 Gwei, and gasLimit is similar to normal eth tx.