initial implementation of token holders

This commit is contained in:
Mayukha Vadari
2026-03-22 01:14:13 -07:00
parent 370a420aa3
commit e89e83c802
12 changed files with 448 additions and 72 deletions

View File

@@ -0,0 +1,96 @@
#pragma once
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHolderBase.h>
namespace xrpl {
class MPToken : public virtual TokenHolderBase
{
public:
MPToken(ReadView const& view, MPTokenIssuance const& issuance, AccountID const& holder)
: ReadOnlySLE(view.read(keylet::mptoken(issuance.getMptID(), holder)), view)
, TokenHolderBase(
view,
view.read(keylet::mptoken(issuance.getMptID(), holder)),
issuance,
holder)
, issuance_(issuance)
{
}
MPTokenIssuance const&
getIssuance() const
{
return issuance_;
}
protected:
MPTokenIssuance const& issuance_;
};
class WritableMPToken : public virtual WritableTokenHolderBase, public virtual MPToken
{
public:
WritableMPToken(ApplyView& view, WritableMPTokenIssuance& issuance, AccountID const& holder)
: ReadOnlySLE(view.peek(keylet::mptoken(issuance.getMptID(), holder)), view)
, TokenHolderBase(
view,
view.peek(keylet::mptoken(issuance.getMptID(), holder)),
issuance,
holder)
, WritableSLE(view.peek(keylet::mptoken(issuance.getMptID(), holder)), view)
, WritableTokenHolderBase(
view,
view.peek(keylet::mptoken(issuance.getMptID(), holder)),
issuance,
holder)
, MPToken(view, issuance, holder)
, writableIssuance_(issuance)
{
}
// Resolve ambiguity: use writable operator-> for non-const, read-only for const
using WritableSLE::operator->;
using MPToken::operator->;
using WritableSLE::operator*;
using MPToken::operator*;
WritableMPTokenIssuance&
getWritableIssuance()
{
return writableIssuance_;
}
static TER
createMPToken(
ApplyView& view,
WritableMPTokenIssuance& issuance,
AccountID const& account,
std::uint32_t const flags)
{
WritableMPToken mptoken(view, issuance, account);
auto const ownerNode =
view.dirInsert(keylet::ownerDir(account), mptoken.key(), describeOwnerDir(account));
if (!ownerNode)
return tecDIR_FULL; // LCOV_EXCL_LINE
mptoken.newSLE();
(*mptoken)[sfAccount] = account;
(*mptoken)[sfMPTokenIssuanceID] = issuance.getMptID();
(*mptoken)[sfFlags] = flags;
(*mptoken)[sfOwnerNode] = *ownerNode;
mptoken.insert();
return tesSUCCESS;
}
protected:
WritableMPTokenIssuance& writableIssuance_;
};
} // namespace xrpl

View File

