Skip to content

Transaction gas costs

Brecht Devos edited this page Feb 7, 2021 · 14 revisions

Collecting block stats

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

Finding tx costs

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**32

    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(1000, 1300, 50):
        print("**noop_cost**: " + str(noop_cost))
        for deposit_cost in range(-1000, 1000, 500):
            print("**deposit_cost**: " + str(deposit_cost))
            for withdrawal_cost in range(45000, 48000, 500):
                print("**withdrawal_cost**: " + str(withdrawal_cost))
                for transfer_cost in range(1200, 1450, 50):
                    for trade_cost in range(1200, 1450, 50):
                        for account_update_cost in range(15000, 19000, 500):
                            for amm_update_cost in range(45000, 48000, 500):
                                for signature_cost in range(3500, 6000, 500):

                                    total_error = 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*4

                                        # accumulate error
                                        total_error = total_error + error


                                    # Check total error
                                    if total_error < best_total_error:
                                        best_total_error = total_error
                                        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("- 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("- 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))

Results

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.

Best fit is found with the following parameters:

Best total error: 6062210
- noop_cost:           1100
- deposit_cost:        -1000
- withdrawal_cost:     45500
- transfer_cost:       1350
- trade_cost:          1350
- account_update_cost: 16000
- amm_update_cost:     46000
- signature_cost:      5000

With a x2 penalty for predictions that are too low we find:

Best total error: 8143239
- noop_cost:           1200
- deposit_cost:        -500
- withdrawal_cost:     46500
- transfer_cost:       1350
- trade_cost:          1350
- account_update_cost: 17500
- amm_update_cost:     46000
- signature_cost:      5000

With a x4 penalty for predictions that are too low we find:

Error rate x4
Best total error: 10378752
- noop_cost:           1100
- deposit_cost:        500
- withdrawal_cost:     47500
- transfer_cost:       1350
- trade_cost:          1350
- account_update_cost: 17000
- amm_update_cost:     46500
- signature_cost:      5000

Conclusion

The average gas costs provide a very good fit to the actual gas used.

Good average gas costs for operations are:

  • Withdrawal: 45,500-47,500 gas
  • Transfer: 1,350 gas
  • Trade: 1,350 gas
  • Account Update: 16,000-17,000 gas
  • AMM join/AMM exit: 101,000 - 102,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.

nothing here

Clone this wiki locally