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
11 changes: 11 additions & 0 deletions include/xrpl/ledger/helpers/AccountRootHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,17 @@ adjustLoanBrokerOwnerCount(
[[nodiscard]] Rate
transferRate(ReadView const& view, AccountID const& issuer);

/**
* Returns the transfer fee charged for a specific currency of the issuer.
* A per-currency TransferFee on the currency's TokenIssuance overrides the
* account-wide TransferRate.
*/
[[nodiscard]] Rate
transferRate(ReadView const& view, AccountID const& issuer, Currency const& currency);

[[nodiscard]] Rate
transferRate(ReadView const& view, Issue const& issue);

/**
* Generate a pseudo-account address from a pseudo owner key.
* @param pseudoOwnerKey The key to generate the address from
Expand Down
4 changes: 3 additions & 1 deletion include/xrpl/ledger/helpers/TokenHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/TokenIssuanceHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Issue.h>
Expand Down Expand Up @@ -391,7 +392,8 @@ directSendNoFee(
AccountID const& uReceiverID,
STAmount const& saAmount,
bool bCheckIssuer,
beast::Journal j);
beast::Journal j,
EnforceSupplyCap enforceSupplyCap = EnforceSupplyCap::Yes);

/**
* Calls static accountSendIOU if saAmount represents Issue.
Expand Down
109 changes: 109 additions & 0 deletions include/xrpl/ledger/helpers/TokenIssuanceHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#pragma once

#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>

#include <cstdint>
#include <optional>

namespace xrpl {

/**
* ceil(value * 10^scale), exact.
*
* The shift is a pure exponent adjustment; the +1 for a fractional
* positive value is exact because a fractional Number is < 10^16.
* Truncation toward zero already is the ceiling for negative values.
*/
[[nodiscard]] Number
tokenScaledCeil(Number const& value, std::uint8_t scale);

/**
* floor(value * 10^scale) as integer base units; nullopt if it does not
* fit in a non-negative int64.
*/
[[nodiscard]] std::optional<std::int64_t>
tokenBaseUnits(Number const& value, std::uint8_t scale);

/**
* True if the issuance violates
* ceil(IssuedAmount * 10^TokenScale) + MPT OutstandingAmount > MaximumAmount.
* Always false for an uncapped issuance.
*/
[[nodiscard]] bool
tokenSupplyExceeded(ReadView const& view, SLE::const_ref sleIssuance);

/**
* Controls whether an IOU credit hard-fails when a capped TokenIssuance
* would exceed its MaximumAmount. Flow-engine steps pass No: their send
* results are advisory (return values ignored, reverse-pass execution can
* legitimately overshoot transiently) and the supply-cap invariant checker
* gates the final state instead.
*/
enum class EnforceSupplyCap : bool { No = false, Yes = true };

/**
* Maintain IssuedAmount on the issuer's TokenIssuance for a trust-line
* balance move of `amount` from `sender` to `receiver`. Only the amount's
* issuer is adjusted: balance moving away from the issuer increases its
* net issuance, balance returning decreases it.
*
* No-op when the amendment is disabled, neither party is the issuer, or no
* TokenIssuance exists. Returns tecSUPPLY_EXCEEDED when the cap is enforced
* and a capped issuer would exceed MaximumAmount.
*/
[[nodiscard]] TER
adjustTokenIssuance(
ApplyView& view,
AccountID const& sender,
AccountID const& receiver,
STAmount const& amount,
EnforceSupplyCap enforceCap,
beast::Journal j);

/**
* Remaining IOU the issuer can issue under the supply cap, floored to the
* representable amount; nullopt when there is no TokenIssuance or no cap.
*/
[[nodiscard]] std::optional<STAmount>
tokenIssuanceHeadroom(ReadView const& view, Issue const& issue);

/**
* True when the issue's TokenIssuance carries the per-currency lock
* (lsfTokenLocked). The per-currency analog of lsfGlobalFreeze.
*/
[[nodiscard]] bool
isTokenLocked(ReadView const& view, Issue const& issue);

/**
* For an MPT issuance bound to a capped TokenIssuance: the additional MPT
* base units that can be minted under the shared cap
* (MaximumAmount - OutstandingAmount - ceil(IssuedAmount * 10^TokenScale)).
* nullopt when unbound or uncapped.
*/
[[nodiscard]] std::optional<std::int64_t>
mptBoundHeadroom(ReadView const& view, SLE::const_ref sleMptIssuance);

/**
* Validate binding an MPTokenIssuance to a TokenIssuance: the MPT issuance
* exists, is issued by `account`, is not already bound, and its
* MaximumAmount/AssetScale equal the TokenIssuance's MaximumAmount/
* TokenScale.
*/
[[nodiscard]] TER
validateTokenBinding(
ReadView const& view,
MPTID const& mptId,
AccountID const& account,
std::optional<std::uint64_t> const& maximumAmount,
std::uint8_t tokenScale);

} // namespace xrpl
9 changes: 9 additions & 0 deletions include/xrpl/protocol/Indexes.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,15 @@ mptokenIssuance(uint256 const& issuanceKey)
return {ltMPTOKEN_ISSUANCE, issuanceKey};
}

