Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions API-CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Version 3.4.0 is not yet released. These changes are available in the 3.4.0 beta

### Additions in 3.4.0

- `book_offers`, `account_offers`: With the `OfferQualifiers` amendment, an offer entry may now include `all_or_none: true` (the offer carries the `lsfAllOrNone` flag) and/or `min_quantity` (the offer's `MinQuantity` amount). These mark execution-qualified ("contingent") offers that cannot be taken to arbitrary depth; clients should exclude them from quoted/takeable depth. The `OfferCreate` transaction gains the `tfAllOrNone` and `tfPostOnly` flags and an optional `MinQuantity` field, and a marketable `tfPostOnly` offer is rejected with the new `tecWOULD_CROSS` result.

- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`.
When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present.
- `ledger`: `nftoken_id`, `nftoken_ids`, and `offer_id` are now included in transaction metadata when transactions are expanded (`expand`, or admin-only `full`), matching the `tx`, `account_tx`, and `subscribe` (`transactions` stream) responses. ([#5706](https://github.com/XRPLF/rippled/pull/5706))

### Bugfixes in 3.4.0
Expand Down
3 changes: 2 additions & 1 deletion include/xrpl/protocol/LedgerFormats.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ enum LedgerEntryType : std::uint16_t {
LEDGER_OBJECT(Offer, \
LSF_FLAG(lsfPassive, 0x00010000) \
LSF_FLAG(lsfSell, 0x00020000) /* True, offer was placed as a sell. */ \
LSF_FLAG(lsfHybrid, 0x00040000)) /* True, offer is hybrid. */ \
LSF_FLAG(lsfHybrid, 0x00040000) /* True, offer is hybrid. */ \
LSF_FLAG(lsfAllOrNone, 0x00080000)) /* True, offer is all-or-none. */ \
\
LEDGER_OBJECT(RippleState, \
LSF_FLAG(lsfLowReserve, 0x00010000) /* True, if entry counts toward reserve. */ \
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/TER.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ enum TECcodes : TERUnderlyingType {
tecNO_SPONSOR_PERMISSION = 200,
tecOUT_OF_GAS = 201,
tecBYTECODE_REJECTED = 202,
tecWOULD_CROSS = 203,
};

//------------------------------------------------------------------------------
Expand Down
4 changes: 3 additions & 1 deletion include/xrpl/protocol/TxFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfImmediateOrCancel, 0x00020000) \
TF_FLAG(tfFillOrKill, 0x00040000) \
TF_FLAG(tfSell, 0x00080000) \
TF_FLAG(tfHybrid, 0x00100000), \
TF_FLAG(tfHybrid, 0x00100000) \
TF_FLAG(tfAllOrNone, 0x00200000) \
TF_FLAG(tfPostOnly, 0x00400000), \
MASK_ADJ(0)) \
\
TRANSACTION(Payment, \
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/features.macro
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.

XRPL_FEATURE(OfferQualifiers, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocolV1_2, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultNo)
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/ledger_entries.macro
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ LEDGER_ENTRY(ltOFFER, 0x006f, Offer, offer, ({
{sfExpiration, SoeOptional},
{sfDomainID, SoeOptional},
{sfAdditionalBooks, SoeOptional},
{sfMinQuantity, SoeOptional},
}))

/** A ledger object which describes a deposit pre-authorization.
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/sfields.macro
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ TYPED_SFIELD(sfLPTokenBalance, AMOUNT, 31)
TYPED_SFIELD(sfFeeAmount, AMOUNT, 32)
TYPED_SFIELD(sfMaxFee, AMOUNT, 33)
TYPED_SFIELD(sfFeeAmountDelta, AMOUNT, 34)
TYPED_SFIELD(sfMinQuantity, AMOUNT, 35)

// variable length (common)
TYPED_SFIELD(sfPublicKey, VL, 1)
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/transactions.macro
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
{sfExpiration, SoeOptional},
{sfOfferSequence, SoeOptional},
{sfDomainID, SoeOptional},
{sfMinQuantity, SoeOptional},
}))

/** This transaction type cancels existing offers to trade one asset for another. */
Expand Down
2 changes: 2 additions & 0 deletions include/xrpl/protocol/jss.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ JSS(accounts); // in: LedgerEntry, Subscribe, handlers/Ledger
JSS(accounts_proposed); // in: Subscribe, Unsubscribe
JSS(action); //
JSS(active); // out: OverlayImpl
JSS(all_or_none); // out: NetworkOPs
JSS(actor); // in/out: AccountTx
JSS(acquiring); // out: LedgerRequest
JSS(address); // out: PeerImp
Expand Down Expand Up @@ -402,6 +403,7 @@ JSS(metaData); //
JSS(metadata); // out: TransactionEntry
JSS(method); // RPC
JSS(methods); //
JSS(min_quantity); // out: NetworkOPs
JSS(metrics); // out: Peers
JSS(min_count); // in: GetCounts
JSS(min_ledger); // in: LedgerCleaner
Expand Down
35 changes: 35 additions & 0 deletions include/xrpl/protocol_autogen/ledger_entries/Offer.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,30 @@ class Offer : public LedgerEntryBase
{
return this->sle_->isFieldPresent(sfAdditionalBooks);
}

/**
* @brief Get sfMinQuantity (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getMinQuantity() const
{
if (hasMinQuantity())
return this->sle_->at(sfMinQuantity);
return std::nullopt;
}

/**
* @brief Check if sfMinQuantity is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMinQuantity() const
{
return this->sle_->isFieldPresent(sfMinQuantity);
}
};

/**
Expand Down Expand Up @@ -404,6 +428,17 @@ class OfferBuilder : public LedgerEntryBuilderBase<OfferBuilder>
return *this;
}

/**
* @brief Set sfMinQuantity (SoeOptional)
* @return Reference to this builder for method chaining.
*/
OfferBuilder&
setMinQuantity(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfMinQuantity] = value;
return *this;
}

/**
* @brief Build and return the completed Offer wrapper.
* @param index The ledger entry index.
Expand Down
37 changes: 37 additions & 0 deletions include/xrpl/protocol_autogen/transactions/OfferCreate.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,32 @@ class OfferCreate : public TransactionBase
{
return this->tx_->isFieldPresent(sfDomainID);
}

/**
* @brief Get sfMinQuantity (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_AMOUNT::type::value_type>
getMinQuantity() const
{
if (hasMinQuantity())
{
return this->tx_->at(sfMinQuantity);
}
return std::nullopt;
}

/**
* @brief Check if sfMinQuantity is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMinQuantity() const
{
return this->tx_->isFieldPresent(sfMinQuantity);
}
};

/**
Expand Down Expand Up @@ -253,6 +279,17 @@ class OfferCreateBuilder : public TransactionBuilderBase<OfferCreateBuilder>
return *this;
}

/**
* @brief Set sfMinQuantity (SoeOptional)
* @return Reference to this builder for method chaining.
*/
OfferCreateBuilder&
setMinQuantity(std::decay_t<typename SF_AMOUNT::type::value_type> const& value)
{
object_[sfMinQuantity] = value;
return *this;
}

/**
* @brief Build and return the OfferCreate wrapper.
* @param publicKey The public key for signing.
Expand Down
22 changes: 22 additions & 0 deletions include/xrpl/tx/invariants/InvariantCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,27 @@ class NoBadOffers
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
};

/**
* @brief Invariant: contingent offers honor their execution floor.
*
* An offer carrying lsfAllOrNone must be consumed in its entirety or not at
* all: a modification that leaves the offer present but reduces its TakerGets
* is a partial fill, which is forbidden. An offer carrying sfMinQuantity must
* never be reduced by less than min(sfMinQuantity, its prior remaining size)
* in one transaction.
*/
class ValidContingentOffers
{
bool bad_ = false;

public:
void
visitEntry(bool, SLE::const_ref, SLE::const_ref);

[[nodiscard]] bool
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&) const;
};

/**
* @brief Invariant: an escrow entry must take a value between 0 and
* kInitialXRP drops exclusive.
Expand Down Expand Up @@ -442,6 +463,7 @@ using InvariantChecks = std::tuple<
NoDeepFreezeTrustLinesWithoutFreeze,
TransfersNotFrozen,
NoBadOffers,
ValidContingentOffers,
NoZeroEscrow,
ValidNewAccountRoot,
ValidNFTokenPage,
Expand Down
18 changes: 18 additions & 0 deletions include/xrpl/tx/paths/AMMOffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,24 @@ class AMMOffer
return std::nullopt;
}

/**
* AMM offers are never all-or-none.
*/
[[nodiscard]] bool
isAllOrNone() const
{
return false;
}

/**
* AMM offers never carry a minimum-quantity floor.
*/
[[nodiscard]] std::optional<TOut>
minQuantity() const
{
return std::nullopt;
}

[[nodiscard]] TAmounts<TIn, TOut> const&
amount() const;

Expand Down
19 changes: 19 additions & 0 deletions include/xrpl/tx/paths/BookTip.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <xrpl/protocol/Quality.h>
#include <xrpl/protocol/STLedgerEntry.h>

#include <boost/container/flat_set.hpp>

namespace xrpl {

class Logs;
Expand All @@ -27,13 +29,30 @@ class BookTip
uint256 index_;
SLE::pointer entry_;
Quality quality_{};
// When set, the next step() leaves the current offer on the book instead
// of deleting it (used to skip a contingent offer without consuming it).
bool keepCurrent_{false};
// Offers kept on the book during this walk. The walk normally advances
// by deleting the consumed tip; a kept offer is not deleted, so step()
// must iterate past every kept entry to reach the rest of its directory.
boost::container::flat_set<uint256> kept_;

public:
/**
* Create the iterator.
*/
BookTip(ApplyView& view, Book const& book);

/**
* Keep the current offer on the book when advancing past it.
*/
void
keepCurrent()
{
keepCurrent_ = true;
kept_.insert(index_);
}

[[nodiscard]] uint256 const&
dir() const noexcept
{
Expand Down
23 changes: 23 additions & 0 deletions include/xrpl/tx/paths/Offer.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Concepts.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Quality.h>
#include <xrpl/protocol/Rules.h>
#include <xrpl/protocol/SField.h>
Expand Down Expand Up @@ -121,6 +122,28 @@ class TOffer
return entry_->key();
}

/**
* Returns true if the offer is all-or-none: it must be consumed in its
* entirety or not at all (see lsfAllOrNone).
*/
[[nodiscard]] bool
isAllOrNone() const
{
return entry_ && entry_->isFlag(lsfAllOrNone);
}

/**
* Returns the offer's minimum executable quantity (sfMinQuantity,
* denominated in TakerGets), capped at the offer's remaining size.
*/
[[nodiscard]] std::optional<TOut>
minQuantity() const
{
if (!entry_ || !entry_->isFieldPresent(sfMinQuantity))
return std::nullopt;
return std::min(toAmount<TOut>(entry_->getFieldAmount(sfMinQuantity)), amounts_.out);
}

[[nodiscard]] Asset const&
assetIn() const;
[[nodiscard]] Asset const&
Expand Down
11 changes: 11 additions & 0 deletions include/xrpl/tx/paths/OfferStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ class TOfferStreamBase
bool
step();

/**
* Keep the current offer on the book when the stream next advances.
* Used to skip a contingent (all-or-none) offer that cannot be taken in
* full, without deleting it.
*/
void
keepCurrentOffer()
{
tip_.keepCurrent();
}

[[nodiscard]] TOut
ownerFunds() const
{
Expand Down
1 change: 1 addition & 0 deletions src/libxrpl/protocol/TER.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ transResults()
MAKE_ERROR(tecNO_SPONSOR_PERMISSION, "Sponsor has not authorized this transaction."),
MAKE_ERROR(tecOUT_OF_GAS, "The WASM code ran out of gas during execution."),
MAKE_ERROR(tecBYTECODE_REJECTED, "The custom WASM code that was run rejected your transaction."),
MAKE_ERROR(tecWOULD_CROSS, "Post-only offer would cross."),

MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."),
MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."),
Expand Down
Loading