@@ -0,0 +1,146 @@
#pragma once
#include <xrpl/basics/Expected.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHolderBase.h>
namespace xrpl {
class RippleState : public virtual TokenHolderBase
{
public:
RippleState(ReadView const& view, IOUToken const& token, AccountID const& holder)
: ReadOnlySLE(view.read(keylet::line(holder, token.getIssuer(), token.getCurrency())), view)
, TokenHolderBase(
view,
view.read(keylet::line(holder, token.getIssuer(), token.getCurrency())),
token,
holder)
, iouToken_(token)
{
}
/** Constructor with explicit SLE (for when SLE is already available) */
RippleState(
ReadView const& view,
std::shared_ptr<SLE const> sle,
IOUToken const& token,
AccountID const& holder)
: ReadOnlySLE(sle, view), TokenHolderBase(view, sle, token, holder), iouToken_(token)
{
}
IOUToken const&
getIOUToken() const
{
return iouToken_;
}
protected:
IOUToken const& iouToken_;
};
class WritableRippleState : public virtual WritableTokenHolderBase, public virtual RippleState
{
public:
WritableRippleState(ApplyView& view, WritableIOUToken& token, AccountID const& holder)
: ReadOnlySLE(view.peek(keylet::line(holder, token.getIssuer(), token.getCurrency())), view)
, TokenHolderBase(
view,
view.peek(keylet::line(holder, token.getIssuer(), token.getCurrency())),
token,
holder)
, WritableSLE(view.peek(keylet::line(holder, token.getIssuer(), token.getCurrency())), view)
, WritableTokenHolderBase(
view,
view.peek(keylet::line(holder, token.getIssuer(), token.getCurrency())),
token,
holder)
, RippleState(view, token, holder)
, writableIOUToken_(token)
{
}
/** Constructor with explicit SLE (for creation or when SLE is already available) */
WritableRippleState(
ApplyView& view,
std::shared_ptr<SLE> sle,
WritableIOUToken& token,
AccountID const& holder)
: ReadOnlySLE(sle, view)
, TokenHolderBase(view, sle, token, holder)
, WritableSLE(sle, view)
, WritableTokenHolderBase(view, sle, token, holder)
, RippleState(view, sle, token, holder)
, writableIOUToken_(token)
{
}
// Resolve ambiguity: use writable operator-> for non-const, read-only for const
using WritableSLE::operator->;
using RippleState::operator->;
using WritableSLE::operator*;
using RippleState::operator*;
WritableIOUToken&
getWritableIOUToken()
{
return writableIOUToken_;
}
static Expected<std::unique_ptr<WritableRippleState>, TER>
createHolding(
ApplyView& view,
WritableIOUToken& token,
AccountID const& accountID,
beast::Journal journal)
{
auto const ter = token.addEmptyHolding(accountID, XRPAmount{0}, journal);
if (ter != tesSUCCESS)
return Unexpected(ter);
return std::make_unique<WritableRippleState>(view, token, accountID);
}
//--------------------------------------------------------------------------
//
// Trust line operations
//
//--------------------------------------------------------------------------
/** Create a trust line
This can set an initial balance.
*/
[[nodiscard]] static TER
trustCreate(
ApplyView& view,
bool const bSrcHigh,
AccountID const& uSrcAccountID,
AccountID const& uDstAccountID,
uint256 const& uIndex, // ripple state entry
WritableAccountRoot& wrappedAcct, // the account being set.
bool const bAuth, // authorize account.
bool const bNoRipple, // others cannot ripple through
bool const bFreeze, // funds cannot leave
bool bDeepFreeze, // can neither receive nor send funds
STAmount const& saBalance, // balance of account being set.
// Issuer should be noAccount()
STAmount const& saLimit, // limit for account being set.
// Issuer should be the account being set.
std::uint32_t uQualityIn,
std::uint32_t uQualityOut,
beast::Journal j);
[[nodiscard]] static TER
trustDelete(
ApplyView& view,
std::shared_ptr<SLE> const& sleRippleState,
AccountID const& uLowAccountID,
AccountID const& uHighAccountID,
beast::Journal j);
protected:
WritableIOUToken& writableIOUToken_;
};
} // namespace xrpl

View File

@@ -216,44 +216,6 @@ creditBalance(
Currency const& currency);
/** @} */
//------------------------------------------------------------------------------
//
// Trust line operations
//
//------------------------------------------------------------------------------
/** Create a trust line
This can set an initial balance.
*/
[[nodiscard]] TER
trustCreate(
ApplyView& view,
bool const bSrcHigh,
AccountID const& uSrcAccountID,
AccountID const& uDstAccountID,
uint256 const& uIndex, // ripple state entry
WritableAccountRoot& wrappedAcct, // the account being set.
bool const bAuth, // authorize account.
bool const bNoRipple, // others cannot ripple through
bool const bFreeze, // funds cannot leave
bool bDeepFreeze, // can neither receive nor send funds
STAmount const& saBalance, // balance of account being set.
// Issuer should be noAccount()
STAmount const& saLimit, // limit for account being set.
// Issuer should be the account being set.
std::uint32_t uQualityIn,
std::uint32_t uQualityOut,
beast::Journal j);
[[nodiscard]] TER
trustDelete(
ApplyView& view,
std::shared_ptr<SLE> const& sleRippleState,
AccountID const& uLowAccountID,
AccountID const& uHighAccountID,
beast::Journal j);
//------------------------------------------------------------------------------
//
// IOU issuance/redemption

View File

