# Transactor Architecture

[← Back to Transactors: Understanding the Lifecycle of a Transaction](/core-dev-bootcamp/module03bis.md)

***

### Introduction

Every transaction that modifies the XRP Ledger—whether it's a payment, an offer, a trust line, or any other operation—is processed by a **Transactor**. The transactor architecture provides a consistent, safe, and extensible framework for implementing transaction types while ensuring that the ledger remains in a valid state.

Understanding this architecture is essential for anyone who wants to implement new transaction types, debug validation failures, or contribute to the rippled codebase. This section explores the layered design of the transaction engine and how each component contributes to the safety and correctness of ledger modifications.

***

### The Transactor Base Class

The `Transactor` class, defined in `src/xrpld/app/tx/detail/Transactor.h`, is the foundation of all transaction processing in rippled. Every transaction type inherits from this base class, which provides:

* **Common validation logic**: Signature verification, fee checks, sequence number validation
* **Helper methods**: Account balance queries, ledger state access, fee calculation
* **Virtual methods**: Hooks for transaction-specific logic (preflight, preclaim, doApply)
* **Transaction context**: Access to the ledger view, transaction data, and application state

#### Core Class Structure

```cpp
class Transactor
{
protected:
    ApplyContext& ctx_;
    beast::WrappedSink sink_;
    beast::Journal const j_;

    AccountID const account_;
    XRPAmount mPriorBalance;   // Balance before fees.
    XRPAmount mSourceBalance;  // Balance after fees.

public:
    enum ConsequencesFactoryType { Normal, Blocker, Custom };

    // Main entry point for transaction application
    ApplyResult operator()();

    ApplyView& view();
    ApplyView const& view() const;

    // Static methods for validation phases
    static NotTEC checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j);
    static NotTEC checkPriorTxAndLastLedger(PreclaimContext const& ctx);
    static TER checkFee(PreclaimContext const& ctx, XRPAmount baseFee);
    static NotTEC checkSign(PreclaimContext const& ctx);
    static XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx);

    // Default implementations that derived classes can override
    static TER preclaim(PreclaimContext const& ctx) { return tesSUCCESS; }

protected:
    TER apply();
    explicit Transactor(ApplyContext& ctx);

    virtual void preCompute();
    virtual TER doApply() = 0;  // Pure virtual - must be implemented
};
```

**Key observations:**

1. **`doApply()` is pure virtual**: Every transaction type must implement this method
2. **`preclaim()` has a default implementation**: Returns `tesSUCCESS`, can be overridden
3. **`mPriorBalance` and `mSourceBalance`**: Track balance before and after fee deduction
4. **`ctx_`**: The `ApplyContext` provides access to the transaction, view, and application

***

### Context Objects

The transactor framework uses three context objects that provide different levels of access at each processing phase:

#### PreflightContext

Used during the preflight phase for stateless validation:

```cpp
struct PreflightContext
{
    Application& app;
    STTx const& tx;
    Rules const rules;
    ApplyFlags flags;
    beast::Journal const j;
};
```

**What it provides:**

* Access to the raw transaction (`tx`)
* Protocol rules that are currently enabled (`rules`)
* Application flags (`flags`)
* Logging journal (`j`)

**What it does NOT provide:**

* Any ledger state (no `view`)

This limitation is intentional—preflight checks must be stateless and deterministic based solely on the transaction content.

#### PreclaimContext

Used during the preclaim phase for read-only ledger validation:

```cpp
struct PreclaimContext
{
    Application& app;
    ReadView const& view;
    TER preflightResult;
    ApplyFlags flags;
    STTx const& tx;
    beast::Journal const j;
};
```

**What it provides:**

* Read-only access to the ledger (`view`)
* The result from preflight (`preflightResult`)
* Transaction and application context

**Key distinction:** The `view` is `ReadView const&`—you can read ledger state but cannot modify it.

#### ApplyContext

Used during the doApply phase for ledger modification:

```cpp
class ApplyContext
{
public:
    Application& app;
    ApplyView& view();
    STTx const& tx;
    beast::Journal journal;
    // ... additional methods
};
```

**What it provides:**

* Full read/write access to the ledger via `view()`
* Transaction data and application services
* Methods for delivering amounts, tracking metadata

***

### Transaction Type Registration

New transaction types must be registered with the transaction engine. This is done through a combination of:

1. **Transaction format definition** in `src/libxrpl/protocol/TxFormats.cpp`
2. **Transactor class implementation** in `src/xrpld/app/tx/detail/`
3. **Registration in `applySteps.cpp`**

#### Example: CheckCreate Registration

In `TxFormats.cpp`:

```cpp
add(jss::CheckCreate,
    ttCHECK_CREATE,
    {
        {sfDestination,     soeREQUIRED},
        {sfSendMax,         soeREQUIRED},
        {sfExpiration,      soeOPTIONAL},
        {sfDestinationTag,  soeOPTIONAL},
        {sfInvoiceID,       soeOPTIONAL},
    },
    commonFields);
```

This defines:

