diff --git a/.credo.exs b/.credo.exs index 253f1d9..8ae5556 100644 --- a/.credo.exs +++ b/.credo.exs @@ -9,7 +9,8 @@ checks: [ {Credo.Check.Readability.AliasOrder, false}, {Credo.Check.Readability.Specs, []}, - {Credo.Check.Refactor.FunctionArity, max_arity: 10} + {Credo.Check.Refactor.FunctionArity, max_arity: 10}, + {Credo.Check.Consistency.SpaceAroundOperators, false} ] } ] diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index be5f2ba..c5cc3bd 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -11,11 +11,11 @@ permissions: # added using https://github.com/step-security/secure-workflows jobs: publish: name: Publish Release to HEX PM - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: - otp: ['23.3'] - elixir: ['1.12'] + otp: ['27.2'] + elixir: ['1.17'] env: HEX_API_KEY: ${{ secrets.HEX_API_KEY }} steps: @@ -31,11 +31,11 @@ jobs: builds.hex.pm:443 - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - uses: erlef/setup-elixir@904c511e63061a8f501ae4b081ce2c0e9cfc70ab # v1.17.4 + - uses: erlef/setup-beam@v1 with: otp-version: ${{ matrix.otp }} elixir-version: ${{ matrix.elixir }} - - uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1 + - uses: actions/cache@v4 with: path: deps key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 871827c..11ceb31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,11 +13,12 @@ permissions: jobs: tests: name: Run tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: - otp: ['23.3', '24.3'] - elixir: ['1.12', '1.13'] + include: + - otp: '26.2' + elixir: '1.16' env: MIX_ENV: test steps: @@ -32,11 +33,11 @@ jobs: *.hex.pm:443 - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - uses: erlef/setup-beam@904c511e63061a8f501ae4b081ce2c0e9cfc70ab # v1.17.4 + - uses: erlef/setup-beam@v1 with: otp-version: ${{ matrix.otp }} elixir-version: ${{ matrix.elixir }} - - uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1 + - uses: actions/cache@v4 with: path: deps key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }} @@ -59,7 +60,7 @@ jobs: finish: needs: tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Harden Runner uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b60af..f3cca44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.23.0 (02.06.2026) + +- Support Protocol 22, 23, and 26 features. +- Upgrade `stellar_base` dependency to `~> 0.17.0`. +- Add `CreateContractArgsV2` for Smart Contract Constructor support (CAP-0058). +- Update `HostFunction` to support `:create_contract_v2` host function type. + ## 0.22.0 (16.08.2024) - Add hash to handle transaction timeout response. See [PR #374](https://github.com/kommitters/stellar_sdk/pull/374) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..249c378 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,118 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Commands + +### Core Development Tasks +- **Install dependencies**: `mix deps.get` +- **Run tests**: `mix test` +- **Run single test**: `mix test test/path/to/test_file.exs` +- **Run tests with specific pattern**: `mix test --grep "pattern"` +- **Format code**: `mix format` +- **Lint code**: `mix credo` (with strict mode enabled) +- **Type checking**: `mix dialyzer` (Dialyxir for static analysis) +- **Generate docs**: `mix docs` +- **Check test coverage**: `mix coveralls` or `mix coveralls.html` + +### Mix Project Configuration +- Elixir version requirement: `~> 1.12` +- Test coverage tool: ExCoveralls +- Code linting: Credo (strict mode, max function arity of 10) +- Static analysis: Dialyxir +- Documentation: ExDoc + +## Code Architecture + +### High-Level Structure +This is the **Elixir Stellar SDK**, a comprehensive library for interacting with the Stellar network. The codebase is organized around two main components: + +1. **`TxBuild`** (`lib/tx_build/`) - Transaction construction and signing +2. **`Horizon`** (`lib/horizon/`) - HTTP client for Horizon API interactions + +### Core Modules + +#### Transaction Building (`lib/tx_build/`) +- **`Stellar.TxBuild`** - Main transaction builder API with behavior pattern +- **`Stellar.TxBuild.Default`** - Default implementation of transaction building +- **Operations** - Individual operation types (Payment, CreateAccount, etc.) +- **Components** - Account, Memo, Signature, Preconditions, TimeBounds, etc. +- Uses behavior pattern for pluggable implementations via `:tx_build_impl` config + +#### Horizon Client (`lib/horizon/`) +- **Resource modules** - Accounts, Transactions, Operations, Ledgers, etc. +- **Client abstraction** - HTTP client interface with `:http_client_impl` config +- **Server configuration** - Public, testnet, futurenet, and custom endpoints +- **Collection handling** - Pagination support with HAL format links +- **Error handling** - Structured error responses and mapping + +#### Utilities +- **`Stellar.KeyPair`** - Key generation and management (Ed25519) +- **`Stellar.Network`** - Network passphrases (testnet, public, etc.) + +### Key Design Patterns + +#### Behavior Pattern +The codebase uses Elixir behaviors extensively: +- `Stellar.TxBuild.Spec` - Defines transaction building interface +- `Stellar.KeyPair.Spec` - Defines key pair operations interface +- Implementation swappable via application configuration + +#### Configuration-Based Implementation Injection +- `:tx_build_impl` - Transaction building implementation (defaults to `Stellar.TxBuild.Default`) +- `:http_client_impl` - HTTP client implementation (defaults to hackney) +- `:hackney_opts` - HTTP client options + +#### Resource-Collection Pattern +Horizon resources follow a consistent pattern: +- Individual resource retrieval (e.g., `retrieve/2`) +- Collection listing with filtering (e.g., `all/2`) +- Related resource listing (e.g., `list_transactions/3`) +- Pagination handled via HAL links converted to functions + +## Important Development Notes + +### Network Configuration +- Default network is **testnet** (`Stellar.Network.testnet_passphrase/0`) +- Production requires explicit **public network** configuration +- Support for futurenet and custom networks + +### Protocol Version Support +The SDK supports Stellar Protocol versions 18-21 with specific version requirements: +- Protocol 20: requires SDK v0.20+ +- Protocol 21: requires SDK v0.21.2+ + +### Key Dependencies +- **stellar_base**: Core Stellar primitives (~> 0.16) +- **hackney**: Default HTTP client (~> 1.17) +- **jason**: JSON encoding/decoding (~> 1.0) +- **ed25519**: Cryptographic operations (~> 1.3) + +### Testing Structure +- Tests mirror the `lib/` structure exactly +- Heavy use of mocking for HTTP interactions +- Comprehensive coverage of transaction building scenarios +- Integration tests for Horizon client functionality + +### Code Quality Standards +- **Credo**: Strict mode enabled, function arity limited to 10 +- **Formatter**: Standard Elixir formatting rules +- **Dialyxir**: Static type analysis for better code quality +- **ExCoveralls**: Test coverage reporting and tracking + +### Stellar-Specific Concepts +When working with this codebase, understand these Stellar concepts: +- **Muxed accounts**: Virtual accounts under a real account with 64-bit ID +- **Operations**: Atomic units of change (payments, offers, account creation) +- **Preconditions**: Time bounds, ledger bounds, sequence number constraints +- **Signatures**: Ed25519, hash(x), and signed payload signature types +- **Transaction envelopes**: Signed transaction containers for network submission + +## Recommended Workflow + +1. **Transaction Building**: Start with `Stellar.TxBuild.new/2`, add operations, set sequence numbers, sign, and generate envelope +2. **Horizon Queries**: Use appropriate resource modules (`Accounts`, `Transactions`, etc.) with server configuration +3. **Testing**: Write tests in corresponding test directory structure, mock HTTP calls for Horizon interactions +4. **Documentation**: Follow existing documentation patterns, especially for public APIs + +This SDK is production-ready with extensive test coverage and follows Stellar network best practices. \ No newline at end of file diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..3c7e4b7 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,46 @@ +# Gemini Code Assistant Context + +## Project Overview + +This project is the Elixir Stellar SDK, a library for interacting with the Stellar network. It allows developers to build, sign, and encode Stellar transactions and operations, as well as query the Horizon API for ledger information. + +The SDK is composed of two main components: + +* **TxBuild**: For constructing Stellar transactions. It provides a comprehensive API to create and customize transactions, including adding operations, memos, preconditions, and signatures. +* **Horizon**: A client for the Horizon API, which allows developers to query for information about accounts, transactions, operations, ledgers, and more. + +The project is built with Elixir and uses `mix` for dependency management and running tasks. + +## Building and Running + +### Dependencies + +Dependencies are managed with `mix` and are listed in the `mix.exs` file. Key dependencies include: + +* `stellar_base`: For Stellar XDR data structures. +* `ed25519`: For cryptographic signing. +* `hackney`: As the default HTTP client. +* `jason`: For JSON parsing. + +### Key Commands + +* **Install dependencies:** + ```bash + mix deps.get + ``` +* **Run tests:** + ```bash + mix test + ``` +* **Format code:** + ```bash + mix format + ``` + +## Development Conventions + +* **Testing**: The project has a comprehensive test suite in the `test` directory. All new contributions should include corresponding tests. +* **Formatting**: Code should be formatted using `mix format`. +* **Branching**: Development should be done on topic branches, not directly on the `main` branch. +* **Commits**: Commit messages should be descriptive and follow the guidelines in the `CONTRIBUTING.md` file. +* **Pull Requests**: Pull requests should be focused on a single issue and should be rebased against the `main` branch. diff --git a/README.md b/README.md index 5713dd1..283bd75 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This library is aimed at developers building Elixir applications that interact w | 19 | >= v0.9 | | 20 | >= v0.20 | | 21 | >= v0.21.2 | +| 22/23/26 | >= v0.23.0 | ## Documentation The **Stellar SDK** is composed of two complementary components: **`TxBuild`** + **`Horizon`**. @@ -36,7 +37,7 @@ The **Stellar SDK** is composed of two complementary components: **`TxBuild`** + ```elixir def deps do [ - {:stellar_sdk, "~> 0.22.0"} + {:stellar_sdk, "~> 0.23.0"} ] end ``` diff --git a/lib/tx_build/create_contract_args_v2.ex b/lib/tx_build/create_contract_args_v2.ex new file mode 100644 index 0000000..8e2c6f5 --- /dev/null +++ b/lib/tx_build/create_contract_args_v2.ex @@ -0,0 +1,97 @@ +defmodule Stellar.TxBuild.CreateContractArgsV2 do + @moduledoc """ + `CreateContractArgsV2` struct definition for Protocol 22+. + """ + alias StellarBase.XDR.{CreateContractArgsV2, SCValList} + alias Stellar.TxBuild.{ContractExecutable, ContractIDPreimage, SCVal} + + @behaviour Stellar.TxBuild.XDR + + @type contract_id_preimage :: ContractIDPreimage.t() + @type contract_executable :: ContractExecutable.t() + @type constructor_args :: list(SCVal.t()) + @type error :: {:error, atom()} + @type validation :: {:ok, any()} | error() + + @type t :: %__MODULE__{ + contract_id_preimage: contract_id_preimage(), + contract_executable: contract_executable(), + constructor_args: constructor_args() + } + + defstruct [:contract_id_preimage, :contract_executable, :constructor_args] + + @impl true + def new(args, opts \\ []) + + def new(args, _opts) when is_list(args) do + contract_id_preimage = Keyword.get(args, :contract_id_preimage) + contract_executable = Keyword.get(args, :contract_executable) + constructor_args = Keyword.get(args, :constructor_args, []) + + with {:ok, contract_id_preimage} <- + validate_contract_id_preimage(contract_id_preimage), + {:ok, contract_executable} <- validate_contract_executable(contract_executable), + {:ok, constructor_args} <- validate_constructor_args(constructor_args) do + %__MODULE__{ + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: constructor_args + } + end + end + + def new(_value, _opts), do: {:error, :invalid_args} + + @impl true + def to_xdr(%__MODULE__{ + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: constructor_args + }) do + contract_executable_xdr = ContractExecutable.to_xdr(contract_executable) + contract_id_preimage_xdr = ContractIDPreimage.to_xdr(contract_id_preimage) + + constructor_args_xdr = + constructor_args + |> Enum.map(&SCVal.to_xdr/1) + |> SCValList.new() + + CreateContractArgsV2.new( + contract_id_preimage_xdr, + contract_executable_xdr, + constructor_args_xdr + ) + end + + def to_xdr(_struct), do: {:error, :invalid_struct} + + @spec validate_contract_id_preimage(contract_id_preimage :: contract_id_preimage()) :: + validation() + defp validate_contract_id_preimage(%ContractIDPreimage{} = contract_id_preimage), + do: {:ok, contract_id_preimage} + + defp validate_contract_id_preimage(_contract_id_preimage), + do: {:error, :invalid_contract_id_preimage} + + @spec validate_contract_executable(contract_executable :: contract_executable()) :: + validation() + defp validate_contract_executable(%ContractExecutable{} = contract_executable), + do: {:ok, contract_executable} + + defp validate_contract_executable(_contract_executable), + do: {:error, :invalid_contract_executable} + + @spec validate_constructor_args(constructor_args :: constructor_args()) :: validation() + defp validate_constructor_args(constructor_args) when is_list(constructor_args) do + if Enum.all?(constructor_args, &is_sc_val?/1), + do: {:ok, constructor_args}, + else: {:error, :invalid_constructor_args} + end + + defp validate_constructor_args(_constructor_args), do: {:error, :invalid_constructor_args} + + @spec is_sc_val?(value :: any()) :: boolean() + defp is_sc_val?(%SCVal{}), do: true + defp is_sc_val?(_), do: false +end diff --git a/lib/tx_build/host_function.ex b/lib/tx_build/host_function.ex index e29da5c..f91df9b 100644 --- a/lib/tx_build/host_function.ex +++ b/lib/tx_build/host_function.ex @@ -2,18 +2,20 @@ defmodule Stellar.TxBuild.HostFunction do @moduledoc """ `HostFunction` struct definition. """ - alias Stellar.TxBuild.{CreateContractArgs, InvokeContractArgs} + alias Stellar.TxBuild.{CreateContractArgs, CreateContractArgsV2, InvokeContractArgs} alias StellarBase.XDR.{HostFunction, HostFunctionType, VariableOpaque} @behaviour Stellar.TxBuild.XDR - @type value :: CreateContractArgs.t() | InvokeContractArgs.t() | binary() + @type value :: + CreateContractArgs.t() | CreateContractArgsV2.t() | InvokeContractArgs.t() | binary() @type error :: {:error, atom()} @type validation :: {:ok, any()} | error() @type type :: :invoke_contract | :create_contract | :upload_contract_wasm + | :create_contract_v2 @type t :: %__MODULE__{ type: type(), value: value() @@ -21,7 +23,7 @@ defmodule Stellar.TxBuild.HostFunction do defstruct [:type, :value] - @allowed_types ~w(invoke_contract create_contract upload_contract_wasm)a + @allowed_types ~w(invoke_contract create_contract upload_contract_wasm create_contract_v2)a @impl true def new(args, opts \\ []) @@ -60,6 +62,17 @@ defmodule Stellar.TxBuild.HostFunction do |> HostFunction.new(type) end + def to_xdr(%__MODULE__{ + type: :create_contract_v2, + value: value + }) do + type = HostFunctionType.new(:HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2) + + value + |> CreateContractArgsV2.to_xdr() + |> HostFunction.new(type) + end + def to_xdr(%__MODULE__{ type: :upload_contract_wasm, value: value @@ -75,6 +88,9 @@ defmodule Stellar.TxBuild.HostFunction do defp validate_host_function({:invoke_contract, %InvokeContractArgs{} = value}), do: {:ok, value} defp validate_host_function({:create_contract, %CreateContractArgs{} = value}), do: {:ok, value} + defp validate_host_function({:create_contract_v2, %CreateContractArgsV2{} = value}), + do: {:ok, value} + defp validate_host_function({:upload_contract_wasm, value}) when is_binary(value), do: {:ok, value} diff --git a/mix.exs b/mix.exs index d2365ce..96ac310 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Stellar.MixProject do use Mix.Project @github_url "https://github.com/kommitters/stellar_sdk" - @version "0.22.0" + @version "0.23.0" def project do [ @@ -37,13 +37,13 @@ defmodule Stellar.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:stellar_base, "~> 0.16"}, + {:stellar_base, "~> 0.17.0"}, {:ed25519, "~> 1.3"}, {:hackney, "~> 1.17"}, {:jason, "~> 1.0"}, {:dialyxir, "~> 1.0", only: [:dev], runtime: false}, {:excoveralls, "~> 0.18", only: :test}, - {:credo, "~> 1.5", only: [:dev, :test], runtime: false}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:ex_doc, "~> 0.31", only: :dev, runtime: false} ] end diff --git a/mix.lock b/mix.lock index 56c868a..b209756 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,13 @@ %{ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, - "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, + "castore": {:hex, :castore, "1.0.19", "6903cabdfd9d1af46454126e7c8385186659dd33ecfb74a885cae52221ad6109", [:mix], [], "hexpm", "3669e6cab13f54c2df26b3e6833745d647f35b6e30d8ddd5975df0d5c842ca98"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, - "crc": {:hex, :crc, "0.10.5", "ee12a7c056ac498ef2ea985ecdc9fa53c1bfb4e53a484d9f17ff94803707dfd8", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3e673b6495a9525c5c641585af1accba59a1eb33de697bedf341e247012c2c7f"}, + "crc": {:hex, :crc, "0.10.6", "a52243715da06265399ade929b12e6807a82ddbd04231d8bd3069480aa890f01", [:mix, :rebar3], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9e832833d48a5fff03cb7488f8aa5c08adda0a5fa8188bbe124cb17c4e39a00d"}, "credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"}, "dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ed25519": {:hex, :ed25519, "1.4.1", "479fb83c3e31987c9cad780e6aeb8f2015fb5a482618cdf2a825c9aff809afc4", [:mix], [], "hexpm", "0dacb84f3faa3d8148e81019ca35f9d8dcee13232c32c9db5c2fb8ff48c80ec7"}, - "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, + "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "elixir_xdr": {:hex, :elixir_xdr, "0.3.11", "baa3c21753232ee8547c5e96502cecd80b63c0f06694e51c43f3ed5b8d5b5b5a", [:mix], [], "hexpm", "d24de398aa75ed8c5fb6f2121a322e6e91f5bce72d791be47dd4197a11e79b51"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.31.1", "8a2355ac42b1cc7b2379da9e40243f2670143721dd50748bf6c3b1184dae2089", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3178c3a407c557d8343479e1ff117a96fd31bafe52a039079593fb0524ef61b0"}, @@ -24,6 +24,6 @@ "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "stellar_base": {:hex, :stellar_base, "0.16.0", "f94beff40b7fc3f7f56a06b2b2d92ae87bbc55baa1480e705490a688586d2a44", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:crc, "~> 0.10.0", [hex: :crc, repo: "hexpm", optional: false]}, {:elixir_xdr, "~> 0.3", [hex: :elixir_xdr, repo: "hexpm", optional: false]}], "hexpm", "d8a9a2dd0cb7b832fec3489d1442c6525a710eeb1a8222cb5371e4ace4ccc400"}, + "stellar_base": {:hex, :stellar_base, "0.17.0", "12647613e96e341f59a071e4abc2b9b4b0ba0fadb3e4a9e85dc1d1945fbf89a1", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:crc, "~> 0.10.0", [hex: :crc, repo: "hexpm", optional: false]}, {:elixir_xdr, "~> 0.3", [hex: :elixir_xdr, repo: "hexpm", optional: false]}], "hexpm", "64ca2ab5aac2c52bc1884be91d513a38d032f2fe8bf3eb8916f1d153812be01a"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, } diff --git a/test/horizon/request_test.exs b/test/horizon/request_test.exs index 935b409..9f88666 100644 --- a/test/horizon/request_test.exs +++ b/test/horizon/request_test.exs @@ -75,7 +75,11 @@ defmodule Stellar.Horizon.RequestTest do headers: [], query: [] } = - Request.new(server, :get, endpoint, path: hash, segment: segment, segment_path: segment_path) + Request.new(server, :get, endpoint, + path: hash, + segment: segment, + segment_path: segment_path + ) end test "add_body/2", %{server: server, endpoint: endpoint, body: body} do diff --git a/test/tx_build/create_contract_args_v2_test.exs b/test/tx_build/create_contract_args_v2_test.exs new file mode 100644 index 0000000..b46bd58 --- /dev/null +++ b/test/tx_build/create_contract_args_v2_test.exs @@ -0,0 +1,129 @@ +defmodule Stellar.TxBuild.CreateContractArgsV2Test do + use ExUnit.Case + + alias Stellar.TxBuild.{ + Asset, + CreateContractArgsV2, + ContractIDPreimage, + ContractExecutable, + SCVal + } + + alias StellarBase.XDR.CreateContractArgsV2, as: CreateContractArgsV2XDR + + setup do + asset = Asset.new(:native) + contract_executable = ContractExecutable.new(:stellar_asset) + contract_id_preimage = ContractIDPreimage.new(from_asset: asset) + constructor_args = [SCVal.new(i32: 123)] + + %{ + asset: asset, + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: constructor_args + } + end + + test "new/1", %{ + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: constructor_args + } do + %CreateContractArgsV2{ + contract_id_preimage: ^contract_id_preimage, + contract_executable: ^contract_executable, + constructor_args: ^constructor_args + } = + CreateContractArgsV2.new( + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: constructor_args + ) + end + + test "new/1 with invalid ContractIDPreimage", %{ + contract_executable: contract_executable, + constructor_args: constructor_args + } do + {:error, :invalid_contract_id_preimage} = + CreateContractArgsV2.new( + contract_id_preimage: "invalid", + contract_executable: contract_executable, + constructor_args: constructor_args + ) + end + + test "new/1 with invalid ContractExecutable", %{ + contract_id_preimage: contract_id_preimage, + constructor_args: constructor_args + } do + {:error, :invalid_contract_executable} = + CreateContractArgsV2.new( + contract_executable: "invalid", + contract_id_preimage: contract_id_preimage, + constructor_args: constructor_args + ) + end + + test "new/1 with invalid ConstructorArgs", %{ + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable + } do + {:error, :invalid_constructor_args} = + CreateContractArgsV2.new( + contract_executable: contract_executable, + contract_id_preimage: contract_id_preimage, + constructor_args: "invalid" + ) + + {:error, :invalid_constructor_args} = + CreateContractArgsV2.new( + contract_executable: contract_executable, + contract_id_preimage: contract_id_preimage, + constructor_args: [123] + ) + end + + test "to_xdr/1", %{ + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: constructor_args + } do + %CreateContractArgsV2XDR{ + contract_id_preimage: %StellarBase.XDR.ContractIDPreimage{ + value: %StellarBase.XDR.Asset{ + asset: %StellarBase.XDR.Void{value: nil}, + type: %StellarBase.XDR.AssetType{identifier: :ASSET_TYPE_NATIVE} + }, + type: %StellarBase.XDR.ContractIDPreimageType{ + identifier: :CONTRACT_ID_PREIMAGE_FROM_ASSET + } + }, + executable: %StellarBase.XDR.ContractExecutable{ + value: %StellarBase.XDR.Void{value: nil}, + type: %StellarBase.XDR.ContractExecutableType{ + identifier: :CONTRACT_EXECUTABLE_STELLAR_ASSET + } + }, + constructor_args: %StellarBase.XDR.SCValList{ + items: [ + %StellarBase.XDR.SCVal{ + type: %StellarBase.XDR.SCValType{identifier: :SCV_I32}, + value: %StellarBase.XDR.Int32{datum: 123} + } + ] + } + } = + CreateContractArgsV2.new( + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: constructor_args + ) + |> CreateContractArgsV2.to_xdr() + end + + test "to_xdr/1 with the struct is invalid" do + {:error, :invalid_struct} = CreateContractArgsV2.to_xdr("invalid_struct") + end +end diff --git a/test/tx_build/host_function_test.exs b/test/tx_build/host_function_test.exs index 6f33288..7c1a737 100644 --- a/test/tx_build/host_function_test.exs +++ b/test/tx_build/host_function_test.exs @@ -6,6 +6,7 @@ defmodule Stellar.TxBuild.HostFunctionTest do ContractIDPreimage, ContractIDPreimageFromAddress, CreateContractArgs, + CreateContractArgsV2, HostFunction, InvokeContractArgs, SCAddress, @@ -51,6 +52,13 @@ defmodule Stellar.TxBuild.HostFunctionTest do contract_executable: contract_executable ) + create_contract_v2_args = + CreateContractArgsV2.new( + contract_id_preimage: contract_id_preimage, + contract_executable: contract_executable, + constructor_args: [SCVal.new(i32: 123)] + ) + # :upload_contract_wasm code = <<0, 97, 115, 109, 1, 0, 0, 0, 1, 19, 4, 96, 1, 126, 1, 126, 96, 2, 126, 126, 1, 126, 96, @@ -66,6 +74,9 @@ defmodule Stellar.TxBuild.HostFunctionTest do create_contract_args: create_contract_args, create_contract_function: HostFunction.new(create_contract: create_contract_args), create_contract_xdr: host_function_xdr(:create_contract, create_contract_args), + create_contract_v2_args: create_contract_v2_args, + create_contract_v2_function: + HostFunction.new(create_contract_v2: create_contract_v2_args), upload_contract_wasm_args: upload_contract_wasm_args, upload_contract_wasm_function: HostFunction.new(upload_contract_wasm: upload_contract_wasm_args), @@ -101,6 +112,15 @@ defmodule Stellar.TxBuild.HostFunctionTest do } = HostFunction.new(upload_contract_wasm: upload_contract_wasm_args) end + test "new/2 create_contract_v2", %{ + create_contract_v2_args: create_contract_v2_args + } do + %HostFunction{ + type: :create_contract_v2, + value: ^create_contract_v2_args + } = HostFunction.new(create_contract_v2: create_contract_v2_args) + end + test "new/2 invalid attributes" do {:error, :invalid_operation_attributes} = HostFunction.new(:invalid) end @@ -123,6 +143,17 @@ defmodule Stellar.TxBuild.HostFunctionTest do ^create_contract_xdr = HostFunction.to_xdr(create_contract_function) end + test "to_xdr/1 create_contract_v2", %{ + create_contract_v2_function: create_contract_v2_function + } do + %StellarBase.XDR.HostFunction{ + type: %StellarBase.XDR.HostFunctionType{ + identifier: :HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2 + }, + value: %StellarBase.XDR.CreateContractArgsV2{} + } = HostFunction.to_xdr(create_contract_v2_function) + end + test "to_xdr/1 upload_contract_wasm", %{ upload_contract_wasm_xdr: upload_contract_wasm_xdr, upload_contract_wasm_function: upload_contract_wasm_function