├── .coveralls.yml ├── .formatter.exs ├── .github └── issue_template.md ├── .gitignore ├── .tool-versions ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── config ├── config.exs └── test.exs ├── lib ├── stellar.ex └── stellar │ ├── accounts.ex │ ├── assets.ex │ ├── base.ex │ ├── effects.ex │ ├── fee_stats.ex │ ├── key_pair.ex │ ├── ledgers.ex │ ├── metrics.ex │ ├── offers.ex │ ├── operations.ex │ ├── order_books.ex │ ├── payment_paths.ex │ ├── payments.ex │ ├── str_key.ex │ ├── trade_aggregations.ex │ ├── trades.ex │ ├── transactions.ex │ └── xdr │ └── types │ ├── ledger_entries.ex │ ├── transaction.ex │ └── types.ex ├── mix.exs ├── mix.lock └── test ├── integration └── accounts_test.exs ├── stellar ├── accounts_test.exs ├── assets_test.exs ├── base_test.exs ├── effects_test.exs ├── fee_stats_test.exs ├── keypair_test.exs ├── ledgers_test.exs ├── metrics_test.exs ├── offers_test.exs ├── operations_test.exs ├── order_books_test.exs ├── payment_paths_test.exs ├── payments_test.exs ├── str_key_test.exs ├── trade_aggregations_test.exs ├── trades_test.exs └── transactions_test.exs ├── stellar_test.exs ├── support ├── http_case.ex └── integration_case.ex └── test_helper.exs /.coveralls.yml: -------------------------------------------------------------------------------- 1 | multi: 2 | excoveralls: cover/excoveralls.json 3 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | **I'm submitting a ...** 3 | - [ ] bug report 4 | - [ ] feature request 5 | - [ ] support request 6 | 7 | **What is the current behavior?** 8 | 9 | **For bugs: please provide steps to reproduce and tell us a little about your environment (e.g., what OS and version of the package you're using)** 10 | 11 | **What is the expected behavior?** 12 | 13 | **For feature requests: why should the behavior change?** 14 | 15 | **Other information (we love stack traces!)** 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | stellar-*.tar 24 | 25 | /.elixir_ls 26 | 27 | /config/*.secret.exs 28 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | erlang 21.1 2 | elixir 1.7.4-otp-21 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: elixir 3 | elixir: 4 | - 1.7 5 | otp_release: 6 | - 21.2 7 | cache: 8 | directories: 9 | - _build 10 | - deps 11 | install: 12 | - gem install coveralls-multi --no-document 13 | - mix local.hex --force 14 | - mix local.rebar --force 15 | - mix deps.get 16 | script: 17 | - MIX_ENV=test mix coveralls.json 18 | - coveralls-multi 19 | before_deploy: 20 | - mix compile 21 | deploy: 22 | skip_cleanup: true 23 | provider: script 24 | script: mix hex.publish --yes 25 | on: 26 | tags: true 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.3.1] - 2019-01-30 9 | 10 | ### Changed 11 | 12 | - Updated Dependencies 13 | 14 | ## [0.3.0] - 2018-09-10 15 | 16 | ### Changed 17 | 18 | - Add `hackney_options` global configuration 19 | 20 | ## [0.2.2] - 2018-03-12 21 | 22 | ### Changed 23 | 24 | - .travis.yml file 25 | 26 | ## [0.2.1] - 2018-03-12 27 | 28 | ### Added 29 | 30 | - Luke Ledet as maintainer 31 | 32 | ## [0.2.0] - 2018-02-01 33 | 34 | ### Added 35 | 36 | - `KeyPair` 37 | - `from_secret/1` for getting a key pair from a secret 38 | - `random/0` for generating a new key pair 39 | - `Operations` 40 | - `all_for_ledger/1` and `all_for_ledger/2` for listing operations for a ledger 41 | - `Effects` 42 | - `all_for_operation/1` and `all_for_operation/2` for listing effects for an operation 43 | - `all_for_ledger/1` and `all_for_ledger/2` for listing effects for an operation 44 | - `Payments` 45 | - `all_for_ledger/1` and `all_for_ledger/2` for listing payments for a ledger 46 | - `Transactions` 47 | - `all_for_ledger/1` and `all_for_ledger/2` for listing transactions for a ledger 48 | - `post/1` for posting a transaction 49 | - `Ledgers` 50 | - `get/1` for getting ledger details 51 | - `all/0` and `all/1` for listing ledgers in the system 52 | - `Trades` 53 | - `all/0` and `all/1` for listing trades in the system 54 | - `all_for_order_book/2` and `all_for_order_book/3` for listing trades for an order book 55 | - `OrderBook` 56 | - `get/1` for getting order book details 57 | - `PaymentPaths` 58 | - `all/3` and `all/4` for listing payment paths 59 | - `TradeAggregations` 60 | - `all/2` and `all/3` for listing trade aggregations 61 | 62 | ## [0.1.1] - 2018-01-27 63 | 64 | ### Fixed 65 | 66 | - Make tests more reliable 67 | 68 | ## [0.1.0] - 2018-01-27 69 | 70 | ### Added 71 | 72 | - `Accounts` 73 | - `get/1` for getting account details 74 | - `get_data/2` for getting data associated with account 75 | - `Assets` 76 | - `all/0` and `all/1` for listing assets in the system 77 | - `Effects` 78 | - `get/1` for getting effect details 79 | - `all/0` and `all/1` for listing effects in the system 80 | - `all_for_account/1` and `all_for_account/2` for listing effects for an account 81 | - `Offers` 82 | - `all_for_account/1` and `all_for_account/2` for listing offers for an account 83 | - `Operations` 84 | - `get/1` for getting operation details 85 | - `all/0` and `all/1` for listing operations in the system 86 | - `all_for_account/1` and `all_for_account/2` for listing operations for an account 87 | - `Payments` 88 | - `all/0` and `all/1` for listing payments in the system 89 | - `all_for_account/1` and `all_for_account/2` for listing payments for an account 90 | - `Transactions` 91 | - `get/1` for getting transaction details 92 | - `all/0` and `all/1` for listing transactions in the system 93 | - `all_for_account/1` and `all_for_account/2` for listing transactions for an account 94 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at support@revelry.co. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing and Development 2 | 3 | ## Development Setup 4 | 5 | * Clone repository 6 | * `mix deps.get` to get dependencies 7 | * `mix compile` to compile project 8 | * `mix test` to run tests 9 | 10 | ## Submitting Changes 11 | 12 | 1. Fork the project 13 | 2. Create a new topic branch to contain your feature, change, or fix. 14 | 3. Make sure all the tests are still passing. 15 | 4. Implement your feature, change, or fix. Make sure to write tests, update and/or add documentation. 16 | 5. Push your topic branch up to your fork. 17 | 6. Open a Pull Request with a clear title and description. 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Revelry Labs LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 5 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 6 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions 9 | of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 12 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 13 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 14 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 15 | DEALINGS IN THE SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stellar 2 | 3 | [![Build Status](https://travis-ci.org/revelrylabs/elixir-stellar-client.svg?branch=master)](https://travis-ci.org/revelrylabs/elixir-stellar-client) 4 | [![Coverage Status](https://opencov.prod.revelry.net/projects/12/badge.svg)](https://opencov.prod.revelry.net/projects/12) 5 | [![Hex.pm](https://img.shields.io/hexpm/dt/stellar.svg)](https://hex.pm/packages/stellar) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 7 | 8 | A [Stellar](https://stellar.org) client for Elixir. 9 | 10 | [Documentation](https://hexdocs.pm/stellar) 11 | 12 | ## Installation 13 | 14 | The package can be installed 15 | by adding `stellar` to your list of dependencies in `mix.exs`: 16 | 17 | ```elixir 18 | def deps do 19 | [ 20 | {:stellar, "~> 0.3.0"} 21 | ] 22 | end 23 | ``` 24 | 25 | Add the following to your configuration: 26 | 27 | ```elixir 28 | config :stellar, network: :public # Default is `:public`. To use test network, use `:test` 29 | config :stellar, hackney_options: [] # Options to pass to Hackney 30 | ``` 31 | 32 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 33 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can 34 | be found at [https://hexdocs.pm/stellar](https://hexdocs.pm/stellar). 35 | 36 | ## Contributing and Development 37 | 38 | Bug reports and pull requests are welcomed. See [CONTRIBUTING.md](https://github.com/revelrylabs/elixir-stellar-client/blob/master/CONTRIBUTING.md) 39 | for development guidance. 40 | 41 | Everyone is welcome to participate in the project. We expect contributors to 42 | adhere the Contributor Covenant Code of Conduct (see [CODE_OF_CONDUCT.md](https://github.com/revelrylabs/elixir-stellar-client/blob/master/CODE_OF_CONDUCT.md)). 43 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :stellar, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:stellar, :key) 18 | # 19 | # You can also configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env}.exs" 31 | 32 | if Mix.env() == :test do 33 | import_config "test.exs" 34 | end 35 | -------------------------------------------------------------------------------- /config/test.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :stellar, network: :test 4 | 5 | # This account ONLY exists on the testnet and is ONLY used in the test suite 6 | config :stellar, :test_account, 7 | public_key: "GAW7DAQWCZS7OKF3KTLDNW3BBRUQOQKMOGPM33SNV5XW6FYVQ2URI7B4", 8 | secret: "SCTQEYOV2FBROO76J2CRBBMJQVNL67OLID74JFC4TSI6QAQ6JLUUFLLA" 9 | 10 | if File.exists?(Path.join([__DIR__, "test.secret.exs"])) do 11 | import_config "test.secret.exs" 12 | end 13 | -------------------------------------------------------------------------------- /lib/stellar.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar do 2 | @moduledoc """ 3 | Stellar Client for Elixir 4 | 5 | ### Setup 6 | 7 | Add the following to your configuration: 8 | 9 | ```elixir 10 | config :stellar, network: :public # Default is `:public`. To use test network, use `:test` 11 | ``` 12 | """ 13 | 14 | @type status :: :ok | :error 15 | @type asset_type :: :native | :credit_alphanum4 | :credit_alphanum12 16 | 17 | @doc """ 18 | Shows the url to the cconfigured network 19 | """ 20 | def network do 21 | Stellar.Base.get_url() 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/stellar/accounts.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Accounts do 2 | @moduledoc """ 3 | Functions for interacting with Accounts 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Gets account details 9 | """ 10 | @spec get(binary) :: {Stellar.status(), map} 11 | def get(accountId) do 12 | Base.get("/accounts/#{accountId}") 13 | end 14 | 15 | @doc """ 16 | Gets a single data associated with the given account 17 | """ 18 | @spec get_data(binary, binary) :: {Stellar.status(), map} 19 | def get_data(accountId, key) do 20 | Base.get("/accounts/#{accountId}/data/#{key}") 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/stellar/assets.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Assets do 2 | @moduledoc """ 3 | Functions for interacting with Assets 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all known assets in one the network 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `asset_code`: Code the asset to filter by 13 | 14 | * `asset_issuer`: Issuer of the Asset to filter by 15 | 16 | * `cursor`: A paging token, specifying where to start returning records from. 17 | 18 | * `order`: The order in which to return rows, "asc" or "desc", ordered by `asset_code` then by `asset_issuer`. 19 | 20 | * `limit`: Maximum number of records to return. 21 | """ 22 | @spec all(Keyword.t()) :: {Stellar.status(), map} 23 | def all(params \\ []) do 24 | query = Base.process_query_params(params) 25 | Base.get("/assets#{query}") 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/stellar/base.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Base do 2 | @moduledoc false 3 | 4 | def get(endpoint, headers \\ %{}) do 5 | url = get_url() 6 | 7 | HTTPoison.get(url <> endpoint, headers, options()) 8 | |> process_response() 9 | end 10 | 11 | def post(endpoint, body, headers \\ %{}) do 12 | url = get_url() 13 | 14 | HTTPoison.post(url <> endpoint, body, headers, options()) 15 | |> process_response() 16 | end 17 | 18 | defp options do 19 | default_options = [recv_timeout: 30_000, timeout: 30_000] 20 | override_options = Application.get_env(:stellar, :hackney_options, []) 21 | 22 | Keyword.merge(default_options, override_options) 23 | end 24 | 25 | def get_url() do 26 | network = Application.get_env(:stellar, :network, :public) 27 | get_network_url(network) 28 | end 29 | 30 | def get_network_url(:test), do: "https://horizon-testnet.stellar.org" 31 | def get_network_url(:public), do: "https://horizon.stellar.org" 32 | def get_network_url(url) when is_binary(url), do: url 33 | 34 | def process_query_params([]), do: "" 35 | def process_query_params(params), do: "?" <> URI.encode_query(params) 36 | 37 | defp process_response(response) do 38 | case response do 39 | {:ok, %HTTPoison.Response{status_code: status_code, body: body}} 40 | when status_code >= 200 and status_code < 300 -> 41 | {:ok, Jason.decode!(body)} 42 | 43 | {:ok, %HTTPoison.Response{body: body}} -> 44 | {:error, Jason.decode!(body)} 45 | 46 | {:error, %HTTPoison.Error{reason: reason}} -> 47 | {:error, %{"detail" => reason}} 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/stellar/effects.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Effects do 2 | @moduledoc """ 3 | Functions for interacting with Effects 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all effects 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `cursor`: A paging token, specifying where to start returning records from. 13 | 14 | * `order`: The order in which to return rows, "asc" or "desc". 15 | 16 | * `limit`: Maximum number of records to return. 17 | """ 18 | @spec all(Keyword.t()) :: {Stellar.status(), map} 19 | def all(params \\ []) do 20 | query = Base.process_query_params(params) 21 | Base.get("/effects#{query}") 22 | end 23 | 24 | @doc """ 25 | Gets effect details 26 | """ 27 | @spec get(binary) :: {Stellar.status(), map} 28 | def get(id) do 29 | Base.get("/effects/#{id}") 30 | end 31 | 32 | @doc """ 33 | Returns all effects for given account 34 | 35 | See `all/1` for allowed optional params 36 | """ 37 | @spec all_for_account(binary, Keyword.t()) :: {Stellar.status(), map} 38 | def all_for_account(accountId, params \\ []) do 39 | query = Base.process_query_params(params) 40 | Base.get("/accounts/#{accountId}/effects#{query}") 41 | end 42 | 43 | @doc """ 44 | Returns all effects for given operation 45 | 46 | See `all/1` for allowed optional params 47 | """ 48 | @spec all_for_operation(binary, Keyword.t()) :: {Stellar.status(), map} 49 | def all_for_operation(operationId, params \\ []) do 50 | query = Base.process_query_params(params) 51 | Base.get("/operations/#{operationId}/effects#{query}") 52 | end 53 | 54 | @doc """ 55 | Returns all effects for given ledger 56 | 57 | See `all/1` for allowed optional params 58 | """ 59 | @spec all_for_ledger(binary, Keyword.t()) :: {Stellar.status(), map} 60 | def all_for_ledger(ledgerId, params \\ []) do 61 | query = Base.process_query_params(params) 62 | Base.get("/ledgers/#{ledgerId}/effects#{query}") 63 | end 64 | 65 | @doc """ 66 | Returns all effects for given transaction 67 | 68 | See `all/1` for allowed optional params 69 | """ 70 | @spec all_for_transaction(binary, Keyword.t()) :: {Stellar.status(), map} 71 | def all_for_transaction(hash, params \\ []) do 72 | query = Base.process_query_params(params) 73 | Base.get("/transactions/#{hash}/effects#{query}") 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /lib/stellar/fee_stats.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.FeeStats do 2 | @moduledoc """ 3 | Functions for interacting with FeeStats 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Gets fee stats 9 | """ 10 | @spec get() :: {Stellar.status(), map} 11 | def get do 12 | Base.get("/fee_stats") 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/stellar/key_pair.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.KeyPair do 2 | @moduledoc """ 3 | Operations for dealing with key pairs 4 | """ 5 | alias Stellar.StrKey 6 | 7 | @doc """ 8 | Generates a key pair from the given secret 9 | """ 10 | @spec from_secret(binary) :: {binary, binary} 11 | def from_secret(secret) do 12 | decoded_secret = StrKey.decode_check!(:ed25519SecretSeed, secret) 13 | derived_public_key = Ed25519.derive_public_key(decoded_secret) 14 | encoded_public_key = StrKey.encode_check!(:ed25519PublicKey, derived_public_key) 15 | 16 | {encoded_public_key, secret} 17 | end 18 | 19 | @doc """ 20 | Generates a new keypair 21 | """ 22 | @spec random() :: {binary, binary} 23 | def random do 24 | {secret, public_key} = Ed25519.generate_key_pair() 25 | encoded_public_key = StrKey.encode_check!(:ed25519PublicKey, public_key) 26 | encoded_secret = StrKey.encode_check!(:ed25519SecretSeed, secret) 27 | 28 | {encoded_public_key, encoded_secret} 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/stellar/ledgers.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Ledgers do 2 | @moduledoc """ 3 | Functions for interacting with Ledgers 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all ledgers 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `cursor`: A paging token, specifying where to start returning records from. 13 | 14 | * `order`: The order in which to return rows, "asc" or "desc". 15 | 16 | * `limit`: Maximum number of records to return. 17 | """ 18 | @spec all(Keyword.t()) :: {Stellar.status(), map} 19 | def all(params \\ []) do 20 | query = Base.process_query_params(params) 21 | Base.get("/ledgers#{query}") 22 | end 23 | 24 | @doc """ 25 | Gets ledger details 26 | """ 27 | @spec get(binary) :: {Stellar.status(), map} 28 | def get(id) do 29 | Base.get("/ledgers/#{id}") 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/stellar/metrics.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Metrics do 2 | @moduledoc """ 3 | Functions for interacting with Metrics 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Gets metrics 9 | """ 10 | @spec get() :: {Stellar.status(), map} 11 | def get do 12 | Base.get("/metrics") 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/stellar/offers.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Offers do 2 | @moduledoc """ 3 | Functions for interacting with Offers 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all offers for given account 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `cursor`: A paging token, specifying where to start returning records from. 13 | 14 | * `order`: The order in which to return rows, "asc" or "desc". 15 | 16 | * `limit`: Maximum number of records to return. 17 | """ 18 | @spec all_for_account(binary, Keyword.t()) :: {Stellar.status(), map} 19 | def all_for_account(accountId, params \\ []) do 20 | query = Base.process_query_params(params) 21 | Base.get("/accounts/#{accountId}/offers#{query}") 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/stellar/operations.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Operations do 2 | @moduledoc """ 3 | Functions for interacting with Operations 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all operations 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `cursor`: A paging token, specifying where to start returning records from. 13 | 14 | * `order`: The order in which to return rows, "asc" or "desc". 15 | 16 | * `limit`: Maximum number of records to return. 17 | """ 18 | @spec all(Keyword.t()) :: {Stellar.status(), map} 19 | def all(params \\ []) do 20 | query = Base.process_query_params(params) 21 | Base.get("/operations#{query}") 22 | end 23 | 24 | @doc """ 25 | Gets operation details 26 | """ 27 | @spec get(binary) :: {Stellar.status(), map} 28 | def get(id) do 29 | Base.get("/operations/#{id}") 30 | end 31 | 32 | @doc """ 33 | Returns all operations for given account 34 | 35 | See `all/1` for allowed optional params 36 | """ 37 | @spec all_for_account(binary, Keyword.t()) :: {Stellar.status(), map} 38 | def all_for_account(accountId, params \\ []) do 39 | query = Base.process_query_params(params) 40 | Base.get("/accounts/#{accountId}/operations#{query}") 41 | end 42 | 43 | @doc """ 44 | Returns all operations for given ledger 45 | 46 | See `all/1` for allowed optional params 47 | """ 48 | @spec all_for_ledger(binary, Keyword.t()) :: {Stellar.status(), map} 49 | def all_for_ledger(ledgerId, params \\ []) do 50 | query = Base.process_query_params(params) 51 | Base.get("/ledgers/#{ledgerId}/operations#{query}") 52 | end 53 | 54 | @doc """ 55 | Returns all operations for given transaction 56 | 57 | See `all/1` for allowed optional params 58 | """ 59 | @spec all_for_transaction(binary, Keyword.t()) :: {Stellar.status(), map} 60 | def all_for_transaction(hash, params \\ []) do 61 | query = Base.process_query_params(params) 62 | Base.get("/transactions/#{hash}/operations#{query}") 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/stellar/order_books.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.OrderBooks do 2 | @moduledoc """ 3 | Functions for interacting with OrderBooks 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns order book details 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `selling_asset_code`: Code of the Asset being sold. 13 | 14 | * `selling_asset_issuer`: Account ID of the issuer of the Asset being sold. 15 | 16 | * `buying_asset_code`: Code of the Asset being bought. 17 | 18 | * `buying_asset_issuer`: Account ID of the issuer of the Asset being bought. 19 | 20 | * `limit`: Maximum number of records to return. 21 | """ 22 | @spec get(Stellar.asset_type(), Stellar.asset_type(), Keyword.t()) :: {Stellar.status(), map} 23 | def get(selling_asset_type, buying_asset_type, params \\ []) do 24 | params = 25 | params 26 | |> Keyword.put(:selling_asset_type, selling_asset_type) 27 | |> Keyword.put(:buying_asset_type, buying_asset_type) 28 | 29 | query = Base.process_query_params(params) 30 | Base.get("/order_book#{query}") 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/stellar/payment_paths.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.PaymentPaths do 2 | @moduledoc """ 3 | Functions for interacting with PaymentPaths 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns payment paths meeting the given parameters 9 | 10 | optional `params` can take any of the following: 11 | 12 | * `destination_asset_code`: The code for the destination. Required if destination_asset_type is not `native`. 13 | 14 | * `destination_asset_issuer`: The issuer for the destination, Required if destination_asset_type is not `native`. 15 | 16 | * `source_account`: The sender’s account id. Any returned path must use a source that the sender can hold. 17 | """ 18 | @spec all(binary, Stellar.asset_type(), number, Keyword.t()) :: {Stellar.status(), map} 19 | def all(destination_account, destination_asset_type, destination_amount, params \\ []) do 20 | params = 21 | params 22 | |> Keyword.put(:destination_account, destination_account) 23 | |> Keyword.put(:destination_amount, destination_amount) 24 | |> Keyword.put(:destination_asset_type, destination_asset_type) 25 | 26 | query = Base.process_query_params(params) 27 | Base.get("/paths#{query}") 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/stellar/payments.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Payments do 2 | @moduledoc """ 3 | Functions for interacting with Payments 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all payments 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `cursor`: A paging token, specifying where to start returning records from. 13 | 14 | * `order`: The order in which to return rows, "asc" or "desc". 15 | 16 | * `limit`: Maximum number of records to return. 17 | """ 18 | @spec all(Keyword.t()) :: {Stellar.status(), map} 19 | def all(params \\ []) do 20 | query = Base.process_query_params(params) 21 | Base.get("/payments#{query}") 22 | end 23 | 24 | @doc """ 25 | Returns all payments for given account 26 | 27 | See `all/1` for allowed optional params 28 | """ 29 | @spec all_for_account(binary, Keyword.t()) :: {Stellar.status(), map} 30 | def all_for_account(accountId, params \\ []) do 31 | query = Base.process_query_params(params) 32 | Base.get("/accounts/#{accountId}/payments#{query}") 33 | end 34 | 35 | @doc """ 36 | Returns all payments for given ledger 37 | 38 | See `all/1` for allowed optional params 39 | """ 40 | @spec all_for_ledger(binary, Keyword.t()) :: {Stellar.status(), map} 41 | def all_for_ledger(ledgerId, params \\ []) do 42 | query = Base.process_query_params(params) 43 | Base.get("/ledgers/#{ledgerId}/payments#{query}") 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/stellar/str_key.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.StrKey do 2 | @moduledoc false 3 | 4 | # Logic copied from https://github.com/stellar/js-stellar-base/blob/master/src/strkey.js 5 | 6 | import Bitwise 7 | 8 | @version_bytes %{ 9 | # G 10 | ed25519PublicKey: 6 <<< 3, 11 | # S 12 | ed25519SecretSeed: 18 <<< 3, 13 | # T 14 | preAuthTx: 19 <<< 3, 15 | # X 16 | sha256Hash: 23 <<< 3 17 | } 18 | 19 | def encode_check!(_, nil) do 20 | raise ArgumentError, "cannot encode nil data" 21 | end 22 | 23 | def encode_check!(version_byte_name, _) 24 | when version_byte_name not in [ 25 | :ed25519PublicKey, 26 | :ed25519SecretSeed, 27 | :preAuthTx, 28 | :sha256Hash 29 | ] do 30 | raise ArgumentError, 31 | "#{version_byte_name} is not a valid version byte name. expected one of :ed25519PublicKey, :ed25519SecretSeed, :preAuthTx, :sha256Hash" 32 | end 33 | 34 | def encode_check!(version_byte_name, data) do 35 | version_byte = @version_bytes[version_byte_name] 36 | 37 | payload = <> <> data 38 | checksum = CRC.crc(:crc_16_xmodem, payload) 39 | unencoded = payload <> <> 40 | Base.encode32(unencoded, padding: false) 41 | end 42 | 43 | def decode_check!(version_byte_name, encoded) do 44 | decoded = Base.decode32!(encoded) 45 | <> = decoded 46 | 47 | expected_version = @version_bytes[version_byte_name] 48 | 49 | if is_nil(expected_version) do 50 | raise ArgumentError, "#{version_byte_name} is not a valid version byte name" 51 | end 52 | 53 | if version_byte != expected_version do 54 | raise ArgumentError, 55 | "invalid version byte. expected #{expected_version}, got #{version_byte}" 56 | end 57 | 58 | expected_checksum = CRC.crc(:crc_16_xmodem, <> <> data) 59 | 60 | if checksum != expected_checksum do 61 | raise ArgumentError, "invalid checksum" 62 | end 63 | 64 | data 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/stellar/trade_aggregations.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.TradeAggregations do 2 | @moduledoc """ 3 | Functions for interacting with TradeAggregations 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns trade aggregations meeting the given parameters 9 | 10 | optional `params` can take any of the following: 11 | 12 | * `start_time`: lower time boundary represented as millis since epoch. 13 | 14 | * `end_time`: upper time boundary represented as millis since epoch 15 | 16 | * `resolution`: segment duration as millis since epoch. Supported values are 5 minutes (300000), 15 minutes (900000), 1 hour (3600000), 1 day (86400000) and 1 week (604800000). 17 | 18 | * `base_asset_code`: Code of base asset, not required if type is `native` 19 | 20 | * `base_asset_issuer`: Issuer of base asset, not required if type is `native` 21 | 22 | * `counter_asset_code`: Code of counter asset, not required if type is `native` 23 | 24 | * `counter_asset_issuer`: Issuer of counter asset, not required if type is `native` 25 | 26 | * `order`: The order in which to return rows, "asc" or "desc". 27 | 28 | * `limit`: Maximum number of records to return. 29 | """ 30 | @spec all(Stellar.asset_type(), Stellar.asset_type(), Keyword.t()) :: {Stellar.status(), map} 31 | def all(base_asset_type, counter_asset_type, params \\ []) do 32 | params = 33 | params 34 | |> Keyword.put(:base_asset_type, base_asset_type) 35 | |> Keyword.put(:counter_asset_type, counter_asset_type) 36 | 37 | query = Base.process_query_params(params) 38 | Base.get("/trade_aggregations#{query}") 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/stellar/trades.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Trades do 2 | @moduledoc """ 3 | Functions for interacting with Trades 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all trades 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `base_asset_type`: Type of base asset. 13 | 14 | * `base_asset_code`: Code of base asset, not required if type is `native`. 15 | 16 | * `base_asset_issuer`: Issuer of base asset, not required if type is `native`. 17 | 18 | * `counter_asset_type`: Type of counter asset. 19 | 20 | * `counter_asset_code`: Code of counter asset, not required if type is `native`. 21 | 22 | * `counter_asset_issuer`: Issuer of counter asset, not required if type is `native`. 23 | 24 | * `offer_id`: filter for by a specific offer id. 25 | 26 | * `cursor`: A paging token, specifying where to start returning records from. 27 | 28 | * `order`: The order in which to return rows, "asc" or "desc". 29 | 30 | * `limit`: Maximum number of records to return. 31 | """ 32 | @spec all(Keyword.t()) :: {Stellar.status(), map} 33 | def all(params \\ []) do 34 | query = Base.process_query_params(params) 35 | Base.get("/trades#{query}") 36 | end 37 | 38 | @doc """ 39 | Returns all trades for given order book 40 | 41 | optional `params` can take any of the following.: 42 | 43 | * `selling_asset_code`: Code of the Asset being sold. 44 | 45 | * `selling_asset_issuer`: Account ID of the issuer of the Asset being sold. 46 | 47 | * `buying_asset_code`: Code of the Asset being bought. 48 | 49 | * `buying_asset_issuer`: Account ID of the issuer of the Asset being bought. 50 | 51 | * `limit`: Maximum number of records to return. 52 | """ 53 | @spec all_for_order_book(Stellar.asset_type(), Stellar.asset_type(), Keyword.t()) :: 54 | {Stellar.status(), map} 55 | def all_for_order_book(selling_asset_type, buying_asset_type, params \\ []) do 56 | params = 57 | params 58 | |> Keyword.put(:selling_asset_type, selling_asset_type) 59 | |> Keyword.put(:buying_asset_type, buying_asset_type) 60 | 61 | query = Base.process_query_params(params) 62 | Base.get("/order_book/trades#{query}") 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/stellar/transactions.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Transactions do 2 | @moduledoc """ 3 | Functions for interacting with Transactions 4 | """ 5 | alias Stellar.Base 6 | 7 | @doc """ 8 | Returns all transactions 9 | 10 | optional `params` can take any of the following.: 11 | 12 | * `cursor`: A paging token, specifying where to start returning records from. 13 | 14 | * `order`: The order in which to return rows, "asc" or "desc". 15 | 16 | * `limit`: Maximum number of records to return. 17 | """ 18 | @spec all(Keyword.t()) :: {Stellar.status(), map} 19 | def all(params \\ []) do 20 | query = Base.process_query_params(params) 21 | Base.get("/transactions#{query}") 22 | end 23 | 24 | @doc """ 25 | Gets transaction details 26 | """ 27 | @spec get(binary) :: {Stellar.status(), map} 28 | def get(hash) do 29 | Base.get("/transactions/#{hash}") 30 | end 31 | 32 | @doc """ 33 | Returns all transactions for given account 34 | 35 | See `all/1` for allowed optional params 36 | """ 37 | @spec all_for_account(binary, Keyword.t()) :: {Stellar.status(), map} 38 | def all_for_account(accountId, params \\ []) do 39 | query = Base.process_query_params(params) 40 | Base.get("/accounts/#{accountId}/transactions#{query}") 41 | end 42 | 43 | @doc """ 44 | Returns all transactions for given ledger 45 | 46 | See `all/1` for allowed optional params 47 | """ 48 | @spec all_for_ledger(binary, Keyword.t()) :: {Stellar.status(), map} 49 | def all_for_ledger(ledgerId, params \\ []) do 50 | query = Base.process_query_params(params) 51 | Base.get("/ledgers/#{ledgerId}/transactions#{query}") 52 | end 53 | 54 | @doc """ 55 | Posts the given Base64 representation of a transaction envelope 56 | """ 57 | @spec post(binary) :: {Stellar.status(), map} 58 | def post(transaction) do 59 | form = 60 | {:multipart, 61 | [ 62 | {"tx", transaction} 63 | ]} 64 | 65 | Base.post("/transactions", form) 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/stellar/xdr/types/ledger_entries.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.XDR.Types.LedgerEntries do 2 | alias XDR.Type.{ 3 | FixedOpaque, 4 | Enum, 5 | Union, 6 | VariableOpaque, 7 | Void, 8 | Int, 9 | Uint, 10 | HyperInt, 11 | HyperUint, 12 | VariableArray 13 | } 14 | 15 | alias Stellar.XDR.Types.SignerKey 16 | alias Stellar.XDR.Types.PublicKey, as: AccountID 17 | 18 | defmodule Thresholds do 19 | use FixedOpaque, len: 4 20 | end 21 | 22 | defmodule String32 do 23 | use VariableArray, spec: [max_len: 32, type: XDR.Type.String] 24 | end 25 | 26 | defmodule String64 do 27 | use VariableArray, spec: [max_len: 64, type: XDR.Type.String] 28 | end 29 | 30 | defmodule DataValue do 31 | use VariableOpaque, max_len: 64 32 | end 33 | 34 | defmodule AssetType do 35 | use Enum, 36 | spec: [ 37 | ASSET_TYPE_NATIVE: 0, 38 | ASSET_TYPE_CREDIT_ALPHANUM4: 1, 39 | ASSET_TYPE_CREDIT_ALPHANUM12: 2 40 | ] 41 | end 42 | 43 | defmodule Asset do 44 | use Union, 45 | spec: [ 46 | switch: AssetType, 47 | cases: [ 48 | {0, Void}, 49 | {1, AssetTypeCreditAlphaNum4}, 50 | {2, AssetTypeCreditAlphaNum12} 51 | ] 52 | ] 53 | end 54 | 55 | defmodule AssetCode4 do 56 | use FixedOpaque, len: 4 57 | end 58 | 59 | defmodule AssetCode12 do 60 | use FixedOpaque, len: 12 61 | end 62 | 63 | defmodule AssetTypeCreditAlphaNum4 do 64 | use XDR.Type.Struct, 65 | spec: [ 66 | assetCode: AssetCode4, 67 | issuer: AccountID 68 | ] 69 | end 70 | 71 | defmodule AssetTypeCreditAlphaNum12 do 72 | use XDR.Type.Struct, 73 | spec: [ 74 | assetCode: AssetCode12, 75 | issuer: AccountID 76 | ] 77 | end 78 | 79 | defmodule Price do 80 | use XDR.Type.Struct, 81 | spec: [ 82 | n: Int, 83 | d: Int 84 | ] 85 | end 86 | 87 | defmodule ThresholdIndexes do 88 | use Enum, 89 | spec: [ 90 | THRESHOLD_MASTER_WEIGHT: 0, 91 | THRESHOLD_LOW: 1, 92 | THRESHOLD_MED: 2, 93 | THRESHOLD_HIGH: 3 94 | ] 95 | end 96 | 97 | defmodule LedgerEntryType do 98 | use Enum, 99 | spec: [ 100 | ACCOUNT: 0, 101 | TRUSTLINE: 1, 102 | OFFER: 2, 103 | DATA: 3 104 | ] 105 | end 106 | 107 | defmodule Signer do 108 | use XDR.Type.Struct, 109 | spec: [ 110 | key: SignerKey, 111 | weight: Uint 112 | ] 113 | end 114 | 115 | defmodule AccountFlags do 116 | use Enum, 117 | spec: [ 118 | AUTH_REQUIRED_FLAG: 0x1, 119 | AUTH_REVOCABLE_FLAG: 0x2, 120 | AUTH_IMMUTABLE_FLAG: 0x4 121 | ] 122 | end 123 | 124 | defmodule AccountEntry do 125 | use XDR.Type.Struct, 126 | spec: [ 127 | accountID: AccountID, 128 | balance: HyperInt, 129 | seqNum: HyperUint, 130 | numSubEntries: Uint, 131 | inflationDest: AccountID, 132 | flags: Uint, 133 | homeDomain: XDR.Type.String, 134 | thresholds: Thresholds, 135 | signers: Signers, 136 | ext: Ext 137 | ] 138 | 139 | defmodule Signers do 140 | use VariableArray, spec: [max_len: 20, type: Signer] 141 | end 142 | end 143 | 144 | defmodule Ext do 145 | use Union, 146 | spec: [ 147 | switch: Int, 148 | cases: [ 149 | {0, Void} 150 | ] 151 | ] 152 | end 153 | 154 | defmodule TrustLineFlags do 155 | use Enum, 156 | spec: [ 157 | AUTHORIZED_FLAG: 1 158 | ] 159 | end 160 | 161 | defmodule TrustLineEntry do 162 | use XDR.Type.Struct, 163 | spec: [ 164 | accountID: AccountID, 165 | asset: Asset, 166 | balance: HyperInt, 167 | limit: HyperInt, 168 | flags: Uint, 169 | ext: Ext 170 | ] 171 | end 172 | 173 | defmodule OfferEntryFlags do 174 | use Enum, 175 | spec: [ 176 | PASSIVE_FLAG: 1 177 | ] 178 | end 179 | 180 | defmodule OfferEntry do 181 | use XDR.Type.Struct, 182 | spec: [ 183 | sellerID: AccountID, 184 | offerID: HyperUint, 185 | selling: Asset, 186 | buying: Asset, 187 | amount: HyperInt, 188 | price: Price, 189 | flags: Uint, 190 | ext: Ext 191 | ] 192 | end 193 | 194 | defmodule DataEntry do 195 | use XDR.Type.Struct, 196 | spec: [ 197 | accountID: AccountID, 198 | dataName: String64, 199 | dataValue: DataValue, 200 | ext: Ext 201 | ] 202 | end 203 | 204 | defmodule LedgerEntry do 205 | use XDR.Type.Struct, 206 | spec: [ 207 | lastModifiedLedgerSeq: Uint, 208 | data: Data, 209 | ext: Ext 210 | ] 211 | 212 | defmodule Data do 213 | use Union, 214 | spec: [ 215 | switch: LedgerEntryType, 216 | cases: [ 217 | {0, AccountEntry}, 218 | {1, TrustLineEntry}, 219 | {2, OfferEntry}, 220 | {3, DataEntry} 221 | ] 222 | ] 223 | end 224 | end 225 | 226 | defmodule EnvelopeType do 227 | use Enum, 228 | spec: [ 229 | ENVELOPE_TYPE_SCP: 1, 230 | ENVELOPE_TYPE_TX: 2, 231 | ENVELOPE_TYPE_AUTH: 3 232 | ] 233 | end 234 | end 235 | -------------------------------------------------------------------------------- /lib/stellar/xdr/types/transaction.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.XDR.Types.Transaction do 2 | alias XDR.Type.{ 3 | Enum, 4 | Union, 5 | Void, 6 | Int, 7 | Uint, 8 | HyperInt, 9 | HyperUint, 10 | VariableArray, 11 | Uint 12 | } 13 | 14 | alias Stellar.XDR.Types.{SignatureHint, Signature, Hash} 15 | alias Stellar.XDR.Types.PublicKey, as: AccountID 16 | 17 | alias Stellar.Types.LedgerEntries.{ 18 | Asset, 19 | Price, 20 | String32, 21 | Signer, 22 | AssetCode4, 23 | AssetCode12, 24 | String64, 25 | DataValue, 26 | Ext, 27 | OfferEntry 28 | } 29 | 30 | defmodule DecoratedSignature do 31 | use XDR.Type.Struct, 32 | spec: [ 33 | hint: SignatureHint, 34 | signature: Signature 35 | ] 36 | end 37 | 38 | defmodule OperationType do 39 | use Enum, 40 | spec: [ 41 | CREATE_ACCOUNT: 0, 42 | PAYMENT: 1, 43 | PATH_PAYMENT: 2, 44 | MANAGE_OFFER: 3, 45 | CREATE_PASSIVE_OFFER: 4, 46 | SET_OPTIONS: 5, 47 | CHANGE_TRUST: 6, 48 | ALLOW_TRUST: 7, 49 | ACCOUNT_MERGE: 8, 50 | INFLATION: 9, 51 | MANAGE_DATA: 10 52 | ] 53 | end 54 | 55 | defmodule CreateAccountOp do 56 | use XDR.Type.Struct, 57 | spec: [ 58 | destination: AccountID, 59 | startingBalance: HyperInt 60 | ] 61 | end 62 | 63 | defmodule PaymentOp do 64 | use XDR.Type.Struct, 65 | spec: [ 66 | destination: AccountID, 67 | asset: Asset, 68 | amount: HyperInt 69 | ] 70 | end 71 | 72 | defmodule PathPaymentOp do 73 | use XDR.Type.Struct, 74 | spec: [ 75 | sendAsset: Asset, 76 | sendMax: HyperInt, 77 | destination: AccountID, 78 | destinationAsset: Asset, 79 | destinationAmount: HyperInt, 80 | path: AssetPaths 81 | ] 82 | 83 | defmodule AssetPaths do 84 | use VariableArray, spec: [max_len: 5, type: Asset] 85 | end 86 | end 87 | 88 | defmodule ManageOfferOp do 89 | use XDR.Type.Struct, 90 | spec: [ 91 | selling: Asset, 92 | buying: Asset, 93 | amount: HyperInt, 94 | price: Price, 95 | offerID: HyperUint 96 | ] 97 | end 98 | 99 | defmodule CreatePassiveOfferOp do 100 | use XDR.Type.Struct, 101 | spec: [ 102 | selling: Asset, 103 | buying: Asset, 104 | amount: HyperInt, 105 | price: Price 106 | ] 107 | end 108 | 109 | defmodule SetOptionsOp do 110 | use XDR.Type.Struct, 111 | spec: [ 112 | inflationDest: AccountID, 113 | clearFlags: Uint, 114 | setFlags: Uint, 115 | masterWeight: Uint, 116 | lowThreshold: Uint, 117 | medThreshold: Uint, 118 | highThreshold: Uint, 119 | homeDomain: String32, 120 | signer: Signer 121 | ] 122 | end 123 | 124 | defmodule ChangeTrustOp do 125 | use XDR.Type.Struct, 126 | spec: [ 127 | line: Asset, 128 | limit: HyperInt 129 | ] 130 | end 131 | 132 | defmodule AllowTrustOp do 133 | use XDR.Type.Struct, 134 | spec: [ 135 | trustor: AccountID, 136 | asset: Asset 137 | ] 138 | 139 | defmodule Asset do 140 | use Union, 141 | spec: [ 142 | switch: AssetType, 143 | cases: [ 144 | {1, AssetCode4}, 145 | {2, AssetCode12} 146 | ] 147 | ] 148 | end 149 | end 150 | 151 | defmodule ManageDataOp do 152 | use XDR.Type.Struct, 153 | spec: [ 154 | dataName: String64, 155 | dataValue: DataValue 156 | ] 157 | end 158 | 159 | defmodule Operation do 160 | use XDR.Type.Struct, 161 | spec: [ 162 | sourceAccount: AccountID, 163 | body: OperationUnion 164 | ] 165 | 166 | defmodule OperationUnion do 167 | use Union, 168 | spec: [ 169 | switch: OperationType, 170 | cases: [ 171 | {0, CreateAccountOp}, 172 | {1, PaymentOp}, 173 | {2, PathPaymentOp}, 174 | {3, ManageOfferOp}, 175 | {4, CreatePassiveOfferOp}, 176 | {5, SetOptionsOp}, 177 | {6, ChangeTrustOp}, 178 | {7, AllowTrustOp}, 179 | {8, AccountID}, 180 | {9, Void}, 181 | {10, ManageDataOp} 182 | ] 183 | ] 184 | end 185 | end 186 | 187 | defmodule MemoType do 188 | use Enum, 189 | spec: [ 190 | MEMO_NONE: 0, 191 | MEMO_TEXT: 1, 192 | MEMO_ID: 2, 193 | MEMO_HASH: 3, 194 | MEMO_RETURN: 4 195 | ] 196 | end 197 | 198 | defmodule Text do 199 | use VariableArray, spec: [max_len: 28, type: XDR.Type.String] 200 | end 201 | 202 | defmodule Memo do 203 | use Union, 204 | spec: [ 205 | switch: MemoType, 206 | cases: [ 207 | {0, Void}, 208 | {1, Text}, 209 | {2, HyperUint}, 210 | {3, Hash}, 211 | {4, Hash} 212 | ] 213 | ] 214 | end 215 | 216 | defmodule TimeBounds do 217 | use XDR.Type.Struct, 218 | spec: [ 219 | minTime: HyperUint, 220 | maxTime: HyperUint 221 | ] 222 | end 223 | 224 | defmodule Transaction do 225 | use XDR.Type.Struct, 226 | spec: [ 227 | sourceAccount: AccountID, 228 | fee: Int, 229 | seqNum: HyperUint, 230 | timeBounds: TimeBounds, 231 | memo: Memo, 232 | operations: Operations, 233 | ext: Ext 234 | ] 235 | 236 | defmodule Operations do 237 | use VariableArray, spec: [max_len: 100, type: Operation] 238 | end 239 | end 240 | 241 | defmodule TransactionSignaturePayload do 242 | use XDR.Type.Struct, 243 | spec: [ 244 | networkId: Hash, 245 | taggedTransaction: TaggedTransaction 246 | ] 247 | 248 | defmodule TaggedTransaction do 249 | use Union, 250 | spec: [ 251 | switch: MemoType, 252 | cases: [ 253 | {0, Transaction} 254 | ] 255 | ] 256 | end 257 | end 258 | 259 | defmodule TransactionEnvelope do 260 | use XDR.Type.Struct, 261 | spec: [ 262 | tx: Transaction, 263 | signatures: DecoratedSignatures 264 | ] 265 | 266 | defmodule DecoratedSignatures do 267 | use VariableArray, spec: [max_len: 20, type: DecoratedSignature] 268 | end 269 | end 270 | 271 | defmodule ClaimOfferAtom do 272 | use XDR.Type.Struct, 273 | spec: [ 274 | sellerID: AccountID, 275 | offerID: HyperUint, 276 | assetSold: Asset, 277 | amountSold: HyperInt, 278 | assetBought: Asset, 279 | amountBought: HyperInt 280 | ] 281 | end 282 | 283 | defmodule CreateAccountResultCode do 284 | use Enum, 285 | spec: [ 286 | CREATE_ACCOUNT_SUCCESS: 0, 287 | CREATE_ACCOUNT_MALFORMED: -1, 288 | CREATE_ACCOUNT_UNDERFUNDED: -2, 289 | CREATE_ACCOUNT_LOW_RESERVE: -3, 290 | CREATE_ACCOUNT_ALREADY_EXIST: -4 291 | ] 292 | end 293 | 294 | defmodule CreateAccountResult do 295 | use Union, 296 | spec: [ 297 | switch: CreateAccountResultCode, 298 | cases: [ 299 | {0, VOID} 300 | ] 301 | ] 302 | end 303 | 304 | defmodule PaymentResultCode do 305 | use Enum, 306 | spec: [ 307 | PAYMENT_SUCCESS: 0, 308 | PAYMENT_MALFORMED: -1, 309 | PAYMENT_UNDERFUNDED: -2, 310 | PAYMENT_SRC_NO_TRUST: -3, 311 | PAYMENT_SRC_NOT_AUTHORIZED: -4, 312 | PAYMENT_NO_DESTINATION: -5, 313 | PAYMENT_NO_TRUST: -6, 314 | PAYMENT_NOT_AUTHORIZED: -7, 315 | PAYMENT_LINE_FULL: -8, 316 | PAYMENT_NO_ISSUER: -9 317 | ] 318 | end 319 | 320 | defmodule PaymentResult do 321 | use Union, 322 | spec: [ 323 | switch: PaymentResultCode, 324 | cases: [ 325 | {0, VOID} 326 | ] 327 | ] 328 | end 329 | 330 | defmodule PathPaymentResultCode do 331 | use Enum, 332 | spec: [ 333 | PATH_PAYMENT_SUCCESS: 0, 334 | PATH_PAYMENT_MALFORMED: -1, 335 | PATH_PAYMENT_UNDERFUNDED: -2, 336 | PATH_PAYMENT_SRC_NO_TRUST: -3, 337 | PATH_PAYMENT_SRC_NOT_AUTHORIZED: -4, 338 | PATH_PAYMENT_NO_DESTINATION: -5, 339 | PATH_PAYMENT_NO_TRUST: -6, 340 | PATH_PAYMENT_NOT_AUTHORIZED: -7, 341 | PATH_PAYMENT_LINE_FULL: -8, 342 | PATH_PAYMENT_NO_ISSUER: -9, 343 | PATH_PAYMENT_TOO_FEW_OFFERS: -10, 344 | PATH_PAYMENT_OFFER_CROSS_SELF: -11, 345 | PATH_PAYMENT_OVER_SENDMAX: -12 346 | ] 347 | end 348 | 349 | defmodule SimplePaymentResult do 350 | use XDR.Type.Struct, 351 | spec: [ 352 | destination: AccountID, 353 | asset: Asset, 354 | amount: HyperInt 355 | ] 356 | end 357 | 358 | defmodule PathPaymentResult do 359 | use Union, 360 | spec: [ 361 | switch: PathPaymentResultCode, 362 | cases: [ 363 | {0, PaymentSuccess}, 364 | {-9, Asset} 365 | ] 366 | ] 367 | 368 | defmodule PaymentSuccess do 369 | use XDR.Type.Struct, 370 | spec: [ 371 | offers: ClaimOfferAtoms, 372 | last: SimplePaymentResult 373 | ] 374 | 375 | defmodule ClaimOfferAtoms do 376 | use VariableArray, spec: [type: ClaimOfferAtom] 377 | end 378 | end 379 | end 380 | 381 | defmodule ManageOfferResultCode do 382 | use Enum, 383 | spec: [ 384 | MANAGE_OFFER_SUCCESS: 0, 385 | MANAGE_OFFER_MALFORMED: -1, 386 | MANAGE_OFFER_SELL_NO_TRUST: -2, 387 | MANAGE_OFFER_BUY_NO_TRUST: -3, 388 | MANAGE_OFFER_SELL_NOT_AUTHORIZED: -4, 389 | MANAGE_OFFER_BUY_NOT_AUTHORIZED: -5, 390 | MANAGE_OFFER_LINE_FULL: -6, 391 | MANAGE_OFFER_UNDERFUNDED: -7, 392 | MANAGE_OFFER_CROSS_SELF: -8, 393 | MANAGE_OFFER_SELL_NO_ISSUER: -9, 394 | MANAGE_OFFER_BUY_NO_ISSUER: -10, 395 | MANAGE_OFFER_NOT_FOUND: -11, 396 | MANAGE_OFFER_LOW_RESERVE: -12 397 | ] 398 | end 399 | 400 | defmodule ManageOfferEffect do 401 | use Enum, 402 | spec: [ 403 | MANAGE_OFFER_CREATED: 0, 404 | MANAGE_OFFER_UPDATED: 1, 405 | MANAGE_OFFER_DELETED: 2 406 | ] 407 | end 408 | 409 | defmodule ClaimOfferAtoms do 410 | use VariableArray, spec: [type: ClaimOfferAtom] 411 | end 412 | 413 | defmodule ManageOfferSuccessResult do 414 | use XDR.Type.Struct, 415 | spec: [ 416 | offersClaimed: ClaimOfferAtoms, 417 | offer: OfferUnion 418 | ] 419 | 420 | defmodule OfferUnion do 421 | use Union, 422 | spec: [ 423 | switch: ManageOfferEffect, 424 | cases: [ 425 | {0, OfferEntry}, 426 | {1, OfferEntry}, 427 | {2, VOID} 428 | ] 429 | ] 430 | end 431 | end 432 | 433 | defmodule ManageOfferResult do 434 | use Union, 435 | spec: [ 436 | switch: ManageOfferResultCode, 437 | cases: [ 438 | {0, ManageOfferSuccessResult} 439 | ] 440 | ] 441 | end 442 | 443 | defmodule SetOptionsResultCode do 444 | use Enum, 445 | spec: [ 446 | SET_OPTIONS_SUCCESS: 0, 447 | SET_OPTIONS_LOW_RESERVE: -1, 448 | SET_OPTIONS_TOO_MANY_SIGNERS: -2, 449 | SET_OPTIONS_BAD_FLAGS: -3, 450 | SET_OPTIONS_INVALID_INFLATION: -4, 451 | SET_OPTIONS_CANT_CHANGE: -5, 452 | SET_OPTIONS_UNKNOWN_FLAG: -6, 453 | SET_OPTIONS_THRESHOLD_OUT_OF_RANGE: -7, 454 | SET_OPTIONS_BAD_SIGNER: -8, 455 | SET_OPTIONS_INVALID_HOME_DOMAIN: -9 456 | ] 457 | end 458 | 459 | defmodule SetOptionsResult do 460 | use Union, 461 | spec: [ 462 | switch: SetOptionsResultCode, 463 | cases: [ 464 | {0, VOID} 465 | ] 466 | ] 467 | end 468 | 469 | defmodule ChangeTrustResultCode do 470 | use Enum, 471 | spec: [ 472 | CHANGE_TRUST_SUCCESS: 0, 473 | CHANGE_TRUST_MALFORMED: -1, 474 | CHANGE_TRUST_NO_ISSUER: -2, 475 | CHANGE_TRUST_INVALID_LIMIT: -3, 476 | CHANGE_TRUST_LOW_RESERVE: -4, 477 | CHANGE_TRUST_SELF_NOT_ALLOWED: -5 478 | ] 479 | end 480 | 481 | defmodule ChangeTrustResult do 482 | use Union, 483 | spec: [ 484 | switch: ChangeTrustResultCode, 485 | cases: [ 486 | {0, VOID} 487 | ] 488 | ] 489 | end 490 | 491 | defmodule AllowTrustResultCode do 492 | use Enum, 493 | spec: [ 494 | ALLOW_TRUST_SUCCESS: 0, 495 | ALLOW_TRUST_MALFORMED: -1, 496 | ALLOW_TRUST_NO_TRUST_LINE: -2, 497 | ALLOW_TRUST_TRUST_NOT_REQUIRED: -3, 498 | ALLOW_TRUST_CANT_REVOKE: -4, 499 | ALLOW_TRUST_SELF_NOT_ALLOWED: -5 500 | ] 501 | end 502 | 503 | defmodule AllowTrustResult do 504 | use Union, 505 | spec: [ 506 | switch: AllowTrustResultCode, 507 | cases: [ 508 | {0, VOID} 509 | ] 510 | ] 511 | end 512 | 513 | defmodule AccountMergeResultCode do 514 | use Enum, 515 | spec: [ 516 | ACCOUNT_MERGE_SUCCESS: 0, 517 | ACCOUNT_MERGE_MALFORMED: -1, 518 | ACCOUNT_MERGE_NO_ACCOUNT: -2, 519 | ACCOUNT_MERGE_IMMUTABLE_SET: -3, 520 | ACCOUNT_MERGE_HAS_SUB_ENTRIES: -4 521 | ] 522 | end 523 | 524 | defmodule AccountMergeResult do 525 | use Union, 526 | spec: [ 527 | switch: AccountMergeResultCode, 528 | cases: [ 529 | {0, HyperInt}, 530 | {-1, VOID} 531 | ] 532 | ] 533 | end 534 | 535 | defmodule InflationResultCode do 536 | use Enum, 537 | spec: [ 538 | INFLATION_SUCCESS: 0, 539 | INFLATION_NOT_TIME: -1 540 | ] 541 | end 542 | 543 | defmodule InflationPayout do 544 | use XDR.Type.Struct, 545 | spec: [ 546 | destination: AccountID, 547 | amount: HyperInt 548 | ] 549 | end 550 | 551 | defmodule InflationResult do 552 | use Union, 553 | spec: [ 554 | switch: InflationResultCode, 555 | cases: [ 556 | {0, Payouts}, 557 | {-1, VOID} 558 | ] 559 | ] 560 | 561 | defmodule Payouts do 562 | use VariableArray, spec: [type: InflationPayout] 563 | end 564 | end 565 | 566 | defmodule ManageDataResultCode do 567 | use Enum, 568 | spec: [ 569 | MANAGE_DATA_SUCCESS: 0, 570 | MANAGE_DATA_NOT_SUPPORTED_YET: -1, 571 | MANAGE_DATA_NAME_NOT_FOUND: -2, 572 | MANAGE_DATA_LOW_RESERVE: -3, 573 | MANAGE_DATA_INVALID_NAME: -4 574 | ] 575 | end 576 | 577 | defmodule ManageDataResult do 578 | use Union, 579 | spec: [ 580 | switch: ManageDataResultCode, 581 | cases: [ 582 | {0, VOID} 583 | ] 584 | ] 585 | end 586 | 587 | defmodule OperationResultCode do 588 | use Enum, 589 | spec: [ 590 | opINNER: 0, 591 | opBAD_AUTH: -1, 592 | opNO_ACCOUNT: -2 593 | ] 594 | end 595 | 596 | defmodule OperationResult do 597 | use Union, 598 | spec: [ 599 | switch: OperationResultCode, 600 | cases: [ 601 | {0, OperationResultInner} 602 | ] 603 | ] 604 | 605 | defmodule OperationResultInner do 606 | use Union, 607 | spec: [ 608 | switch: OperationType, 609 | cases: [ 610 | {0, CreateAccountResult}, 611 | {1, PaymentResult}, 612 | {2, PathPaymentResult}, 613 | {3, ManageOfferResult}, 614 | {4, ManageOfferResult}, 615 | {5, SetOptionsResult}, 616 | {6, ChangeTrustResult}, 617 | {7, AllowTrustResult}, 618 | {8, AccountMergeResult}, 619 | {9, InflationResult}, 620 | {10, ManageDataResult} 621 | ] 622 | ] 623 | end 624 | end 625 | 626 | defmodule TransactionResultCode do 627 | use Enum, 628 | spec: [ 629 | txSUCCESS: 0, 630 | txFAILED: -1, 631 | txTOO_EARLY: -2, 632 | txTOO_LATE: -3, 633 | txMISSING_OPERATION: -4, 634 | txBAD_SEQ: -5, 635 | txBAD_AUTH: -6, 636 | txINSUFFICIENT_BALANCE: -7, 637 | txNO_ACCOUNT: -8, 638 | txINSUFFICIENT_FEE: -9, 639 | txBAD_AUTH_EXTRA: -10, 640 | txINTERNAL_ERROR: -11 641 | ] 642 | end 643 | 644 | defmodule TransactionResult do 645 | use XDR.Type.Struct, 646 | spec: [ 647 | feeCharged: HyperInt, 648 | result: TransactionResultResult, 649 | ext: Ext 650 | ] 651 | 652 | defmodule TransactionResultResult do 653 | use Union, 654 | spec: [ 655 | switch: TransactionResultCode, 656 | cases: [ 657 | {0, OperationResults}, 658 | {-1, OperationResults}, 659 | {-2, VOID} 660 | ] 661 | ] 662 | 663 | defmodule OperationResults do 664 | use VariableArray, spec: [type: OperationResult] 665 | end 666 | end 667 | end 668 | end 669 | -------------------------------------------------------------------------------- /lib/stellar/xdr/types/types.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.XDR.Types do 2 | alias XDR.Type.{FixedOpaque, Enum, Union, VariableOpaque} 3 | 4 | defmodule Hash do 5 | use FixedOpaque, len: 32 6 | end 7 | 8 | defmodule UInt256 do 9 | use FixedOpaque, len: 32 10 | end 11 | 12 | defmodule CryptoKeyType do 13 | use Enum, 14 | spec: [ 15 | KEY_TYPE_ED25519: 0, 16 | KEY_TYPE_PRE_AUTH_TX: 1, 17 | KEY_TYPE_HASH_X: 2 18 | ] 19 | end 20 | 21 | defmodule PublicKeyType do 22 | use Enum, 23 | spec: [ 24 | PUBLIC_KEY_TYPE_ED25519: 0 25 | ] 26 | end 27 | 28 | defmodule SignerKeyType do 29 | use Enum, 30 | spec: [ 31 | SIGNER_KEY_TYPE_ED25519: 0, 32 | SIGNER_KEY_TYPE_PRE_AUTH_TX: 1, 33 | SIGNER_KEY_TYPE_HASH_X: 2 34 | ] 35 | end 36 | 37 | defmodule PublicKey do 38 | use Union, 39 | spec: [ 40 | switch: PublicKeyType, 41 | cases: [ 42 | {0, UInt256} 43 | ] 44 | ] 45 | end 46 | 47 | defmodule SignerKey do 48 | use Union, 49 | spec: [ 50 | switch: SignerKeyType, 51 | cases: [ 52 | {0, UInt256}, 53 | {1, UInt256}, 54 | {2, UInt256} 55 | ] 56 | ] 57 | end 58 | 59 | defmodule Signature do 60 | use VariableOpaque, max_len: 64 61 | end 62 | 63 | defmodule SignatureHint do 64 | use FixedOpaque, len: 4 65 | end 66 | 67 | defmodule Key32 do 68 | use FixedOpaque, len: 32 69 | end 70 | 71 | defmodule Curve25519Secret do 72 | use XDR.Type.Struct, 73 | spec: [ 74 | key: Key32 75 | ] 76 | end 77 | 78 | defmodule Curve25519Public do 79 | use XDR.Type.Struct, 80 | spec: [ 81 | key: Key32 82 | ] 83 | end 84 | 85 | defmodule HmacSha256Key do 86 | use XDR.Type.Struct, 87 | spec: [ 88 | key: Key32 89 | ] 90 | end 91 | 92 | defmodule HmacSha256Mac do 93 | use XDR.Type.Struct, 94 | spec: [ 95 | mac: Key32 96 | ] 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :stellar, 7 | version: "0.3.1", 8 | description: description(), 9 | elixir: "~> 1.6", 10 | elixirc_paths: elixirc_paths(Mix.env()), 11 | start_permanent: Mix.env() == :prod, 12 | deps: deps(), 13 | package: package(), 14 | test_coverage: [tool: ExCoveralls], 15 | preferred_cli_env: [ 16 | coveralls: :test, 17 | "coveralls.detail": :test, 18 | "coveralls.post": :test, 19 | "coveralls.html": :test 20 | ], 21 | 22 | # Docs 23 | name: "Stellar", 24 | source_url: "https://github.com/revelrylabs/elixir-stellar-client", 25 | homepage_url: "https://github.com/revelrylabs/elixir-stellar-client", 26 | # The main page in the docs 27 | docs: [main: "Stellar", extras: ["README.md"]] 28 | ] 29 | end 30 | 31 | # Run "mix help compile.app" to learn about applications. 32 | def application do 33 | [ 34 | extra_applications: [:logger] 35 | ] 36 | end 37 | 38 | # Run "mix help deps" to learn about dependencies. 39 | defp deps do 40 | [ 41 | {:httpoison, "~> 1.0"}, 42 | {:jason, "~> 1.0"}, 43 | {:crc, "~> 0.10.1"}, 44 | {:ed25519, "~> 1.1"}, 45 | {:ex_doc, "~> 0.23.0", only: :dev}, 46 | {:bypass, "~> 2.1.0", only: :test}, 47 | {:excoveralls, "~> 0.10.3", only: :test}, 48 | {:xdr, "~> 0.1.1"} 49 | ] 50 | end 51 | 52 | defp elixirc_paths(:test), do: ["lib", "test/support"] 53 | defp elixirc_paths(_), do: ["lib"] 54 | 55 | defp description do 56 | """ 57 | Stellar API client for Elixir 58 | """ 59 | end 60 | 61 | defp package do 62 | [ 63 | files: ["lib", "mix.exs", "README.md", "LICENSE", "CHANGELOG.md"], 64 | maintainers: ["Bryan Joseph", "Luke Ledet"], 65 | licenses: ["MIT"], 66 | links: %{ 67 | "GitHub" => "https://github.com/revelrylabs/elixir-stellar-client" 68 | }, 69 | build_tools: ["mix"] 70 | ] 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, 3 | "certifi": {:hex, :certifi, "2.5.3", "70bdd7e7188c804f3a30ee0e7c99655bc35d8ac41c23e12325f36ab449b70651", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "ed516acb3929b101208a9d700062d520f3953da3b6b918d866106ffa980e1c10"}, 4 | "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"}, 5 | "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"}, 6 | "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"}, 7 | "crc": {:hex, :crc, "0.10.1", "87a0478e5a930926f1062397c9eb32d981f6b3abbc352d6d6fecf45a1725b249", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "ed3a9a673d4726fd12d6f58811f014e33cb3926b28f9639b12456ccb241d0f9b"}, 8 | "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm", "e3be2bc3ae67781db529b80aa7e7c49904a988596e2dbff897425b48b3581161"}, 9 | "earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"}, 10 | "ed25519": {:hex, :ed25519, "1.3.1", "35a363527d1c85c773eae8b5e2dfe133c693c270a20e281a7021937929da5c2e", [:mix], [], "hexpm", "e94d5899c68f8a4826cc796c12e4bb5fd8814bee092651a809137df17ef7d5d9"}, 11 | "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"}, 12 | "ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"}, 13 | "excoveralls": {:hex, :excoveralls, "0.10.6", "e2b9718c9d8e3ef90bc22278c3f76c850a9f9116faf4ebe9678063310742edc2", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b06c73492aa9940c4c29cfc1356bcf5540ae318f17b423749a0615a66ee3e049"}, 14 | "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, 15 | "hackney": {:hex, :hackney, "1.17.0", "717ea195fd2f898d9fe9f1ce0afcc2621a41ecfe137fae57e7fe6e9484b9aa99", [:rebar3], [{:certifi, "~>2.5", [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.3", [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", "64c22225f1ea8855f584720c0e5b3cd14095703af1c9fbc845ba042811dc671c"}, 16 | "httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"}, 17 | "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, 18 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, 19 | "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, 20 | "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, 21 | "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, 22 | "math": {:hex, :math, "0.3.0", "e14e7291115201cb155a3567e66d196bf5088a6f55b030d598107d7ae934a11c", [:mix], [], "hexpm", "5f2a14eeb8ed70b14c2dcd7c09e7e7b61aaa856d1fc0f07f53bf0f3586c561c1"}, 23 | "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, 24 | "mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"}, 25 | "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, 26 | "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, 27 | "ok": {:hex, :ok, "1.11.0", "0acbb73d98b5198db963ae710dad2e6efeed05149871a30e1eb08a91ae4f7042", [:mix], [], "hexpm", "726361bef6a75aae43006223a419ec8570c06e4da836930f27ed72ca210dc3c3"}, 28 | "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, 29 | "plug": {:hex, :plug, "1.11.0", "f17217525597628298998bc3baed9f8ea1fa3f1160aa9871aee6df47a6e4d38e", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d9c633f0499f9dc5c2fd069161af4e2e7756890b81adcbb2ceaa074e8308876"}, 30 | "plug_cowboy": {:hex, :plug_cowboy, "2.4.1", "779ba386c0915027f22e14a48919a9545714f849505fa15af2631a0d298abf0f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d72113b6dff7b37a7d9b2a5b68892808e3a9a752f2bf7e503240945385b70507"}, 31 | "plug_crypto": {:hex, :plug_crypto, "1.2.0", "1cb20793aa63a6c619dd18bb33d7a3aa94818e5fd39ad357051a67f26dfa2df6", [:mix], [], "hexpm", "a48b538ae8bf381ffac344520755f3007cc10bd8e90b240af98ea29b69683fc2"}, 32 | "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, 33 | "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, 34 | "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, 35 | "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, 36 | "xdr": {:hex, :xdr, "0.1.2", "6774f6b921267ab87c09d24e0a0cceca03503f6506f10b33321bf856df25ad0c", [:mix], [{:math, "~> 0.3.0", [hex: :math, repo: "hexpm", optional: false]}, {:ok, "~> 1.9", [hex: :ok, repo: "hexpm", optional: false]}], "hexpm", "c7417a9c4d88c87ea3e16baae0df92698a37c8f5615e3c4e58a177987fedafaa"}, 37 | } 38 | -------------------------------------------------------------------------------- /test/integration/accounts_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Accounts.IntegrationTest do 2 | use Stellar.IntegrationCase 3 | alias Stellar.Accounts 4 | 5 | test "get account details", %{public_key: public_key} do 6 | assert {:ok, %{"id" => ^public_key}} = Accounts.get(public_key) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/stellar/accounts_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Accounts.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Accounts 4 | 5 | test "get account details", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/accounts/123456", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"id": "123456"}>) 8 | end) 9 | 10 | assert {:ok, %{"id" => "123456"}} = Accounts.get("123456") 11 | end 12 | 13 | test "get account data", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/accounts/123456/data/user-id", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"value": "MTAw"}>) 16 | end) 17 | 18 | assert {:ok, %{"value" => "MTAw"}} = Accounts.get_data("123456", "user-id") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/stellar/assets_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Assets.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Assets 4 | 5 | test "get assets with no parameters", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/assets", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 8 | end) 9 | 10 | assert {:ok, %{"_embedded" => _}} = Assets.all() 11 | end 12 | 13 | test "get assets with parameters", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/assets", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 16 | end) 17 | 18 | assert {:ok, %{"_embedded" => _}} = 19 | Assets.all( 20 | asset_code: "USD", 21 | order: "desc" 22 | ) 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/stellar/base_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Base.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Base 4 | 5 | describe "get_network_url" do 6 | test "returns test network when :test specified" do 7 | assert Base.get_network_url(:test) == "https://horizon-testnet.stellar.org" 8 | end 9 | 10 | test "returns public network when :public specified" do 11 | assert Base.get_network_url(:public) == "https://horizon.stellar.org" 12 | end 13 | 14 | test "returns url when a string is specified" do 15 | assert Base.get_network_url("http://example.com") == "http://example.com" 16 | end 17 | end 18 | 19 | describe "process_query_params" do 20 | test "returns empty string when params is empty" do 21 | assert Base.process_query_params([]) == "" 22 | end 23 | 24 | test "returns query string when params is not empty" do 25 | assert Base.process_query_params(asset_code: "USD") == "?asset_code=USD" 26 | end 27 | end 28 | 29 | describe "general error responses" do 30 | test "Handles error response", %{bypass: bypass} do 31 | Bypass.expect_once(bypass, "GET", "/accounts/unknown_id", fn conn -> 32 | Plug.Conn.resp( 33 | conn, 34 | 404, 35 | ~s<{"status": 404, "type": "https://stellar.org/horizon-errors/not_found", "title": "Resource Missing"}> 36 | ) 37 | end) 38 | 39 | assert {:error, %{"status" => 404}} = Base.get("/accounts/unknown_id") 40 | end 41 | 42 | test "Handles http client error", %{bypass: bypass} do 43 | Bypass.expect_once(bypass, fn conn -> 44 | Plug.Conn.resp(conn, 200, "{}") 45 | end) 46 | 47 | assert {:ok, _} = Base.get("/accounts/unknown_id") 48 | 49 | Bypass.down(bypass) 50 | 51 | assert {:error, %{"detail" => _}} = Base.get("/accounts/unknown_id") 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/stellar/effects_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Effects.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Effects 4 | 5 | test "get effect details", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/effects/123456", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"id": "123456"}>) 8 | end) 9 | 10 | assert {:ok, %{"id" => "123456"}} = Effects.get("123456") 11 | end 12 | 13 | test "get all effects", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/effects", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 16 | end) 17 | 18 | assert {:ok, %{"_embedded" => _}} = Effects.all() 19 | end 20 | 21 | test "get all effects for an account", %{bypass: bypass} do 22 | Bypass.expect_once(bypass, "GET", "/accounts/123456/effects", fn conn -> 23 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 24 | end) 25 | 26 | assert {:ok, %{"_embedded" => _}} = Effects.all_for_account("123456") 27 | end 28 | 29 | test "get all effects for an operation", %{bypass: bypass} do 30 | Bypass.expect_once(bypass, "GET", "/operations/123456/effects", fn conn -> 31 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 32 | end) 33 | 34 | assert {:ok, %{"_embedded" => _}} = Effects.all_for_operation("123456") 35 | end 36 | 37 | test "get all effects for a ledger", %{bypass: bypass} do 38 | Bypass.expect_once(bypass, "GET", "/ledgers/123456/effects", fn conn -> 39 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 40 | end) 41 | 42 | assert {:ok, %{"_embedded" => _}} = Effects.all_for_ledger("123456") 43 | end 44 | 45 | test "get all effects for a transaction", %{bypass: bypass} do 46 | Bypass.expect_once(bypass, "GET", "/transactions/123456/effects", fn conn -> 47 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 48 | end) 49 | 50 | assert {:ok, %{"_embedded" => _}} = Effects.all_for_transaction("123456") 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /test/stellar/fee_stats_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.FeeStats.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.FeeStats 4 | 5 | test "get fee_stats", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/fee_stats", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"last_ledger": "22606298"}>) 8 | end) 9 | 10 | assert {:ok, %{"last_ledger" => _}} = FeeStats.get() 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/stellar/keypair_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.KeyPair.Test do 2 | use ExUnit.Case, async: true 3 | alias Stellar.KeyPair 4 | 5 | # testing example from https://www.stellar.org/developers/js-stellar-base/reference/building-transactions.html#class 6 | test "example works" do 7 | secret = "SBK2VIYYSVG76E7VC3QHYARNFLY2EAQXDHRC7BMXBBGIFG74ARPRMNQM" 8 | 9 | {public_key, ^secret} = 10 | KeyPair.from_secret("SBK2VIYYSVG76E7VC3QHYARNFLY2EAQXDHRC7BMXBBGIFG74ARPRMNQM") 11 | 12 | assert public_key == "GDHMW6QZOL73SHKG2JA3YHXFDHM46SS5ZRWEYF5BCYHX2C5TVO6KZBYL" 13 | end 14 | 15 | test "random" do 16 | assert {_public_key, _secret} = KeyPair.random() 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/stellar/ledgers_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Ledgers.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Ledgers 4 | 5 | test "get ledger details", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/ledgers/123456", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"id": "123456"}>) 8 | end) 9 | 10 | assert {:ok, %{"id" => "123456"}} = Ledgers.get("123456") 11 | end 12 | 13 | test "get all ledgers", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/ledgers", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 16 | end) 17 | 18 | assert {:ok, %{"_embedded" => _}} = Ledgers.all() 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/stellar/metrics_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Metrics.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Metrics 4 | 5 | test "get metrics", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/metrics", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"goroutines": {"value": 3193}}>) 8 | end) 9 | 10 | assert {:ok, %{"goroutines" => _}} = Metrics.get() 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/stellar/offers_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Offers.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Offers 4 | 5 | test "get all offers for an account", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/accounts/123456/offers", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 8 | end) 9 | 10 | assert {:ok, %{"_embedded" => _}} = Offers.all_for_account("123456") 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/stellar/operations_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Operations.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Operations 4 | 5 | test "get operation details", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/operations/123456", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"id": "123456"}>) 8 | end) 9 | 10 | assert {:ok, %{"id" => "123456"}} = Operations.get("123456") 11 | end 12 | 13 | test "get all operations", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/operations", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 16 | end) 17 | 18 | assert {:ok, %{"_embedded" => _}} = Operations.all() 19 | end 20 | 21 | test "get all operations for an account", %{bypass: bypass} do 22 | Bypass.expect_once(bypass, "GET", "/accounts/123456/operations", fn conn -> 23 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 24 | end) 25 | 26 | assert {:ok, %{"_embedded" => _}} = Operations.all_for_account("123456") 27 | end 28 | 29 | test "get all operations for a ledger", %{bypass: bypass} do 30 | Bypass.expect_once(bypass, "GET", "/ledgers/123456/operations", fn conn -> 31 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 32 | end) 33 | 34 | assert {:ok, %{"_embedded" => _}} = Operations.all_for_ledger("123456") 35 | end 36 | 37 | test "get all operations for a transaction", %{bypass: bypass} do 38 | Bypass.expect_once(bypass, "GET", "/transactions/123456/operations", fn conn -> 39 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 40 | end) 41 | 42 | assert {:ok, %{"_embedded" => _}} = Operations.all_for_transaction("123456") 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/stellar/order_books_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.OrderBooks.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.OrderBooks 4 | 5 | test "get order book details", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/order_book", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"bids": []}>) 8 | end) 9 | 10 | assert {:ok, %{"bids" => []}} = OrderBooks.get(:native, :native) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/stellar/payment_paths_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.PaymentPaths.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.PaymentPaths 4 | 5 | test "get all payment paths", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/paths", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 8 | end) 9 | 10 | assert {:ok, %{"_embedded" => _}} = 11 | PaymentPaths.all( 12 | "GAEDTJ4PPEFVW5XV2S7LUXBEHNQMX5Q2GM562RJGOQG7GVCE5H3HIB4V", 13 | 20.1, 14 | :native 15 | ) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/stellar/payments_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Payments.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Payments 4 | 5 | test "get all payments", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/payments", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 8 | end) 9 | 10 | assert {:ok, %{"_embedded" => _}} = Payments.all() 11 | end 12 | 13 | test "get all payments for an account", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/accounts/123456/payments", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 16 | end) 17 | 18 | assert {:ok, %{"_embedded" => _}} = Payments.all_for_account("123456") 19 | end 20 | 21 | test "get all payments for a ledger", %{bypass: bypass} do 22 | Bypass.expect_once(bypass, "GET", "/ledgers/123456/payments", fn conn -> 23 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 24 | end) 25 | 26 | assert {:ok, %{"_embedded" => _}} = Payments.all_for_ledger("123456") 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/stellar/str_key_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.StrKey.Test do 2 | use ExUnit.Case, async: true 3 | alias Stellar.{StrKey, KeyPair} 4 | 5 | test "encode_check! with nil data raises" do 6 | assert_raise ArgumentError, fn -> 7 | StrKey.encode_check!(:ed25519SecretSeed, nil) 8 | end 9 | end 10 | 11 | test "encode_check! with invalid version_byte_name raises" do 12 | data = 13 | <<85, 170, 163, 24, 149, 77, 255, 19, 245, 22, 224, 124, 2, 45, 42, 241, 162, 2, 23, 25, 14 | 226, 47, 133, 151, 8, 76, 130, 155, 252, 4, 95, 22>> 15 | 16 | assert_raise ArgumentError, fn -> 17 | StrKey.encode_check!(:something, data) 18 | end 19 | end 20 | 21 | test "decode_check! with unknown version_byte_name raises" do 22 | data = "SBK2VIYYSVG76E7VC3QHYARNFLY2EAQXDHRC7BMXBBGIFG74ARPRMNQM" 23 | 24 | assert_raise ArgumentError, fn -> 25 | StrKey.decode_check!(:something, data) 26 | end 27 | end 28 | 29 | test "decode_check! with version_byte_name not matching raises" do 30 | data = "SBK2VIYYSVG76E7VC3QHYARNFLY2EAQXDHRC7BMXBBGIFG74ARPRMNQM" 31 | 32 | assert_raise ArgumentError, fn -> 33 | StrKey.decode_check!(:ed25519PublicKey, data) 34 | end 35 | end 36 | 37 | test "decode_check! with checksum not matching raises" do 38 | data = "SBK2VIYYSVG76E7VC3QHYARNFLY2EAQXDHRC7BMXBBGIFG74ARPRMNMQ" 39 | 40 | assert_raise ArgumentError, fn -> 41 | StrKey.decode_check!(:ed25519SecretSeed, data) 42 | end 43 | end 44 | 45 | test "decode_check! to encode_check! and back" do 46 | Enum.each(1..10, fn _ -> 47 | {public, secret} = KeyPair.random() 48 | 49 | data = StrKey.decode_check!(:ed25519SecretSeed, secret) 50 | assert secret == StrKey.encode_check!(:ed25519SecretSeed, data) 51 | 52 | data = StrKey.decode_check!(:ed25519PublicKey, public) 53 | assert public == StrKey.encode_check!(:ed25519PublicKey, data) 54 | end) 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /test/stellar/trade_aggregations_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.TradeAggregations.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.TradeAggregations 4 | 5 | test "get all trade aggregrations", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/trade_aggregations", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 8 | end) 9 | 10 | assert {:ok, %{"_embedded" => _}} = 11 | TradeAggregations.all( 12 | :native, 13 | :native 14 | ) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/stellar/trades_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Trades.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Trades 4 | 5 | test "get all trades", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/trades", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 8 | end) 9 | 10 | assert {:ok, %{"_embedded" => _}} = Trades.all() 11 | end 12 | 13 | test "get all trades for an order book", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/order_book/trades", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 16 | end) 17 | 18 | assert {:ok, %{"_embedded" => _}} = Trades.all_for_order_book(:native, :native) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/stellar/transactions_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Stellar.Transactions.Test do 2 | use Stellar.HttpCase 3 | alias Stellar.Transactions 4 | 5 | test "get transaction details", %{bypass: bypass} do 6 | Bypass.expect_once(bypass, "GET", "/transactions/123456", fn conn -> 7 | Plug.Conn.resp(conn, 200, ~s<{"id": "123456"}>) 8 | end) 9 | 10 | assert {:ok, %{"id" => "123456"}} = Transactions.get("123456") 11 | end 12 | 13 | test "get all transactions", %{bypass: bypass} do 14 | Bypass.expect_once(bypass, "GET", "/transactions", fn conn -> 15 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 16 | end) 17 | 18 | assert {:ok, %{"_embedded" => _}} = Transactions.all() 19 | end 20 | 21 | test "get all transactions for an account", %{bypass: bypass} do 22 | Bypass.expect_once(bypass, "GET", "/accounts/123456/transactions", fn conn -> 23 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 24 | end) 25 | 26 | assert {:ok, %{"_embedded" => _}} = Transactions.all_for_account("123456") 27 | end 28 | 29 | test "get all transactions for a ledger", %{bypass: bypass} do 30 | Bypass.expect_once(bypass, "GET", "/ledgers/123456/transactions", fn conn -> 31 | Plug.Conn.resp(conn, 200, ~s<{"_embedded": { "records": [] }}>) 32 | end) 33 | 34 | assert {:ok, %{"_embedded" => _}} = Transactions.all_for_ledger("123456") 35 | end 36 | 37 | test "post a transaction", %{bypass: bypass} do 38 | Bypass.expect_once(bypass, "POST", "/transactions", fn conn -> 39 | Plug.Conn.resp(conn, 200, ~s<{"hash": ""}>) 40 | end) 41 | 42 | assert {:ok, %{"hash" => _}} = Transactions.post("my_xdr_envelope") 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/stellar_test.exs: -------------------------------------------------------------------------------- 1 | defmodule StellarTest do 2 | use ExUnit.Case, async: true 3 | doctest Stellar 4 | 5 | test "shows configured network" do 6 | Application.put_env(:stellar, :network, :test) 7 | assert Stellar.network() =~ "testnet" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/support/http_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.HttpCase do 2 | @moduledoc false 3 | 4 | use ExUnit.CaseTemplate 5 | 6 | using do 7 | quote do 8 | end 9 | end 10 | 11 | setup do 12 | bypass = Bypass.open() 13 | url = "http://localhost:#{bypass.port}" 14 | Application.put_env(:stellar, :network, url) 15 | {:ok, bypass: bypass} 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/support/integration_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Stellar.IntegrationCase do 2 | @moduledoc false 3 | 4 | use ExUnit.CaseTemplate 5 | 6 | using do 7 | quote do 8 | end 9 | end 10 | 11 | setup do 12 | Application.put_env(:stellar, :network, :test) 13 | test_account = Application.get_env(:stellar, :test_account) 14 | {:ok, test_account} 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | Application.ensure_all_started(:bypass) 3 | --------------------------------------------------------------------------------