eth 0.4.18 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,329 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Provides the `Eth` module.
16
+ module Eth
17
+
18
+ # Provides the `Tx` module supporting various transaction types.
19
+ module Tx
20
+
21
+ # Provides support for EIP-1559 transactions utilizing EIP-2718
22
+ # types and envelopes.
23
+ class Eip1559
24
+
25
+ # The EIP-155 Chain ID.
26
+ attr_reader :chain_id
27
+
28
+ # The transaction nonce provided by the signer.
29
+ attr_reader :signer_nonce
30
+
31
+ # The transaction max priority fee per gas in Wei.
32
+ attr_reader :max_priority_fee_per_gas
33
+
34
+ # The transaction max fee per gas in Wei.
35
+ attr_reader :max_fee_per_gas
36
+
37
+ # The gas limit for the transaction.
38
+ attr_reader :gas_limit
39
+
40
+ # The recipient address.
41
+ attr_reader :destination
42
+
43
+ # The transaction amount in Wei.
44
+ attr_reader :amount
45
+
46
+ # The transaction data payload.
47
+ attr_reader :payload
48
+
49
+ # An optional EIP-2930 access list.
50
+ attr_reader :access_list
51
+
52
+ # The signature's y-parity byte (not v).
53
+ attr_reader :signature_y_parity
54
+
55
+ # The signature `r` value.
56
+ attr_reader :signature_r
57
+
58
+ # The signature `s` value.
59
+ attr_reader :signature_s
60
+
61
+ # The sender address.
62
+ attr_reader :sender
63
+
64
+ # The transaction type.
65
+ attr_reader :type
66
+
67
+ # Create a type-2 (EIP-1559) transaction payload object that
68
+ # can be prepared for envelope, signature and broadcast.
69
+ #
70
+ # @param params [Hash] all necessary transaction fields.
71
+ # @option params [Integer] :nonce the signer nonce.
72
+ # @option params [Integer] :priority_fee the max priority fee per gas.
73
+ # @option params [Integer] :max_gas_fee the max transaction fee per gas.
74
+ # @option params [Integer] :gas_limit the gas limit.
75
+ # @option params [Eth::Address] :from the sender address.
76
+ # @option params [Eth::Address] :to the reciever address.
77
+ # @option params [Integer] :value the transaction value.
78
+ # @option params [String] :data the transaction data payload.
79
+ # @option params [Array] :access_list an optional access list.
80
+ def initialize(params)
81
+ fields = { recovery_id: nil, r: 0, s: 0 }.merge params
82
+
83
+ # populate optional fields with serializable empty values
84
+ fields[:chain_id] = Tx.sanitize_chain fields[:chain_id]
85
+ fields[:from] = Tx.sanitize_address fields[:from]
86
+ fields[:to] = Tx.sanitize_address fields[:to]
87
+ fields[:value] = Tx.sanitize_amount fields[:value]
88
+ fields[:data] = Tx.sanitize_data fields[:data]
89
+
90
+ # ensure sane values for all mandatory fields
91
+ fields = Tx.validate_params fields
92
+ fields[:access_list] = Tx.sanitize_list fields[:access_list]
93
+
94
+ # ensure gas limit is not too low
95
+ minimum_cost = Tx.estimate_intrinsic_gas fields[:data], fields[:access_list]
96
+ raise ParameterError, "Transaction gas limit is too low, try #{minimum_cost}!" if fields[:gas_limit].to_i < minimum_cost
97
+
98
+ # populate class attributes
99
+ @signer_nonce = fields[:nonce].to_i
100
+ @max_priority_fee_per_gas = fields[:priority_fee].to_i
101
+ @max_fee_per_gas = fields[:max_gas_fee].to_i
102
+ @gas_limit = fields[:gas_limit].to_i
103
+ @sender = fields[:from].to_s
104
+ @destination = fields[:to].to_s
105
+ @amount = fields[:value].to_i
106
+ @payload = fields[:data]
107
+ @access_list = fields[:access_list]
108
+
109
+ # the signature v is set to the chain id for unsigned transactions
110
+ @signature_y_parity = fields[:recovery_id]
111
+ @chain_id = fields[:chain_id]
112
+
113
+ # the signature fields are empty for unsigned transactions.
114
+ @signature_r = fields[:r]
115
+ @signature_s = fields[:s]
116
+
117
+ # last but not least, set the type.
118
+ @type = TYPE_1559
119
+ end
120
+
121
+ # Overloads the constructor for decoding raw transactions and creating unsigned copies.
122
+ konstructor :decode, :unsigned_copy
123
+
124
+ # Decodes a raw transaction hex into an Eth::Tx::Eip1559
125
+ # transaction object.
126
+ #
127
+ # @param hex [String] the raw transaction hex-string.
128
+ # @return [Eth::Tx::Eip1559] transaction payload.
129
+ # @raise [TransactionTypeError] if transaction type is invalid.
130
+ # @raise [ParameterError] if transaction is missing fields.
131
+ # @raise [DecoderError] if transaction decoding fails.
132
+ def decode(hex)
133
+ hex = Util.remove_hex_prefix hex
134
+ type = hex[0, 2]
135
+ raise TransactionTypeError, "Invalid transaction type #{type}!" if type.to_i(16) != TYPE_1559
136
+
137
+ bin = Util.hex_to_bin hex[2..]
138
+ tx = RLP.decode(bin)
139
+
140
+ # decoded transactions always have 9 + 3 fields, even if they are empty or zero
141
+ raise ParameterError, "Transaction missing fields!" if tx.size < 9
142
+
143
+ # populate the 9 payload fields
144
+ chain_id = Util.deserialize_big_endian_to_int tx[0]
145
+ nonce = Util.deserialize_big_endian_to_int tx[1]
146
+ priority_fee = Util.deserialize_big_endian_to_int tx[2]
147
+ max_gas_fee = Util.deserialize_big_endian_to_int tx[3]
148
+ gas_limit = Util.deserialize_big_endian_to_int tx[4]
149
+ to = Util.bin_to_hex tx[5]
150
+ value = Util.deserialize_big_endian_to_int tx[6]
151
+ data = tx[7]
152
+ access_list = tx[8]
153
+
154
+ # populate class attributes
155
+ @chain_id = chain_id.to_i
156
+ @signer_nonce = nonce.to_i
157
+ @max_priority_fee_per_gas = priority_fee.to_i
158
+ @max_fee_per_gas = max_gas_fee.to_i
159
+ @gas_limit = gas_limit.to_i
160
+ @destination = to.to_s
161
+ @amount = value.to_i
162
+ @payload = data
163
+ @access_list = access_list
164
+
165
+ # populate the 3 signature fields
166
+ if tx.size == 9
167
+ _set_signature(nil, 0, 0)
168
+ elsif tx.size == 12
169
+ recovery_id = Util.bin_to_hex(tx[9]).to_i(16)
170
+ r = Util.bin_to_hex tx[10]
171
+ s = Util.bin_to_hex tx[11]
172
+
173
+ # allows us to force-setting a signature if the transaction is signed already
174
+ _set_signature(recovery_id, r, s)
175
+ else
176
+ raise_error DecoderError, "Cannot decode EIP-1559 payload!"
177
+ end
178
+
179
+ # last but not least, set the type.
180
+ @type = TYPE_1559
181
+
182
+ # recover sender address
183
+ v = Chain.to_v recovery_id, chain_id
184
+ public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v.to_s(16)}", chain_id)
185
+ address = Util.public_key_to_address(public_key).to_s
186
+ @sender = Tx.sanitize_address address
187
+ end
188
+
189
+ # Creates an unsigned copy of a transaction payload.
190
+ #
191
+ # @param tx [Eth::Tx::Eip1559] an EIP-1559 transaction payload.
192
+ # @return [Eth::Tx::Eip1559] an unsigned EIP-1559 transaction payload.
193
+ # @raise [TransactionTypeError] if transaction type does not match.
194
+ def unsigned_copy(tx)
195
+
196
+ # not checking transaction validity unless it's of a different class
197
+ raise TransactionTypeError, "Cannot copy transaction of different payload type!" unless tx.instance_of? Tx::Eip1559
198
+
199
+ # populate class attributes
200
+ @signer_nonce = tx.signer_nonce
201
+ @max_priority_fee_per_gas = tx.max_priority_fee_per_gas
202
+ @max_fee_per_gas = tx.max_fee_per_gas
203
+ @gas_limit = tx.gas_limit
204
+ @destination = tx.destination
205
+ @amount = tx.amount
206
+ @payload = tx.payload
207
+ @access_list = tx.access_list
208
+ @chain_id = tx.chain_id
209
+
210
+ # force-set signature to unsigned
211
+ _set_signature(nil, 0, 0)
212
+
213
+ # keep the 'from' field blank
214
+ @sender = Tx.sanitize_address nil
215
+
216
+ # last but not least, set the type.
217
+ @type = TYPE_1559
218
+ end
219
+
220
+ # Sign the transaction with a given key.
221
+ #
222
+ # @param key [Eth::Key] the key-pair to use for signing.
223
+ # @return [String] a transaction hash.
224
+ # @raise [SignatureError] if transaction is already signed.
225
+ # @raise [SignatureError] if sender address does not match signing key.
226
+ def sign(key)
227
+ if Tx.is_signed? self
228
+ raise Signature::SignatureError, "Transaction is already signed!"
229
+ end
230
+
231
+ # ensure the sender address matches the given key
232
+ unless @sender.nil? or sender.empty?
233
+ signer_address = Tx.sanitize_address key.address.to_s
234
+ from_address = Tx.sanitize_address @sender
235
+ raise Signature::SignatureError, "Signer does not match sender" unless signer_address == from_address
236
+ end
237
+
238
+ # sign a keccak hash of the unsigned, encoded transaction
239
+ signature = key.sign(unsigned_hash, @chain_id)
240
+ r, s, v = Signature.dissect signature
241
+ recovery_id = Chain.to_recovery_id v.to_i(16), @chain_id
242
+ @signature_y_parity = recovery_id
243
+ @signature_r = r
244
+ @signature_s = s
245
+ return hash
246
+ end
247
+
248
+ # Encodes a raw transaction object, wraps it in an EIP-2718 envelope
249
+ # with an EIP-1559 type prefix.
250
+ #
251
+ # @return [String] a raw, RLP-encoded EIP-1559 type transaction object.
252
+ # @raise [SignatureError] if the transaction is not yet signed.
253
+ def encoded
254
+ unless Tx.is_signed? self
255
+ raise Signature::SignatureError, "Transaction is not signed!"
256
+ end
257
+ tx_data = []
258
+ tx_data.push Util.serialize_int_to_big_endian @chain_id
259
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
260
+ tx_data.push Util.serialize_int_to_big_endian @max_priority_fee_per_gas
261
+ tx_data.push Util.serialize_int_to_big_endian @max_fee_per_gas
262
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
263
+ tx_data.push Util.hex_to_bin @destination
264
+ tx_data.push Util.serialize_int_to_big_endian @amount
265
+ tx_data.push @payload
266
+ tx_data.push @access_list
267
+ tx_data.push Util.serialize_int_to_big_endian @signature_y_parity
268
+ tx_data.push Util.hex_to_bin @signature_r
269
+ tx_data.push Util.hex_to_bin @signature_s
270
+ tx_encoded = RLP.encode tx_data
271
+
272
+ # create an EIP-2718 envelope with EIP-1559 type payload
273
+ tx_type = Util.serialize_int_to_big_endian @type
274
+ return "#{tx_type}#{tx_encoded}"
275
+ end
276
+
277
+ # Gets the encoded, enveloped, raw transaction hex.
278
+ #
279
+ # @return [String] the raw transaction hex.
280
+ def hex
281
+ Util.bin_to_hex encoded
282
+ end
283
+
284
+ # Gets the transaction hash.
285
+ #
286
+ # @return [String] the transaction hash.
287
+ def hash
288
+ Util.bin_to_hex Util.keccak256 encoded
289
+ end
290
+
291
+ # Encodes the unsigned transaction payload in an EIP-1559 envelope,
292
+ # required for signing.
293
+ #
294
+ # @return [String] an RLP-encoded, unsigned, enveloped EIP-1559 transaction.
295
+ def unsigned_encoded
296
+ tx_data = []
297
+ tx_data.push Util.serialize_int_to_big_endian @chain_id
298
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
299
+ tx_data.push Util.serialize_int_to_big_endian @max_priority_fee_per_gas
300
+ tx_data.push Util.serialize_int_to_big_endian @max_fee_per_gas
301
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
302
+ tx_data.push Util.hex_to_bin @destination
303
+ tx_data.push Util.serialize_int_to_big_endian @amount
304
+ tx_data.push @payload
305
+ tx_data.push @access_list
306
+ tx_encoded = RLP.encode tx_data
307
+
308
+ # create an EIP-2718 envelope with EIP-1559 type payload (unsigned)
309
+ tx_type = Util.serialize_int_to_big_endian @type
310
+ return "#{tx_type}#{tx_encoded}"
311
+ end
312
+
313
+ # Gets the sign-hash required to sign a raw transaction.
314
+ #
315
+ # @return [String] a Keccak-256 hash of an unsigned transaction.
316
+ def unsigned_hash
317
+ Util.keccak256 unsigned_encoded
318
+ end
319
+
320
+ private
321
+
322
+ def _set_signature(recovery_id, r, s)
323
+ @signature_y_parity = recovery_id
324
+ @signature_r = r
325
+ @signature_s = s
326
+ end
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,321 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Provides the `Eth` module.
16
+ module Eth
17
+
18
+ # Provides the `Tx` module supporting various transaction types.
19
+ module Tx
20
+
21
+ # Provides legacy support for transactions on blockchains that do not
22
+ # implement EIP-1559 but still want to utilize EIP-2718 envelopes.
23
+ class Eip2930
24
+
25
+ # The EIP-155 Chain ID.
26
+ attr_reader :chain_id
27
+
28
+ # The transaction nonce provided by the signer.
29
+ attr_reader :signer_nonce
30
+
31
+ # The gas price for the transaction in Wei.
32
+ attr_reader :gas_price
33
+
34
+ # The gas limit for the transaction.
35
+ attr_reader :gas_limit
36
+
37
+ # The recipient address.
38
+ attr_reader :destination
39
+
40
+ # The transaction amount in Wei.
41
+ attr_reader :amount
42
+
43
+ # The transaction data payload.
44
+ attr_reader :payload
45
+
46
+ # An optional EIP-2930 access list.
47
+ attr_reader :access_list
48
+
49
+ # The signature's y-parity byte (not v).
50
+ attr_reader :signature_y_parity
51
+
52
+ # The signature `r` value.
53
+ attr_reader :signature_r
54
+
55
+ # The signature `s` value.
56
+ attr_reader :signature_s
57
+
58
+ # The sender address.
59
+ attr_reader :sender
60
+
61
+ # The transaction type.
62
+ attr_reader :type
63
+
64
+ # Create a legacy type-1 (EIP-2930) transaction payload object that
65
+ # can be prepared for envelope, signature and broadcast. Should not
66
+ # be used unless there is no EIP-1559 support.
67
+ #
68
+ #
69
+ # @param params [Hash] all necessary transaction fields.
70
+ # @option params [Integer] :nonce the signer nonce.
71
+ # @option params [Integer] :gas_price the gas price.
72
+ # @option params [Integer] :gas_limit the gas limit.
73
+ # @option params [Eth::Address] :from the sender address.
74
+ # @option params [Eth::Address] :to the reciever address.
75
+ # @option params [Integer] :value the transaction value.
76
+ # @option params [String] :data the transaction data payload.
77
+ # @option params [Array] :access_list an optional access list.
78
+ def initialize(params)
79
+ fields = { recovery_id: nil, r: 0, s: 0 }.merge params
80
+
81
+ # populate optional fields with serializable empty values
82
+ fields[:chain_id] = Tx.sanitize_chain fields[:chain_id]
83
+ fields[:from] = Tx.sanitize_address fields[:from]
84
+ fields[:to] = Tx.sanitize_address fields[:to]
85
+ fields[:value] = Tx.sanitize_amount fields[:value]
86
+ fields[:data] = Tx.sanitize_data fields[:data]
87
+
88
+ # ensure sane values for all mandatory fields
89
+ fields = Tx.validate_legacy_params fields
90
+ fields[:access_list] = Tx.sanitize_list fields[:access_list]
91
+
92
+ # ensure gas limit is not too low
93
+ minimum_cost = Tx.estimate_intrinsic_gas fields[:data], fields[:access_list]
94
+ raise ParameterError, "Transaction gas limit is too low, try #{minimum_cost}!" if fields[:gas_limit].to_i < minimum_cost
95
+
96
+ # populate class attributes
97
+ @signer_nonce = fields[:nonce].to_i
98
+ @gas_price = fields[:gas_price].to_i
99
+ @gas_limit = fields[:gas_limit].to_i
100
+ @sender = fields[:from].to_s
101
+ @destination = fields[:to].to_s
102
+ @amount = fields[:value].to_i
103
+ @payload = fields[:data]
104
+ @access_list = fields[:access_list]
105
+
106
+ # the signature v is set to the chain id for unsigned transactions
107
+ @signature_y_parity = fields[:recovery_id]
108
+ @chain_id = fields[:chain_id]
109
+
110
+ # the signature fields are empty for unsigned transactions.
111
+ @signature_r = fields[:r]
112
+ @signature_s = fields[:s]
113
+
114
+ # last but not least, set the type.
115
+ @type = TYPE_2930
116
+ end
117
+
118
+ # Overloads the constructor for decoding raw transactions and creating unsigned copies.
119
+ konstructor :decode, :unsigned_copy
120
+
121
+ # Decodes a raw transaction hex into an Eth::Tx::Eip2930
122
+ # transaction object.
123
+ #
124
+ # @param hex [String] the raw transaction hex-string.
125
+ # @return [Eth::Tx::Eip2930] transaction payload.
126
+ # @raise [TransactionTypeError] if transaction type is invalid.
127
+ # @raise [ParameterError] if transaction is missing fields.
128
+ # @raise [DecoderError] if transaction decoding fails.
129
+ def decode(hex)
130
+ hex = Util.remove_hex_prefix hex
131
+ type = hex[0, 2]
132
+ raise TransactionTypeError, "Invalid transaction type #{type}!" if type.to_i(16) != TYPE_2930
133
+
134
+ bin = Util.hex_to_bin hex[2..]
135
+ tx = RLP.decode(bin)
136
+
137
+ # decoded transactions always have 8 + 3 fields, even if they are empty or zero
138
+ raise ParameterError, "Transaction missing fields!" if tx.size < 8
139
+
140
+ # populate the 8 payload fields
141
+ chain_id = Util.deserialize_big_endian_to_int tx[0]
142
+ nonce = Util.deserialize_big_endian_to_int tx[1]
143
+ gas_price = Util.deserialize_big_endian_to_int tx[2]
144
+ gas_limit = Util.deserialize_big_endian_to_int tx[3]
145
+ to = Util.bin_to_hex tx[4]
146
+ value = Util.deserialize_big_endian_to_int tx[5]
147
+ data = tx[6]
148
+ access_list = tx[7]
149
+
150
+ # populate class attributes
151
+ @chain_id = chain_id.to_i
152
+ @signer_nonce = nonce.to_i
153
+ @gas_price = gas_price.to_i
154
+ @gas_limit = gas_limit.to_i
155
+ @destination = to.to_s
156
+ @amount = value.to_i
157
+ @payload = data
158
+ @access_list = access_list
159
+
160
+ # populate the 3 signature fields
161
+ if tx.size == 8
162
+ _set_signature(nil, 0, 0)
163
+ elsif tx.size == 11
164
+ recovery_id = Util.bin_to_hex(tx[8]).to_i(16)
165
+ r = Util.bin_to_hex tx[9]
166
+ s = Util.bin_to_hex tx[10]
167
+
168
+ # allows us to force-setting a signature if the transaction is signed already
169
+ _set_signature(recovery_id, r, s)
170
+ else
171
+ raise_error DecoderError, "Cannot decode EIP-2930 payload!"
172
+ end
173
+
174
+ # last but not least, set the type.
175
+ @type = TYPE_2930
176
+
177
+ # recover sender address
178
+ v = Chain.to_v recovery_id, chain_id
179
+ public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v.to_s(16)}", chain_id)
180
+ address = Util.public_key_to_address(public_key).to_s
181
+ @sender = Tx.sanitize_address address
182
+ end
183
+
184
+ # Creates an unsigned copy of a transaction payload.
185
+ #
186
+ # @param tx [Eth::Tx::Eip2930] an EIP-2930 transaction payload.
187
+ # @return [Eth::Tx::Eip2930] an unsigned EIP-2930 transaction payload.
188
+ # @raise [TransactionTypeError] if transaction type does not match.
189
+ def unsigned_copy(tx)
190
+
191
+ # not checking transaction validity unless it's of a different class
192
+ raise TransactionTypeError, "Cannot copy transaction of different payload type!" unless tx.instance_of? Tx::Eip2930
193
+
194
+ # populate class attributes
195
+ @signer_nonce = tx.signer_nonce
196
+ @gas_price = tx.gas_price
197
+ @gas_limit = tx.gas_limit
198
+ @destination = tx.destination
199
+ @amount = tx.amount
200
+ @payload = tx.payload
201
+ @access_list = tx.access_list
202
+ @chain_id = tx.chain_id
203
+
204
+ # force-set signature to unsigned
205
+ _set_signature(nil, 0, 0)
206
+
207
+ # keep the 'from' field blank
208
+ @sender = Tx.sanitize_address nil
209
+
210
+ # last but not least, set the type.
211
+ @type = TYPE_2930
212
+ end
213
+
214
+ # Sign the transaction with a given key.
215
+ #
216
+ # @param key [Eth::Key] the key-pair to use for signing.
217
+ # @return [String] a transaction hash.
218
+ # @raise [SignatureError] if transaction is already signed.
219
+ # @raise [SignatureError] if sender address does not match signing key.
220
+ def sign(key)
221
+ if Tx.is_signed? self
222
+ raise Signature::SignatureError, "Transaction is already signed!"
223
+ end
224
+
225
+ # ensure the sender address matches the given key
226
+ unless @sender.nil? or sender.empty?
227
+ signer_address = Tx.sanitize_address key.address.to_s
228
+ from_address = Tx.sanitize_address @sender
229
+ raise Signature::SignatureError, "Signer does not match sender" unless signer_address == from_address
230
+ end
231
+
232
+ # sign a keccak hash of the unsigned, encoded transaction
233
+ signature = key.sign(unsigned_hash, @chain_id)
234
+ r, s, v = Signature.dissect signature
235
+ recovery_id = Chain.to_recovery_id v.to_i(16), @chain_id
236
+ @signature_y_parity = recovery_id
237
+ @signature_r = r
238
+ @signature_s = s
239
+ return hash
240
+ end
241
+
242
+ # Encodes a raw transaction object, wraps it in an EIP-2718 envelope
243
+ # with an EIP-2930 type prefix.
244
+ #
245
+ # @return [String] a raw, RLP-encoded EIP-2930 type transaction object.
246
+ # @raise [SignatureError] if the transaction is not yet signed.
247
+ def encoded
248
+ unless Tx.is_signed? self
249
+ raise Signature::SignatureError, "Transaction is not signed!"
250
+ end
251
+ tx_data = []
252
+ tx_data.push Util.serialize_int_to_big_endian @chain_id
253
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
254
+ tx_data.push Util.serialize_int_to_big_endian @gas_price
255
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
256
+ tx_data.push Util.hex_to_bin @destination
257
+ tx_data.push Util.serialize_int_to_big_endian @amount
258
+ tx_data.push @payload
259
+ tx_data.push @access_list
260
+ tx_data.push Util.serialize_int_to_big_endian @signature_y_parity
261
+ tx_data.push Util.hex_to_bin @signature_r
262
+ tx_data.push Util.hex_to_bin @signature_s
263
+ tx_encoded = RLP.encode tx_data
264
+
265
+ # create an EIP-2718 envelope with EIP-2930 type payload
266
+ tx_type = Util.serialize_int_to_big_endian @type
267
+ return "#{tx_type}#{tx_encoded}"
268
+ end
269
+
270
+ # Gets the encoded, enveloped, raw transaction hex.
271
+ #
272
+ # @return [String] the raw transaction hex.
273
+ def hex
274
+ Util.bin_to_hex encoded
275
+ end
276
+
277
+ # Gets the transaction hash.
278
+ #
279
+ # @return [String] the transaction hash.
280
+ def hash
281
+ Util.bin_to_hex Util.keccak256 encoded
282
+ end
283
+
284
+ # Encodes the unsigned transaction payload in an EIP-2930 envelope,
285
+ # required for signing.
286
+ #
287
+ # @return [String] an RLP-encoded, unsigned, enveloped EIP-2930 transaction.
288
+ def unsigned_encoded
289
+ tx_data = []
290
+ tx_data.push Util.serialize_int_to_big_endian @chain_id
291
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
292
+ tx_data.push Util.serialize_int_to_big_endian @gas_price
293
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
294
+ tx_data.push Util.hex_to_bin @destination
295
+ tx_data.push Util.serialize_int_to_big_endian @amount
296
+ tx_data.push @payload
297
+ tx_data.push @access_list
298
+ tx_encoded = RLP.encode tx_data
299
+
300
+ # create an EIP-2718 envelope with EIP-2930 type payload (unsigned)
301
+ tx_type = Util.serialize_int_to_big_endian @type
302
+ return "#{tx_type}#{tx_encoded}"
303
+ end
304
+
305
+ # Gets the sign-hash required to sign a raw transaction.
306
+ #
307
+ # @return [String] a Keccak-256 hash of an unsigned transaction.
308
+ def unsigned_hash
309
+ Util.keccak256 unsigned_encoded
310
+ end
311
+
312
+ private
313
+
314
+ def _set_signature(recovery_id, r, s)
315
+ @signature_y_parity = recovery_id
316
+ @signature_r = r
317
+ @signature_s = s
318
+ end
319
+ end
320
+ end
321
+ end