Skip to content

Instantly share code, notes, and snippets.

@karlfloersch
Last active November 8, 2021 23:46
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karlfloersch/6ac6f853cfcc79e8a0c2f8ff683256de to your computer and use it in GitHub Desktop.
Save karlfloersch/6ac6f853cfcc79e8a0c2f8ff683256de to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 107,
"id": "630aad2b",
"metadata": {},
"outputs": [],
"source": [
"class Transaction:\n",
" def __init__(self, l1_gas_cost, l2_gas_cost, frequency):\n",
" assert frequency > 0 and frequency <= 1\n",
"\n",
" self.l1_gas_cost = l1_gas_cost\n",
" self.l2_gas_cost = l2_gas_cost\n",
" self.frequency = frequency\n",
"\n",
" def get_l1_gas_cost(self):\n",
" \"\"\" Gas cost of the transaction on L1 \"\"\"\n",
" return self.l1_gas_cost\n",
"\n",
" def get_l2_gas_cost(self):\n",
" \"\"\" Gas cost of the transaction on L2 \"\"\"\n",
" return self.l2_gas_cost\n",
"\n",
" def get_frequency(self):\n",
" \"\"\" The frequency of this type of transaction. Must be between 0 and 1 \"\"\"\n",
" return self.frequency\n",
" \n",
" def __str__(self):\n",
" return (f\"\"\"L1 Gas Cost: {self.l1_gas_cost}\n",
"L2 Gas Cost: {self.l2_gas_cost}\n",
"Frequency: {self.frequency}\n",
"\"\"\")\n",
"\n",
"class AverageTransaction(Transaction):\n",
" def __init__(self, transactions):\n",
" l1_gas_cost = 0\n",
" l2_gas_cost = 0\n",
" total_frequency = 0\n",
" for tx in transactions:\n",
" l1_gas_cost += tx['l1_gas_cost'] * tx['frequency']\n",
" l2_gas_cost += tx['l2_gas_cost'] * tx['frequency']\n",
" total_frequency += tx['frequency']\n",
" \n",
" assert total_frequency == 1, \"sum of all tx.get_frequency() must == 1\"\n",
" super(AverageTransaction, self).__init__(\n",
" l1_gas_cost=l1_gas_cost,\n",
" l2_gas_cost=l2_gas_cost,\n",
" frequency=1\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 108,
"id": "2fd72e5c",
"metadata": {},
"outputs": [],
"source": [
"txs = [\n",
" {\n",
" # transfer\n",
" 'l1_gas_cost': 100,\n",
" 'l2_gas_cost': 100000,\n",
" 'frequency': 0.5\n",
" },\n",
" {\n",
" # swap\n",
" 'l1_gas_cost': 200,\n",
" 'l2_gas_cost': 800000,\n",
" 'frequency': 0.5\n",
" }\n",
"]\n",
"\n",
"tx = AverageTransaction(txs)\n",
"\n",
"# sanity check test\n",
"assert tx.get_l1_gas_cost() == (100 + 200) // 2\n",
"assert tx.get_l2_gas_cost() == (100000 + 800000) // 2\n",
"assert tx.get_frequency() == 1"
]
},
{
"cell_type": "code",
"execution_count": 109,
"id": "0c5300d2",
"metadata": {},
"outputs": [],
"source": [
"class SimpleDemandCurve:\n",
" def __init__(self, points):\n",
" self.points = points\n",
" self.points.sort(key=lambda point: point[0])\n",
" # Make sure intersections with x=0 and y=0 are supplied\n",
" x_intersection = [-1,-1]\n",
" y_intersection = [-1,-1]\n",
" for idx, p in enumerate(points):\n",
" if p[0] == 0:\n",
" y_intersection = p\n",
" if p[1] == 0:\n",
" x_intersection = p\n",
" # Make sure points are monotonoic\n",
" if idx + 1 < len(points):\n",
" assert points[idx][1] >= points[idx+1][1], 'Points must be monotonoically decreasing'\n",
" assert x_intersection[0] != -1 and y_intersection[0] != -1, 'Must supply points for x=0 & y=0'\n",
" self.max_quantity = x_intersection[0]\n",
" self.max_price = y_intersection[1]\n",
" self.min_price = 0\n",
" self.min_quantity = 0\n",
" \n",
" def get_price(self, quantity):\n",
" # Enforce upper and lower bounds\n",
" assert quantity >= 0, 'Quantity must not be negative'\n",
" if quantity > self.max_quantity:\n",
" return 0\n",
" # Interpolate for the rest of the values\n",
" self.points.sort(key=lambda point: point[0])\n",
" xp = list(map(lambda point: point[0], self.points))\n",
" fp = list(map(lambda point: point[1], self.points))\n",
" return np.interp(quantity, xp, fp)\n",
" \n",
" def get_quantity(self, price):\n",
" # Enforce upper and lower bounds\n",
" assert price >= 0, 'Price must not be negative'\n",
" if price > self.max_price:\n",
" return 0\n",
" # Interpolate for the rest of the values\n",
" self.points.sort(key=lambda point: point[1])\n",
" xp = list(map(lambda point: point[1], self.points))\n",
" fp = list(map(lambda point: point[0], self.points))\n",
" return np.interp(price, xp, fp)"
]
},
{
"cell_type": "code",
"execution_count": 110,
"id": "bb660753",
"metadata": {},
"outputs": [],
"source": [
"# Sanity checks for the demand curve\n",
"# Valid demand curve\n",
"example_demand_curve = SimpleDemandCurve([\n",
" [0, 10], # price denominated in USD\n",
" [0.05, 1],\n",
" [0.1, 0.5],\n",
" [0.1, 0]\n",
"])\n",
"assert example_demand_curve.get_price(0.05) == 1\n",
"assert example_demand_curve.get_price(0.075) == 0.75\n",
"assert example_demand_curve.get_quantity(1) == 0.05\n",
"assert round(example_demand_curve.get_quantity(0.75), 3) == 0.075\n",
"\n",
"# Test that fee=0 point and tps=0 point are manditory\n",
"did_throw = False\n",
"try:\n",
" # Generate demand curve missing a tps==0 value\n",
" example_demand_curve = SimpleDemandCurve([\n",
" [0.001, 10], # Missing a tps==0\n",
" [0.1, 0]\n",
" ])\n",
"except (AssertionError):\n",
" did_throw = True\n",
" try:\n",
" # Generate demand curve missing a fee==0 value\n",
" example_demand_curve = SimpleDemandCurve([\n",
" [10, 0], \n",
" [0.1, 1] # Missing a fee==0\n",
" ])\n",
" except (AssertionError):\n",
" # Make sure we threw in both cases\n",
" did_throw = True\n",
"assert did_throw"
]
},
{
"cell_type": "code",
"execution_count": 126,
"id": "5c7cbfb4",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy.optimize import fsolve\n",
"import matplotlib.pyplot as plt\n",
"\n",
"def find_intersection(fun1, fun2, x0):\n",
" return fsolve(lambda x : fun1(x) - fun2(x), x0)\n",
"\n",
"def gwei_to_eth(gwei):\n",
" return gwei / 1000000000\n",
"\n",
"def const_fn(const_return_val):\n",
" return lambda x : const_return_val\n",
"\n",
"class TransactionSupplyDemandModel:\n",
" \"\"\"\n",
" Config:\n",
" {\n",
" txs: List[Transaction],\n",
" tps_demand_curve: SimpleDemandCurve\n",
" l1_gas_per_second: int\n",
" l2_gas_per_second: int\n",
" fee_scaler: float\n",
" }\n",
" \"\"\"\n",
"\n",
" def __init__(self, config):\n",
" self.config = config\n",
" self.tx = AverageTransaction(config['txs'])\n",
"\n",
" def get_l1_gas_price(self):\n",
" return self.config['l1_gas_price']\n",
" \n",
" def get_l1_tx_fee(self):\n",
" fee = self.tx.get_l1_gas_cost() * self.get_l1_gas_price()\n",
" return fee * self.config['fee_scaler']\n",
"\n",
" def get_l1_tx_fee_in_dollars(self):\n",
" min_fee_in_gwei = self.get_l1_tx_fee()\n",
" min_fee_in_dollars = gwei_to_eth(self.get_l1_tx_fee()) * self.config['eth_price']\n",
" return min_fee_in_dollars\n",
"\n",
" def get_max_tps(self):\n",
" max_tps_for_l1 = self.config['l1_gas_per_second'] // self.tx.get_l1_gas_cost()\n",
" max_tps_for_l2 = self.config['l2_gas_per_second'] // self.tx.get_l2_gas_cost()\n",
" return min(max_tps_for_l1, max_tps_for_l2)\n",
"\n",
" def get_fee(self):\n",
" \"\"\" Calculate expected fee per tx given the TPS demand curve \"\"\"\n",
" demand_curve = self.config['tps_demand_curve']\n",
" max_tps = min(self.get_max_tps(), demand_curve.max_quantity)\n",
" fee_given_max_tps = demand_curve.get_price(max_tps)\n",
" fee = max(self.get_l1_tx_fee_in_dollars(), fee_given_max_tps)\n",
" return fee\n",
"\n",
" def get_tps(self):\n",
" \"\"\" Calculate expected TPS given the TPS demand curve \"\"\"\n",
" fee = self.get_fee()\n",
" demand_curve = self.config['tps_demand_curve']\n",
" return demand_curve.get_quantity(fee)\n",
"\n",
" def plot(self):\n",
" x_points = np.arange(0, self.get_max_tps() * 1.3)\n",
" demand_curve = self.config['tps_demand_curve']\n",
" supply_floor = const_fn(self.get_l1_tx_fee_in_dollars())\n",
" f = np.array(list(map(demand_curve.get_price, x_points)))\n",
" g = np.array(list(map(supply_floor, x_points)))\n",
" plt.plot(x_points, f, '-')\n",
" plt.plot(x_points, g, '-', color='orange')\n",
" plt.axvline(self.get_max_tps(), color='orange')\n",
" fee = self.get_fee()\n",
" tps = self.get_tps()\n",
" plt.plot(tps, fee, 'ro')\n",
" plt.xlim([0, self.get_max_tps()*1.3])\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 127,
"id": "7903134a",
"metadata": {},
"outputs": [],
"source": [
"# Globals\n",
"base_l1_gas_per_second = 1100000\n",
"base_l2_gas_per_second = 11000000\n",
"fee_scaler = 1.5\n",
"\n",
"# Helper\n",
"def get_simple_demand_curve(tps_points):\n",
" points = list(map(lambda p : [p['tps'], p['fee']], tps_points))\n",
" return SimpleDemandCurve(points)"
]
},
{
"cell_type": "code",
"execution_count": 129,
"id": "fe6d7592",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fee: $1.5525 USD\n",
"TPS: 27.9 transactions per second\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAWoAAAD4CAYAAADFAawfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAduUlEQVR4nO3de3BU55nn8e/T3brfG11bICSMuYNkDNiOWdtx7PgSA1V7mUrGszW7lS12plK1maqpmppsqnZr/vBUbe3uTFKbna3xJplssh4nm4kTMLEd3+M4jg0Cc7/YgAQGCQRIIEAI3d7947QaQQQSQq1zTvfvU9Wl7tOt049E8+Pwnvc8rznnEBGR4Ir4XYCIiNyaglpEJOAU1CIiAaegFhEJOAW1iEjAxdKx08rKStfY2JiOXYtI0PUe8r6WLvS3jpDZvn37Wedc1XjPpSWoGxsbaW1tTceuRSTo3nzE+/rYu35WETpmduxmz2noQ0Qk4BTUIiIBp6AWEQk4BbWISMApqEVEAm5Ssz7MrB24CAwDQ865VeksSkRErrmd6Xmfd86dTVslIiIyrrQMfQwOj6RjtyIiWWmyQe2A181su5ltHO8FZrbRzFrNrPXkmfPTVqCISLab7NDHWufcSTOrBt4ws4POuffGvsA59zzwPEBZwyKtRiAiMk0mdUTtnDuZ/NoF/BxYc6vX9w8Oc7jr0p1XJyIiEwe1mRWZWcnofeCLwN6Jvm/zro47r05ERCZ1RF0DvG9mu4CtwC+dc6/d6huK82K8vKsDrccoInLnJhyjds4dBZpvZ6flBTm0nb3MnpMXWDG7fKq1iYgIaZqeV1qQQ07U2LxTwx8iIncqLUEdjRgPL6jm5d0dDI9o+ENE5E6krdfHhpYEp3uvsrWtO11vISKSFdIW1I8trqEwN6rZHyIidyhtQV2QG+XxJTW8ureTgSFdUi4iMlVpbXO6oSXB+b5BfvPpmXS+jYhIRktrUK+dX0V5YY6GP0RE7kBagzo3FuHp5XW8sf80fQND6XwrEZGMlfYVXtY3J+gbGObNA13pfisRkYyU9qBe0xintjRfF7+IiExR2oM6EjHWNdfx60+6ON83kO63ExHJODOyuO365noGhx2v7T01E28nIpJRZiSol9WX0lRZpNkfIiJTMCNBbWasa07wu6PnON3bPxNvKSKSMWYkqMGb/eEcbNndOVNvKSKSEWYsqOdXF7M0UarhDxGR2zRjQQ3eUfWuz87TfvbyTL6tiEiozWhQr2tOAPCyjqpFRCZtRoM6UV7AmsY4m7SeoojIpM1oUAOsa0lwuOsSBzovzvRbi4iE0owH9ZeW1xGLGJt2nZzptxYRCaUZD+p4US5r765ky65ORrSeoojIhGY8qMGb/XHy/BV2HO/x4+1FRELFl6D+4tJa8mIRNqmjnojIhHwJ6uK8GI8truGVPZ0MDWs9RRGRW/ElqMGbU33u8gC/PXLOrxJERELBt6B+ZGEVJfkxNu3U7A8RkVvxLajzc6I8ubSW1/edpn9w2K8yREQCz7egBljfkuDS1SHeOaj1FEVEbsbXoH5g3iwqi/M0+0NE5BZ8DepYNMIzK+p4+1AXvf2DfpYiIhJYvgY1eLM/BoZGeH3fab9LEREJJN+DemVDObMrCjT7Q0TkJnwPajNjfXOCD46c48zFq36XIyISOL4HNcCGlnqGRxyv7NF6iiIiNwpEUC+sLWFhTYnWUxQRGcekg9rMomb2sZltSUch61sSbD/Ww4mevnTsXkQktG7niPrrwIF0FbI+tZ6ihj9ERMaaVFCb2WzgS8B301XInHgh9zSUa/aHiMgNJntE/S3gL4Cb9iQ1s41m1mpmrWfOnJlSMeubExw8dZFPT2s9RRGRURMGtZk9A3Q557bf6nXOueedc6ucc6uqqqqmVMyXVtQRMXRSUURkjMkcUT8IrDezduDHwKNm9n/TUUx1ST6fu6uSTTs7cE7rKYqIwCSC2jn3DefcbOdcI/Bl4G3n3B+lq6D1LQmOd/ex68SFdL2FiEioBGIe9VhPLK0lNxphszrqiYgAtxnUzrl3nXPPpKsYgLKCHB5ZWMWW3R0Mj2j4Q0QkcEfU4F1S3nXxKh8d1XqKIiKBDOovLK6mKDeq2R8iIgQ0qPNzojyxtJZX9nRydUjrKYpIdgtkUAOsa0nQ2z/Ee5+c9bsUERFfBTao186vpKIwR8MfIpL1AhvUOdEIX1pRx5v7T3P56pDf5YiI+CawQQ2wvrmeK4PDvHlA6ymKSPYKdFCvmltBXVm+Ln4RkawW6KCORLz1FH/9yRl6Lg/4XY6IiC8CHdQA65oTDI04Xt17yu9SRER8EfigXpooZV5VEZt3aUEBEclOgQ9qM2NDcz0ftXVz6kK/3+WIiMy4wAc1eK1PnYMtu3VSUUSyTyiCuqmyiOX1Zbr4RUSyUiiCGmBDS4LdJy7Qdvay36WIiMyo0AT1MysSmKE51SKSdUIT1LVl+dzXFGfTrpNaT1FEskpoghq8S8qPnrnMvo5ev0sREZkxoQrqp5bVEosYL+ukoohkkVAFdUVRLg8tqGLzrg5GtJ6iiGSJUAU1eLM/Oi/003qsx+9SRERmROiC+rHFNeTnRHRJuYhkjdAFdVFejMcW1/DL3Z0MDo/4XY6ISNqFLqgB1jcn6Okb5P3DWk9RRDJfKIP64YVVlObHeFkXv4hIFghlUOfFojy1rI5f7TvFlYFhv8sREUmrUAY1eB31Lg8M8/bBLr9LERFJq9AG9f3zZlFVkqfZHyKS8UIb1NGI8cyKOt45eIYLVwb9LkdEJG1CG9Tgzf4YGB7hV1pPUUQyWKiDumVOOQ3xQi0oICIZLdRBbWasb07wwZGzdF3UeooikplCHdTgzf4YcfDK7k6/SxERSYvQB/WCmhIW1ZawScMfIpKhQh/U4B1Vf3z8PJ919/ldiojItMuIoF63IgGgk4oikpEmDGozyzezrWa2y8z2mdlfzURht2NOvJB751Zo4VsRyUiTOaK+CjzqnGsGWoAnzez+tFY1BeubExw6fZFDpy76XYqIyLSaMKid51LyYU7yFrh1sJ5eXkc0YrqkXEQyzqTGqM0samY7gS7gDefcR+O8ZqOZtZpZ65kzZ6a5zIlVleTxubtmsXlXB84F7t8REZEpm1RQO+eGnXMtwGxgjZktG+c1zzvnVjnnVlVVVU1zmZOzvjnBZ91X+Piz8768v4hIOtzWrA/n3HngHeDJtFRzh55YVktuLKKTiiKSUSYz66PKzMqT9wuAx4GDaa5rSkrzc3h0YTVbdncypPUURSRDTOaIug54x8x2A9vwxqi3pLesqdvQkuDspat8eLTb71JERKZFbKIXOOd2A/fMQC3T4vOLqinOi7F510nW3l3pdzkiIncsI65MHCs/J8oXl9bw6t5TXB3SeooiEn4ZF9QAG1rqudg/xLuHZn6aoIjIdMvIoH7wrlnMKspV7w8RyQgZGdSxaISnl9fx5v7TXLo65Hc5IiJ3JCODGrzZH1eHRnhjv9ZTFJFwy9igXtlQQX15gS5+EZHQy9igjkSMZ5rr+M2nZ+m+POB3OSIiU5axQQ2wobmeoRHHK3u0nqKIhFdGB/XiuhLmVxdr9oeIhFpGB7WZsb45wbb2bjrOX/G7HBGRKcnooAav9alzsGW3jqpFJJwyPqgbK4tonl2m4Q8RCa2MD2qAdc0J9p7s5ciZSxO/WEQkYLImqM3QnGoRCaWsCOqa0nzub5rFy1pPUURCKCuCGmB9S4KjZy+zr6PX71JERG5L1gT1U8tqyYkam3ae9LsUEZHbkjVBXV6Yy8MLqnh5VycjIxr+EJHwyJqgBu+k4qnefra2az1FEQmPrArqx5fUUJAT1ZxqEQmVrArqwtwYjy+p4ZU9nQwMjfhdjojIpGRVUIN3Sfn5vkHeP6z1FEUkHLIuqB9aUEVZQY4ufhGR0Mi6oM6NRXh6eS2v7z/NlYFhv8sREZlQ1gU1eLM/+gaGefPAab9LERGZUFYG9X1Ns6guydPsDxEJhawM6mjEWNec4N1DXVzoG/S7HBGRW8rKoAZv9sfgsOO1fVpPUUSCLWuDesXsMubOKtTwh4gEXtYGtZmxoTnB746co6u33+9yRERuKmuDGrzWpyMOtuzW8IeIBFdWB/X86hKW1JVq+ENEAi2rgxq8o+qdn53n+Lk+v0sRERlX1gf1uuYEAJt3aUEBEQmmrA/q+vICVjdWaPhDRAJrwqA2szlm9o6Z7TezfWb29ZkobCatb07wyelLHDyl9RRFJHgmc0Q9BPy5c24JcD/wNTNbkt6yZtbTy+uIRoxN6qgnIgE0YVA75zqdczuS9y8CB4D6dBc2k2YV57F2fiWbd3ZwWnOqRSRgYrfzYjNrBO4BPhrnuY3ARoCGhobpqG1GfXn1HP70hR3c99dvMXdWIasb46xpjLO6KU7jrELMzO8SRSRLmXOTW5HbzIqBXwPPOedeutVrV61a5VpbW6ehvJm19+QFPjx6jq1t3bQe66H78gAAVSV5rG6sYHVjnNWNcRbXlRKNKLhFxvXmI97Xx971s4rQMbPtzrlV4z03qSNqM8sBfga8MFFIh9my+jKW1Zfx7/7ZPJxzHDlzia1tPWxr72ZrWzev7DkFQElejJVzK1jTFGdNU5wVs8vIi0V9rl5EMtWEQW3e//m/Bxxwzv1N+ksKBjNjfnUJ86tL+MP7vKGck+evsK2tm63t3Wxr6+a//uoQ4K0a0zK7nNVN3lH3vXMrKMnP8bN8EckgEw59mNla4DfAHmB06e7/6Jx75WbfE9ahj9vVfXmAbcnQ3tbezd6OXoZHHBGDxXWl3jh3kzdcUlWS53e5IjNDQx9TckdDH8659wENyI4jXpTLE0treWJpLQCXrw7x8fHzqSPuH287zg8+aAegqbIodXJyTWOcOfECnaAUkUm5rVkfcmtFeTHW3l3J2rsrARgYGmFvxwW2tnnB/ereTn7S+hkAtaX5ydCuYHVTnAXVJUR0glJExqGgTqPcWISVDRWsbKjgTx6+i5ERxyddF5Pj3D1sa+vm5eSl62UFOaya64X26sY4y+vLyI1l/RX+IoKCekZFIsai2lIW1Zbyrx9oxDnHiZ4r3hF3u3eS8q2DXQDk50RomVOeGi5Z2VBBUZ7+uESykf7m+8jMmBMvZE68kH9x72wAzly8SmsytLe1d/Oddw4z8ra3IO+yhHeCcvSoO16U6/NPICIzQUEdMFUleTy1vI6nltcBcLF/kO3HepKzS3r44YfH+O77bQDMry725nInZ5ckygv8LF1E0kRBHXAl+Tk8srCaRxZWA9A/OMyekxdSwyUv7+zgHz86Dlxr2To6s2R+dbFmlohkAAV1yOTnRFOXsgMMjzgOnupNBff7h8/xi2QXwHhRLquSV1CuboyzNFFKLKoTlCJho6AOuWjEWJooY2mijH/7YBPOOdrP9V27grK9m9f3nwagMDfKyoZkz5KmCu6ZU0FBri59Fwk6BXWGMTOaKotoqiziD1bPAeB0b/+1mSVt3XzrrU9wDnKixrL6stQ496q5ccoKdem7SNAoqLNATWk+65oTqfUhL/QNsv14N1vbetjado7vv9/G3//6KGawsKYkNbNkTWOc2rJ8n6sXEQV1FiorzOHRRTU8uqgGgCsDw+z87Lw3s6S9m5/tOMGPPjwGQEM82Zs72XCqqbJIJyhFZpiCWijIjfLAXbN44K5ZAAwNj7C/89oJyncOdfGzHScAqCy+1pt7TdNNenO/8AJ885tw/Dg0NMBzz8Gzz870jyWSMRTU8nti0QgrZpezYnb5mN7cl1Nj3Fvbunl17+/35l7dGKflN78k90//BPr6vJ0dOwYbN3r3FdYiUzLpFV5ux6oFJa717+6d9v1KcFwdGuFi/yC9/UNc7B/kysAwAPd84xB53YO//w3VefDC/TNcpfiiZ6f3taLFzyrCo6IF7v3Wna/wInKjvFiEvOI8Kou9PtuDw46L/YPk9owT0oA7c5XuywOU5MfI1VxukduSnqAuXaim4VkmB4gDNDR6wx036Cit5sHffROAeZVF180sUW/uDKOFA6adjqhlej33nDcmPTpGDVBYSM3/+O+89PDnUqvhvLbvVKo3d01p3nWr4SysUW9ukbEU1DK9Rk8Y3jDrI/bss6wEVjZU8O+Tvbk/7bqUWg1na1s3W3Z3AlCaH2NV8jL5NU0VLK8vV29uyWrpOZmYJWsmyvQZrzf30TOXAfXmDh0NfUyJTiZK4Kk3t8jNKaglsMbrzb3j+PlUw6kbe3OvboxzX5MX3vXqzS0ZREEtoVGSn8PDC6p4eEEVAFeHhtlz4kJqnHvLrg5e3Kre3JJ5FNQSWnmxKKsa46xqjMMj13pzezNLevjtkWu9uSsKc1jVGE+Ncy9NlJKj+dwSEgpqyRhje3P/m2Rv7mPn+q7NLGnv5o0xvbnvaSj3ZpY0xrmnQb25JbgU1JKxzIzGyiIaK4v4g1XXenNvSwV3D99+69Pre3Mn53OrN7cEiYJaskpNaT7PrEjwzIpkb+4rg2w/5vXm3tbezfd/28bfv6fe3BIsCmrJamUF1/fm7h9M9uZODpW8NKY395x4QWqoZHVTnHnqzS0zREEtMkZ+TpT7583i/nnXenMf6LzI1vZutrad491DZ3hpx0kAKotzUwsN37Q3t8g0UFCL3EIsGmH57DKWzy7jq2ubbtmbu3i0N3djBWuaZrFidhn5OTpBKXdOQS1yG8yM+dXFzK8u5itrGgDoOH8lFdzb2rv5b6+fASA3GqF5TllqnPveuRWU5usEpdw+BbXIHUqUF7ChpZ4NLfUA9FweoPVYTyq8n3/vKH/37hEiBovrSq/rFFhVkudz9RIGCmqRaVZRlMvjS2p4fIl3grJvYIiPj59PHXH/ZNtn/OCDdgCaKouuW4OyIV6oE5TyexTUImlWmBvjwfmVPDi/EoDB4RH2nryQPOLu4fX9p/l/rd7iwdUleanpgKsb4yyqVW9uUVCLzLicaIR7Giq4p6GCjQ/ByIjj8JlLqZOT29q7+aV6c8sYCmoRn0UixoKaEhbUlPBH988dtzf32we7AG+typY55akugerNnR30JywSMOP15j57KdmbO3kF5dje3EtHe3M3xlndWMGsYp2gzDQTBrWZfR94Buhyzi1Lf0kicqPK4jyeXFbHk8u83tyXrg6xY8zMkh99eIzvJXtz31VVxJqmazNLZlcU+lm6TIPJHFH/APgO8MP0liIik1WcF+OhBVU8dLPe3Ls7eXGrt3hwoiw/tRLOmqY486uKdYIyZCYMaufce2bWOAO1iMgUjdeb+9Cpi2xtO8e29h4+OHKOTcne3OWFOayaG6d5dhlL60tZmiijuiRP0wIDbNrGqM1sI7ARoKGhYbp2KyJTEI0YSxKlLEmUjtubu/VYD28eOJ16fWVxLksSZSxNlCZvZcyNF+rIOyCmLaidc88Dz4O3Cvl07VdE7tx4vbl7+wc50NHLvtTtAh8cPsvQiPfXtyg3yuK6a8G9JFHKgpoSTQ/0gWZ9iGSp0vwc7ps3i/uSnQLBG+v+9PQl9nVcSAX4T7ef4P/8zmv1mhM17q4uuXbkXV/G4rpSijVFMK302xWRlLxYlGX1ZSyrL0ttGx5xtJ+7nDrq3t/Ry1sHu/jpdu9qSjNonFXEkjpvqOXZvkEK86Lk+vVDZKDJTM97EXgEqDSzE8B/ds59L92FiUgwRCPGXVXF3FVVzPpmb2Uc5xyne6+OOfK+wK4T5/nlnk7undcLwH947s3UsMno1znxAp20nILJzPr4ykwUIiLhYWbUluVTW5bPFxbXpLZf6BuEt/6avoFhHiyrZF/HBd779CzDyXHvkrwYi8ecsFyaKGV+dbFWhJ+Ahj5EZNqUFeZAQQ5lBTn87WMtgLe82aFTF1NH3vs6enlx63H6B0cAyI1FWFhzbdx7SaKMxXUlFOYqnkbpNyEiaZWfE6V5TjnNc8pT24ZHHG1nL1034+S1faf48TbvIh0zrwXs0humDMaLsnPkW0EtIjMuGjHmV5cwv7okteCCc46OC/3sH3PkveNYDy/v6kh9X11ZfuqoezTA68szf9xbQS0igWBm1JcXUF9ekFp0AbwVc/Z39l43ZfDtg10kh70pK8hhyeh87+SVlvMqi4hl0Li3glpEAq2iKPe6hRcArgwMc/DUtYt19ndc4IcfHmNgyBv3zotFWJS6WMcL70W1JaFdbFhBLSKhU5AbTS2+MGpoeIQjZy5fN2Vwy64O/vGj4wBEDO6qKv69KYNlhcFfcFhBLSIZIRaNsLC2hIW1Jfzzld620UUYRo+693X08uHRbn6x89q4d315wfXhXV9KbWl+oMa9FdQikrHGLsLw5LLa1PZzl65eN+Nkf0cvbxw4jUuOe8eLcr2TlsmrLZcmymiqLCLqU5MqBbWIZJ1ZxXnX9fMGuHx1iAOdvd6Jy5O97Ou8wD/8tp2BYW/cuzA3yqLakuuGTRbUFpMXS/+4t4JaRAQoyotd6+mdNDA0wuGua02q9nf08vOPT/KjD70mVbGIMb+6OHXUvTTZWrY0f3rHvRXUIiI3kRuLpPp6/6vktpERx/HuvuumDP7m07O8tONk6vsa4oXXzThZmiilujR/ynUoqEVEbkMkcq2399PL61Lbuy72p466RwP81b2nUs9XFuclj7xvf3EGBbWIyDSoLsmnemE+n19YndrW2z/Iwc6L112s87/fO5panKE4L8YjC6v4zh+uvOW+FdQiImlSmp+TWhF+1I2LM5TkTxzDCmoRkRk03uIME8mci+FFRDKUglpEJOAU1CIiAaegFhEJOAW1iEjAKahFRAJOQS0iEnAKahGRgDM32oB1OndqdhE4NO07Tr9K4KzfRUyRaveHavdHWGu/Vd1znXNV4z2RrisTDznnVqVp32ljZq1hrBtUu19Uuz/CWvtU69bQh4hIwCmoRUQCLl1B/Xya9ptuYa0bVLtfVLs/wlr7lOpOy8lEERGZPhr6EBEJOAW1iEjATWtQm9mTZnbIzA6b2V9O576nm5l938y6zGzvmG1xM3vDzD5Nfq3ws8abMbM5ZvaOme03s31m9vXk9sDXb2b5ZrbVzHYla/+r5PYmM/so+dn5iZnl+l3reMwsamYfm9mW5OOw1N1uZnvMbKeZtSa3Bf7zAmBm5Wb2T2Z20MwOmNkDYajdzBYmf9+jt14z+7Op1D5tQW1mUeB/Ak8BS4CvmNmS6dp/GvwAePKGbX8JvOWcuxt4K/k4iIaAP3fOLQHuB76W/F2Hof6rwKPOuWagBXjSzO4H/gvwt865+UAP8FX/SrylrwMHxjwOS90An3fOtYyZxxuGzwvAt4HXnHOLgGa833/ga3fOHUr+vluAe4E+4OdMpXbn3LTcgAeAX415/A3gG9O1/3TcgEZg75jHh4C65P06vAt3fK9zEj/HJuDxsNUPFAI7gPvwrtaKjfdZCsoNmJ38i/UosAWwMNSdrK0dqLxhW+A/L0AZ0EZy4kOYar+h3i8Cv51q7dM59FEPfDbm8YnktjCpcc51Ju+fAmr8LGYyzKwRuAf4iJDUnxw+2Al0AW8AR4Dzzrmh5EuC+tn5FvAXwEjy8SzCUTeAA143s+1mtjG5LQyflybgDPAPySGn75pZEeGofawvAy8m79927TqZeBPO++cu0HMXzawY+BnwZ8653rHPBbl+59yw8/47OBtYAyzyt6KJmdkzQJdzbrvftUzRWufcSryhya+Z2UNjnwzw5yUGrAT+l3PuHuAyNwwVBLh2AJLnLdYDP73xucnWPp1BfRKYM+bx7OS2MDltZnUAya9dPtdzU2aWgxfSLzjnXkpuDk39AM6588A7eEMG5WY22nsmiJ+dB4H1ZtYO/Bhv+OPbBL9uAJxzJ5Nfu/DGSdcQjs/LCeCEc+6j5ON/wgvuMNQ+6ilgh3PudPLxbdc+nUG9Dbg7eRY8F+9Qf/M07n8mbAb+OHn/j/HGfgPHzAz4HnDAOfc3Y54KfP1mVmVm5cn7BXhj6wfwAvtfJl8WuNqdc99wzs12zjXifbbfds49S8DrBjCzIjMrGb2PN166lxB8Xpxzp4DPzGxhctMXgP2EoPYxvsK1YQ+YSu3TPGD+NPAJ3pjjN/0ewJ+g1heBTmAQ71/tr+KNOb4FfAq8CcT9rvMmta/F++/SbmBn8vZ0GOoHVgAfJ2vfC/yn5PZ5wFbgMN5/EfP8rvUWP8MjwJaw1J2scVfytm/072YYPi/JOluA1uRn5hdARYhqLwLOAWVjtt127bqEXEQk4HQyUUQk4BTUIiIBp6AWEQk4BbWISMApqEVEAk5BLSIScApqEZGA+/8UV0a+IA1UCwAAAABJRU5ErkJggg==\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"########## Example ##########\n",
"########## ^^^^^^^^ ##########\n",
"##############################\n",
"\n",
"config = {\n",
" 'l1_gas_per_second': 11000000,\n",
" 'l1_gas_price': 180,\n",
" 'l2_gas_per_second': 11000000,\n",
" 'fee_scaler': fee_scaler,\n",
" 'eth_price': 2300,\n",
" 'txs': [\n",
" {\n",
" # mint\n",
" 'l1_gas_cost': 2500,\n",
" 'l2_gas_cost': 200000,\n",
" 'frequency': 0.5\n",
" },\n",
" {\n",
" # burn\n",
" 'l1_gas_cost': 2500,\n",
" 'l2_gas_cost': 200000,\n",
" 'frequency': 0.5\n",
" }\n",
" ],\n",
" 'tps_demand_curve': get_simple_demand_curve([\n",
" { 'fee': 5, 'tps': 0 }, # fee denominated in USD\n",
" { 'fee': 2, 'tps': 10 },\n",
" { 'fee': 1, 'tps': 50 },\n",
" { 'fee': 0, 'tps': 100 }\n",
" ])\n",
"}\n",
"\n",
"m = TransactionSupplyDemandModel(config)\n",
"\n",
"print(f\"Fee: ${m.get_fee()} USD\")\n",
"print(f\"TPS: {m.get_tps()} transactions per second\")\n",
"m.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f86b0ea5",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment