From 27d921c9fe27f827991f6d2c9795f7d5126654fe Mon Sep 17 00:00:00 2001 From: Frederico Date: Mon, 16 Dec 2024 17:44:04 +0100 Subject: [PATCH 01/16] implementation of the TFM tool --- transaction_fee_models/README.md | 5 + transaction_fee_models/eip-vs-stablefee.ipynb | 155 ++++++++++ transaction_fee_models/simulation.py | 115 +++++++ .../simulation_parameters.py | 35 +++ transaction_fee_models/tx_fees_models.py | 291 ++++++++++++++++++ 5 files changed, 601 insertions(+) create mode 100644 transaction_fee_models/README.md create mode 100644 transaction_fee_models/eip-vs-stablefee.ipynb create mode 100644 transaction_fee_models/simulation.py create mode 100644 transaction_fee_models/simulation_parameters.py create mode 100644 transaction_fee_models/tx_fees_models.py diff --git a/transaction_fee_models/README.md b/transaction_fee_models/README.md new file mode 100644 index 0000000..0af048b --- /dev/null +++ b/transaction_fee_models/README.md @@ -0,0 +1,5 @@ +# Transaction Fee Models + +This folder contains the implementation of a tool that compares two different Transaction Fee Models (TFMs) under the same "demand for blockspace". + +Please refer to the blog post [not-published-yet](not-published-yet) for more details, and to the notebook `eip-vs-stablefee.ipynb` for usage. diff --git a/transaction_fee_models/eip-vs-stablefee.ipynb b/transaction_fee_models/eip-vs-stablefee.ipynb new file mode 100644 index 0000000..631be52 --- /dev/null +++ b/transaction_fee_models/eip-vs-stablefee.ipynb @@ -0,0 +1,155 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Transaction Fees on Nomos" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "\n", + "import plotly.express as px\n", + "import pandas as pd\n", + "pd.options.plotting.backend = \"plotly\"\n", + "from simulation import run_simulation\n", + "from simulation_parameters import SimulationParameters\n", + "from tx_fees_models import PROTOCOL_CONSTANTS" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "days = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "params = SimulationParameters(\n", + " num_blocks=int(days * 24 * 60 * 60 / 12), # number of blocks in \"days\",\n", + " demand_sizes=[50, 1000, 2000], # very low, normal, very high demand\n", + " demand_probabilities=[0.05, 0.9, 0.05], # they must sum to 1\n", + " fee_cap_range=(-0.05, 0.3), \n", + " max_tip_pct=0.1,\n", + " scale_block_size_limits=(0.0, 0.2),\n", + " probability_stop_below_gas_limit=0.5\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_stats_merged, df_chain_stats_merged = run_simulation(params)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_stats_merged[[\"price_EIP\", \"price_StableFee\"]].plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_stats_merged[[\"num_tx_EIP\", \"num_tx_StableFee\"]].plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_stats_merged[[\"tot_paid_EIP\", \"tot_paid_StableFee\"]].plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(df_stats_merged[\"num_tx_EIP\"].sub(df_stats_merged[\"num_tx_StableFee\"]) > 0).value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_chain_stats_merged[[\"tot_gas_used_EIP\", \"tot_gas_used_StableFee\"]].plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = df_chain_stats_merged[\"tot_gas_used_EIP\"].plot.bar()\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " x0=0,\n", + " y0=PROTOCOL_CONSTANTS[\"TARGET_GAS_USED\"],\n", + " x1=len(df_chain_stats_merged),\n", + " y1=PROTOCOL_CONSTANTS[\"TARGET_GAS_USED\"],\n", + " line=dict(color=\"red\", width=2)\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "python-3.12-abm", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/transaction_fee_models/simulation.py b/transaction_fee_models/simulation.py new file mode 100644 index 0000000..f1041dc --- /dev/null +++ b/transaction_fee_models/simulation.py @@ -0,0 +1,115 @@ +from typing import List, Dict, Any, Tuple +import numpy as np +import pandas as pd +import tqdm + +from simulation_parameters import SimulationParameters +from tx_fees_models import Block, Blockchain, MemPool, TransactionFeeMechanism, \ + EIP1559, StableFee, Transaction, create_demand + + +def _generate_random_bool(p_true=0.5): + return np.random.choice([True, False], p=(p_true, 1.-p_true), size=1)[0] + + +def run_simulation(params: SimulationParameters) -> Tuple[pd.DataFrame, pd.DataFrame]: + + blockchain:Dict[str,Blockchain] = { + "EIP": Blockchain(), + "StableFee": Blockchain() + } + + mempool:Dict[str, MemPool] = { + "EIP": MemPool(), + "StableFee": MemPool() + } + + tf_models:dict[str, TransactionFeeMechanism] = { + "EIP": EIP1559(), + "StableFee": StableFee() + } + + df_stats:Dict[str, pd.DataFrame] = { + "EIP": pd.DataFrame( + 0, + columns=["demand_size", "num_tx", "price", "tot_paid"], + index=range(params.num_blocks) + ), + "StableFee": pd.DataFrame( + 0, + columns=["demand_size", "num_tx", "price", "tot_paid"], + index=range(params.num_blocks) + ) + } + + pbar = tqdm.tqdm(total=params.num_blocks) + + for b in range(params.num_blocks): + + # transactions are created with random values + demand_size:int = np.random.choice(params.demand_sizes, p=params.demand_probabilities, size=1)[0] + + demand:List[Transaction] = create_demand( + demand_size, + fee_cap_range=params.fee_cap_range, + max_tip_pct=params.max_tip_pct, + variable_gas=True + ) + + stop_below_gas_limit:bool = _generate_random_bool(params.probability_stop_below_gas_limit) + + scale_block_size = np.random.uniform( + params.scale_block_size_limits[0], params.scale_block_size_limits[1] + ) + + for chain, pool, tfm, stats in zip( + blockchain.values(), mempool.values(), tf_models.values(), df_stats.values() + ): + scaled_demand:List[Transaction] = tfm.scale_demand(demand) + + # transactions are added to the mempool + pool.add_txs(scaled_demand) + + # transactions are selected from the mempool based on the gas premium + selected_transactions, to_be_purged_transactions = tfm.select_transactions( + pool, + stop_below_gas_limit=stop_below_gas_limit, + scale_block_size=scale_block_size + ) + + # selected transactions are added to the blockchain + chain.add_block(Block(selected_transactions)) + + # the price is updated based on the selected transactions + tfm.update_price(chain) + + # base fee is updated for the next round + stats.loc[b, "demand_size"] = demand_size + stats.loc[b, "num_tx"] = len(selected_transactions) + stats.loc[b, "price"] = tfm.get_current_price() + stats.loc[b, "tot_paid"] = tfm.total_paid_fees(selected_transactions) + + # clear the mempool + pool.remove_txs(selected_transactions) + pool.remove_txs(to_be_purged_transactions) + + pbar.update(1) + + pbar.close() + + df_stats_merged = pd.concat( + [df_stats[mechanism].add_suffix("_" + mechanism) for mechanism in df_stats.keys()], + axis=1 + ) + + df_chain_stats:Dict[str,pd.DataFrame] = { + "EIP": blockchain["EIP"].compute_stats(), + "StableFee": blockchain["StableFee"].compute_stats() + } + + df_chain_stats_merged = pd.concat( + [df_chain_stats[mechanism].add_suffix("_" + mechanism) for mechanism in df_stats.keys()], + axis=1 + ) + + return df_stats_merged, df_chain_stats_merged diff --git a/transaction_fee_models/simulation_parameters.py b/transaction_fee_models/simulation_parameters.py new file mode 100644 index 0000000..13490dc --- /dev/null +++ b/transaction_fee_models/simulation_parameters.py @@ -0,0 +1,35 @@ +from dataclasses import dataclass +from typing import List, Dict, Any, Tuple + + +@dataclass +class SimulationParameters: + def __init__( + self, + num_blocks: int = 100, + demand_sizes: List[int] = [50, 1000, 2000], # very low, normal, very high demand + demand_probabilities: List[float] = [0.05, 0.9, 0.05], + fee_cap_range:Tuple[float]=(-0.1, 0.1), + max_tip_pct:float=0.1, + scale_block_size_limits: Tuple[float] = (0.0, 0.2), + probability_stop_below_gas_limit: float = 0.5, + ): + assert len(demand_sizes) == len(demand_probabilities), "demand_sizes and demand_probabilities must have the same length" + assert abs(sum(demand_probabilities) - 1.0) < 1.e-12, "demand_probabilities must sum to 1.0" + assert 0.0 <= probability_stop_below_gas_limit <= 1.0, "probability_stop_below_gas_limit must be between 0 and 1" + assert len(scale_block_size_limits) == 2, "scale_block_size_limits must be a tuple of length 2" + assert scale_block_size_limits[0] <= scale_block_size_limits[1], "scale_block_size_limits must be in increasing order" + assert all([0.0 <= p <= 1.0 for p in demand_probabilities]), "demand_probabilities must be between 0 and 1" + assert scale_block_size_limits[0] >= 0.0, "scale_block_size_limits must be positive" + assert scale_block_size_limits[1] <= 1.0, "scale_block_size_limits must be less than or equal to 1.0" + assert len(fee_cap_range) == 2, "fee_cap_range must be a tuple of length 2" + assert fee_cap_range[0] <= fee_cap_range[1], "fee_cap_range must be in increasing order" + assert max_tip_pct >= 0.0, "max_tip_pct must be positive" + + self.num_blocks = num_blocks + self.demand_sizes = demand_sizes + self.demand_probabilities = demand_probabilities + self.fee_cap_range = fee_cap_range + self.max_tip_pct = max_tip_pct + self.scale_block_size_limits = scale_block_size_limits + self.probability_stop_below_gas_limit = probability_stop_below_gas_limit \ No newline at end of file diff --git a/transaction_fee_models/tx_fees_models.py b/transaction_fee_models/tx_fees_models.py new file mode 100644 index 0000000..5b92307 --- /dev/null +++ b/transaction_fee_models/tx_fees_models.py @@ -0,0 +1,291 @@ +import numpy as np +import pandas as pd +from typing import List, Tuple, Dict +from dataclasses import dataclass, field +import uuid +import copy + + +PROTOCOL_CONSTANTS = { + "TARGET_GAS_USED": 12500000.0, # 12.5 million gas + "MAX_GAS_ALLOWED": 25000000.0, # 25 million gas + "INITIAL_BASEFEE": 1.0, # 10^9 wei +} + + +class Transaction: + + def __init__(self, gas_used:float, fee_cap:float, tip:float): + self.gas_used = gas_used + self.fee_cap = fee_cap + self.tip = tip # this is ignored in the case of Stable Fee + self.tx_hash = uuid.uuid4().int + + +@dataclass +class MemPool: + pool: Dict[int, Transaction] = field(default_factory=dict) + + def add_tx(self, tx: Transaction): + self.pool[tx.tx_hash] = tx + + def add_txs(self, txs: List[Transaction]): + for tx in txs: + self.add_tx(tx) + + def remove_tx(self, tx: Transaction): + self.pool.pop(tx.tx_hash) + + def remove_txs(self, txs: List[Transaction]): + for tx in txs: + self.remove_tx(tx) + + def __len__(self): + return len(self.pool) + + +@dataclass +class Block(): + txs:Dict[int, Transaction] + + def add_tx(self, tx: Transaction): + self.txs[tx.tx_hash] = tx + + def add_txs(self, txs: List[Transaction]): + for tx in txs: + self.add_tx(tx) + + def print_dataframe(self) -> pd.DataFrame: + _df = pd.DataFrame( + [ + [tx.gas_used, tx.fee_cap, tx.tip] + for tx in self.txs + ], + columns=['gas_used', 'fee_cap', 'tip'], + index=range(len(self.txs)) + ) + _df.index.name = "tx" + return _df + + +class Blockchain: + + def __init__(self, blocks: List[Block]=None): + self.blocks:List[Block] = [] + if blocks: + self.add_blocks(blocks) + + def add_block(self, block: Block): + self.blocks.append(block) + + def add_blocks(self, blocks: List[Block]): + for block in blocks: + self.add_block(block) + + def get_last_block(self): + return self.blocks[-1] + + def compute_stats(self) -> pd.DataFrame: + # compute total gas used, total gas premium, total fee cap, average gas premium, average fee cap + _df = pd.DataFrame( + 0., + columns=['tot_gas_used', 'tot_fee_cap', 'tot_tips', 'avg_fee_cap', 'avg_tips', 'gas_target'], + index=range(len(self.blocks)) + ) + + for b, block in enumerate(self.blocks): + num_tx = float(len(block.txs)) + tot_gas_used, tot_fee_cap, tot_tips = np.sum( + [ + [tx.gas_used, tx.fee_cap, tx.tip] + for tx in block.txs + ], + axis=0 + ) + _df.iloc[b,:] = np.array( + [ + tot_gas_used, tot_fee_cap, tot_tips, + tot_fee_cap/num_tx, tot_tips/num_tx, PROTOCOL_CONSTANTS["TARGET_GAS_USED"] + ] + ) + + return _df + + +def create_demand( + num_txs: int, + fee_cap_range:Tuple[float]=(-0.1, 0.1), + max_tip_pct:float=0.1, + variable_gas:bool=True + ) -> List[Transaction]: + + # these are levels that need to be scaled by the price/base fee of the specific transaction fee model + fee_caps = (1. + np.random.uniform(fee_cap_range[0], fee_cap_range[1], num_txs)) + tip = np.random.uniform(0., max_tip_pct, num_txs) # 0.1 is the max gas premium factor + + if variable_gas: + return _create_demand_variable_gas(fee_caps, tip) + else: + return _create_demand_const_gas(fee_caps, tip) + + +def _create_demand_const_gas(fee_caps:np.ndarray, tip:np.ndarray) -> List[Transaction]: + + demand: List[Transaction] = [] + gas_used = 21000 + + for fc, tp in zip(fee_caps, tip): + tx = Transaction( + gas_used = gas_used, + fee_cap = fc, + tip= tp + ) + demand.append(tx) + + return demand + + +def _create_demand_variable_gas(fee_caps:np.ndarray, tip:np.ndarray) -> List[Transaction]: + + demand: List[Transaction] = [] + + gas_used = np.random.choice( + [ + 21_000, # eth transfer + 45_000, # erc20 transfer + 50_000, # token approval + 200_000, # token swap + 150_000, # NFT (ERC721) minting + 75_000, # NFT transfer + 120_000, # NFT (ERC1155) minting + 500_000, # smart contract deployment + ], + p=(0.3, 0.3, 0.1, 0.2, 0.03, 0.03, 0.03, 0.01), + size=len(fee_caps) + ) + + for gu, fc, tp in zip(gas_used, fee_caps, tip): + tx = Transaction( + gas_used = gu, + fee_cap = fc, + tip = tp + ) + demand.append(tx) + + return demand + + +class TransactionFeeMechanism: + + def __init__(self): + self.price:List[float] = [] + self.price.append(PROTOCOL_CONSTANTS["INITIAL_BASEFEE"]) + + def update_price(self, blockchain:Blockchain): + raise NotImplementedError + + def get_current_price(self) -> float: + return self.price[-1] + + def scale_demand(self, demand:List[Transaction]) -> List[Transaction]: + cur_price:float = self.get_current_price() + scaled_demand = copy.deepcopy(demand) + for tx in scaled_demand: + tx.fee_cap *= cur_price + tx.tip *= cur_price + return scaled_demand + + def _select_from_sorted_txs( + self, sorted_txs:List[Transaction], stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + ) -> Tuple[List[Transaction], List[Transaction]]: + + # select transactions so that the sum of gas used is less than the block gas limit + selected_txs_idx = 0 + to_be_purged_txs_idx = 0 + gas_used = 0 + + # introduce some randomness in the selection in case there are too many transactions + # this is to simulate the fact that miners may not always select the most profitable transactions + # this increases or decreases the number of transactions selected based on the stop_below_gas_limit flag, + # which is also randomly selected + fac = 1.0 + (1. - 2.*stop_below_gas_limit) * scale_block_size + + for tx in sorted_txs: + if gas_used + tx.gas_used < fac * PROTOCOL_CONSTANTS["TARGET_GAS_USED"]: + selected_txs_idx += 1 + if gas_used + tx.gas_used < 4 * PROTOCOL_CONSTANTS["MAX_GAS_ALLOWED"]: # enough space for 4 full blocks + to_be_purged_txs_idx += 1 + else: + break + gas_used += tx.gas_used + + return sorted_txs[:selected_txs_idx], sorted_txs[to_be_purged_txs_idx:] + + def select_transactions( + self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + ) -> Tuple[List[Transaction], List[Transaction]]: + raise NotImplementedError + + def total_paid_fees(self, txs: List[Transaction]) -> float: + raise NotImplementedError + + +class EIP1559(TransactionFeeMechanism): + + def __init__(self): + self.base_factor:float = 1./8. + super().__init__() + + def update_price(self, blockchain:Blockchain) -> float: + base_fee:float = self.get_current_price() + gas_used:float = sum([tx.gas_used for tx in blockchain.get_last_block().txs]) + delta:float = (gas_used - PROTOCOL_CONSTANTS["TARGET_GAS_USED"])/PROTOCOL_CONSTANTS["TARGET_GAS_USED"] + self.price.append( + base_fee * np.exp(delta * self.base_factor) # (1. + delta * self.base_factor) + ) + + def select_transactions( + self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + ) -> Tuple[List[Transaction], List[Transaction]]: + + base_fee:float = self.get_current_price() + + # Sort transactions by fee cap + sorted_txs:List[Transaction] = sorted( + mempool.pool.values(), + key=lambda tx: min(tx.fee_cap, base_fee+tx.tip) * tx.gas_used, + reverse=True + ) + + return self._select_from_sorted_txs(sorted_txs, stop_below_gas_limit, scale_block_size) + + def total_paid_fees(self, txs: List[Transaction]) -> float: + base_fee:float = self.get_current_price() + return sum([min(tx.fee_cap, base_fee+tx.tip) * tx.gas_used for tx in txs]) + + +class StableFee(TransactionFeeMechanism): + + def __init__(self): + super().__init__() + + def update_price(self, blockchain:Blockchain): + new_price:float = np.min([tx.fee_cap for tx in blockchain.get_last_block().txs]) + self.price.append(new_price) + + def select_transactions( + self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + ) -> Tuple[List[Transaction], List[Transaction]]: + + # Sort transactions by fee cap + sorted_txs = sorted( + mempool.pool.values(), + key=lambda tx: tx.fee_cap * tx.gas_used, + reverse=True + ) + + return self._select_from_sorted_txs(sorted_txs, stop_below_gas_limit, scale_block_size) + + def total_paid_fees(self, txs: List[Transaction]) -> float: + price:float = self.get_current_price() + return sum([price * tx.gas_used for tx in txs]) \ No newline at end of file From 7402e9f54cceb51c7bd5c49e06b14c296eb929f9 Mon Sep 17 00:00:00 2001 From: Frederico Date: Mon, 16 Dec 2024 19:54:19 +0100 Subject: [PATCH 02/16] in EIP, base fee and actual price are different things. --- transaction_fee_models/tx_fees_models.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/transaction_fee_models/tx_fees_models.py b/transaction_fee_models/tx_fees_models.py index 5b92307..9ece398 100644 --- a/transaction_fee_models/tx_fees_models.py +++ b/transaction_fee_models/tx_fees_models.py @@ -234,21 +234,26 @@ class EIP1559(TransactionFeeMechanism): def __init__(self): self.base_factor:float = 1./8. + self.base_fee:List[float] = [] + self.base_fee.append(PROTOCOL_CONSTANTS["INITIAL_BASEFEE"]) super().__init__() def update_price(self, blockchain:Blockchain) -> float: - base_fee:float = self.get_current_price() - gas_used:float = sum([tx.gas_used for tx in blockchain.get_last_block().txs]) + base_fee:float = self.base_fee[-1] + last_txs:List[Transaction] = blockchain.get_last_block().txs + gas_used:float = sum([tx.gas_used for tx in last_txs]) delta:float = (gas_used - PROTOCOL_CONSTANTS["TARGET_GAS_USED"])/PROTOCOL_CONSTANTS["TARGET_GAS_USED"] - self.price.append( + self.base_fee.append( base_fee * np.exp(delta * self.base_factor) # (1. + delta * self.base_factor) ) + sum_price:float = sum([min(tx.fee_cap, base_fee+tx.tip) for tx in last_txs]) + self.price.append(sum_price/float(len(last_txs))) def select_transactions( self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0 ) -> Tuple[List[Transaction], List[Transaction]]: - base_fee:float = self.get_current_price() + base_fee:float = self.base_fee[-1] # Sort transactions by fee cap sorted_txs:List[Transaction] = sorted( @@ -260,7 +265,7 @@ class EIP1559(TransactionFeeMechanism): return self._select_from_sorted_txs(sorted_txs, stop_below_gas_limit, scale_block_size) def total_paid_fees(self, txs: List[Transaction]) -> float: - base_fee:float = self.get_current_price() + base_fee:float = self.base_fee[-1] return sum([min(tx.fee_cap, base_fee+tx.tip) * tx.gas_used for tx in txs]) From 702fb35b7be507a2f2060e7a5bac218d04b45a82 Mon Sep 17 00:00:00 2001 From: Frederico Date: Mon, 16 Dec 2024 22:05:08 +0100 Subject: [PATCH 03/16] ignore png files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7712c5f..7703656 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ simlib/**/target simlib/**/Cargo.lock simlib/test.json *.ignore* +*.png From ec8dc2f433b01a737275ae96b3674807265ce252 Mon Sep 17 00:00:00 2001 From: Frederico Date: Mon, 16 Dec 2024 22:05:28 +0100 Subject: [PATCH 04/16] implementation of min price --- transaction_fee_models/tx_fees_models.py | 36 ++++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/transaction_fee_models/tx_fees_models.py b/transaction_fee_models/tx_fees_models.py index 9ece398..8178032 100644 --- a/transaction_fee_models/tx_fees_models.py +++ b/transaction_fee_models/tx_fees_models.py @@ -9,7 +9,8 @@ import copy PROTOCOL_CONSTANTS = { "TARGET_GAS_USED": 12500000.0, # 12.5 million gas "MAX_GAS_ALLOWED": 25000000.0, # 25 million gas - "INITIAL_BASEFEE": 1.0, # 10^9 wei + "INITIAL_BASEFEE": 1.0, # 10^9 wei = 1 Gwei + "MIN_PRICE": 0.1, # 10^8 wei = 0.1 Gwei } @@ -120,7 +121,8 @@ def create_demand( ) -> List[Transaction]: # these are levels that need to be scaled by the price/base fee of the specific transaction fee model - fee_caps = (1. + np.random.uniform(fee_cap_range[0], fee_cap_range[1], num_txs)) + fee_caps = 1. + np.random.normal(fee_cap_range[0], fee_cap_range[1], num_txs) + #fee_caps = (1. + np.random.uniform(fee_cap_range[0], fee_cap_range[1], num_txs)) tip = np.random.uniform(0., max_tip_pct, num_txs) # 0.1 is the max gas premium factor if variable_gas: @@ -196,7 +198,7 @@ class TransactionFeeMechanism: return scaled_demand def _select_from_sorted_txs( - self, sorted_txs:List[Transaction], stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + self, sorted_txs:List[Transaction], stop_below_gas_limit:bool=False, scale_block_size:float=1.0, purge_after:int=4 ) -> Tuple[List[Transaction], List[Transaction]]: # select transactions so that the sum of gas used is less than the block gas limit @@ -213,7 +215,7 @@ class TransactionFeeMechanism: for tx in sorted_txs: if gas_used + tx.gas_used < fac * PROTOCOL_CONSTANTS["TARGET_GAS_USED"]: selected_txs_idx += 1 - if gas_used + tx.gas_used < 4 * PROTOCOL_CONSTANTS["MAX_GAS_ALLOWED"]: # enough space for 4 full blocks + if gas_used + tx.gas_used < purge_after * PROTOCOL_CONSTANTS["MAX_GAS_ALLOWED"]: # enough space for X full blocks to_be_purged_txs_idx += 1 else: break @@ -222,7 +224,7 @@ class TransactionFeeMechanism: return sorted_txs[:selected_txs_idx], sorted_txs[to_be_purged_txs_idx:] def select_transactions( - self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0, purge_after:int=4 ) -> Tuple[List[Transaction], List[Transaction]]: raise NotImplementedError @@ -247,10 +249,10 @@ class EIP1559(TransactionFeeMechanism): base_fee * np.exp(delta * self.base_factor) # (1. + delta * self.base_factor) ) sum_price:float = sum([min(tx.fee_cap, base_fee+tx.tip) for tx in last_txs]) - self.price.append(sum_price/float(len(last_txs))) + self.price.append( max(sum_price/float(len(last_txs)), PROTOCOL_CONSTANTS["MIN_PRICE"]) ) def select_transactions( - self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0, purge_after:int=4 ) -> Tuple[List[Transaction], List[Transaction]]: base_fee:float = self.base_fee[-1] @@ -262,7 +264,12 @@ class EIP1559(TransactionFeeMechanism): reverse=True ) - return self._select_from_sorted_txs(sorted_txs, stop_below_gas_limit, scale_block_size) + return self._select_from_sorted_txs( + sorted_txs, + stop_below_gas_limit=stop_below_gas_limit, + scale_block_size=scale_block_size, + purge_after=purge_after + ) def total_paid_fees(self, txs: List[Transaction]) -> float: base_fee:float = self.base_fee[-1] @@ -276,10 +283,10 @@ class StableFee(TransactionFeeMechanism): def update_price(self, blockchain:Blockchain): new_price:float = np.min([tx.fee_cap for tx in blockchain.get_last_block().txs]) - self.price.append(new_price) + self.price.append(max(new_price, PROTOCOL_CONSTANTS["MIN_PRICE"])) def select_transactions( - self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0 + self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0, purge_after:int=4 ) -> Tuple[List[Transaction], List[Transaction]]: # Sort transactions by fee cap @@ -289,8 +296,13 @@ class StableFee(TransactionFeeMechanism): reverse=True ) - return self._select_from_sorted_txs(sorted_txs, stop_below_gas_limit, scale_block_size) - + return self._select_from_sorted_txs( + sorted_txs, + stop_below_gas_limit=stop_below_gas_limit, + scale_block_size=scale_block_size, + purge_after=purge_after + ) + def total_paid_fees(self, txs: List[Transaction]) -> float: price:float = self.get_current_price() return sum([price * tx.gas_used for tx in txs]) \ No newline at end of file From 906bb976198898b8fe803022cecdb0b2b6375a0f Mon Sep 17 00:00:00 2001 From: Frederico Date: Mon, 16 Dec 2024 22:06:25 +0100 Subject: [PATCH 05/16] parameter to purge low offer tx --- transaction_fee_models/simulation.py | 3 ++- transaction_fee_models/simulation_parameters.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/transaction_fee_models/simulation.py b/transaction_fee_models/simulation.py index f1041dc..71b9805 100644 --- a/transaction_fee_models/simulation.py +++ b/transaction_fee_models/simulation.py @@ -74,7 +74,8 @@ def run_simulation(params: SimulationParameters) -> Tuple[pd.DataFrame, pd.DataF selected_transactions, to_be_purged_transactions = tfm.select_transactions( pool, stop_below_gas_limit=stop_below_gas_limit, - scale_block_size=scale_block_size + scale_block_size=scale_block_size, + purge_after=params.purge_after ) # selected transactions are added to the blockchain diff --git a/transaction_fee_models/simulation_parameters.py b/transaction_fee_models/simulation_parameters.py index 13490dc..15b082e 100644 --- a/transaction_fee_models/simulation_parameters.py +++ b/transaction_fee_models/simulation_parameters.py @@ -13,6 +13,7 @@ class SimulationParameters: max_tip_pct:float=0.1, scale_block_size_limits: Tuple[float] = (0.0, 0.2), probability_stop_below_gas_limit: float = 0.5, + purge_after: int = 4, ): assert len(demand_sizes) == len(demand_probabilities), "demand_sizes and demand_probabilities must have the same length" assert abs(sum(demand_probabilities) - 1.0) < 1.e-12, "demand_probabilities must sum to 1.0" @@ -25,6 +26,7 @@ class SimulationParameters: assert len(fee_cap_range) == 2, "fee_cap_range must be a tuple of length 2" assert fee_cap_range[0] <= fee_cap_range[1], "fee_cap_range must be in increasing order" assert max_tip_pct >= 0.0, "max_tip_pct must be positive" + assert purge_after >= 1, "purge_after must be at least 1" self.num_blocks = num_blocks self.demand_sizes = demand_sizes @@ -32,4 +34,5 @@ class SimulationParameters: self.fee_cap_range = fee_cap_range self.max_tip_pct = max_tip_pct self.scale_block_size_limits = scale_block_size_limits - self.probability_stop_below_gas_limit = probability_stop_below_gas_limit \ No newline at end of file + self.probability_stop_below_gas_limit = probability_stop_below_gas_limit + self.purge_after = purge_after \ No newline at end of file From cd5c6772abc6494ebf4b3404d827164c4cf6b3d7 Mon Sep 17 00:00:00 2001 From: Frederico Date: Mon, 16 Dec 2024 22:07:03 +0100 Subject: [PATCH 06/16] playing with parameters --- transaction_fee_models/eip-vs-stablefee.ipynb | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/transaction_fee_models/eip-vs-stablefee.ipynb b/transaction_fee_models/eip-vs-stablefee.ipynb index 631be52..b41d8c8 100644 --- a/transaction_fee_models/eip-vs-stablefee.ipynb +++ b/transaction_fee_models/eip-vs-stablefee.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -26,7 +26,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -35,18 +35,19 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "params = SimulationParameters(\n", " num_blocks=int(days * 24 * 60 * 60 / 12), # number of blocks in \"days\",\n", - " demand_sizes=[50, 1000, 2000], # very low, normal, very high demand\n", - " demand_probabilities=[0.05, 0.9, 0.05], # they must sum to 1\n", - " fee_cap_range=(-0.05, 0.3), \n", + " demand_sizes=[0, 50, 1000, 2000], # very low, normal, very high demand\n", + " demand_probabilities=[0.02, 0.06, 0.86, 0.06], # they must sum to 1\n", + " fee_cap_range=(0.05, 0.1), # in percentage, mean and std deviation\n", " max_tip_pct=0.1,\n", - " scale_block_size_limits=(0.0, 0.2),\n", - " probability_stop_below_gas_limit=0.5\n", + " scale_block_size_limits=(0.05, 0.1),\n", + " probability_stop_below_gas_limit=0.5,\n", + " purge_after=2\n", ")" ] }, @@ -65,7 +66,9 @@ "metadata": {}, "outputs": [], "source": [ - "df_stats_merged[[\"price_EIP\", \"price_StableFee\"]].plot()" + "df_stats_merged[[\"price_EIP\", \"price_StableFee\"]].plot(\n", + " title=\"EIP-1559 vs StableFee price\",\n", + ")" ] }, { @@ -86,6 +89,15 @@ "df_stats_merged[[\"tot_paid_EIP\", \"tot_paid_StableFee\"]].plot()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_stats_merged[[\"demand_size_EIP\"]].plot.bar()" + ] + }, { "cell_type": "code", "execution_count": null, From 7a99edfbb6f6e8f6a7c16e2d4dc5de2ba71d71c5 Mon Sep 17 00:00:00 2001 From: Frederico Date: Tue, 31 Dec 2024 00:07:31 +0100 Subject: [PATCH 07/16] variable gas limit as parameter --- transaction_fee_models/simulation_parameters.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/transaction_fee_models/simulation_parameters.py b/transaction_fee_models/simulation_parameters.py index 15b082e..2c33eda 100644 --- a/transaction_fee_models/simulation_parameters.py +++ b/transaction_fee_models/simulation_parameters.py @@ -14,6 +14,7 @@ class SimulationParameters: scale_block_size_limits: Tuple[float] = (0.0, 0.2), probability_stop_below_gas_limit: float = 0.5, purge_after: int = 4, + variable_gas_limits: bool = True ): assert len(demand_sizes) == len(demand_probabilities), "demand_sizes and demand_probabilities must have the same length" assert abs(sum(demand_probabilities) - 1.0) < 1.e-12, "demand_probabilities must sum to 1.0" @@ -35,4 +36,5 @@ class SimulationParameters: self.max_tip_pct = max_tip_pct self.scale_block_size_limits = scale_block_size_limits self.probability_stop_below_gas_limit = probability_stop_below_gas_limit - self.purge_after = purge_after \ No newline at end of file + self.purge_after = purge_after + self.variable_gas_limits = variable_gas_limits \ No newline at end of file From c89b94dc8a28273537d899251e7a220e1c1a5ea0 Mon Sep 17 00:00:00 2001 From: Frederico Date: Tue, 31 Dec 2024 00:07:57 +0100 Subject: [PATCH 08/16] max price --- transaction_fee_models/tx_fees_models.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/transaction_fee_models/tx_fees_models.py b/transaction_fee_models/tx_fees_models.py index 8178032..4e99620 100644 --- a/transaction_fee_models/tx_fees_models.py +++ b/transaction_fee_models/tx_fees_models.py @@ -10,7 +10,8 @@ PROTOCOL_CONSTANTS = { "TARGET_GAS_USED": 12500000.0, # 12.5 million gas "MAX_GAS_ALLOWED": 25000000.0, # 25 million gas "INITIAL_BASEFEE": 1.0, # 10^9 wei = 1 Gwei - "MIN_PRICE": 0.1, # 10^8 wei = 0.1 Gwei + "MIN_PRICE": 0.5, # 10^8 wei = 0.1 Gwei + "MAX_PRICE": 10.0 # 10^11 wei = 100 Gwei } @@ -249,7 +250,13 @@ class EIP1559(TransactionFeeMechanism): base_fee * np.exp(delta * self.base_factor) # (1. + delta * self.base_factor) ) sum_price:float = sum([min(tx.fee_cap, base_fee+tx.tip) for tx in last_txs]) - self.price.append( max(sum_price/float(len(last_txs)), PROTOCOL_CONSTANTS["MIN_PRICE"]) ) + self.price.append( + np.clip( + sum_price/float(len(last_txs)), + a_min=PROTOCOL_CONSTANTS["MIN_PRICE"], + a_max=PROTOCOL_CONSTANTS["MAX_PRICE"] + ) + ) def select_transactions( self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0, purge_after:int=4 @@ -282,8 +289,15 @@ class StableFee(TransactionFeeMechanism): super().__init__() def update_price(self, blockchain:Blockchain): - new_price:float = np.min([tx.fee_cap for tx in blockchain.get_last_block().txs]) - self.price.append(max(new_price, PROTOCOL_CONSTANTS["MIN_PRICE"])) + last_txs:List[Transaction] = blockchain.get_last_block().txs + new_price:float = np.min([tx.fee_cap for tx in last_txs]) + self.price.append( + np.clip( + new_price, + a_min=PROTOCOL_CONSTANTS["MIN_PRICE"], + a_max=PROTOCOL_CONSTANTS["MAX_PRICE"] + ) + ) def select_transactions( self, mempool: MemPool, stop_below_gas_limit:bool=False, scale_block_size:float=1.0, purge_after:int=4 From 2f8d6469054d3873814c53231b3ad5a2affba1e8 Mon Sep 17 00:00:00 2001 From: Frederico Date: Tue, 31 Dec 2024 00:08:16 +0100 Subject: [PATCH 09/16] variable and fixed gas limit have the same mean --- transaction_fee_models/tx_fees_models.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/transaction_fee_models/tx_fees_models.py b/transaction_fee_models/tx_fees_models.py index 4e99620..b6a124d 100644 --- a/transaction_fee_models/tx_fees_models.py +++ b/transaction_fee_models/tx_fees_models.py @@ -135,7 +135,16 @@ def create_demand( def _create_demand_const_gas(fee_caps:np.ndarray, tip:np.ndarray) -> List[Transaction]: demand: List[Transaction] = [] - gas_used = 21000 + gas_used = np.mean([ # 73_850 gas limit + 21_000 * 0.3, # eth transfer + 45_000 * 0.3, # erc20 transfer + 50_000 * 0.1, # token approval + 200_000 * 0.2, # token swap + 150_000 * 0.03, # NFT (ERC721) minting + 75_000* 0.03, # NFT transfer + 120_000 * 0.03, # NFT (ERC1155) minting + 500_000 * 0.01, # smart contract deployment + ]) for fc, tp in zip(fee_caps, tip): tx = Transaction( From 323fcf56a043fdeb9c56605189f73eef09e983a0 Mon Sep 17 00:00:00 2001 From: Frederico Date: Tue, 31 Dec 2024 00:08:33 +0100 Subject: [PATCH 10/16] fixed seed to make results depend only on input parameters --- transaction_fee_models/simulation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/transaction_fee_models/simulation.py b/transaction_fee_models/simulation.py index 71b9805..decaf61 100644 --- a/transaction_fee_models/simulation.py +++ b/transaction_fee_models/simulation.py @@ -1,5 +1,6 @@ from typing import List, Dict, Any, Tuple import numpy as np +np.random.seed(42) import pandas as pd import tqdm From b6a1e5db681d39f32f67d4769335f311529151db Mon Sep 17 00:00:00 2001 From: Frederico Date: Tue, 31 Dec 2024 00:09:03 +0100 Subject: [PATCH 11/16] variable gas limit is a parameter --- transaction_fee_models/simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transaction_fee_models/simulation.py b/transaction_fee_models/simulation.py index decaf61..5f756da 100644 --- a/transaction_fee_models/simulation.py +++ b/transaction_fee_models/simulation.py @@ -54,7 +54,7 @@ def run_simulation(params: SimulationParameters) -> Tuple[pd.DataFrame, pd.DataF demand_size, fee_cap_range=params.fee_cap_range, max_tip_pct=params.max_tip_pct, - variable_gas=True + variable_gas=params.variable_gas_limits ) stop_below_gas_limit:bool = _generate_random_bool(params.probability_stop_below_gas_limit) From 4c014275deb319e91998a0b84a854d11b91321ee Mon Sep 17 00:00:00 2001 From: Frederico Date: Tue, 31 Dec 2024 00:09:18 +0100 Subject: [PATCH 12/16] playing with parameters --- transaction_fee_models/eip-vs-stablefee.ipynb | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/transaction_fee_models/eip-vs-stablefee.ipynb b/transaction_fee_models/eip-vs-stablefee.ipynb index b41d8c8..6e36149 100644 --- a/transaction_fee_models/eip-vs-stablefee.ipynb +++ b/transaction_fee_models/eip-vs-stablefee.ipynb @@ -38,16 +38,26 @@ "execution_count": 3, "metadata": {}, "outputs": [], + "source": [ + "demand = 500" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], "source": [ "params = SimulationParameters(\n", " num_blocks=int(days * 24 * 60 * 60 / 12), # number of blocks in \"days\",\n", - " demand_sizes=[0, 50, 1000, 2000], # very low, normal, very high demand\n", - " demand_probabilities=[0.02, 0.06, 0.86, 0.06], # they must sum to 1\n", - " fee_cap_range=(0.05, 0.1), # in percentage, mean and std deviation\n", - " max_tip_pct=0.1,\n", - " scale_block_size_limits=(0.05, 0.1),\n", + " demand_sizes=[demand], # very low, normal, very high demand\n", + " demand_probabilities=[1.], # they must sum to 1\n", + " fee_cap_range=(0., 0.00001), # in percentage, mean and std deviation\n", + " max_tip_pct=0.1, # this only affects the EIP-1559 simulation\n", + " scale_block_size_limits=(0.0, 0.05),\n", " probability_stop_below_gas_limit=0.5,\n", - " purge_after=2\n", + " purge_after=4,\n", + " variable_gas_limits=False\n", ")" ] }, @@ -66,8 +76,8 @@ "metadata": {}, "outputs": [], "source": [ - "df_stats_merged[[\"price_EIP\", \"price_StableFee\"]].plot(\n", - " title=\"EIP-1559 vs StableFee price\",\n", + "df_stats_merged[[\"price_StableFee\"]].plot(\n", + " title=f\"StableFee price - {demand} txs - variable gas limits\",\n", ")" ] }, From e64c573ec29ff930ddbeadc2476afa36aa8e3970 Mon Sep 17 00:00:00 2001 From: Frederico Date: Thu, 2 Jan 2025 20:00:01 +0100 Subject: [PATCH 13/16] scale_block_size is a float now --- transaction_fee_models/simulation.py | 2 +- transaction_fee_models/simulation_parameters.py | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/transaction_fee_models/simulation.py b/transaction_fee_models/simulation.py index 5f756da..8686e7c 100644 --- a/transaction_fee_models/simulation.py +++ b/transaction_fee_models/simulation.py @@ -60,7 +60,7 @@ def run_simulation(params: SimulationParameters) -> Tuple[pd.DataFrame, pd.DataF stop_below_gas_limit:bool = _generate_random_bool(params.probability_stop_below_gas_limit) scale_block_size = np.random.uniform( - params.scale_block_size_limits[0], params.scale_block_size_limits[1] + 0., params.scale_block_size ) for chain, pool, tfm, stats in zip( diff --git a/transaction_fee_models/simulation_parameters.py b/transaction_fee_models/simulation_parameters.py index 2c33eda..b02a789 100644 --- a/transaction_fee_models/simulation_parameters.py +++ b/transaction_fee_models/simulation_parameters.py @@ -11,19 +11,17 @@ class SimulationParameters: demand_probabilities: List[float] = [0.05, 0.9, 0.05], fee_cap_range:Tuple[float]=(-0.1, 0.1), max_tip_pct:float=0.1, - scale_block_size_limits: Tuple[float] = (0.0, 0.2), + scale_block_size: float = 0.2, probability_stop_below_gas_limit: float = 0.5, purge_after: int = 4, variable_gas_limits: bool = True ): assert len(demand_sizes) == len(demand_probabilities), "demand_sizes and demand_probabilities must have the same length" assert abs(sum(demand_probabilities) - 1.0) < 1.e-12, "demand_probabilities must sum to 1.0" - assert 0.0 <= probability_stop_below_gas_limit <= 1.0, "probability_stop_below_gas_limit must be between 0 and 1" - assert len(scale_block_size_limits) == 2, "scale_block_size_limits must be a tuple of length 2" - assert scale_block_size_limits[0] <= scale_block_size_limits[1], "scale_block_size_limits must be in increasing order" assert all([0.0 <= p <= 1.0 for p in demand_probabilities]), "demand_probabilities must be between 0 and 1" - assert scale_block_size_limits[0] >= 0.0, "scale_block_size_limits must be positive" - assert scale_block_size_limits[1] <= 1.0, "scale_block_size_limits must be less than or equal to 1.0" + assert 0.0 <= probability_stop_below_gas_limit <= 1.0, "probability_stop_below_gas_limit must be between 0 and 1" + assert 0 < scale_block_size, "scale_block_size must be strictly positive" + assert scale_block_size <= 1.0, "scale_block_size must be less than or equal to 1.0" assert len(fee_cap_range) == 2, "fee_cap_range must be a tuple of length 2" assert fee_cap_range[0] <= fee_cap_range[1], "fee_cap_range must be in increasing order" assert max_tip_pct >= 0.0, "max_tip_pct must be positive" @@ -34,7 +32,7 @@ class SimulationParameters: self.demand_probabilities = demand_probabilities self.fee_cap_range = fee_cap_range self.max_tip_pct = max_tip_pct - self.scale_block_size_limits = scale_block_size_limits + self.scale_block_size = scale_block_size self.probability_stop_below_gas_limit = probability_stop_below_gas_limit self.purge_after = purge_after self.variable_gas_limits = variable_gas_limits \ No newline at end of file From 1f85cae4e06f9c3b1e9e71d3249357c571e61ab9 Mon Sep 17 00:00:00 2001 From: Frederico Date: Thu, 2 Jan 2025 20:00:49 +0100 Subject: [PATCH 14/16] improving identation --- transaction_fee_models/tx_fees_models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/transaction_fee_models/tx_fees_models.py b/transaction_fee_models/tx_fees_models.py index b6a124d..69733e1 100644 --- a/transaction_fee_models/tx_fees_models.py +++ b/transaction_fee_models/tx_fees_models.py @@ -208,7 +208,10 @@ class TransactionFeeMechanism: return scaled_demand def _select_from_sorted_txs( - self, sorted_txs:List[Transaction], stop_below_gas_limit:bool=False, scale_block_size:float=1.0, purge_after:int=4 + self, + sorted_txs:List[Transaction], + stop_below_gas_limit:bool=False, + scale_block_size:float=1.0, purge_after:int=4 ) -> Tuple[List[Transaction], List[Transaction]]: # select transactions so that the sum of gas used is less than the block gas limit From 22760e53b489d3315d0f0f7ab8c7989b0c0a5397 Mon Sep 17 00:00:00 2001 From: Frederico Date: Sun, 5 Jan 2025 14:22:15 +0100 Subject: [PATCH 15/16] updating gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7703656..e388b87 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ simlib/**/Cargo.lock simlib/test.json *.ignore* *.png +*/.DS_Store From 556231357414c11f7bf693e89ac37b9c0313abfb Mon Sep 17 00:00:00 2001 From: Frederico Date: Sun, 5 Jan 2025 14:22:24 +0100 Subject: [PATCH 16/16] playing with parameters --- transaction_fee_models/eip-vs-stablefee.ipynb | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/transaction_fee_models/eip-vs-stablefee.ipynb b/transaction_fee_models/eip-vs-stablefee.ipynb index 6e36149..fe8e5a5 100644 --- a/transaction_fee_models/eip-vs-stablefee.ipynb +++ b/transaction_fee_models/eip-vs-stablefee.ipynb @@ -39,7 +39,11 @@ "metadata": {}, "outputs": [], "source": [ - "demand = 500" + "demand = 5000\n", + "\n", + "std_dev = 0.01/100.\n", + "variable_gas_limits = True\n", + "below_gas_limit = 0.5" ] }, { @@ -52,12 +56,12 @@ " num_blocks=int(days * 24 * 60 * 60 / 12), # number of blocks in \"days\",\n", " demand_sizes=[demand], # very low, normal, very high demand\n", " demand_probabilities=[1.], # they must sum to 1\n", - " fee_cap_range=(0., 0.00001), # in percentage, mean and std deviation\n", - " max_tip_pct=0.1, # this only affects the EIP-1559 simulation\n", - " scale_block_size_limits=(0.0, 0.05),\n", - " probability_stop_below_gas_limit=0.5,\n", + " fee_cap_range=(0., std_dev), # in percentage, mean and std deviation\n", + " max_tip_pct=0, # this only affects the EIP-1559 simulation\n", + " scale_block_size=0.05,\n", + " probability_stop_below_gas_limit=below_gas_limit,\n", " purge_after=4,\n", - " variable_gas_limits=False\n", + " variable_gas_limits=variable_gas_limits\n", ")" ] }, @@ -76,8 +80,13 @@ "metadata": {}, "outputs": [], "source": [ - "df_stats_merged[[\"price_StableFee\"]].plot(\n", - " title=f\"StableFee price - {demand} txs - variable gas limits\",\n", + "df_stats_merged[[\"price_StableFee\", \"price_EIP\"]].plot(\n", + " title=f'''\n", + "StableFee vs EIP-1559 - {demand} txs -\n", + "{\"variable\" if variable_gas_limits else \"fixed\"} gas/tx -\n", + "{std_dev * 100:.2e}% standard deviation - \n", + "{below_gas_limit * 100:.2f}% chance to stop below gas limit\n", + "''',\n", ")" ] },