-
Notifications
You must be signed in to change notification settings - Fork 126
Transaction gas costs
This Dune query can be used to collect block stats: https://explore.duneanalytics.com/queries/19430/source
This query returns stats for all blocks of block size 386 in the last 7 days, containing 262 blocks when I executed the query.
Data can be pasted in a worksheet and saved as block_stats.csv
. My data can be downloaded here: https://drive.google.com/file/d/1QRjag7wwgNlzRWYVh14H_1EXXOhwcbdp/view?usp=sharing
The following python script can be used to run over a bunch of parameters to find the closest fit. An extra penalty can be given to predictions that are lower than the actual gas used by the block.
import csv
from collections import namedtuple
MyStruct = namedtuple("BlockStats", "gas_used num_noops num_deposits num_withdrawals num_transfers num_trades num_account_updates num_amm_updates num_signatures")
with open('block_stats.csv', newline='') as csvfile:
csv_reader = csv.reader(csvfile, delimiter=',', quotechar='"')
block_stats = []
line_count = 0
for row in csv_reader:
if line_count == 0:
print(f'Column names are {", ".join(row)}')
line_count += 1
else:
line_count += 1
block_stat = MyStruct(int(row[1]), int(row[2]), int(row[3]), int(row[4]), int(row[5]), int(row[6]), int(row[7]), int(row[8]), int(row[9]))
block_stats.append(block_stat)
print(f'Processed {line_count} lines.')
best_total_error = 2**64
best_total_gas_delta = 0
best_noop_cost = 0
best_deposit_cost = 0
best_withdrawal_cost = 0
best_transfer_cost = 0
best_trade_cost= 0
best_account_update_cost = 0
best_amm_update_cost = 0
best_signature_cost = 0
'''
# Used to roughly find good values
for noop_cost in range(0, 2000, 200):
print("**noop_cost**: " + str(noop_cost))
for deposit_cost in range(-10000, 10000, 2000):
print("**deposit_cost**: " + str(deposit_cost))
for withdrawal_cost in range(30000, 60000, 5000):
print("**withdrawal_cost**: " + str(withdrawal_cost))
for transfer_cost in range(400, 2000, 200):
for trade_cost in range(400, 2000, 200):
for account_update_cost in range(5000, 20000, 2000):
for amm_update_cost in range(30000, 70000, 5000):
for signature_cost in range(1000, 10000, 2000):
'''
'''
# Used to more accurately find the better values
for noop_cost in range(800, 1300, 100):
print("**noop_cost**: " + str(noop_cost))
for deposit_cost in range(-2000, 2000, 1000):
print("**deposit_cost**: " + str(deposit_cost))
for withdrawal_cost in range(42000, 48000, 1000):
print("**withdrawal_cost**: " + str(withdrawal_cost))
for transfer_cost in range(1000, 1500, 100):
for trade_cost in range(1000, 1500, 100):
for account_update_cost in range(13000, 17000, 1000):
for amm_update_cost in range(40000, 50000, 1000):
for signature_cost in range(1000, 9000, 1000):
'''
# Used to find the best values
for noop_cost in range(0, 50, 50):
print("**noop_cost**: " + str(noop_cost))
for deposit_cost in range(-1000, 500, 500):
print("**deposit_cost**: " + str(deposit_cost))
for withdrawal_cost in range(44000, 48000, 500):
print("**withdrawal_cost**: " + str(withdrawal_cost))
for transfer_cost in range(1300, 1450, 50):
for trade_cost in range(1300, 1450, 50):
for account_update_cost in range(14500, 18500, 500):
for amm_update_cost in range(44500, 48000, 500):
for signature_cost in range(4500, 7000, 500):
total_error = 0
total_gas_delta = 0
# Run over all blocks
for stat in block_stats:
prediction = noop_cost * stat.num_noops + \
deposit_cost * stat.num_deposits + \
withdrawal_cost * stat.num_withdrawals + \
transfer_cost * stat.num_transfers + \
trade_cost * stat.num_trades + \
account_update_cost * stat.num_account_updates + \
amm_update_cost * stat.num_amm_updates + \
signature_cost * stat.num_signatures
error = abs(prediction - stat.gas_used)
# Extra penalty for predictions that are too low
if prediction < stat.gas_used:
error = error*1
# accumulate error
total_error = total_error + error
# accumulate gas
total_gas_delta = total_gas_delta + (prediction - stat.gas_used)
# Check total error
if total_error < best_total_error:
best_total_error = total_error
best_total_gas_delta = total_gas_delta
best_noop_cost = noop_cost
best_deposit_cost = deposit_cost
best_withdrawal_cost = withdrawal_cost
best_transfer_cost = transfer_cost
best_trade_cost = trade_cost
best_account_update_cost = account_update_cost
best_amm_update_cost = amm_update_cost
best_signature_cost = signature_cost
print("best error updated: " + str(total_error))
print("total gas delta: " + str(total_gas_delta))
print("- noop_cost: " + str(best_noop_cost))
print("- deposit_cost: " + str(best_deposit_cost))
print("- withdrawal_cost: " + str(best_withdrawal_cost))
print("- transfer_cost: " + str(best_transfer_cost))
print("- trade_cost: " + str(best_trade_cost))
print("- account_update_cost: " + str(best_account_update_cost))
print("- amm_update_cost: " + str(best_amm_update_cost))
print("- signature_cost: " + str(best_signature_cost))
print("Best total error: " + str(best_total_error))
print("Total gas delta: " + str(best_total_gas_delta))
print("- noop_cost: " + str(best_noop_cost))
print("- deposit_cost: " + str(best_deposit_cost))
print("- withdrawal_cost: " + str(best_withdrawal_cost))
print("- transfer_cost: " + str(best_transfer_cost))
print("- trade_cost: " + str(best_trade_cost))
print("- account_update_cost: " + str(best_account_update_cost))
print("- amm_update_cost: " + str(best_amm_update_cost))
print("- signature_cost: " + str(best_signature_cost))
The error rate can be visualised on Dune: https://explore.duneanalytics.com/queries/19403/source#39659
A positive error means the predicted gas usage is higher than the actual gas used. A negative error means the prediction gas usage is lower than the actual gas used.
Total gas delta
is the difference between the sum of all gas usage predictions minus the sum of all gas used. So it can be seen as the "gas surplus" if positive, or "gas deficit" if negative.
Best total error: 7377650
Total gas delta: -413108
- noop_cost: 0
- deposit_cost: -1000
- withdrawal_cost: 45000
- transfer_cost: 1300
- trade_cost: 1350
- account_update_cost: 17500
- amm_update_cost: 46000
- signature_cost: 6000
Best total error: 10388670
Total gas delta: 2698842
- noop_cost: 0
- deposit_cost: 0
- withdrawal_cost: 46000
- transfer_cost: 1350
- trade_cost: 1350
- account_update_cost: 17500
- amm_update_cost: 46000
- signature_cost: 6500
Best total error: 14554087
Total gas delta: 6162342
- noop_cost: 0
- deposit_cost: 0
- withdrawal_cost: 47500
- transfer_cost: 1350
- trade_cost: 1350
- account_update_cost: 18000
- amm_update_cost: 47000
- signature_cost: 6000
The average gas costs provide a very good fit to the actual gas used, mostly within 5%. All 3 cases have a gas surplus.
Good average gas costs for operations are:
- Withdrawal: 46,000-47,500 gas
- Transfer: 1,300 - 1,350 gas
- Trade: 1,350 gas
- Account Update: 17,500-18,000 gas
- AMM join/AMM exit: 102,000 - 104,000 gas
An AMM join/exit is: 2x AMM updates + 3x transfers + 1x signature verification
Once compression is enabled gas costs for noops/trades/transfers should go down. Submitting multiple blocks at once when we have enough throughput will also lower gas costs.
This Dune query can be used to find the min, max and average gas used for fast withdrawals per token: https://explore.duneanalytics.com/queries/19463
The query uses all transactions and will automatically add tokens to the table as fast withdrawals for the token are done.
I think the average gas used + some small extra buffer would be a fair cost to charge users.
Because it's a stand-alone tx it's also possible to use the web3 function to estimate the gas used. But remember that that gas estimation does not estimate the actual gas used, only the min gas needed not to have out of gas errors, so it could estimate a higher gas value than actually used.
Loopring Foundation
nothing here