├── site ├── assets │ └── favicon.ico ├── templates │ ├── contribute.html │ ├── xls.html │ └── base.html ├── requirements.txt ├── build_site.py └── xls_parser.py ├── .gitignore ├── XLS-0047-PriceOracles └── README.pdf ├── .pre-commit-config.yaml ├── .github ├── workflows │ ├── lint.yml │ ├── validate-xls.yml │ └── deploy.yml └── dependabot.yml ├── README.md ├── XLS-0004-trustline-uri └── README.md ├── LICENSE ├── XLS-0006-visual-account-icons └── README.md ├── XLS-0002-destination-information ├── README.md └── reference.js ├── XLS-0003-deeplink-signed-transactions └── README.md ├── XLS-0060-default-autobridge └── README.md ├── XLS-0052-NFTokenMintOffer └── README.md ├── XLS-0037-concise-transaction-identifier-ctid ├── QUICKSTART.md ├── ctid.php ├── ctid.cpp ├── ctid.py ├── ctid.ts └── ctid.js ├── XLS-0063-signin └── README.md ├── XLS-0067-charge └── README.md ├── XLS-0018-bootstrapping └── README.md ├── XLS-0071-initial-owner-reserve-exemptio └── README.md ├── XLS-0055-remit └── README.md ├── XLS-0012-secret-numbers └── README.md ├── XLS-0046-dynamic-non-fungible-tokens └── README.md ├── XLS_TEMPLATE.md ├── XLS-0061-cross-currency-nftokenacceptof └── README.md ├── XLS-0041-xpop └── README.md ├── XLS-0051-nftoken-escrow └── README.md ├── XLS-0010-non-transferable-tokens └── README.md ├── XLS-0021-asset-code-prefixes └── README.md ├── XLS-0017-xfl └── README.md ├── XLS-0050-validator-toml-infra-details └── README.md ├── CONTRIBUTING.md ├── XLS-0022-api-versioning └── README.md ├── XLS-0064-pseudo-account └── README.md ├── XLS-0025-enhanced-secret-numbers └── README.md ├── XLS-0016-nft-metadata └── README.md ├── XLS-0023-lite-accounts └── README.md ├── XLS-0095-rename-rippled-to-xrpld └── README.md └── XLS-0054-nftokenoffer-destination-tag └── README.md /site/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRPLF/XRPL-Standards/HEAD/site/assets/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.idea 3 | _site/ 4 | 5 | # Python cache files 6 | __pycache__/ 7 | *.pyc 8 | *.pyo 9 | -------------------------------------------------------------------------------- /XLS-0047-PriceOracles/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRPLF/XRPL-Standards/HEAD/XLS-0047-PriceOracles/README.pdf -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: mixed-line-ending 8 | - id: check-merge-conflict 9 | args: [--assume-in-merge] 10 | 11 | - repo: https://github.com/rbubley/mirrors-prettier 12 | rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2 13 | hooks: 14 | - id: prettier 15 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | prettier: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v6 15 | 16 | - name: Set up Node.js 17 | uses: actions/setup-node@v6 18 | with: 19 | node-version: "24" 20 | 21 | - name: Install dependencies 22 | run: npm install -g prettier@3.6.2 # match version in pre-commit-config.yaml 23 | 24 | - name: Run Prettier 25 | run: prettier --check . 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XRP Ledger Standards 2 | 3 | XRP Ledger Standards (XLSs) describe standards and specifications relating to the XRP Ledger ecosystem that help achieve the following goals: 4 | 5 | - Ensure interoperability and compatibility between XRP Ledger core protocol, ecosystem applications, tools, and platforms. 6 | - Maintain a continued, excellent user experience around every application or system. 7 | - Drive alignment and agreement in the XRPL community (i.e., developers, users, operators, etc). 8 | 9 | # [Contributing](./CONTRIBUTING.md) 10 | 11 | The exact process for organizing and contributing to this repository is defined in [CONTRIBUTING.md](./CONTRIBUTING.md). If you would like to contribute, please read more there. 12 | -------------------------------------------------------------------------------- /site/templates/contribute.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} {% block content %} 2 |
2 | xls: 4 3 | title: Trustline Add URI 4 | description: A URI standard for instructing wallets to add trustlines following the design of XLS-2d 5 | author: Richard Holland (@RichardAH) 6 | discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/25 7 | status: Stagnant 8 | category: Ecosystem 9 | created: 2019-03-06 10 |11 | 12 | I suggest we follow on closely from the design of XLS-2d 13 | 14 | Query parameters: 15 | `action=trustline` 16 | `limit=
2 | xls: 6 3 | title: Standard for Visual Account Icons 4 | description: A standard for visually distinguishing XRPL accounts by generating unique icons for each account, regardless of address format. 5 | author: Richard Holland (@RichardAH) 6 | discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/24 7 | status: Final 8 | category: Ecosystem 9 | created: 2019-09-22 10 |11 | 12 | Following from [XLS-5d](https://github.com/XRPLF/XLS-0005-standards-for-addressing), it has become necessary to provide XRPL users a way to identify their XRPL account, which effectively now has two different identifiers: an 'r-address' and an 'X-address'. 13 | 14 | To solve this problem XLS-6d provides for a standard way to visually identify accounts irrespective of which addressing system is used by the rest of the user interface. 15 | 16 | For a user account take the X-address of the account without destination tag and feed it into hashicon https://www.npmjs.com/package/hashicon 17 | 18 | For an exchange, take the X-address of the account with the destination tag and feed it into hashicon. 19 | 20 | It's recommended that these icons are displayed alongside addresses to help reduce user confusion. See examples in Figures 1 and 2. 21 | 22 |  23 | Figure 1 24 | 25 |  26 | Figure 2 27 | -------------------------------------------------------------------------------- /XLS-0002-destination-information/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 2 3 | title: XRPL destination information 4 | description: A standard for encoding destination information with backwards compatibility for web browsers 5 | author: Wietse Wind11 | 12 | Currently several apps are using a variety of methods to encode destination information; 13 | 14 | - rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY?dt=123 15 | - rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:123 16 | - Deprecated Ripple URI syntax: https://ripple.com//send?to=rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY&amount=30&dt=123 17 | 18 | I feel the best way to implement this, is by allowing Apps to register Deep Links (URI handlers) while keeping the destinations backwards compatible with Web browsers, so users without any app to handle a URI can display a page by the app creator with an instruction. 19 | 20 | I propose to allow any URL prefix, with a default set of GET URL params, supporting the existing params proposed in the (deprecated) Ripple URI standard; 21 | 22 | so: 23 | 24 | { any URL } ? { params } 25 | 26 | Where params may _all_ be optional except for the account address: 27 | 28 | - `to` (account address, rXXXX..) 29 | - `dt` (destination tag, uint32) 30 | - `amount` (float, default currency: XRP (1000000 drops)) 31 | - ... 32 | 33 | So App 1 may use: 34 | https://someapp.com/sendXrp?to=... 35 | 36 | ... While App 2 uses: 37 | https://anotherapp.net/deposit-xrp?to=...&dt=1234&amount=10 38 | -------------------------------------------------------------------------------- /XLS-0003-deeplink-signed-transactions/README.md: -------------------------------------------------------------------------------- 1 |6 | discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/27 7 | status: Stagnant 8 | category: Ecosystem 9 | created: 2019-02-25 10 |
2 | xls: 3 3 | title: Sharing and deeplinking signed transactions 4 | description: A standard for encoding HEX signed transactions for air-gapped submissions with deep link support 5 | author: Wietse Wind11 | 12 | Currently several apps are using a variety of methods to encode HEX signed transactions (to be submitted air gapped); 13 | 14 | - { HEX } 15 | - ripple:signed-transaction:{ HEX } 16 | - ripple://signed/{ HEX } 17 | 18 | I feel the best way to implement this, is by allowing Apps to register Deep Links (URI handlers) while keeping the destinations backwards compatible with Web browsers, so users without any app to handle a URI can display a page by the app creator with an instruction. 19 | 20 | I propose to allow any URL prefix: 21 | { any URI with exactly one folder } / { HEX } 22 | 23 | So App 1 may use: 24 | https://someapp.com/submitTx/{ HEX } 25 | 26 | ... While App 2 uses: 27 | https://someapp.com/send-xrpl-tx/{ HEX } 28 | 29 | I would propose to limit the amount of slashes before the HEX to a _fixed amount of slashes_ (eg. one folder, as the App 1 and App 2 examples show) so parsers can easily split the URI without having to use regular expressions. 30 | 31 | Limitations: 32 | MSIE limits URL parsing to 2,083 chars. Safari 65k chars. Todo: need to test this on modern mobile OS'es. 33 | 34 | P.S. Another option (proposal) would be to use a specific syntax (prefix) instead of fixed (one) folder; eg. 35 | `https://xrpl-labs.com/xrpl:signed-transaction:6 | discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/26 7 | status: Stagnant 8 | category: Ecosystem 9 | created: 2019-02-25 10 |
2 | xls: 60 3 | title: Default AutoBridge 4 | description: Use autobridging in IOU-IOU Payment transactions 5 | author: tequ (@tequdev) 6 | created: 2024-02-05 7 | status: Stagnant 8 | category: Amendment 9 |10 | 11 | ## Abstract 12 | 13 | Currently, if the Path field is not specified in an IOU-IOU Payment transaction, only direct pairs of two currencies are used internally. This proposal would change this default behavior to use IOU-XRP-IOU as well as OfferCreate transaction's. 14 | 15 | ## Changes 16 | 17 | If the Path field is unspecified in the IOU_A->IOU_B Payment transaction, the path through IOU_A/XRP and IOU_B/XRP is used in addition to the IOU_A/IOU_B path. 18 | 19 | If existing transaction types or future transaction types to be implemented are similarly cross-currency transactions, it will be possible to use bridge paths in addition to direct paths by default. 20 | 21 | ### Technical 22 | 23 | Add an additional path with XRP as the intermediate between two currencies to in flow() method in Flow.cpp when `defaultPaths`=`true`. 24 | 25 | Use XRP-mediated paths to the paths under the following conditions: 26 | 27 | - Path is not specified 28 | - SendMax is set. 29 | - The issuer and currency of Amount and SendMax are different. 30 | 31 | ### Payment transaction 32 | 33 | If the Path field is not specified and the SendMax field is set, the transaction will use the XRP-mediated path. 34 | 35 | No impact if the NoRippleDirect flag is set. 36 | 37 | ### OfferCreate transaction 38 | 39 | No impact as Path is always set. 40 | 41 | ### CheckCash transaction 42 | 43 | (using flow()) 44 | 45 | No impact as it only processes when the fields corresponding to Amount and SendMax are the same. 46 | 47 | _If future amendments allow cross-currency checks, composite paths will be available._ 48 | 49 | ### XChainBridge transaction 50 | 51 | (using flow()) 52 | 53 | No impact as SendMax is processed as null. 54 | 55 | ### PathFind command 56 | 57 | (using flow()) 58 | No impact as DefaultPaths is false. 59 | -------------------------------------------------------------------------------- /XLS-0052-NFTokenMintOffer/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 52 3 | title: NFTokenMintOffer 4 | description: Extension to NFTokenMint transaction to allow NFToken Sell Offer creation at the same time as minting 5 | author: tequ (@tequdev) 6 | discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/147 7 | status: Final 8 | category: Amendment 9 | requires: [XLS-20](../XLS-0020-non-fungible-tokens/README.md) 10 | created: 2023-11-21 11 |12 | 13 | ## Abstract 14 | 15 | This proposal extends the minting capabilities of NFToken (XLS-20). 16 | 17 | NFToken are not only held by the issuer, but are often distributed or sold by the issuer to others. 18 | 19 | XRPL NFToken requires two transactions (`NFTokenMint` and `NFTokenCreateOffer`) to be sent by the issuer before the NFToken can be minted and distributed (or sold). 20 | NFTokenOfferMint extends the existing `NFTokenMint` transaction to allow an **NFToken Sell Offer** to be created at the same time as the NFToken is minted. 21 | 22 | NFTokenOfferMint is expected to significantly improve the experience of NFT projects and users. 23 | 24 | ## Specification 25 | 26 | ### New `NFTokenMint` Transaction Field 27 | 28 | Add 3 new fields to the`NFTokenMint` transaction. 29 | 30 | | Field Name | Required? | JSON Type | Internal Type | 31 | | ------------- | :-------: | :---------------: | :-----------: | 32 | | `Amount` | | `Currency Amount` | `AMOUNT` | 33 | | `Destination` | | `string` | `AccountID` | 34 | | `Expiration` | | `number` | `UINT32` | 35 | 36 | These fields have the same meaning as the field of the same name used in the `NFTokenCreateOffer` transaction, but the `Amount` field is not a required field. 37 | 38 | If the `Amount` field (and the other 2 fields) are not specified, `NFTokenMint` transaction behaves exactly like a previous `NFTokenMint` transaction. 39 | 40 | ### Creating an `NFTokenOffer` 41 | 42 | Since NFToken issuers can only create a sell offer for their NFToken, the `Owner` field is not set and `tfSellNFToken` flag is always set in the `NFTokenOffer` created in an `NFTokenMint` transaction. 43 | 44 | In an extended `NFTokenMint` transaction, an `NFTokenOffer` is created when 45 | 46 | - `Amount` field is specified. 47 | 48 | An error occurs in the following cases 49 | 50 | - One or both of `Destination` and `Expiration` fields are specified, but the `Amount` field is not . 51 | 52 | ### Owner Reserve 53 | 54 | The `NFTokenPage` and `NFTokenOffer` reserves will not change, but will create the possibility of up to 2 incremental reserves (NFTokenPage, NFTokenOffer) in an `NFTokenMint` transaction. 55 | -------------------------------------------------------------------------------- /site/templates/base.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
2 | xls: 63 3 | title: SignIn Transaction 4 | description: A dedicated transaction type for off-chain signing in with wallets 5 | author: Denis Angell (@dangell7) 6 | created: 2024-03-26 7 | status: Stagnant 8 | category: Ecosystem 9 |10 | 11 | # Problem Statement 12 | 13 | In the XRPL ecosystem, certain wallets (Ledger) restrict users from signing arbitrary hex messages as a security measure to protect against malicious activities. This limitation poses a challenge for applications that require user authentication through signature verification. As a result, some applications resort to using low drop Payment transactions as a workaround for authentication, which is not an ideal solution and can lead to unnecessary ledger bloat. To provide a more secure and efficient method for user authentication, a dedicated transaction type for signing in is necessary. 14 | 15 | # Proposal 16 | 17 | We propose the introduction of a new transaction type called "SignIn" that includes only the common transaction fields along with an additional field, `sfData`, which is an arbitrary data hex field. This transaction type will be specifically designed for applications to authenticate users by allowing them to sign a piece of data that can be verified by the application. 18 | 19 | > Importantly, `SignIn` transactions are not intended to be submitted to the ledger. 20 | 21 | ## New Transaction Type: `SignIn` 22 | 23 | The `SignIn` transaction is a new transaction type that allows users to sign an arbitrary piece of data for the purpose of authentication. This transaction type is not intended to transfer any funds or alter the ledger state in any way, but rather to provide a verifiable signature that applications can use to authenticate users. 24 | 25 | The transaction has the following fields: 26 | 27 | | Field | Type | Required | Description | 28 | | ----------------- | -------------- | -------- | ------------------------------------------------------------------------- | 29 | | sfTransactionType | String | ✔️ | The type of transaction, which is "SignIn" for this proposal. | 30 | | sfAccount | AccountID | ✔️ | The account of the user signing in. | 31 | | sfData | VariableLength | ✔️ | The arbitrary data to be signed by the user, represented as a hex string. | 32 | 33 | Example `SignIn` transaction: 34 | 35 | ```json 36 | { 37 | "Account": "rExampleAccountAddress", 38 | "TransactionType": "SignIn", 39 | "Data": "48656C6C6F205852504C2041757468656E7469636174696F6E" 40 | } 41 | ``` 42 | 43 | In this example, the `Data` field contains a hex-encoded string that the user's wallet will sign. The application can then verify the signature against the user's public key to authenticate the user. 44 | -------------------------------------------------------------------------------- /XLS-0067-charge/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 67 3 | title: Charge 4 | description: A feature that focuses on fee collection and makes monetization easier and simpler for platforms, wallet services, and users to use 5 | author: tequ (@tequdev) 6 | created: 2024-04-22 7 | status: Stagnant 8 | category: Amendment 9 |10 | 11 | # Abstract 12 | 13 | One way to monetize NFT, AMM and other platforms that use XRP Ledger is to charge a fee from separate Payment transaction. XLS-56d (Batch/Atomic Transaction) has already been proposed as a way to do this in a single transaction. Here, I propose a feature that focuses on fee collection and makes monetization easier and simpler for platforms, wallet services, and users to use. 14 | 15 | # Specification 16 | 17 | ### Transaction common field 18 | 19 | Add the following field as one of transaction common fields. 20 | This field is an optional field and be available for any transaction. 21 | 22 | | Field Name | Required? | JSON Type | Internal Type | 23 | | ---------- | :-------: | :-------: | :-----------: | 24 | | `Charge` | | `object` | `Object` | 25 | 26 | ## `Charge` Field 27 | 28 | | Field Name | Required? | JSON Type | Internal Type | 29 | | ---------------- | :-------: | :------------------: | :-----------: | 30 | | `Amount` | ✅ | `object` or `string` | `Amount` | 31 | | `Destination` | ✅ | `string` | `Account` | 32 | | `DestinationTag` | | `number` | `UInt16` | 33 | 34 | #### `Amount` Field 35 | 36 | Like the `Amount` field used in other transaction types, it represents a native token when specified as a string, or an issued token when specified as an object. 37 | 38 | For tokens for which a TransferRate has been specified by the issuer, this field represents the amount of tokens sent by the sender of the transaction and doesn't guarantee the amount of tokens the destination account will receive. 39 | 40 | ### `Destination` Field 41 | 42 | The account to which the fee will be sent. 43 | 44 | It is not possible to specify the account from which the transaction originates or an account that has not been activated. 45 | 46 | # Matters to Consider 47 | 48 | These are not included in the above specifications, but are subject to discussion. 49 | 50 | ## `SendMax` Field 51 | 52 | We could add `SendMax` to specify exactly how much the sender pays and how much the receiver receives, but that would be more complicated. 53 | 54 | ## CrossCurrency 55 | 56 | It is possible to make cross-currency payments by specifying different currencies in the `SendMax` and `Amount` fields, but this will increase transaction load. 57 | 58 | The `Path` field cannot specified and the default Path is used. 59 | e.g. 60 | 61 | - AAA/BBB without XLS-60d 62 | - AAA/XRP, BBB/XRP, AAA/BBB with XLS-60d 63 | 64 | ## Multiple charges 65 | 66 | Multiple currencies can be set as fees by specifying multiple `Charge` fields as an array in the `Charges` field, but this increases the transaction load (especially if cross-currency is also available). 67 | -------------------------------------------------------------------------------- /XLS-0018-bootstrapping/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 18 3 | title: Standard For Bootstrapping XRPLD Networks 4 | description: An experimental procedure to bootstrapping XRP Ledger Network 5 | author: Richard Holland (@RichardAH) 6 | created: 2021-03-25 7 | status: Stagnant 8 | category: Ecosystem 9 |10 | 11 | This procedure is experimental and additions amendments or recommendations based on experience to this standard draft are welcome. 12 | 13 | ### Problem Definition 14 | 15 | `Xrpld` / `rippled`[[1]](https://github.com/ripple/rippled) is the software that processes, achieves consensus upon and serves the decentralised public XRP Ledger, as well as numerous other public and private ledgers. 16 | 17 | From time to time a decentralised network may stall, fork, or otherwise fail to achieve consensus because of a bug, network instability, unreliable nodes or operators, a deliberate attack, or any combination of these. In this event the bootstrapping procedure to return the network to normal operation is important and should be followed correctly to avoid (further) forks and halts. 18 | 19 | ### Terminology 20 | 21 | `Validator` refers to a rippled instance configured to publish validations of ledgers. 22 | `UNL` refers to a list of validator public keys that the network has collectively agreed are allowed to validate ledgers. 23 | `Node` refers to a validator currently listed in the network's `UNL`. 24 | 25 | ### Procedure — Cold Start 26 | 27 | Cold start describes a situation in which _none_ of the validators in a network are currently running. This situation can arise because they crashed, were terminated automatically or were terminated manually (or any combination of these.) 28 | 29 | 1. Select a node from your UNL to be the `leader`. The remaining nodes will be referred to as `followers`. 30 | 2. On the `leader` run rippled from your terminal in the foreground with the following flags: 31 | `./rippled --valid --quorum 1 --load` 32 | 3. On each follower run rippled from your terminal in the foreground with the following flags: 33 | `./rippled --net --quorum 1` 34 | 4. In a seperate terminal, on each of the `followers` and on the `leader` you may monitor whether or not ledgers are closing correctly using: 35 | `./rippled ledger closed` 36 | In particular ensure the ledger index is incrementing and the ledgers are validated. 37 | 5. On the `leader` terminate the foreground rippled process. (Ctrl + C) 38 | 6. On the `leader` run rippled normally as a daemon. 39 | 7. On the `leader` in the second terminal continue to monitor the last closed ledger until validation is consistently achieved, as per 4. 40 | 8. Select one `follower` node. 41 | 9. Terminate the foreground process on the selected `follower` and start rippled normally. 42 | 10. Monitor for LCL validation as per 4. 43 | 11. Repeat from step 8 until all nodes are running normally. 44 | 45 | ### Troubleshooting 46 | 47 | Small networks may require the `quorum` size to be permanently overridden for network stability. To do this modify the control script that runs rippled to include `--quorum X` where X is the number of nodes in your UNL less 1. 48 | 49 | ### Other cases TBD 50 | -------------------------------------------------------------------------------- /XLS-0071-initial-owner-reserve-exemptio/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 71 3 | title: Initial Owner Reserve Exemption 4 | description: The first two account `objects` that are counted towards the `OwnerCount` shall not increase the `Owner Reserve` 5 | author: Vet (@xVet) 6 | created: 2024-07-01 7 | status: Stagnant 8 | category: Amendment 9 |10 | 11 | ## Terminology 12 | 13 | The reserve requirement has two parts: 14 | 15 | The `Base Reserve` is a minimum amount of XRP that is required for each address in the ledger. 16 | The `Owner Reserve` is an increase to the reserve requirement for each object that the address owns in the ledger. The cost per item is also called the incremental reserve. 17 | 18 | ## Abstract 19 | 20 | This proposal introduces the general initial owner reserve exemption. 21 | 22 | The first two account `objects` that are counted towards the `OwnerCount` shall not increase the `Owner Reserve`. We enforce the increase of the `Owner Reserve` , if `OwnerCount > 2` , else zero (Free) - For all objects that can be owned by an account e.g Offers, DID, NFTs, Trustlines, Oracles etc. 23 | 24 | ## Motivation 25 | 26 | 1. We do this to allow new created accounts by users / enterprises to be immediately able to use the XRP Ledger without the need initial XRP for reserves to accept an NFT, Loyalty points, Stablecoin, a RWA etc thus signficantly reduces the initial pain point of using the XRP Ledger without compromising the logic behind reserves, if accounts wish to own more objects then the XRPL will enforce just normally the reserve requierments. 27 | 28 | Owning an object on the XRP Ledger is a powerful feature that should be allowed up to the threshold of 2, to be free for any account. 29 | 30 | 2. In order to reduce the `base` and `owner` reserve long term, this step is a good middle ground. Typically, the concern around `account deletion fee` is associated with this topic. With this proposal we can keep this fee stable, as it is the owner reserve, reduce the base reserve long term but also get around the initial pain point of owner reserve funding 31 | 32 | 3. Xahau has the import feature, whereby if you import an activated XRPL account to Xahau, you get also 5 object slots for free. This seems to work well, where the `account deletion fee` on the XRPL is enough friction to prevent spam. 33 | 34 | 4. We already have this owner reserve exemption in a single case, for trustlines or more specific the `SetTrust` transaction, which helped trustlines to be a very popular and poweful feature on the XRPL for any new user. 35 | 36 | ## Implementation example 37 | 38 | XRPAmount const reserveCreate( 39 | (uOwnerCount < 2) ? XRPAmount(beast::zero) 40 | : view().fees().accountReserve(uOwnerCount + 1)); 41 | 42 | Enforcing increase of owner reserve if OwnerCount > 2 in the `SetTrust`transaction 43 | 44 | ## FAQ 45 | 46 | ### A: Can this allow spam attacks ? 47 | 48 | I don't see any attack vector that scales due to only the first two account object threshold, evidence in regards to Trustlines suggests that this is not a concern yet. Worst case, validators increase owner reserves in case spamming is observed, but the account deletion fee should be enough to prevent this. 49 | 50 | ### A: How to roll this exemption out and which transactors should have it ? 51 | 52 | It makes sense to include this exemption over time to as many object creation transaction as possible in case of adoption, while closely monitoring the network. 53 | -------------------------------------------------------------------------------- /XLS-0002-destination-information/reference.js: -------------------------------------------------------------------------------- 1 | function xls2d(uri) { 2 | const cleaned_uri = uri 3 | .replace(/^(.*:.*)?\?/gm, "") 4 | .replace(/\?/gim, "&") 5 | .replace(/^.*?:\/\//, "") 6 | .replace(/^ripple:/gim, ""); 7 | 8 | function clean() { 9 | return cleaned_uri; 10 | } 11 | 12 | function to() { 13 | //NB: this regex is case sensitive to assist in correctly matching XRP ledger addresses 14 | var match = 15 | /(?:(?:^|&)(?:to|TO|tO|To)=|^)([rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz]{25,55})/gm.exec( 16 | cleaned_uri, 17 | ); 18 | return match == null ? false : match[1]; 19 | } 20 | 21 | function dt() { 22 | var match = /(?:^|&)dt=([0-9]+)|:([0-9]+)$/gim.exec(cleaned_uri); 23 | if (match != null) return match[1] ? match[1] : match[2]; 24 | return false; 25 | } 26 | 27 | function amount() { 28 | var match = /(?:^|&)am(?:oun)?t=([0-9\.]+)/gim.exec(cleaned_uri); 29 | return match == null ? false : match[1]; 30 | } 31 | 32 | function currency() { 33 | var match = 34 | /(?:^|&)cur(?:rency)?=(?:([rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz]{25,55}):)?([A-Z]{3}|[A-Fa-f]{40})/gim.exec( 35 | cleaned_uri, 36 | ); 37 | return match == null 38 | ? false 39 | : { issuer: match[1] ? match[1] : false, currency: match[2] }; 40 | } 41 | 42 | function invoiceid() { 43 | var match = /(?:^|&)inv(?:oice)?(?:id)?=([a-f]{64})/gim.exec(cleaned_uri); 44 | return match == null ? false : match[1]; 45 | } 46 | 47 | return { 48 | uri: uri, 49 | clean: clean(), 50 | to: to(), 51 | dt: dt(), 52 | amount: amount(), 53 | currency: currency(), 54 | invoiceid: invoiceid(), 55 | }; 56 | } 57 | 58 | var examples = [ 59 | "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY?dt=123", 60 | "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:123", 61 | "https://ripple.com//send?to=rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY&amount=30&dt=123", 62 | "https://sub.domain.site.edu.au//send?to=rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY&amount=30&dt=123", 63 | "https://someapp.com/sendXrp?to=rRippleBlah&dt=4&invoiceid=abcdef", 64 | "deposit-xrp?to=blah", 65 | "?rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:123", 66 | "rAhDr1qUEG4gHXt6m6zyRE4oogDDWmYvcgotCqyyEpArk8", 67 | "to=rAhDr1qUEG4gHXt6m6zyRE4oogDDWmXFdzQdZdH9SJzcNJ", 68 | "to=rAhDr1qUEG4gHXt6m6zyRE4oogDDWmXFdzQdZdH9SJzcNJ¤cy=rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B:USD", 69 | "to=rAhDr1qUEG4gHXt6m6zyRE4oogDDWmXFdzQdZdH9SJzcNJ¤cy=USD", 70 | "to=rAhDr1qUEG4gHXt6m6zyRE4oogDDWmXFdzQdZdH9SJzcNJ¤cy=rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B:USD&invid=DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", 71 | "scheme://uri/folders?rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY:123", 72 | "scheme://uri/folders?rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY&amount=4:123", // this one a bit iffy 73 | "XVLhHMPHU98es4dbozjVtdWzVrDjtV8xvjGQTYPiAx6gwDC", 74 | "to=XVLhHMPHU98es4dbozjVtdWzVrDjtV8xvjGQTYPiAx6gwDC", 75 | "ripple:XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u", 76 | "ripple:XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u:58321", 77 | "ripple:XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u:58321¤cy=rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B:USD", 78 | "ripple:XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u:58321¤cy=XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u:ABC", 79 | "xrpl://XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u", 80 | "xrp://XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u", 81 | "f3w54ygsdfgfserga", 82 | ]; 83 | 84 | for (var i in examples) console.log(xls2d(examples[i])); 85 | -------------------------------------------------------------------------------- /XLS-0055-remit/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 55 3 | title: Remit 4 | description: Atomic Multi-Asset Payments for XRPL Protocol Chains 5 | author: Richard Holland (@RichardAH) 6 | created: 2023-12-03 7 | status: Final 8 | category: Amendment 9 |10 | 11 | ## Introduction 12 | 13 | **_Remit_** is a new payment transactor designed for XRPL Protocol Chains, which allows a sender to send multiple currencies and tokens atomically to a specified destination. It is a push payment that delivers "no matter what" and is designed for retail and Hooks use-cases. 14 | 15 | ## Constraints 16 | 17 | Using _Remit_ the sender may send: 18 | 19 | - one or more Issued Currencies, and/or, 20 | - one or more pre-existing URITokens owned by the sender and/or, 21 | - one new URIToken created in-line as part of the transaction. 22 | 23 | The transactor has the following behaviours: 24 | 25 | - If the destination does not exist, the sender pays to create it. 26 | - If the destination does not have the required trust-lines, the sender pays to create these. 27 | - The exact amounts and tokens are always delivered as specified in the transaction, if validated with code tesSUCCESS. 28 | - The sender pays all transfer fees on currencies, and the fees are not subtracted from the sent amount. 29 | - Where the sender pays to create objects, this is a separate amount not taken from the amounts specified in the transaction. 30 | - The transaction is atomic, either all amounts and tokens are delivered in the exact specified amount or none of them are. 31 | - The transactor does no conversion or pathing, the sender must already have the balances and tokens they wish to send. 32 | - When no amounts or tokens are specified, the transaction may still succeed. This can be used to create an account or ensure an account already exists. 33 | 34 | ## Specification 35 | 36 | A _Remit_ transaction contains the following fields: 37 | Field | Required | Type | Description 38 | -|-|-|- 39 | sfAccount | ✅ |AccountID | The sender 40 | sfDestination | ✅ |AccountID | The recipient. Does not need to exist. Will be created if doesn't exist. 41 | sfDestinationTag | ❌ |UInt32 | May be used by the destination to differentiate sub-accounts. 42 | sfAmounts | ❌|Array of Amounts | Each of the currencies (if any) to send to the destination. Sender must have funded trustlines for each. Destination does not need trustlines. 43 | sfURITokenIDs | ❌| Array of URITokenIDs | Each of the URITokens (if any) to send to the destination. 44 | sfMintURIToken | ❌| URIToken | If included, an inline URIToken to be created and delivered to the destination, for example a receipt. 45 | sfBlob | ❌ | Blob | A hex blob up to 128 kib to supply to a receiving Hook at the destination. 46 | sfInform | ❌ | AccountID | A third party. If supplied, their Hooks will be weakly executed (but the sender will pay for that execution). 47 | sfInvoiceID | ❌ | UInt256 | An arbitrary identifier for this remittance. 48 | 49 | ## Example Transaction 50 | 51 | ``` 52 | { 53 | "TransactionType" : "Remit", 54 | "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", 55 | "Destination" : "raod38dcxe6AGWfqxtZjPUfUdyrkWDe7d", 56 | "Amounts" : [ 57 | { 58 | "AmountEntry" : { 59 | "Amount" : "123" 60 | } 61 | }, 62 | { 63 | "AmountEntry" : { 64 | "Amount" : { 65 | "currency" : "USD", 66 | "issuer" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", 67 | "value" : "10" 68 | } 69 | } 70 | }, 71 | { 72 | "AmountEntry" : { 73 | "Amount" : { 74 | "currency" : "ABC", 75 | "issuer" : "rpfZurEGaJvpioTaxYpjh1EAtMXFN1HdtB", 76 | "value" : "12" 77 | } 78 | } 79 | } 80 | ], 81 | "URITokenIDs" : [ 82 | "C24DAF43927556F379F7B8616176E57ACEFF1B5D016DC896222603A6DD11CE05", 83 | "5E69C2D692E12D615B5FAC4A41989DA1EA5FD36E8F869B9ECA1121F561E33D2A" 84 | ], 85 | "MintURIToken" : { 86 | "URI" : "68747470733A2F2F736F6D652E757269" 87 | } 88 | } 89 | ``` 90 | 91 | ## Unwanted Remits 92 | 93 | Users who do not want to receive _Remit_ transactions can either set the _asfDisallowIncomingRemit_ on their accounts or install a Hook that will regulate incoming Remits. Alternatively they can simply burn unwanted URITokens or return unwanted Issued Currencies in order to claim the reserve that was paid by the sender. 94 | 95 | ## Chain Requirements 96 | 97 | XLS-55 depends on the adoption of XLS-35 on the applicable chain. 98 | -------------------------------------------------------------------------------- /XLS-0012-secret-numbers/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 12 3 | title: Secret Numbers 4 | description: Derive XRPL account keypairs based on 8x 6 digits for user-friendly, language-agnostic account secrets 5 | author: Wietse Wind11 | 12 | # XLS-12: Secret Numbers 13 | 14 | ### Derive XRPL account keypairs based on 8x 6 digits 15 | 16 | ##### Abstract 17 | 18 | Existing XRPL account secrets are prone to typo's and not that user friendly. Using numbers means the secrets will be language (spoken, written) agnostic. Existing secrets (family seed, mnemonic) may be intimidating for the public that's relatively new to cryptocurrencies / blockchain technology & very prone to user error (eg. when writing down or reading). 19 | 20 | ## Background 21 | 22 | The common formats for XRPL account secrets are (at the time of writing the first (TypeScript) [Secret Numbers implementation](https://github.com/WietseWind/xrpl-secret-numbers), July 2019): 23 | 24 | - Family Seed, eg. `sh1HiK7SwjS1VxFdXi7qeMHRedrYX` 25 | - Mnemonic, eg. `car banana apple road ...` 26 | 27 | Both secrets contain characters that can be easily confuesed or misread. A range of numbers (0-9) is easier to write down (alphabet of just 10 defferent chars (numbers)). 28 | 29 | The Secret Numbers encoding method can be used on HEX private keys as well. As HEX private keys are hardly being used & for the sake of offering backwards compatibility for generated Secret Numbers, I propose this to be used with the entropy that can be used for ripple-keypairs as well. 30 | 31 | ## Secret Numbers 32 | 33 | A secret based on the Secret Numbers standard contains 8 blocks of 6 digits: the first five digits are int representations of the entropy, the 6th digit is a checksum based on the five preceding digits + the block number. 34 | 35 | A secret now looks like: 36 | 37 | ``` 38 | 554872 394230 209376 323698 140250 387423 652803 258676 39 | ``` 40 | 41 | A block indicator can be added to make it easier for users to enter the right block: 42 | 43 | ``` 44 | A. 554872 45 | B. 394230 46 | ... 47 | H. 258676 48 | ``` 49 | 50 | The first five digits are the decimal representation of a chunk of the entropy buffer, and will be in the range of `0 - 65535` (`0000 - FFFF`). 51 | 52 | ## Compatibility 53 | 54 | Secret Numbers can be used as entropy-param for eg. the `generateSeed` method of [ripple-keypairs](https://github.com/ripple/ripple-keypairs). This means a secret based on the Secret Numbers standard can always be turned in a family seed for backwards compatibility with existing XRPL clients. 55 | 56 | ## Encoding / Decoding 57 | 58 | - Secret Numbers contain 6 digits in the range of 0-9 per position. 59 | - Secret numbers contain 5 digits (int) entropy and a 6th checksum. 60 | - The checksum digit is based on the 5 digits entropy and the block position. This way a block of 6 digits entered at the wrong position (A-H) can be detected as being invalid. 61 | - A "Secret Number"-chunk should be represented as a string containing six digits, as there can be leading zeroes. 62 | 63 | ### Calculating 64 | 65 | Position is the block number starting at zero (0 - 7). The position then multiplied & incremented (`* 2 + 1`) to make sure it results in an odd number. 66 | 67 | ``` 68 | calculateChecksum(position: number, value: number): number { 69 | return value * (position * 2 + 1) % 9 70 | } 71 | ``` 72 | 73 | ##### Samples 74 | 75 | | HEX | Decimal | Block # | Calculation | Checksum | Result | 76 | | ---- | ------- | ------- | ------------------------- | -------- | ------ | 77 | | AF71 | 44913 | 0 | `44913 * (0 * 2 + 1) % 9` | 3 | 449133 | 78 | | 0000 | 0 | 2 | ` 0 * (2 * 2 + 1) % 9` | 0 | 000000 | 79 | | FFFF | 65535 | 3 | `65535 * (3 * 2 + 1) % 9` | 6 | 655356 | 80 | | FFFF | 65535 | 4 | `65535 * (4 * 2 + 1) % 9` | 0 | 655350 | 81 | | CD91 | 52625 | 7 | `52625 * (7 * 2 + 1) % 9` | 3 | 526253 | 82 | 83 | ## Implementations 84 | 85 | - TypeScript [](https://www.npmjs.com/xrpl-secret-numbers) - Github: https://github.com/WietseWind/xrpl-secret-numbers 86 | 87 | ## Representations 88 | 89 | #### String 90 | 91 | ``` 92 | 554872 394230 <...> 258676 93 | ``` 94 | 95 | #### Human Readable & entry 96 | 97 | ``` 98 | A. 554872 99 | B. 394230 100 | ... 101 | H. 258676 102 | ``` 103 | 104 | #### QR Codes 105 | 106 | ``` 107 | xrplsn:554872394230<...>258676 108 | ``` 109 | -------------------------------------------------------------------------------- /XLS-0037-concise-transaction-identifier-ctid/ctid.php: -------------------------------------------------------------------------------- 1 | 0xFFFFFFF || $ledger_seq < 0) 8 | throw new Exception("ledger_seq must not be greater than 268435455 or less than 0."); 9 | 10 | if (!is_numeric($txn_index)) 11 | throw new Exception("txn_index must be a number."); 12 | if ($txn_index > 0xFFFF || $txn_index < 0) 13 | throw new Exception("txn_index must not be greater than 65535 or less than 0."); 14 | 15 | if (!is_numeric($network_id)) 16 | throw new Exception("network_id must be a number."); 17 | if ($network_id > 0xFFFF || $network_id < 0) 18 | throw new Exception("network_id must not be greater than 65535 or less than 0."); 19 | 20 | $ledger_part = dechex($ledger_seq); 21 | $txn_part = dechex($txn_index); 22 | $network_part = dechex($network_id); 23 | 24 | if (strlen($ledger_part) < 7) 25 | $ledger_part = str_repeat("0", 7 - strlen($ledger_part)) . $ledger_part; 26 | if (strlen($txn_part) < 4) 27 | $txn_part = str_repeat("0", 4 - strlen($txn_part)) . $txn_part; 28 | if (strlen($network_part) < 4) 29 | $network_part = str_repeat("0", 4 - strlen($network_part)) . $network_part; 30 | 31 | return strtoupper("C" . $ledger_part . $txn_part . $network_part); 32 | } 33 | 34 | function decodeCTID($ctid) 35 | { 36 | if (is_string($ctid)) 37 | { 38 | if (!ctype_xdigit($ctid)) 39 | throw new Exception("ctid must be a hexadecimal string"); 40 | if (strlen($ctid) !== 16) 41 | throw new Exception("ctid must be exactly 16 nibbles and start with a C"); 42 | } else 43 | throw new Exception("ctid must be a hexadecimal string"); 44 | 45 | if (substr($ctid, 0, 1) !== 'C') 46 | throw new Exception("ctid must be exactly 16 nibbles and start with a C"); 47 | 48 | $ledger_seq = substr($ctid, 1, 7); 49 | $txn_index = substr($ctid, 8, 4); 50 | $network_id = substr($ctid, 12, 4); 51 | return array( 52 | "ledger_seq" => hexdec($ledger_seq), 53 | "txn_index" => hexdec($txn_index), 54 | "network_id" => hexdec($network_id) 55 | ); 56 | } 57 | 58 | // NOTE TO DEVELOPER: 59 | // you only need the two functions above, below are test cases, if you want them. 60 | 61 | print("Running tests...\n"); 62 | 63 | function assert_test($x) 64 | { 65 | if (!$x) 66 | echo "test failed!\n"; 67 | else 68 | echo "test passed\n"; 69 | } 70 | 71 | // Test case 1: Valid input values 72 | assert_test(encodeCTID(0xFFFFFFF, 0xFFFF, 0xFFFF) == "CFFFFFFFFFFFFFFF"); 73 | assert_test(encodeCTID(0, 0, 0) == "C000000000000000"); 74 | assert_test(encodeCTID(1, 2, 3) == "C000000100020003"); 75 | assert_test(encodeCTID(13249191, 12911, 49221) == "C0CA2AA7326FC045"); 76 | 77 | // Test case 2: ledger_seq greater than 0xFFFFFFF 78 | try { 79 | encodeCTID(0x10000000, 0xFFFF, 0xFFFF); 80 | assert_test(false); 81 | } catch (Exception $e) { 82 | assert_test(strcmp($e->getMessage(), "ledger_seq must not be greater than 268435455 or less than 0.") == 0); 83 | } 84 | try { 85 | encodeCTID(-1, 0xFFFF, 0xFFFF); 86 | assert_test(false); 87 | } catch (Exception $e) { 88 | assert_test(strcmp($e->getMessage(), "ledger_seq must not be greater than 268435455 or less than 0.") == 0); 89 | } 90 | 91 | // Test case 3: txn_index greater than 0xFFFF 92 | try { 93 | encodeCTID(0xFFFFFFF, 0x10000, 0xFFFF); 94 | assert_test(false); 95 | } catch (Exception $e) { 96 | assert_test(strcmp($e->getMessage(), "txn_index must not be greater than 65535 or less than 0.") == 0); 97 | } 98 | try { 99 | encodeCTID(0xFFFFFFF, -1, 0xFFFF); 100 | assert_test(false); 101 | } catch (Exception $e) { 102 | assert_test(strcmp($e->getMessage(), "txn_index must not be greater than 65535 or less than 0.") == 0); 103 | } 104 | 105 | // Test case 4: network_id greater than 0xFFFF 106 | try { 107 | encodeCTID(0xFFFFFFF, 0xFFFF, 0x10000); 108 | assert_test(false); 109 | } catch (Exception $e) { 110 | assert_test(strcmp($e->getMessage(), "network_id must not be greater than 65535 or less than 0.") == 0); 111 | } 112 | try { 113 | encodeCTID(0xFFFFFFF, 0xFFFF, -1); 114 | assert_test(false); 115 | } catch (Exception $e) { 116 | assert_test(strcmp($e->getMessage(), "network_id must not be greater than 65535 or less than 0.") == 0); 117 | } 118 | 119 | // Test case 5: Valid input values 120 | assert_test(decodeCTID("CFFFFFFFFFFFFFFF") == array("ledger_seq" => 0xFFFFFFF, "txn_index" => 0xFFFF, "network_id" => 0xFFFF)); 121 | assert_test(decodeCTID("C000000000000000") == array("ledger_seq" => 0, "txn_index" => 0, "network_id" => 0)); 122 | assert_test(decodeCTID("C000000100020003") == array("ledger_seq" =>1, "txn_index" => 2, "network_id" => 3)); 123 | assert_test(decodeCTID("C0CA2AA7326FC045") == array("ledger_seq" =>13249191, "txn_index" => 12911, "network_id" => 49221)); 124 | 125 | 126 | print("Done!\n"); 127 | 128 | ?> 129 | -------------------------------------------------------------------------------- /XLS-0046-dynamic-non-fungible-tokens/README.md: -------------------------------------------------------------------------------- 1 |, Nik Bougalis (@nbougalis) 6 | discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/15 7 | status: Final 8 | category: Ecosystem 9 | created: 2020-05-13 10 |
2 | xls: 46 3 | title: Dynamic Non Fungible Tokens (dNFTs) 4 | description: Support for XLS-20 NFTs to modify and upgrade token properties as mutable NFTs 5 | author: Vet (@xVet), Mayukha Vadari (@mvadari), TeQu (@tequdev) 6 | discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/130 7 | status: Final 8 | category: Ecosystem 9 | requires: [XLS-20](../XLS-0020-non-fungible-tokens/README.md) 10 | created: 2023-08-18 11 |12 | 13 | ## Abstract 14 | 15 | This proposal aims to provide support for XLS-20 NFTs to modify/upgrade token properties. 16 | 17 | [XLS-20](link to spec) provides [Non-Fungible Token](link to docs) support, these tokens are immutable and don’t allow any changes. Currently NFTs can’t be modified, resulting often in new mints, which leads to ledger bloat that eat valuable resources as well as experimental approaches to use website endpoints to mimic dynamic abilities. 18 | 19 | Apart from the use of immutable NFTs, there is a wide range of use cases around dNFTs (Dynamic Non-Fungible Tokens) which are considered mutable NFTs. The goal is to provide both options to developers and users to cover all aspects of non fungibility, while choosing the least invasive approach to achieve this functionality. 20 | 21 | ## Motivation 22 | 23 | Usually, NFTs are typically static in nature. A static NFT refers to an NFT that possesses unchanging and immutable characteristics stored on the blockchain, rendering them unmodifiable. These static NFTs encompass various forms like images, videos, GIF files, music, and unlockable components. For instance, an illustration of a basketball player making a shot into a hoop serves as an example of a static NFT. 24 | 25 | On the other hand, `dynamic NFTs`, often abbreviated as dNFTs, represent the next phase in the evolution of the NFT landscape. These `dynamic NFTs` seamlessly integrate the inherent uniqueness of NFTs with the inclusion of dynamic data inputs. These inputs can arise from calculations conducted either on the blockchain or off-chain. 26 | 27 | Oracles could supply dynamic real-world data to NFTs. To illustrate, a `dynamic NFT` might showcase real-time updates of a basketball player's performance statistics as they actively play. 28 | 29 | ## Specification 30 | 31 | ### 3. New Transactors and Flags 32 | 33 | ### We will specify the following: 34 | 35 | New Transactor 36 | - NFTokenModify 37 | 38 | New Flags 39 | - tfMutable 40 | 41 | ### 3.1 tfMutable 42 | 43 | New flags for `NFTokenMint`: 44 | 45 | | Flag Name | Flag Value | Description | 46 | | ----------- | :----------: | :---------------------------------------------------------------------: | 47 | | `tfMutable` | `0x00000010` | `Allow issuer (or an entity authorized by the issuer) to modify “URI”.` | 48 | 49 | ### 3.2 NFTokenModify 50 | 51 | `NFTokenModify` is used to modify the URI property of a NFT: 52 | 53 | Transaction-specific Fields 54 | 55 | | Field Name | Required? | JSON Type | Internal Type | 56 | | ----------------- | :-------: | :-------: | :-----------: | 57 | | `TransactionType` | `✔️` | `string` | `UINT16` | 58 | 59 | Indicates the `account` which is owning the NFT, in case of `Owner` not specified, it's implied that the submitting `account` is also the `Owner` of the NFT. 60 | 61 | | Field Name | Required? | JSON Type | Internal Type | 62 | | ---------- | :-------: | :-------: | :-----------: | 63 | | `Owner` | | `string` | `ACCOUNT ID` | 64 | 65 | Indicates the `NFToken` object to be modified. 66 | 67 | | Field Name | Required? | JSON Type | Internal Type | 68 | | ----------- | :-------: | :-------: | :-----------: | 69 | | `NFTokenID` | `✔️` | `string` | `UINT256` | 70 | 71 | The new `URI` that points to data and/or metadata associated with the NFT. 72 | If a `URI` is omitted then the corresponding `URI` record in the XRP ledger, if present, is removed. 73 | 74 | | Field Name | Required? | JSON Type | Internal Type | 75 | | ---------- | :-------: | :-------: | :-----------: | 76 | | `URI` | | `string` | `BLOB` | 77 | 78 | Example (modify URI): 79 | 80 | { 81 | "TransactionType": "NFTokenModify", 82 | "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", 83 | "Owner": "rogue5HnPRSszD9CWGSUz8UGHMVwSSKF6", 84 | "Fee": "10", 85 | "Sequence": 33, 86 | “NFTokenID”: “0008C350C182B4F213B82CCFA4C6F59AD76F0AFCFBDF04D5A048C0A300000007", 87 | "URI": "697066733A2F2F62616679626569636D6E73347A736F6C686C6976346C746D6E356B697062776373637134616C70736D6C6179696970666B73746B736D3472746B652F5665742E706E67", 88 | 89 | ... 90 | 91 | } 92 | 93 | If `tfMutable` is not set, executing NFTokenModify should fail! 94 | 95 | If `tfMutable` is set, executing NFTokenModify should fail when neither `Issuer` or an `account` authorized via `NFTokenMinter`, according to the specific flag, is executing the transaction. 96 | 97 | This approach takes into consideration that `NFToken Flags` are part of the `NFTokenID`, mutating anything that is part of the `NFTokenID` must be avoided. 98 | -------------------------------------------------------------------------------- /XLS_TEMPLATE.md: -------------------------------------------------------------------------------- 1 |
2 | xls: [XLS number] 3 | title: [The title is a few words, not a complete sentence] 4 | description: [Description is one full (short) sentence] 5 | implementation: [optional - link to rippled PR for Amendment/System XLSes] 6 | author: [a comma separated list of the author(s) with email addresses] 7 | category: [Amendment | System | Ecosystem | Meta] 8 | status: [Draft | Final | Living | Deprecated | Stagnant | Withdrawn] 9 | proposal-from: [link to XRPL-Standards Proposal discussion where this XLS was proposed] 10 | requires: [optional - XLS number(s) if this depends on other features] 11 | created: YYYY-MM-DD 12 | updated: YYYY-MM-DD 13 |14 | 15 | > **Note:** This is the suggested template for new XLS specifications. After you have filled in the requisite fields, please delete the guidance text (italicized bracketed instructions). 16 | 17 | _[The requirements to sections depend on the type of proposal. For example, amendments require some information that may not be relevant for other kinds of proposals. Please adapt the template as appropriate.]_ 18 | 19 | _[The title should be 44 characters or less. The title should NOT include "XLS" prefix or the XLS number.]_ 20 | 21 | _[For Proposals (pre-Draft stage), the title must include the category prefix (e.g., "Meta XLS: XLS Process and Guidelines").]_ 22 | 23 | # [Title] 24 | 25 | ## 1. Abstract 26 | 27 | _[Abstract is a multi-sentence (short paragraph) technical summary. This should be a very terse and human-readable version of the specification section. Someone should be able to read only the abstract to get the gist of what this specification does.]_ 28 | 29 | ## 2. Motivation _(Optional)_ 30 | 31 | _[A motivation section is critical for XLSes that want to change the protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the XLS solves. This section may be omitted if the motivation is evident.]_ 32 | 33 | ## 3. Specification 34 | 35 | _[The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations.]_ 36 | 37 | > **Note:** It is recommended to follow RFC 2119 and RFC 8174. Do not remove the key word definitions if RFC 2119 and RFC 8174 are followed. 38 | 39 | _[For Amendment XLSes, use the [AMENDMENT_TEMPLATE.md](AMENDMENT_TEMPLATE.md) to structure this section with detailed subsections for Serialized Types, Ledger Entries, Transactions, Permissions, and RPCs as needed.]_ 40 | 41 | _[For other XLS types, provide a clear technical specification.]_ 42 | 43 | ## 4. Rationale 44 | 45 | _[The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale should discuss important objections or concerns raised during discussion around the XLS.]_ 46 | 47 | ## 5. Backwards Compatibility _(Optional)_ 48 | 49 | _[All XLSes that introduce backwards incompatibilities must include a section describing these incompatibilities and their consequences. The XLS must explain how the author proposes to deal with these incompatibilities. This section may be omitted if the proposal does not introduce any backwards incompatibilities, but this section must be included if backward incompatibilities exist.]_ 50 | 51 | ## 6. Test Plan _(Optional)_ 52 | 53 | _[A description of the process to test the feature the authors are proposing. The test plan may be either inlined in the XLS file or included in the `../xls-###/
2 | xls: 61 3 | title: CrossCurrency NFTokenAcceptOffer 4 | description: Allow cross-currency NFToken transactions using multiple currencies 5 | author: tequ (@tequdev) 6 | created: 2024-02-26 7 | status: Stagnant 8 | category: Amendment 9 |10 | 11 | # Abstract 12 | 13 | XRPL's NFToken functionality, currently implemented as XLS-20, only allows the use of a single currency for transactions. 14 | 15 | This proposal allows cross-currency NFToken transactions using multiple currencies. 16 | 17 | Creators and marketplaces will be able to sell NFTs without being tied to a currency, and users will be able to buy NFTs without being tied to a currency. 18 | 19 | This will increase revenue opportunities for creators and marketplaces and create significant tokenomics. 20 | 21 | # Specification 22 | 23 | ## `NFTokenAcceptOffer` Transaction 24 | 25 | Add the following field: 26 | 27 | | Field Name | Required? | JSON Type | Internal Type | 28 | | ---------- | :-------: | :------------------: | :-----------: | 29 | | `Amount` | | `object` or `string` | `AMOUNT` | 30 | 31 | ### `Amount` Field 32 | 33 | When accepting an NFTokenOffer with the tfSellOffer flag set, the `Amount` field acts like the `SendMax` field, and when accepting an NFTokenOffer without tfSellOffer set, the `DeliverMin` field acts like the `Amount` field. 34 | 35 | It doesn't have to match the `Amount` of the Offeror specified in `NFTokenSellOffer` or `NFTokenBuyOffer`. 36 | 37 | In broker mode, the `Amount` field must not be present. 38 | 39 | ### `NFTokenBrokerFee` Field 40 | 41 | The existing `NFTokenBrokerFee` field can be any currency amount. 42 | 43 | ### CrossCurrency Accept 44 | 45 | The currency of `Amount` can be different from the currency of the BuyOffer / SellOffer. 46 | The currency of `NFTokenBrokerFee` can be different from the currency of the BuyOffer / SellOffer. 47 | 48 | Buyer must specify a sufficient amount that can be transferred to Seller, nftoken issuer(nftoken transfer fee), broker(broker fee), token issuer(token transfer fee). 49 | 50 | NFToken transfer fees are sent in the buyer's currency. 51 | NFToken with the tfOnlyXRP flag set will be paid in XRP. 52 | 53 | If there was insufficient amount or liquidity, the transaction will fail. 54 | 55 | ## `NFTokenCreateOffer` Transaction 56 | 57 | No change in fields. 58 | 59 | Change to only check if the NFToken issuer has a trustline for the currenfy of `Amount` field when creating a BuyOffer for an NFToken with royalties. 60 | 61 | ## The `NFTokenPage` Object 62 | 63 | No changes. 64 | 65 | ## The `NFTokenOffer` Object 66 | 67 | No changes. 68 | 69 | # Cases 70 | 71 | ## `Amount` Field not specified in `NFTokenAcceptOffer` Transaction 72 | 73 | According to XLS-20 specifications. Except for NFTs with royalties, issuer's trust line [must be checked](https://github.com/XRPLF/rippled/issues/4925). 74 | 75 | ## `Amount` Field specified and equal to `Amount` in `NFTokenOffer` 76 | 77 | DEX liquidity will not be used and existing transfer processing will be used. 78 | If the quantity in the `Amount` cannot be sent to the seller, the transaction will fail. 79 | 80 | ## `Amount` Field specified and not same as `Amount` in `NFTokenOffer` 81 | 82 | DEX liquidity will be used and other transfer processing (used in Payment/CheckCash) will be used. 83 | If the quantity in `Amount` cannot be sent to the seller, the transaction will fail. 84 | 85 | If the buyer's `Amount` can send more than the specified amount of the seller's `Amount`, the amount will be sent up to the maximum amount of the buyer's `Amount`. 86 | 87 | ## `NFTokenBrokerFee` Field specified 88 | 89 | Convert and send the amount from the buyer's `Amount` to satisfy the `NFTokenBrokerFee`. 90 | 91 | ## Accept NFToken with TransferFee 92 | 93 | Paid in the same currency as the buyer's `Amount`. 94 | If no trustline is set, the transaction will fail. 95 | 96 | ## Accept NFToken with TransferFee, `tfOnlyXRP`, `Amount` Field specified, `NFTokenBrokerFee` Field specified 97 | 98 | Up to 3 times currency will be converted and up to 3 times token transfer fees will be charged. 99 | 100 | 1. From Buyer to Broker (`NFTokenBrokerFee` currency, Buyer's `Amount` currency fee) 101 | 1. From Buyer to Issuer (`TransferFee` currency or XRP, Buyer's `Amount` currency fee) 102 | 1. From Buyer to Seller (`Amount` currency, Buyer's `Amount` currency fee) 103 | 104 | # Concern 105 | 106 | ## Transaction load 107 | 108 | Processing load is expected to increase due to up to three cross-currency remittance processes (flows) within a single transaction. 109 | 110 | However, instead of merely being concerned about the increased load of a single transaction, it should be noted that in the current, similar processing would have to be spread over two or more transactions, NFTokenAccept and CrossCurrency Payment. 111 | 112 | ## Royalty for NFToken with tfOnlyFlag 113 | 114 | The royalty is sent by converting a portion of the buyer's Amount (TransferFee %) into XRP, so if there is not enough liquidity, the royalty might be almost zero. 115 | 116 | # Note 117 | 118 | The process used by Payment is used for currency conversion and remittance processing. 119 | Path cannot be specified, and only direct AAA->BBB pairs are used at this time. 120 | 121 | If auto-bridge is enabled by default by XLS-60d, the auto-bridge path will also be used for the currency conversion process in this proposal. 122 | -------------------------------------------------------------------------------- /XLS-0041-xpop/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 41 3 | title: XRPL Proof of Payment Standard (XPOP) 4 | author: Richard Holland (@RichardAH) 5 | description: An offline non-interactive cryptographic proof that a transaction was successfully submitted to the XRP Ledger and what its impact (metadata) was 6 | created: 2023-05-04 7 | status: Final 8 | category: Ecosystem 9 |10 | 11 | # XLS-41d 12 | 13 | # Abstract 14 | 15 | An XRPL Proof of Payment (XPOP) is an offline non-interactive cryptographic proof that a transaction was successfully submitted to the XRP Ledger and what its impact (metadata) was. 16 | 17 | # Background 18 | 19 | The XRPL is comprised of a chain of blocks (Ledgers) co-operatively and deterministically computed, shared and subsequently signed (validated) by a quorum of rippled nodes (validators) operating in a socially trusted group known as a Unique Node List (UNL). The UNL is typically published by a trusted third party in a format known as a Validator List (VL). (Examples: https://vl.xrplf.com, https://vl.ripple.com). Each VL is cryptographically signed by a master publishing key. Users of the network ultimately trust this publisher (key) to choose appropriate validators for the UNL that will co-operate to make forward progress and not conspire to defraud them. 20 | 21 | # Proof 22 | 23 | Each VL contains a list of validators signed for by the VL publisher key. 24 | 25 | Each validator is a key that signs validation messages over each Ledger header. 26 | 27 | Each Ledger header contains the root hash of two patricia merkle tries: 28 | 29 | - the current (”account”) state for the ledger, and 30 | - the transactions and their metadata in that Ledger. 31 | 32 | A quorum (from a given VL) of signed validation messages proves a Ledger was correctly closed and became part of the block chain. 33 | 34 | Thus if one trusts the VL publisher key, then one can form a complete chain of validation from the VL key down to a transaction and its meta data without connectivity to the internet. This is an XPOP. 35 | 36 | # Format 37 | 38 | XPOPs are a JSON object with the following schema: 39 | 40 | ``` 41 | { 42 | "ledger": { 43 | "acroot": merkle root of account state map | hex string, 44 | "txroot": merkle root of transaction map | hex string , 45 | "close": close time | int, 46 | "coins": total drops | int or string, 47 | "cres": close time resolution | int, 48 | "flags": ledger flags | int, 49 | "pclose": parent close time | int, 50 | "phash": parent hash | hex string, 51 | }, 52 | "transaction": { 53 | "blob": serialized transaction | hex string, 54 | "meta": serialized metadata | hex string, 55 | "proof":
2 | xls: 51d 3 | title: NFToken Escrows 4 | author: Mayukha Vadari (@mvadari) 5 | created: 2023-11-17 6 | status: Stagnant 7 | category: Amendment 8 |9 | 10 | # NFToken Escrows 11 | 12 | ## Abstract 13 | 14 | The XRP Ledger currently only supports escrows for one type of token: XRP. Now that XLS-20 is live on the network and there are almost 4 million NFTs on the ledger, users may want to also be able to put their NFTs in an escrow, just as they would with their funds. 15 | 16 | ## 1. Overview 17 | 18 | This spec proposes modifications to one existing on-ledger object and one existing transaction: 19 | 20 | - `Escrow` ledger object 21 | - `EscrowCreate` transaction 22 | 23 | This change will require an amendment, tentatively called `featureNFTokenEscrow`. 24 | 25 | ## 2. On-Ledger Object: `Escrow` 26 | 27 | The [`Escrow` object](https://xrpl.org/escrow-object.html) already exists on the XRPL. We propose a slight modification to support NFT escrows. 28 | 29 | ### 2.1. Fields 30 | 31 | As a reference, these are the existing escrow object fields. 32 | 33 | | Field Name | Required? | JSON Type | Internal Type | 34 | | ------------------------ | --------- | --------- | ------------- | 35 | | `LedgerIndex` | ✔️ | `string` | `HASH256` | 36 | | `LedgerEntryType` | ✔️ | `string` | `UINT16` | 37 | | `Owner` | ✔️ | `string` | `AccountID` | 38 | | `OwnerNode` | ✔️ | `string` | `UInt32` | 39 | | `Amount` | ✔️ | `Amount` | `Amount` | 40 | | `Condition` | | `string` | `Blob` | 41 | | `CancelAfter` | | `number` | `UInt32` | 42 | | `FinishAfter` | | `number` | `UInt32` | 43 | | `Destination` | ✔️ | `string` | `AccountID` | 44 | | `DestinationNode` | ✔️ | `string` | `UInt32` | 45 | | `DestinationTag` | | `number` | `UInt32` | 46 | | `PreviousTxnId` | ✔️ | `string` | `HASH256` | 47 | | `PreviousLedgerSequence` | ✔️ | `number` | `UInt32` | 48 | | `SourceTag` | | `number` | `UInt32` | 49 | 50 | We propose these modifications: 51 | 52 | | Field Name | Required? | JSON Type | Internal Type | 53 | | ---------- | --------- | --------- | ------------- | 54 | | `Amount` | | `Amount` | `Amount` | 55 | | `NFTokens` | | `array` | `STArray` | 56 | 57 | #### 2.1.1. `Amount` 58 | 59 | The `Amount` field is still used as it currently is, but it is now optional, to support escrows that only hold NFTs. An `Escrow` object must have an `Amount` field and/or an `NFTokens` field. 60 | 61 | #### 2.1.2. `NFTokens` 62 | 63 | The NFTs that are in the escrow. One escrow can hold up to 32 NFTs, equivalent to one `NFTokenPage`. 64 | 65 | NFTs stored in an escrow cannot be burned (and cannot be modified either, if [XLS-46](https://github.com/XRPLF/XRPL-Standards/discussions/130) is enabled). 66 | 67 | ## 3. Transaction: `EscrowCreate` 68 | 69 | The [`EscrowCreate` transaction](https://xrpl.org/escrowcreate.html) already exists on the XRPL. We propose a slight modification to support NFT escrows. 70 | 71 | ### 3.1. Fields 72 | 73 | As a reference, these are the existing `EscrowCreate` fields: 74 | 75 | | Field Name | Required? | JSON Type | Internal Type | 76 | | ----------------- | --------- | --------- | ------------- | 77 | | `TransactionType` | ✔️ | `string` | `UInt16` | 78 | | `Account` | ✔️ | `string` | `AccountID` | 79 | | `Amount` | ✔️ | `Amount` | `Amount` | 80 | | `Destination` | ✔️ | `string` | `AccountID` | 81 | | `Condition` | | `string` | `Blob` | 82 | | `CancelAfter` | | `number` | `UInt32` | 83 | | `FinishAfter` | | `number` | `UInt32` | 84 | | `SourceTag` | | `number` | `UInt32` | 85 | | `DestinationTag` | | `number` | `UInt32` | 86 | 87 | We propose these modifications: 88 | | Field Name | Required? | JSON Type | Internal Type | 89 | |------------|-----------|-----------|---------------| 90 | |`Amount`| |`Amount`|`Amount`| 91 | |`NFTokenIDs`| |`array`|`STArray`| 92 | 93 | #### 3.1.1. `Amount` 94 | 95 | The `Amount` field is still used as it currently is, but it is now optional, to support escrows that only hold NFTs. An `EscrowCreate` transaction must have an `Amount` field and/or an `NFTokens` field. 96 | 97 | #### 3.1.2. `NFTokenIDs` 98 | 99 | This field contains the `NFTokenID`s of the `NFToken`s that the user wants to put in an escrow. There can be up to 32 NFTs in an escrow. 100 | 101 | # Appendix 102 | 103 | ## Appendix A: FAQ 104 | 105 | ### A.1: Why not use a separate `NFTokenEscrow` object? 106 | 107 | That felt like an unnecessary complication and would involve a lot of repeated code. 108 | 109 | [//]: # "Also, I didn't want to rewrite out every part of the current escrow implementation in the spec" 110 | 111 | ### A.2: Can I put XRP and NFTs in the same escrow? 112 | 113 | Yes. 114 | 115 | ### A.3: Why is this so much simpler than for issued currency escrows? 116 | 117 | The complication with issued currencies is that they must be held by accounts in [trustlines](https://xrpl.org/trust-lines-and-issuing.html). There is currently no way for a ledger object (such as an escrow) to own another ledger object (such as a trustline). 118 | 119 | NFTs have no such requirement - all that defines an NFT is the `NFTokenID` and the `URI`, which can easily be held by an object instead of an account. 120 | -------------------------------------------------------------------------------- /XLS-0037-concise-transaction-identifier-ctid/ctid.cpp: -------------------------------------------------------------------------------- 1 | #include
2 | xls: 10 3 | title: Non-Transferable Token (NTT) standard 4 | author: RichardAH (@RichardAH) 5 | created: 2020-04-05 6 | status: Stagnant 7 | category: Ecosystem 8 |9 | 10 | ## Changelog 11 | 12 | 28-7-21: Name of this standard was changed from `Issuer Controlled Token` to `Non-Transferable Token` to reflect industry norms 13 | 14 | ## 1. Introduction 15 | 16 | The XRPL supports 160bit currency codes as discussed here: [https://xrpl.org/currency-formats.html#nonstandard-currency-codes](https://xrpl.org/currency-formats.html#nonstandard-currency-codes) which include as a subset the standard three letter ISO 4217 codes the reader is probably familiar with (e.g. “USD”, “CAD”, etc.) 17 | 18 | The intention of currency codes on the XRPL is to provide fungible IOUs that can be “rippled” between accounts across trusted issuers, facilitating end to end value exchange across multiple unrelated parties on the ledger. 19 | 20 | However in recent years, particularly with the rise in popularity of programmable money platforms such as Ethereum, it has become apparent that tokens outside the typical fungible form might also fill important use-cases. 21 | 22 | ## 2. User Controlled Tokens 23 | 24 | The standard IOU on the XRPL exists as a relationship between two parties: a _user_ and an _issuer_. In order to use the issuer’s token, the user must first add a trustline to their XRPL account for the specified currency code and issuer. Once the trustline exists the issuer (or potentially another user) may send IOUs to that trustline balance. More background here: [https://xrpl.org/trust-lines-and-issuing.html](https://xrpl.org/trust-lines-and-issuing.html) 25 | 26 | ## 3. Non-transferable Tokens (aka Issuer Controlled Tokens) 27 | 28 | This standard proposes a second way to use the trustline mechanism on the XRPL: An issuer controlled token. 29 | 30 | In this model the user no longer is required to add a trustline to the issuer. In fact the user need not do anything. Instead the issuer creates a trustline from the user to itself with a nominal unity balance for a 160 bit “custom” currency code and with rippling disabled. This currency code contains the token information, the trustline itself is just a storage mechanism. 31 | 32 | Any third party looking at the user’s XRPL account will see this “token” attached to the user’s account. 33 | 34 | Issuer Controlled Tokens have the following properties: 35 | 36 | 1. The token is completely controlled by the issuer. The issuer may at any time close the trustline unilaterally, erasing the token from the ledger. 37 | 2. The token is inherently non-fungible (although a fungibility layer could be added through smart contacts.) 38 | 3. The issuer can revoke and re-issue the token at any time with an updated currency code. 39 | 4. The token’s currency encodes 160 bits of arbitrary data (152 bits if excluding the first 8 bits as a type code). 40 | 5. The token is publicly visible to anyone who looks up the user’s account on the XRPL. 41 | 42 | Possible uses for such a token include black and whitelisting / stamps, smart contract per-account state storage and payment and informational pointers expanding on the purpose, behaviour and/or ownership of an XRPL account. 43 | 44 | ## 4. Namespace 45 | 46 | Much like addresses in Internet Protocol space, 160bit currency codes are at risk of nonsensical and overlapping allocation without some sort of standardised allocation scheme. 47 | 48 | By convention the first byte of the 160bit currency code determines the currency code “type”. Byte 0x00 (XRP) cannot be used, and 0x01 has been used previously for demurrage. No complete database of known currency types exists. Some adhoc defacto standards have emerged such as “if the currency code decodes to valid ASCII then display the said ASCII string” but these are not widespread. 49 | 50 | In order to preserve top level “type” codes this standard proposes to reserve 0xFF as the type code for Issuer Controlled Tokens. 51 | 52 | ## 5. Token Specification 53 | 54 | [ Type = 0xFF ] [ Sub type = 0x0000-0xFFFF ] [ Payload = 136b ] 55 | 56 | Byte 0 – Type code: must be 0xFF meaning Issuer Controlled Token 57 | 58 | Byte 1-2 — Subtype code: Unsigned big endian 16 bit integer. Allocated according to a standards database such as this one. This number 0-65,535 tells wallets how to interpret this token. 59 | 60 | Byte 3-19 – Payload bytes: 136 bits of arbitrary data the token encodes. 61 | 62 | ## 6. Spam Prevention 63 | 64 | Since it is possible for anyone to be an issuer they may produce a token that appears on any user’s account (provided they are willing to lock up 5 XRP to do so). This produces something of a problem for wallet software: which tokens are legitimate and which are spam? 65 | 66 | Wallets should give the user the option to display all Issuer Controlled Tokens or to display only tokens from a whitelist pre-populated by the wallet software. That whitelist should include as a subset a community controlled whitelist described below. The wallet should also allow the end user to update and add entries to the whitelist. 67 | 68 | ## 7. Allocation and Community Bulletin Board 69 | 70 | It is proposed that a community “bulletin board” account be created on the XRPL that has no known private key, I.e. a blackhole account. To this account issuer controlled tokens will be assigned from a community XRPL account controlled collectively by key members of the XRPL community through multi-signing. 71 | 72 | In order to whitelist an issuer of a token, the issuer’s 20 byte account ID is truncated at the end by five bytes. Five bytes are then prepended to the start of the account ID in comprising: 0xFF FF FF, followed by the sub-type of the white listed token. 73 | 74 | An alternative way to do this would have been to open a trustline to the issuer from the community account, however the above has an additional advantage: by changing the sub-type of the entry to 0xFF FF F**E** we can signal that the token is actually blacklisted rather than whitelisted. Thus the concept of a bulletin board. 75 | 76 | Further information relevant to all wallets can be stored on the community bulletin board in this way, simply by allocating subtype numbers for those notice types. For example known scam accounts can be placed on the bulletin board, or URL pointers to lists of known scam accounts. 77 | -------------------------------------------------------------------------------- /XLS-0037-concise-transaction-identifier-ctid/ctid.py: -------------------------------------------------------------------------------- 1 | def encodeCTID(ledger_seq, txn_index, network_id): 2 | if not isinstance(ledger_seq, int): 3 | raise ValueError("ledger_seq must be a number.") 4 | if ledger_seq > 0xFFFFFFF or ledger_seq < 0: 5 | raise ValueError("ledger_seq must not be greater than 268435455 or less than 0.") 6 | 7 | if not isinstance(txn_index, int): 8 | raise ValueError("txn_index must be a number.") 9 | if txn_index > 0xFFFF or txn_index < 0: 10 | raise ValueError("txn_index must not be greater than 65535 or less than 0.") 11 | 12 | if not isinstance(network_id, int): 13 | raise ValueError("network_id must be a number.") 14 | if network_id > 0xFFFF or network_id < 0: 15 | raise ValueError("network_id must not be greater than 65535 or less than 0.") 16 | 17 | ctid_value = ((0xC0000000 + ledger_seq) << 32) + (txn_index << 16) + network_id 18 | return format(ctid_value, 'x').upper() 19 | 20 | def decodeCTID(ctid): 21 | if isinstance(ctid, str): 22 | if not ctid.isalnum(): 23 | raise ValueError("ctid must be a hexadecimal string or BigInt") 24 | 25 | if len(ctid) != 16: 26 | raise ValueError("ctid must be exactly 16 nibbles and start with a C") 27 | 28 | ctid_value = int(ctid, 16) 29 | elif isinstance(ctid, int): 30 | ctid_value = ctid 31 | else: 32 | raise ValueError("ctid must be a hexadecimal string or BigInt") 33 | 34 | if ctid_value > 0xFFFFFFFFFFFFFFFF or ctid_value & 0xF000000000000000 != 0xC000000000000000: 35 | raise ValueError("ctid must be exactly 16 nibbles and start with a C") 36 | 37 | ledger_seq = (ctid_value >> 32) & 0xFFFFFFF 38 | txn_index = (ctid_value >> 16) & 0xFFFF 39 | network_id = ctid_value & 0xFFFF 40 | return { 41 | 'ledger_seq': ledger_seq, 42 | 'txn_index': txn_index, 43 | 'network_id': network_id 44 | } 45 | 46 | // NOTE TO DEVELOPER: 47 | // you only need the two functions above, below are test cases, if you want them. 48 | 49 | import unittest 50 | 51 | class TestEncodeAndDecodeCTID(unittest.TestCase): 52 | def test(self): 53 | # Test case 1: Valid input values 54 | self.assertEqual(encodeCTID(0xFFFFFFF, 0xFFFF, 0xFFFF), "CFFFFFFFFFFFFFFF") 55 | self.assertEqual(encodeCTID(0, 0, 0), "C000000000000000") 56 | self.assertEqual(encodeCTID(1, 2, 3), "C000000100020003") 57 | self.assertEqual(encodeCTID(13249191, 12911, 49221), "C0CA2AA7326FC045") 58 | 59 | # Test case 2: ledger_seq greater than 0xFFFFFFF or less than 0 60 | with self.assertRaises(ValueError, msg="ledger_seq must not be greater than 268435455 or less than 0."): 61 | encodeCTID(0x10000000, 0xFFFF, 0xFFFF) 62 | encodeCTID(-1, 0xFFFF, 0xFFFF) 63 | 64 | # Test case 3: txn_index greater than 0xFFFF or less than 0 65 | with self.assertRaises(ValueError, msg="txn_index must not be greater than 65535 or less than 0."): 66 | encodeCTID(0xFFFFFFF, 0x10000, 0xFFFF) 67 | encodeCTID(0xFFFFFFF, -1, 0xFFFF) 68 | 69 | # Test case 4: network_id greater than 0xFFFF or less than 0 70 | with self.assertRaises(ValueError, msg="network_id must not be greater than 65535 or less than 0."): 71 | encodeCTID(0xFFFFFFF, 0xFFFF, -1) 72 | 73 | # Test case 5: Valid input values 74 | self.assertDictEqual(decodeCTID("CFFFFFFFFFFFFFFF"), {'ledger_seq': 0xFFFFFFF, 'txn_index': 0xFFFF, 'network_id': 0xFFFF}) 75 | self.assertDictEqual(decodeCTID("C000000000000000"), {'ledger_seq': 0, 'txn_index': 0, 'network_id': 0}) 76 | self.assertDictEqual(decodeCTID("C000000100020003"), {'ledger_seq': 1, 'txn_index': 2, 'network_id': 3}) 77 | self.assertDictEqual(decodeCTID("C0CA2AA7326FC045"), {'ledger_seq': 13249191, 'txn_index': 12911, 'network_id': 49221}) 78 | 79 | # Test case 6: ctid not a string or big int 80 | with self.assertRaises(ValueError, msg="ctid must be a hexadecimal string or BigInt"): 81 | decodeCTID(0xCFF) 82 | 83 | # Test case 7: ctid not a hexadecimal string 84 | with self.assertRaises(ValueError, msg="ctid must be a hexadecimal string or BigInt"): 85 | decodeCTID("C003FFFFFFFFFFFG") 86 | 87 | # Test case 8: ctid not exactly 16 nibbles 88 | with self.assertRaises(ValueError, msg="ctid must be exactly 16 nibbles and start with a C"): 89 | decodeCTID("C003FFFFFFFFFFF") 90 | 91 | # Test case 9: ctid too large to be a valid CTID value 92 | with self.assertRaises(ValueError, msg="ctid must be exactly 16 nibbles and start with a C"): 93 | decodeCTID("CFFFFFFFFFFFFFFFF") 94 | 95 | # Test case 10: ctid doesn't start with a C nibble 96 | with self.assertRaises(ValueError, msg="ctid must be exactly 16 nibbles and start with a C"): 97 | decodeCTID("FFFFFFFFFFFFFFFF") 98 | 99 | # the same tests again but using bigint instead of string 100 | # 101 | 102 | # Test case 11: Valid input values 103 | self.assertDictEqual(decodeCTID(0xCFFFFFFFFFFFFFFF), {'ledger_seq': 0xFFFFFFF, 'txn_index': 0xFFFF, 'network_id': 0xFFFF}) 104 | self.assertDictEqual(decodeCTID(0xC000000000000000), {'ledger_seq': 0, 'txn_index': 0, 'network_id': 0}) 105 | self.assertDictEqual(decodeCTID(0xC000000100020003), {'ledger_seq': 1, 'txn_index': 2, 'network_id': 3}) 106 | self.assertDictEqual(decodeCTID(0xC0CA2AA7326FC045), {'ledger_seq': 13249191, 'txn_index': 12911, 'network_id': 49221}) 107 | 108 | # Test case 12: ctid not exactly 16 nibbles 109 | with self.assertRaises(ValueError, msg="ctid must be exactly 16 nibbles and start with a C"): 110 | decodeCTID(0xC003FFFFFFFFFFF) 111 | 112 | # Test case 13: ctid too large to be a valid CTID value 113 | with self.assertRaises(ValueError, msg="ctid must be exactly 16 nibbles and start with a C"): 114 | decodeCTID(0xCFFFFFFFFFFFFFFFF) 115 | 116 | # Test case 14: ctid doesn't start with a C nibble 117 | with self.assertRaises(ValueError, msg="ctid must be exactly 16 nibbles and start with a C"): 118 | decodeCTID(0xFFFFFFFFFFFFFFFF) 119 | 120 | 121 | if __name__ == '__main__': 122 | (TestEncodeAndDecodeCTID()).test() 123 | -------------------------------------------------------------------------------- /XLS-0021-asset-code-prefixes/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 21 3 | title: 21: Allocating Asset Code Prefixes 4 | description: This proposal defines a mechanism for setting aside prefixes for specific formats and publishing a list of formats potentially in use. 5 | author: Rome Reginelli (@mDuo13) 6 | created: 2021-07-28 7 | status: Stagnant 8 | category: Ecosystem 9 |10 | 11 | # Proposal for Allocating Asset Code Prefixes 12 | 13 | Asset codes in the XRP Ledger protocol are natively 160 bits; the reference implementation of the core server defines a shortcut to display 3-character "ISO 4217-like" currency codes using ASCII. These [standard-currency codes](https://xrpl.org/currency-formats.html#standard-currency-codes) use the prefix `0x00` to distinguish them from other asset codes and to prevent haphazard and inconsistent decoding and display of asset codes. (Note: XRP does not usually use a currency code, but when it does, it has an all-zeroes value, which can be considered a special case of the Standard Currency Codes format.) 14 | 15 | Various other formats have been proposed and even implemented, but until now there has been no organization around the reserving of different prefixes. This proposal defines a mechanism for setting aside prefixes for specific formats and publishing a list of formats potentially in use. 16 | 17 | ## Canonical List 18 | 19 | A table of asset code prefixes would be added to xrpl.org and maintained by the XRPL.org contributors. A starting point for the table might be something like this: 20 | 21 | | Prefix | Status | Name | Standard | 22 | | ------------- | ------ | ------------------------------------------- | ------------------------------------------------------------------ | 23 | | `0x00` | ✅ | Standard Currency Codes | https://xrpl.org/currency-formats.html#standard-currency-codes | 24 | | `0x01` | ❌ | Interest-Bearing (Demurrage) Currency Codes | https://xrpl.org/demurrage.html | 25 | | `0x02` | ❌ | XLS-14d Non-Fungible Tokens | https://github.com/XRPLF/XRPL-Standards/discussions/30 | 26 | | `0x03` | 📄 | XLS-30d AMM LP Tokens | https://github.com/XRPLF/XRPL-Standards/discussions/78 | 27 | | `0x20`—`0x7E` | ⚠️ | Full ASCII codes | | 28 | | `0xEC` | 📄 | Extended Prefixes. | Reserved for additional prefixes. [See below](#extended-prefixes). | 29 | | `0xFF` | 📄 | Non-Transferrable Tokens | https://github.com/XRPLF/XRPL-Standards/discussions/20 | 30 | 31 | Any prefixes not listed in the table are considered available (🆓). 32 | 33 | Explanation of status labels: 34 | 35 | - ✅ Accepted. This prefix is used for a specific format which has been accepted an XRP Ledger Standard. 36 | - 📄 Proposed. This prefix has been set aside for use with a proposed or in-development standard. 37 | - ❌ Deprecated. This prefix was previously used, but the associated format is not currently recommended for implementation or use. 38 | - ⚠️ Reserved. This prefix does not have a proper standard, but is discouraged to prevent overlap with emergent usage or for other reasons. 39 | - 🆓 Available. This prefix has not yet been used by any standard format. 40 | 41 | ### Reserving Prefixes 42 | 43 | To reserve a prefix, one would create an [XRPL standard draft](https://github.com/XRPLF/XRPL-Standards) per the standard procedure, and note in the draft the request to receive either a specific unused prefix or the next available prefix. A maintainer for XRPL.org can check the proposal and, judging that it is made in good faith, update the list on XRPL.org to add the new prefix as "Reserved" with a link to the draft discussion. 44 | 45 | Proposals that reserve multiple prefixes are discouraged, but may be granted if after additional scrutiny it is justified to set aside multiple top-level prefixes for the proposal. In most cases, sub-types or variations on the same format can be defined in the asset code's payload data, such as the next 8 bits following the prefix. 46 | 47 | When the corresponding XRPL Standards Draft is accepted as a full standard, the prefix changes from the "Reserved" to "Accepted" status. A maintainer of XRPL.org can update the list to track the new status of the code, and update the link to point to the full standard. Similarly, a standards proposal can move an Accepted or Reserved prefix to Deprecated. 48 | 49 | If a Standards Draft is withdrawn, any prefix(es) reserved for that draft can be made available again or moved to Deprecated, based on the best judgment of maintainers as to whether the reuse of a prefix is likely to lead to confusion, incompatibilities, or other problems. 50 | 51 | ## Extended Prefixes 52 | 53 | Since it seems possible that, eventually, there may be more than 256 different formats, the prefix `0xEC` is reserved for additional prefixes. For any asset code starting in `0xEC`, the next 8 bits indicate the rest of the currency code. For example, `0xEC00` would be the first extended prefix, then `0xEC01`, and so on, with `0xECEC` being reserved for further-extended prefixes, recursively. 54 | 55 | Extended prefixes are less desirable than regular prefixes since, by the nature of the asset code's fixed size, extended prefixes have fewer remaining bits to work with. With a normal currency code, the prefix occupies 8 bits, leaving 152 bits for the rest of the asset code. With singly-extended prefixes, the prefix is 16 bits, leaving 144 bits of payload. With doubly-extended prefixes, it drops to 132 bits of payload, and so on. Still, this is a decent amount of room to work with, especially since each format can define a potentially large number of unique asset codes. 56 | 57 | ## ASCII Codes 58 | 59 | It seems that some XRPL users may already be using a de-facto standard of using the entire 160-bit asset code value to represent a text code / name for the asset. We can avoid overlapping with this usage by avoiding any codes that start with printable ASCII characters, which have codes from `0x20` through `0x7E`. 60 | 61 | There's probably not much we can do for asset codes in the wild that use extended ASCII or UTF-8. A subsequent proposal could set aside a prefix for a more standardized and flexible format. (For example, you may want to set aside a few bytes for a currency symbol e.g. $, €, ¥, or 💩.) 62 | -------------------------------------------------------------------------------- /XLS-0037-concise-transaction-identifier-ctid/ctid.ts: -------------------------------------------------------------------------------- 1 | const encodeCTID = ( 2 | ledger_seq: number, 3 | txn_index: number, 4 | network_id: number, 5 | ): string => { 6 | if (ledger_seq > 0xfffffff || ledger_seq < 0) 7 | throw new Error( 8 | "ledger_seq must not be greater than 268435455 or less than 0.", 9 | ); 10 | 11 | if (txn_index > 0xffff || txn_index < 0) 12 | throw new Error("txn_index must not be greater than 65535 or less than 0."); 13 | 14 | if (network_id > 0xffff || network_id < 0) 15 | throw new Error( 16 | "network_id must not be greater than 65535 or less than 0.", 17 | ); 18 | 19 | return ( 20 | ((BigInt(0xc0000000) + BigInt(ledger_seq)) << 32n) + 21 | (BigInt(txn_index) << 16n) + 22 | BigInt(network_id) 23 | ) 24 | .toString(16) 25 | .toUpperCase(); 26 | }; 27 | 28 | const decodeCTID = ( 29 | ctid: string | bigint, 30 | ): { ledger_seq: number; txn_index: number; network_id: number } => { 31 | let ctidValue: bigint; 32 | if (typeof ctid === "string") { 33 | if (!/^[0-9A-F]+$/.test(ctid)) 34 | throw new Error("ctid must be a hexadecimal string or BigInt"); 35 | if (ctid.length !== 16) 36 | throw new Error("ctid must be exactly 16 nibbles and start with a C"); 37 | 38 | ctidValue = BigInt(`0x${ctid}`); 39 | } else ctidValue = ctid; 40 | 41 | if ( 42 | ctidValue > 0xffffffffffffffffn || 43 | (ctidValue & 0xf000000000000000n) !== 0xc000000000000000n 44 | ) 45 | throw new Error("ctid must be exactly 16 nibbles and start with a C"); 46 | 47 | const ledger_seq = Number((ctidValue >> 32n) & 0xfffffffn); 48 | const txn_index = Number((ctidValue >> 16n) & 0xffffn); 49 | const network_id = Number(ctidValue & 0xffffn); 50 | return { ledger_seq, txn_index, network_id }; 51 | }; 52 | 53 | // NOTE TO DEVELOPER: 54 | // you only need the two functions above, below are test cases, if you want them. 55 | import { strict as assert } from "assert"; 56 | const tests = (): void => { 57 | console.log("Running test cases..."); 58 | // Test cases For encodeCTID 59 | 60 | // Test case 1: Valid input values 61 | assert.equal(encodeCTID(0xfffffff, 0xffff, 0xffff), "CFFFFFFFFFFFFFFF"); 62 | assert.equal(encodeCTID(0, 0, 0), "C000000000000000"); 63 | assert.equal(encodeCTID(1, 2, 3), "C000000100020003"); 64 | assert.equal(encodeCTID(13249191, 12911, 49221), "C0CA2AA7326FC045"); 65 | 66 | // Test case 2: ledger_seq greater than 0xFFFFFFF 67 | assert.throws( 68 | () => encodeCTID(0x10000000, 0xffff, 0xffff), 69 | /ledger_seq must not be greater than 268435455 or less than 0./, 70 | ); 71 | assert.throws( 72 | () => encodeCTID(-1, 0xffff, 0xffff), 73 | /ledger_seq must not be greater than 268435455 or less than 0./, 74 | ); 75 | 76 | // Test case 3: txn_index greater than 0xFFFF 77 | assert.throws( 78 | () => encodeCTID(0xfffffff, 0x10000, 0xffff), 79 | /txn_index must not be greater than 65535 or less than 0./, 80 | ); 81 | assert.throws( 82 | () => encodeCTID(0xfffffff, -1, 0xffff), 83 | /txn_index must not be greater than 65535 or less than 0./, 84 | ); 85 | 86 | // Test case 4: network_id greater than 0xFFFF 87 | assert.throws( 88 | () => encodeCTID(0xfffffff, 0xffff, 0x10000), 89 | /network_id must not be greater than 65535 or less than 0./, 90 | ); 91 | assert.throws( 92 | () => encodeCTID(0xfffffff, 0xffff, -1), 93 | /network_id must not be greater than 65535 or less than 0./, 94 | ); 95 | 96 | // Test cases For decodeCTID 97 | 98 | // Test case 5: Valid input values 99 | assert.deepEqual(decodeCTID("CFFFFFFFFFFFFFFF"), { 100 | ledger_seq: 0xfffffff, 101 | txn_index: 0xffff, 102 | network_id: 0xffff, 103 | }); 104 | assert.deepEqual(decodeCTID("C000000000000000"), { 105 | ledger_seq: 0, 106 | txn_index: 0, 107 | network_id: 0, 108 | }); 109 | assert.deepEqual(decodeCTID("C000000100020003"), { 110 | ledger_seq: 1, 111 | txn_index: 2, 112 | network_id: 3, 113 | }); 114 | assert.deepEqual(decodeCTID("C0CA2AA7326FC045"), { 115 | ledger_seq: 13249191, 116 | txn_index: 12911, 117 | network_id: 49221, 118 | }); 119 | 120 | // Test case 6: ctid not a string or big int 121 | // impossible in typescript, left commented for completeness 122 | //assert.throws(() => decodeCTID(0xCFF), /ctid must be a hexadecimal string or BigInt/); 123 | 124 | // Test case 7: ctid not a hexadecimal string 125 | assert.throws( 126 | () => decodeCTID("C003FFFFFFFFFFFG"), 127 | /ctid must be a hexadecimal string or BigInt/, 128 | ); 129 | 130 | // Test case 8: ctid not exactly 16 nibbles 131 | assert.throws( 132 | () => decodeCTID("C003FFFFFFFFFFF"), 133 | /ctid must be exactly 16 nibbles and start with a C/, 134 | ); 135 | 136 | // Test case 9: ctid too large to be a valid CTID value 137 | assert.throws( 138 | () => decodeCTID("CFFFFFFFFFFFFFFFF"), 139 | /ctid must be exactly 16 nibbles and start with a C/, 140 | ); 141 | 142 | // Test case 10: ctid doesn't start with a C nibble 143 | assert.throws( 144 | () => decodeCTID("FFFFFFFFFFFFFFFF"), 145 | /ctid must be exactly 16 nibbles and start with a C/, 146 | ); 147 | 148 | // Test case 11: Valid input values 149 | assert.deepEqual(decodeCTID(0xcfffffffffffffffn), { 150 | ledger_seq: 0xfffffff, 151 | txn_index: 0xffff, 152 | network_id: 0xffff, 153 | }); 154 | assert.deepEqual(decodeCTID(0xc000000000000000n), { 155 | ledger_seq: 0, 156 | txn_index: 0, 157 | network_id: 0, 158 | }); 159 | assert.deepEqual(decodeCTID(0xc000000100020003n), { 160 | ledger_seq: 1, 161 | txn_index: 2, 162 | network_id: 3, 163 | }); 164 | assert.deepEqual(decodeCTID(0xc0ca2aa7326fc045n), { 165 | ledger_seq: 13249191, 166 | txn_index: 12911, 167 | network_id: 49221, 168 | }); 169 | 170 | // Test case 12: ctid not exactly 16 nibbles 171 | assert.throws( 172 | () => decodeCTID(0xc003fffffffffffn), 173 | /ctid must be exactly 16 nibbles and start with a C/, 174 | ); 175 | 176 | // Test case 13: ctid too large to be a valid CTID value 177 | assert.throws( 178 | () => decodeCTID(0xcffffffffffffffffn), 179 | /ctid must be exactly 16 nibbles and start with a C/, 180 | ); 181 | 182 | // Test case 14: ctid doesn't start with a C nibble 183 | assert.throws( 184 | () => decodeCTID(0xffffffffffffffffn), 185 | /ctid must be exactly 16 nibbles and start with a C/, 186 | ); 187 | 188 | console.log("Done."); 189 | }; 190 | 191 | tests(); 192 | -------------------------------------------------------------------------------- /XLS-0017-xfl/README.md: -------------------------------------------------------------------------------- 1 |
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: System 9 |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 "
2 | xls: 50 3 | title: Aiming for a healthy distribution of (validator) infrastructure 4 | description: Best practices for the geographical & provider distribution of validators 5 | author: Wietse Wind (@WietseWind) 6 | created: 2023-11-14 7 | status: Final 8 | category: Ecosystem 9 |10 | 11 | # Abstract 12 | 13 | The network is at risk of centralised consensus failure when too many validators (especially dUNL validators) are running on the same cloud provider/network. While negative [UNL helps](.), the network is still at the risk of a halt when a large infra/cloud provider has an outage. 14 | 15 | This proposal introduces best practices for the geographical & provider distribution of validators, something those composing dUNL contents & those adding trusted validators to their `rippled` & `xahaud` config manually. 16 | 17 | ℹ️ Note: this proposal is only focussing at **validators**, as their (instant lack of) availability could harm network forward progress. All arguments (motivation) apply to RPC nodes, hubs, as well, but e.g. RPC nodes could benefit from live HTTP failover availability & them being offline doesn't directly harm the network's forward progress. 18 | 19 | # Motivation 20 | 21 | With high performing cloud servers (VPS, dedicated) are more and more common, and with cloud providers often beating convenience and price of self hosting / colocation, several validator operators are running their validators at the same cloud providers. Independently executed network crawls show large clusters of validators at only a handful cloud providers. Most common providers are: 22 | 23 | - Google (Google Cloud Platform) 24 | - Amazon AWS 25 | - DigitalOcean 26 | - Hetzner 27 | 28 | When looking at traceroutes, things look even worse when taking the datacenter/POP of these cloud providers into account, as cloud providers, through availability and pricing, route customers to specific preferred datacenter locations (and thus: routes). 29 | 30 | As a result of a significant number of validators running on cloud infra at a small number of cloud providers, an outage at one cloud provider (datacenter, power, infra, routing, BGP, ...) can potentially drop the network below the consensus threshold. With Negative UNL taking 256 ledgers to kick in, this is unwanted. 31 | 32 | # Proposal 33 | 34 | Things to take into account picking a suitable provider (cloud, infrastructure): 35 | 36 | - Significant distribution across ASN (networks) as per validator IP address 37 | - Significant distribution across physical location (datacenters, POPs) 38 | 39 | ### 1. Validator operator resource location selection 40 | 41 | Validator operators (even when not on the UNL, if they prefer to be on dUNL / in each other's validator lists) should always: 42 | 43 | - Take the above into account 44 | - Preferably prevent running their validator on a cloud provider 45 | - If running on a cloud provider: preferably pick the least used cloud provider & cloud provider datacenter location (POP) by other validator operators 46 | 47 | ### 2. TOML contents 48 | 49 | A typical `validator` section in the `xrp-ledger.toml` and `xahau.toml` preferably contains a `server_country` property: 50 | 51 | ```toml 52 | [[VALIDATORS]] 53 | ... 54 | server_country = "??" 55 | ``` 56 | 57 | The following properties **must** be added: 58 | 59 | - `network_asn` (integer) containing the ASN (autonomous system number) of the IP address the validator uses to connect out (see Appendix A & Appendix B) 60 | 61 | #### ⚖️ **CONSIDERATION: publishing the ASN could be considered an attack vector, as it would expose the provider to DDoS to take down connectivity to certain validators. However, if the network is sufficiently decentralised/distributed from a infrastructure point of view, taking down one or to routes wouldn't harm the network.** 62 | 63 | The following properties **should** be added: 64 | 65 | - `server_location` (string) containing a written explanation of provider provided location details (see Appendix A) 66 | - `server_cloud` (boolean) if the server is running at a cloud provider (e.g. VPS / cloud dedicated: **a server you will never physically see, won't know how to find is a cloud server.**, so VPS / dedicated rented machine: cloud = true. Your own bare metal, colocated: cloud = false) 67 | 68 | #### ⚠️ **NEVER PUBLISH YOUR VALIDATOR IP ADDRESS IN YOUR TOML!** 69 | 70 | ### 3. UNL publishers & validator operators 71 | 72 | UNL publishers & validator operators should start to obtain ASN & geographical location from trusted validator operators, and take them into account when composing the UNL list contents. 73 | 74 | UNL publishers should not add validators to the UNL without the above TOML properties. 75 | 76 | # Call for immediate action 77 | 78 | ### Validator operators 79 | 80 | - Update your TOML with the above information 81 | - Check your manual validator list additions for the above properties 82 | 83 | ### UNL publishers 84 | 85 | - Check the validators on the published UNL for the above TOML properties 86 | - Call for adding the above TOML properties 87 | - Communicate a "Grace Period" for those not disclosing this info 88 | - (If communication is possible) work with operators of reliable validators if they are running on cloud/infra hotspots, ask them to migrate 89 | - (If communication is not possible) over time, replace validator operators on cloud/infra hotspots, for reliable validators providing more provider & geographical redundancy. 90 | 91 | # Appendix A - sample TOML values 92 | 93 | ### Example: Cloud: VPS / Rented dedicated 94 | 95 | ```toml 96 | server_country = "NL" 97 | server_location = "DigitalOcean AMS3 (Amsterdam, NL)" 98 | server_cloud = true 99 | network_asn = 14061 100 | ``` 101 | 102 | ### Example: Non-Cloud: Self hosted or own hardware, colocated 103 | 104 | ```toml 105 | server_country = "NL" 106 | server_location = "Private datacenter (Utrecht area, NL)" 107 | server_cloud = false 108 | network_asn = 16089 109 | ``` 110 | 111 | # Appendix B - obtaining the ASN (autonomous system number) 112 | 113 | To obtain the ASN for your IP address (usually your provider or provider's upstream provider), you will need the IP address used by your validator to connect out to others. Providing there are no proxy settings & the machine has native IP connectivity to the outside world, you can find/confirm your outgoing IP address by executing (bash): 114 | 115 | ```bash 116 | curl --silent https://myip.wtf/text 117 | ``` 118 | 119 | You can find the ASN for the IP range this IP is allocated from using: 120 | 121 | ```bash 122 | whois -h whois.cymru.com {ip} 123 | ``` 124 | 125 | A one-line command to obtain IP and obtain ASN: 126 | 127 | ```bash 128 | whois -h whois.cymru.com $(curl --silent https://myip.wtf/text) 129 | ``` 130 | 131 | Sample output: 132 | 133 | ``` 134 | AS | IP | AS Name 135 | 14061 | 31.239.49.82 | MyBackYardServerRack LTD USA 136 | ``` 137 | 138 | In this case `14061` is the integer value for your `network_asn` property in your TOML file. 139 | 140 | #### ⚠️ **Warning! The AS name may contain a location identifier. This usually refers to the original place the IP range was allocated / the headquarters of the owner of the IP range. This is in NO WAY an indication of the physical location of your machine.** 141 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | > [!IMPORTANT] 4 | > This process is in a state of flux right now, and this document is still referring to the old process. Please refer to https://github.com/XRPLF/XRPL-Standards/discussions/340 instead. 5 | 6 | The work of the [XRP Ledger](https://xrpl.org) community is open, collaborative, and welcoming of all contributors participating in good faith. Part of that effort involves standardization, and this document outlines how anyone can contribute to that process. 7 | 8 | ## Licensing 9 | 10 | Any XRPL Standards document can be referred to interchangeably as an "XLS", "XRPL Standard", or "document". Copyright on all content is subject to the terms of this [license](LICENSE), and all contributors grant everyone a royalty-free license to use their contributions, including the following grants: 11 | 12 | - Copyright: a royalty-free license to anyone to use any contributions submitted to this repository. 13 | - Patent: a commitment to license on a royalty-free basis any essential patent claims relating to any contributions in this repository. 14 | 15 | ## Specification Process 16 | 17 | ### 1. Start a Discussion & Gather Feedback 18 | 19 | Before opening a PR with any kind of formal proposal, please first gather community input by starting a [Discussion](https://github.com/XRPLF/XRPL-Standards/discussions). Discussions are suitable for early work-in-progress: ask, suggest, add, and make sweeping changes. Collecting such feedback helps to refine your concept, and is required in order to move forward in the specification process. 20 | 21 | #### Discussion Title 22 | 23 | When creating a new discussion for your idea, the discussion title should follow the naming convention `XLS-{0000}d {Title}`, where `{0000}` is a unique number for the XLS, `d` indicates that the document is a Draft (i.e., work in progress), and `{Title}` is a descriptive title for the proposed document. 24 | 25 | #### Specification Number 26 | 27 | Use the next number that has not yet been used. If a conflict occurs, it will be fixed by a maintainer or editor. Maintainers or editors also reserve the right to remove or re-number proposals as necessary. The number is important, as it will be used to reference features and ideas throughout the community. 28 | 29 | ### 2. Closing a Discussion 30 | 31 | When a discussion has produced a well-refined standard, authors should post a comment to the discussion noting that the discussion will be closed in a few days. This allows time (for those engaged with the discussion) to submit final commentary. 32 | 33 | Once this waiting period has elapsed, the standard's author may close the discussion from further comment, and then move the discussion to a [**specification pull request**](#3-specification-pull-requests) to add the standard into the repository as a markdown file (see below for specification formats). 34 | 35 | Next, the discussion author should edit the discussion to include a link to the PR. The last comment on the discussion should also be a link to the PR. 36 | 37 | The intention of this workflow is that the discussion be closed from further comments, with further comments instead being posted on the PR for fine-tuning and alignment with implementation or adoption, as appropriate. 38 | 39 | ### 3. Specification Pull Requests 40 | 41 | When opening a specification PR, there are two document types: _Drafts_ and _Candidate Specifications_. The type and status of any particular document has no bearing on the priority of that document. Typically, the further along in the process, the more likely it is that any particular XLS will be implemented or adopted. 42 | 43 | #### Drafts 44 | 45 | A _Draft_ is a document that proposes some new feature, protocol or idea related to the XRP Ledger. The criteria for getting the document merged is minimal: project maintainers will review the document to ensure style conformance and applicability, but will otherwise not require editorial fixes before allowing it to be published. 46 | 47 | A document does not need to have an implementation in order to become a Draft. A Draft may or may not have implementation(s) available and no code is required prior to the Draft being published. 48 | 49 | A Draft is often stable enough for people to experiment with, but has not necessarily been endorsed by the entire community. When there are competing Drafts that address the same problem in different ways, all such Drafts can continue to be refined, promoted, and used independently, without any blocking. Implementors can create software to implement this type of standard into their codebase to explore how it works in a real world setting. 50 | 51 | Any, or all, competing Drafts _may_ graduate into a Candidate Specification. 52 | 53 | Notice that a Draft is not a [rubber-stamp](https://idioms.thefreedictionary.com/rubber-stamp) of the content in any particular document. Documents can still undergo significant changes and potentially be discarded all together. 54 | 55 | ##### Publishing a Draft 56 | 57 | To publish a new Draft, submit a Pull Request to this repo with a new folder and a new Markdown file. The folder MUST follow the naming convention `XLS-{0000}d-{title}` where `{0000}` is the unique number referencing the XLS, `d` indicates that the document is a Draft, and `{title}` is a lower case title with spaces replaced by hyphens (`-`). The submission should have front-matter (similar to GitHub pages rendered from Markdown) specifying at least a `title` and `type`. The `type` MUST have the value `draft`. 58 | 59 | An example draft name is: `XLS-20d-non-fungible-token-support-native` 60 | 61 | Use the following template when creating the Markdown file: [xls-template.md](./xls-template.md) 62 | 63 | Assuming there is consensus to publish, one of the project maintainers will review the submission and confirm the document's XLS number, often making a follow-up commit to the PR which renames the file as appropriate. 64 | 65 | #### Candidate Specifications 66 | 67 | A _Candidate Specification_ is a document that was previously a Draft, but is considered stable enough by the community such that no further changes are required. Once an XLS becomes a Candidate Specification, no further substantive changes are allowed under the same XLS number. 68 | 69 | Refinements in detail are still allowed and recommended. For example, you can clarify exact error cases and define the error codes and transaction result codes that occur in those cases. 70 | 71 | ##### Publishing a Candidate Specification 72 | 73 | When a Draft is considered stable, there is a call for review from the community to publish the document as a Candidate Specification by making a PR to remove the `d` from the document folder name and update the `type` to `candidate-specification`. 74 | 75 | Once published as a Candidate Specification, no further substantive changes are allowed under the same XLS number. 76 | 77 | For Specifications that require changes or implementation in the XRP Ledger server software and protocol, the Candidate Specification cannot be published until the relevant change has been merged into [the software's `master` branch](https://github.com/XRPLF/rippled/tree/master). 78 | 79 | #### Errata 80 | 81 | The community may discover errors in a Candidate Specification. In these circumstances, it is possible to update the document to fix typos or clarify the original meaning of the document. 82 | 83 | ### Deprecated or Rejected XLSs 84 | 85 | An XLS document may be rejected after public discussion has settled and comments have been made summarizing the rationale for rejection. Similarly, a document may be deprecated when its use should be discouraged. A member of the core maintainer team will move rejected and deprecated proposals to the `/rejected` folder in this repo. 86 | -------------------------------------------------------------------------------- /XLS-0022-api-versioning/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 22 3 | title: rippled API Versioning 4 | description: The API version number allows for evolving the `rippled` API while maintaining backward compatibility 5 | author: Elliot Lee (@intelliot), Peng Wang (@pwang200) 6 | status: Final 7 | category: System 8 | created: 2021-08-11 9 |10 | 11 | # rippled API Versioning 12 | 13 | rippled offers an API (application programming interface) that apps use to integrate with the XRP Ledger. In order to evolve and improve this API over time, while providing consistency and ensuring backward compatibility for clients, the API shall be versioned with an `API version number`. 14 | 15 | For JSON-RPC and WebSocket (the most commonly-used interfaces), the API version number field is called `"api_version"`. 16 | 17 | ### JSON-RPC 18 | 19 | ```json 20 | { 21 | "method": "...", 22 | "params": [ 23 | { 24 | "api_version": 1, 25 | "...": "..." 26 | } 27 | ] 28 | } 29 | ``` 30 | 31 | Notice that "api_version" is [not a top-level field](https://github.com/ripple/rippled/issues/3065). 32 | 33 | ### WebSocket 34 | 35 | ```json 36 | { 37 | "api_version": 1, 38 | "id": 4, 39 | "command": "...", 40 | "...": "..." 41 | } 42 | ``` 43 | 44 | Following the [WebSocket request format](https://xrpl.org/request-formatting.html), it is placed at the top level. 45 | 46 | ### rippled command line 47 | 48 | The RPC command and its parameters are parsed first. Then, a JSON-RPC request is created. rippled shall not support multiple versions of the command-parameter parsers, so the JSON-RPC requests will have one API version number. The latest API version number shall be used. Under the hood, rippled shall insert the `"api_version"` field internally. 49 | 50 | ### gRPC 51 | 52 | The version number for gRPC is specified as part of the package name of the gRPC service definition (in .proto files). The .proto files of different versions shall be placed in version-specific folders. 53 | 54 | Multiple versions of the API are created by constructing multiple services, located in different namespaces. From gRPC's perspective, they are simply different services. All of the services can be registered to a single gRPC server. 55 | 56 | ## Specification 57 | 58 | **The API version number shall be a single unsigned integer.** This is simpler than the major.minor.patch format, which is already used for versions of rippled itself. When it comes to API versions, there is no need for additional complexity beyond a single integer. 59 | 60 | **The API version number shall be increased if, and only if, we introduce one or more breaking changes to the API.** When the version number is increased, it is increased by one. 61 | 62 | **The lowest valid version number shall be 1.** The version number 0 is invalid. 63 | 64 | **rippled shall support a range of versions with consecutive version numbers.** At the moment, the range is [1, 1]. 65 | 66 | **Client requests targeting an API version that is out of the supported version range shall be rejected.** For safety and ease of understanding, we do not allow "forward compatibility" where, if a version number in the request is higher than the supported range, we would lower its version number to the highest supported version number. Most clients know in advance which rippled servers they are connecting to (indeed, this is required for the XRPL security model). So it is unlikely that they will "mistakenly" make a request to a server that does not support the API version that they require. And if they do, it is better to respond with an error: the client can then choose to handle the error in a way that is appropriate for its use case. 67 | 68 | **Client targeting APIs with versions 2 and above shall specify the version number in their requests.** The WebSocket and JSON-RPC (HTTP) requests shall include the version number in the request payloads. 69 | 70 | **Requests without a version number shall be handled as version 1 requests.** This provides backward compatibility to the time when API version numbers did not yet exist. 71 | 72 | **Responses do not need to include the API version number.** This saves some bandwidth and encourages the "best practice" of having the client track its requests with the `id` field. 73 | 74 | **When a client sends multiple requests over a persistent connection (e.g. WebSockets), each request may use a different API version number.** This gives clients flexibility, with the ability to "progressively" update to new API versions in specific requests, without changing their entire integration. 75 | 76 | **When using the rippled command line interface, the latest API version shall be used.** The command line interface is typically used for development, debugging, and one-off requests. For simplicity and to ensure that developers always pay attention to the evolution of the API, these requests shall use the latest API version. 77 | 78 | **An API version that available in a stable release of rippled is considered "ready".** From the client's perspective, alpha/beta API versions generally should not exist. 79 | 80 | ## Implementation Details 81 | 82 | There are four (4) RPC interfaces: 83 | 84 | 1. JSON-RPC (HTTP) 85 | 2. WebSocket 86 | 3. rippled command line 87 | 4. gRPC 88 | 89 | The API version number is a single 32-bit unsigned integer. A range of consecutive API versions are supported by rippled. The lower and upper bounds of the range shall be hardcoded. Different rippled software versions may support different API version ranges. 90 | 91 | For JSON-RPC and WebSocket, when a client requests an API version that is out of the supported range, the returned error message shall include the string "Unsupported API version", the version number targeted by the request, and the lower and upper bounds of the supported range. 92 | 93 | ## What is considered a breaking change? 94 | 95 | - Deleting (removing), renaming, changing the type of, or changing the meaning of a field of a request or a response. 96 | - Changing the order of position-based parameters, or inserting a new field in front of existing position-based parameters. 97 | - Deleting or renaming an API method (function). 98 | - Changing the behavior of an API method in a way that is visible to existing clients. 99 | - Changing HTTP status codes[^1]. 100 | 101 | gRPC only: 102 | 103 | - Changing a proto field number. 104 | - Deleting or renaming an enum or enum value. 105 | - Moving fields into or out of a oneof, split, or merge oneof. 106 | - Changing the label of a message field, i.e. optional, repeated, required. 107 | - Changing the stream value of a method request or response. 108 | - Deleting or renaming a package or service. 109 | 110 | ## What is not a breaking change? 111 | 112 | - Adding a new field to a request or response message, if it is not a position-based parameter. 113 | - Adding a new API method (function). 114 | 115 | ## References 116 | 117 | - [Documentation: API Versioning](https://xrpl.org/request-formatting.html#api-versioning) 118 | - [API versioning #3155 (rippled PR)](https://github.com/ripple/rippled/pull/3155): Merged and released in [rippled v1.5.0](https://github.com/ripple/rippled/releases/tag/1.5.0) 119 | - [Original Requirements](https://github.com/pwang200/RippledRPCDesign/blob/API_versioning/requirement/requirements.md) 120 | - [Original Design Document](https://github.com/pwang200/RippledRPCDesign/blob/API_versioning/design/design.md) 121 | 122 | [^1]: 123 | Changes to response status codes. For example, this is a breaking change: 124 | **Before:** POST /values returns 400 for missing property 125 | **After:** POST /values returns 409 for missing property 126 | [Reference](https://community.blackbaud.com/blogs/69/3219) 127 | -------------------------------------------------------------------------------- /site/build_site.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Build script for XLS Standards static site generator. 4 | Converts markdown XLS files to HTML and creates an index page. 5 | """ 6 | 7 | import os 8 | import shutil 9 | from pathlib import Path 10 | 11 | import markdown 12 | from jinja2 import Environment, FileSystemLoader 13 | 14 | from xls_parser import find_xls_documents 15 | from collections import Counter 16 | 17 | 18 | def convert_markdown_to_html(content: str) -> str: 19 | """Convert markdown content to HTML.""" 20 | md = markdown.Markdown( 21 | extensions=["extra", "codehilite", "toc", "tables"], 22 | extension_configs={ 23 | "codehilite": {"css_class": "highlight"}, 24 | "toc": {"permalink": True}, 25 | }, 26 | ) 27 | return md.convert(content) 28 | 29 | 30 | def build_site(): 31 | """Main function to build the static site.""" 32 | 33 | # Setup directories 34 | source_dir = Path(__file__).parent.resolve() 35 | root_dir = source_dir.parent 36 | site_dir = source_dir / "_site" 37 | template_dir = source_dir / "templates" 38 | assets_dir = source_dir / "assets" 39 | 40 | # Set base URL for GitHub Pages (can be overridden with env var) 41 | base_url = os.environ.get("GITHUB_PAGES_BASE_URL", "/XRPL-Standards") if "GITHUB_REPOSITORY" in os.environ else os.environ.get("GITHUB_PAGES_BASE_URL", ".") 42 | 43 | # Clean and create site directory 44 | if site_dir.exists(): 45 | shutil.rmtree(site_dir) 46 | site_dir.mkdir() 47 | 48 | # Create subdirectories 49 | (site_dir / "xls").mkdir() 50 | (site_dir / "category").mkdir() # New directory for category pages 51 | (site_dir / "assets").mkdir() 52 | 53 | # Setup Jinja2 environment 54 | if not template_dir.exists(): 55 | raise FileNotFoundError(f"Templates directory not found: {template_dir}") 56 | 57 | env = Environment(loader=FileSystemLoader(template_dir)) 58 | 59 | # Find and parse all XLS documents using the parser module 60 | xls_docs = find_xls_documents(root_dir) 61 | 62 | # Generate HTML for each document 63 | for doc in xls_docs: 64 | folder = root_dir / doc.folder 65 | readme_path = folder / "README.md" 66 | 67 | try: 68 | with open(readme_path, "r", encoding="utf-8") as f: 69 | content = f.read() 70 | 71 | # Convert to HTML 72 | html_content = convert_markdown_to_html(content) 73 | 74 | # Render XLS page 75 | xls_template = env.get_template("xls.html") 76 | rendered_html = xls_template.render( 77 | doc=doc, 78 | content=html_content, 79 | title=f"XLS-{doc.number}: {doc.title}", 80 | base_url=".." if base_url == "." else base_url, 81 | ) 82 | 83 | # Write XLS HTML file 84 | output_path = site_dir / "xls" / f"{doc.folder}.html" 85 | with open(output_path, "w", encoding="utf-8") as f: 86 | f.write(rendered_html) 87 | 88 | print(f"Generated: {output_path}") 89 | 90 | except Exception as e: 91 | print(f"Error processing {doc.folder}: {e}") 92 | raise 93 | 94 | # Sort documents by number in reverse order (later ones more relevant) 95 | xls_docs.sort(key=lambda x: int(x.number), reverse=True) 96 | 97 | # Group documents by category for category pages and navigation 98 | categories = {} 99 | for doc in xls_docs: 100 | category = doc.category 101 | if category not in categories: 102 | categories[category] = [] 103 | categories[category].append(doc) 104 | 105 | # Generate category pages 106 | category_template = env.get_template("category.html") 107 | all_categories = [(cat, len(docs)) for cat, docs in sorted(categories.items())] 108 | 109 | for category, category_docs in categories.items(): 110 | # Sort category documents by number in reverse order 111 | category_docs.sort(key=lambda x: int(x.number), reverse=True) 112 | 113 | category_html = category_template.render( 114 | title=f"{category} XLS Standards", 115 | category=category, 116 | category_docs=category_docs, 117 | all_categories=all_categories, 118 | total_count=len(xls_docs), 119 | base_url=".." if base_url == "." else base_url, 120 | ) 121 | 122 | # Write category HTML file 123 | category_file = site_dir / "category" / f"{category.lower()}.html" 124 | with open(category_file, "w", encoding="utf-8") as f: 125 | f.write(category_html) 126 | 127 | print(f"Generated category page: {category_file}") 128 | 129 | # Generate index page with category navigation 130 | index_template = env.get_template("index.html") 131 | index_html = index_template.render( 132 | title="XRP Ledger Standards (XLS)", 133 | total_count=len(xls_docs), 134 | xls_docs=xls_docs, 135 | all_categories=all_categories, 136 | base_url=base_url, 137 | ) 138 | 139 | # Write index file 140 | with open(site_dir / "index.html", "w", encoding="utf-8") as f: 141 | f.write(index_html) 142 | 143 | # Generate contribute page from CONTRIBUTING.md 144 | contributing_path = root_dir / "CONTRIBUTING.md" 145 | if contributing_path.exists(): 146 | try: 147 | with open(contributing_path, "r", encoding="utf-8") as f: 148 | contributing_content = f.read() 149 | 150 | # Convert markdown to HTML 151 | contributing_html_content = convert_markdown_to_html(contributing_content) 152 | 153 | # Render contribute page 154 | contribute_template = env.get_template("contribute.html") 155 | contribute_html = contribute_template.render( 156 | title="Contributing to XLS Standards", 157 | content=contributing_html_content, 158 | base_url=base_url, 159 | ) 160 | 161 | # Write contribute file 162 | with open(site_dir / "contribute.html", "w", encoding="utf-8") as f: 163 | f.write(contribute_html) 164 | 165 | print(f"Generated contribute page from CONTRIBUTING.md") 166 | 167 | except Exception as e: 168 | print(f"Error generating contribute page: {e}") 169 | else: 170 | print("Warning: CONTRIBUTING.md not found") 171 | 172 | # Copy CSS file 173 | css_source = assets_dir / "style.css" 174 | css_dest = site_dir / "assets" / "style.css" 175 | if css_source.exists(): 176 | shutil.copy2(css_source, css_dest) 177 | else: 178 | raise FileNotFoundError(f"CSS file not found: {css_source}") 179 | 180 | # Copy favicon 181 | favicon_source = assets_dir / "favicon.ico" 182 | favicon_dest = site_dir / "assets" / "favicon.ico" 183 | if favicon_source.exists(): 184 | shutil.copy2(favicon_source, favicon_dest) 185 | else: 186 | print(f"Warning: Favicon not found: {favicon_source}") 187 | 188 | print(f"Site built successfully! Generated {len(xls_docs)} XLS documents.") 189 | 190 | # Count by status for reporting 191 | # Count documents by status (case-insensitive, no hardcoding) 192 | status_counts = Counter(getattr(doc, "status", "").strip().lower() or "unknown" for doc in xls_docs) 193 | 194 | for status, count in status_counts.items(): 195 | print(f"- {status.capitalize()}: {count}") 196 | 197 | 198 | if __name__ == "__main__": 199 | build_site() 200 | -------------------------------------------------------------------------------- /XLS-0064-pseudo-account/README.md: -------------------------------------------------------------------------------- 1 |
2 | xls: 64 3 | title: Pseudo-Account 4 | description: A standard for a "pseudo-account" AccountRoot object to be associated with one or more ledger entries. 5 | author: Vito Tumas (@Tapanito) 6 | status: Draft 7 | category: Amendment 8 | created: 2025-03-04 9 | updated: 2025-08-29 10 |11 | 12 | ### Abstract 13 | 14 | This document proposes a standard for a _pseudo-account_, an `AccountRoot` ledger entry that can be associated with one or more other ledger entries. A pseudo-account is designed to hold and/or issue assets on behalf of its associated entries, enabling protocol-level functionality that requires an on-ledger entity to manage funds. 15 | 16 | ### Motivation 17 | 18 | The XRP Ledger is an account-based system where assets (XRP, IOUs, etc.) can only be held by an `AccountRoot` entry. However, several advanced protocols, such as Automated Market Makers (AMMs), lending pools, and vaults, require a ledger _object_ itself to hold and manage assets. 19 | 20 | The [XLS-30 (AMM)](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0030-automated-market-maker#readme) specification pioneered this concept by introducing a pseudo-account linked to each `AMM` instance. This allows the AMM to track its token balances and issue Liquidity Provider Tokens (`LPTokens`). 21 | 22 | This specification formalizes and standardizes the requirements for an `AccountRoot` when it functions as a pseudo-account, ensuring a consistent and secure implementation across different protocols. It defines mandatory flags, a naming convention for linking fields, and the core invariants that any protocol using a pseudo-account must enforce. 23 | 24 | ### Specification 25 | 26 | #### Ledger Entries 27 | 28 | This specification defines a set of mandatory properties and fields for an `AccountRoot` ledger entry when it is used as a pseudo-account. 29 | 30 | ##### **`AccountRoot`** 31 | 32 | ###### **Object Identifier** 33 | 34 | The address of the pseudo-account's `AccountRoot` must be derived deterministically and be difficult to predict before creation. This prevents malicious actors from front-running the creation transaction by pre-funding the address. The protocol creating the `AccountRoot` must ensure the derived address is unoccupied. 35 | 36 | A nonce-based approach is used to generate the unique `AccountRoot` ID: 37 | 38 | 1. Initialize a nonce, $i$, to $0$. 39 | 2. Compute a candidate ID: `AccountID` = `SHA512-Half`($i$ || `ParentLedgerHash` || `