stellar / js-stellar-base

The lowest-level stellar helper library. It consists of classes to read, write, hash, and sign Stellar xdr
https://stellar.github.io/js-stellar-base/
Apache License 2.0
108 stars 137 forks source link

Add JSONification of Soroban invocation trees. #669

Closed Shaptic closed 1 year ago

github-actions[bot] commented 1 year ago

Size Change: +41.6 kB (+1%)

Total Size: 3.11 MB

Filename Size Change
dist/stellar-base.js 2.28 MB +31.4 kB (+1%)
dist/stellar-base.min.js 828 kB +10.1 kB (+1%)

compressed-size-action

Shaptic commented 1 year ago

Thank you for the review, @paulbellamy :pray:

try to do stuff in the dapp example [...] motivating example [...]

I forgot to include the context that motivated this: Slack thread. The idea is for Freighter et al. to have meaningful popups when asking users to authorize invocation trees.

The JSON produces is marginally more human-friendly

Hard disagree, but this isn't obvious without an example. Flattening the structure and leveraging scValToNative makes a huge difference in readability. Which one is easier to grok (both are JSON.stringifyd)? And which one would be easier for a dapp user (not dev)?


Scenario

This is a complicated, albeit contrived invocation tree whose pseudo-structure is supposed to represent a high-level contract interaction involving two transfers of native SAC tokens underneath it.

 purchase("SomeNft:G...", 7 xlm)
     |
     +--- swap(xlm, usdc, from, to)
     |      |
     |      |- xlm.transfer(from, to, 7)
     |      |
     |      |- usdc.transfer(to, from, 1)
     |
     +--- someNft.transfer(invoker, contract, 1)

XDR

```xdr { "_attributes": { "function": { "_switch": { "name": "sorobanAuthorizedFunctionTypeContractFn", "value": 0 }, "_arm": "contractFn", "_value": { "_attributes": { "contractAddress": { "_switch": { "name": "scAddressTypeContract", "value": 1 }, "_arm": "contractId", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 232, 8, 251, 255, 185, 70, 175, 127, 248, 65, 1, 210, 82, 54, 174, 141, 74, 125, 242, 129, 116, 44, 34, 107, 197, 3, 127, 111, 110, 162, 113, 83 ] } }, "functionName": "purchase", "args": [ { "_switch": { "name": "scvString", "value": 14 }, "_arm": "str", "_armType": { "_maxLength": 4294967295 }, "_value": "SomeNft:GBS7D7AJFWKIMWLD7KFAV2AXHSKCOE4DSSVKZQBGJCC3FRGS7WAS5WA6" }, { "_switch": { "name": "scvU64", "value": 5 }, "_arm": "u64", "_value": { "_value": "7" } } ] } } }, "subInvocations": [ { "_attributes": { "function": { "_switch": { "name": "sorobanAuthorizedFunctionTypeContractFn", "value": 0 }, "_arm": "contractFn", "_value": { "_attributes": { "contractAddress": { "_switch": { "name": "scAddressTypeContract", "value": 1 }, "_arm": "contractId", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 83, 50, 135, 62, 59, 72, 227, 3, 235, 42, 185, 176, 229, 104, 99, 81, 154, 204, 198, 232, 58, 194, 20, 154, 167, 60, 88, 140, 145, 89, 180, 48 ] } }, "functionName": "swap", "args": [ { "_switch": { "name": "scvString", "value": 14 }, "_arm": "str", "_armType": { "_maxLength": 4294967295 }, "_value": "native" }, { "_switch": { "name": "scvString", "value": 14 }, "_arm": "str", "_armType": { "_maxLength": 4294967295 }, "_value": "USDC:GDEWIEQEB657FDO3GZIUTYS57T4M65QP352A2PN4CJPBCR55I5FWVAKG" }, { "_switch": { "name": "scvAddress", "value": 18 }, "_arm": "address", "_value": { "_switch": { "name": "scAddressTypeAccount", "value": 0 }, "_arm": "accountId", "_value": { "_switch": { "name": "publicKeyTypeEd25519", "value": 0 }, "_arm": "ed25519", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 76, 219, 245, 104, 41, 198, 51, 53, 239, 66, 121, 235, 61, 236, 110, 210, 177, 117, 102, 181, 16, 35, 129, 214, 69, 128, 154, 252, 64, 79, 52, 127 ] } } } }, { "_switch": { "name": "scvAddress", "value": 18 }, "_arm": "address", "_value": { "_switch": { "name": "scAddressTypeAccount", "value": 0 }, "_arm": "accountId", "_value": { "_switch": { "name": "publicKeyTypeEd25519", "value": 0 }, "_arm": "ed25519", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 52, 74, 39, 21, 56, 39, 157, 177, 87, 193, 83, 66, 151, 143, 125, 19, 6, 239, 212, 82, 148, 51, 22, 140, 81, 169, 48, 184, 248, 113, 124, 83 ] } } } } ] } } }, "subInvocations": [ { "_attributes": { "function": { "_switch": { "name": "sorobanAuthorizedFunctionTypeContractFn", "value": 0 }, "_arm": "contractFn", "_value": { "_attributes": { "contractAddress": { "_switch": { "name": "scAddressTypeContract", "value": 1 }, "_arm": "contractId", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 36, 67, 12, 76, 242, 197, 130, 66, 144, 142, 29, 145, 208, 242, 207, 18, 181, 117, 253, 41, 48, 141, 3, 69, 36, 114, 219, 194, 117, 150, 132, 109 ] } }, "functionName": "transfer", "args": [ { "_switch": { "name": "scvAddress", "value": 18 }, "_arm": "address", "_value": { "_switch": { "name": "scAddressTypeAccount", "value": 0 }, "_arm": "accountId", "_value": { "_switch": { "name": "publicKeyTypeEd25519", "value": 0 }, "_arm": "ed25519", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 76, 219, 245, 104, 41, 198, 51, 53, 239, 66, 121, 235, 61, 236, 110, 210, 177, 117, 102, 181, 16, 35, 129, 214, 69, 128, 154, 252, 64, 79, 52, 127 ] } } } }, { "_switch": { "name": "scvString", "value": 14 }, "_arm": "str", "_armType": { "_maxLength": 4294967295 }, "_value": "7" } ] } } }, "subInvocations": [] } }, { "_attributes": { "function": { "_switch": { "name": "sorobanAuthorizedFunctionTypeContractFn", "value": 0 }, "_arm": "contractFn", "_value": { "_attributes": { "contractAddress": { "_switch": { "name": "scAddressTypeContract", "value": 1 }, "_arm": "contractId", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 56, 133, 65, 88, 225, 40, 234, 250, 236, 145, 45, 73, 128, 111, 87, 142, 198, 124, 243, 151, 85, 5, 37, 42, 194, 53, 2, 51, 250, 227, 108, 176 ] } }, "functionName": "transfer", "args": [ { "_switch": { "name": "scvAddress", "value": 18 }, "_arm": "address", "_value": { "_switch": { "name": "scAddressTypeAccount", "value": 0 }, "_arm": "accountId", "_value": { "_switch": { "name": "publicKeyTypeEd25519", "value": 0 }, "_arm": "ed25519", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 76, 219, 245, 104, 41, 198, 51, 53, 239, 66, 121, 235, 61, 236, 110, 210, 177, 117, 102, 181, 16, 35, 129, 214, 69, 128, 154, 252, 64, 79, 52, 127 ] } } } }, { "_switch": { "name": "scvString", "value": 14 }, "_arm": "str", "_armType": { "_maxLength": 4294967295 }, "_value": "7" } ] } } }, "subInvocations": [] } } ] } }, { "_attributes": { "function": { "_switch": { "name": "sorobanAuthorizedFunctionTypeContractFn", "value": 0 }, "_arm": "contractFn", "_value": { "_attributes": { "contractAddress": { "_switch": { "name": "scAddressTypeContract", "value": 1 }, "_arm": "contractId", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 232, 8, 251, 255, 185, 70, 175, 127, 248, 65, 1, 210, 82, 54, 174, 141, 74, 125, 242, 129, 116, 44, 34, 107, 197, 3, 127, 111, 110, 162, 113, 83 ] } }, "functionName": "GBGNX5LIFHDDGNPPIJ46WPPMN3JLC5LGWUICHAOWIWAJV7CAJ42H6FBG", "args": [ { "_switch": { "name": "scvAddress", "value": 18 }, "_arm": "address", "_value": { "_switch": { "name": "scAddressTypeAccount", "value": 0 }, "_arm": "accountId", "_value": { "_switch": { "name": "publicKeyTypeEd25519", "value": 0 }, "_arm": "ed25519", "_armType": { "_length": 32 }, "_value": { "type": "Buffer", "data": [ 76, 219, 245, 104, 41, 198, 51, 53, 239, 66, 121, 235, 61, 236, 110, 210, 177, 117, 102, 181, 16, 35, 129, 214, 69, 128, 154, 252, 64, 79, 52, 127 ] } } } }, { "_switch": { "name": "scvU64", "value": 5 }, "_arm": "u64", "_value": { "_value": "1" } } ] } } }, "subInvocations": [] } } ] } } ```

JSON

