0033 XLS-33d: Compact Fungible Tokens (CFT) #82
Replies: 9 comments 14 replies
-
Thanks @sappenin and @nbougalis! Before reading all details I skimmed through & have a first remark/item I'd like more people to comment on. It's about the name. If we want to make it clear(er) for people outside of the XRPL ecosystem that the XRPL is a network that can handle issued tokens, I think it's best not to add specific XRPL token type references to the transaction names. (Referring to the C, compact) To us (existing XRPL devs, techies) it's compact, because we know the XRPL and current Trust Lines and IOU's New people don't, or don't even know the XRPL could even deal with tokens (that's a problem throughout the XRPL's history, people don't know what it can do for a long time) So instead of CFTokenPage, CFTokenIssuanceCreate, wouldn't it be much easier to understand for new XRPL users if it was just "IssueToken" and "TokenPage"? As there's already a distinction between the two as one is called token and the other TrustLine+IOU? |
Beta Was this translation helpful? Give feedback.
-
It appears that CFTokenIssuanceFreeze is also represented as a CFTokenIssuanceSet. |
Beta Was this translation helpful? Give feedback.
-
One thing with the NFT-related transactions that we should consider before implementing this feature is the size of metadata. With NFToken-related transactions, any change to an NFTokenPage object results in pretty much the entire (large) page being listed in both the "PreviousFields" and the "FinalFields" sections of the metadata (see XRPLF/rippled#4333 for details & examples). CFTs have the potential to change even more often, so we should make sure to do some optimization there so that it doesn't cause a massive increase in the size of transaction metadata and the amount of space a given amount of transaction history occupies (both in RAM and on disk). |
Beta Was this translation helpful? Give feedback.
-
In a |
Beta Was this translation helpful? Give feedback.
-
Have we considered what a clawback capability might look like with CFTs? I suspect some ideas on clawback of IOU might translate over to CFT as well |
Beta Was this translation helpful? Give feedback.
-
Implementation wise, if we are going to incorporating CFT into |
Beta Was this translation helpful? Give feedback.
-
have we thought about disabling freeze by default? so instead of David S. said that there were lots of criticisms around how freeze is enabled by default for IOUs. I don't have an opinion, just a thought. |
Beta Was this translation helpful? Give feedback.
-
Hello group: per the new XLS Contributing process, it is my opinion that we have reach a "well-refined standard." As such, I propose that we move this discussion to a file (via this PR) and work on final changes using additional PRs, for better change-tracking. For example -- there are some Clawback changes that either I or @ledhed2222 will propose to the spec once it's in file-form (so we can better track proposals and changes). Please comment here if you would like to object to moving this spec/discussion forward in the process into a (Note that per the Contributing guidelines, moving a spec into the DRAFT state does not mean any kind of endorsement, nor does it mean that this specification will become adopted. It is solely meant as a mechanism to enable better change tracking using PRs.) |
Beta Was this translation helpful? Give feedback.
-
As noted by @sappenin , this has been moved here: XLS-33d: Compact Fungible Tokens (CFTs). Closing this discussion in order to focus future comments on that PR. |
Beta Was this translation helpful? Give feedback.
-
See the updated version of XLS-33d
A previous version of the spec follows:
1. Compact Fungible Tokens (CFTs)
1.1. Abstract
Trustlines are a fundamental building block for a variety of XRP Ledger tokenization features, including CBDCs and fiat-collateralized stablecoins. However, as more and more token issuers embrace the XRP Ledger, the current size of each trustline will become an impediment to ledger stability and accessibility.
This proposal introduces extensions to the XRP Ledger to support a more compact fungible token (i.e., CFT) type, along with operations to enumerate, purchase, sell and hold such tokens.
Unlike Trustlines, CFTs do not represent bidirectional debt relationships. Instead, CFTs function more like a unidirectional trustline with only one balance, making it simpler to support common tokenization requirements, including even non-monetery use-cases such as tracking reputation points in an online game.
Perhaps as important, however, CFTs require significantly less space than trustlines: ~52 bytes for each CFT held by a token holder, as compared to at least 234 bytes for every new trustline (see Appendix 1 for a more detailed comparison).
1.1.1. Advantages and Disadvantages
Advantages
A+B=A
for non-zeroB
or(A+B)+C != A+(B+C)
).Disadvantages
1.1.2. Assumptions
This proposal makes a variety of assumptions, based upon observations of existing trustline usage in order to produce the most compact representations of data. These assumptions include:
1.2. Creating Compact Fungible Tokens
1.2.1. On-Ledger Data Structures
We propose two new objects and one new ledger structure:
CFTokenIssuance
is a new object that describes a fungible token issuance created by an issuer.CFToken
is a new object that describes a single account's holdings of an issued token.CFTokenPage
is a ledger structure that contains a set ofCFToken
objects owned by the same token holder.1.2.1.1. The
CFTokenIssuance
objectThe
CFTokenIssuance
object represents a single CFT issuance and holds data associated with the issuance itself. Token issuances are created using theCFTokenIssuanceCreate
transaction and can, optionally, be destroyed by theCFTokenIssuanceDestroy
transaction.1.2.1.1.1.
CFTokenIssuance
Ledger IdentifierThe ID of an CFTokenIssuance object, a.k.a
CFTokenIssuanceID
is the result of SHA512-Half of the following values, concatenated in order:1.2.1.1.2. Fields
CFTokenIssuance
objects are stored in the ledger and tracked in an Owner Directory owned by the issuer. Issuances have the following required and optional fields:LedgerEntryType
number
UINT16
Flags
number
UINT32
Issuer
string
ACCOUNTID
AssetCode
string
UINT160
AssetScale
number
UINT8
MaximumAmount
string
UINT64
OutstandingAmount
string
UINT64
LockedAmount
string
UINT64
TransferFee
number
UINT16
Metadata
string
BLOB
OwnerNode
number
UINT64
1.2.1.1.2.1.
LedgerEntryType
The value 0x007E, mapped to the string
CFTokenIssuance
, indicates that this object describes a Compact Fungible Token (CFT).1.2.1.1.2.2.
Flags
A set of flags indicating properties or other options associated with this
CFTokenIssuance
object. The type specific flags proposed are:lsfFrozen
0x0001
lsfCannotFreezeBalances
0x0002
lsfRequiresAuthorization
0x0004
lsfCanEscrow
0x0008
lsfCanTrade
0x0010
lsfTransferable
0x0020
With the exception of the
lsfFrozen
flag, which can be mutated via the**CFTokenIssuanceSet**
transactions, these flags are immutable: they can only be set during theCFTokenIssuanceCreate
transaction and cannot be changed later.1.2.1.1.2.3.
Issuer
The address of the account that controls both the issuance amounts and characteristics of a particular fungible token.
1.2.1.1.2.4.
AssetCode
A 160-bit blob of data. It is reccommended to use only upper-case ASCII letters in addition to the ASCII digits 0 through 9, the dot (
.
) character and the dash (-
) chracter (dots and dashes should never be the first character of an asset code, and should not be repeated in sequence in any asset code).While it's possible to store any arbitrary data in this field, implementations that detect the above reccommended character conformance should display them as ASCII for human readability, allowing issuers to support well-known ISO-4207 currency codes in addition to custom codes. This also helps prevents spoofing attacks where a homoglyph might be used to trick a person into using the wrong asset code.
1.2.1.1.2.5.
AssetScale
An asset scale is the difference, in orders of magnitude, between a standard unit and a corresponding fractional unit. More formally, the asset scale is a non-negative integer (0, 1, 2, …) such that one standard unit equals 10^(-scale) of a corresponding fractional unit. If the fractional unit equals the standard unit, then the asset scale is 0.
1.2.1.1.2.6.
MaximumAmount
This value is an unsigned number that specifies the maximum number of CFTs that can be distributed to non-issuing accounts (i.e.,
minted
). For issuances that do not have a maximum limit, this value should be set to 0xFFFFFFFFFFFFFFFF.1.2.1.1.2.7.
OutstandingAmount
Specifies the sum of all token amounts that have been minted to all token holders. This value can be stored on ledger as a
default
type so that when it's value is 0, it takes up less space on ledger. This value is increased whenever an issuer pays CFTs to a non-issuer account, and decreased whenever a non-issuer pays CFTs into the issuing account.1.2.1.1.2.8.
TransferFee
This value specifies the fee, in tenths of a basis point, charged by the issuer for secondary sales of the token, if such sales are allowed at all. Valid values for this field are between 0 and 50,000 inclusive. A value of 1 is equivalent to 1/10 of a basis point or 0.001%, allowing transfer rates between 0% and 50%. A
TransferFee
of 50,000 corresponds to 50%. The default value for this field is 0.1.2.1.1.2.9.
OwnerNode
Identifies the page in the owner's directory where this item is referenced.
1.2.1.1.2. Example
CFTokenIssuance
JSON1.2.1.1.3. How do
CFTokenIssuance
objects work?Any account may issue up to 32 Compact Fungible Tokens, but each issuance must have a different
AssetCode
.1.2.1.1.3.1. Searching for a
CFTokenIssuance
objectCFT Issuances are uniquely identified by a combination of a type-specific prefix, the isser address and an asset code. To locate a specific
CFTokenIssuance
, the first step is to locate the owner directory for the issuer. Then, find the directory that holdsCFTokenIssuance
ledger objects and iterate through each entry to find the instance with the desiredAssetCode
. If that entry does not exist then theCFTokenIssuance
does not exist for the given account.1.2.1.1.3.2. Adding a
CFTokenIssuance
objectA
CFTokenIssuance
object can be added by using the same approach to find theCFTokenIssuance
, and adding it to that directory. If, after addition, the number of CFTs in the directory would exceed 32, then the operation must fail.1.2.1.1.3.3. Removing a
CFTokenIssuance
objectA
CFTokenIssuance
can be removed using the same approach, but only if theCurMintedAmount
is equal to 0.1.2.1.1.3.4. Reserve for
CFTokenIssuance
objectEach
CFTokenIssuance
costs an incremental reserve to the owner account. This specification allows up to 32CFTokenIssuance
entries per account.1.2.1.2. The
CFToken
objectThe
CFToken
object represents an amount of a token held by an account that is not the token issuer. CFTs are acquired via ordinary Payment or DEX transactions, and can optionally be redeemed or exchanged using these same types of transactions.1.2.1.2.1 Fields
A
CFToken
object can have the following required and optional fields. Notice that, unlike other objects, no field is needed to identify the object type or current owner of the object, because CFT holdings are grouped into pages that implicitly define the object type and identify the holder.CFTIssuanceID
string
UINT256
Amount
string
UINT64
LockedAmount
string
UINT64
Flags
number
UINT32
1.2.1.2.1.1.
CFTIssuanceID
The
CFTokenIssuance
identifier.1.2.1.2.1.2.
Amount
This value specifies a positive amount of tokens currently held by the owner. Valid values for this field are between 0x0 and 0xFFFFFFFFFFFFFFFF.
1.2.1.2.1.3.
LockedAmount
This value specifies a positive amount of tokens that are currently held in a token holder's account but that are unavailable to be used by the token holder. Locked tokens might, for example, represent value currently being held in escrow, or value that is otherwise inaccessible to the token holder for some other reason, such as an account freeze.
This value is stored as a
default
value such that it's initial value is0
, in order to save space on the ledger for a an empty CFT holding.1.2.1.2.1.4.
Flags
A set of flags indicating properties or other options associated with this
CFTokenIssuance
object. The type specific flags proposed are:lsfFrozen
0x0001
LockedAmount
must equal theAmount
value.1.2.1.2.2. Example CFToken JSON
1.2.1.3. The
CFTokenPage
ledger entryThis object represents a collection of
CFToken
objects owned by the same account. It is important to note that theCFToken
objects themselves reside within this page, instead of in a dedicated object entry in theSHAMap
. An account can have multipleCFTokenPage
ledger objects, which form a doubly-linked list (DLL).In the interest of minimizing the size of a page and optimizing storage, the
Owner
field is not present since it is encoded as part of the object's ledger identifier (more details in theCFTokenPageID
discussion below).1.2.1.3.1 Fields
A
CFTokenPage
object may have the following required and optional fields:LedgerEntryType
string
UINT16
PreviousPageMin
string
UINT256
NextPageMin
string
UINT256
PreviousTxnID
string
HASH256
PreviousTxnLgrSeq
number
UINT32
CFTokens
object
TOKEN
1.2.1.3.1.1.
**LedgerEntryType**
Identifies the type of ledger object. This proposal recommends the value
0x007F
as the reserved ledger entry type.1.2.1.3.1.2.
**PreviousPageMin**
The locator of the previous page, if any. Details about this field and how it should be used are outlined below, after the construction of the
CFTokenPageID
is explained.1.2.1.3.1.3.
**NextPageMin**
The locator of the next page, if any. Details about this field and how it should be used are outlined below, after the construction of the
CFTokenPageID
is explained.1.2.1.3.1.4.
**PreviousTxnID**
Identifies the transaction ID of the transaction that most recently modified this
CFTokenPage
object.1.2.1.3.1.5.
**PreviousTxnLgrSeq**
The sequence of the ledger that contains the transaction that most recently modified this
CFTokenPage
object.1.2.1.3.1.6.
**CFTokens**
The collection of
CFToken
objects contained in thisCFTokenPage
object. This specification places an upper bound of 32CFToken
objects per page. Objects should be stored in sorted order, from low to high with the low order 96 bits of theTokenID
used as the sorting parameter.1.2.1.3.2. CFTokenPage ID Format
Unlike other object identifiers on the XRP Ledger, which are derived by hashing a collection of data using
SHA512-Half
,CFTokenPage
identifiers are constructed so as to specfically allow for the adoption of a more efficient paging structure, designed to enable user accounts to efficiently hold many CFTs at the same time.To that end, a unique CFTokenPage ID (a.k.a.,
CFTokenPageID
) is derived by concatenating a 196-bit value that uniquely identifies a particular account's holdings of CFT, followed by a 64-bit value that uniquely identifies a particular CFT issuance. Using this construction enables efficient lookups of individualCFTokenPage
objects without requiring iteration of the doubly-linked list of all CFTokenPages.More formally, we assume:
high196(x)
returns the "high" 196 bits of a 256-bit value.low64(x)
returns the "low" 64-bits of a 256-bit value.CFTokenIssuanceID
uniqely identifies a CFT Issuance as defined above in ["CFTokenIssuance Ledger Identifier"].CFTokenHolderID
uniquely identifies a holder of some amount of CFT (as opposed to other token types such as an NFT) and is defined as the result of SHA512-Half of the following values, concatenated in order:CFTokenIssuance
ledger identifier key (0x007E).AccountID
of the CFT holder.Therefore:
CFTokenPageID
equalhigh196(CFTokenHolderId)
concatenated withlow64(CFTokenIssuanceId)
.CFTokenIssuanceID
A
only be included in a page withCFTokenPageId
B
if and only iflow64(A) >= low64(B)
.1.2.1.3.3. Example CFTokenPage JSON
1.2.1.3.4. How do
CFTokenPage
objects work?The page within which a
CFToken
entry is stored will be formed as described above. This is needed to find the correct starting point in the doubly-linked list ofCFTokenPage
objects if that list is large. This is because it is inefficient to have to traverse the list from the beginning if an account holds thousands ofCFToken
objects in hundreds ofCFTokenPage
objects.1.2.1.3.4.1. Searching a
CFToken
objectTo search for a specific
CFToken
, the first step is to locate theCFTokenPage
, if any, that should contain thatCFToken
. For that do the following:Compute the
CFTokenPageID
using the account of the owner and theCFTIssuanceID
of the token, as described above. Then search for the ledger entry whose identifier is less than or equal to that value. If that entry does not exist or is not aCFTokenPage
, theCFToken
is not held by the given account.1.2.1.3.4.2. Adding a
CFToken
objectA
CFToken
object can be added by using the same approach to find theCFTokenPage
it should be in and adding it to that page. If after addition the page overflows, find thenext
andprevious
pages (if any) and balance those three pages, inserting a new page as/if needed.1.2.1.3.4.2. Removing a
CFToken
objectA
CFToken
can be removed by using the same approach. If the number ofCFToken
in the page goes below a certain threshhold, an attempt will be made to consolidate the page with aprevious
or subsequent page and recover the reserve.1.2.1.3.4.3. Reserve for
CFTokenPage
objectEach
CFTokenPage
costs an incremental reserve to the owner account. This specification allows up to 32CFToken
entries per page, which means that for accounts that hold multiple CFTs the effective reserve cost per Fungible Token can be as low as R/32 where R is the incremental reserve.1.3 Transactions
This proposal introduces several new transactions to allow for the creation and deletion of CFT issuances. Likewise, this proposal introduce several new transactions for minting and redeeming discrete instances of CFTs. All transactions introduced by this proposal incorporate the common transaction fields that are shared by all transactions. Common fields are not documented in this proposal unless needed because this proposal introduces new possible values for such fields.
1.3.1 Transactions for Creating and Destroying Compact Fungible Token Issuances on XRPL
We define three transactions related to CFT Issuances:
CFTokenIssuanceCreate
andCFTokenIssuanceDestroy
andCFTokenIssuanceSet
for minting, destroying, and updating CFT Issuances respectively on XRPL.1.3.1.1 The
CFTokenIssuanceCreate
transactionThe
CFTokenIssuanceCreate
transaction creates anCFTokenIssuance
object and adds it to the relevant directory node of thecreator
. This transaction is the only opportunity anissuer
has to specify any token fields that are defined as immutable (e.g., CFT Flags).If the transaction is successful, the newly created token will be owned by the account (the
creator
account) which executed the transaction.1.3.1.1.1 Transaction-specific Fields
TransactionType
object
UINT16
Indicates the new transaction type
CFTokenIssuanceCreate
. The integer value is25 (TODO)
.AssetCode
string
BLOB
A 160-bit blob of data. It is reccommended to use only upper-case ASCII letters in addition to the ASCII digits 0 through 9. While it's possible to store any arbitrary data in this field, implementations that detect the above reccommended characters should display them as ASCII for human readability. This also helps prevents spoofing attacks where a homoglyph might be used to trick a person into using the wrong asset code.
AssetScale
number
UINT8
An asset scale is the difference, in orders of magnitude, between a standard unit and a corresponding fractional unit. More formally, the asset scale is a non-negative integer (0, 1, 2, …) such that one standard unit equals 10^(-scale) of a corresponding fractional unit. If the fractional unit equals the standard unit, then the asset scale is 0.
Flags
number
UINT16
Specifies the flags for this transaction. In addition to the universal transaction flags that are applicable to all transactions (e.g.,
tfFullyCanonicalSig
), the following transaction-specific flags are defined and used to set the appropriate fields in the Fungible Token:lsfFrozen
0x0001
lsfCannotFreezeBalances
0x0002
lsfRequiresAuthorization
0x0004
lsfCanEscrow
0x0008
lsfCanTrade
0x0010
lsfTransferable
0x0020
TransferFee
number
UINT16
The value specifies the fee to charged by the issuer for secondary sales of the Token, if such sales are allowed. Valid values for this field are between 0 and 50,000 inclusive, allowing transfer rates of between 0.000% and 50.000% in increments of 0.001.
The field MUST NOT be present if the
tfTransferable
flag is not set. If it is, the transaction should fail and a fee should be claimed.MaximumAmount
string
UINT64
The maximum asset amount of this token that should ever be issued.
Metadata
string
BLOB
Arbitrary metadata about this issuance, in hex format.
1.3.1.1.2 Example
CFTokenIssuanceCreate
transactionThis transaction assumes that the issuer of the token is the signer of the transaction.
1.3.2 The
CFTokenIssuanceDestroy
transactionThe
CFTokenIssuanceDestroy
transaction is used to remove anCFTokenIssuance
object from the directory node in which it is being held, effectively removing the token from the ledger ("destroying" it).If this operation succeeds, the corresponding
CFTokenIssuance
is removed and the owner’s reserve requirement is reduced by one.1.3.2.1 Transaction-specific Fields
TransactionType
string
UINT16
Indicates the new transaction type
CFTokenIssuanceDestroy
. The integer value is26
(TODO).CFTokenIssuance
string
UINT256
Identifies the
CFTokenIssuance
object to be removed by the transaction.1.3.2.2 Example
CFTokenIssuanceDestroy
JSON1.3.3 The
CFTokenIssuanceSet
Transaction1.3.3.1 CFTokenIssuanceSet
TransactionType
object
UINT16
Indicates the new transaction type
CFTokenIssuanceSet
. The integer value is28 (TODO)
.CFTokenIssuance
string
UINT256
The
CFTokenIssuance
identifier.Account
string
ACCOUNTID
An optional XRPL Address of an individual token holder balance to freeze/unfreeze. If omitted, this transaction will apply to all any accounts holding CFTs.
Flag
string
UINT64
1.3.3.2 Example
CFTokenIssuanceSet
JSON1.3.3.1.1 CFTokenSet Flags
Transactions of the
CFTokenFreeze
type support additional values in the Flags field, as follows:tfSetFreeze
0x0001
tfSetUnFreeze
0x0002
Appendix 1: Current Trustline Storage Requirements
As described in issue #3866, the size of a RippleState object is anywhere from 234 to 250 bytes plus a minimum of 32 bytes for object owner tracking, described in more detail here:
TODO: Validate these numbers as they may be slightly low for truslintes. For example, in addition to the above data, trustlines require two directory entries for low/high nodes that get created for each trustline (i.e., for each RippleState object). This creates two root objects, each 98 bytes, adding 196 bytes in total per unique issuer/holder. Conversely, for CFTs, the page structure allows for up to 32 issuances to be held by a single token holder while only incurring around 102 bytes for a CFTokenPage. Thus, every time an account wants to hold a new token, the current Trustline implementation would require at least 430 bytes every time. If we imagine a single account holding 20 tokens, CFTs would require ~1040 bytes, whereas trustlines would require ~8,600 bytes!
Potential Future Directions
This specification does not currently enable nor address how CFTs would interact with the DEX, or be addded to any other ledger objects like escrows, payment channels, checks, or any forthcoming AMM specification. However, support for CFTs with these objects should be considered (e.g., perhaps by adapting this proposal to CFTs).
Implementation Notes
At present, there is no
CFTRedeem
transaction because holders of a CFT can use a normal payment transaction to send CFT back to the issuer, thus "redeeming" CFT and removing it from circulation. Note that in order for this to work, issuer accounts may not hold CFT.For CFTokenIssuances that have the
lsfRequiresAuthorization
flag set, it is envisioned that a DepositPreauth transaction could be used with minor adaptations to distinguish between pre-authorized trustlines and pre-authorized CFTs. Alternatively, we might consider deposit_preauth objects might apply to both, under the assumption that a single issuer restricting trustlines will want to make the same restrictions around CFTs emanating from the same issuer account.Beta Was this translation helpful? Give feedback.
All reactions