klay 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,296 @@
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 Klay
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, EIP-2718, or EIP-2930.
23
+ class Legacy
24
+
25
+ # The transaction nonce provided by the signer.
26
+ attr_reader :signer_nonce
27
+
28
+ # The gas price for the transaction in Wei.
29
+ attr_reader :gas_price
30
+
31
+ # The gas limit for the transaction.
32
+ attr_reader :gas_limit
33
+
34
+ # The recipient address.
35
+ attr_reader :destination
36
+
37
+ # The transaction amount in Wei.
38
+ attr_reader :amount
39
+
40
+ # The transaction data payload.
41
+ attr_reader :payload
42
+
43
+ # The signature `v` byte.
44
+ attr_reader :signature_v
45
+
46
+ # The signature `r` value.
47
+ attr_reader :signature_r
48
+
49
+ # The signature `s` value.
50
+ attr_reader :signature_s
51
+
52
+ # The EIP-155 chain ID field.
53
+ # Ref: https://eips.ethereum.org/EIPS/eip-155
54
+ attr_reader :chain_id
55
+
56
+ # The sender address.
57
+ attr_reader :sender
58
+
59
+ # The transaction type.
60
+ attr_reader :type
61
+
62
+ # Create a legacy transaction object that can be prepared for
63
+ # signature and broadcast. Should not be used unless there is
64
+ # no EIP-1559 support.
65
+ #
66
+ # @param params [Hash] all necessary transaction fields.
67
+ # @option params [Integer] :nonce the signer nonce.
68
+ # @option params [Integer] :gas_price the gas price.
69
+ # @option params [Integer] :gas_limit the gas limit.
70
+ # @option params [Eth::Address] :from the sender address.
71
+ # @option params [Eth::Address] :to the reciever address.
72
+ # @option params [Integer] :value the transaction value.
73
+ # @option params [String] :data the transaction data payload.
74
+ # @param chain_id [Integer] the EIP-155 Chain ID.
75
+ # @raise [ParameterError] if gas limit is too low.
76
+ def initialize(params, chain_id = Chain::CYPRESS)
77
+ fields = { v: chain_id, r: 0, s: 0 }.merge params
78
+
79
+ # populate optional fields with serializable empty values
80
+ fields[:value] = Tx.sanitize_amount fields[:value]
81
+ fields[:from] = Tx.sanitize_address fields[:from]
82
+ fields[:to] = Tx.sanitize_address fields[:to]
83
+ fields[:data] = Tx.sanitize_data fields[:data]
84
+
85
+ # ensure sane values for all mandatory fields
86
+ fields = Tx.validate_legacy_params fields
87
+
88
+ # ensure gas limit is not too low
89
+ minimum_cost = Tx.estimate_intrinsic_gas fields[:data]
90
+ raise ParameterError, "Transaction gas limit is too low, try #{minimum_cost}!" if fields[:gas_limit].to_i < minimum_cost
91
+
92
+ # populate class attributes
93
+ @signer_nonce = fields[:nonce].to_i
94
+ @gas_price = fields[:gas_price].to_i
95
+ @gas_limit = fields[:gas_limit].to_i
96
+ @sender = fields[:from].to_s
97
+ @destination = fields[:to].to_s
98
+ @amount = fields[:value].to_i
99
+ @payload = fields[:data]
100
+
101
+ # the signature v is set to the chain id for unsigned transactions
102
+ @signature_v = fields[:v]
103
+ @chain_id = chain_id
104
+
105
+ # the signature fields are empty for unsigned transactions.
106
+ @signature_r = fields[:r]
107
+ @signature_s = fields[:s]
108
+
109
+ # last but not least, set the type.
110
+ @type = TYPE_LEGACY
111
+ end
112
+
113
+ # overloads the constructor for decoding raw transactions and creating unsigned copies
114
+ konstructor :decode, :unsigned_copy
115
+
116
+ # Decodes a raw transaction hex into an {Eth::Tx::Legacy}
117
+ # transaction object.
118
+ #
119
+ # @param hex [String] the raw transaction hex-string.
120
+ # @return [Eth::Tx::Legacy] transaction object.
121
+ # @raise [ParameterError] if transaction misses fields.
122
+ def decode(hex)
123
+ bin = Util.hex_to_bin hex
124
+ tx = Rlp.decode bin
125
+
126
+ # decoded transactions always have 9 fields, even if they are empty or zero
127
+ raise ParameterError, "Transaction missing fields!" if tx.size < 9
128
+
129
+ # populate the 9 fields
130
+ nonce = Util.deserialize_big_endian_to_int tx[0]
131
+ gas_price = Util.deserialize_big_endian_to_int tx[1]
132
+ gas_limit = Util.deserialize_big_endian_to_int tx[2]
133
+ to = Util.bin_to_hex tx[3]
134
+ value = Util.deserialize_big_endian_to_int tx[4]
135
+ data = tx[5]
136
+ v = Util.bin_to_hex tx[6]
137
+ r = Util.bin_to_hex tx[7]
138
+ s = Util.bin_to_hex tx[8]
139
+
140
+ # try to recover the chain id from v
141
+ chain_id = Chain.to_chain_id Util.deserialize_big_endian_to_int tx[6]
142
+
143
+ # populate class attributes
144
+ @signer_nonce = nonce.to_i
145
+ @gas_price = gas_price.to_i
146
+ @gas_limit = gas_limit.to_i
147
+ @destination = to.to_s
148
+ @amount = value.to_i
149
+ @payload = data
150
+ @chain_id = chain_id
151
+
152
+ # allows us to force-setting a signature if the transaction is signed already
153
+ _set_signature(v, r, s)
154
+
155
+ unless chain_id.nil?
156
+
157
+ # recover sender address
158
+ public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v}", chain_id)
159
+ address = Util.public_key_to_address(public_key).to_s
160
+ @sender = Tx.sanitize_address address
161
+ else
162
+
163
+ # keep the 'from' field blank
164
+ @sender = Tx.sanitize_address nil
165
+ end
166
+
167
+ # last but not least, set the type.
168
+ @type = TYPE_LEGACY
169
+ end
170
+
171
+ # Creates an unsigned copy of a transaction.
172
+ #
173
+ # @param tx [Eth::Tx::Legacy] an legacy transaction object.
174
+ # @return [Eth::Tx::Legacy] an unsigned transaction object.
175
+ # @raise [TransactionTypeError] if transaction type does not match.
176
+ def unsigned_copy(tx)
177
+
178
+ # not checking transaction validity unless it's of a different class
179
+ raise TransactionTypeError, "Cannot copy transaction of different type!" unless tx.instance_of? Tx::Legacy
180
+
181
+ # populate class attributes
182
+ @signer_nonce = tx.signer_nonce
183
+ @gas_price = tx.gas_price
184
+ @gas_limit = tx.gas_limit
185
+ @destination = tx.destination
186
+ @amount = tx.amount
187
+ @payload = tx.payload
188
+ @chain_id = tx.chain_id
189
+
190
+ # force-set signature to unsigned
191
+ _set_signature(tx.chain_id, 0, 0)
192
+
193
+ # keep the 'from' field blank
194
+ @sender = Tx.sanitize_address nil
195
+
196
+ # last but not least, set the type.
197
+ @type = TYPE_LEGACY
198
+ end
199
+
200
+ # Sign the transaction with a given key.
201
+ #
202
+ # @param key [Eth::Key] the key-pair to use for signing.
203
+ # @return [String] a transaction hash.
204
+ # @raise [Signature::SignatureError] if transaction is already signed.
205
+ # @raise [Signature::SignatureError] if sender address does not match signing key.
206
+ def sign(key)
207
+ if Tx.is_signed? self
208
+ raise Signature::SignatureError, "Transaction is already signed!"
209
+ end
210
+
211
+ # ensure the sender address matches the given key
212
+ unless @sender.nil? or sender.empty?
213
+ signer_address = Tx.sanitize_address key.address.to_s
214
+ from_address = Tx.sanitize_address @sender
215
+ raise Signature::SignatureError, "Signer does not match sender" unless signer_address == from_address
216
+ end
217
+
218
+ # sign a keccak hash of the unsigned, encoded transaction
219
+ signature = key.sign(unsigned_hash, @chain_id)
220
+ r, s, v = Signature.dissect signature
221
+ @signature_v = v
222
+ @signature_r = r
223
+ @signature_s = s
224
+ return hash
225
+ end
226
+
227
+ # Encodes a raw transaction object.
228
+ #
229
+ # @return [String] a raw, RLP-encoded legacy transaction.
230
+ # @raise [Signature::SignatureError] if the transaction is not yet signed.
231
+ def encoded
232
+ unless Tx.is_signed? self
233
+ raise Signature::SignatureError, "Transaction is not signed!"
234
+ end
235
+ tx_data = []
236
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
237
+ tx_data.push Util.serialize_int_to_big_endian @gas_price
238
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
239
+ tx_data.push Util.hex_to_bin @destination
240
+ tx_data.push Util.serialize_int_to_big_endian @amount
241
+ tx_data.push Rlp::Sedes.binary.serialize @payload
242
+ tx_data.push Util.serialize_int_to_big_endian @signature_v
243
+ tx_data.push Util.serialize_int_to_big_endian @signature_r
244
+ tx_data.push Util.serialize_int_to_big_endian @signature_s
245
+ Rlp.encode tx_data
246
+ end
247
+
248
+ # Gets the encoded, raw transaction hex.
249
+ #
250
+ # @return [String] the raw transaction hex.
251
+ def hex
252
+ Util.bin_to_hex encoded
253
+ end
254
+
255
+ # Gets the transaction hash.
256
+ #
257
+ # @return [String] the transaction hash.
258
+ def hash
259
+ Util.bin_to_hex Util.keccak256 encoded
260
+ end
261
+
262
+ # Encodes the unsigned transaction object, required for signing.
263
+ #
264
+ # @return [String] an RLP-encoded, unsigned transaction.
265
+ def unsigned_encoded
266
+ tx_data = []
267
+ tx_data.push Util.serialize_int_to_big_endian @signer_nonce
268
+ tx_data.push Util.serialize_int_to_big_endian @gas_price
269
+ tx_data.push Util.serialize_int_to_big_endian @gas_limit
270
+ tx_data.push Util.hex_to_bin @destination
271
+ tx_data.push Util.serialize_int_to_big_endian @amount
272
+ tx_data.push Rlp::Sedes.binary.serialize @payload
273
+ tx_data.push Util.serialize_int_to_big_endian @chain_id
274
+ tx_data.push Util.serialize_int_to_big_endian 0
275
+ tx_data.push Util.serialize_int_to_big_endian 0
276
+ Rlp.encode tx_data
277
+ end
278
+
279
+ # Gets the sign-hash required to sign a raw transaction.
280
+ #
281
+ # @return [String] a Keccak-256 hash of an unsigned transaction.
282
+ def unsigned_hash
283
+ Util.keccak256 unsigned_encoded
284
+ end
285
+
286
+ private
287
+
288
+ # Force-sets an existing signature of a decoded transaction.
289
+ def _set_signature(v, r, s)
290
+ @signature_v = v
291
+ @signature_r = r
292
+ @signature_s = s
293
+ end
294
+ end
295
+ end
296
+ end
data/lib/klay/tx.rb ADDED
@@ -0,0 +1,327 @@
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
+ require "konstructor"
16
+
17
+ require "klay/chain"
18
+ require "klay/tx/eip1559"
19
+ require "klay/tx/eip2930"
20
+ require "klay/tx/legacy"
21
+ require "klay/unit"
22
+
23
+ # Provides the {Eth} module.
24
+ module Klay
25
+
26
+ # Provides the `Tx` module supporting various transaction types.
27
+ module Tx
28
+ extend self
29
+
30
+ # Provides a special transaction error if transaction type is unknown.
31
+ class TransactionTypeError < TypeError; end
32
+
33
+ # Provides an decoder error if transaction cannot be decoded.
34
+ class DecoderError < StandardError; end
35
+
36
+ # Provides a parameter error if parameter types are invalid.
37
+ class ParameterError < TypeError; end
38
+
39
+ # The minimum transaction gas limit required for a value transfer.
40
+ DEFAULT_GAS_LIMIT = 21_000.freeze
41
+
42
+ # The "default" transaction gas price of 20 GWei. Do not use.
43
+ DEFAULT_GAS_PRICE = (20 * Unit::GWEI).freeze
44
+
45
+ # The calldata gas cost of a non-zero byte as per EIP-2028.
46
+ COST_NON_ZERO_BYTE = 16.freeze
47
+
48
+ # The calldata gas cost of a zero byte.
49
+ COST_ZERO_BYTE = 4.freeze
50
+
51
+ # The access list gas cost of a storage key as per EIP-2930.
52
+ COST_STORAGE_KEY = 1_900.freeze
53
+
54
+ # The access list gas cost of an address as per EIP-2930.
55
+ COST_ADDRESS = 2_400.freeze
56
+
57
+ # The maximum transaction gas limit is bound by the block gas limit.
58
+ BLOCK_GAS_LIMIT = 25_000_000.freeze
59
+
60
+ # The legacy transaction type is 0.
61
+ TYPE_LEGACY = 0x00.freeze
62
+
63
+ # The EIP-2930 transaction type is 1.
64
+ TYPE_2930 = 0x01.freeze
65
+
66
+ # The EIP-1559 transaction type is 2.
67
+ TYPE_1559 = 0x02.freeze
68
+
69
+ # The zero byte is 0x00.
70
+ ZERO_BYTE = "\x00".freeze
71
+
72
+ # Creates a new transaction of any type for given parameters and chain ID.
73
+ # Required parameters are (optional in brackets):
74
+ # - EIP-1559: chain_id, nonce, priority_fee, max_gas_fee, gas_limit(, from, to,
75
+ # value, data, access_list)
76
+ # - EIP-2930: chain_id, nonce, gas_price, gas_limit, access_list(, from, to,
77
+ # value, data)
78
+ # - Legacy: nonce, gas_price, gas_lmit(, from, to, value, data)
79
+ #
80
+ # @param params [Hash] all necessary transaction fields.
81
+ # @param chain_id [Integer] the EIP-155 Chain ID (legacy transactions only).
82
+ def new(params, chain_id = Chain::CYPRESS)
83
+
84
+ # if we deal with max gas fee parameter, attempt EIP-1559
85
+ unless params[:max_gas_fee].nil?
86
+ params[:chain_id] = chain_id if params[:chain_id].nil?
87
+ return Tx::Eip1559.new params
88
+ end
89
+
90
+ # if we deal with access list parameter, attempt EIP-2930
91
+ unless params[:access_list].nil?
92
+ params[:chain_id] = chain_id if params[:chain_id].nil?
93
+ return Tx::Eip2930.new params
94
+ end
95
+
96
+ # if nothing else, go with legacy transactions
97
+ chain_id = params[:chain_id] if !params[:chain_id].nil? and params[:chain_id] != chain_id
98
+ return Tx::Legacy.new params, chain_id
99
+ end
100
+
101
+ # Decodes a transaction hex of any known type (2, 1, or legacy).
102
+ #
103
+ # @param hex [String] the raw transaction hex-string.
104
+ # @return [Eth::Tx] transaction payload.
105
+ # @raise [TransactionTypeError] if the transaction type is unknown.
106
+ def decode(hex)
107
+ hex = Util.remove_hex_prefix hex
108
+ type = hex[0, 2].to_i(16)
109
+ case type
110
+ when TYPE_1559
111
+
112
+ # EIP-1559 transaction (type 2)
113
+ return Tx::Eip1559.decode hex
114
+ when TYPE_2930
115
+
116
+ # EIP-2930 transaction (type 1)
117
+ return Tx::Eip2930.decode hex
118
+ else
119
+
120
+ # Legacy transaction if first byte is RLP (>= 192)
121
+ if type >= 0xc0
122
+ return Tx::Legacy.decode hex
123
+ else
124
+ raise TransactionTypeError, "Cannot decode unknown transaction type #{type}!"
125
+ end
126
+ end
127
+ end
128
+
129
+ # Creates an unsigned copy of any transaction object.
130
+ #
131
+ # @param tx [Eth::Tx] any transaction payload.
132
+ # @return [Eth::Tx] an unsigned transaction payload of the same type.
133
+ # @raise [TransactionTypeError] if the transaction type is unknown.
134
+ def unsigned_copy(tx)
135
+ case tx.type
136
+ when TYPE_1559
137
+
138
+ # EIP-1559 transaction (type 2)
139
+ return Tx::Eip1559.unsigned_copy tx
140
+ when TYPE_2930
141
+
142
+ # EIP-2930 transaction (type 1)
143
+ return Tx::Eip2930.unsigned_copy tx
144
+ when TYPE_LEGACY
145
+
146
+ # Legacy transaction ("type 0")
147
+ return Tx::Legacy.unsigned_copy tx
148
+ end
149
+ raise TransactionTypeError, "Cannot copy unknown transaction type #{tx.type}!"
150
+ end
151
+
152
+ # Estimates intrinsic gas for provided call data (EIP-2028) and
153
+ # access lists (EIP-2930).
154
+ #
155
+ # @param data [String] the call data.
156
+ # @param list [Array] the access list.
157
+ # @return [Integer] the estimated intrinsic gas cost.
158
+ def estimate_intrinsic_gas(data = "", list = [])
159
+ gas = DEFAULT_GAS_LIMIT
160
+ unless data.nil? or data.empty?
161
+ data = Util.hex_to_bin data if Util.is_hex? data
162
+
163
+ # count zero bytes
164
+ zero = data.count ZERO_BYTE
165
+ gas += zero * COST_ZERO_BYTE
166
+
167
+ # count non-zero bytes
168
+ none = data.size - zero
169
+ gas += none * COST_NON_ZERO_BYTE
170
+ end
171
+ unless list.nil? or list.empty?
172
+ list.each do |entry|
173
+
174
+ # count addresses
175
+ gas += COST_ADDRESS
176
+
177
+ entry.last.each do |key|
178
+
179
+ # count storage keys
180
+ gas += COST_STORAGE_KEY
181
+ end
182
+ end
183
+ end
184
+ return gas
185
+ end
186
+
187
+ # Validates the common type-2 transaction fields such as nonce, priority
188
+ # fee, max gas fee, gas limit, amount, and access list.
189
+ #
190
+ # @param fields [Hash] the transaction fields.
191
+ # @return [Hash] the validated transaction fields.
192
+ # @raise [ParameterError] if nonce is an invalid integer.
193
+ # @raise [ParameterError] if priority fee is invalid.
194
+ # @raise [ParameterError] if max gas fee is invalid.
195
+ # @raise [ParameterError] if gas limit is invalid.
196
+ # @raise [ParameterError] if amount is invalid.
197
+ # @raise [ParameterError] if access list is invalid.
198
+ def validate_params(fields)
199
+ if fields[:nonce].nil? or fields[:nonce] < 0
200
+ raise ParameterError, "Invalid signer nonce #{fields[:nonce]}!"
201
+ end
202
+ if fields[:priority_fee].nil? or fields[:priority_fee] < 0
203
+ raise ParameterError, "Invalid gas priority fee #{fields[:priority_fee]}!"
204
+ end
205
+ if fields[:max_gas_fee].nil? or fields[:max_gas_fee] < 0
206
+ raise ParameterError, "Invalid max gas fee #{fields[:max_gas_fee]}!"
207
+ end
208
+ if fields[:gas_limit].nil? or fields[:gas_limit] < DEFAULT_GAS_LIMIT or fields[:gas_limit] > BLOCK_GAS_LIMIT
209
+ raise ParameterError, "Invalid gas limit #{fields[:gas_limit]}!"
210
+ end
211
+ unless fields[:value] >= 0
212
+ raise ParameterError, "Invalid transaction value #{fields[:value]}!"
213
+ end
214
+ unless fields[:access_list].nil? or fields[:access_list].is_a? Array
215
+ raise ParameterError, "Invalid access list #{fields[:access_list]}!"
216
+ end
217
+ return fields
218
+ end
219
+
220
+ # Validates the common legacy transaction fields such as nonce, gas
221
+ # price, gas limit, amount, and access list.
222
+ #
223
+ # @param fields [Hash] the transaction fields.
224
+ # @return [Hash] the validated transaction fields.
225
+ # @raise [ParameterError] if nonce is an invalid integer.
226
+ # @raise [ParameterError] if gas price is invalid.
227
+ # @raise [ParameterError] if gas limit is invalid.
228
+ # @raise [ParameterError] if amount is invalid.
229
+ # @raise [ParameterError] if access list is invalid.
230
+ def validate_legacy_params(fields)
231
+ if fields[:nonce].nil? or fields[:nonce] < 0
232
+ raise ParameterError, "Invalid signer nonce #{fields[:nonce]}!"
233
+ end
234
+ if fields[:gas_price].nil? or fields[:gas_price] < 0
235
+ raise ParameterError, "Invalid gas price #{fields[:gas_price]}!"
236
+ end
237
+ if fields[:gas_limit].nil? or fields[:gas_limit] < DEFAULT_GAS_LIMIT or fields[:gas_limit] > BLOCK_GAS_LIMIT
238
+ raise ParameterError, "Invalid gas limit #{fields[:gas_limit]}!"
239
+ end
240
+ unless fields[:value] >= 0
241
+ raise ParameterError, "Invalid transaction value #{fields[:value]}!"
242
+ end
243
+ unless fields[:access_list].nil? or fields[:access_list].is_a? Array
244
+ raise ParameterError, "Invalid access list #{fields[:access_list]}!"
245
+ end
246
+ return fields
247
+ end
248
+
249
+ # Populates the transaction chain id field with a serializable default
250
+ # value (1) in case it is undefined.
251
+ #
252
+ # @param id [Integer] the transaction chain id.
253
+ # @return [Integer] the sanitized transaction chain id.
254
+ def sanitize_chain(id)
255
+ id = Chain::CYPRESS if id.nil?
256
+ return id
257
+ end
258
+
259
+ # Populates the transaction destination address with a serializable
260
+ # empty value in case it is undefined; also ensures the address is
261
+ # checksummed but not prefixed for consistency.
262
+ #
263
+ # @param addr [String] the transaction destination address.
264
+ # @return [String] the sanitized transaction destination address.
265
+ def sanitize_address(addr)
266
+ addr = "" if addr.nil?
267
+ if addr.is_a? String and !addr.empty?
268
+ addr = Address.new(addr).to_s
269
+ addr = Util.remove_hex_prefix addr
270
+ end
271
+ return addr
272
+ end
273
+
274
+ # Populates the transaction value field with a serializable empty value
275
+ # in case it is undefined.
276
+ #
277
+ # @param val [Integer] the transaction value.
278
+ # @return [Integer] the sanitized transaction value.
279
+ def sanitize_amount(val)
280
+ val = 0 if val.nil?
281
+ return val
282
+ end
283
+
284
+ # Populates the transaction payload field with a serializable empty value
285
+ # in case it is undefined; also ensures the data is binary not hex.
286
+ #
287
+ # @param data [String] the transaction payload data.
288
+ # @return [String] the sanitized transaction payload data.
289
+ def sanitize_data(data)
290
+ data = "" if data.nil?
291
+
292
+ # ensure payload to be binary if it's hex, otherwise we'll treat it raw
293
+ data = Util.hex_to_bin data if Util.is_hex? data
294
+ return data
295
+ end
296
+
297
+ # Populates the transaction access list field with a serializable empty
298
+ # array in case it is undefined; also ensures the nested data is binary
299
+ # not hex.
300
+ #
301
+ # @param list [Array] the transaction access list.
302
+ # @return [Array] the sanitized transaction access list.
303
+ def sanitize_list(list)
304
+ list = [] if list.nil?
305
+ list.each_with_index do |value, index|
306
+ if value.is_a? Array
307
+
308
+ # recursively check the entire array
309
+ list[index] = sanitize_list value
310
+ elsif Util.is_hex? value
311
+
312
+ # only modify if we find a hex value
313
+ list[index] = Util.hex_to_bin value
314
+ end
315
+ end
316
+ return list
317
+ end
318
+
319
+ # Allows to check wether a transaction is signed already.
320
+ #
321
+ # @return [Bool] true if transaction is already signed.
322
+ def is_signed?(tx)
323
+ !tx.signature_r.nil? and tx.signature_r != 0 and
324
+ !tx.signature_s.nil? and tx.signature_s != 0
325
+ end
326
+ end
327
+ end
data/lib/klay/unit.rb ADDED
@@ -0,0 +1,49 @@
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
+ require "bigdecimal"
16
+
17
+ # Provides the {Eth} module.
18
+ module Klay
19
+
20
+ # Provides constants for common Ethereum units.
21
+ module Unit
22
+ extend self
23
+
24
+ # Ethereum unit 1 wei := 0.000000000000000001 Ether.
25
+ WEI = BigDecimal("1e0").freeze
26
+
27
+ # Ethereum unit 1 babbage := 0.000000000000001 Ether or 1_000 wei.
28
+ BABBAGE = BigDecimal("1e3").freeze
29
+
30
+ # Ethereum unit 1 lovelace := 0.000000000001 Ether or 1_000_000 wei.
31
+ LOVELACE = BigDecimal("1e6").freeze
32
+
33
+ # Ethereum unit 1 shannon := 0.000000001 Ether or 1_000_000_000 wei.
34
+ SHANNON = BigDecimal("1e9").freeze
35
+
36
+ # Ethereum unit 1 szabo := 0.000_001 Ether or 1_000_000_000_000 wei.
37
+ SZABO = BigDecimal("1e12").freeze
38
+
39
+ # Ethereum unit 1 finney := 0.001 Ether or 1_000_000_000_000_000 wei.
40
+ FINNEY = BigDecimal("1e15").freeze
41
+
42
+ # Ethereum unit 1 Ether := 1_000_000_000_000_000_000 wei.
43
+ ETHER = BigDecimal("1e18").freeze
44
+
45
+ # Ethereum unit 1 Gwei := 0.000000001 Ether or 1_000_000_000 wei.
46
+ # Same as shannon, but more commonly used (billion wei).
47
+ GWEI = SHANNON.freeze
48
+ end
49
+ end