-
-
Save karlfloersch/6ac6f853cfcc79e8a0c2f8ff683256de to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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": "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\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