README.md

# NumscriptEx
NumscriptEx is a library that allows its users to check and run Numscripts in Elixir. If this is your first time hearing about
Numscripts, here is a quick explanation:

[Numscript](https://docs.formance.com/numscript/) is a DSL made by [Formance](https://www.formance.com/)
that simplifies complex financial transactions with scripts that are easy to read,
so you don't need a big, complex and error-prone codebase to deal with your finances.

You can see and execute some examples at the [Numscript Playground](https://playground.numscript.org/?template=simple-send).

## Installation
You will just need to add `:numscriptex` as a dependency on your `mix.exs`, and run the `mix deps.get` command:

```elixir
def deps do
  [
    {:numscriptex, "~> 0.2.1"}
  ]
end
```
### Configuration
NumscriptEx needs some external assets ([Numscript-WASM](https://github.com/PagoPlus/numscript-wasm)),
and you can override the default version.

Available configurations:
- `:version` the binary release version to use (see [numscript-wasm releases](https://github.com/PagoPlus/numscript-wasm/releases/)).
- `:retries` number of times to retry the download in case of a network failure.

Ex:
```elixir
config :numscriptex,
       version: "0.0.2",
       retries: 3
```
These above are the default values.

## Usage
This library basically has two core functions: `Numscriptex.check/1` and `Numscriptex.run/2`.

Want to check if your script is valid and ready to go? Use the `check/1` function.
Already checked the script and want to execute it? Use the `run/2` function.

But before introducing these two functions, you will need to know what is the `Numscriptex.Run` struct.

### Numscriptex.Run
A numscript needs some other data aside the script itself to run correctly, and
`Numscriptex.Run` solves this problem.

If you want to know what exactly these additional data are, you can see the
[Numscript Playground](https://playground.numscript.org/?template=simple-send) for examples.

The abstraction is made by creating a struct:
```elixir
iex>  %Numscriptex.Run{
...>    balances: %{},
...>    metadata: %{},
...>    variables: %{}
...>  }
```
Where:
- `:balances` a map with the account's assets balances.
- `:metadata` [metada variables](https://docs.formance.com/numscript/reference/metadata);
- `:variables` [variables](https://docs.formance.com/numscript/reference/variables) used inside the script.

And to create a new struct, you can use the `Numscriptex.Run.put/3` or `Numscriptex.Run.put!/3` functions. Ex:
```elixir
iex>  variables = %{"order" => "orders:2345"}
...>  balances = %{"orders:2345" => %{"USD/2" => 1000}}
...>  metadata = %{
...>    "merchants:1234" => %{"commission" => "15%"},
...>    "orders:2345" => %{"merchant" => "merchants:1234"}
...>  }
...>
...>  Numscriptex.Run.new()
...>  |> Numscriptex.Run.put!(:balances, balances)
...>  |> Numscriptex.Run.put!(:metadata, metadata)
...>  |> Numscriptex.Run.put!(:variables, variables)
```

Will return:

```elixir
iex> %Numscriptex.Run{
...>   variables: %{"orders:2345" => %{"USD/2" => 1000}},
...>   balances: %{"order" => "orders:2345"},
...>   metadata: %{
...>     "merchants:1234" => %{"commission" => "15%"},
...>     "orders:2345" => %{"merchant" => "merchants:1234"}
...>   }
...> }
```

Kindly reminder: you will always need a valid `Numscriptex.Run` struct to successfully execute your scripts.

### Check
To use `Numscriptex.check/1` you just have to pass your numscript as it's argument. Ex:

```elixir
iex>  "tmp/script.num"
...>  |> File.read!()
...>  |> Numscriptex.check()
{:ok, %{script: script}
```

You don´t need to necessarily read from a file, as long as it is a string it's fine.

Sometimes, even if your script is valid, it could also return some warnings, infos or hints inside the map.
Ex:
```elixir
iex> {:ok, %{
...>     script: "your numscript here",
...>     warnings: [
...>             %CheckLog{
...>               character: 10,
...>               level: :warning,
...>               line: 1,
...>               message: "warning message"
...>             }
...>           ],
...>     hints: [
...>             %CheckLog{
...>               character: 2,
...>               level: :hint,
...>               line: 7,
...>               message: "hint message"
...>             }
...>           ],
...>     infos: [
...>             %CheckLog{
...>               character: 9,
...>               level: :info,
...>               line: 14,
...>               message: "info message"
...>             }
...>           ]
...>   }
...> }
```
The `:script` is the only field that will always return if your script is valid, the other three are optional.
### Run
To use `Numscriptex.run/2` your first argument must be your script (the same you used in `Numscriptex.check/1`), and the second must be the `%Numscriptex.Run{}` struct. Ex:

```elixir
iex>  Numscriptex.run(script, struct)
{:ok, result}
```

Where result will be something like this:
```elixir
iex> %{
...>   postings: [
...>           %{
...>             amount: 100,
...>             asset: "USD/2",
...>             destination: "bar",
...>             source: "foo"
...>           }
...>         ],
...>   balances: [
...>           %{
...>             account: "foo",
...>             asset: "EUR/2",
...>             final_balance: 300,
...>             initial_balance: 300
...>           },
...>           %{
...>             account: "foo",
...>             asset: "USD/2",
...>             final_balance: 400,
...>             initial_balance: 500
...>           },
...>           %{
...>             account: "bar",
...>             asset: "USD/2",
...>             final_balance: 100,
...>             initial_balance: 0
...>           }
...>         ],
...>   accountMeta: %{}
...>   txMeta: %{}
...> }
```

## License
Copyright (c) 2025 MedFlow

This library is MIT licensed. See the [LICENSE](https://github.com/PagoPlus/numscriptex/blob/main/README.md) for details.