From a352507985a1bb04b918ee5f4da682644cf5ae44 Mon Sep 17 00:00:00 2001 From: Einer Zapata Date: Wed, 24 Jul 2024 08:54:07 -0500 Subject: [PATCH] Release v0.21 (#169) * Add endpoint get_version_info (#157) * Add state_changes in Simulate Transaction response (#164) * Add state_changes in Simulate Transaction response * Add nil option in for state_changes property * Add endpoint get transaction (#165) * Add endpoint get transactions * Add tests and improve the code * Add readme documentation * Add TransactionsPayload in the params for endpoint spec * Add endpoint get fee stats (#166) * Add endpoint getFeedStats * Add tests for get fee stats * Add readme section for getFeeStats * Fix typo (#167) * Add prerelease v0.21.0 (#168) * Add prerelease v0.21.0 * Update the release date --- CHANGELOG.md | 8 + README.md | 152 +++++++++++- lib/rpc.ex | 6 + .../events_payload/events_payload.ex | 24 +- lib/rpc/endpoints/get_events.ex | 3 +- lib/rpc/endpoints/get_fee_stats.ex | 19 ++ lib/rpc/endpoints/get_transactions.ex | 22 ++ lib/rpc/endpoints/get_version_info.ex | 19 ++ lib/rpc/endpoints/helper.ex | 30 +++ lib/rpc/endpoints/spec.ex | 4 +- .../transactions_payload.ex | 45 ++++ lib/rpc/responses/get_fee_stats_response.ex | 16 ++ .../responses/get_transactions_response.ex | 33 +++ .../responses/get_version_info_response.ex | 24 ++ .../simulate_transaction_response.ex | 3 + mix.exs | 4 +- mix.lock | 14 +- test/rpc/endpoints/get_fee_stats_test.exs | 114 +++++++++ test/rpc/endpoints/get_transactions_test.exs | 84 +++++++ test/rpc/endpoints/get_version_info_test.exs | 50 ++++ .../transactions_payload_test.exs | 112 +++++++++ .../responses/get_fee_stats_response_test.exs | 94 +++++++ .../get_transactions_response_test.exs | 66 +++++ test/rpc/responses/get_versions_info_test.exs | 39 +++ test/rpc_test.exs | 229 +++++++++++++++++- 25 files changed, 1176 insertions(+), 38 deletions(-) create mode 100644 lib/rpc/endpoints/get_fee_stats.ex create mode 100644 lib/rpc/endpoints/get_transactions.ex create mode 100644 lib/rpc/endpoints/get_version_info.ex create mode 100644 lib/rpc/endpoints/helper.ex create mode 100644 lib/rpc/endpoints/transactions_payload/transactions_payload.ex create mode 100644 lib/rpc/responses/get_fee_stats_response.ex create mode 100644 lib/rpc/responses/get_transactions_response.ex create mode 100644 lib/rpc/responses/get_version_info_response.ex create mode 100644 test/rpc/endpoints/get_fee_stats_test.exs create mode 100644 test/rpc/endpoints/get_transactions_test.exs create mode 100644 test/rpc/endpoints/get_version_info_test.exs create mode 100644 test/rpc/endpoints/transactions_payload/transactions_payload_test.exs create mode 100644 test/rpc/responses/get_fee_stats_response_test.exs create mode 100644 test/rpc/responses/get_transactions_response_test.exs create mode 100644 test/rpc/responses/get_versions_info_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0198915..f4fb5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.21.0 (24.07.2024) + +- [Stellar Protocol 21 support](https://github.com/kommitters/soroban.ex/issues/158). + - Create the getVersionInfo endpoint + - Create the getTransactions endpoint + - Create the getFeeStats endpoint + - Update the simulateTransaction response + ## 0.20.1 (25.04.2024) - Add stale issues policy. See [PR #154](https://github.com/kommitters/soroban.ex/pull/154) diff --git a/README.md b/README.md index de07103..5d0e4a1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ```elixir def deps do [ - {:soroban, "~> 0.20.1"} + {:soroban, "~> 0.21.0"} ] end ``` @@ -219,6 +219,7 @@ Soroban.RPC.Server.local() # http://localhost:8000 Submit a trial contract invocation to get back return values, expected ledger footprint, and expected costs. **Parameters** + - `server`: `Soroban.RPC.Server` struct - The Soroban-RPC server to interact with. - `params`: Parameters to simulate the transaction: - `transaction`: `` - The transaction to be simulated (serialized in base64). @@ -250,6 +251,7 @@ Soroban.RPC.simulate_transaction(server, transaction: base64_envelope, addl_reso cost: %{cpu_insns: "1048713", mem_bytes: "1201148"}, latest_ledger: 45075181, restore_preamble: nil, + state_changes: nil, error: nil }} @@ -323,6 +325,72 @@ Soroban.RPC.get_transaction(server, hash) ``` +#### Get Transactions + +The getTransactions method return a detailed list of transactions starting from the user specified starting point. + +**Parameters** + +- `server`: `Soroban.RPC.Server` struct - The Soroban-RPC server to interact with. +- `TransactionsPayload`: + + - `start_ledger`: Stringified ledger sequence number to fetch events after (inclusive). This method will return an error if start_ledger is less than the oldest ledger stored in this node, or greater than the latest ledger seen by this node. If a cursor is included in the request, start_ledger must be omitted. + + - `cursor`: A string ID that points to a specific location in a collection of responses and is pulled from the paging_token value of a record. When a cursor is provided Soroban-RPC will not include the element whose id matches the cursor in the response. Only elements which appear after the cursor are included. + + - `limit`: The maximum number of records returned. For getTransactions, this ranges from 1 to 200 and defaults to 50. + +**Example** + +```elixir +alias Soroban.RPC.TransactionsPayload + +server = Soroban.RPC.Server.testnet() + +start_ledger = 600000 +limit = 2 + +transactions_payload = TransactionsPayload.new(start_ledger: start_ledger, limit: limit) + +Soroban.RPC.get_transactions(server, transactions_payload) + +{:ok, + %Soroban.RPC.GetTransactionsResponse{ + transactions: [ + %{ + status: "FAILED", + applicationOrder: 1, + feeBump: false, + envelopeXdr: + "AAAAAgAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAASwAAKkyAAXlMwAAAAEAAAAAAAAAAAAAAABmWeASAAAAAQAAABR3YWxsZXQ6MTcxMjkwNjMzNjUxMAAAAAEAAAABAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAAQAAAABwOSvou8mtwTtCkysVioO35TSgyRir2+WGqO8FShG/GAAAAAFVQUgAAAAAAO371tlrHUfK+AvmQvHje1jSUrvJb3y3wrJ7EplQeqTkAAAAAAX14QAAAAAAAAAAAeuAU2wAAABAn+6A+xXvMasptAm9BEJwf5Y9CLLQtV44TsNqS8ocPmn4n8Rtyb09SBiFoMv8isYgeQU5nAHsIwBNbEKCerusAQ==", + resultXdr: "AAAAAAAAAGT/////AAAAAQAAAAAAAAAB////+gAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAF0YpYBQAAKkyAAXlMgAAAAsAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRGgAAAABmWd/VAAAAAAAAAAEAHNEbAAAAAAAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAABdGKWAUAACpMgAF5TMAAAALAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + }, + %{ + status: "SUCCESS", + applicationOrder: 2, + feeBump: false, + envelopeXdr: + "AAAAAgAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAPQkAAGgQZAAAANgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAABB90WssODNIgi6BHveqzxTRmIpvAFRyVNM+Hm2GVuCcAAAAAAAAAAAq6aHAHZ2sd9aPbRsskrlXMLWIwqs4Sv2Bk+VwuIR+9wAAABdIdugAAAAAAAAAAAIu2qsYAAAAQERzKOqYYiPXNwsiL8ADAG/f45RBssmf3umGzw4qKkLGlObuPdX0buWmTGrhI13SG38F2V8Mp9DI+eDkcCjMSAOGVuCcAAAAQHnm0o/r+Gsl+6oqBgSbqoSY37gflvQB3zZRghuir0N75UVerd0Q50yG5Zfu08i2crhx6uk+5HYTl8/Sa7uZ+Qc=", + resultXdr: "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALgRm6n57AKz92hLeEpt4BrgS+IEeoAVgzT2ge4u2qsYAAAAADwzS2gAGgQZAAAANQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzPVAAAAABmWdZ2AAAAAAAAAAEAHNEbAAAAAAAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAAAAA8M0toABoEGQAAADYAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAwAAAAMAHNEaAAAAAAAAAAAQfdFrLDgzSIIugR73qs8U0ZiKbwBUclTTPh5thlbgnABZJUSd0V2hAAAAawAAAlEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAaBGEAAAAAZkspCwAAAAAAAAABABzRGwAAAAAAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAWSUtVVp1oQAAAGsAAAJRAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAwAAAAAAGgRhAAAAAGZLKQsAAAAAAAAAAAAc0RsAAAAAAAAAACrpocAdnax31o9tGyySuVcwtYjCqzhK/YGT5XC4hH73AAAAF0h26AAAHNEbAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + } + ], + latest_ledger: 1_888_542, + latest_ledger_close_timestamp: 1_717_166_057, + oldest_ledger: 1_871_263, + oldest_ledger_close_timestamp: 1_717_075_350, + cursor: "8111217537191937" + }} + +``` + #### Get Health General node health check. @@ -421,6 +489,88 @@ Soroban.RPC.get_ledger_entries(server, keys) ``` +#### Get Version Info + +Version information about the RPC and Captive core. + +**Parameters** + +- `server`: `Soroban.RPC.Server` struct - The Soroban-RPC server to interact with. + +**Example** + +```elixir +server = Soroban.RPC.Server.testnet() +Soroban.RPC.get_version_info(server) + +{:ok, + %Soroban.RPC.GetVersionInfoResponse{ + version: "21.4.0-dbb390c6bb99024122fccb12c8219af67d50db04", + commit_hash: "dbb390c6bb99024122fccb12c8219af67d50db04", + build_time_stamp: "2024-07-10T14:50:09", + captive_core_version: "stellar-core 21.1.1 (b3aeb14cc798f6d11deb2be913041be916f3b0cc)", + protocol_version: 21 + }} + +``` + +#### Get Fee Stats + +Statistics for charged inclusion fees. The inclusion fee statistics are calculated from the inclusion fees that were paid for the transactions to be included onto the ledger. + +**Parameters** + +- `server`: `Soroban.RPC.Server` struct - The Soroban-RPC server to interact with. + +**Example** + +```elixir +server = Soroban.RPC.Server.testnet() +Soroban.RPC.get_fee_stats(server) + +{:ok, + %Soroban.RPC.GetFeeStatsResponse{ + soroban_inclusion_fee: %{ + max: "100", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + transaction_count: "6", + ledger_count: 50 + }, + inclusion_fee: %{ + max: "200", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "114", + p70: "150", + p80: "150", + p90: "200", + p95: "200", + p99: "200", + transaction_count: "36", + ledger_count: 10 + }, + latest_ledger: 620373 + }} + +``` + #### Get Events Clients can request a filtered list of events emitted by a given ledger range. diff --git a/lib/rpc.ex b/lib/rpc.ex index 4b0484b..3af5cd0 100644 --- a/lib/rpc.ex +++ b/lib/rpc.ex @@ -5,12 +5,15 @@ defmodule Soroban.RPC do alias Soroban.RPC.{ GetEvents, + GetFeeStats, GetHealth, GetLatestLedger, GetLedgerEntries, GetLedgerEntriesResponse, GetNetwork, GetTransaction, + GetTransactions, + GetVersionInfo, SendTransaction, SimulateTransaction } @@ -36,11 +39,14 @@ defmodule Soroban.RPC do as: :request defdelegate get_transaction(server, hash), to: GetTransaction, as: :request + defdelegate get_transactions(server, payload), to: GetTransactions, as: :request defdelegate get_ledger_entries(server, keys), to: GetLedgerEntries, as: :request defdelegate get_events(server, payload), to: GetEvents, as: :request defdelegate get_health(server), to: GetHealth, as: :request defdelegate get_latest_ledger(server), to: GetLatestLedger, as: :request defdelegate get_network(server), to: GetNetwork, as: :request + defdelegate get_fee_stats(server), to: GetFeeStats, as: :request + defdelegate get_version_info(server), to: GetVersionInfo, as: :request defp encode_xdr(%LedgerKey{} = ledger_key) do ledger_key diff --git a/lib/rpc/endpoints/events_payload/events_payload.ex b/lib/rpc/endpoints/events_payload/events_payload.ex index 62a2488..600272a 100644 --- a/lib/rpc/endpoints/events_payload/events_payload.ex +++ b/lib/rpc/endpoints/events_payload/events_payload.ex @@ -2,20 +2,15 @@ defmodule Soroban.RPC.EventsPayload do @moduledoc """ `EventsPayload` struct definition. """ + import Soroban.RPC.Helper alias Soroban.RPC.EventFilter @type args :: Keyword.t() - @type cursor :: binary() | nil - @type cursor_validation :: {:ok, cursor()} - @type limit :: number() | nil - @type limit_validation :: {:ok, limit()} @type error :: {:error, atom()} @type start_ledger :: non_neg_integer() | nil - @type start_ledger_validation :: {:ok, start_ledger()} | error() @type filters :: list(EventFilter.t()) | nil @type filters_validation :: {:ok, filters()} | error() @type pagination :: map() | nil - @type pagination_validation :: {:ok, pagination()} | error() @type request_args :: map() | :error @type t :: %__MODULE__{ start_ledger: start_ledger(), @@ -66,23 +61,6 @@ defmodule Soroban.RPC.EventsPayload do def to_request_args(_struct), do: :error - @spec validate_start_ledger(start_ledger :: start_ledger()) :: start_ledger_validation() - defp validate_start_ledger(start_ledger) when is_number(start_ledger) and start_ledger >= 0, - do: {:ok, start_ledger} - - defp validate_start_ledger(nil), do: {:ok, nil} - defp validate_start_ledger(_start_ledger), do: {:error, :invalid_start_ledger} - - @spec validate_cursor(cursor :: cursor()) :: cursor_validation() - defp validate_cursor(cursor) when is_binary(cursor), do: {:ok, cursor} - defp validate_cursor(nil), do: {:ok, nil} - defp validate_cursor(_cursor), do: {:error, :invalid_cursor} - - @spec validate_limit(limit :: limit()) :: limit_validation() - defp validate_limit(limit) when is_number(limit), do: {:ok, limit} - defp validate_limit(nil), do: {:ok, nil} - defp validate_limit(_limit), do: {:error, :invalid_limit} - @spec validate_filters(filters :: filters()) :: filters_validation() defp validate_filters([%EventFilter{} = filter | _] = filters) when length(filters) in 1..5 do if Enum.any?(filters, fn f -> f.__struct__ != filter.__struct__ end), diff --git a/lib/rpc/endpoints/get_events.ex b/lib/rpc/endpoints/get_events.ex index 39f505e..a6c9613 100644 --- a/lib/rpc/endpoints/get_events.ex +++ b/lib/rpc/endpoints/get_events.ex @@ -4,8 +4,7 @@ defmodule Soroban.RPC.GetEvents do """ @behaviour Soroban.RPC.Endpoint.Spec - alias Soroban.RPC.EventsPayload - alias Soroban.RPC.{GetEventsResponse, Request} + alias Soroban.RPC.{EventsPayload, GetEventsResponse, Request} @endpoint "getEvents" diff --git a/lib/rpc/endpoints/get_fee_stats.ex b/lib/rpc/endpoints/get_fee_stats.ex new file mode 100644 index 0000000..b5546e6 --- /dev/null +++ b/lib/rpc/endpoints/get_fee_stats.ex @@ -0,0 +1,19 @@ +defmodule Soroban.RPC.GetFeeStats do + @moduledoc """ + GetFeeStats request implementation for Soroban RPC. + """ + @behaviour Soroban.RPC.Endpoint.Spec + + alias Soroban.RPC.{GetFeeStatsResponse, Request} + + @endpoint "getFeeStats" + + @impl true + def request(server, _params \\ nil) do + server + |> Request.new(@endpoint) + |> Request.add_headers([{"Content-Type", "application/json"}]) + |> Request.perform() + |> Request.results(as: GetFeeStatsResponse) + end +end diff --git a/lib/rpc/endpoints/get_transactions.ex b/lib/rpc/endpoints/get_transactions.ex new file mode 100644 index 0000000..688c5b4 --- /dev/null +++ b/lib/rpc/endpoints/get_transactions.ex @@ -0,0 +1,22 @@ +defmodule Soroban.RPC.GetTransactions do + @moduledoc """ + GetTransactions request implementation for Soroban RPC. + """ + @behaviour Soroban.RPC.Endpoint.Spec + + alias Soroban.RPC.{GetTransactionsResponse, Request, TransactionsPayload} + + @endpoint "getTransactions" + + @impl true + def request(server, %TransactionsPayload{} = transactions_payload) do + payload = TransactionsPayload.to_request_args(transactions_payload) + + server + |> Request.new(@endpoint) + |> Request.add_headers([{"Content-Type", "application/json"}]) + |> Request.add_params(payload) + |> Request.perform() + |> Request.results(as: GetTransactionsResponse) + end +end diff --git a/lib/rpc/endpoints/get_version_info.ex b/lib/rpc/endpoints/get_version_info.ex new file mode 100644 index 0000000..3a34c9d --- /dev/null +++ b/lib/rpc/endpoints/get_version_info.ex @@ -0,0 +1,19 @@ +defmodule Soroban.RPC.GetVersionInfo do + @moduledoc """ + GetVersionInfo request implementation for Soroban RPC. + """ + @behaviour Soroban.RPC.Endpoint.Spec + + alias Soroban.RPC.{GetVersionInfoResponse, Request} + + @endpoint "getVersionInfo" + + @impl true + def request(server, _params \\ nil) do + server + |> Request.new(@endpoint) + |> Request.add_headers([{"Content-Type", "application/json"}]) + |> Request.perform() + |> Request.results(as: GetVersionInfoResponse) + end +end diff --git a/lib/rpc/endpoints/helper.ex b/lib/rpc/endpoints/helper.ex new file mode 100644 index 0000000..336b306 --- /dev/null +++ b/lib/rpc/endpoints/helper.ex @@ -0,0 +1,30 @@ +defmodule Soroban.RPC.Helper do + @moduledoc """ + Helper functions for RPC endpoints. + """ + + @type error :: {:error, atom()} + @type start_ledger :: non_neg_integer() | nil + @type start_ledger_validation :: {:ok, start_ledger()} | error() + @type cursor :: binary() | nil + @type cursor_validation :: {:ok, cursor()} + @type limit :: number() | nil + @type limit_validation :: {:ok, limit()} + + @spec validate_start_ledger(start_ledger :: start_ledger()) :: start_ledger_validation() + def validate_start_ledger(start_ledger) when is_number(start_ledger) and start_ledger >= 0, + do: {:ok, start_ledger} + + def validate_start_ledger(nil), do: {:ok, nil} + def validate_start_ledger(_start_ledger), do: {:error, :invalid_start_ledger} + + @spec validate_cursor(cursor :: cursor()) :: cursor_validation() + def validate_cursor(cursor) when is_binary(cursor), do: {:ok, cursor} + def validate_cursor(nil), do: {:ok, nil} + def validate_cursor(_cursor), do: {:error, :invalid_cursor} + + @spec validate_limit(limit :: limit()) :: limit_validation() + def validate_limit(limit) when is_number(limit), do: {:ok, limit} + def validate_limit(nil), do: {:ok, nil} + def validate_limit(_limit), do: {:error, :invalid_limit} +end diff --git a/lib/rpc/endpoints/spec.ex b/lib/rpc/endpoints/spec.ex index 04fb390..685f0ad 100644 --- a/lib/rpc/endpoints/spec.ex +++ b/lib/rpc/endpoints/spec.ex @@ -3,10 +3,10 @@ defmodule Soroban.RPC.Endpoint.Spec do Specifies the callbacks to build the Soroban's endpoints. """ - alias Soroban.RPC.{Error, EventsPayload, HTTPError, Server} + alias Soroban.RPC.{Error, EventsPayload, HTTPError, Server, TransactionsPayload} @type response :: {:ok, struct()} | {:error, Error.t() | HTTPError.t()} - @type params :: String.t() | EventsPayload.t() | keyword() | nil + @type params :: String.t() | EventsPayload.t() | keyword() | nil | TransactionsPayload.t() @callback request(server :: Server.t(), params :: params()) :: response() end diff --git a/lib/rpc/endpoints/transactions_payload/transactions_payload.ex b/lib/rpc/endpoints/transactions_payload/transactions_payload.ex new file mode 100644 index 0000000..9a1b6cb --- /dev/null +++ b/lib/rpc/endpoints/transactions_payload/transactions_payload.ex @@ -0,0 +1,45 @@ +defmodule Soroban.RPC.TransactionsPayload do + @moduledoc """ + `TransactionsPayload` struct definition. + """ + import Soroban.RPC.Helper + + @type args :: Keyword.t() + @type start_ledger :: non_neg_integer() | nil + @type pagination :: map() | nil + @type request_args :: map() | :error + @type t :: %__MODULE__{ + start_ledger: start_ledger(), + pagination: pagination() + } + + defstruct [:start_ledger, :pagination] + + @spec new(args :: args()) :: t() + def new(args) when is_list(args) do + start_ledger = Keyword.get(args, :start_ledger) + cursor = Keyword.get(args, :cursor) + limit = Keyword.get(args, :limit) + + with {:ok, start_ledger} <- validate_start_ledger(start_ledger), + {:ok, cursor} <- validate_cursor(cursor), + {:ok, limit} <- validate_limit(limit) do + %__MODULE__{ + start_ledger: start_ledger, + pagination: %{cursor: cursor, limit: limit} + } + end + end + + def new(_args), do: {:error, :invalid_args} + + @spec to_request_args(t()) :: request_args() + def to_request_args(%__MODULE__{ + start_ledger: start_ledger, + pagination: pagination + }) do + %{startLedger: start_ledger, filters: nil, pagination: pagination} + end + + def to_request_args(_struct), do: :error +end diff --git a/lib/rpc/responses/get_fee_stats_response.ex b/lib/rpc/responses/get_fee_stats_response.ex new file mode 100644 index 0000000..0a811e3 --- /dev/null +++ b/lib/rpc/responses/get_fee_stats_response.ex @@ -0,0 +1,16 @@ +defmodule Soroban.RPC.GetFeeStatsResponse do + @moduledoc """ + `GetFeeStatsResponse` struct definition. + """ + + @behaviour Soroban.RPC.Response.Spec + + @type soroban_inclusion_fee :: map() + @type inclusion_fee :: map() + @type latest_ledger :: non_neg_integer() + + defstruct [:soroban_inclusion_fee, :inclusion_fee, :latest_ledger] + + @impl true + def new(attrs), do: struct(%__MODULE__{}, attrs) +end diff --git a/lib/rpc/responses/get_transactions_response.ex b/lib/rpc/responses/get_transactions_response.ex new file mode 100644 index 0000000..2dd24e4 --- /dev/null +++ b/lib/rpc/responses/get_transactions_response.ex @@ -0,0 +1,33 @@ +defmodule Soroban.RPC.GetTransactionsResponse do + @moduledoc """ + `GetTransactionsResponse` struct definition. + """ + @behaviour Soroban.RPC.Response.Spec + + @type transactions :: list(map()) + @type latest_ledger :: non_neg_integer() + @type latest_ledger_close_timestamp :: String.t() + @type oldest_ledger :: non_neg_integer() + @type oldest_ledger_close_timestamp :: String.t() + @type cursor :: String.t() + @type t :: %__MODULE__{ + latest_ledger: latest_ledger(), + latest_ledger_close_timestamp: latest_ledger_close_timestamp(), + oldest_ledger: oldest_ledger(), + oldest_ledger_close_timestamp: oldest_ledger_close_timestamp(), + cursor: cursor(), + transactions: transactions() + } + + defstruct [ + :transactions, + :latest_ledger, + :latest_ledger_close_timestamp, + :oldest_ledger, + :oldest_ledger_close_timestamp, + :cursor + ] + + @impl true + def new(attrs), do: struct(%__MODULE__{}, attrs) +end diff --git a/lib/rpc/responses/get_version_info_response.ex b/lib/rpc/responses/get_version_info_response.ex new file mode 100644 index 0000000..38cbf07 --- /dev/null +++ b/lib/rpc/responses/get_version_info_response.ex @@ -0,0 +1,24 @@ +defmodule Soroban.RPC.GetVersionInfoResponse do + @moduledoc """ + `GetVersionInfoResponse` struct definition. + """ + @behaviour Soroban.RPC.Response.Spec + + @type version :: String.t() + @type commit_hash :: String.t() + @type build_time_stamp :: String.t() + @type captive_core_version :: String.t() + @type protocol_version :: non_neg_integer() + @type t :: %__MODULE__{ + version: version(), + commit_hash: commit_hash(), + build_time_stamp: build_time_stamp(), + captive_core_version: captive_core_version(), + protocol_version: protocol_version() + } + + defstruct [:version, :commit_hash, :build_time_stamp, :captive_core_version, :protocol_version] + + @impl true + def new(attrs), do: struct(%__MODULE__{}, attrs) +end diff --git a/lib/rpc/responses/simulate_transaction_response.ex b/lib/rpc/responses/simulate_transaction_response.ex index 407fef3..688676d 100644 --- a/lib/rpc/responses/simulate_transaction_response.ex +++ b/lib/rpc/responses/simulate_transaction_response.ex @@ -12,6 +12,7 @@ defmodule Soroban.RPC.SimulateTransactionResponse do @type restore_preamble :: map() | nil @type latest_ledger :: non_neg_integer() @type error :: String.t() | nil + @type state_changes :: list(map()) | nil @type t :: %__MODULE__{ min_resource_fee: min_resource_fee(), @@ -21,6 +22,7 @@ defmodule Soroban.RPC.SimulateTransactionResponse do events: events(), restore_preamble: restore_preamble(), latest_ledger: latest_ledger(), + state_changes: state_changes(), error: error() } @@ -32,6 +34,7 @@ defmodule Soroban.RPC.SimulateTransactionResponse do :events, :restore_preamble, :latest_ledger, + :state_changes, :error ] diff --git a/mix.exs b/mix.exs index 9fcc4e8..e26618d 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Soroban.MixProject do use Mix.Project - @version "0.20.1" + @version "0.21.0" @github_url "https://github.com/kommitters/soroban.ex" def project do @@ -44,7 +44,7 @@ defmodule Soroban.MixProject do {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false}, {:ex_doc, "~> 0.31", only: :dev, runtime: false}, {:excoveralls, "~> 0.18", only: :test}, - {:stellar_sdk, "~> 0.21"}, + {:stellar_sdk, "~> 0.21.2"}, {:hackney, "~> 1.18"} ] end diff --git a/mix.lock b/mix.lock index 33fa288..58410ce 100644 --- a/mix.lock +++ b/mix.lock @@ -1,30 +1,30 @@ %{ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, - "castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"}, + "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, "credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"}, "dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ed25519": {:hex, :ed25519, "1.4.1", "479fb83c3e31987c9cad780e6aeb8f2015fb5a482618cdf2a825c9aff809afc4", [:mix], [], "hexpm", "0dacb84f3faa3d8148e81019ca35f9d8dcee13232c32c9db5c2fb8ff48c80ec7"}, - "elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"}, - "elixir_xdr": {:hex, :elixir_xdr, "0.3.10", "6443c48f29494b67bf48ef04d2abef2fccfb08d484c688bc3cbd0db390719b24", [:mix], [], "hexpm", "4041f284f802750c9c0b8c0fa5c69cd24911f73668a1d8eb42cbc4c8b8aa5b50"}, + "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, + "elixir_xdr": {:hex, :elixir_xdr, "0.3.11", "baa3c21753232ee8547c5e96502cecd80b63c0f06694e51c43f3ed5b8d5b5b5a", [:mix], [], "hexpm", "d24de398aa75ed8c5fb6f2121a322e6e91f5bce72d791be47dd4197a11e79b51"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, "excoveralls": {:hex, :excoveralls, "0.18.0", "b92497e69465dc51bc37a6422226ee690ab437e4c06877e836f1c18daeb35da9", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1109bb911f3cb583401760be49c02cbbd16aed66ea9509fc5479335d284da60b"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "stellar_base": {:hex, :stellar_base, "0.15.0", "893b7ee194aa0c732fbc16be431418d8269dc0221b70ecdb6320705540ba395f", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:crc, "~> 0.10.0", [hex: :crc, repo: "hexpm", optional: false]}, {:elixir_xdr, "~> 0.3", [hex: :elixir_xdr, repo: "hexpm", optional: false]}], "hexpm", "411aba27c89c03ac237457c171740428aef6f958aeebb4811723436e9105dc60"}, - "stellar_sdk": {:hex, :stellar_sdk, "0.21.0", "e5ec2d39f67f8c1f77f3021bb977537913acbb8879d15323366a5f16b7455426", [:mix], [{:ed25519, "~> 1.3", [hex: :ed25519, repo: "hexpm", optional: false]}, {:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:stellar_base, "~> 0.15", [hex: :stellar_base, repo: "hexpm", optional: false]}], "hexpm", "3b3ab5fda4810131353472d23b22dc427c461c0c89df8514cbcb440bf3d6a0dd"}, + "stellar_base": {:hex, :stellar_base, "0.16.0", "f94beff40b7fc3f7f56a06b2b2d92ae87bbc55baa1480e705490a688586d2a44", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:crc, "~> 0.10.0", [hex: :crc, repo: "hexpm", optional: false]}, {:elixir_xdr, "~> 0.3", [hex: :elixir_xdr, repo: "hexpm", optional: false]}], "hexpm", "d8a9a2dd0cb7b832fec3489d1442c6525a710eeb1a8222cb5371e4ace4ccc400"}, + "stellar_sdk": {:hex, :stellar_sdk, "0.21.2", "b82476a62406ff7c200f5e0c639cb8d98b51a9416c162a17f99f590a66d95ed4", [:mix], [{:ed25519, "~> 1.3", [hex: :ed25519, repo: "hexpm", optional: false]}, {:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:stellar_base, "~> 0.16", [hex: :stellar_base, repo: "hexpm", optional: false]}], "hexpm", "e4212452b162540894b9a2a428c477a6a6f1ef0b5f8e3c22fcbd5543979f50ff"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, } diff --git a/test/rpc/endpoints/get_fee_stats_test.exs b/test/rpc/endpoints/get_fee_stats_test.exs new file mode 100644 index 0000000..605ae06 --- /dev/null +++ b/test/rpc/endpoints/get_fee_stats_test.exs @@ -0,0 +1,114 @@ +defmodule Soroban.RPC.CannedGetFeeStatsClientImpl do + @moduledoc false + @behaviour Soroban.RPC.Client.Spec + + @impl true + def request(_endpoint, _url, _header, _body, _opts) do + send(self(), {:request, "RESPONSE"}) + + {:ok, + %{ + soroban_inclusion_fee: %{ + max: "100", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + transaction_count: "7", + ledger_count: 50 + }, + inclusion_fee: %{ + max: "200", + min: "100", + mode: "200", + p10: "100", + p20: "100", + p30: "100", + p40: "150", + p50: "200", + p60: "200", + p70: "200", + p80: "200", + p90: "200", + p95: "200", + p99: "200", + transaction_count: "27", + ledger_count: 10 + }, + latest_ledger: 619_731 + }} + end +end + +defmodule Soroban.RPC.GetFeeStatsTest do + use ExUnit.Case + + alias Soroban.RPC.{ + CannedGetFeeStatsClientImpl, + GetFeeStats, + GetFeeStatsResponse, + Server + } + + setup do + Application.put_env(:soroban, :http_client_impl, CannedGetFeeStatsClientImpl) + + on_exit(fn -> + Application.delete_env(:soroban, :http_client_impl) + end) + + %{server: Server.testnet()} + end + + test "request/1", %{server: server} do + {:ok, + %GetFeeStatsResponse{ + soroban_inclusion_fee: %{ + max: "100", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + transaction_count: "7", + ledger_count: 50 + }, + inclusion_fee: %{ + max: "200", + min: "100", + mode: "200", + p10: "100", + p20: "100", + p30: "100", + p40: "150", + p50: "200", + p60: "200", + p70: "200", + p80: "200", + p90: "200", + p95: "200", + p99: "200", + transaction_count: "27", + ledger_count: 10 + }, + latest_ledger: 619_731 + }} = GetFeeStats.request(server) + end +end diff --git a/test/rpc/endpoints/get_transactions_test.exs b/test/rpc/endpoints/get_transactions_test.exs new file mode 100644 index 0000000..0215b03 --- /dev/null +++ b/test/rpc/endpoints/get_transactions_test.exs @@ -0,0 +1,84 @@ +defmodule Soroban.RPC.GetTransactionsCannedClientImpl do + @moduledoc false + + @behaviour Soroban.RPC.Client.Spec + + @impl true + def request(_endpoint, _url, _headers, _body, _opts) do + send(self(), {:request, "RESPONSE"}) + + {:ok, + %{ + transactions: [ + %{ + status: "FAILED", + applicationOrder: 1, + feeBump: false, + envelopeXdr: + "AAAAAgAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAASwAAKkyAAXlMwAAAAEAAAAAAAAAAAAAAABmWeASAAAAAQAAABR3YWxsZXQ6MTcxMjkwNjMzNjUxMAAAAAEAAAABAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAAQAAAABwOSvou8mtwTtCkysVioO35TSgyRir2+WGqO8FShG/GAAAAAFVQUgAAAAAAO371tlrHUfK+AvmQvHje1jSUrvJb3y3wrJ7EplQeqTkAAAAAAX14QAAAAAAAAAAAeuAU2wAAABAn+6A+xXvMasptAm9BEJwf5Y9CLLQtV44TsNqS8ocPmn4n8Rtyb09SBiFoMv8isYgeQU5nAHsIwBNbEKCerusAQ==", + resultXdr: "AAAAAAAAAGT/////AAAAAQAAAAAAAAAB////+gAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAF0YpYBQAAKkyAAXlMgAAAAsAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRGgAAAABmWd/VAAAAAAAAAAEAHNEbAAAAAAAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAABdGKWAUAACpMgAF5TMAAAALAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + }, + %{ + status: "SUCCESS", + applicationOrder: 2, + feeBump: false, + envelopeXdr: + "AAAAAgAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAPQkAAGgQZAAAANgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAABB90WssODNIgi6BHveqzxTRmIpvAFRyVNM+Hm2GVuCcAAAAAAAAAAAq6aHAHZ2sd9aPbRsskrlXMLWIwqs4Sv2Bk+VwuIR+9wAAABdIdugAAAAAAAAAAAIu2qsYAAAAQERzKOqYYiPXNwsiL8ADAG/f45RBssmf3umGzw4qKkLGlObuPdX0buWmTGrhI13SG38F2V8Mp9DI+eDkcCjMSAOGVuCcAAAAQHnm0o/r+Gsl+6oqBgSbqoSY37gflvQB3zZRghuir0N75UVerd0Q50yG5Zfu08i2crhx6uk+5HYTl8/Sa7uZ+Qc=", + resultXdr: "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALgRm6n57AKz92hLeEpt4BrgS+IEeoAVgzT2ge4u2qsYAAAAADwzS2gAGgQZAAAANQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzPVAAAAABmWdZ2AAAAAAAAAAEAHNEbAAAAAAAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAAAAA8M0toABoEGQAAADYAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAwAAAAMAHNEaAAAAAAAAAAAQfdFrLDgzSIIugR73qs8U0ZiKbwBUclTTPh5thlbgnABZJUSd0V2hAAAAawAAAlEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAaBGEAAAAAZkspCwAAAAAAAAABABzRGwAAAAAAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAWSUtVVp1oQAAAGsAAAJRAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAwAAAAAAGgRhAAAAAGZLKQsAAAAAAAAAAAAc0RsAAAAAAAAAACrpocAdnax31o9tGyySuVcwtYjCqzhK/YGT5XC4hH73AAAAF0h26AAAHNEbAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + } + ], + latest_ledger: 1_888_542, + latest_ledger_close_timestamp: 1_717_166_057, + oldest_ledger: 1_871_263, + oldest_ledger_close_timestamp: 1_717_075_350, + cursor: "8111217537191937" + }} + end +end + +defmodule Soroban.RPC.GetTransactionsTest do + use ExUnit.Case + + alias Soroban.RPC.{ + GetTransactions, + GetTransactionsCannedClientImpl, + GetTransactionsResponse, + Server, + TransactionsPayload + } + + setup do + Application.put_env(:soroban, :http_client_impl, GetTransactionsCannedClientImpl) + + on_exit(fn -> + Application.delete_env(:soroban, :http_client_impl) + end) + + start_ledger = 1000 + limit = 2 + + transaction_payload = TransactionsPayload.new(start_ledger: start_ledger, limit: limit) + + %{payload: transaction_payload, server: Server.testnet()} + end + + test "request/2", %{payload: payload, server: server} do + {:ok, + %GetTransactionsResponse{ + transactions: _transactions, + latest_ledger: 1_888_542, + latest_ledger_close_timestamp: 1_717_166_057, + oldest_ledger: 1_871_263, + oldest_ledger_close_timestamp: 1_717_075_350, + cursor: "8111217537191937" + }} = GetTransactions.request(server, payload) + end +end diff --git a/test/rpc/endpoints/get_version_info_test.exs b/test/rpc/endpoints/get_version_info_test.exs new file mode 100644 index 0000000..aab1f67 --- /dev/null +++ b/test/rpc/endpoints/get_version_info_test.exs @@ -0,0 +1,50 @@ +defmodule Soroban.RPC.CannedGetVersionInfoClientImpl do + @moduledoc false + @behaviour Soroban.RPC.Client.Spec + + @impl true + def request(_endpoint, _url, _headers, _body, _opts) do + send(self(), {:request, "RESPONSE"}) + + {:ok, + %{ + version: "21.1.0", + commit_hash: "fcd2f0523f04279bae4502f3e3fa00ca627e6f6a", + build_time_stamp: "2024-05-10T11:18:38", + captive_core_version: "stellar-core 21.0.0.rc2 (c6f474133738ae5f6d11b07963ca841909210273)", + protocol_version: 21 + }} + end +end + +defmodule Soroban.RPC.GetVersionInfoTest do + use ExUnit.Case + + alias Soroban.RPC.{ + CannedGetVersionInfoClientImpl, + GetVersionInfo, + GetVersionInfoResponse, + Server + } + + setup do + Application.put_env(:soroban, :http_client_impl, CannedGetVersionInfoClientImpl) + + on_exit(fn -> + Application.delete_env(:soroban, :http_client_impl) + end) + + %{server: Server.testnet()} + end + + test "request/1", %{server: server} do + {:ok, + %GetVersionInfoResponse{ + version: "21.1.0", + commit_hash: "fcd2f0523f04279bae4502f3e3fa00ca627e6f6a", + build_time_stamp: "2024-05-10T11:18:38", + captive_core_version: "stellar-core 21.0.0.rc2 (c6f474133738ae5f6d11b07963ca841909210273)", + protocol_version: 21 + }} = GetVersionInfo.request(server) + end +end diff --git a/test/rpc/endpoints/transactions_payload/transactions_payload_test.exs b/test/rpc/endpoints/transactions_payload/transactions_payload_test.exs new file mode 100644 index 0000000..1c1c6e2 --- /dev/null +++ b/test/rpc/endpoints/transactions_payload/transactions_payload_test.exs @@ -0,0 +1,112 @@ +defmodule Soroban.RPC.TransactionsPayloadTest do + use ExUnit.Case + alias Soroban.RPC.TransactionsPayload + + setup do + start_ledger = 674_736 + cursor = "1234-1" + limit = 2 + + payload = + TransactionsPayload.new( + start_ledger: start_ledger, + cursor: cursor, + limit: limit + ) + + %{ + start_ledger: start_ledger, + cursor: cursor, + limit: limit, + payload: payload + } + end + + describe "new/1" do + test "with valid values", %{ + start_ledger: start_ledger, + cursor: cursor, + limit: limit + } do + %TransactionsPayload{ + start_ledger: ^start_ledger, + pagination: %{cursor: ^cursor, limit: ^limit} + } = + TransactionsPayload.new( + start_ledger: start_ledger, + cursor: cursor, + limit: limit + ) + end + + test "with invalid struct" do + assert TransactionsPayload.new(nil) == {:error, :invalid_args} + end + + test "with nil start_ledger", %{ + cursor: cursor, + limit: limit + } do + %TransactionsPayload{ + start_ledger: nil, + pagination: %{cursor: ^cursor, limit: ^limit} + } = + TransactionsPayload.new( + start_ledger: nil, + cursor: cursor, + limit: limit + ) + end + + test "with nil cursor", %{ + start_ledger: start_ledger, + limit: limit + } do + %TransactionsPayload{ + start_ledger: ^start_ledger, + pagination: %{cursor: nil, limit: ^limit} + } = + TransactionsPayload.new( + start_ledger: start_ledger, + cursor: nil, + limit: limit + ) + end + + test "with nil limit", %{ + start_ledger: start_ledger, + cursor: cursor + } do + %TransactionsPayload{ + start_ledger: ^start_ledger, + pagination: %{cursor: ^cursor, limit: nil} + } = + TransactionsPayload.new( + start_ledger: start_ledger, + cursor: cursor, + limit: nil + ) + end + end + + describe "to_request_args/1" do + test "with valid struct", %{ + start_ledger: start_ledger, + cursor: cursor, + limit: limit, + payload: payload + } do + expected = %{ + startLedger: start_ledger, + filters: nil, + pagination: %{cursor: cursor, limit: limit} + } + + assert TransactionsPayload.to_request_args(payload) == expected + end + + test "with an invalid struct" do + assert TransactionsPayload.to_request_args(nil) == :error + end + end +end diff --git a/test/rpc/responses/get_fee_stats_response_test.exs b/test/rpc/responses/get_fee_stats_response_test.exs new file mode 100644 index 0000000..8b82f50 --- /dev/null +++ b/test/rpc/responses/get_fee_stats_response_test.exs @@ -0,0 +1,94 @@ +defmodule Soroban.RPC.GetFeeStatsResponseTest do + use ExUnit.Case + alias Soroban.RPC.GetFeeStatsResponse + + setup do + %{ + result: %{ + soroban_inclusion_fee: %{ + max: "100", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + transaction_count: "7", + ledger_count: 50 + }, + inclusion_fee: %{ + max: "200", + min: "100", + mode: "200", + p10: "100", + p20: "100", + p30: "100", + p40: "150", + p50: "200", + p60: "200", + p70: "200", + p80: "200", + p90: "200", + p95: "200", + p99: "200", + transaction_count: "27", + ledger_count: 10 + }, + latest_ledger: 619_731 + } + } + end + + describe "new/1" do + test "when successful transaction", %{ + result: result + } do + %GetFeeStatsResponse{ + soroban_inclusion_fee: %{ + max: "100", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + transaction_count: "7", + ledger_count: 50 + }, + inclusion_fee: %{ + max: "200", + min: "100", + mode: "200", + p10: "100", + p20: "100", + p30: "100", + p40: "150", + p50: "200", + p60: "200", + p70: "200", + p80: "200", + p90: "200", + p95: "200", + p99: "200", + transaction_count: "27", + ledger_count: 10 + }, + latest_ledger: 619_731 + } = GetFeeStatsResponse.new(result) + end + end +end diff --git a/test/rpc/responses/get_transactions_response_test.exs b/test/rpc/responses/get_transactions_response_test.exs new file mode 100644 index 0000000..21f957e --- /dev/null +++ b/test/rpc/responses/get_transactions_response_test.exs @@ -0,0 +1,66 @@ +defmodule Soroban.RPC.GetTransactionsResponseTest do + use ExUnit.Case + + alias Soroban.RPC.GetTransactionsResponse + + setup do + result = %{ + transactions: [ + %{ + status: "FAILED", + applicationOrder: 1, + feeBump: false, + envelopeXdr: + "AAAAAgAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAASwAAKkyAAXlMwAAAAEAAAAAAAAAAAAAAABmWeASAAAAAQAAABR3YWxsZXQ6MTcxMjkwNjMzNjUxMAAAAAEAAAABAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAAQAAAABwOSvou8mtwTtCkysVioO35TSgyRir2+WGqO8FShG/GAAAAAFVQUgAAAAAAO371tlrHUfK+AvmQvHje1jSUrvJb3y3wrJ7EplQeqTkAAAAAAX14QAAAAAAAAAAAeuAU2wAAABAn+6A+xXvMasptAm9BEJwf5Y9CLLQtV44TsNqS8ocPmn4n8Rtyb09SBiFoMv8isYgeQU5nAHsIwBNbEKCerusAQ==", + resultXdr: "AAAAAAAAAGT/////AAAAAQAAAAAAAAAB////+gAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAF0YpYBQAAKkyAAXlMgAAAAsAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRGgAAAABmWd/VAAAAAAAAAAEAHNEbAAAAAAAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAABdGKWAUAACpMgAF5TMAAAALAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + }, + %{ + status: "SUCCESS", + applicationOrder: 2, + feeBump: false, + envelopeXdr: + "AAAAAgAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAPQkAAGgQZAAAANgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAABB90WssODNIgi6BHveqzxTRmIpvAFRyVNM+Hm2GVuCcAAAAAAAAAAAq6aHAHZ2sd9aPbRsskrlXMLWIwqs4Sv2Bk+VwuIR+9wAAABdIdugAAAAAAAAAAAIu2qsYAAAAQERzKOqYYiPXNwsiL8ADAG/f45RBssmf3umGzw4qKkLGlObuPdX0buWmTGrhI13SG38F2V8Mp9DI+eDkcCjMSAOGVuCcAAAAQHnm0o/r+Gsl+6oqBgSbqoSY37gflvQB3zZRghuir0N75UVerd0Q50yG5Zfu08i2crhx6uk+5HYTl8/Sa7uZ+Qc=", + resultXdr: "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALgRm6n57AKz92hLeEpt4BrgS+IEeoAVgzT2ge4u2qsYAAAAADwzS2gAGgQZAAAANQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzPVAAAAABmWdZ2AAAAAAAAAAEAHNEbAAAAAAAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAAAAA8M0toABoEGQAAADYAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAwAAAAMAHNEaAAAAAAAAAAAQfdFrLDgzSIIugR73qs8U0ZiKbwBUclTTPh5thlbgnABZJUSd0V2hAAAAawAAAlEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAaBGEAAAAAZkspCwAAAAAAAAABABzRGwAAAAAAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAWSUtVVp1oQAAAGsAAAJRAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAwAAAAAAGgRhAAAAAGZLKQsAAAAAAAAAAAAc0RsAAAAAAAAAACrpocAdnax31o9tGyySuVcwtYjCqzhK/YGT5XC4hH73AAAAF0h26AAAHNEbAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + } + ], + latest_ledger: 1_888_542, + latest_ledger_close_timestamp: 1_717_166_057, + oldest_ledger: 1_871_263, + oldest_ledger_close_timestamp: 1_717_075_350, + cursor: "8111217537191937" + } + + %{result: result} + end + + describe "new/1" do + test "result transaction", %{ + result: + %{ + transactions: transactions, + latest_ledger: latest_ledger, + latest_ledger_close_timestamp: latest_ledger_close_timestamp, + oldest_ledger: oldest_ledger, + oldest_ledger_close_timestamp: oldest_ledger_close_timestamp, + cursor: cursor + } = result + } do + assert %GetTransactionsResponse{ + transactions: ^transactions, + latest_ledger: ^latest_ledger, + latest_ledger_close_timestamp: ^latest_ledger_close_timestamp, + oldest_ledger: ^oldest_ledger, + oldest_ledger_close_timestamp: ^oldest_ledger_close_timestamp, + cursor: ^cursor + } = GetTransactionsResponse.new(result) + end + end +end diff --git a/test/rpc/responses/get_versions_info_test.exs b/test/rpc/responses/get_versions_info_test.exs new file mode 100644 index 0000000..8f94f93 --- /dev/null +++ b/test/rpc/responses/get_versions_info_test.exs @@ -0,0 +1,39 @@ +defmodule Soroban.RPC.GetVetsionInfoResponseTest do + use ExUnit.Case + + alias Soroban.RPC.GetVersionInfoResponse + + setup do + %{ + result: %{ + version: "21.1.0", + commit_hash: "fcd2f0523f04279bae4502f3e3fa00ca627e6f6a", + build_time_stamp: "2024-05-10T11:18:38", + captive_core_version: + "stellar-core 21.0.0.rc2 (c6f474133738ae5f6d11b07963ca841909210273)", + protocol_version: 21 + } + } + end + + describe "new/1" do + test "when successful transaction", %{ + result: + %{ + version: version, + commit_hash: commit_hash, + build_time_stamp: build_time_stamp, + captive_core_version: captive_core_version, + protocol_version: protocol_version + } = result + } do + %GetVersionInfoResponse{ + version: ^version, + commit_hash: ^commit_hash, + build_time_stamp: ^build_time_stamp, + captive_core_version: ^captive_core_version, + protocol_version: ^protocol_version + } = GetVersionInfoResponse.new(result) + end + end +end diff --git a/test/rpc_test.exs b/test/rpc_test.exs index a5456ae..ab9e16f 100644 --- a/test/rpc_test.exs +++ b/test/rpc_test.exs @@ -226,6 +226,122 @@ defmodule Soroban.RPC.CannedRPCGetTransactionClientImpl do end end +defmodule Soroban.RPC.CannedRPCGetVersionInfoClientImpl do + @moduledoc false + @behaviour Soroban.RPC.Client.Spec + + @impl true + def request(_endpoint, _url, _headers, _body, _opts) do + send(self(), {:request, "RESPONSE"}) + + {:ok, + %{ + version: "21.1.0", + commit_hash: "fcd2f0523f04279bae4502f3e3fa00ca627e6f6a", + build_time_stamp: "2024-05-10T11:18:38", + captive_core_version: "stellar-core 21.0.0.rc2 (c6f474133738ae5f6d11b07963ca841909210273)", + protocol_version: 21 + }} + end +end + +defmodule Soroban.RPC.CannedRPCGetTransactionsClientImpl do + @moduledoc false + + @behaviour Soroban.RPC.Client.Spec + + @impl true + def request(_endpoint, _url, _headers, _body, _opts) do + send(self(), {:request, "RESPONSE"}) + + {:ok, + %{ + transactions: [ + %{ + status: "FAILED", + applicationOrder: 1, + feeBump: false, + envelopeXdr: + "AAAAAgAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAASwAAKkyAAXlMwAAAAEAAAAAAAAAAAAAAABmWeASAAAAAQAAABR3YWxsZXQ6MTcxMjkwNjMzNjUxMAAAAAEAAAABAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAAQAAAABwOSvou8mtwTtCkysVioO35TSgyRir2+WGqO8FShG/GAAAAAFVQUgAAAAAAO371tlrHUfK+AvmQvHje1jSUrvJb3y3wrJ7EplQeqTkAAAAAAX14QAAAAAAAAAAAeuAU2wAAABAn+6A+xXvMasptAm9BEJwf5Y9CLLQtV44TsNqS8ocPmn4n8Rtyb09SBiFoMv8isYgeQU5nAHsIwBNbEKCerusAQ==", + resultXdr: "AAAAAAAAAGT/////AAAAAQAAAAAAAAAB////+gAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAF0YpYBQAAKkyAAXlMgAAAAsAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRGgAAAABmWd/VAAAAAAAAAAEAHNEbAAAAAAAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAABdGKWAUAACpMgAF5TMAAAALAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + }, + %{ + status: "SUCCESS", + applicationOrder: 2, + feeBump: false, + envelopeXdr: + "AAAAAgAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAPQkAAGgQZAAAANgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAABB90WssODNIgi6BHveqzxTRmIpvAFRyVNM+Hm2GVuCcAAAAAAAAAAAq6aHAHZ2sd9aPbRsskrlXMLWIwqs4Sv2Bk+VwuIR+9wAAABdIdugAAAAAAAAAAAIu2qsYAAAAQERzKOqYYiPXNwsiL8ADAG/f45RBssmf3umGzw4qKkLGlObuPdX0buWmTGrhI13SG38F2V8Mp9DI+eDkcCjMSAOGVuCcAAAAQHnm0o/r+Gsl+6oqBgSbqoSY37gflvQB3zZRghuir0N75UVerd0Q50yG5Zfu08i2crhx6uk+5HYTl8/Sa7uZ+Qc=", + resultXdr: "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=", + resultMetaXdr: + "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALgRm6n57AKz92hLeEpt4BrgS+IEeoAVgzT2ge4u2qsYAAAAADwzS2gAGgQZAAAANQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzPVAAAAABmWdZ2AAAAAAAAAAEAHNEbAAAAAAAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAAAAA8M0toABoEGQAAADYAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAwAAAAMAHNEaAAAAAAAAAAAQfdFrLDgzSIIugR73qs8U0ZiKbwBUclTTPh5thlbgnABZJUSd0V2hAAAAawAAAlEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAaBGEAAAAAZkspCwAAAAAAAAABABzRGwAAAAAAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAWSUtVVp1oQAAAGsAAAJRAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAwAAAAAAGgRhAAAAAGZLKQsAAAAAAAAAAAAc0RsAAAAAAAAAACrpocAdnax31o9tGyySuVcwtYjCqzhK/YGT5XC4hH73AAAAF0h26AAAHNEbAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ledger: 1_888_539, + createdAt: 1_717_166_042 + } + ], + latest_ledger: 1_888_542, + latest_ledger_close_timestamp: 1_717_166_057, + oldest_ledger: 1_871_263, + oldest_ledger_close_timestamp: 1_717_075_350, + cursor: "8111217537191937" + }} + end +end + +defmodule Soroban.RPC.CannedRPCGetFeeStatsClientImpl do + @moduledoc false + @behaviour Soroban.RPC.Client.Spec + + @impl true + def request(_endpoint, _url, _header, _body, _opts) do + send(self(), {:request, "RESPONSE"}) + + {:ok, + %{ + soroban_inclusion_fee: %{ + max: "100", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + transaction_count: "7", + ledger_count: 50 + }, + inclusion_fee: %{ + max: "200", + min: "100", + mode: "200", + p10: "100", + p20: "100", + p30: "100", + p40: "150", + p50: "200", + p60: "200", + p70: "200", + p80: "200", + p90: "200", + p95: "200", + p99: "200", + transaction_count: "27", + ledger_count: 10 + }, + latest_ledger: 619_731 + }} + end +end + defmodule Soroban.RPCTest do use ExUnit.Case @@ -233,12 +349,15 @@ defmodule Soroban.RPCTest do alias Soroban.RPC.{ CannedRPCGetEventsClientImpl, + CannedRPCGetFeeStatsClientImpl, CannedRPCGetHealthClientImpl, CannedRPCGetLatestLedgerClientImpl, CannedRPCGetLedgerEntriesClientImpl, CannedRPCGetLedgerEntriesForAccountClientImpl, CannedRPCGetNetworkClientImpl, CannedRPCGetTransactionClientImpl, + CannedRPCGetTransactionsClientImpl, + CannedRPCGetVersionInfoClientImpl, CannedRPCSendTransactionClientImpl, CannedRPCSimulateTransactionClientImpl, EventFilter, @@ -249,10 +368,12 @@ defmodule Soroban.RPCTest do GetLedgerEntriesResponse, GetNetworkResponse, GetTransactionResponse, + GetTransactionsResponse, SendTransactionResponse, Server, SimulateTransactionResponse, - TopicFilter + TopicFilter, + TransactionsPayload } alias Soroban.Types.Symbol @@ -388,6 +509,37 @@ defmodule Soroban.RPCTest do end end + describe "get_transactions/2" do + setup do + Application.put_env(:soroban, :http_client_impl, CannedRPCGetTransactionsClientImpl) + + on_exit(fn -> + Application.delete_env(:soroban, :http_client_impl) + end) + + start_ledger = 1000 + limit = 2 + cursor = "8111217537191937" + + transaction_payload = + TransactionsPayload.new(start_ledger: start_ledger, limit: limit, cursor: cursor) + + %{payload: transaction_payload} + end + + test "request/2", %{server: server, payload: payload} do + {:ok, + %GetTransactionsResponse{ + transactions: _transactions, + latest_ledger: 1_888_542, + latest_ledger_close_timestamp: 1_717_166_057, + oldest_ledger: 1_871_263, + oldest_ledger_close_timestamp: 1_717_075_350, + cursor: "8111217537191937" + }} = RPC.get_transactions(server, payload) + end + end + describe "get_health/1" do setup do Application.put_env(:soroban, :http_client_impl, CannedRPCGetHealthClientImpl) @@ -519,4 +671,79 @@ defmodule Soroban.RPCTest do }} = RPC.get_events(server, event) end end + + describe "get_version_info/1" do + setup do + Application.put_env(:soroban, :http_client_impl, CannedRPCGetVersionInfoClientImpl) + + on_exit(fn -> + Application.delete_env(:soroban, :http_client_impl) + end) + end + + test "request/1", %{server: server} do + {:ok, + %{ + version: "21.1.0", + commit_hash: "fcd2f0523f04279bae4502f3e3fa00ca627e6f6a", + build_time_stamp: "2024-05-10T11:18:38", + captive_core_version: + "stellar-core 21.0.0.rc2 (c6f474133738ae5f6d11b07963ca841909210273)", + protocol_version: 21 + }} = RPC.get_version_info(server) + end + end + + describe "get_feed_stats/1" do + setup do + Application.put_env(:soroban, :http_client_impl, CannedRPCGetFeeStatsClientImpl) + + on_exit(fn -> + Application.delete_env(:soroban, :http_client_impl) + end) + end + + test "request/1", %{server: server} do + {:ok, + %{ + soroban_inclusion_fee: %{ + max: "100", + min: "100", + mode: "100", + p10: "100", + p20: "100", + p30: "100", + p40: "100", + p50: "100", + p60: "100", + p70: "100", + p80: "100", + p90: "100", + p95: "100", + p99: "100", + transaction_count: "7", + ledger_count: 50 + }, + inclusion_fee: %{ + max: "200", + min: "100", + mode: "200", + p10: "100", + p20: "100", + p30: "100", + p40: "150", + p50: "200", + p60: "200", + p70: "200", + p80: "200", + p90: "200", + p95: "200", + p99: "200", + transaction_count: "27", + ledger_count: 10 + }, + latest_ledger: 619_731 + }} = RPC.get_fee_stats(server) + end + end end