Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix scvals to print in json string #305

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

chowbao
Copy link
Contributor

@chowbao chowbao commented Jan 23, 2025

PR Checklist

PR Structure

  • This PR has reasonably narrow scope (if not, break it down into smaller PRs).
  • This PR avoids mixing refactoring changes with feature changes (split into two PRs
    otherwise).
  • This PR's title starts with the jira ticket associated with the PR.

Thoroughness

  • This PR adds tests for the most critical parts of the new functionality or fixes.
  • I've updated the README with the added features, breaking changes, new instructions on how to use the repository. I updated the description of the fuction with the changes that were made.

Release planning

  • I've decided if this PR requires a new major/minor/patch version accordingly to
    semver, and I've changed the name of the BRANCH to major/_ , minor/_ or patch/* .

What

github issue

Fix printing of ScVals for contract_data and history_contract_events to print in a JSON parsable format

Why

The ScVal.String() function uses the %v to print out the ScVal struct which causes some oddities in how nested maps, arrays, and contract instances were converted to where they did not conform to a normal map/json structure. By fixing ScVals to print in a JSON parsable format we can more easily dive into decoded keys/vals using parse_json() in BigQuery

Known limitations

N/A

@chowbao chowbao requested a review from a team as a code owner January 23, 2025 15:57
Comment on lines +171 to +180
// printScValJSON is used to print json parsable ScVal output instead of
// the ScVal.String() output which prints out %v values that aren't parsable
// Example: {"type":"Map","value":"[{collateral [{1 2386457777}]} {liabilities []} {supply []}]"}
//
// This function traverses through the ScVal structure by calling removeNulls which recursively removes
// the null pointers from the given ScVal.
//
// The output is then: {"type":"Map","value":"[{\"collateral\": \"[{\"1\": \"2386457777\"}]\"} {\"liabilities\": \"[]\"} {\"supply\": \"[]\"}]\"}
// where "value" is a valid JSON string
func printScValJSON(scVal xdr.ScVal) (string, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts or opinions on the way this is done?

In theory json.Marshal would have been enough but it would output a JSON string with a null value for each null pointer in ScVal (which is a lot of bloat). The options I thought of were to regex away the nulls (bad) or traverse and remove the nulls recursively (thanks chatGPT).

The only quirk I see is that the Types embedded in the JSON string are no longer transformed into their string representation. For example it won't say type: Byte but will be type: 0 in reference to the xdr.ScVal.Type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that this also belongs in the xdr/xdrill instead of here in stellar-etl but I think that can be redone when the changes/contract events xdrill is written and stellar-etl is refactored

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

➕ agree, this ultimately should be abstracted outside of stellar-etl. I think it's fine in here for now.

Regex will be fairly hard to maintain, and I don't think we should materialize a null value for each null pointer. I think that will make it equally hard to traverse the JSON.

What are the implications for Types no longer represented as the xdr.ScVal.Type? Will this make it harder for developers to infer anything about the value? I suspect no since we are parsing the data as string but want to confirm that this won't impact flexibility for end users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it will impact downstream users. I think it is pretty obvious from the value string JSON what the type will be. In any case the user can always take a look at the enum for the ScVal.Type

const (
	ScValTypeScvBool                      ScValType = 0
	ScValTypeScvVoid                      ScValType = 1
	ScValTypeScvError                     ScValType = 2
	ScValTypeScvU32                       ScValType = 3
...

Like all the standard types (bool, int, string, map, vec) will be instantly obvious
The weirder ones are

	ScValTypeScvContractInstance          ScValType = 19
	ScValTypeScvLedgerKeyContractInstance ScValType = 20
	ScValTypeScvLedgerKeyNonce            ScValType = 21

But I would argue these are already special cases and only power users (us) will be doing stuff with them

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhhhhhh if it's the enum that's fine! if needed, we can always create a lookup in dbt to translate back to the string val representation.

@@ -57,7 +57,7 @@ func makeContractEventTestOutput() (output [][]ContractEventOutput, err error) {
topicsDecoded["topics_decoded"] = []map[string]string{
{
"type": "B",
"value": "true",
"value": "{\"B\":true,\"Type\":0}",
Copy link
Contributor Author

@chowbao chowbao Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example of the change output. The value is the full ScVal conversion instead of just the relevant ScVal type. I did it this way because I think it's nicer to have full ScVal context when parsing in BigQuery with parse_json instead of looking at both "type" and "value" fields

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this negate the need for type then? Would the key of type always be included in value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically yes. It is duplicative

I think the "type": "B" (or any other type) is useful for filtering specific types of values. Like I only want maps and vecs. Filter out ScValContractInstances or vice versa

I think then having the "Type" embedded in the value will then also be helpful if you filtered for both vecs and maps and after reading the string JSON want to apply different logic for vecs vs maps (because they would be parsed differently).

I can see it both ways though. Maybe it is easier with the explicit type but I personally feel this is easier

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amishas157 thoughts on this? i'd like a second opinion. I think you're right, we maintain type as a standalone key for filtering purposes. i'm fine with the schema as defined.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am imagining to get the actual value, one may need to perform

topicsDecoded["topics_decoded"]["value"][topicsDecoded["topics_decoded"]["type"]]

Basically use Type outside value as key. In here Type inside value is not much helpful. But think it does not hurt to keep it either

Copy link
Contributor

@sydneynotthecity sydneynotthecity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass looks good, just a couple comments

@@ -57,7 +57,7 @@ func makeContractEventTestOutput() (output [][]ContractEventOutput, err error) {
topicsDecoded["topics_decoded"] = []map[string]string{
{
"type": "B",
"value": "true",
"value": "{\"B\":true,\"Type\":0}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this negate the need for type then? Would the key of type always be included in value?

Comment on lines +171 to +180
// printScValJSON is used to print json parsable ScVal output instead of
// the ScVal.String() output which prints out %v values that aren't parsable
// Example: {"type":"Map","value":"[{collateral [{1 2386457777}]} {liabilities []} {supply []}]"}
//
// This function traverses through the ScVal structure by calling removeNulls which recursively removes
// the null pointers from the given ScVal.
//
// The output is then: {"type":"Map","value":"[{\"collateral\": \"[{\"1\": \"2386457777\"}]\"} {\"liabilities\": \"[]\"} {\"supply\": \"[]\"}]\"}
// where "value" is a valid JSON string
func printScValJSON(scVal xdr.ScVal) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

➕ agree, this ultimately should be abstracted outside of stellar-etl. I think it's fine in here for now.

Regex will be fairly hard to maintain, and I don't think we should materialize a null value for each null pointer. I think that will make it equally hard to traverse the JSON.

What are the implications for Types no longer represented as the xdr.ScVal.Type? Will this make it harder for developers to infer anything about the value? I suspect no since we are parsing the data as string but want to confirm that this won't impact flexibility for end users.

@leighmcculloch
Copy link
Member

leighmcculloch commented Jan 23, 2025

print in a JSON parsable format

There is a canonical XDR-JSON format that more tooling is using, the Lab uses it, the CLI uses it, the Soroban Rust SDK uses it, and this could use it too. It's implemented in the Rust XDR lib, and that lib is also published as a Wasm powered NPM package. You could take the Wasm and experiment with powering the JSON generation with it too so that you're using the same JSON format.

If the JSON format is largely internal, then this doesn't matter, but has much more value if users see it.

Rust XDR Lib: https://github.com/stellar/rs-stellar-xdr
Wasm-powdered JS Lib: https://github.com/stellar/js-stellar-xdr-json

If this is of interest we can build a Wasm-powered Go lib that does same.

@chowbao
Copy link
Contributor Author

chowbao commented Jan 23, 2025

print in a JSON parsable format

There is a canonical XDR-JSON format that more tooling is using, the Lab uses it, the CLI uses it, the Soroban Rust SDK uses it, and this could use it too. It's implemented in the Rust XDR lib, and that lib is also published as a Wasm powered NPM package. You could take the Wasm and experiment with powering the JSON generation with it too so that you're using the same JSON format.

If the JSON format is largely internal, then this doesn't matter, but has much more value if users see it.

Rust XDR Lib: https://github.com/stellar/rs-stellar-xdr Wasm-powdered JS Lib: https://github.com/stellar/js-stellar-xdr-json

If this is of interest we can build a Wasm-powered Go lib that does same.

Ooo it would be nice if there was the Go lib equivalent. Not sure how much interest there would be for it though.

As it pertains to this PR and change it is purely for stellar-etl (basically internal). I don't believe it is worth porting (or calling rust or js) for this specific case.

You could take the Wasm and experiment with powering the JSON generation with it too so that you're using the same JSON format.

Is there an existing example of this JSON format? I assume it is largely the same because it is just ScVals but doesn't hurt to check.

@leighmcculloch
Copy link
Member

Is there an existing example of this JSON format? I assume it is largely the same because it is just ScVals but doesn't hurt to check.

Install the stellar-cli (e.g. brew install stellar-cli) and then try some of these commands.

The CLI manual is at: https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli#stellar-xdr-decode

The following commands example encodes and decodes, they work in reverse with the encode subcommand accepting JSON and the decode command accepting XDR. There are other types of XDR supported too, such as streams (the format that contract specs and meta are in), or framed streams (the format bucket lists are in).

ScVal u128:

$ echo 'AAAACQAAAAAAAAAAAAAAABfzm7A=' | stellar xdr decode --type ScVal
{"u128":{"hi":0,"lo":401841072}}

ScVal symbol:

$ echo '{"symbol":"Balance"}' | stellar xdr encode --type ScVal
AAAADwAAAAdCYWxhbmNlAA==

ScVal vec:

$ stellar xdr encode --type ScVal << -
{
  "vec": [
    {"symbol":"Balance"},
    {"address":"CCZC4HGM2BW7TIIS4KBDQEZX67DFZLOL6TZXGUGD6SJZS6IJ5JUAEZZR"}
  ]
}
-
AAAAEAAAAAEAAAACAAAADwAAAAdCYWxhbmNlAAAAABIAAAABsi4czNBt+aES4oI4Ezf3xlyty/Tzc1DD9JOZeQnqaAI=

TransactionEnvelope:

$ stellar xdr decode --type TransactionEnvelope << -
AAAAAgAAAAA7ngKfAUS0/FGZa4KJx3HXU6lwFOcBJPM4wex7LzuKWQAXRG8DNjELAABDtQAAAAAAAAAAAAAAAQAAAAEAAAAAO54CnwFEtPxRmWuCicdx11OpcBTnASTzOMHsey87ilkAAAAYAAAAAAAAAAFgM7QlDnBOMU+wZJc9GF25IsrgvScrpb/xmqxXDxKsLwAAAAxzd2FwX2NoYWluZWQAAAAFAAAAEgAAAAAAAAAAO54CnwFEtPxRmWuCicdx11OpcBTnASTzOMHsey87ilkAAAAQAAAAAQAAAAMAAAAQAAAAAQAAAAMAAAAQAAAAAQAAAAIAAAASAAAAAU/Gc7OAVjfdyvL/XCmETYqJhcv3At3YYEIHPi20nac3AAAAEgAAAAHwRzqr9avExxzorwU4KSlA9zOqbAQhtZve1+izS+RdtQAAAA0AAAAgPc3KJWpQNSWHv7K1ripxhdZhAV72hOLED3iq/u3YJoAAAAASAAAAAU/Gc7OAVjfdyvL/XCmETYqJhcv3At3YYEIHPi20nac3AAAAEAAAAAEAAAADAAAAEAAAAAEAAAACAAAAEgAAAAFPxnOzgFY33cry/1wphE2KiYXL9wLd2GBCBz4ttJ2nNwAAABIAAAABre/OWa7lKWj3YGHUlMJSW3Vln6QpamX0me8p5WR35JYAAAANAAAAIJrHqc3iOsKtoREF7qpC5DwuqDMsoKqPQfWNcWAnTXGOAAAAEgAAAAGt785ZruUpaPdgYdSUwlJbdWWfpClqZfSZ7ynlZHfklgAAABAAAAABAAAAAwAAABAAAAABAAAAAgAAABIAAAABJbT82FmuwvpjSEOMSJs8PBDJi20hvk/TyzDLaJU++XcAAAASAAAAAa3vzlmu5Slo92Bh1JTCUlt1ZZ+kKWpl9JnvKeVkd+SWAAAADQAAACCy4C/PymyW+K1cvYTneEp3ezbZyWokWUAsT0WEYqq38AAAABIAAAABJbT82FmuwvpjSEOMSJs8PBDJi20hvk/TyzDLaJU++XcAAAASAAAAAfBHOqv1q8THHOivBTgpKUD3M6psBCG1m97X6LNL5F21AAAACQAAAAAAAAAAAAAAAAAAzMgAAAAJAAAAAAAAAAAAAAAAF/ObsAAAAAEAAAAAAAAAAAAAAAFgM7QlDnBOMU+wZJc9GF25IsrgvScrpb/xmqxXDxKsLwAAAAxzd2FwX2NoYWluZWQAAAAFAAAAEgAAAAAAAAAAO54CnwFEtPxRmWuCicdx11OpcBTnASTzOMHsey87ilkAAAAQAAAAAQAAAAMAAAAQAAAAAQAAAAMAAAAQAAAAAQAAAAIAAAASAAAAAU/Gc7OAVjfdyvL/XCmETYqJhcv3At3YYEIHPi20nac3AAAAEgAAAAHwRzqr9avExxzorwU4KSlA9zOqbAQhtZve1+izS+RdtQAAAA0AAAAgPc3KJWpQNSWHv7K1ripxhdZhAV72hOLED3iq/u3YJoAAAAASAAAAAU/Gc7OAVjfdyvL/XCmETYqJhcv3At3YYEIHPi20nac3AAAAEAAAAAEAAAADAAAAEAAAAAEAAAACAAAAEgAAAAFPxnOzgFY33cry/1wphE2KiYXL9wLd2GBCBz4ttJ2nNwAAABIAAAABre/OWa7lKWj3YGHUlMJSW3Vln6QpamX0me8p5WR35JYAAAANAAAAIJrHqc3iOsKtoREF7qpC5DwuqDMsoKqPQfWNcWAnTXGOAAAAEgAAAAGt785ZruUpaPdgYdSUwlJbdWWfpClqZfSZ7ynlZHfklgAAABAAAAABAAAAAwAAABAAAAABAAAAAgAAABIAAAABJbT82FmuwvpjSEOMSJs8PBDJi20hvk/TyzDLaJU++XcAAAASAAAAAa3vzlmu5Slo92Bh1JTCUlt1ZZ+kKWpl9JnvKeVkd+SWAAAADQAAACCy4C/PymyW+K1cvYTneEp3ezbZyWokWUAsT0WEYqq38AAAABIAAAABJbT82FmuwvpjSEOMSJs8PBDJi20hvk/TyzDLaJU++XcAAAASAAAAAfBHOqv1q8THHOivBTgpKUD3M6psBCG1m97X6LNL5F21AAAACQAAAAAAAAAAAAAAAAAAzMgAAAAJAAAAAAAAAAAAAAAAF/ObsAAAAAEAAAAAAAAAAfBHOqv1q8THHOivBTgpKUD3M6psBCG1m97X6LNL5F21AAAACHRyYW5zZmVyAAAAAwAAABIAAAAAAAAAADueAp8BRLT8UZlrgonHcddTqXAU5wEk8zjB7HsvO4pZAAAAEgAAAAFgM7QlDnBOMU+wZJc9GF25IsrgvScrpb/xmqxXDxKsLwAAAAoAAAAAAAAAAAAAAAAAAMzIAAAAAAAAAAEAAAAAAAAADQAAAAYAAAABJbT82FmuwvpjSEOMSJs8PBDJi20hvk/TyzDLaJU++XcAAAAUAAAAAQAAAAYAAAABT8Zzs4BWN93K8v9cKYRNiomFy/cC3dhgQgc+LbSdpzcAAAAUAAAAAQAAAAYAAAABYDO0JQ5wTjFPsGSXPRhduSLK4L0nK6W/8ZqsVw8SrC8AAAAQAAAAAQAAAAIAAAAPAAAADlRva2Vuc1NldFBvb2xzAAAAAAANAAAAIGOZMBN74PxDLl5yIVwkoq/ydJqOyQvJoIrNnY+xQJJsAAAAAQAAAAYAAAABYDO0JQ5wTjFPsGSXPRhduSLK4L0nK6W/8ZqsVw8SrC8AAAAQAAAAAQAAAAIAAAAPAAAADlRva2Vuc1NldFBvb2xzAAAAAAANAAAAIGOtGoRPjRZUHSjRc2HFd/594YgXEmeubfyZCkROpzCfAAAAAQAAAAYAAAABYDO0JQ5wTjFPsGSXPRhduSLK4L0nK6W/8ZqsVw8SrC8AAAAQAAAAAQAAAAIAAAAPAAAADlRva2Vuc1NldFBvb2xzAAAAAAANAAAAIPrUgNbr1z0JGLIUVPrToeuPgMUu6V41cjIPzuRI63YIAAAAAQAAAAYAAAABYDO0JQ5wTjFPsGSXPRhduSLK4L0nK6W/8ZqsVw8SrC8AAAAUAAAAAQAAAAYAAAABgBdpEMDtExocHiH9irvJRhjmZINGNLCz+nLu8EuXI4QAAAAUAAAAAQAAAAYAAAABre/OWa7lKWj3YGHUlMJSW3Vln6QpamX0me8p5WR35JYAAAAUAAAAAQAAAAYAAAAB8Ec6q/WrxMcc6K8FOCkpQPczqmwEIbWb3tfos0vkXbUAAAAUAAAAAQAAAAcmxJUBmvt0SPaQqC1uZtj6sa0/0+e0rsfVVCCZZsnRnQAAAActdwlG1UKbCYj9M5BM8ZtUmII/Zoy2mgpQKOmIAj0gQAAAAAdVgns0uhb/MFR5ZkXgvTGlDocZTMEj3N2ODyLnosydmAAAAAdk7wvGli8GVGwOAKEYd8hp14wIBD951Q7iwJyxuDfEdwAAABIAAAAAAAAAADueAp8BRLT8UZlrgonHcddTqXAU5wEk8zjB7HsvO4pZAAAAAQAAAAA7ngKfAUS0/FGZa4KJx3HXU6lwFOcBJPM4wex7LzuKWQAAAAF5RVRIAAAAAPEGkLqlpq4vx0TZPu2Z8XsLO3DlKk9IRhccwkEZhAZIAAAABgAAAAEltPzYWa7C+mNIQ4xImzw8EMmLbSG+T9PLMMtolT75dwAAABAAAAABAAAAAgAAAA8AAAAHQmFsYW5jZQAAAAASAAAAATz6JWHCsqoRS8pYoOVkU/TpnA0ZHWkUvOFnLC1QehAhAAAAAQAAAAYAAAABJbT82FmuwvpjSEOMSJs8PBDJi20hvk/TyzDLaJU++XcAAAAQAAAAAQAAAAIAAAAPAAAAB0JhbGFuY2UAAAAAEgAAAAFgM7QlDnBOMU+wZJc9GF25IsrgvScrpb/xmqxXDxKsLwAAAAEAAAAGAAAAATz6JWHCsqoRS8pYoOVkU/TpnA0ZHWkUvOFnLC1QehAhAAAAFAAAAAEAAAAGAAAAAU/Gc7OAVjfdyvL/XCmETYqJhcv3At3YYEIHPi20nac3AAAAEAAAAAEAAAACAAAADwAAAAdCYWxhbmNlAAAAABIAAAABYDO0JQ5wTjFPsGSXPRhduSLK4L0nK6W/8ZqsVw8SrC8AAAABAAAABgAAAAFPxnOzgFY33cry/1wphE2KiYXL9wLd2GBCBz4ttJ2nNwAAABAAAAABAAAAAgAAAA8AAAAHQmFsYW5jZQAAAAASAAAAAZ2Qc1KLTkZP8gvBfoTfCm85iOnvi4FCOA76J/bUqS7PAAAAAQAAAAYAAAABT8Zzs4BWN93K8v9cKYRNiomFy/cC3dhgQgc+LbSdpzcAAAAQAAAAAQAAAAIAAAAPAAAAB0JhbGFuY2UAAAAAEgAAAAGyLhzM0G35oRLigjgTN/fGXK3L9PNzUMP0k5l5CepoAgAAAAEAAAAGAAAAAYAXaRDA7RMaHB4h/Yq7yUYY5mSDRjSws/py7vBLlyOEAAAAEAAAAAEAAAACAAAADwAAAAhQb29sRGF0YQAAABIAAAABPPolYcKyqhFLylig5WRT9OmcDRkdaRS84WcsLVB6ECEAAAABAAAABgAAAAGAF2kQwO0TGhweIf2Ku8lGGOZkg0Y0sLP6cu7wS5cjhAAAABAAAAABAAAAAgAAAA8AAAAIUG9vbERhdGEAAAASAAAAAZ2Qc1KLTkZP8gvBfoTfCm85iOnvi4FCOA76J/bUqS7PAAAAAQAAAAYAAAABgBdpEMDtExocHiH9irvJRhjmZINGNLCz+nLu8EuXI4QAAAAQAAAAAQAAAAIAAAAPAAAACFBvb2xEYXRhAAAAEgAAAAGyLhzM0G35oRLigjgTN/fGXK3L9PNzUMP0k5l5CepoAgAAAAEAAAAGAAAAAZ2Qc1KLTkZP8gvBfoTfCm85iOnvi4FCOA76J/bUqS7PAAAAFAAAAAEAAAAGAAAAAa3vzlmu5Slo92Bh1JTCUlt1ZZ+kKWpl9JnvKeVkd+SWAAAAEAAAAAEAAAACAAAADwAAAAdCYWxhbmNlAAAAABIAAAABPPolYcKyqhFLylig5WRT9OmcDRkdaRS84WcsLVB6ECEAAAABAAAABgAAAAGt785ZruUpaPdgYdSUwlJbdWWfpClqZfSZ7ynlZHfklgAAABAAAAABAAAAAgAAAA8AAAAHQmFsYW5jZQAAAAASAAAAAWAztCUOcE4xT7Bklz0YXbkiyuC9Jyulv/GarFcPEqwvAAAAAQAAAAYAAAABre/OWa7lKWj3YGHUlMJSW3Vln6QpamX0me8p5WR35JYAAAAQAAAAAQAAAAIAAAAPAAAAB0JhbGFuY2UAAAAAEgAAAAGyLhzM0G35oRLigjgTN/fGXK3L9PNzUMP0k5l5CepoAgAAAAEAAAAGAAAAAbIuHMzQbfmhEuKCOBM398Zcrcv083NQw/STmXkJ6mgCAAAAFAAAAAEAAAAGAAAAAfBHOqv1q8THHOivBTgpKUD3M6psBCG1m97X6LNL5F21AAAAEAAAAAEAAAACAAAADwAAAAdCYWxhbmNlAAAAABIAAAABYDO0JQ5wTjFPsGSXPRhduSLK4L0nK6W/8ZqsVw8SrC8AAAABAAAABgAAAAHwRzqr9avExxzorwU4KSlA9zOqbAQhtZve1+izS+RdtQAAABAAAAABAAAAAgAAAA8AAAAHQmFsYW5jZQAAAAASAAAAAZ2Qc1KLTkZP8gvBfoTfCm85iOnvi4FCOA76J/bUqS7PAAAAAQINQZ0AAkeUAAAioAAAAAAAFb3PAAAAAS87ilkAAABA9nnY4iru1B6tkWw+cd5jOcGXW39Jd4tT0ORF+3ccTtbeaVcgCwK7CP1ZqASh2+SIFg5EkL+7wpInWYbvmtMUDA==
-
{"tx":{"tx":{"source_account":"GA5Z4AU7AFCLJ7CRTFVYFCOHOHLVHKLQCTTQCJHTHDA6Y6ZPHOFFSATE","fee":1524847,"seq_num":231426354170577845,"cond":"none","memo":"none","operations":[{"source_account":"GA5Z4AU7AFCLJ7CRTFVYFCOHOHLVHKLQCTTQCJHTHDA6Y6ZPHOFFSATE","body":{"invoke_host_function":{"host_function":{"invoke_contract":{"contract_address":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK","function_name":"swap_chained","args":[{"address":"GA5Z4AU7AFCLJ7CRTFVYFCOHOHLVHKLQCTTQCJHTHDA6Y6ZPHOFFSATE"},{"vec":[{"vec":[{"vec":[{"address":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF"},{"address":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG"}]},{"bytes":"3dcdca256a50352587bfb2b5ae2a7185d661015ef684e2c40f78aafeedd82680"},{"address":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF"}]},{"vec":[{"vec":[{"address":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF"},{"address":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75"}]},{"bytes":"9ac7a9cde23ac2ada11105eeaa42e43c2ea8332ca0aa8f41f58d7160274d718e"},{"address":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75"}]},{"vec":[{"vec":[{"address":"CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA"},{"address":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75"}]},{"bytes":"b2e02fcfca6c96f8ad5cbd84e7784a777b36d9c96a2459402c4f458462aab7f0"},{"address":"CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA"}]}]},{"address":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG"},{"u128":{"hi":0,"lo":52424}},{"u128":{"hi":0,"lo":401841072}}]}},"auth":[{"credentials":"source_account","root_invocation":{"function":{"contract_fn":{"contract_address":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK","function_name":"swap_chained","args":[{"address":"GA5Z4AU7AFCLJ7CRTFVYFCOHOHLVHKLQCTTQCJHTHDA6Y6ZPHOFFSATE"},{"vec":[{"vec":[{"vec":[{"address":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF"},{"address":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG"}]},{"bytes":"3dcdca256a50352587bfb2b5ae2a7185d661015ef684e2c40f78aafeedd82680"},{"address":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF"}]},{"vec":[{"vec":[{"address":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF"},{"address":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75"}]},{"bytes":"9ac7a9cde23ac2ada11105eeaa42e43c2ea8332ca0aa8f41f58d7160274d718e"},{"address":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75"}]},{"vec":[{"vec":[{"address":"CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA"},{"address":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75"}]},{"bytes":"b2e02fcfca6c96f8ad5cbd84e7784a777b36d9c96a2459402c4f458462aab7f0"},{"address":"CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA"}]}]},{"address":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG"},{"u128":{"hi":0,"lo":52424}},{"u128":{"hi":0,"lo":401841072}}]}},"sub_invocations":[{"function":{"contract_fn":{"contract_address":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG","function_name":"transfer","args":[{"address":"GA5Z4AU7AFCLJ7CRTFVYFCOHOHLVHKLQCTTQCJHTHDA6Y6ZPHOFFSATE"},{"address":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK"},{"i128":{"hi":0,"lo":52424}}]}},"sub_invocations":[]}]}}]}}}],"ext":{"v1":{"ext":"v0","resources":{"footprint":{"read_only":[{"contract_data":{"contract":"CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK","key":{"vec":[{"symbol":"TokensSetPools"},{"bytes":"639930137be0fc432e5e72215c24a2aff2749a8ec90bc9a08acd9d8fb140926c"}]},"durability":"persistent"}},{"contract_data":{"contract":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK","key":{"vec":[{"symbol":"TokensSetPools"},{"bytes":"63ad1a844f8d16541d28d17361c577fe7de188171267ae6dfc990a444ea7309f"}]},"durability":"persistent"}},{"contract_data":{"contract":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK","key":{"vec":[{"symbol":"TokensSetPools"},{"bytes":"fad480d6ebd73d0918b21454fad3a1eb8f80c52ee95e3572320fcee448eb7608"}]},"durability":"persistent"}},{"contract_data":{"contract":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CCABO2IQYDWRGGQ4DYQ73CV3ZFDBRZTEQNDDJMFT7JZO54CLS4RYJROY","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_code":{"hash":"26c495019afb7448f690a82d6e66d8fab1ad3fd3e7b4aec7d554209966c9d19d"}},{"contract_code":{"hash":"2d770946d5429b0988fd33904cf19b5498823f668cb69a0a5028e988023d2040"}},{"contract_code":{"hash":"55827b34ba16ff3054796645e0bd31a50e87194cc123dcdd8e0f22e7a2cc9d98"}},{"contract_code":{"hash":"64ef0bc6962f06546c0e00a11877c869d78c08043f79d50ee2c09cb1b837c477"}}],"read_write":[{"account":{"account_id":"GA5Z4AU7AFCLJ7CRTFVYFCOHOHLVHKLQCTTQCJHTHDA6Y6ZPHOFFSATE"}},{"trustline":{"account_id":"GA5Z4AU7AFCLJ7CRTFVYFCOHOHLVHKLQCTTQCJHTHDA6Y6ZPHOFFSATE","asset":{"credit_alphanum4":{"asset_code":"yETH","issuer":"GDYQNEF2UWTK4L6HITMT53MZ6F5QWO3Q4UVE6SCGC4OMEQIZQQDERQFD"}}}},{"contract_data":{"contract":"CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA","key":{"vec":[{"symbol":"Balance"},{"address":"CA6PUJLBYKZKUEKLZJMKBZLEKP2OTHANDEOWSFF44FTSYLKQPIICCJBE"}]},"durability":"persistent"}},{"contract_data":{"contract":"CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA","key":{"vec":[{"symbol":"Balance"},{"address":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK"}]},"durability":"persistent"}},{"contract_data":{"contract":"CA6PUJLBYKZKUEKLZJMKBZLEKP2OTHANDEOWSFF44FTSYLKQPIICCJBE","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF","key":{"vec":[{"symbol":"Balance"},{"address":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK"}]},"durability":"persistent"}},{"contract_data":{"contract":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF","key":{"vec":[{"symbol":"Balance"},{"address":"CCOZA42SRNHEMT7SBPAX5BG7BJXTTCHJ56FYCQRYB35CP5WUVEXM7B3F"}]},"durability":"persistent"}},{"contract_data":{"contract":"CBH4M45TQBLDPXOK6L7VYKMEJWFITBOL64BN3WDAIIDT4LNUTWTTOCKF","key":{"vec":[{"symbol":"Balance"},{"address":"CCZC4HGM2BW7TIIS4KBDQEZX67DFZLOL6TZXGUGD6SJZS6IJ5JUAEZZR"}]},"durability":"persistent"}},{"contract_data":{"contract":"CCABO2IQYDWRGGQ4DYQ73CV3ZFDBRZTEQNDDJMFT7JZO54CLS4RYJROY","key":{"vec":[{"symbol":"PoolData"},{"address":"CA6PUJLBYKZKUEKLZJMKBZLEKP2OTHANDEOWSFF44FTSYLKQPIICCJBE"}]},"durability":"persistent"}},{"contract_data":{"contract":"CCABO2IQYDWRGGQ4DYQ73CV3ZFDBRZTEQNDDJMFT7JZO54CLS4RYJROY","key":{"vec":[{"symbol":"PoolData"},{"address":"CCOZA42SRNHEMT7SBPAX5BG7BJXTTCHJ56FYCQRYB35CP5WUVEXM7B3F"}]},"durability":"persistent"}},{"contract_data":{"contract":"CCABO2IQYDWRGGQ4DYQ73CV3ZFDBRZTEQNDDJMFT7JZO54CLS4RYJROY","key":{"vec":[{"symbol":"PoolData"},{"address":"CCZC4HGM2BW7TIIS4KBDQEZX67DFZLOL6TZXGUGD6SJZS6IJ5JUAEZZR"}]},"durability":"persistent"}},{"contract_data":{"contract":"CCOZA42SRNHEMT7SBPAX5BG7BJXTTCHJ56FYCQRYB35CP5WUVEXM7B3F","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75","key":{"vec":[{"symbol":"Balance"},{"address":"CA6PUJLBYKZKUEKLZJMKBZLEKP2OTHANDEOWSFF44FTSYLKQPIICCJBE"}]},"durability":"persistent"}},{"contract_data":{"contract":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75","key":{"vec":[{"symbol":"Balance"},{"address":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK"}]},"durability":"persistent"}},{"contract_data":{"contract":"CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75","key":{"vec":[{"symbol":"Balance"},{"address":"CCZC4HGM2BW7TIIS4KBDQEZX67DFZLOL6TZXGUGD6SJZS6IJ5JUAEZZR"}]},"durability":"persistent"}},{"contract_data":{"contract":"CCZC4HGM2BW7TIIS4KBDQEZX67DFZLOL6TZXGUGD6SJZS6IJ5JUAEZZR","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_data":{"contract":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG","key":{"vec":[{"symbol":"Balance"},{"address":"CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK"}]},"durability":"persistent"}},{"contract_data":{"contract":"CDYEOOVL6WV4JRY45CXQKOBJFFAPOM5KNQCCDNM333L6RM2L4RO3LKYG","key":{"vec":[{"symbol":"Balance"},{"address":"CCOZA42SRNHEMT7SBPAX5BG7BJXTTCHJ56FYCQRYB35CP5WUVEXM7B3F"}]},"durability":"persistent"}}]},"instructions":34423197,"read_bytes":149396,"write_bytes":8864},"resource_fee":1424847}}},"signatures":[{"hint":"2f3b8a59","signature":"f679d8e22aeed41ead916c3e71de6339c1975b7f49778b53d0e445fb771c4ed6de6957200b02bb08fd59a804a1dbe488160e4490bfbbc292275986ef9ad3140c"}]}}

If you're not sure what a type is, use echo 'AAAz...' | stellar xdr guess.

If there are any ideas for improvement to the stellar xdr command, please open them on https://github.com/stellar/rs-stellar-xdr. ❤️

Some things worth calling out:

The JSON output will include 64-bit integers, so you need to use a custom JSON parser, which is easy, in JavaScript runtimes which only support integers up to 53-bits. This won't impact Go usage, or most non-JS runtimes, that supports 64-bit integers in JSON.

Strings are escaped ascii UTF-8 encoded, not simply UTF-8 encoded.

These two attributes aren't well documented in the dev docs, but added them now:

@leighmcculloch
Copy link
Member

The JSON can also be explored at:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants