Skip to main content

How Base Gas Works

Aptos transactions by default charge a base gas fee, regardless of market conditions. For each transaction, this "base gas" amount is based on three conditions:

  1. Instructions.
  2. Storage.
  3. Payload.

The more function calls, branching conditional statements, etc. that a transaction requires, the more instruction gas it will cost. Likewise, the more reads from and writes into global storage that a transaction requires, the more storage gas it will cost. Finally, the more bytes in a transaction payload, the more it will cost.

As explained in the optimization principles section, storage gas has by far the largest effect on base gas. For background on the Aptos gas model, see The Making of the Aptos Gas Schedule.

Instruction gas

Basic instruction gas parameters are defined at and include the following instruction types:


nopA no-operation

Control flow

br_trueExecute conditional true branch
br_falseExecute conditional false branch


popPop from stack
ld_u8Load a u8
ld_u64Load a u64
ld_u128Load a u128
ld_trueLoad a true
ld_falseLoad a false
ld_const_baseBase cost to load a constant
ld_const_per_bytePer-byte cost to load a constant

Local scope

imm_borrow_locImmutably borrow
mut_borrow_locMutably borrow
imm_borrow_fieldImmutably borrow a field
mut_borrow_fieldMutably borrow a field
copy_loc_baseBase cost to copy


call_baseBase cost for a function call
call_per_argCost per function argument
call_generic_per_ty_argCost per type argument


pack_baseBase cost to pack a struct
pack_per_fieldCost to pack a struct, per field
unpack_baseBase cost to unpack a struct
unpack_per_fieldCost to unpack a struct, per field


read_ref_baseBase cost to read from a reference
write_ref_baseBase cost to write to a reference
freeze_refFreeze a reference


cast_u8Cast to a u8
cast_u64Cast to a u64
cast_u128Cast to a u128




bit_orOR: |
bit_andAND: &
xorXOR: ^
shlShift left: <<
shrShift right: >>


orOR: ||
andAND: &&
notNOT: !


ltLess than: <
gtGreater than: >
leLess than or equal to: <=
geGreater than or equal to: >=
eq_baseBase equality cost: ==
neq_baseBase not equal cost: !=

Global storage

imm_borrow_global_baseBase cost to immutably borrow: borrow_global<T>()
mut_borrow_global_baseBase cost to mutably borrow: borrow_global_mut<T>()
exists_baseBase cost to check existence: exists<T>()
move_from_baseBase cost to move from: move_from<T>()
move_to_baseBase cost to move to: move_to<T>()


vec_len_baseLength of a vector
vec_imm_borrow_baseImmutably borrow an element
vec_mut_borrow_baseMutably borrow an element
vec_push_back_basePush back
vec_pop_back_basePop from the back
vec_swap_baseSwap elements
vec_pack_baseBase cost to pack a vector
vec_pack_per_elemCost to pack a vector per element
vec_unpack_baseBase cost to unpack a vector
vec_unpack_per_expected_elemBase cost to unpack a vector per element

Additional storage gas parameters are defined in,, and other assorted source files in aptos-gas/src/.

Storage gas

Storage gas is defined in storage_gas.move, which is accompanied by a comprehensive and internally-linked DocGen file at

In short:

  1. In initialize(), base_8192_exponential_curve() is used to generate an exponential curve whereby per-item and per-byte costs increase rapidly as utilization approaches an upper bound.
  2. Parameters are reconfigured each epoch via on_reconfig(), based on item-wise and byte-wise utilization ratios.
  3. Reconfigured parameters are stored in StorageGas, which contains the following fields:
per_item_readCost to read an item from global storage
per_item_createCost to create an item in global storage
per_item_writeCost to overwrite an item in global storage
per_byte_readCost to read a byte from global storage
per_byte_createCost to create a byte in global storage
per_byte_writeCost to overwrite a byte in global storage

Here, an item is either a resource having the key attribute, or an entry in a table, and notably, per-byte costs are assessed on the entire size of an item. As stated in, for example, if an operation mutates a u8 field in a resource that has five other u128 fields, the per-byte gas write cost will account for (5128)/8+1=81(5 * 128) / 8 + 1 = 81 bytes.


Byte-wise fees are similarly assessed on vectors, which consume i=0n1ei+b(n)\sum_{i = 0}^{n - 1} e_i + b(n) bytes, where:

  • nn is the number of elements in the vector
  • eie_i is the size of element ii
  • b(n)b(n) is a "base size" which is a function of nn

See the BCS sequence specification for more information on vector base size (technically a ULEB128), which typically occupies just one byte in practice, such that a vector of 100 u8 elements accounts for 100+1=101100 + 1 = 101 bytes. Hence per the item-wise read methodology described above, reading the last element of such a vector is treated as a 101-byte read.

Payload gas

Payload gas is defined in transaction/, which incorporates storage gas with several payload- and pricing-associated parameters:

