Skip to content

Commit b915db7

Browse files
authored
Merge branch 'master' into xls-51d
2 parents 68d9681 + 7ce783a commit b915db7

File tree

2 files changed

+341
-0
lines changed

2 files changed

+341
-0
lines changed

XLS-0016-nft-metadata/README.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<pre>
2+
xls: 16
3+
title: NFT Metadata
4+
author: Hubert Getrouw (@HubertG97)
5+
created: 2021-03-17
6+
status: Stagnant
7+
category: Community
8+
</pre>
9+
10+
In addition to @WietseWind 's [XLS-14d](https://github.com/XRPLF/XRPL-Standards/discussions/30) and @RichardAH 's proposal [XLS-15d](https://github.com/XRPLF/XRPL-Standards/discussions/34) here is a proposal to create a standard for the creation of metadata for the tokens created with a CTI in the currency code.
11+
12+
When issuing an indivisible token on the XRPL the only data given is the currency code. For optimal usage, there has to be more metadata for an NFT. For example a description and a URI to an IPFS file.
13+
Using the Concise Transaction Identifier, a prior transaction can be used to mark the metadata contained in the memo's field for the use of the NFT.
14+
15+
The [Memos Field](https://xrpl.org/transaction-common-fields.html#memos-field) in an XRPL transaction is presented as an array of objects which contains one memo field per object. This field consists of the following fields:
16+
17+
- MemoData
18+
- MemoFormat
19+
- MemoType
20+
21+
The MemoData field can be used for the metadata itself and the MemoFormat indicates the nature of the given data inside the MemoData field (MIME type). To create a certain hierarchy for covering failure of the URI specified, the MemoType field contains the numbering of the data named as shown below followed by the MIME type:
22+
23+
- NFT/0 - _Description_ - `text/plain`
24+
- NFT/1 - _Author_ - `text/plain`
25+
- NFT/2 - _Primary URI_ - `text/uri`
26+
- NFT/3 - _Back-up URI_ - `text/uri`
27+
- NFT/4 - _Reduced image Data URI as last back-up_ - `text/uri`
28+
29+
The usage of a back-up URI and Data URI can be seen as optional and can be replaced with other kinds of data that have the preference of the issuer of the NFT for example contact information.
30+
The limit of storage is 1kb of data in the memo's field in total. Multiple memos can be used to give as much information as fits to the 1kb of data.
31+
32+
If there is only one memo on the CTI referenced transaction and the memo data contains a URI of any sort then this is deemed to be the NFT content. The multiple memo's structure will be the advanced method to issue NFTs. The standard will also be compatible with previously created NFTs referred to as the simple method.
33+
34+
---
35+
36+
**Issuing**
37+
38+
For the metadata, there has to be created a transaction from the same address as the issuer of the NFT to for example a hot wallet. This transaction of 1 drop contains the description and URIs needed for the NFT.
39+
40+
The currency code for an NFT consists of 3 parts:
41+
42+
- Prefix 02 for HEX currency code
43+
- [CTI](https://github.com/XRPLF/XRPL-Standards/discussions/34) (Concise Transaction Identifier)
44+
- Short name converted to HEX for the NFT to a maximum of 12 characters or less (filled up with 0's if it's less)
45+
46+
After this, a Trust line can be set up using the above currency code and the NFTs being transferred from the issuing address to the hot wallet.
47+
48+
---
49+
50+
### Advanced method
51+
52+
_For example_
53+
54+
**Issuer:** `rBzoA1EXxE2FeGV4Z57pMGRuzd3dfKxVUt`
55+
**Hot wallet:** `rp9d3gds8bY7hkP8FmNqJZ1meMtYLtyPoz`
56+
57+
The JSON for the metadata transaction would look like this:
58+
59+
```
60+
{
61+
"Account": "rBzoA1EXxE2FeGV4Z57pMGRuzd3dfKxVUt",
62+
"TransactionType": "Payment",
63+
"Amount": "1",
64+
"Destination": "rp9d3gds8bY7hkP8FmNqJZ1meMtYLtyPoz",
65+
"Fee": "100000",
66+
"Memos": [{
67+
"Memo": {
68+
"MemoData": "546861742773206F6E6520736D616C6C20696D616765206F66206D6F6F6E2C206F6E65206769616E74206C65617020666F72204E4654206F6E20746865205852504C",
69+
"MemoFormat": "746578742F706C61696E",
70+
"MemoType": "6E66742F30"
71+
}
72+
},
73+
{
74+
"Memo": {
75+
"MemoData": "48756265727420476574726F7577",
76+
"MemoFormat": "746578742F706C61696E",
77+
"MemoType": "6E66742F31"
78+
}
79+
},
80+
{
81+
"Memo": {
82+
"MemoData": "697066733A2F2F62616679626569686561786B696A3276656D6B7337726B716E6F67367933367579793337626B33346D697533776F72636A756F6833747532773279",
83+
"MemoFormat": "746578742F757269",
84+
"MemoType": "6E66742F32"
85+
}
86+
},
87+
88+
{
89+
"Memo": {
90+
"MemoData": "68747470733A2F2F676574726F75772E636F6D2F696D672F707572706C656D6F6F6E2E706E67",
91+
"MemoFormat": "746578742F757269",
92+
"MemoType": "6E66742F33"
93+
}
94+
},
95+
{
96+
"Memo": {
97+
"MemoData": "646174613A696D6167652F6769663B6261736536342C52306C474F446C684641415541505141414141414142415145434167494441774D4542415146425155474267594842776348392F66342B506A352B666E362B7672372B2F76382F507A392F66332B2F76377741414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414143483542414541414241414C4141414141415541425141414157524943534F5A476D65614B7175624F74437A664D7551564D456937674D41494D554D684942554F4D784941454141544A736942674F4A674267654267456A476E413063673945524446464342364E4D6248305945684543454579784844414354316571383352486C69654D73474147414641414949436A495051776F4F66675A4A41544A5A5932414C4255634B535A516A684552424A41384355774F6750414B6649326445547156554A6A774166364345435352694F436F4D42416F6A6A61675149514137",
98+
"MemoFormat": "746578742F757269",
99+
"MemoType": "6E66742F34"
100+
}
101+
}]
102+
103+
104+
}
105+
```
106+
107+
Converted to human-readable output's [this](https://xrpcharts.ripple.com/#/transactions/24ADC5D0EF72DDA45A7464A7F74B762801FA12C26F6BFEEFC61CF72140623F27) transaction
108+
109+
Using this transaction's txn hash, txn_index, ledger_hash, and ledger_index creates a CTI of `23080995397183855`
110+
Converted to HEX it will be `52000B03B6296F`
111+
112+
The name of the NFT will be '_Purple moon_'.
113+
After conversing this to HEX the complete currency code looks like this `0252000B03B6296F507572706C65206D6F6F6E00`
114+
115+
**02** - _XRPL NFT identifier_
116+
**52000B03B6296F** - _CTI_
117+
**507572706C65206D6F6F6E00** - _NFT name_
118+
119+
When Issuing a token the same address has to be used as the sender of the aforementioned transaction.
120+
As explained in this [blogpost](https://coil.com/p/Huub/Introduction-to-NFT-on-the-XRP-Ledger/4ee41zWW-) a Trust line has to be created between the Issuer and the hot wallet.
121+
122+
Make sure the issuer address has an `AccountSet` of `SetFlag` to `8`
123+
124+
```
125+
{
126+
"TransactionType": "TrustSet",
127+
"Account": "rp9d3gds8bY7hkP8FmNqJZ1meMtYLtyPoz",
128+
"Fee": "12",
129+
"Flags" : 131072,
130+
"LimitAmount": {
131+
"currency": "0252000B03B6296F507572706C65206D6F6F6E00",
132+
"issuer": "rBzoA1EXxE2FeGV4Z57pMGRuzd3dfKxVUt",
133+
"value": "1000000000000000e-95"
134+
}
135+
}
136+
```
137+
138+
In the currency field the HEX converted currency code is used.
139+
The value is set to `1000000000000000e-95` which will result in 10 NFTs.
140+
More explanation about this can be found in @WietseWind's proposal [XLS-14d](https://github.com/XRPLF/XRPL-Standards/discussions/30)
141+
142+
Last step is to send the tokens from the issuer to the hot wallet.
143+
144+
```
145+
{
146+
"TransactionType": "Payment",
147+
"Account": "rBzoA1EXxE2FeGV4Z57pMGRuzd3dfKxVUt",
148+
"Fee": "12",
149+
"Destination" : "rp9d3gds8bY7hkP8FmNqJZ1meMtYLtyPoz",
150+
"Amount": {
151+
"currency": "0252000B03B6296F507572706C65206D6F6F6E00",
152+
"issuer": "rBzoA1EXxE2FeGV4Z57pMGRuzd3dfKxVUt",
153+
"value": "1000000000000000e-95"
154+
}
155+
}
156+
```
157+
158+
Now there are 10 Purple moon NFTs on address `rp9d3gds8bY7hkP8FmNqJZ1meMtYLtyPoz`
159+
160+
### Simple Method
161+
162+
The JSON for the metadata transaction would look like this:
163+
164+
```
165+
{
166+
"Account": "rBzoA1EXxE2FeGV4Z57pMGRuzd3dfKxVUt",
167+
"TransactionType": "Payment",
168+
"Amount": "1",
169+
"Destination": "rp9d3gds8bY7hkP8FmNqJZ1meMtYLtyPoz",
170+
"Fee": "100000",
171+
"Memos": [{
172+
"Memo": {
173+
"MemoData": "697066733A2F2F62616679626569666C6A667870786F7A6E6A703273357266697270666A756E7876706B71737863727133766C626F6C346536717A376F7972693571",
174+
"MemoFormat": "746578742F757269",
175+
"MemoType": "7872706C2F6E6674"
176+
}
177+
}]
178+
}
179+
```
180+
181+
Converted to human-readable output's [this](https://xrpcharts.ripple.com/#/transactions/7DFCD417FCEE35F7BB3ABECD05C27BA71F1E845BFD29C19AF3CF5E55B44EA55C) transaction
182+
183+
After that, a trust set and sending the tokens is the same as the advanced method

XLS-0017-xfl/README.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<pre>
2+
xls: 17
3+
title: XFL Developer-friendly representation of XRPL balances
4+
descrption: Introduces developer-friendly representation of XRPL balances.
5+
author: RichardAH (@RichardAH)
6+
created: 2021-03-19
7+
status: Final
8+
category: Protocol
9+
</pre>
10+
11+
# Background
12+
13+
The XRP ledger allows for two types of balances on accounts:
14+
15+
- Native `xrp` balances
16+
- IOU/Token `trustline` balances
17+
18+
Native balances are encoded and processed as signed 63bit integer values with an implicit decimal point at the 6th place from the right. A single unit is referred to as a drop. Thus the smallest possible value is `1 drop` represented logically as: `0.000001 xrp`.
19+
20+
Trustline balances are encoded and processed as a 63 bit decimal floating point number in the following format:
21+
{`sign bit` `8 bits of exponent` `54 bits of mantissa`}
22+
Note: This is not the IEEE floating point format (which is base 2.)
23+
24+
- The exponent is biased by 97. Thus an encoded exponent of `0b00000000` is `10^(-97)`.
25+
- The mantissa is normalised between `10^15` and `10^16 - 1`
26+
- The canonical zero of this format is all bits 0.
27+
28+
A 64th disambiguation bit is prepended (most significant bit) to both datatypes, which is `0` in the case of a native balance and `1` in the case of a trustline balance. [[1]](https://xrpl.org/serialization.html#amount-fields)
29+
30+
# Problem Definition
31+
32+
Performing computations between these two number formats can be difficult and error-prone for third party developers.
33+
34+
One existing partial solution is passing these amounts around as human-readable decimal numbers encoded as strings. This is computationally intensive and still does not allow numbers of one type to be mathematically operated on with numbers of the other type in a consistent and predictable way.
35+
36+
Internally the XRPL requires two independent types, however any xrp balance less than `10B` will translate without loss of precision into the trustline balance type. For amounts of xrp above `10B` (but less than `100B`) only 1 significant figure is lost from the least significant side. Thus in the worst possible case (moving >= `10B` XRP) `10` drops may be lost from an amount.
37+
38+
The benefits of representing xrp balances in the trustline balance format are:
39+
40+
- Much simpler developer experience (less mentally demanding, lower barrier to entry)
41+
- Can compute exchange rates between trustline balances and xrp
42+
- Can store all balance types in a single data type
43+
- A unified singular set of safe math routines which are much less likely to be used wrongly by developers
44+
45+
# XFL Format
46+
47+
The XFL format is designed for ease of use and maximum compatibility across a wide variety of processors and platforms.
48+
49+
For maximum ease of passing and returning from (pure) functions all XFL numbers are encoded into an `enclosing number` which is always a signed 64 bit integer, as follows:
50+
51+
Note: bit 63 is the most significant bit
52+
53+
- bit 63: `enclosing sign bit` always 0 for a valid XFL
54+
- bit 62: `internal sign bit` 0 = negative 1 = positive. note: this is not the int64_t's sign bit.
55+
- bit 61 - 53: `exponent` biased such that `0b000000000` = -97
56+
- bit 53 - 0: `mantissa` between `10^15` and `10^16 - 1`
57+
58+
Special case:
59+
60+
- Canonical zero: enclosing number = 0
61+
62+
Any XFL with a negative enclosing sign bit is `invalid`. This _DOES NOT_ refer to the internal sign bit inside the XFL format. It is definitely possible to have a negative value represented in an XFL, however these always exist with a _POSITIVE_ enclosing sign bit.
63+
64+
Invalid (negative enclosing sign bit) XFL values are reserved for propagation of error codes. If an invalid XFL is passed to an XFL processing function (for example `float_multiply`) it too should return an invalid XFL.
65+
66+
# Examples
67+
68+
| Number | Enclosing | To String |
69+
| ------ | ------------------- | ----------------------------- |
70+
| -1 | 1478180677777522688 | -1000000000000000 \* 10^(-15) |
71+
| 0 | 0000000000000000000 | [canonical zero] |
72+
| +1 | 6089866696204910592 | +1000000000000000 \* 10^(-15) |
73+
| +PI | 6092008288858500385 | +3141592653589793 \* 10^(-15) |
74+
| -PI | 1480322270431112481 | -3141592653589793 \* 10^(-15) |
75+
76+
# Reference Implementations
77+
78+
Javascript:
79+
80+
```js
81+
const minMantissa = 1000000000000000n;
82+
const maxMantissa = 9999999999999999n;
83+
const minExponent = -96;
84+
const maxExponent = 80;
85+
86+
function make_xfl(exponent, mantissa) {
87+
// convert types as needed
88+
if (typeof exponent != "bigint") exponent = BigInt(exponent);
89+
90+
if (typeof mantissa != "bigint") mantissa = BigInt(mantissa);
91+
92+
// canonical zero
93+
if (mantissa == 0n) return 0n;
94+
95+
// normalize
96+
let is_negative = mantissa < 0;
97+
if (is_negative) mantissa *= -1n;
98+
99+
while (mantissa > maxMantissa) {
100+
mantissa /= 10n;
101+
exponent++;
102+
}
103+
while (mantissa < minMantissa) {
104+
mantissa *= 10n;
105+
exponent--;
106+
}
107+
108+
// canonical zero on mantissa underflow
109+
if (mantissa == 0) return 0n;
110+
111+
// under and overflows
112+
if (exponent > maxExponent || exponent < minExponent) return -1; // note this is an "invalid" XFL used to propagate errors
113+
114+
exponent += 97n;
115+
116+
let xfl = !is_negative ? 1n : 0n;
117+
xfl <<= 8n;
118+
xfl |= BigInt(exponent);
119+
xfl <<= 54n;
120+
xfl |= BigInt(mantissa);
121+
122+
return xfl;
123+
}
124+
125+
function get_exponent(xfl) {
126+
if (xfl < 0n) throw "Invalid XFL";
127+
if (xfl == 0n) return 0n;
128+
return ((xfl >> 54n) & 0xffn) - 97n;
129+
}
130+
131+
function get_mantissa(xfl) {
132+
if (xfl < 0n) throw "Invalid XFL";
133+
if (xfl == 0n) return 0n;
134+
return xfl - ((xfl >> 54n) << 54n);
135+
}
136+
137+
function is_negative(xfl) {
138+
if (xfl < 0n) throw "Invalid XFL";
139+
if (xfl == 0n) return false;
140+
return ((xfl >> 62n) & 1n) == 0n;
141+
}
142+
143+
function to_string(xfl) {
144+
if (xfl < 0n) throw "Invalid XFL";
145+
if (xfl == 0n) return "<zero>";
146+
return (
147+
(is_negative(xfl) ? "-" : "+") +
148+
get_mantissa(xfl) +
149+
" * 10^(" +
150+
get_exponent(xfl) +
151+
")"
152+
);
153+
}
154+
```
155+
156+
C:
157+
158+
- See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86)

0 commit comments

Comments
 (0)