Keylet
tokenIssuance(AccountID const& issuer, Currency const& currency) noexcept;

inline Keylet
tokenIssuance(uint256 const& key)
{
return {ltTOKEN_ISSUANCE, key};
}

Keylet
mptoken(MPTID const& issuanceID, AccountID const& holder) noexcept;

Expand Down
8 changes: 7 additions & 1 deletion include/xrpl/protocol/LedgerFormats.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,13 @@ enum LedgerEntryType : std::uint16_t {
\
LEDGER_OBJECT(Sponsorship, \
LSF_FLAG(lsfSponsorshipRequireSignForFee, 0x00010000) \
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000))
LSF_FLAG(lsfSponsorshipRequireSignForReserve, 0x00020000)) \
\
LEDGER_OBJECT(TokenIssuance, \
LSF_FLAG(lsfTokenLocked, 0x00000001) /* True, per-currency global freeze */ \
LSF_FLAG(lsfTokenCannotLock, 0x00000002) /* True, issuer renounced the per-currency lock */ \
LSF_FLAG(lsfTokenWrapped, 0x00000004) /* True, owned by a pseudo-account (blackholed issuer) */ \
LSF_FLAG(lsfTokenVerifiedSupply, 0x00000008)) /* True, IssuedAmount reflects verified legacy supply */

// clang-format on

Expand Down
14 changes: 14 additions & 0 deletions include/xrpl/protocol/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,20 @@ constexpr std::uint32_t kMaxInvestmentPeriod = std::chrono::seconds{std::chrono:
*/
constexpr std::uint8_t kMaxAssetCheckDepth = 5;

/**
* Maximum MaximumAmount of a TokenIssuance, in base units (10^15). Bounded
* to what Number/STAmount arithmetic represents exactly, so the supply-cap
* comparison can never be affected by mantissa rounding.
*/
constexpr std::uint64_t kMaxTokenIssuanceAmount = 1'000'000'000'000'000ull;
static_assert(kMaxTokenIssuanceAmount <= kMaxMpTokenAmount);

/**
* Maximum TokenScale of a TokenIssuance: 10^scale must stay in exact int64
* range. 10^19 > 2^63-1 > 10^18
*/
constexpr std::uint8_t kMaxTokenIssuanceScale = 18;

/**
* A ledger index.
*/
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,
tecSUPPLY_EXCEEDED = 203,
};

//------------------------------------------------------------------------------
Expand Down
10 changes: 10 additions & 0 deletions include/xrpl/protocol/TxFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal;
TF_FLAG(tfMPTSetCanHoldConfidentialBalance, 0x00000100), \
MASK_ADJ(0)) \
\
TRANSACTION(TokenIssuanceCreate, \
TF_FLAG(tfTokenCannotLock, 0x00000001), \
MASK_ADJ(0)) \
\
TRANSACTION(TokenIssuanceSet, \
TF_FLAG2(tfTokenCannotLock, 0x00000001) \
TF_FLAG(tfTokenLock, 0x00000002) \
TF_FLAG(tfTokenUnlock, 0x00000004), \
MASK_ADJ(0)) \
\
TRANSACTION(NFTokenCreateOffer, \
TF_FLAG(tfSellNFToken, 0x00000001), \
MASK_ADJ(0)) \
Expand Down
40 changes: 21 additions & 19 deletions include/xrpl/protocol/TxSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,27 @@ enum class Delegation { Delegable, NotDelegable };
* TxSettings::privileges) and enforced in InvariantCheck.cpp.
*/
enum class Privilege : std::uint16_t {
NoPriv = 0x0000, // The transaction can not do any of the enumerated operations
CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object.
CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account,
// which implies createAcct
MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object
MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT
// object, but does not have to
OverrideFreeze = 0x0010, // The transaction can override some freeze rules
ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT
CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance
DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance
MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT
// object (except by issuer)
MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT
// object (except by issuer)
MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create.
MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault
MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault
MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer.
NoPriv = 0x0000, // The transaction can not do any of the enumerated operations
CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object.
CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account,
// which implies createAcct
MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object
MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT
// object, but does not have to
OverrideFreeze = 0x0010, // The transaction can override some freeze rules
ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT
CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance
DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance
MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT
// object (except by issuer)
MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT
// object (except by issuer)
MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create.
MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault
MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault
MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer.
CreateTokenIssuance = 0x4000, // The transaction can create a new token issuance
DestroyTokenIssuance = 0x8000, // The transaction can destroy a token issuance
};

