Skip to content

Commit

Permalink
Release v0.21 (#169)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
einerzg authored Jul 24, 2024
1 parent ad1e369 commit a352507
Show file tree
Hide file tree
Showing 25 changed files with 1,176 additions and 38 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
152 changes: 151 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
```elixir
def deps do
[
{:soroban, "~> 0.20.1"}
{:soroban, "~> 0.21.0"}
]
end
```
Expand Down Expand Up @@ -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`: `<xdr.TransactionEnvelope>` - The transaction to be simulated (serialized in base64).
Expand Down Expand Up @@ -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
}}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions lib/rpc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ defmodule Soroban.RPC do

alias Soroban.RPC.{
GetEvents,
GetFeeStats,
GetHealth,
GetLatestLedger,
GetLedgerEntries,
GetLedgerEntriesResponse,
GetNetwork,
GetTransaction,
GetTransactions,
GetVersionInfo,
SendTransaction,
SimulateTransaction
}
Expand All @@ -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
Expand Down
24 changes: 1 addition & 23 deletions lib/rpc/endpoints/events_payload/events_payload.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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),
Expand Down
3 changes: 1 addition & 2 deletions lib/rpc/endpoints/get_events.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
19 changes: 19 additions & 0 deletions lib/rpc/endpoints/get_fee_stats.ex
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions lib/rpc/endpoints/get_transactions.ex
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions lib/rpc/endpoints/get_version_info.ex
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions lib/rpc/endpoints/helper.ex
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions lib/rpc/endpoints/spec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit a352507

Please sign in to comment.