Simplify InnerObject JSON format
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
Memoscommon field of transactions - Members of the
Signerscommon field of transactions. - Members of the
SignerEntriesfield of SignerListSet transactions, andSignerEntriesfield of SignerList ledger entries. - Members of the
AffectedNodesfield in transaction metadata. - Members of the
NFTokensfield of NFTokenPage ledger entries. - Members of the
PriceDataSeriesfield of Oracle ledger entries. - Members of the
AuthAccountsfield of AMMBid transactions andAuctionSlot.AuthAccountsfield of AMM ledger entries. - Members of the
VoteSlotsfield of AMM ledger entries. - Members of the
AuthorizeCredentialsandUnauthorizeCredentialsfields of DepositPreauth transactions and same-named fields of DepositPreauth ledger entries. - Members of the
Permissionsfield of DelegateSet transactions andPermissionsfield 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
RawTransactionsandBatchSignersfields 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.
This commit uses name (IMO it needs to be a lowercase field since it's not an actual SField): https://github.com/mvadari/rippled/commit/15bae8776366e51c615d05cefa9db91f0ea21b0c
Whatever name is chosen for the field, it needs to be ~camel~ snake case, since it's synthetic.
-
name -
object_type
Not CamelCase, snake_case 🙂
Not
CamelCase,snake_case🙂
🤦