Module: Eth::Tx
- Extended by:
- Tx
- Included in:
- Tx
- Defined in:
- lib/eth/tx.rb,
lib/eth/tx/legacy.rb,
lib/eth/tx/eip1559.rb,
lib/eth/tx/eip2930.rb,
lib/eth/tx/eip7702.rb
Overview
Provides the Tx
module supporting various transaction types.
Defined Under Namespace
Classes: DecoderError, Eip1559, Eip2930, Eip7702, Legacy, ParameterError, TransactionTypeError
Constant Summary collapse
- DEFAULT_GAS_LIMIT =
The minimum transaction gas limit required for a value transfer.
21_000.freeze
- DEFAULT_PRIORITY_FEE =
The “default” transaction priority fee of 1.01 GWei. Do not use.
(1.01 * Unit::GWEI).freeze
- DEFAULT_GAS_PRICE =
The “default” transaction gas price of 42.69 GWei. Do not use.
(42.69 * Unit::GWEI).freeze
- COST_NON_ZERO_BYTE =
The calldata gas cost of a non-zero byte as per EIP-2028.
16.freeze
- COST_ZERO_BYTE =
The calldata gas cost of a zero byte.
4.freeze
- COST_INITCODE_WORD =
The initcode gas cost for each word (32 bytes).
2.freeze
- COST_STORAGE_KEY =
The access list gas cost of a storage key as per EIP-2930.
1_900.freeze
- COST_ADDRESS =
The access list gas cost of an address as per EIP-2930.
2_400.freeze
- BLOCK_GAS_LIMIT =
The maximum transaction gas limit is bound by the block gas limit.
30_000_000.freeze
- TYPE_LEGACY =
The legacy transaction type is 0.
0x00.freeze
- TYPE_2930 =
The EIP-2930 transaction type is 1.
0x01.freeze
- TYPE_1559 =
The EIP-1559 transaction type is 2.
0x02.freeze
- TYPE_4844 =
The EIP-4844 transaction type is 3.
0x03.freeze
- TYPE_7702 =
The EIP-7702 transaction type is 4.
0x04.freeze
- ZERO_BYTE =
The zero byte is 0x00.
"\x00".freeze
- CREATE_GAS =
Smart contract transaction gas cost
32_000.freeze
Class Method Summary collapse
-
.decode(hex) ⇒ Eth::Tx
Decodes a transaction hex of any known type (2, 1, or legacy).
-
.estimate_intrinsic_gas(data = "", list = []) ⇒ Integer
Estimates intrinsic gas for provided call data (EIP-2028) and access lists (EIP-2930).
-
.new(params, chain_id = Chain::ETHEREUM) ⇒ Object
Creates a new transaction of any type for given parameters and chain ID.
-
.sanitize_address(addr) ⇒ String
Populates the transaction destination address with a serializable empty value in case it is undefined; also ensures the address is checksummed but not prefixed for consistency.
-
.sanitize_amount(val) ⇒ Integer
Populates the transaction value field with a serializable empty value in case it is undefined.
-
.sanitize_chain(id) ⇒ Integer
Populates the transaction chain id field with a serializable default value (1) in case it is undefined.
-
.sanitize_data(data) ⇒ String
Populates the transaction payload field with a serializable empty value in case it is undefined; also ensures the data is binary not hex.
-
.sanitize_list(list) ⇒ Array
Populates the transaction access list field with a serializable empty array in case it is undefined; also ensures the nested data is binary not hex.
-
.signed?(tx) ⇒ Bool
Allows to check wether a transaction is signed already.
-
.unsigned_copy(tx) ⇒ Eth::Tx
Creates an unsigned copy of any transaction object.
-
.validate_eip1559_params(fields) ⇒ Hash
Validates the common type-2 transaction fields such as priority fee and max gas fee.
-
.validate_eip7702_params(fields) ⇒ Hash
Validates that the type-4 transaction field authorization list is present.
-
.validate_legacy_params(fields) ⇒ Hash
Validates the common legacy transaction fields such as gas price.
-
.validate_params(fields) ⇒ Hash
Validates the common transaction fields such as nonce, gas limit, amount, and access list.
Instance Method Summary collapse
-
#decode(hex) ⇒ Eth::Tx
Decodes a transaction hex of any known type (2, 1, or legacy).
-
#estimate_intrinsic_gas(data = "", list = []) ⇒ Integer
Estimates intrinsic gas for provided call data (EIP-2028) and access lists (EIP-2930).
-
#new(params, chain_id = Chain::ETHEREUM) ⇒ Object
Creates a new transaction of any type for given parameters and chain ID.
-
#sanitize_address(addr) ⇒ String
Populates the transaction destination address with a serializable empty value in case it is undefined; also ensures the address is checksummed but not prefixed for consistency.
-
#sanitize_amount(val) ⇒ Integer
Populates the transaction value field with a serializable empty value in case it is undefined.
-
#sanitize_chain(id) ⇒ Integer
Populates the transaction chain id field with a serializable default value (1) in case it is undefined.
-
#sanitize_data(data) ⇒ String
Populates the transaction payload field with a serializable empty value in case it is undefined; also ensures the data is binary not hex.
-
#sanitize_list(list) ⇒ Array
Populates the transaction access list field with a serializable empty array in case it is undefined; also ensures the nested data is binary not hex.
-
#signed?(tx) ⇒ Bool
Allows to check wether a transaction is signed already.
-
#unsigned_copy(tx) ⇒ Eth::Tx
Creates an unsigned copy of any transaction object.
-
#validate_eip1559_params(fields) ⇒ Hash
Validates the common type-2 transaction fields such as priority fee and max gas fee.
-
#validate_eip7702_params(fields) ⇒ Hash
Validates that the type-4 transaction field authorization list is present.
-
#validate_legacy_params(fields) ⇒ Hash
Validates the common legacy transaction fields such as gas price.
-
#validate_params(fields) ⇒ Hash
Validates the common transaction fields such as nonce, gas limit, amount, and access list.
Class Method Details
.decode(hex) ⇒ Eth::Tx
Decodes a transaction hex of any known type (2, 1, or legacy).
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/eth/tx.rb', line 135 def decode(hex) hex = Util.remove_hex_prefix hex type = hex[0, 2].to_i(16) case type when TYPE_1559 # EIP-1559 transaction (type 2) return Tx::Eip1559.decode hex when TYPE_2930 # EIP-2930 transaction (type 1) return Tx::Eip2930.decode hex when TYPE_4844 # EIP-4844 transaction (type 3) raise NotimplementedError, "EIP-4844 blob transactions are not implemented" when TYPE_7702 # EIP-7702 transaction (type 4) return Tx::Eip7702.decode hex else # Legacy transaction if first byte is RLP (>= 192) if type >= 0xc0 return Tx::Legacy.decode hex else raise TransactionTypeError, "Cannot decode unknown transaction type #{type}!" end end end |
.estimate_intrinsic_gas(data = "", list = []) ⇒ Integer
Estimates intrinsic gas for provided call data (EIP-2028) and access lists (EIP-2930). Respects initcode word cost (EIP-3860).
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/eth/tx.rb', line 204 def estimate_intrinsic_gas(data = "", list = []) gas = DEFAULT_GAS_LIMIT unless data.nil? or data.empty? data = Util.hex_to_bin data if Util.hex? data # count zero bytes zero = data.count ZERO_BYTE gas += zero * COST_ZERO_BYTE # count non-zero bytes none = data.size - zero gas += none * COST_NON_ZERO_BYTE # count "words" as per EIP-3860 word_count = (data.length.to_f / 32.0).ceil gas += word_count * COST_INITCODE_WORD end unless list.nil? or list.empty? list.each do |entry| # count addresses gas += COST_ADDRESS entry.last.each do |key| # count storage keys gas += COST_STORAGE_KEY end end end return gas.to_i end |
.new(params, chain_id = Chain::ETHEREUM) ⇒ Object
Creates a new transaction of any type for given parameters and chain ID. Required parameters are (optional in brackets): - EIP-1559: chain_id, nonce, priority_fee, max_gas_fee, gas_limit(, from, to, value, data, access_list) - EIP-2930: chain_id, nonce, gas_price, gas_limit, access_list(, from, to, value, data) - EIP-7702: chain_id, nonce, priority_fee, max_gas_fee, gas_limit, authorizations(, from, to, value, data, access_list) - Legacy: nonce, gas_price, gas_limit(, from, to, value, data)
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/eth/tx.rb', line 100 def new(params, chain_id = Chain::ETHEREUM) # if we deal with blobs, attempt EIP-4844 (not implemented) unless params[:max_fee_per_blob_gas].nil? raise NotimplementedError, "EIP-4844 blob transactions are not implemented" end # if we deal with authorizations, attempt EIP-7702 unless params[:authorization_list].nil? params[:chain_id] = chain_id if params[:chain_id].nil? return Tx::Eip7702.new params end # if we deal with max gas fee parameter, attempt EIP-1559 unless params[:max_gas_fee].nil? params[:chain_id] = chain_id if params[:chain_id].nil? return Tx::Eip1559.new params end # if we deal with access list parameter, attempt EIP-2930 unless params[:access_list].nil? params[:chain_id] = chain_id if params[:chain_id].nil? return Tx::Eip2930.new params end # if nothing else, go with legacy transactions chain_id = params[:chain_id] if !params[:chain_id].nil? and params[:chain_id] != chain_id return Tx::Legacy.new params, chain_id end |
.sanitize_address(addr) ⇒ String
Populates the transaction destination address with a serializable empty value in case it is undefined; also ensures the address is checksummed but not prefixed for consistency.
321 322 323 324 325 326 327 328 |
# File 'lib/eth/tx.rb', line 321 def sanitize_address(addr) addr = "" if addr.nil? if addr.is_a? String and !addr.empty? addr = Address.new(addr).to_s addr = Util.remove_hex_prefix addr end return addr end |
.sanitize_amount(val) ⇒ Integer
Populates the transaction value field with a serializable empty value in case it is undefined.
335 336 337 338 |
# File 'lib/eth/tx.rb', line 335 def sanitize_amount(val) val = 0 if val.nil? return val end |
.sanitize_chain(id) ⇒ Integer
Populates the transaction chain id field with a serializable default value (1) in case it is undefined.
310 311 312 313 |
# File 'lib/eth/tx.rb', line 310 def sanitize_chain(id) id = Chain::ETHEREUM if id.nil? return id end |
.sanitize_data(data) ⇒ String
Populates the transaction payload field with a serializable empty value in case it is undefined; also ensures the data is binary not hex.
345 346 347 348 349 350 351 |
# File 'lib/eth/tx.rb', line 345 def sanitize_data(data) data = "" if data.nil? # ensure payload to be binary if it's hex, otherwise we'll treat it raw data = Util.hex_to_bin data if Util.hex? data return data end |
.sanitize_list(list) ⇒ Array
Populates the transaction access list field with a serializable empty array in case it is undefined; also ensures the nested data is binary not hex.
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/eth/tx.rb', line 359 def sanitize_list(list) list = [] if list.nil? list.each_with_index do |value, index| if value.is_a? Array # recursively check the entire array list[index] = sanitize_list value elsif Util.hex? value # only modify if we find a hex value list[index] = Util.hex_to_bin value end end return list end |
.signed?(tx) ⇒ Bool
Allows to check wether a transaction is signed already.
378 379 380 381 |
# File 'lib/eth/tx.rb', line 378 def signed?(tx) !tx.signature_r.nil? and tx.signature_r != 0 and !tx.signature_s.nil? and tx.signature_s != 0 end |
.unsigned_copy(tx) ⇒ Eth::Tx
Creates an unsigned copy of any transaction object.
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/eth/tx.rb', line 172 def unsigned_copy(tx) case tx.type when TYPE_1559 # EIP-1559 transaction (type 2) return Tx::Eip1559.unsigned_copy tx when TYPE_2930 # EIP-2930 transaction (type 1) return Tx::Eip2930.unsigned_copy tx when TYPE_4844 # EIP-4844 transaction (type 3) raise NotimplementedError, "EIP-4844 blob transactions are not implemented" when TYPE_7702 # EIP-7702 transaction (type 4) return Tx::Eip7702.unsigned_copy tx when TYPE_LEGACY # Legacy transaction ("type 0") return Tx::Legacy.unsigned_copy tx end raise TransactionTypeError, "Cannot copy unknown transaction type #{tx.type}!" end |
.validate_eip1559_params(fields) ⇒ Hash
Validates the common type-2 transaction fields such as priority fee and max gas fee.
271 272 273 274 275 276 277 278 279 |
# File 'lib/eth/tx.rb', line 271 def validate_eip1559_params(fields) if fields[:priority_fee].nil? or fields[:priority_fee] < 0 raise ParameterError, "Invalid gas priority fee #{fields[:priority_fee]}!" end if fields[:max_gas_fee].nil? or fields[:max_gas_fee] < 0 raise ParameterError, "Invalid max gas fee #{fields[:max_gas_fee]}!" end return fields end |
.validate_eip7702_params(fields) ⇒ Hash
Validates that the type-4 transaction field authorization list is present
286 287 288 289 290 291 |
# File 'lib/eth/tx.rb', line 286 def validate_eip7702_params(fields) unless fields[:authorization_list].nil? or fields[:authorization_list].is_a? Array raise ParameterError, "Invalid authorization list #{fields[:authorization_list]}!" end return fields end |
.validate_legacy_params(fields) ⇒ Hash
Validates the common legacy transaction fields such as gas price.
298 299 300 301 302 303 |
# File 'lib/eth/tx.rb', line 298 def validate_legacy_params(fields) if fields[:gas_price].nil? or fields[:gas_price] < 0 raise ParameterError, "Invalid gas price #{fields[:gas_price]}!" end return fields end |
.validate_params(fields) ⇒ Hash
Validates the common transaction fields such as nonce, gas limit, amount, and access list.
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/eth/tx.rb', line 246 def validate_params(fields) if fields[:nonce].nil? or fields[:nonce] < 0 raise ParameterError, "Invalid signer nonce #{fields[:nonce]}!" end if fields[:gas_limit].nil? or fields[:gas_limit] < DEFAULT_GAS_LIMIT or (fields[:gas_limit] > BLOCK_GAS_LIMIT and fields[:chain_id] == Chain::ETHEREUM) raise ParameterError, "Invalid gas limit #{fields[:gas_limit]}!" end unless fields[:value] >= 0 raise ParameterError, "Invalid transaction value #{fields[:value]}!" end unless fields[:access_list].nil? or fields[:access_list].is_a? Array raise ParameterError, "Invalid access list #{fields[:access_list]}!" end return fields end |
Instance Method Details
#decode(hex) ⇒ Eth::Tx
Decodes a transaction hex of any known type (2, 1, or legacy).
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/eth/tx.rb', line 135 def decode(hex) hex = Util.remove_hex_prefix hex type = hex[0, 2].to_i(16) case type when TYPE_1559 # EIP-1559 transaction (type 2) return Tx::Eip1559.decode hex when TYPE_2930 # EIP-2930 transaction (type 1) return Tx::Eip2930.decode hex when TYPE_4844 # EIP-4844 transaction (type 3) raise NotimplementedError, "EIP-4844 blob transactions are not implemented" when TYPE_7702 # EIP-7702 transaction (type 4) return Tx::Eip7702.decode hex else # Legacy transaction if first byte is RLP (>= 192) if type >= 0xc0 return Tx::Legacy.decode hex else raise TransactionTypeError, "Cannot decode unknown transaction type #{type}!" end end end |
#estimate_intrinsic_gas(data = "", list = []) ⇒ Integer
Estimates intrinsic gas for provided call data (EIP-2028) and access lists (EIP-2930). Respects initcode word cost (EIP-3860).
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/eth/tx.rb', line 204 def estimate_intrinsic_gas(data = "", list = []) gas = DEFAULT_GAS_LIMIT unless data.nil? or data.empty? data = Util.hex_to_bin data if Util.hex? data # count zero bytes zero = data.count ZERO_BYTE gas += zero * COST_ZERO_BYTE # count non-zero bytes none = data.size - zero gas += none * COST_NON_ZERO_BYTE # count "words" as per EIP-3860 word_count = (data.length.to_f / 32.0).ceil gas += word_count * COST_INITCODE_WORD end unless list.nil? or list.empty? list.each do |entry| # count addresses gas += COST_ADDRESS entry.last.each do |key| # count storage keys gas += COST_STORAGE_KEY end end end return gas.to_i end |
#new(params, chain_id = Chain::ETHEREUM) ⇒ Object
Creates a new transaction of any type for given parameters and chain ID. Required parameters are (optional in brackets): - EIP-1559: chain_id, nonce, priority_fee, max_gas_fee, gas_limit(, from, to, value, data, access_list) - EIP-2930: chain_id, nonce, gas_price, gas_limit, access_list(, from, to, value, data) - EIP-7702: chain_id, nonce, priority_fee, max_gas_fee, gas_limit, authorizations(, from, to, value, data, access_list) - Legacy: nonce, gas_price, gas_limit(, from, to, value, data)
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/eth/tx.rb', line 100 def new(params, chain_id = Chain::ETHEREUM) # if we deal with blobs, attempt EIP-4844 (not implemented) unless params[:max_fee_per_blob_gas].nil? raise NotimplementedError, "EIP-4844 blob transactions are not implemented" end # if we deal with authorizations, attempt EIP-7702 unless params[:authorization_list].nil? params[:chain_id] = chain_id if params[:chain_id].nil? return Tx::Eip7702.new params end # if we deal with max gas fee parameter, attempt EIP-1559 unless params[:max_gas_fee].nil? params[:chain_id] = chain_id if params[:chain_id].nil? return Tx::Eip1559.new params end # if we deal with access list parameter, attempt EIP-2930 unless params[:access_list].nil? params[:chain_id] = chain_id if params[:chain_id].nil? return Tx::Eip2930.new params end # if nothing else, go with legacy transactions chain_id = params[:chain_id] if !params[:chain_id].nil? and params[:chain_id] != chain_id return Tx::Legacy.new params, chain_id end |
#sanitize_address(addr) ⇒ String
Populates the transaction destination address with a serializable empty value in case it is undefined; also ensures the address is checksummed but not prefixed for consistency.
321 322 323 324 325 326 327 328 |
# File 'lib/eth/tx.rb', line 321 def sanitize_address(addr) addr = "" if addr.nil? if addr.is_a? String and !addr.empty? addr = Address.new(addr).to_s addr = Util.remove_hex_prefix addr end return addr end |
#sanitize_amount(val) ⇒ Integer
Populates the transaction value field with a serializable empty value in case it is undefined.
335 336 337 338 |
# File 'lib/eth/tx.rb', line 335 def sanitize_amount(val) val = 0 if val.nil? return val end |
#sanitize_chain(id) ⇒ Integer
Populates the transaction chain id field with a serializable default value (1) in case it is undefined.
310 311 312 313 |
# File 'lib/eth/tx.rb', line 310 def sanitize_chain(id) id = Chain::ETHEREUM if id.nil? return id end |
#sanitize_data(data) ⇒ String
Populates the transaction payload field with a serializable empty value in case it is undefined; also ensures the data is binary not hex.
345 346 347 348 349 350 351 |
# File 'lib/eth/tx.rb', line 345 def sanitize_data(data) data = "" if data.nil? # ensure payload to be binary if it's hex, otherwise we'll treat it raw data = Util.hex_to_bin data if Util.hex? data return data end |
#sanitize_list(list) ⇒ Array
Populates the transaction access list field with a serializable empty array in case it is undefined; also ensures the nested data is binary not hex.
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/eth/tx.rb', line 359 def sanitize_list(list) list = [] if list.nil? list.each_with_index do |value, index| if value.is_a? Array # recursively check the entire array list[index] = sanitize_list value elsif Util.hex? value # only modify if we find a hex value list[index] = Util.hex_to_bin value end end return list end |
#signed?(tx) ⇒ Bool
Allows to check wether a transaction is signed already.
378 379 380 381 |
# File 'lib/eth/tx.rb', line 378 def signed?(tx) !tx.signature_r.nil? and tx.signature_r != 0 and !tx.signature_s.nil? and tx.signature_s != 0 end |
#unsigned_copy(tx) ⇒ Eth::Tx
Creates an unsigned copy of any transaction object.
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/eth/tx.rb', line 172 def unsigned_copy(tx) case tx.type when TYPE_1559 # EIP-1559 transaction (type 2) return Tx::Eip1559.unsigned_copy tx when TYPE_2930 # EIP-2930 transaction (type 1) return Tx::Eip2930.unsigned_copy tx when TYPE_4844 # EIP-4844 transaction (type 3) raise NotimplementedError, "EIP-4844 blob transactions are not implemented" when TYPE_7702 # EIP-7702 transaction (type 4) return Tx::Eip7702.unsigned_copy tx when TYPE_LEGACY # Legacy transaction ("type 0") return Tx::Legacy.unsigned_copy tx end raise TransactionTypeError, "Cannot copy unknown transaction type #{tx.type}!" end |
#validate_eip1559_params(fields) ⇒ Hash
Validates the common type-2 transaction fields such as priority fee and max gas fee.
271 272 273 274 275 276 277 278 279 |
# File 'lib/eth/tx.rb', line 271 def validate_eip1559_params(fields) if fields[:priority_fee].nil? or fields[:priority_fee] < 0 raise ParameterError, "Invalid gas priority fee #{fields[:priority_fee]}!" end if fields[:max_gas_fee].nil? or fields[:max_gas_fee] < 0 raise ParameterError, "Invalid max gas fee #{fields[:max_gas_fee]}!" end return fields end |
#validate_eip7702_params(fields) ⇒ Hash
Validates that the type-4 transaction field authorization list is present
286 287 288 289 290 291 |
# File 'lib/eth/tx.rb', line 286 def validate_eip7702_params(fields) unless fields[:authorization_list].nil? or fields[:authorization_list].is_a? Array raise ParameterError, "Invalid authorization list #{fields[:authorization_list]}!" end return fields end |
#validate_legacy_params(fields) ⇒ Hash
Validates the common legacy transaction fields such as gas price.
298 299 300 301 302 303 |
# File 'lib/eth/tx.rb', line 298 def validate_legacy_params(fields) if fields[:gas_price].nil? or fields[:gas_price] < 0 raise ParameterError, "Invalid gas price #{fields[:gas_price]}!" end return fields end |
#validate_params(fields) ⇒ Hash
Validates the common transaction fields such as nonce, gas limit, amount, and access list.
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/eth/tx.rb', line 246 def validate_params(fields) if fields[:nonce].nil? or fields[:nonce] < 0 raise ParameterError, "Invalid signer nonce #{fields[:nonce]}!" end if fields[:gas_limit].nil? or fields[:gas_limit] < DEFAULT_GAS_LIMIT or (fields[:gas_limit] > BLOCK_GAS_LIMIT and fields[:chain_id] == Chain::ETHEREUM) raise ParameterError, "Invalid gas limit #{fields[:gas_limit]}!" end unless fields[:value] >= 0 raise ParameterError, "Invalid transaction value #{fields[:value]}!" end unless fields[:access_list].nil? or fields[:access_list].is_a? Array raise ParameterError, "Invalid access list #{fields[:access_list]}!" end return fields end |