```json { "type": "execute", "args": { "source": "CDUAR677XFDK677YIEA5EURWV2GUU7PSQF2CYITLYUBX633OUJYVGTSS", "function": "purchase", "args": [ "SomeNft:GBS7D7AJFWKIMWLD7KFAV2AXHSKCOE4DSSVKZQBGJCC3FRGS7WAS5WA6", "7" ] }, "subInvocations": [ { "type": "execute", "args": { "source": "CBJTFBZ6HNEOGA7LFK43BZLIMNIZVTGG5A5MEFE2U46FRDERLG2DAJTB", "function": "swap", "args": [ "native", "USDC:GDEWIEQEB657FDO3GZIUTYS57T4M65QP352A2PN4CJPBCR55I5FWVAKG", "GBGNX5LIFHDDGNPPIJ46WPPMN3JLC5LGWUICHAOWIWAJV7CAJ42H6FBG", "GA2EUJYVHATZ3MKXYFJUFF4PPUJQN36UKKKDGFUMKGUTBOHYOF6FHSUZ" ] }, "subInvocations": [ { "type": "execute", "args": { "source": "CASEGDCM6LCYEQUQRYOZDUHSZ4JLK5P5FEYI2A2FERZNXQTVS2CG3BAH", "function": "transfer", "args": [ "GBGNX5LIFHDDGNPPIJ46WPPMN3JLC5LGWUICHAOWIWAJV7CAJ42H6FBG", "7" ] }, "subInvocations": [] }, { "type": "execute", "args": { "source": "CA4IKQKY4EUOV6XMSEWUTADPK6HMM7HTS5KQKJJKYI2QEM724NWLAKEI", "function": "transfer", "args": [ "GBGNX5LIFHDDGNPPIJ46WPPMN3JLC5LGWUICHAOWIWAJV7CAJ42H6FBG", "7" ] }, "subInvocations": [] } ] }, { "type": "execute", "args": { "source": "CDUAR677XFDK677YIEA5EURWV2GUU7PSQF2CYITLYUBX633OUJYVGTSS", "function": "GBGNX5LIFHDDGNPPIJ46WPPMN3JLC5LGWUICHAOWIWAJV7CAJ42H6FBG", "args": [ "GBGNX5LIFHDDGNPPIJ46WPPMN3JLC5LGWUICHAOWIWAJV7CAJ42H6FBG", "1" ] }, "subInvocations": [] } ] } ```

Analysis

While I will admit that the contract IDs don't help you (e.g. what is CDUAR...?), this could easily be built upon by e.g. block explorers or Freighter itself caching explanations for common contracts.

paulbellamy commented 1 year ago

Putting on my, "non technical user" hat. Both the xdr and json examples you gave look like unintelligible computer-nonsense. Fine, the json outputs contract IDs instead of byte arrays, etc. 🤷

Interestingly, when you actually wanted to explain what the tree structure you were building does you invented a different (more graphical) format to do that. 😉

 purchase("SomeNft:G...", 7 xlm)
     |
     +--- swap(xlm, usdc, from, to)
     |      |
     |      |- xlm.transfer(from, to, 7)
     |      |
     |      |- usdc.transfer(to, from, 1)
     |
     +--- someNft.transfer(invoker, contract, 1)

^ This is what I would expect wallets to do. So if xdr -> json -> html is easier for wallets/dapps than xdr -> html, then fine, I guess.

lmorgan824 commented 1 year ago

We'd vote for xdr -> json -> html, a little more wiggle room and MUCH easier for non-XDR or new devs to understand.

Lou

On Wed, Aug 23, 2023 at 3:45 AM Paul Bellamy @.***> wrote:

Putting on my, "non technical user" hat. Both the xdr and json examples you gave look like unintelligible computer-nonsense. Fine, the json outputs contract IDs instead of byte arrays, etc. 🤷

Interestingly, when you actually wanted to explain what the tree structure you were building does you invented a different (more graphical) format to do that. 😉

purchase("SomeNft:G...", 7 xlm) +--- swap(xlm, usdc, from, to)
- xlm.transfer(from, to, 7)
- usdc.transfer(to, from, 1)
 +--- someNft.transfer(invoker, contract, 1)

^ This is what I would expect wallets to do. So if xdr -> json -> html is easier for wallets/dapps than xdr -> html, then fine, I guess.

— Reply to this email directly, view it on GitHub https://github.com/stellar/js-stellar-base/pull/669#issuecomment-1689542779, or unsubscribe https://github.com/notifications/unsubscribe-auth/APB6MJAG5QMUWTC3UNEMNWTXWW7LPANCNFSM6AAAAAA3KUHODY . You are receiving this because you are subscribed to this thread.Message ID: @.***>

-- Regards, Lou Morgan

Chief Technology Officer @.*** www.blocktimefinancial.com 262-716-1201 (M) 262-368-1150 (O)

paulbellamy commented 1 year ago

To clarify, by "xdr" in the above comment, I mean the js classes from stellar-base, not like base64 strings. But sounds like an intermediate representation might still be helpful. 👍