* The JSON name (`jss::CheckCreate`)
* The transaction type enum (`ttCHECK_CREATE`)
* Required and optional fields
* Common fields inherited by all transactions

***

### The Transactor Hierarchy

All specific transaction types inherit from `Transactor` and implement their own validation logic:

```
Transactor (base class)
├── CreateCheck
├── CashCheck
├── CancelCheck
├── Payment
├── CreateOffer
├── CancelOffer
├── SetTrust
├── SetAccount
├── SetRegularKey
├── SetSignerList
├── CreateTicket
├── EscrowCreate
├── EscrowFinish
├── EscrowCancel
├── PayChan (PaymentChannelCreate, Claim, Fund)
├── NFTokenMint
├── NFTokenBurn
├── NFTokenCreateOffer
├── NFTokenAcceptOffer
├── NFTokenCancelOffer
├── AMMCreate
├── AMMDeposit
├── AMMWithdraw
├── AMMVote
├── AMMBid
├── AMMDelete
├── DeleteAccount
├── DepositPreauth
├── Clawback
└── ... (and more)
```

Each derived class typically implements:

1. **`preflight()`**: Static method for stateless validation
2. **`preclaim()`**: Static method for ledger-state validation
3. **`doApply()`**: Instance method for applying changes

***

### The invokePreflight Template

The base class provides a template method that orchestrates the preflight phase:

```cpp
template <class T>
NotTEC
Transactor::invokePreflight(PreflightContext const& ctx)
{
    // 1. Check if the transaction type's feature is enabled
    auto const feature =
        Permission::getInstance().getTxFeature(ctx.tx.getTxnType());
    if (feature && !ctx.rules.enabled(*feature))
        return temDISABLED;

    // 2. Check any extra features the transaction requires
    if (!T::checkExtraFeatures(ctx))
        return temDISABLED;

    // 3. Run preflight1 (account, fee, flags validation)
    if (auto const ret = preflight1(ctx, T::getFlagsMask(ctx)))
        return ret;

    // 4. Run the transaction-specific preflight
    if (auto const ret = T::preflight(ctx))
        return ret;

    // 5. Run preflight2 (signature validation)
    if (auto const ret = preflight2(ctx))
        return ret;

    // 6. Run any post-signature validation
    return T::preflightSigValidated(ctx);
}
```

This template ensures consistent ordering of validation steps across all transaction types.

***

### Key Member Variables

#### account\_

The `AccountID` of the transaction sender, extracted from the `sfAccount` field:

```cpp
AccountID const account_;
```

This is set during construction and used throughout transaction processing.

#### mPriorBalance and mSourceBalance

```cpp
XRPAmount mPriorBalance;   // Balance before fees
XRPAmount mSourceBalance;  // Balance after fees
```

These track the sender's XRP balance:

* `mPriorBalance`: Balance at the start of transaction processing
* `mSourceBalance`: Balance after the transaction fee is deducted

This is used for reserve calculations—the reserve is checked against `mPriorBalance` to allow accounts to dip into reserves to pay fees.

#### ctx\_

```cpp
ApplyContext& ctx_;
```

The `ApplyContext` provides access to:

* `ctx_.tx`: The transaction being processed
* `ctx_.view()`: The ledger view for reading/writing
* `ctx_.app`: The application instance
* `ctx_.journal`: Logging

***

### Helper Methods

The base class provides several helper methods used by derived transactors:

#### view()

```cpp
ApplyView& view() { return ctx_.view(); }
```

Returns the ledger view for reading and modifying ledger state.

#### calculateBaseFee()

```cpp
static XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx);
```

Calculates the base transaction fee based on the transaction type and current fee settings.

#### minimumFee()

```cpp
static XRPAmount minimumFee(
    Application& app,
    XRPAmount baseFee,
    Fees const& fees,
    ApplyFlags flags);
```

Calculates the minimum fee required considering current load and fee escalation.

***

### Codebase References

| File                                     | Description                      |
| ---------------------------------------- | -------------------------------- |
| `src/xrpld/app/tx/detail/Transactor.h`   | Base Transactor class definition |
| `src/xrpld/app/tx/detail/Transactor.cpp` | Base class implementation        |
| `src/xrpld/app/tx/detail/ApplyContext.h` | ApplyContext definition          |
| `src/xrpld/app/tx/applySteps.cpp`        | Transaction type dispatch        |
| `src/libxrpl/protocol/TxFormats.cpp`     | Transaction format definitions   |

***

### Key Takeaways

1. **Inheritance Model**: All transaction types inherit from `Transactor`, ensuring consistent behavior
2. **Context Objects**: Three distinct contexts provide appropriate access at each phase
3. **Pure Virtual doApply**: Every transaction must implement its own state modification logic
4. **Template-Based Preflight**: The `invokePreflight` template ensures consistent validation ordering
5. **Balance Tracking**: `mPriorBalance` and `mSourceBalance` enable proper reserve handling


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.xrpl-commons.org/core-dev-bootcamp/module03bis/transactor-architecture.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