min_transaction_gas_unitsMinimum internal gas units for a transaction, charged at the start of execution
large_transaction_cutoffSize, in bytes, above which transactions will be charged an additional amount per byte
intrinsic_gas_per_byteInternal gas units charged per byte for payloads above large_transaction_cutoff
maximum_number_of_gas_unitsUpper limit on external gas units for a transaction
min_price_per_gas_unitMinimum gas price allowed for a transaction
max_price_per_gas_unitMaximum gas price allowed for a transaction
max_transaction_size_in_bytesMaximum transaction payload size in bytes
gas_unit_scaling_factorConversion factor between internal gas units and external gas units

Here, "internal gas units" are defined as constants in source files like and storage_gas.move, which are more granular than "external gas units" by a factor of gas_unit_scaling_factor: to convert from internal gas units to external gas units, divide by gas_unit_scaling_factor. Then, to convert from external gas units to octas, multiply by the "gas price", which denotes the number of octas per unit of external gas.

Optimization principles

Unit and pricing constants

As of the time of this writing, min_price_per_gas_unit in transaction/ is defined as aptos_global_constants::GAS_UNIT_PRICE (which is itself defined as 100), with other noteworthy transaction/ constants as follows:


See Payload gas for the meaning of these constants.

Storage gas

As of the time of this writing, initialize() sets the following minimum storage gas amounts:

Data styleOperationSymbolMinimum internal gas
Per itemReadrir_i300,000
Per itemCreatecic_i5,000,000
Per itemWritewiw_i300,000
Per byteReadrbr_b300
Per byteCreatecbc_b5,000
Per byteWritewbw_b5,000

Maximum amounts are 100 times the minimum amounts, which means that for a utilization ratio of 40% or less, total gas costs will be on the order of 1 to 1.5 times the minimum amounts (see base_8192_exponential_curve() for supporting calculations). Hence, in terms of octas, initial mainnet gas costs can be estimated as follows (divide internal gas by scaling factor, then multiply by minimum gas price):

OperationOperationMinimum octas
Per-item readrir_i3000
Per-item createcic_i50,000
Per-item writewiw_i3000
Per-byte readrbr_b3
Per-byte createcbc_b50
Per-byte writewbw_b50

Here, the most expensive per-item operation by far is creating a new item (via either move_to<T>() or adding to a table), which costs nearly 17 times as much as reading or overwriting an old item: ci=16.6ri=16.6wic_i = 16.\overline{6} r_i = 16.\overline{6} w_i. Additionally:

  • Writes cost the same as reads on a per-item basis: wi=riw_i = r_i
  • On a per-byte basis, however, writes cost the same as creates: wb=cbw_b = c_b
  • Per-byte writes and creates cost nearly 17 times as much as per-byte reads: wb=cb=16.6rbw_b = c_b = 16.\overline{6} r_b
  • Per-item reads cost 1000 times as much as per-byte reads: ri=1000rbr_i = 1000 r_b
  • Per-item creates cost 1000 times as much as per-byte creates: ci=1000cbc_i = 1000 c_b
  • Per-item writes cost 60 times as much as per-byte writes: wi=60wbw_i = 60 w_b

Hence per-item operations cost 1000 times more than per-byte operations for both reads and creates, but only 60 times more for writes.

Thus, in the absence of a legitimate economic incentive to deallocate from global storage (via either move_from<T>() or by removing from a table), the most effective storage gas optimization strategy is as follows:

  1. Minimize per-item creations
  2. Track unused items and overwrite them, rather than creating new items, when possible
  3. Contain per-item writes to as few items as possible
  4. Read, rather than write, whenever possible
  5. Minimize the number of bytes in all operations, especially writes

Instruction gas

As of the time of this writing, all instruction gas operations are multiplied by the EXECUTION_GAS_MULTIPLIER defined in, which is set to 20. Hence the following representative operations assume gas costs as follows (divide internal gas by scaling factor, then multiply by minimum gas price):

OperationMinimum octas
Table add/borrow/remove box240
Function call200
Load constant130
Globally borrow100
Read/write reference40
Load u128 on stack16
Table box operation per byte2

(Note that per-byte table box operation instruction gas does not account for storage gas, which is assessed separately).

For comparison, reading a 100-byte item costs ri+100rb=3000+1003=3300r_i + 100 * r_b = 3000 + 100 * 3 = 3300 octas at minimum, some 16.5 times as much as a function call, and in general, instruction gas costs are largely dominated by storage gas costs.

Notably, however, there is still technically an incentive to reduce the number of function calls in a program, but engineering efforts are more effectively dedicated to writing modular, decomposed code that is geared toward reducing storage gas costs, rather than attempting to write repetitive code blocks with fewer nested functions (in nearly all cases).

In extreme cases it is possible for instruction gas to far outweigh storage gas, for example if a loopwise mathematical function takes 10,000 iterations to converge; but again this is an extreme case and for most applications storage gas has a larger impact on base gas than does instruction gas.

Payload gas

As of the time of this writing, transaction/ defines the minimum amount of internal gas per transaction as 1,500,000 internal units (15,000 octas at minimum), an amount that increases by 2,000 internal gas units (20 octas minimum) per byte for payloads larger than 600 bytes, with the maximum number of bytes permitted in a transaction set at 65536. Hence in practice, payload gas is unlikely to be a concern.