Class: Eth::Tx::Eip1559
- Inherits:
-
Object
- Object
- Eth::Tx::Eip1559
- Defined in:
- lib/eth/tx/eip1559.rb
Overview
Provides support for EIP-1559 transactions utilizing EIP-2718 types and envelopes. Ref: eips.ethereum.org/EIPS/eip-1559
Instance Attribute Summary collapse
-
#access_list ⇒ Object
readonly
An optional EIP-2930 access list.
-
#amount ⇒ Object
readonly
The transaction amount in Wei.
-
#chain_id ⇒ Object
readonly
The EIP-155 Chain ID.
-
#destination ⇒ Object
readonly
The recipient address.
-
#gas_limit ⇒ Object
readonly
The gas limit for the transaction.
-
#max_fee_per_gas ⇒ Object
readonly
The transaction max fee per gas in Wei.
-
#max_priority_fee_per_gas ⇒ Object
readonly
The transaction max priority fee per gas in Wei.
-
#payload ⇒ Object
readonly
The transaction data payload.
-
#sender ⇒ Object
readonly
The sender address.
-
#signature_r ⇒ Object
readonly
The signature
r
value. -
#signature_s ⇒ Object
readonly
The signature
s
value. -
#signature_y_parity ⇒ Object
readonly
The signature’s y-parity byte (not v).
-
#signer_nonce ⇒ Object
readonly
The transaction nonce provided by the signer.
-
#type ⇒ Object
readonly
The transaction type.
Instance Method Summary collapse
-
#decode(hex) ⇒ Eth::Tx::Eip1559
Decodes a raw transaction hex into an Eip1559 transaction object.
-
#encoded ⇒ String
Encodes a raw transaction object, wraps it in an EIP-2718 envelope with an EIP-1559 type prefix.
-
#hash ⇒ String
Gets the transaction hash.
-
#hex ⇒ String
Gets the encoded, enveloped, raw transaction hex.
-
#initialize(params) ⇒ Eip1559
constructor
Create a type-2 (EIP-1559) transaction payload object that can be prepared for envelope, signature and broadcast.
-
#sign(key) ⇒ String
Sign the transaction with a given key.
-
#unsigned_copy(tx) ⇒ Eth::Tx::Eip1559
Creates an unsigned copy of a transaction payload.
-
#unsigned_encoded ⇒ String
Encodes the unsigned transaction payload in an EIP-1559 envelope, required for signing.
-
#unsigned_hash ⇒ String
Gets the sign-hash required to sign a raw transaction.
Constructor Details
#initialize(params) ⇒ Eip1559
Create a type-2 (EIP-1559) transaction payload object that can be prepared for envelope, signature and broadcast. Ref: eips.ethereum.org/EIPS/eip-1559
86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 |
# File 'lib/eth/tx/eip1559.rb', line 86 def initialize(params) fields = { recovery_id: nil, r: 0, s: 0 }.merge params # populate optional fields with serializable empty values fields[:chain_id] = Tx.sanitize_chain fields[:chain_id] fields[:from] = Tx.sanitize_address fields[:from] fields[:to] = Tx.sanitize_address fields[:to] fields[:value] = Tx.sanitize_amount fields[:value] fields[:data] = Tx.sanitize_data fields[:data] # ensure sane values for all mandatory fields fields = Tx.validate_params fields fields = Tx.validate_eip1559_params fields fields[:access_list] = Tx.sanitize_list fields[:access_list] # ensure gas limit is not too low minimum_cost = Tx.estimate_intrinsic_gas fields[:data], fields[:access_list] raise ParameterError, "Transaction gas limit is too low, try #{minimum_cost}!" if fields[:gas_limit].to_i < minimum_cost # populate class attributes @signer_nonce = fields[:nonce].to_i @max_priority_fee_per_gas = fields[:priority_fee].to_i @max_fee_per_gas = fields[:max_gas_fee].to_i @gas_limit = fields[:gas_limit].to_i @sender = fields[:from].to_s @destination = fields[:to].to_s @amount = fields[:value].to_i @payload = fields[:data] @access_list = fields[:access_list] # the signature v is set to the chain id for unsigned transactions @signature_y_parity = fields[:recovery_id] @chain_id = fields[:chain_id] # the signature fields are empty for unsigned transactions. @signature_r = fields[:r] @signature_s = fields[:s] # last but not least, set the type. @type = TYPE_1559 end |
Instance Attribute Details
#access_list ⇒ Object (readonly)
An optional EIP-2930 access list. Ref: eips.ethereum.org/EIPS/eip-2930
53 54 55 |
# File 'lib/eth/tx/eip1559.rb', line 53 def access_list @access_list end |
#amount ⇒ Object (readonly)
The transaction amount in Wei.
46 47 48 |
# File 'lib/eth/tx/eip1559.rb', line 46 def amount @amount end |
#chain_id ⇒ Object (readonly)
The EIP-155 Chain ID. Ref: eips.ethereum.org/EIPS/eip-155
28 29 30 |
# File 'lib/eth/tx/eip1559.rb', line 28 def chain_id @chain_id end |
#destination ⇒ Object (readonly)
The recipient address.
43 44 45 |
# File 'lib/eth/tx/eip1559.rb', line 43 def destination @destination end |
#gas_limit ⇒ Object (readonly)
The gas limit for the transaction.
40 41 42 |
# File 'lib/eth/tx/eip1559.rb', line 40 def gas_limit @gas_limit end |
#max_fee_per_gas ⇒ Object (readonly)
The transaction max fee per gas in Wei.
37 38 39 |
# File 'lib/eth/tx/eip1559.rb', line 37 def max_fee_per_gas @max_fee_per_gas end |
#max_priority_fee_per_gas ⇒ Object (readonly)
The transaction max priority fee per gas in Wei.
34 35 36 |
# File 'lib/eth/tx/eip1559.rb', line 34 def max_priority_fee_per_gas @max_priority_fee_per_gas end |
#payload ⇒ Object (readonly)
The transaction data payload.
49 50 51 |
# File 'lib/eth/tx/eip1559.rb', line 49 def payload @payload end |
#sender ⇒ Object (readonly)
The sender address.
65 66 67 |
# File 'lib/eth/tx/eip1559.rb', line 65 def sender @sender end |
#signature_r ⇒ Object (readonly)
The signature r
value.
59 60 61 |
# File 'lib/eth/tx/eip1559.rb', line 59 def signature_r @signature_r end |
#signature_s ⇒ Object (readonly)
The signature s
value.
62 63 64 |
# File 'lib/eth/tx/eip1559.rb', line 62 def signature_s @signature_s end |
#signature_y_parity ⇒ Object (readonly)
The signature’s y-parity byte (not v).
56 57 58 |
# File 'lib/eth/tx/eip1559.rb', line 56 def signature_y_parity @signature_y_parity end |
#signer_nonce ⇒ Object (readonly)
The transaction nonce provided by the signer.
31 32 33 |
# File 'lib/eth/tx/eip1559.rb', line 31 def signer_nonce @signer_nonce end |
#type ⇒ Object (readonly)
The transaction type.
68 69 70 |
# File 'lib/eth/tx/eip1559.rb', line 68 def type @type end |
Instance Method Details
#decode(hex) ⇒ Eth::Tx::Eip1559
Decodes a raw transaction hex into an Eth::Tx::Eip1559 transaction object.
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 166 167 168 169 170 171 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 197 198 199 |
# File 'lib/eth/tx/eip1559.rb', line 139 def decode(hex) hex = Util.remove_hex_prefix hex type = hex[0, 2] raise TransactionTypeError, "Invalid transaction type #{type}!" if type.to_i(16) != TYPE_1559 bin = Util.hex_to_bin hex[2..] tx = Rlp.decode bin # decoded transactions always have 9 + 3 fields, even if they are empty or zero raise ParameterError, "Transaction missing fields!" if tx.size < 9 # populate the 9 payload fields chain_id = Util.deserialize_big_endian_to_int tx[0] nonce = Util.deserialize_big_endian_to_int tx[1] priority_fee = Util.deserialize_big_endian_to_int tx[2] max_gas_fee = Util.deserialize_big_endian_to_int tx[3] gas_limit = Util.deserialize_big_endian_to_int tx[4] to = Util.bin_to_hex tx[5] value = Util.deserialize_big_endian_to_int tx[6] data = tx[7] access_list = tx[8] # populate class attributes @chain_id = chain_id.to_i @signer_nonce = nonce.to_i @max_priority_fee_per_gas = priority_fee.to_i @max_fee_per_gas = max_gas_fee.to_i @gas_limit = gas_limit.to_i @destination = to.to_s @amount = value.to_i @payload = data @access_list = access_list # populate the 3 signature fields if tx.size == 9 _set_signature(nil, 0, 0) elsif tx.size == 12 recovery_id = Util.bin_to_hex(tx[9]).to_i(16) r = Util.bin_to_hex tx[10] s = Util.bin_to_hex tx[11] # allows us to force-setting a signature if the transaction is signed already _set_signature(recovery_id, r, s) else raise DecoderError, "Cannot decode EIP-1559 payload!" end # last but not least, set the type. @type = TYPE_1559 unless recovery_id.nil? # recover sender address v = Chain.to_v recovery_id, chain_id public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id) address = Util.public_key_to_address(public_key).to_s @sender = Tx.sanitize_address address else # keep the 'from' field blank @sender = Tx.sanitize_address nil end end |
#encoded ⇒ String
Encodes a raw transaction object, wraps it in an EIP-2718 envelope with an EIP-1559 type prefix.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/eth/tx/eip1559.rb', line 265 def encoded unless Tx.signed? self raise Signature::SignatureError, "Transaction is not signed!" end tx_data = [] tx_data.push Util.serialize_int_to_big_endian @chain_id tx_data.push Util.serialize_int_to_big_endian @signer_nonce tx_data.push Util.serialize_int_to_big_endian @max_priority_fee_per_gas tx_data.push Util.serialize_int_to_big_endian @max_fee_per_gas tx_data.push Util.serialize_int_to_big_endian @gas_limit tx_data.push Util.hex_to_bin @destination tx_data.push Util.serialize_int_to_big_endian @amount tx_data.push Rlp::Sedes.binary.serialize @payload tx_data.push Rlp::Sedes.infer(@access_list).serialize @access_list tx_data.push Util.serialize_int_to_big_endian @signature_y_parity tx_data.push Util.serialize_int_to_big_endian @signature_r tx_data.push Util.serialize_int_to_big_endian @signature_s tx_encoded = Rlp.encode tx_data # create an EIP-2718 envelope with EIP-1559 type payload tx_type = Util.serialize_int_to_big_endian @type return "#{tx_type}#{tx_encoded}" end |
#hash ⇒ String
Gets the transaction hash.
299 300 301 |
# File 'lib/eth/tx/eip1559.rb', line 299 def hash Util.bin_to_hex Util.keccak256 encoded end |
#hex ⇒ String
Gets the encoded, enveloped, raw transaction hex.
292 293 294 |
# File 'lib/eth/tx/eip1559.rb', line 292 def hex Util.bin_to_hex encoded end |
#sign(key) ⇒ String
Sign the transaction with a given key.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/eth/tx/eip1559.rb', line 238 def sign(key) if Tx.signed? self raise Signature::SignatureError, "Transaction is already signed!" end # ensure the sender address matches the given key unless @sender.nil? or sender.empty? signer_address = Tx.sanitize_address key.address.to_s from_address = Tx.sanitize_address @sender raise Signature::SignatureError, "Signer does not match sender" unless signer_address == from_address end # sign a keccak hash of the unsigned, encoded transaction signature = key.sign(unsigned_hash, @chain_id) r, s, v = Signature.dissect signature recovery_id = Chain.to_recovery_id v.to_i(16), @chain_id @signature_y_parity = recovery_id @signature_r = r @signature_s = s return hash end |
#unsigned_copy(tx) ⇒ Eth::Tx::Eip1559
Creates an unsigned copy of a transaction payload.
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 |
# File 'lib/eth/tx/eip1559.rb', line 206 def unsigned_copy(tx) # not checking transaction validity unless it's of a different class raise TransactionTypeError, "Cannot copy transaction of different payload type!" unless tx.instance_of? Tx::Eip1559 # populate class attributes @signer_nonce = tx.signer_nonce @max_priority_fee_per_gas = tx.max_priority_fee_per_gas @max_fee_per_gas = tx.max_fee_per_gas @gas_limit = tx.gas_limit @destination = tx.destination @amount = tx.amount @payload = tx.payload @access_list = tx.access_list @chain_id = tx.chain_id # force-set signature to unsigned _set_signature(nil, 0, 0) # keep the 'from' field blank @sender = Tx.sanitize_address nil # last but not least, set the type. @type = TYPE_1559 end |
#unsigned_encoded ⇒ String
Encodes the unsigned transaction payload in an EIP-1559 envelope, required for signing.
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/eth/tx/eip1559.rb', line 307 def unsigned_encoded tx_data = [] tx_data.push Util.serialize_int_to_big_endian @chain_id tx_data.push Util.serialize_int_to_big_endian @signer_nonce tx_data.push Util.serialize_int_to_big_endian @max_priority_fee_per_gas tx_data.push Util.serialize_int_to_big_endian @max_fee_per_gas tx_data.push Util.serialize_int_to_big_endian @gas_limit tx_data.push Util.hex_to_bin @destination tx_data.push Util.serialize_int_to_big_endian @amount tx_data.push Rlp::Sedes.binary.serialize @payload tx_data.push Rlp::Sedes.infer(@access_list).serialize @access_list tx_encoded = Rlp.encode tx_data # create an EIP-2718 envelope with EIP-1559 type payload (unsigned) tx_type = Util.serialize_int_to_big_endian @type return "#{tx_type}#{tx_encoded}" end |