@@ -0,0 +1,155 @@
#pragma once
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/ledger/helpers/WrappedSLEBase.h>
#include <xrpl/protocol/STLedgerEntry.h>
namespace xrpl {
class TokenHolderBase : public virtual ReadOnlySLE
{
public:
TokenHolderBase(
ReadView const& view,
std::shared_ptr<SLE const> sle,
TokenBase const& token,
AccountID const& holder)
: ReadOnlySLE(sle, view), token_(token), holder_(holder), holderAccount_(holder, view)
{
}
/** Constructor with explicit keylet (for when SLE lookup is needed) */
TokenHolderBase(
ReadView const& view,
Keylet const& key,
TokenBase const& token,
AccountID const& holder)
: ReadOnlySLE(key, view), token_(token), holder_(holder), holderAccount_(holder, view)
{
}
TokenHolderBase() = delete;
AccountID const&
getHolder() const
{
return holder_;
}
TokenBase const&
getToken() const
{
return token_;
}
[[nodiscard]] bool
isFrozen(int depth = 0) const
{
return token_.isFrozen(holder_, depth);
}
[[nodiscard]] bool
isDeepFrozen(int depth = 0) const
{
return token_.isDeepFrozen(holder_, depth);
}
[[nodiscard]] TER
checkFrozen() const
{
return token_.checkFrozen(holder_);
}
[[nodiscard]] TER
checkDeepFrozen() const
{
return token_.checkDeepFrozen(holder_);
}
STAmount
accountHolds(
FreezeHandling zeroIfFrozen,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const
{
return token_.accountHolds(holder_, zeroIfFrozen, j, includeFullBalance);
}
[[nodiscard]] STAmount
accountHolds(
FreezeHandling zeroIfFrozen,
AuthHandling zeroIfUnauthorized,
beast::Journal j,
SpendableHandling includeFullBalance = shSIMPLE_BALANCE) const
{
return token_.accountHolds(
holder_, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
}
[[nodiscard]] TER
requireAuth(AuthType authType = AuthType::Legacy, int depth = 0) const
{
return token_.requireAuth(holder_, authType, depth);
}
[[nodiscard]] TER
canTransfer(AccountID const& to) const
{
return token_.canTransfer(holder_, to);
}
[[nodiscard]] TER
canTransfer(TokenHolderBase const& to) const
{
return token_.canTransfer(holder_, to.getHolder());
}
protected:
TokenBase const& token_;
AccountID const holder_;
AccountRoot holderAccount_;
};
class WritableTokenHolderBase : public virtual TokenHolderBase, public virtual WritableSLE
{
public:
WritableTokenHolderBase(
ApplyView& view,
std::shared_ptr<SLE> sle,
WritableTokenBase& token,
AccountID const& holder)
: ReadOnlySLE(sle, view)
, TokenHolderBase(view, sle, token, holder)
, WritableSLE(sle, view)
, writableToken_(token)
, writableHolderAccount_(holder, view)
{
}
/** Constructor with explicit keylet (for creation or lookup by key) */
WritableTokenHolderBase(
ApplyView& view,
Keylet const& key,
WritableTokenBase& token,
AccountID const& holder)
: ReadOnlySLE(key, view)
, TokenHolderBase(view, key, token, holder)
, WritableSLE(key, view)
, writableToken_(token)
, writableHolderAccount_(holder, view)
{
}
WritableTokenBase&
getWritableToken()
{
return writableToken_;
}
protected:
WritableTokenBase& writableToken_;
WritableAccountRoot writableHolderAccount_;
};
} // namespace xrpl

View File

@@ -60,6 +60,12 @@ public:
return readView_;
}
Keylet const&
key() const
{
return key_;
}
STLedgerEntry const*
operator->() const
{
@@ -80,12 +86,21 @@ protected:
/** Constructor for read-only context (ReadView) */
explicit ReadOnlySLE(std::shared_ptr<SLE const> sle, ReadView const& view)
: sle_(std::move(sle)), readView_(view)
: sle_(std::move(sle))
, readView_(view)
, key_(sle_ ? Keylet(sle_->getType(), sle_->key()) : Keylet(ltANY, uint256{}))
{
}
/** Constructor for read-only context (ReadView) with explicit keylet */
explicit ReadOnlySLE(Keylet const& key, ReadView const& view)
: sle_(view.read(key)), readView_(view), key_(key)
{
}
std::shared_ptr<SLE const> sle_; // Always valid (const view)
ReadView const& readView_; // Always valid
Keylet key_; // Keylet for this entry
};
/**
@@ -192,8 +207,8 @@ protected:
{
}
ApplyView& applyView_; // ApplyView for write contexts (first for init order)
Keylet const key_;
ApplyView& applyView_; // ApplyView for write contexts (first for init order)
Keylet key_; // Keylet for this entry
std::shared_ptr<SLE> mutableSle_; // Mutable SLE for write contexts
};