Class: XRPL::Transaction
- Inherits:
-
Object
- Object
- XRPL::Transaction
- Defined in:
- lib/xrpl/transaction.rb
Overview
A transaction, built from the field formats in definitions.json.
The subclasses below are not written by hand: one is created for each entry in TRANSACTION_FORMATS when this file loads, so syncing definitions.json updates the models with it and they cannot drift from the ledger.
tx = XRPL::Transaction::Payment.new(
account: wallet.classic_address,
destination: receiver.classic_address,
amount: '1000000'
)
tx.validate!
client.submit_and_wait(tx, wallet: wallet)
Fields are given in snake_case and stored under the ledger's own PascalCase names, so #to_h hands the binary codec exactly what it expects. A plain Hash still works everywhere a transaction is accepted; these classes are an addition, not a replacement.
Defined Under Namespace
Classes: ValidationError
Constant Summary collapse
- REQUIRED =
rippled's SOEStyle: what the format says about a field.
0- OPTIONAL =
1- DEFAULT =
2- DEFINITIONS =
BinaryCodec::Definitions.instance.raw
- COMMON_FORMAT =
Fields every transaction carries, regardless of type.
DEFINITIONS['TRANSACTION_FORMATS'].fetch('common').freeze
- SUPPLIED_LATER =
Required by the format, but supplied by autofill and signing rather than by the caller. Demanding them up front would make #validate! useless at the point where it is actually worth running.
%w[ TransactionType Sequence Fee SigningPubKey TxnSignature LastLedgerSequence ].freeze
- FIELD_TO_ACCESSOR =
DEFINITIONS['FIELDS'].to_h { |name, _| [name, underscore(name)] }.freeze
- ACCESSOR_TO_FIELD =
FIELD_TO_ACCESSOR.invert.freeze
Class Attribute Summary collapse
-
.flags ⇒ Object
readonly
Flag name -> bit, e.g.
-
.format ⇒ Object
readonly
Ledger field name -> optionality, including the common fields.
-
.transaction_type ⇒ Object
readonly
The ledger's name for this transaction type, e.g.
Class Method Summary collapse
-
.define_types! ⇒ Object
Builds one subclass per transaction type, with an accessor for every field the type accepts and a constant for every flag it defines.
-
.for(type) ⇒ Object
The class for a transaction type name, or nil if the ledger has no such type.
-
.from(hash) ⇒ Object
Build the right subclass from a transaction hash, PascalCase or snake.
-
.resolve(name) ⇒ Object
Translates an accessor name to the ledger's field name.
-
.underscore(name) ⇒ Object
Ledger field name -> snake_case accessor, and back.
Instance Method Summary collapse
- #==(other) ⇒ Object (also: #eql?)
-
#[](name) ⇒ Object
Reads a field by accessor name, symbol or ledger name.
-
#[]=(name, value) ⇒ Object
Writes a field, rejecting anything the type does not define.
- #hash ⇒ Object
-
#initialize(fields = {}) ⇒ Transaction
constructor
A new instance of Transaction.
- #inspect ⇒ Object
-
#missing_fields ⇒ Object
Fields the format requires that have not been set, ignoring the ones autofill and signing provide.
-
#to_blob ⇒ Object
The serialised transaction, as hex.
-
#to_h ⇒ Object
(also: #to_hash)
The transaction as the binary codec wants it: ledger field names, with TransactionType filled in.
- #valid? ⇒ Boolean
-
#validate! ⇒ Object
Raises unless every required field is present.
Constructor Details
#initialize(fields = {}) ⇒ Transaction
Returns a new instance of Transaction.
94 95 96 97 |
# File 'lib/xrpl/transaction.rb', line 94 def initialize(fields = {}) @fields = {} fields.each { |name, value| self[name] = value } end |
Class Attribute Details
.flags ⇒ Object (readonly)
Flag name -> bit, e.g. "tfPartialPayment" => 131072.
74 75 76 |
# File 'lib/xrpl/transaction.rb', line 74 def flags @flags end |
.format ⇒ Object (readonly)
Ledger field name -> optionality, including the common fields.
71 72 73 |
# File 'lib/xrpl/transaction.rb', line 71 def format @format end |
.transaction_type ⇒ Object (readonly)
The ledger's name for this transaction type, e.g. "Payment".
68 69 70 |
# File 'lib/xrpl/transaction.rb', line 68 def transaction_type @transaction_type end |
Class Method Details
.define_types! ⇒ Object
Builds one subclass per transaction type, with an accessor for every field the type accepts and a constant for every flag it defines.
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/xrpl/transaction.rb', line 172 def self.define_types! DEFINITIONS['TRANSACTION_FORMATS'].each do |type, own_format| next if type == 'common' format = (COMMON_FORMAT + own_format) .to_h { |field| [field['name'], field['optionality']] } .freeze klass = Class.new(self) klass.instance_variable_set(:@transaction_type, type) klass.instance_variable_set(:@format, format) klass.instance_variable_set(:@flags, (DEFINITIONS['TRANSACTION_FLAGS'][type] || {}).freeze) format.each_key do |field| accessor = FIELD_TO_ACCESSOR[field] or next klass.define_method(accessor) { self[field] } klass.define_method("#{accessor}=") { |value| self[field] = value } end klass.flags.each do |name, bit| klass.const_set(underscore(name).upcase, bit) end const_set(type, klass) end end |
.for(type) ⇒ Object
The class for a transaction type name, or nil if the ledger has no such type.
79 80 81 |
# File 'lib/xrpl/transaction.rb', line 79 def self.for(type) const_get(type) if const_defined?(type, false) end |
.from(hash) ⇒ Object
Build the right subclass from a transaction hash, PascalCase or snake.
84 85 86 87 88 89 90 91 92 |
# File 'lib/xrpl/transaction.rb', line 84 def self.from(hash) type = hash['TransactionType'] || hash[:TransactionType] || hash[:transaction_type] raise ValidationError, 'Transaction hash has no TransactionType' unless type klass = self.for(type.to_s) raise ValidationError, "Unknown transaction type #{type}" unless klass klass.new(hash) end |
.resolve(name) ⇒ Object
Translates an accessor name to the ledger's field name.
116 117 118 119 120 121 |
# File 'lib/xrpl/transaction.rb', line 116 def self.resolve(name) key = name.to_s return key if FIELD_TO_ACCESSOR.key?(key) ACCESSOR_TO_FIELD[key] || key end |
.underscore(name) ⇒ Object
Ledger field name -> snake_case accessor, and back.
XChain, NFToken and MPToken are brand names rather than acronyms, so they are folded to a single word the way the reference SDKs write them.
48 49 50 51 52 53 54 55 56 57 |
# File 'lib/xrpl/transaction.rb', line 48 def self.underscore(name) name .sub(/\AXChain/, 'Xchain') .sub(/\ANFToken/, 'Nftoken') .sub(/\AMPToken/, 'Mptoken') .gsub(/([A-Z]{2,})s(?=[A-Z]|\z)/) { "#{Regexp.last_match(1).capitalize}s" } .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .downcase end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
157 158 159 |
# File 'lib/xrpl/transaction.rb', line 157 def ==(other) other.is_a?(Transaction) && other.to_h == to_h end |
#[](name) ⇒ Object
Reads a field by accessor name, symbol or ledger name.
100 101 102 |
# File 'lib/xrpl/transaction.rb', line 100 def [](name) @fields[self.class.resolve(name)] end |
#[]=(name, value) ⇒ Object
Writes a field, rejecting anything the type does not define.
105 106 107 108 109 110 111 112 113 |
# File 'lib/xrpl/transaction.rb', line 105 def []=(name, value) field = self.class.resolve(name) unless self.class.format.key?(field) raise ValidationError, "#{self.class.transaction_type} has no field #{field}" end value.nil? ? @fields.delete(field) : @fields[field] = value end |
#hash ⇒ Object
162 163 164 |
# File 'lib/xrpl/transaction.rb', line 162 def hash to_h.hash end |
#inspect ⇒ Object
166 167 168 |
# File 'lib/xrpl/transaction.rb', line 166 def inspect "#<#{self.class.name} #{to_h.inspect}>" end |
#missing_fields ⇒ Object
Fields the format requires that have not been set, ignoring the ones autofill and signing provide.
132 133 134 135 136 137 |
# File 'lib/xrpl/transaction.rb', line 132 def missing_fields self.class.format .select { |field, optionality| optionality == REQUIRED } .keys .reject { |field| SUPPLIED_LATER.include?(field) || @fields.key?(field) } end |
#to_blob ⇒ Object
The serialised transaction, as hex.
153 154 155 |
# File 'lib/xrpl/transaction.rb', line 153 def to_blob BinaryCodec.json_to_binary(to_h) end |
#to_h ⇒ Object Also known as: to_hash
The transaction as the binary codec wants it: ledger field names, with TransactionType filled in.
125 126 127 |
# File 'lib/xrpl/transaction.rb', line 125 def to_h { 'TransactionType' => self.class.transaction_type }.merge(@fields) end |
#valid? ⇒ Boolean
139 140 141 |
# File 'lib/xrpl/transaction.rb', line 139 def valid? missing_fields.empty? end |
#validate! ⇒ Object
Raises unless every required field is present.
144 145 146 147 148 149 150 |
# File 'lib/xrpl/transaction.rb', line 144 def validate! missing = missing_fields return self if missing.empty? raise ValidationError, "#{self.class.transaction_type} is missing #{missing.join(', ')}" end |