Skip to content

Simplify InnerObject JSON format #5604

@mDuo13

Description

@mDuo13

Summary

Inner objects in the JSON format of transactions have an awkward format consisting of a JSON object with one key, which is the object type, whose value is the actual contents of the inner object.

They would be more concise and generally easier to parse if they were "unwrapped".

Motivation

It's somewhat awkward to parse through a lot of these types of objects and also a hassle to create objects in this format. Intuitively, people just care about the contents of the inner object, so it's easy to forget that you need to wrap it. And if you're iterating over a mixed list, it's usually more awkward to iterate over the keys of the wrappers to figure out what type they are than it would be to just check a type field.

{
    // ... other transaction fields ...
    "Memos": [
        {
            "Memo": {
                "MemoType": "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
                "MemoData": "72656e74"
            }
        },
        {
            "Memo": {
                "MemoType": "746578742f706c61696e",
                "MemoData": "4920616d207369636b206f66207772617070696e67206d79206d656d6f73206c696b6520746869732e"
            }
        }
    ]
}

Solution

When serializing "inner objects" to JSON, instead of using an object in an object, serialize the object with its keys at the top level alongside a field that indicates the inner object type. There are several other cases of using this paradigm in the API, including lists of transactions (TransactionType), ledger entries (LedgerEntryType), and subscription messages (type).

I suggest ObjectType as the type field, although Type would also be fine. For example:

{
    // ... other transaction fields ...
    "Memos": [
        {
            "ObjectType": "Memo",
            "MemoType": "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
            "MemoData": "72656e74"
        },
        {
            "ObjectType": "Memo",
            "MemoType": "746578742f706c61696e",
            "MemoData": "4920616d207369636b206f66207772617070696e67206d79206d656d6f73206c696b6520746869732e"
        }
    ]
}

A list of places this should apply (maybe not exhaustive):

  • Members of the Memos common field of transactions
  • Members of the Signers common field of transactions.
  • Members of the SignerEntries field of SignerListSet transactions, and SignerEntries field of SignerList ledger entries.
  • Members of the AffectedNodes field in transaction metadata.
  • Members of the NFTokens field of NFTokenPage ledger entries.
  • Members of the PriceDataSeries field of Oracle ledger entries.
  • Members of the AuthAccounts field of AMMBid transactions and AuctionSlot.AuthAccounts field of AMM ledger entries.
  • Members of the VoteSlots field of AMM ledger entries.
  • Members of the AuthorizeCredentials and UnauthorizeCredentials fields of DepositPreauth transactions and same-named fields of DepositPreauth ledger entries.
  • Members of the Permissions field of DelegateSet transactions and Permissions field of Delegate ledger entries.
  • Probably some XChain stuff (I'm not going to look too closely since I don't think this amendment is likely to become enabled)
  • Members of the RawTransactions and BatchSigners fields of Batch transactions.

See also: sfields.macro OBJECT type fields

This change should be gated by an API version switch (API v3 or whichever). However, it does not change the binary format of transactions so it does not need to be on an amendment.

Optionally, client applications and/or signing functions that take transaction JSON can accept user input that omits the Type field in a context where it's obvious what type it should be (only one type is allowed in the array and the appropriate fields for that type are supplied).

Paths Not Taken

Omitting the type field entirely seems reasonable in many of these cases, since inner objects are usually used in lists that can only contain one type. But, since there's at least one case of a list with mixed objects (AffectedNodes), it seems best to just always provide the type field for consistency.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions