Class: BlockGiven::SignedTransaction
- Inherits:
-
Object
- Object
- BlockGiven::SignedTransaction
- Defined in:
- lib/block_given/signed_transaction.rb
Overview
A signed transaction that has not been broadcast yet. Its hash is derived
from the signed bytes, so it is known before any network call: persist
hash and nonce, then broadcast. Whatever happens to the RPC call, the
transaction can be found again with client.transaction(hash).
signed = registry.prepare_write(:record, movement_id, tx: { nonce: call.nonce })
call.update!(tx_hash: signed.hash, nonce: signed.nonce, status: :submitted)
signed.broadcast # => BlockGiven::Transaction
signed.replacement.broadcast # same nonce, fees bumped by 12.5%
hash is the transaction hash (a String, like Transaction#hash), so instances
are not usable as Hash keys; compare them with ==.
Constant Summary collapse
- MIN_REPLACEMENT_MULTIPLIER =
Nodes reject a same-nonce replacement whose fees are not at least 10% higher.
1.1- DEFAULT_REPLACEMENT_MULTIPLIER =
1.125
Instance Attribute Summary collapse
-
#hash ⇒ Object
readonly
Returns the value of attribute hash.
-
#interface ⇒ Object
readonly
Returns the value of attribute interface.
-
#params ⇒ Object
readonly
Returns the value of attribute params.
-
#raw ⇒ Object
readonly
Returns the value of attribute raw.
-
#wallet ⇒ Object
readonly
Returns the value of attribute wallet.
Class Method Summary collapse
-
.from_raw(raw, wallet:, interface: nil) ⇒ Object
Rebuilds a SignedTransaction from persisted raw bytes (after a restart, typically), so it can be broadcast again or replaced.
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#broadcast ⇒ Object
(also: #submit)
eth_sendRawTransaction.
- #chain_id ⇒ Object
- #client ⇒ Object
- #data ⇒ Object
- #from ⇒ Object
- #gas ⇒ Object
- #gas_price ⇒ Object
-
#initialize(raw:, params:, wallet:, interface: nil) ⇒ SignedTransaction
constructor
interface: an Abi::Interface used to name custom errors when the node rejects the broadcast with revert data (set by Contract#prepare_write).
- #inspect ⇒ Object
- #legacy? ⇒ Boolean
- #max_fee_per_gas ⇒ Object
- #max_priority_fee_per_gas ⇒ Object
- #nonce ⇒ Object
-
#replacement(fee_multiplier: DEFAULT_REPLACEMENT_MULTIPLIER) ⇒ Object
Re-signs the same payload with the same nonce and higher fees, to replace a transaction stuck in the mempool.
- #to ⇒ Object
- #to_h ⇒ Object
- #to_s ⇒ Object
-
#transaction ⇒ Object
The Transaction handle for this hash, without broadcasting (status, receipt...).
- #value ⇒ Object
- #with_interface(interface) ⇒ Object
Constructor Details
#initialize(raw:, params:, wallet:, interface: nil) ⇒ SignedTransaction
interface: an Abi::Interface used to name custom errors when the node rejects the broadcast with revert data (set by Contract#prepare_write).
61 62 63 64 65 66 67 |
# File 'lib/block_given/signed_transaction.rb', line 61 def initialize(raw:, params:, wallet:, interface: nil) @raw = Utils.prefix_hex(raw) @params = params.freeze @wallet = wallet @interface = interface @hash = Utils.keccak256(@raw) end |
Instance Attribute Details
#hash ⇒ Object (readonly)
Returns the value of attribute hash.
57 58 59 |
# File 'lib/block_given/signed_transaction.rb', line 57 def hash @hash end |
#interface ⇒ Object (readonly)
Returns the value of attribute interface.
57 58 59 |
# File 'lib/block_given/signed_transaction.rb', line 57 def interface @interface end |
#params ⇒ Object (readonly)
Returns the value of attribute params.
57 58 59 |
# File 'lib/block_given/signed_transaction.rb', line 57 def params @params end |
#raw ⇒ Object (readonly)
Returns the value of attribute raw.
57 58 59 |
# File 'lib/block_given/signed_transaction.rb', line 57 def raw @raw end |
#wallet ⇒ Object (readonly)
Returns the value of attribute wallet.
57 58 59 |
# File 'lib/block_given/signed_transaction.rb', line 57 def wallet @wallet end |
Class Method Details
.from_raw(raw, wallet:, interface: nil) ⇒ Object
Rebuilds a SignedTransaction from persisted raw bytes (after a restart, typically), so it
can be broadcast again or replaced. The bytes must have been signed by wallet.
24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/block_given/signed_transaction.rb', line 24 def self.from_raw(raw, wallet:, interface: nil) decoded = begin Eth::Tx.decode(Utils.prefix_hex(raw)) rescue StandardError => e raise InvalidArgumentError, "cannot decode signed transaction: #{e.}" end sender = Utils.checksum_address(Utils.prefix_hex(decoded.sender)) # eth returns unprefixed hex unless Utils.same_address?(sender, wallet.address) raise InvalidArgumentError, "signed transaction was sent by #{sender}, not by wallet #{wallet.address}" end new(raw: raw, params: params_from(decoded, sender), wallet: wallet, interface: interface) end |
Instance Method Details
#==(other) ⇒ Object
119 |
# File 'lib/block_given/signed_transaction.rb', line 119 def ==(other) = other.is_a?(SignedTransaction) && other.raw == raw |
#broadcast ⇒ Object Also known as: submit
eth_sendRawTransaction. Returns a Transaction carrying the locally computed hash.
86 87 88 89 90 91 92 93 94 |
# File 'lib/block_given/signed_transaction.rb', line 86 def broadcast sent = with_decoded_errors { client.send_raw_transaction(raw) } unless sent.hash.to_s.casecmp?(hash) client.logger.warn do "[block_given] node returned #{sent.hash} for signed transaction #{hash}" end end transaction end |
#chain_id ⇒ Object
79 |
# File 'lib/block_given/signed_transaction.rb', line 79 def chain_id = params[:chain_id] |
#client ⇒ Object
71 |
# File 'lib/block_given/signed_transaction.rb', line 71 def client = wallet.client |
#data ⇒ Object
76 |
# File 'lib/block_given/signed_transaction.rb', line 76 def data = params[:data] |
#from ⇒ Object
73 |
# File 'lib/block_given/signed_transaction.rb', line 73 def from = params[:from] |
#gas ⇒ Object
78 |
# File 'lib/block_given/signed_transaction.rb', line 78 def gas = params[:gas] |
#gas_price ⇒ Object
82 |
# File 'lib/block_given/signed_transaction.rb', line 82 def gas_price = params[:gas_price] |
#inspect ⇒ Object
120 |
# File 'lib/block_given/signed_transaction.rb', line 120 def inspect = "#<BlockGiven::SignedTransaction #{hash} nonce=#{nonce} to=#{to}>" |
#legacy? ⇒ Boolean
83 |
# File 'lib/block_given/signed_transaction.rb', line 83 def legacy? = !gas_price.nil? |
#max_fee_per_gas ⇒ Object
80 |
# File 'lib/block_given/signed_transaction.rb', line 80 def max_fee_per_gas = params[:max_fee_per_gas] |
#max_priority_fee_per_gas ⇒ Object
81 |
# File 'lib/block_given/signed_transaction.rb', line 81 def max_priority_fee_per_gas = params[:max_priority_fee_per_gas] |
#nonce ⇒ Object
77 |
# File 'lib/block_given/signed_transaction.rb', line 77 def nonce = params[:nonce] |
#replacement(fee_multiplier: DEFAULT_REPLACEMENT_MULTIPLIER) ⇒ Object
Re-signs the same payload with the same nonce and higher fees, to replace a transaction stuck in the mempool. Fees are the max of (current fees x fee_multiplier) and a fresh estimate from the node, so the replacement also catches up with the market. Both transactions share a nonce: only one can be mined.
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/block_given/signed_transaction.rb', line 104 def replacement(fee_multiplier: DEFAULT_REPLACEMENT_MULTIPLIER) if fee_multiplier < MIN_REPLACEMENT_MULTIPLIER raise InvalidArgumentError, "fee_multiplier must be >= #{MIN_REPLACEMENT_MULTIPLIER} (got #{fee_multiplier})" end raise InvalidArgumentError, "cannot replace a transaction without fee params (use .from_raw)" unless fees? wallet.signed_transaction( to: to, value: value, data: data, gas: gas, nonce: nonce, chain_id: chain_id, access_list: params[:access_list], **with_decoded_errors { bumped_fees(fee_multiplier) } ).with_interface(interface) end |
#to ⇒ Object
74 |
# File 'lib/block_given/signed_transaction.rb', line 74 def to = params[:to] |
#to_h ⇒ Object
116 |
# File 'lib/block_given/signed_transaction.rb', line 116 def to_h = params.merge(hash: hash, raw: raw) |
#to_s ⇒ Object
118 |
# File 'lib/block_given/signed_transaction.rb', line 118 def to_s = hash |
#transaction ⇒ Object
The Transaction handle for this hash, without broadcasting (status, receipt...).
98 |
# File 'lib/block_given/signed_transaction.rb', line 98 def transaction = client.transaction(hash) |
#value ⇒ Object
75 |
# File 'lib/block_given/signed_transaction.rb', line 75 def value = params[:value] |
#with_interface(interface) ⇒ Object
69 |
# File 'lib/block_given/signed_transaction.rb', line 69 def with_interface(interface) = self.class.new(raw: raw, params: params, wallet: wallet, interface: interface) |