eth 0.4.12 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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