Class: Klay::Tx::Eip1559
- Inherits:
-
Object
- Object
- Klay::Tx::Eip1559
- Defined in:
- lib/klay/tx/eip1559.rb
Overview
Provides support for EIP-1559 transactions utilizing EIP-2718 types and envelopes. Ref: https://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) ⇒ Klay::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) ⇒ Klay::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: https://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 |
# File 'lib/klay/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[: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: https://eips.ethereum.org/EIPS/eip-2930
53 54 55 |
# File 'lib/klay/tx/eip1559.rb', line 53 def access_list @access_list end |
#amount ⇒ Object (readonly)
The transaction amount in Wei.
46 47 48 |
# File 'lib/klay/tx/eip1559.rb', line 46 def amount @amount end |
#chain_id ⇒ Object (readonly)
The EIP-155 Chain ID. Ref: https://eips.ethereum.org/EIPS/eip-155
28 29 30 |
# File 'lib/klay/tx/eip1559.rb', line 28 def chain_id @chain_id end |
#destination ⇒ Object (readonly)
The recipient address.
43 44 45 |
# File 'lib/klay/tx/eip1559.rb', line 43 def destination @destination end |
#gas_limit ⇒ Object (readonly)
The gas limit for the transaction.
40 41 42 |
# File 'lib/klay/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/klay/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/klay/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/klay/tx/eip1559.rb', line 49 def payload @payload end |
#sender ⇒ Object (readonly)
The sender address.
65 66 67 |
# File 'lib/klay/tx/eip1559.rb', line 65 def sender @sender end |
#signature_r ⇒ Object (readonly)
The signature r
value.
59 60 61 |
# File 'lib/klay/tx/eip1559.rb', line 59 def signature_r @signature_r end |
#signature_s ⇒ Object (readonly)
The signature s
value.
62 63 64 |
# File 'lib/klay/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/klay/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/klay/tx/eip1559.rb', line 31 def signer_nonce @signer_nonce end |
#type ⇒ Object (readonly)
The transaction type.
68 69 70 |
# File 'lib/klay/tx/eip1559.rb', line 68 def type @type end |
Instance Method Details
#decode(hex) ⇒ Klay::Tx::Eip1559
Decodes a raw transaction hex into an Klay::Tx::Eip1559 transaction object.
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 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 |
# File 'lib/klay/tx/eip1559.rb', line 138 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_error DecoderError, "Cannot decode EIP-1559 payload!" end # last but not least, set the type. @type = TYPE_1559 # recover sender address v = Chain.to_v recovery_id, chain_id public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v.to_s(16)}", chain_id) address = Util.public_key_to_address(public_key).to_s @sender = Tx.sanitize_address address end |
#encoded ⇒ String
Encodes a raw transaction object, wraps it in an EIP-2718 envelope with an EIP-1559 type prefix.
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/klay/tx/eip1559.rb', line 259 def encoded unless Tx.is_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.
293 294 295 |
# File 'lib/klay/tx/eip1559.rb', line 293 def hash Util.bin_to_hex Util.keccak256 encoded end |
#hex ⇒ String
Gets the encoded, enveloped, raw transaction hex.
286 287 288 |
# File 'lib/klay/tx/eip1559.rb', line 286 def hex Util.bin_to_hex encoded end |
#sign(key) ⇒ String
Sign the transaction with a given key.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/klay/tx/eip1559.rb', line 232 def sign(key) if Tx.is_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) ⇒ Klay::Tx::Eip1559
Creates an unsigned copy of a transaction payload.
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/klay/tx/eip1559.rb', line 200 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.
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/klay/tx/eip1559.rb', line 301 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 |