diff --git a/Cargo.lock b/Cargo.lock index aea22fb231..a6747f3010 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,12 +49,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -804,9 +798,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.35" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" dependencies = [ "find-msvc-tools", "jobserver", @@ -839,17 +833,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -1170,9 +1163,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.175" +version = "1.0.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84aa1f8258b77022835f4ce5bd3b5aa418b969494bd7c3cb142c88424eb4c715" +checksum = "85b04ade63e106c145cdcd3482932299c2dcd36b15f1d5c596c06edf365966fe" dependencies = [ "cc", "cxxbridge-cmd", @@ -1184,9 +1177,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.175" +version = "1.0.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e2aa0ea9f398b72f329197cfad624fcb16b2538d3ffb0f71f51cd19fa2a512" +checksum = "ba16a03d510b0e52be1952831ae1656e3ac9f6ac0c293a7b261670a0c7ff3b54" dependencies = [ "cc", "codespan-reporting", @@ -1199,9 +1192,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.175" +version = "1.0.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "902e9553c7db1cc00baee88d6a531792d3e1aaab06ed6d1dcd606647891ea693" +checksum = "1c3aefd0d58a2df90cd67fe2eccdf7fb6e6905e9886ae5feb2258ce0dec23063" dependencies = [ "clap", "codespan-reporting", @@ -1213,15 +1206,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.175" +version = "1.0.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b2b0b4d405850b0048447786b70c2502c84e4d5c4c757416abc0500336edfc" +checksum = "0b735d976ca632f9e94b9de9b0ab8283de65e2641e7ae9d5abc3e6b467ea6673" [[package]] name = "cxxbridge-macro" -version = "1.0.175" +version = "1.0.179" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd2a8fe0dfa4a2207b80ca9492c0d5dc8752b66f5631d93b23065f40f6a943d3" +checksum = "42fffb686dc0b7bab364ee41fcf5a04c295c7e7a5f07cd38155c1c3bda437666" dependencies = [ "indexmap 2.11.0", "proc-macro2", @@ -1707,9 +1700,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" [[package]] name = "fixedbitset" @@ -1934,7 +1927,7 @@ dependencies = [ "js-sys", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi 0.14.4+wasi-0.2.4", "wasm-bindgen", ] @@ -2906,7 +2899,7 @@ dependencies = [ "log", "secret-service", "security-framework 2.11.1", - "security-framework 3.3.0", + "security-framework 3.4.0", "windows-sys 0.60.2", "zeroize", ] @@ -3040,9 +3033,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c349c75e1ab4a03bd6b33fe6cbd3c479c5dd443e44ad732664d72cb0e755475" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" dependencies = [ "cc", ] @@ -4295,7 +4288,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.3.0", + "security-framework 3.4.0", ] [[package]] @@ -4526,9 +4519,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" dependencies = [ "bitflags", "core-foundation 0.10.1", @@ -4539,9 +4532,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -5092,9 +5085,9 @@ version = "23.1.4" [[package]] name = "soroban-ledger-snapshot" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "845f16b4f451466641cb90276e661eba4061c4836179ca17fc3eb1b50e266af0" +checksum = "3823372b72cab2e7ff2ced62bbffa11fce8da0713a224f122141558cab174647" dependencies = [ "serde", "serde_json", @@ -5106,9 +5099,9 @@ dependencies = [ [[package]] name = "soroban-sdk" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c11b7f617a5b355ad8eba4c6ace360d735acc45517447ea832ebe176ca90d0" +checksum = "af0e5bf6702f5952d78c5b2bcd05b0349f9a570cc62028d90dac3710b40cbe65" dependencies = [ "arbitrary", "bytes-lit", @@ -5129,9 +5122,9 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4d963b7368dd518e9190ee4785df27c260b9bb765069e6d90898f33f83166d" +checksum = "b38abe20199c5d9fbff232381aa4e8e83302b34e82e38fbb090f41f1284fc920" dependencies = [ "darling", "heck 0.5.0", @@ -5149,9 +5142,9 @@ dependencies = [ [[package]] name = "soroban-spec" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "156a0803ed3f91f7ef159152421aec4441d33cf62e78ae4935f690a2c7798fa0" +checksum = "72526d30f8825b859afa7e0b94549dad05c58a6c928b0763620412744512d7e2" dependencies = [ "base64 0.22.1", "stellar-xdr", @@ -5175,9 +5168,9 @@ dependencies = [ [[package]] name = "soroban-spec-rust" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a49542f65185e4f0040354a1143289d3725df81397114053ce8602fb8c5bee" +checksum = "9088cb8307dad026cda494971c4f13c76f9427ab26becb7cd691da95dc5e9b1d" dependencies = [ "prettyplease", "proc-macro2", @@ -5268,18 +5261,18 @@ dependencies = [ [[package]] name = "soroban-token-sdk" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee9c54174bfefca00e6f334f873eb3ff22690b732447a5817bd208114a113a1" +checksum = "4f6124c98e1919cd71186553a70b6bd7be8b032aacbb91dd033bf4b02616c3a9" dependencies = [ "soroban-sdk", ] [[package]] name = "soroban-token-spec" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f964cb8777df385255619fdaed503908d591d8b43b388360bb1236d97f90cb" +checksum = "79ab6a6940dc083e3ad979affd4511f4dcb0783a267b98dca3c1f6c561a4e40c" dependencies = [ "soroban-sdk", "soroban-token-sdk", @@ -5328,9 +5321,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stellar-asset-spec" -version = "23.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce533790c5b54b5f78d8db3785dd25ec6e0d09df72a668ac35c8caaf3464505" +checksum = "5b7d0cd5bf3557d26abba06fc649b7c49c1f991d66327d3aea8fe055f4527f1b" dependencies = [ "soroban-sdk", "soroban-token-sdk", @@ -6380,9 +6373,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.3+wasi-0.2.4" +version = "0.14.4+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a" dependencies = [ "wit-bindgen", ] @@ -6642,11 +6635,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -6664,7 +6657,7 @@ dependencies = [ "windows-collections", "windows-core", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -6685,7 +6678,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -6697,7 +6690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -6729,6 +6722,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -6736,7 +6735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6745,7 +6744,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -6756,7 +6755,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6765,7 +6764,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6804,6 +6803,15 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -6841,7 +6849,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -6858,7 +6866,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -7020,9 +7028,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" [[package]] name = "writeable" @@ -7128,18 +7136,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 696c434a13..04c968f2ce 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -4230,9 +4230,251 @@ Fetch ledger information ###### **Subcommands:** +- `entry` — Work with ledger entries - `latest` — Get the latest ledger sequence and information from the network - `fetch` — +## `stellar ledger entry` + +Work with ledger entries + +**Usage:** `stellar ledger entry ` + +###### **Subcommands:** + +- `fetch` — Fetch ledger entries. This command supports all types of ledger entries supported by the RPC. Read more about the RPC command here: [https://developers.stellar.org/docs/data/apis/rpc/api-reference/methods/getLedgerEntries#types-of-ledgerkeys](https://developers.stellar.org/docs/data/apis/rpc/api-reference/methods/getLedgerEntries#types-of-ledgerkeys) + +## `stellar ledger entry fetch` + +Fetch ledger entries. This command supports all types of ledger entries supported by the RPC. Read more about the RPC command here: [https://developers.stellar.org/docs/data/apis/rpc/api-reference/methods/getLedgerEntries#types-of-ledgerkeys](https://developers.stellar.org/docs/data/apis/rpc/api-reference/methods/getLedgerEntries#types-of-ledgerkeys) + +**Usage:** `stellar ledger entry fetch ` + +###### **Subcommands:** + +- `account` — Fetch account entry by public key or alias +- `contract-data` — Fetch contract ledger entry by address or alias and storage key +- `claimable-balance` — Fetch a claimable balance ledger entry by id +- `liquidity-pool` — Fetch a liquidity pool ledger entry by id +- `contract-code` — Fetch a Contract's WASM bytecode by WASM hash +- `trustline` — Fetch a trustline by account and asset +- `data` — Fetch key-value data entries attached to an account (see manageDataOp) +- `offer` — Fetch an offer by account and offer id + +## `stellar ledger entry fetch account` + +Fetch account entry by public key or alias + +**Usage:** `stellar ledger entry fetch account [OPTIONS] --account ` + +###### **Options:** + +- `--account ` — Account alias or address to lookup +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +- `--hd-path ` — If identity is a seed phrase use this hd path, default is 0 + +## `stellar ledger entry fetch contract-data` + +Fetch contract ledger entry by address or alias and storage key + +**Usage:** `stellar ledger entry fetch contract-data [OPTIONS] --contract ` + +###### **Options:** + +- `--contract ` — Contract alias or address to fetch +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +- `--durability ` — Storage entry durability + + Default value: `persistent` + + Possible values: + - `persistent`: Persistent + - `temporary`: Temporary + +- `--key ` — Storage key (symbols only) +- `--key-xdr ` — Storage key (base64-encoded XDR) + +## `stellar ledger entry fetch claimable-balance` + +Fetch a claimable balance ledger entry by id + +**Usage:** `stellar ledger entry fetch claimable-balance [OPTIONS]` + +###### **Options:** + +- `--id ` — Claimable Balance Ids to fetch an entry for +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +## `stellar ledger entry fetch liquidity-pool` + +Fetch a liquidity pool ledger entry by id + +**Usage:** `stellar ledger entry fetch liquidity-pool [OPTIONS]` + +###### **Options:** + +- `--id ` — Liquidity pool ids +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +## `stellar ledger entry fetch contract-code` + +Fetch a Contract's WASM bytecode by WASM hash + +**Usage:** `stellar ledger entry fetch contract-code [OPTIONS]` + +###### **Options:** + +- `--wasm-hash ` — Get WASM bytecode by hash +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +## `stellar ledger entry fetch trustline` + +Fetch a trustline by account and asset + +**Usage:** `stellar ledger entry fetch trustline [OPTIONS] --account --asset ` + +###### **Options:** + +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +- `--account ` — Account alias or address to lookup +- `--asset ` — Assets to get trustline info for +- `--hd-path ` — If account is a seed phrase use this hd path, default is 0 + +## `stellar ledger entry fetch data` + +Fetch key-value data entries attached to an account (see manageDataOp) + +**Usage:** `stellar ledger entry fetch data [OPTIONS] --account --data-name ` + +###### **Options:** + +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +- `--account ` — Account alias or address to lookup +- `--data-name ` — Fetch key-value data entries attached to an account (see manageDataOp) +- `--hd-path ` — If identity is a seed phrase use this hd path, default is 0 + +## `stellar ledger entry fetch offer` + +Fetch an offer by account and offer id + +**Usage:** `stellar ledger entry fetch offer [OPTIONS] --account --offer ` + +###### **Options:** + +- `--rpc-url ` — RPC server endpoint +- `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider +- `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +- `-n`, `--network ` — Name of network to use from config +- `--global` — ⚠️ Deprecated: global config is always on +- `--config-dir ` — Location of config directory. By default, it uses `$XDG_CONFIG_HOME/stellar` if set, falling back to `~/.config/stellar` otherwise. Contains configuration files, aliases, and other persistent settings +- `--output ` — Format of the output + + Default value: `json` + + Possible values: + - `json`: JSON output of the ledger entry with parsed XDRs (one line, not formatted) + - `json-formatted`: Formatted (multiline) JSON output of the ledger entry with parsed XDRs + - `xdr`: Original RPC output (containing XDRs) + +- `--account ` — Account alias or address to lookup +- `--offer ` — ID of an offer made on the Stellar DEX +- `--hd-path ` — If identity is a seed phrase use this hd path, default is 0 + ## `stellar ledger latest` Get the latest ledger sequence and information from the network diff --git a/cmd/crates/soroban-test/tests/it/integration/ledger.rs b/cmd/crates/soroban-test/tests/it/integration/ledger.rs index 66352f6788..2da9d76e6e 100644 --- a/cmd/crates/soroban-test/tests/it/integration/ledger.rs +++ b/cmd/crates/soroban-test/tests/it/integration/ledger.rs @@ -1,5 +1,6 @@ use soroban_rpc::{GetLatestLedgerResponse, GetLedgersResponse}; use soroban_test::{AssertExt, TestEnv}; +mod entry; #[tokio::test] async fn ledger_latest() { diff --git a/cmd/crates/soroban-test/tests/it/integration/ledger/entry.rs b/cmd/crates/soroban-test/tests/it/integration/ledger/entry.rs new file mode 100644 index 0000000000..7e5589be89 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/ledger/entry.rs @@ -0,0 +1,661 @@ +use sha2::{Digest, Sha256}; +use soroban_cli::{ + config::{address::UnresolvedMuxedAccount, locator}, + tx::builder::TxExt, + xdr::{ + self, AccountId, AlphaNum4, Asset, AssetCode4, ChangeTrustAsset, ChangeTrustOp, + ClaimPredicate, ClaimableBalanceId, Claimant, ClaimantV0, ContractDataDurability, + CreateClaimableBalanceOp, CreateClaimableBalanceResult, Hash, LedgerEntryData, LedgerKey, + LedgerKeyAccount, LedgerKeyClaimableBalance, LedgerKeyContractCode, LedgerKeyContractData, + LedgerKeyData, LedgerKeyLiquidityPool, LedgerKeyTrustLine, Limits, + LiquidityPoolConstantProductParameters, LiquidityPoolParameters, Operation, OperationBody, + OperationResult, OperationResultTr, PoolId, PublicKey, ScAddress, ScVal, String64, StringM, + TransactionEnvelope, TransactionResult, TransactionResultResult, TrustLineAsset, Uint256, + VecM, WriteXdr, + }, +}; + +use soroban_rpc::FullLedgerEntries; +use soroban_rpc::GetTransactionResponse; +use soroban_spec_tools::utils::padded_hex_from_str; +use soroban_test::AssertExt; +use soroban_test::TestEnv; +use stellar_strkey::{ed25519::PublicKey as StrkeyPublicKeyEd25519, Contract}; + +use crate::integration::util::{deploy_contract, test_address, DeployOptions, HELLO_WORLD}; + +// account data tests +// todo: test with --offer, +#[tokio::test] +async fn ledger_entry_account_with_alias() { + let sandbox = &TestEnv::new(); + let account_alias = "new_account"; + let new_account_addr = new_account(sandbox, account_alias); + let output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("account") + .arg("--account") + .arg(account_alias) + .arg("--network") + .arg("testnet") + .assert() + .success() + .stdout_as_str(); + + let (_, expected_key) = expected_account_ledger_key(&new_account_addr).await; + let parsed: FullLedgerEntries = serde_json::from_str(&output).expect("Failed to parse JSON"); + + assert!(!parsed.entries.is_empty()); + assert_eq!(parsed.entries[0].key, expected_key); + assert!(matches!( + parsed.entries[0].val, + LedgerEntryData::Account { .. } + )); +} + +#[tokio::test] +async fn ledger_entry_account_with_account_addr() { + let sandbox = &TestEnv::new(); + let new_account_addr = new_account(sandbox, "new_account"); + let output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("account") + .arg("--account") + .arg(&new_account_addr) + .arg("--network") + .arg("testnet") + .assert() + .success() + .stdout_as_str(); + + let (_, expected_key) = expected_account_ledger_key(&new_account_addr).await; + let parsed: FullLedgerEntries = serde_json::from_str(&output).expect("Failed to parse JSON"); + + assert!(!parsed.entries.is_empty()); + assert_eq!(parsed.entries[0].key, expected_key); + assert!(matches!( + parsed.entries[0].val, + LedgerEntryData::Account { .. } + )); +} + +#[tokio::test] +async fn ledger_entry_trustline_asset_usdc() { + let sandbox = &TestEnv::new(); + let test_account_alias = "test"; + let test_account_address = test_address(sandbox); + let issuer_alias = "test1"; + let issuer_address = new_account(sandbox, issuer_alias); + let asset = &format!("usdc:{issuer_address}"); + let limit = 100_000; + let initial_balance = 100; + issue_asset( + sandbox, + &test_account_address, + &issuer_alias, + asset, + limit, + initial_balance, + ) + .await; + + let output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("trustline") + .arg("--account") + .arg(test_account_alias) + .arg("--network") + .arg("testnet") + .arg("--asset") + .arg(asset) + .assert() + .success() + .stdout_as_str(); + + let (account_id, _expected_account_key) = + expected_account_ledger_key(&test_account_address).await; + let issuer_account_id = get_account_id(&issuer_address); + + let trustline_asset = TrustLineAsset::CreditAlphanum4(AlphaNum4 { + asset_code: AssetCode4(*b"usdc"), + issuer: issuer_account_id, + }); + let expected_trustline_key = LedgerKey::Trustline(LedgerKeyTrustLine { + account_id, + asset: trustline_asset, + }); + + let parsed: FullLedgerEntries = serde_json::from_str(&output).expect("Failed to parse JSON"); + assert!(!parsed.entries.is_empty()); + + let trustline_entry = &parsed.entries[0]; + assert_eq!(trustline_entry.key, expected_trustline_key); + assert!(matches!( + trustline_entry.val, + LedgerEntryData::Trustline { .. } + )); +} + +#[tokio::test] +async fn ledger_entry_account_data() { + let sandbox = &TestEnv::new(); + let account_alias = "new_account"; + let new_account_addr = new_account(sandbox, account_alias); + let data_name = "test_data_key"; + add_account_data(sandbox, account_alias, data_name, "abcdef").await; + + let output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("data") + .arg("--account") + .arg(account_alias) + .arg("--network") + .arg("testnet") + .arg("--data-name") + .arg(data_name) + .assert() + .success() + .stdout_as_str(); + + let parsed: FullLedgerEntries = serde_json::from_str(&output).expect("Failed to parse JSON"); + assert!(!parsed.entries.is_empty()); + + let (account_id, _) = expected_account_ledger_key(&new_account_addr).await; + + let data_entry = &parsed.entries[0]; + let name_bounded_string = StringM::<64>::try_from(data_name).unwrap(); + let expected_data_key = LedgerKey::Data(LedgerKeyData { + account_id, + data_name: String64::from(name_bounded_string), + }); + assert_eq!(data_entry.key, expected_data_key); + assert!(matches!(data_entry.val, LedgerEntryData::Data { .. })); +} + +// contract data tests +#[tokio::test] +async fn ledger_entry_contract_data() { + let sandbox = &TestEnv::new(); + let test_account_alias = "test"; + let contract_id = deploy_contract( + sandbox, + HELLO_WORLD, + DeployOptions { + deployer: Some(test_account_alias.to_string()), + ..Default::default() + }, + ) + .await; + + let storage_key = "COUNTER"; + let storage_key_xdr = ScVal::Symbol(storage_key.try_into().unwrap()) + .to_xdr_base64(Limits::none()) + .unwrap(); + + // update contract storage + sandbox + .invoke_with_test(&["--id", &contract_id, "--", "inc"]) + .await + .unwrap(); + + // get entry by key + let key_output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("contract-data") + .arg("--contract") + .arg(&contract_id) + .arg("--network") + .arg("testnet") + .arg("--key") + .arg(storage_key) + .assert() + .success() + .stdout_as_str(); + let parsed_key_output: FullLedgerEntries = + serde_json::from_str(&key_output).expect("Failed to parse JSON"); + assert!(!parsed_key_output.entries.is_empty()); + + // get entry by key xdr + let key_xdr_output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("contract-data") + .arg("--contract") + .arg(&contract_id) + .arg("--network") + .arg("testnet") + .arg("--key-xdr") + .arg(storage_key_xdr) + .assert() + .success() + .stdout_as_str(); + + let parsed_key_xdr_output: FullLedgerEntries = + serde_json::from_str(&key_xdr_output).expect("Failed to parse JSON"); + assert!(!parsed_key_xdr_output.entries.is_empty()); + + let expected_contract_data_key = expected_contract_ledger_key(&contract_id, storage_key).await; + + assert_eq!(parsed_key_output.entries[0].key, expected_contract_data_key); + assert!(matches!( + parsed_key_output.entries[0].val, + LedgerEntryData::ContractData { .. } + )); + + assert_eq!( + parsed_key_xdr_output.entries[0].key, + expected_contract_data_key + ); + assert!(matches!( + parsed_key_xdr_output.entries[0].val, + LedgerEntryData::ContractData { .. } + )); + + // the output should be the same regardless of key format + assert_eq!(parsed_key_output.entries, parsed_key_xdr_output.entries); +} + +// top level test +#[tokio::test] +async fn ledger_entry_contract_code() { + let sandbox = &TestEnv::new(); + let test_account_alias = "test"; + let wasm = HELLO_WORLD; + let wasm_path = wasm.path(); + let contract_wasm_hash = sandbox + .new_assert_cmd("contract") + .arg("upload") + .arg("--wasm") + .arg(wasm_path) + .assert() + .success() + .stdout_as_str(); + + deploy_contract( + sandbox, + HELLO_WORLD, + DeployOptions { + deployer: Some(test_account_alias.to_string()), + ..Default::default() + }, + ) + .await; + + // get the contract's wasm bytecode + let output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("contract-code") + .arg("--wasm-hash") + .arg(&contract_wasm_hash) + .arg("--network") + .arg("testnet") + .assert() + .success() + .stdout_as_str(); + let parsed_output: FullLedgerEntries = + serde_json::from_str(&output).expect("Failed to parse JSON"); + assert!(!parsed_output.entries.is_empty()); + + let hash = Hash( + padded_hex_from_str(&contract_wasm_hash, 32) + .unwrap() + .try_into() + .unwrap(), + ); + let expected_contract_key = LedgerKey::ContractCode(LedgerKeyContractCode { hash }); + + assert_eq!(parsed_output.entries[0].key, expected_contract_key); + assert!(matches!( + parsed_output.entries[0].val, + LedgerEntryData::ContractCode { .. } + )); +} + +#[tokio::test] +async fn ledger_entry_claimable_balance() { + let sandbox = &TestEnv::new(); + // create a claimable balance + let sender_alias = "test"; + let sender = test_address(sandbox); + let claimant = new_account(sandbox, "claimant"); + let tx_env = claimable_balance_tx_env(&sender, &claimant); + let tx_xdr = tx_env.to_xdr_base64(Limits::none()).unwrap(); + let updated_tx = update_seq_number(sandbox, &tx_xdr); + let tx_output = sign_and_send(sandbox, sender_alias, &updated_tx).await; + let response: GetTransactionResponse = + serde_json::from_str(&tx_output).expect("Failed to parse JSON"); + let id = extract_claimable_balance_id(response).unwrap(); + + // fetch the claimable-balance + let output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("claimable-balance") + .arg("--id") + .arg(id.to_string()) + .arg("--network") + .arg("local") + .assert() + .success() + .stdout_as_str(); + let parsed_output: FullLedgerEntries = + serde_json::from_str(&output).expect("Failed to parse JSON"); + assert!(!parsed_output.entries.is_empty()); + let expected_key = LedgerKey::ClaimableBalance(LedgerKeyClaimableBalance { + balance_id: ClaimableBalanceId::ClaimableBalanceIdTypeV0(id), + }); + assert_eq!(parsed_output.entries[0].key, expected_key); + assert!(matches!( + parsed_output.entries[0].val, + LedgerEntryData::ClaimableBalance { .. } + )); +} + +#[tokio::test] +async fn ledger_entry_liquidity_pool() { + let sandbox = &TestEnv::new(); + let test_account_alias = "test"; + let test_account_address = test_address(sandbox); + // issue usdc + let issuer_alias = "test1"; + let issuer_address = new_account(sandbox, issuer_alias); + let asset = &format!("usdc:{issuer_address}"); + let limit = 100_000; + let initial_balance = 100; + issue_asset( + sandbox, + &test_account_address, + &issuer_alias, + asset, + limit, + initial_balance, + ) + .await; + + // create liquidity pool + let (tx_env, pool_id) = liquidity_pool_tx_env(&test_account_address, &issuer_address); + let tx_xdr = tx_env.to_xdr_base64(Limits::none()).unwrap(); + let updated_tx = update_seq_number(sandbox, &tx_xdr); + sign_and_send(sandbox, test_account_alias, &updated_tx).await; + + // fetch the liquidity pool + let output = sandbox + .new_assert_cmd("ledger") + .arg("entry") + .arg("fetch") + .arg("liquidity-pool") + .arg("--id") + .arg(pool_id.to_string()) + .arg("--network") + .arg("local") + .assert() + .success() + .stdout_as_str(); + let parsed_output: FullLedgerEntries = + serde_json::from_str(&output).expect("Failed to parse JSON"); + assert!(!parsed_output.entries.is_empty()); + let expected_key = LedgerKey::LiquidityPool(LedgerKeyLiquidityPool { + liquidity_pool_id: PoolId(Hash(pool_id.0)), + }); + assert_eq!(parsed_output.entries[0].key, expected_key); + assert!(matches!( + parsed_output.entries[0].val, + LedgerEntryData::LiquidityPool { .. } + )); +} + +// Helper Fns +fn new_account(sandbox: &TestEnv, name: &str) -> String { + sandbox.generate_account(name, None).assert().success(); + sandbox.fund_account(name).success(); + + sandbox + .new_assert_cmd("keys") + .args(["address", name]) + .assert() + .success() + .stdout_as_str() +} + +async fn issue_asset( + sandbox: &TestEnv, + test_addr: &str, + issuer_alias: &str, + asset: &str, + limit: u64, + initial_balance: u64, +) { + let client = sandbox.network.rpc_client().unwrap(); + let test_before = client.get_account(test_addr).await.unwrap(); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "change-trust", + "--line", + asset, + "--limit", + limit.to_string().as_str(), + ]) + .assert() + .success() + .stdout_as_str(); + + let after = client.get_account(test_addr).await.unwrap(); + assert_eq!(test_before.num_sub_entries + 1, after.num_sub_entries); + + // Send asset to the test + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "payment", + "--destination", + test_addr, + "--asset", + asset, + "--amount", + initial_balance.to_string().as_str(), + "--source", + issuer_alias, + ]) + .assert() + .success(); +} + +async fn expected_account_ledger_key(account_addr: &str) -> (AccountId, LedgerKey) { + let account_id = get_account_id(account_addr); + let ledger_key = LedgerKey::Account(LedgerKeyAccount { + account_id: account_id.clone(), + }); + (account_id, ledger_key) +} + +fn get_account_id(account_addr: &str) -> AccountId { + let strkey = StrkeyPublicKeyEd25519::from_string(account_addr).unwrap().0; + + let uint256 = Uint256(strkey); + let pk = PublicKey::PublicKeyTypeEd25519(uint256); + AccountId(pk) +} + +async fn expected_contract_ledger_key(contract_id: &str, storage_key: &str) -> LedgerKey { + let contract_bytes: [u8; 32] = Contract::from_string(contract_id).unwrap().0; + let contract_id = Hash(contract_bytes); + LedgerKey::ContractData(LedgerKeyContractData { + contract: ScAddress::Contract(contract_id.into()), + key: ScVal::Symbol(storage_key.try_into().unwrap()), + durability: ContractDataDurability::Persistent, + }) +} + +async fn add_account_data(sandbox: &TestEnv, account_alias: &str, key: &str, value: &str) { + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "manage-data", + "--data-name", + key, + "--data-value", + value, + "--source", + account_alias, + ]) + .assert() + .success(); +} + +fn claimable_balance_tx_env(sender: &str, destination: &str) -> TransactionEnvelope { + let destination_id = get_account_id(&destination); + let claimant = Claimant::ClaimantTypeV0(ClaimantV0 { + destination: destination_id, + predicate: ClaimPredicate::Unconditional, + }); + let claimants = VecM::try_from(vec![claimant]).unwrap(); + let create_op = Operation { + source_account: None, + body: OperationBody::CreateClaimableBalance(CreateClaimableBalanceOp { + asset: Asset::Native, + amount: 10_000_000, + claimants: claimants, + }), + }; + + let source: UnresolvedMuxedAccount = sender.parse().unwrap(); + let resolved_source = source + .resolve_muxed_account_sync(&locator::Args::default(), None) + .unwrap(); + + xdr::Transaction::new_tx(resolved_source, 1000, 1, create_op).into() +} + +fn liquidity_pool_tx_env( + test_account_address: &str, + usdc_issuer_address: &str, +) -> (TransactionEnvelope, Uint256) { + let issuer_account_id = get_account_id(&usdc_issuer_address); + let usdc_asset = Asset::CreditAlphanum4(AlphaNum4 { + asset_code: AssetCode4(*b"usdc"), + issuer: issuer_account_id, + }); + + let asset_a = Asset::Native; + let asset_b = usdc_asset; + let fee = 30; + + let line = ChangeTrustAsset::PoolShare(LiquidityPoolParameters::LiquidityPoolConstantProduct( + LiquidityPoolConstantProductParameters { + asset_a: asset_a.clone(), + asset_b: asset_b.clone(), + fee, + }, + )); + let op = Operation { + source_account: None, + body: OperationBody::ChangeTrust(ChangeTrustOp { + line: line, + limit: i64::MAX, + }), + }; + + let source: UnresolvedMuxedAccount = test_account_address.parse().unwrap(); + let resolved_source = source + .resolve_muxed_account_sync(&locator::Args::default(), None) + .unwrap(); + + let tx = xdr::Transaction::new_tx(resolved_source, 1000, 1, op).into(); + + let pool_id = compute_pool_id(asset_a.clone(), asset_b.clone(), fee); + + (tx, pool_id) +} + +fn update_seq_number(sandbox: &TestEnv, tx_xdr: &str) -> String { + sandbox + .new_assert_cmd("tx") + .arg("update") + .arg("seq-num") + .arg("next") + .write_stdin(tx_xdr.as_bytes()) + .assert() + .success() + .stdout_as_str() +} + +async fn sign_and_send(sandbox: &TestEnv, sign_with: &str, tx: &str) -> String { + let tx_signed = sandbox + .new_assert_cmd("tx") + .arg("sign") + .arg("--sign-with-key") + .arg(sign_with) + .write_stdin(tx.as_bytes()) + .assert() + .success() + .stdout_as_str(); + + sandbox + .new_assert_cmd("tx") + .arg("send") + .write_stdin(tx_signed.as_bytes()) + .assert() + .success() + .stdout(predicates::str::contains("SUCCESS")) + .stdout_as_str() +} + +fn extract_claimable_balance_id(response: GetTransactionResponse) -> Option { + if let Some(result) = response.result { + if let TransactionResult { + result: TransactionResultResult::TxSuccess(results), + .. + } = result + { + if let Some(OperationResult::OpInner(OperationResultTr::CreateClaimableBalance( + CreateClaimableBalanceResult::Success( + ClaimableBalanceId::ClaimableBalanceIdTypeV0(hash), + ), + ))) = results.first() + { + return Some(hash.clone()); + } + } + } + None +} + +fn compute_pool_id(asset_a: Asset, asset_b: Asset, fee: i32) -> Uint256 { + let (asset_a, asset_b) = if asset_a < asset_b { + (asset_a, asset_b) + } else { + (asset_b, asset_a) + }; + + let pool_params = LiquidityPoolParameters::LiquidityPoolConstantProduct( + LiquidityPoolConstantProductParameters { + asset_a, + asset_b, + fee, + }, + ); + + let mut hasher = Sha256::new(); + hasher.update(pool_params.to_xdr(Limits::none()).unwrap()); + let hash = hasher.finalize(); + + Uint256(hash.into()) +} diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index a7ab010350..bb327a07b3 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -144,18 +144,19 @@ impl Cmd { error, } })?, - serde_json::to_string_pretty(&live_until_ledger_seq).map_err(|error| { - Error::CannotPrintJsonResult { + serde_json::to_string_pretty(&live_until_ledger_seq.unwrap_or_default()) + .map_err(|error| Error::CannotPrintJsonResult { result: val.clone(), error, - } - })?, + })?, ], Output::Xdr => [ key.to_xdr_base64(Limits::none())?, val.to_xdr_base64(Limits::none())?, last_modified_ledger.to_xdr_base64(Limits::none())?, - live_until_ledger_seq.to_xdr_base64(Limits::none())?, + live_until_ledger_seq + .unwrap_or_default() + .to_xdr_base64(Limits::none())?, ], }; out.write_record(output) diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/account.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/account.rs new file mode 100644 index 0000000000..ae1e322dc9 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/account.rs @@ -0,0 +1,67 @@ +use std::array::TryFromSliceError; +use std::fmt::Debug; + +use super::args::Args; +use crate::{ + commands::config::{self, locator}, + xdr::{LedgerKey, LedgerKeyAccount, MuxedAccount}, +}; +use clap::{command, Parser}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + /// Account alias or address to lookup + #[arg(long)] + pub account: String, + + #[command(flatten)] + pub args: Args, + + /// If identity is a seed phrase use this hd path, default is 0 + #[arg(long)] + pub hd_path: Option, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] config::key::Error), + #[error("provided asset is invalid: {0}")] + InvalidAsset(String), + #[error("provided data name is invalid: {0}")] + InvalidDataName(String), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + TryFromSliceError(#[from] TryFromSliceError), + #[error(transparent)] + Run(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_account_keys(&mut ledger_keys)?; + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_account_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + let acc = self.muxed_account(&self.account)?; + let key = LedgerKey::Account(LedgerKeyAccount { + account_id: acc.account_id(), + }); + + ledger_keys.push(key); + + Ok(()) + } + + fn muxed_account(&self, account: &str) -> Result { + Ok(self + .args + .locator + .read_key(account)? + .muxed_account(self.hd_path)?) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/account_data.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/account_data.rs new file mode 100644 index 0000000000..ee99a7430e --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/account_data.rs @@ -0,0 +1,77 @@ +use std::array::TryFromSliceError; +use std::fmt::Debug; + +use super::args::Args; +use crate::{ + commands::config::{self, locator}, + xdr::{self, LedgerKey, LedgerKeyData, MuxedAccount, String64}, +}; +use clap::{command, Parser}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub args: Args, + + /// Account alias or address to lookup + #[arg(long)] + pub account: String, + + /// Fetch key-value data entries attached to an account (see manageDataOp) + #[arg(long, required = true)] + pub data_name: Vec, + + /// If identity is a seed phrase use this hd path, default is 0 + #[arg(long)] + pub hd_path: Option, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] config::key::Error), + #[error("provided asset is invalid: {0}")] + InvalidAsset(String), + #[error("provided data name is invalid: {0}")] + InvalidDataName(String), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + TryFromSliceError(#[from] TryFromSliceError), + #[error(transparent)] + Run(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_data_keys(&mut ledger_keys)?; + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_data_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + let acc = self.muxed_account(&self.account)?; + for data_name in &self.data_name { + let data_name: xdr::StringM<64> = data_name + .parse() + .map_err(|_| Error::InvalidDataName(data_name.clone()))?; + let data_name = String64(data_name); + let key = LedgerKey::Data(LedgerKeyData { + account_id: acc.clone().account_id(), + data_name, + }); + ledger_keys.push(key); + } + + Ok(()) + } + + fn muxed_account(&self, account: &str) -> Result { + Ok(self + .args + .locator + .read_key(account)? + .muxed_account(self.hd_path)?) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/args.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/args.rs new file mode 100644 index 0000000000..33d5b6e0ca --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/args.rs @@ -0,0 +1,70 @@ +use crate::{ + config::{ + locator, + network::{self, Network}, + }, + rpc, + xdr::LedgerKey, +}; + +#[derive(Debug, clap::Args, Clone)] +#[group(skip)] +pub struct Args { + #[command(flatten)] + pub network: network::Args, + + #[command(flatten)] + pub locator: locator::Args, + + /// Format of the output + #[arg(long, default_value = "json")] + pub output: OutputFormat, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Serde(#[from] serde_json::Error), + #[error(transparent)] + Rpc(#[from] rpc::Error), +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum, Default)] +pub enum OutputFormat { + /// JSON output of the ledger entry with parsed XDRs (one line, not formatted) + #[default] + Json, + /// Formatted (multiline) JSON output of the ledger entry with parsed XDRs + JsonFormatted, + /// Original RPC output (containing XDRs) + Xdr, +} + +impl Args { + pub fn network(&self) -> Result { + Ok(self.network.get(&self.locator)?) + } + + pub async fn run(&self, ledger_keys: Vec) -> Result<(), Error> { + let network = self.network.get(&self.locator)?; + let client = network.rpc_client()?; + match self.output { + OutputFormat::Json => { + let resp = client.get_full_ledger_entries(&ledger_keys).await?; + println!("{}", serde_json::to_string(&resp)?); + } + OutputFormat::Xdr => { + let resp = client.get_ledger_entries(&ledger_keys).await?; + println!("{}", serde_json::to_string(&resp)?); + } + OutputFormat::JsonFormatted => { + let resp = client.get_full_ledger_entries(&ledger_keys).await?; + println!("{}", serde_json::to_string_pretty(&resp)?); + } + } + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/claimable_balance.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/claimable_balance.rs new file mode 100644 index 0000000000..2aefd9ef18 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/claimable_balance.rs @@ -0,0 +1,51 @@ +use super::args::Args; +use crate::xdr::{ + ClaimableBalanceId::ClaimableBalanceIdTypeV0, Hash, LedgerKey, LedgerKeyClaimableBalance, +}; +use clap::{command, Parser}; +use hex::FromHexError; +use soroban_spec_tools::utils::padded_hex_from_str; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + /// Claimable Balance Ids to fetch an entry for + #[arg(long)] + pub id: Vec, + + #[command(flatten)] + pub args: Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + FromHexError(#[from] FromHexError), + #[error("provided hash value is invalid: {0}")] + InvalidHash(String), + #[error(transparent)] + Run(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_keys(&mut ledger_keys)?; + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + for x in &self.id { + let padded_hex = padded_hex_from_str(x, 32)?; + let hash_bytes: [u8; 32] = padded_hex + .try_into() + .map_err(|_| Error::InvalidHash(x.clone()))?; + let hash = Hash(hash_bytes); + let key = LedgerKey::ClaimableBalance(LedgerKeyClaimableBalance { + balance_id: ClaimableBalanceIdTypeV0(hash), + }); + ledger_keys.push(key); + } + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/contract_code.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/contract_code.rs new file mode 100644 index 0000000000..bf2eb097ce --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/contract_code.rs @@ -0,0 +1,43 @@ +use super::args::Args; +use crate::xdr::{Hash, LedgerKey, LedgerKeyContractCode}; +use clap::{command, Parser}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + /// Get WASM bytecode by hash + #[arg(long)] + pub wasm_hash: Vec, + + #[command(flatten)] + pub args: Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("provided hash value is invalid: {0}")] + InvalidHash(String), + #[error(transparent)] + Run(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_keys(&mut ledger_keys)?; + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + for hash in &self.wasm_hash { + let hash = Hash( + soroban_spec_tools::utils::contract_id_from_str(hash) + .map_err(|_| Error::InvalidHash(hash.clone()))?, + ); + let key = LedgerKey::ContractCode(LedgerKeyContractCode { hash }); + ledger_keys.push(key); + } + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/contract_data.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/contract_data.rs new file mode 100644 index 0000000000..8c6102dee9 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/contract_data.rs @@ -0,0 +1,90 @@ +use super::args::Args; +use crate::{ + commands::contract::Durability, + config::{self, locator}, + xdr::{ + self, ContractDataDurability, ContractId, Hash, LedgerKey, LedgerKeyContractData, Limits, + ReadXdr, ScAddress, ScVal, + }, +}; +use clap::{command, Parser}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + /// Contract alias or address to fetch + #[arg(long)] + pub contract: config::UnresolvedContract, + + #[command(flatten)] + pub args: Args, + + /// Storage entry durability + #[arg(long, value_enum, default_value = "persistent")] + pub durability: Durability, + + /// Storage key (symbols only) + #[arg(long = "key", required_unless_present = "key_xdr")] + pub key: Option>, + + /// Storage key (base64-encoded XDR) + #[arg(long = "key-xdr", required_unless_present = "key")] + pub key_xdr: Option>, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Run(#[from] super::args::Error), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + Spec(#[from] soroban_spec_tools::Error), + #[error(transparent)] + StellarXdr(#[from] stellar_xdr::curr::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_keys(&mut ledger_keys)?; + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + let network = self.args.network()?; + let contract_id = self + .contract + .resolve_contract_id(&self.args.locator, &network.network_passphrase)?; + let contract_address_arg = ScAddress::Contract(ContractId(Hash(contract_id.0))); + + if let Some(keys) = &self.key { + for key in keys { + let key = LedgerKey::ContractData(LedgerKeyContractData { + contract: contract_address_arg.clone(), + key: soroban_spec_tools::from_string_primitive( + key, + &xdr::ScSpecTypeDef::Symbol, + )?, + durability: ContractDataDurability::Persistent, + }); + + ledger_keys.push(key); + } + } + + if let Some(keys) = &self.key_xdr { + for key in keys { + let key = LedgerKey::ContractData(LedgerKeyContractData { + contract: contract_address_arg.clone(), + key: ScVal::from_xdr_base64(key, Limits::none())?, + durability: ContractDataDurability::Persistent, + }); + + ledger_keys.push(key); + } + } + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/liquidity_pool.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/liquidity_pool.rs new file mode 100644 index 0000000000..631823c8b4 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/liquidity_pool.rs @@ -0,0 +1,51 @@ +use super::args::Args; +use crate::xdr::{Hash, LedgerKey, LedgerKeyLiquidityPool, PoolId}; +use clap::{command, Parser}; +use hex::FromHexError; +use soroban_spec_tools::utils::padded_hex_from_str; +use std::fmt::Debug; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + /// Liquidity pool ids + #[arg(long)] + pub id: Vec, + + #[command(flatten)] + pub args: Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + FromHexError(#[from] FromHexError), + #[error("provided hash value is invalid: {0}")] + InvalidHash(String), + #[error(transparent)] + Run(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_keys(&mut ledger_keys)?; + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + for x in &self.id { + let padded_hex = padded_hex_from_str(x, 32)?; + let hash_bytes: [u8; 32] = padded_hex + .try_into() + .map_err(|_| Error::InvalidHash(x.clone()))?; + let hash = Hash(hash_bytes); + let key = LedgerKey::LiquidityPool(LedgerKeyLiquidityPool { + liquidity_pool_id: PoolId(hash), + }); + ledger_keys.push(key); + } + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/mod.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/mod.rs new file mode 100644 index 0000000000..02beca3d15 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/mod.rs @@ -0,0 +1,68 @@ +use clap::Parser; +use std::fmt::Debug; + +pub mod account; +pub mod account_data; +pub mod args; +pub mod claimable_balance; +pub mod contract_code; +pub mod contract_data; +pub mod liquidity_pool; +pub mod offer; +pub mod trustline; + +#[derive(Debug, Parser)] +pub enum Cmd { + /// Fetch account entry by public key or alias. + Account(account::Cmd), + /// Fetch contract ledger entry by address or alias and storage key. + ContractData(contract_data::Cmd), + ///Fetch a claimable balance ledger entry by id + ClaimableBalance(claimable_balance::Cmd), + ///Fetch a liquidity pool ledger entry by id + LiquidityPool(liquidity_pool::Cmd), + /// Fetch a Contract's WASM bytecode by WASM hash + ContractCode(contract_code::Cmd), + /// Fetch a trustline by account and asset + Trustline(trustline::Cmd), + /// Fetch key-value data entries attached to an account (see manageDataOp) + Data(account_data::Cmd), + /// Fetch an offer by account and offer id + Offer(offer::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Account(#[from] account::Error), + #[error(transparent)] + ContractData(#[from] contract_data::Error), + #[error(transparent)] + ClaimableBalance(#[from] claimable_balance::Error), + #[error(transparent)] + LiquidityPool(#[from] liquidity_pool::Error), + #[error(transparent)] + Wasm(#[from] contract_code::Error), + #[error(transparent)] + Trustline(#[from] trustline::Error), + #[error(transparent)] + Data(#[from] account_data::Error), + #[error(transparent)] + Offer(#[from] offer::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + match self { + Cmd::Account(cmd) => cmd.run().await?, + Cmd::ContractData(cmd) => cmd.run().await?, + Cmd::ClaimableBalance(cmd) => cmd.run().await?, + Cmd::LiquidityPool(cmd) => cmd.run().await?, + Cmd::ContractCode(cmd) => cmd.run().await?, + Cmd::Trustline(cmd) => cmd.run().await?, + Cmd::Data(cmd) => cmd.run().await?, + Cmd::Offer(cmd) => cmd.run().await?, + } + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/offer.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/offer.rs new file mode 100644 index 0000000000..1c2dcc2bb0 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/offer.rs @@ -0,0 +1,74 @@ +use std::array::TryFromSliceError; +use std::fmt::Debug; + +use super::args::Args; +use crate::{ + commands::config::{self, locator}, + xdr::{LedgerKey, LedgerKeyOffer, MuxedAccount}, +}; +use clap::{command, Parser}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub args: Args, + + /// Account alias or address to lookup + #[arg(long)] + pub account: String, + + /// ID of an offer made on the Stellar DEX + #[arg(long, required = true)] + pub offer: Vec, + + /// If identity is a seed phrase use this hd path, default is 0 + #[arg(long)] + pub hd_path: Option, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] config::key::Error), + #[error("provided asset is invalid: {0}")] + InvalidAsset(String), + #[error("provided data name is invalid: {0}")] + InvalidDataName(String), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + TryFromSliceError(#[from] TryFromSliceError), + #[error(transparent)] + Run(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_offer_keys(&mut ledger_keys)?; + + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_offer_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + let acc = self.muxed_account(&self.account)?; + for offer in &self.offer { + let key = LedgerKey::Offer(LedgerKeyOffer { + seller_id: acc.clone().account_id(), + offer_id: *offer, + }); + ledger_keys.push(key); + } + + Ok(()) + } + + fn muxed_account(&self, account: &str) -> Result { + Ok(self + .args + .locator + .read_key(account)? + .muxed_account(self.hd_path)?) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/fetch/trustline.rs b/cmd/soroban-cli/src/commands/ledger/entry/fetch/trustline.rs new file mode 100644 index 0000000000..2cf08ecb92 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/fetch/trustline.rs @@ -0,0 +1,104 @@ +use std::array::TryFromSliceError; +use std::fmt::Debug; + +use super::args::Args; +use crate::{ + commands::config::{self, locator}, + xdr::{ + AccountId, AlphaNum12, AlphaNum4, AssetCode12, AssetCode4, LedgerKey, LedgerKeyTrustLine, + MuxedAccount, PublicKey, TrustLineAsset, Uint256, + }, +}; +use clap::{command, Parser}; +use stellar_strkey::ed25519::PublicKey as Ed25519PublicKey; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub args: Args, + + /// Account alias or address to lookup + #[arg(long)] + pub account: String, + + /// Assets to get trustline info for + #[arg(long, required = true)] + pub asset: Vec, + + /// If account is a seed phrase use this hd path, default is 0 + #[arg(long)] + pub hd_path: Option, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] config::key::Error), + #[error("provided asset is invalid: {0}")] + InvalidAsset(String), + #[error("provided data name is invalid: {0}")] + InvalidDataName(String), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + TryFromSliceError(#[from] TryFromSliceError), + #[error(transparent)] + Run(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let mut ledger_keys = vec![]; + self.insert_asset_keys(&mut ledger_keys)?; + Ok(self.args.run(ledger_keys).await?) + } + + fn insert_asset_keys(&self, ledger_keys: &mut Vec) -> Result<(), Error> { + let acc = self.muxed_account(&self.account)?; + for asset in &self.asset { + let asset = if asset.eq_ignore_ascii_case("XLM") { + TrustLineAsset::Native + } else if asset.contains(':') { + let mut parts = asset.split(':'); + let code = parts.next().ok_or(Error::InvalidAsset(asset.clone()))?; + let issuer = parts.next().ok_or(Error::InvalidAsset(asset.clone()))?; + if parts.next().is_some() { + Err(Error::InvalidAsset(asset.clone()))?; + } + let source_bytes = Ed25519PublicKey::from_string(issuer).unwrap().0; + let issuer = AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(source_bytes))); + + match code.len() { + 4 => TrustLineAsset::CreditAlphanum4(AlphaNum4 { + asset_code: AssetCode4(code.as_bytes().try_into()?), + issuer, + }), + 12 => TrustLineAsset::CreditAlphanum12(AlphaNum12 { + asset_code: AssetCode12(code.as_bytes().try_into()?), + issuer, + }), + _ => Err(Error::InvalidAsset(asset.clone()))?, + } + } else { + Err(Error::InvalidAsset(asset.clone()))? + }; + + let key = LedgerKey::Trustline(LedgerKeyTrustLine { + account_id: acc.clone().account_id(), + asset, + }); + + ledger_keys.push(key); + } + Ok(()) + } + + fn muxed_account(&self, account: &str) -> Result { + Ok(self + .args + .locator + .read_key(account)? + .muxed_account(self.hd_path)?) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/entry/mod.rs b/cmd/soroban-cli/src/commands/ledger/entry/mod.rs new file mode 100644 index 0000000000..67e02d6118 --- /dev/null +++ b/cmd/soroban-cli/src/commands/ledger/entry/mod.rs @@ -0,0 +1,25 @@ +use clap::Parser; +pub mod fetch; + +#[derive(Debug, Parser)] +pub enum Cmd { + /// Fetch ledger entries. This command supports all types of ledger entries supported by the RPC. + /// Read more about the RPC command here: [https://developers.stellar.org/docs/data/apis/rpc/api-reference/methods/getLedgerEntries#types-of-ledgerkeys](https://developers.stellar.org/docs/data/apis/rpc/api-reference/methods/getLedgerEntries#types-of-ledgerkeys) + #[command(subcommand)] + Fetch(fetch::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Fetch(#[from] fetch::Error), +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + match self { + Cmd::Fetch(cmd) => cmd.run().await?, + } + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/ledger/mod.rs b/cmd/soroban-cli/src/commands/ledger/mod.rs index 64e7fd755d..8d5f94ae2d 100644 --- a/cmd/soroban-cli/src/commands/ledger/mod.rs +++ b/cmd/soroban-cli/src/commands/ledger/mod.rs @@ -1,10 +1,14 @@ use crate::commands::global; use clap::Subcommand; +pub mod entry; mod fetch; mod latest; #[derive(Debug, Subcommand)] pub enum Cmd { + /// Work with ledger entries. + #[command(subcommand)] + Entry(entry::Cmd), /// Get the latest ledger sequence and information from the network Latest(latest::Cmd), Fetch(fetch::Cmd), @@ -12,6 +16,8 @@ pub enum Cmd { #[derive(thiserror::Error, Debug)] pub enum Error { + #[error(transparent)] + Entry(#[from] entry::Error), #[error(transparent)] Latest(#[from] latest::Error), #[error(transparent)] @@ -21,6 +27,7 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { match &self { + Cmd::Entry(cmd) => cmd.run().await?, Cmd::Latest(cmd) => cmd.run(global_args).await?, Cmd::Fetch(cmd) => cmd.run(global_args).await?, } diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index d3203fbbb6..a6aa805618 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -136,9 +136,9 @@ impl Root { Cmd::Version(version) => version.run(), Cmd::Keys(id) => id.run(&self.global_args).await?, Cmd::Tx(tx) => tx.run(&self.global_args).await?, + Cmd::Ledger(ledger) => ledger.run(&self.global_args).await?, Cmd::Cache(cache) => cache.run()?, Cmd::Env(env) => env.run(&self.global_args)?, - Cmd::Ledger(env) => env.run(&self.global_args).await?, Cmd::FeeStats(env) => env.run(&self.global_args).await?, } Ok(())