// The inner static_cast is not redundant: the underlying type is narrower than
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(TokenIssuance, Supported::No, 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
18 changes: 18 additions & 0 deletions include/xrpl/protocol/detail/ledger_entries.macro
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfIssuerKeyEpoch, SoeOptional},
{sfAuditorKeyEpoch, SoeOptional},
{sfConfidentialOutstandingAmount, SoeDefault},
{sfTokenIssuanceID, SoeOptional},
}))

/** A ledger object which tracks MPToken
Expand Down Expand Up @@ -649,5 +650,22 @@ LEDGER_ENTRY(ltSPONSORSHIP, 0x0090, Sponsorship, sponsorship, ({
{sfSponseeNode, SoeRequired},
}))

/** A ledger object which describes an IOU token issuance.
\sa keylet::tokenIssuance
*/
LEDGER_ENTRY(ltTOKEN_ISSUANCE, 0x0091, TokenIssuance, token_issuance, ({
{sfIssuer, SoeRequired},
{sfCurrency, SoeRequired},
{sfMaximumAmount, SoeOptional},
{sfIssuedAmount, SoeDefault},
{sfTokenScale, SoeDefault},
{sfMPTokenIssuanceID, SoeOptional},
{sfTransferFee, SoeOptional},
{sfMPTokenMetadata, SoeOptional},
{sfOwnerNode, SoeRequired},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
}))

#undef EXPAND
#undef LEDGER_ENTRY_DUPLICATE
4 changes: 4 additions & 0 deletions include/xrpl/protocol/detail/sfields.macro
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ TYPED_SFIELD(sfTransactionResult, UINT8, 3)
TYPED_SFIELD(sfScale, UINT8, 4)
TYPED_SFIELD(sfAssetScale, UINT8, 5)
TYPED_SFIELD(sfLEVersion, UINT8, 6)
TYPED_SFIELD(sfTokenScale, UINT8, 7)

// 8-bit integers (uncommon)
TYPED_SFIELD(sfTickSize, UINT8, 16)
Expand Down Expand Up @@ -220,6 +221,7 @@ TYPED_SFIELD(sfLoanID, UINT256, 38)
TYPED_SFIELD(sfReferenceHolding, UINT256, 39)
TYPED_SFIELD(sfBlindingFactor, UINT256, 40)
TYPED_SFIELD(sfObjectID, UINT256, 41)
TYPED_SFIELD(sfTokenIssuanceID, UINT256, 42)

// number (common)
TYPED_SFIELD(sfNumber, NUMBER, 1)
Expand All @@ -239,6 +241,7 @@ TYPED_SFIELD(sfPrincipalRequested, NUMBER, 14)
TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15, SField::kSmdNeedsAsset | SField::kSmdDefault)
TYPED_SFIELD(sfPeriodicPayment, NUMBER, 16)
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17, SField::kSmdNeedsAsset | SField::kSmdDefault)
TYPED_SFIELD(sfIssuedAmount, NUMBER, 18, SField::kSmdNeedsAsset | SField::kSmdDefault)

// 32-bit signed (common)
TYPED_SFIELD(sfLoanScale, INT32, 1)
Expand Down Expand Up @@ -374,6 +377,7 @@ UNTYPED_SFIELD(sfPaths, PATHSET, 1)
// currency
TYPED_SFIELD(sfBaseAsset, CURRENCY, 1)
TYPED_SFIELD(sfQuoteAsset, CURRENCY, 2)
TYPED_SFIELD(sfCurrency, CURRENCY, 3)

// issue
TYPED_SFIELD(sfLockingChainIssue, ISSUE, 1)
Expand Down
Loading