#pragma once #include #include #include #include #include #include #include #include #include #include namespace xrpl::transactions { /** * @brief Base class for all transaction wrapper types. * * Provides type-safe read-only accessors for common transaction fields. * This is an immutable wrapper around STTx. Use the corresponding Builder classes * to construct new transactions. */ class TransactionBase { public: /** * @brief Construct a transaction wrapper from an existing STTx object. * @param tx The underlying transaction object to wrap */ explicit TransactionBase(std::shared_ptr tx) : tx_(std::move(tx)) { } /** * @brief Validate the transaction * @return true if validation passes, false otherwise */ [[nodiscard]] bool validate(std::string& reason) const { if (!protocol_autogen::validateSTObject( *tx_, TxFormats::getInstance().findByType(tx_->getTxnType())->getSOTemplate())) { // LCOV_EXCL_START reason = "Transaction failed schema validation"; return false; // LCOV_EXCL_STOP } // Pseudo transactions are not submitted to the network if (isPseudoTx(*tx_)) { return true; } return passesLocalChecks(*tx_, reason); } /** * @brief Get the transaction type. * @return The type of this transaction */ [[nodiscard]] xrpl::TxType getTransactionType() const { return tx_->getTxnType(); } /** * @brief Get the account initiating the transaction (sfAccount). * * This field is REQUIRED for all transactions. * @return The account ID of the transaction sender */ [[nodiscard]] AccountID getAccount() const { return tx_->at(sfAccount); } /** * @brief Get the sequence number of the transaction (sfSequence). * * This field is REQUIRED for all transactions. * @return The sequence number */ [[nodiscard]] std::uint32_t getSequence() const { return tx_->at(sfSequence); } /** * @brief Get the transaction fee (sfFee). * * This field is REQUIRED for all transactions. * @return The fee amount */ [[nodiscard]] STAmount getFee() const { return tx_->at(sfFee); } /** * @brief Get the signing public key (sfSigningPubKey). * * This field is REQUIRED for all transactions. * @return The public key used for signing as a blob */ [[nodiscard]] Blob getSigningPubKey() const { return tx_->getFieldVL(sfSigningPubKey); } /** * @brief Get the transaction flags (sfFlags). * * This field is OPTIONAL. * @return The flags value if present, std::nullopt otherwise */ [[nodiscard]] std::optional getFlags() const { if (tx_->isFieldPresent(sfFlags)) return tx_->at(sfFlags); return std::nullopt; } /** * @brief Check if the transaction has flags set. * @return true if sfFlags is present, false otherwise */ [[nodiscard]] bool hasFlags() const { return tx_->isFieldPresent(sfFlags); } /** * @brief Get the source tag (sfSourceTag). * * This field is OPTIONAL and used to identify the source of a payment. * @return The source tag value if present, std::nullopt otherwise */ [[nodiscard]] std::optional getSourceTag() const { if (tx_->isFieldPresent(sfSourceTag)) return tx_->at(sfSourceTag); return std::nullopt; } /** * @brief Check if the transaction has a source tag. * @return true if sfSourceTag is present, false otherwise */ [[nodiscard]] bool hasSourceTag() const { return tx_->isFieldPresent(sfSourceTag); } /** * @brief Get the previous transaction ID (sfPreviousTxnID). * * This field is OPTIONAL and used for transaction chaining. * @return The previous transaction ID if present, std::nullopt otherwise */ [[nodiscard]] std::optional getPreviousTxnID() const { if (tx_->isFieldPresent(sfPreviousTxnID)) return tx_->at(sfPreviousTxnID); return std::nullopt; } /** * @brief Check if the transaction has a previous transaction ID. * @return true if sfPreviousTxnID is present, false otherwise */ [[nodiscard]] bool hasPreviousTxnID() const { return tx_->isFieldPresent(sfPreviousTxnID); } /** * @brief Get the last ledger sequence (sfLastLedgerSequence). * * This field is OPTIONAL and specifies the latest ledger sequence * in which this transaction can be included. * @return The last ledger sequence if present, std::nullopt otherwise */ [[nodiscard]] std::optional getLastLedgerSequence() const { if (tx_->isFieldPresent(sfLastLedgerSequence)) return tx_->at(sfLastLedgerSequence); return std::nullopt; } /** * @brief Check if the transaction has a last ledger sequence. * @return true if sfLastLedgerSequence is present, false otherwise */ [[nodiscard]] bool hasLastLedgerSequence() const { return tx_->isFieldPresent(sfLastLedgerSequence); } /** * @brief Get the account transaction ID (sfAccountTxnID). * * This field is OPTIONAL and used to track transaction sequences. * @return The account transaction ID if present, std::nullopt otherwise */ [[nodiscard]] std::optional getAccountTxnID() const { if (tx_->isFieldPresent(sfAccountTxnID)) return tx_->at(sfAccountTxnID); return std::nullopt; } /** * @brief Check if the transaction has an account transaction ID. * @return true if sfAccountTxnID is present, false otherwise */ [[nodiscard]] bool hasAccountTxnID() const { return tx_->isFieldPresent(sfAccountTxnID); } /** * @brief Get the operation limit (sfOperationLimit). * * This field is OPTIONAL and limits the number of operations in a transaction. * @return The operation limit if present, std::nullopt otherwise */ [[nodiscard]] std::optional getOperationLimit() const { if (tx_->isFieldPresent(sfOperationLimit)) return tx_->at(sfOperationLimit); return std::nullopt; } /** * @brief Check if the transaction has an operation limit. * @return true if sfOperationLimit is present, false otherwise */ [[nodiscard]] bool hasOperationLimit() const { return tx_->isFieldPresent(sfOperationLimit); } /** * @brief Get the memos array (sfMemos). * * This field is OPTIONAL and contains arbitrary data attached to the transaction. * @note This is an untyped field (STArray). * @return A reference wrapper to the memos array if present, std::nullopt otherwise */ [[nodiscard]] std::optional> getMemos() const { if (tx_->isFieldPresent(sfMemos)) return tx_->getFieldArray(sfMemos); return std::nullopt; } /** * @brief Check if the transaction has memos. * @return true if sfMemos is present, false otherwise */ [[nodiscard]] bool hasMemos() const { return tx_->isFieldPresent(sfMemos); } /** * @brief Get the ticket sequence (sfTicketSequence). * * This field is OPTIONAL and used when consuming a ticket instead of a sequence number. * @return The ticket sequence if present, std::nullopt otherwise */ [[nodiscard]] std::optional getTicketSequence() const { if (tx_->isFieldPresent(sfTicketSequence)) return tx_->at(sfTicketSequence); return std::nullopt; } /** * @brief Check if the transaction has a ticket sequence. * @return true if sfTicketSequence is present, false otherwise */ [[nodiscard]] bool hasTicketSequence() const { return tx_->isFieldPresent(sfTicketSequence); } /** * @brief Get the transaction signature (sfTxnSignature). * * This field is OPTIONAL and contains the signature for single-signed transactions. * @return The transaction signature as a blob if present, std::nullopt otherwise */ [[nodiscard]] std::optional getTxnSignature() const { if (tx_->isFieldPresent(sfTxnSignature)) return tx_->getFieldVL(sfTxnSignature); return std::nullopt; } /** * @brief Check if the transaction has a transaction signature. * @return true if sfTxnSignature is present, false otherwise */ [[nodiscard]] bool hasTxnSignature() const { return tx_->isFieldPresent(sfTxnSignature); } /** * @brief Get the signers array (sfSigners). * * This field is OPTIONAL and contains the list of signers for multi-signed transactions. * @note This is an untyped field (STArray). * @return A reference wrapper to the signers array if present, std::nullopt otherwise */ [[nodiscard]] std::optional> getSigners() const { if (tx_->isFieldPresent(sfSigners)) return tx_->getFieldArray(sfSigners); return std::nullopt; } /** * @brief Check if the transaction has signers. * @return true if sfSigners is present, false otherwise */ [[nodiscard]] bool hasSigners() const { return tx_->isFieldPresent(sfSigners); } /** * @brief Get the network ID (sfNetworkID). * * This field is OPTIONAL and identifies the network this transaction is intended for. * @return The network ID if present, std::nullopt otherwise */ [[nodiscard]] std::optional getNetworkID() const { if (tx_->isFieldPresent(sfNetworkID)) return tx_->at(sfNetworkID); return std::nullopt; } /** * @brief Check if the transaction has a network ID. * @return true if sfNetworkID is present, false otherwise */ [[nodiscard]] bool hasNetworkID() const { return tx_->isFieldPresent(sfNetworkID); } /** * @brief Get the delegate account (sfDelegate). * * This field is OPTIONAL and specifies a delegate account for the transaction. * @return The delegate account ID if present, std::nullopt otherwise */ [[nodiscard]] std::optional getDelegate() const { if (tx_->isFieldPresent(sfDelegate)) return tx_->at(sfDelegate); return std::nullopt; } /** * @brief Check if the transaction has a delegate account. * @return true if sfDelegate is present, false otherwise */ [[nodiscard]] bool hasDelegate() const { return tx_->isFieldPresent(sfDelegate); } /** * @brief Get the underlying STTx object. * * Provides direct access to the wrapped transaction object for cases * where the type-safe accessors are insufficient. * @return A constant reference to the underlying STTx object */ [[nodiscard]] std::shared_ptr getSTTx() const { return tx_; } protected: /** @brief The underlying transaction object being wrapped. */ std::shared_ptr tx_; }; } // namespace xrpl::transactions