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 is a DSL made by Formance 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.
Installation
You will just need to add :numscriptex as a dependency on your mix.exs, and run the mix deps.get command:
def deps do
[
{:numscriptex, "~> 0.2.3"}
]
endConfiguration
NumscriptEx needs some external assets (Numscript-WASM), and you can override the default version.
Available configurations:
:versionthe binary release version to use (see numscript-wasm releases).:retriesnumber of times to retry the download in case of a network failure.
Ex:
config :numscriptex,
version: "0.0.2",
retries: 3These above are the default values.
Usage
You can build Numscripts dynamically with Numscriptex.Builder.build/1, check if your script is valid with Numscriptex.check/1,
and last but not least, you can run your script with Numscriptex.run/2.
You can read more about the Numscriptex.Builder module and how to use it on its guide
And before introducing the other 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 for examples.
The abstraction is made by creating a struct:
iex> %Numscriptex.Run{
...> balances: %{},
...> metadata: %{},
...> variables: %{}
...> }Where:
:balancesa map with the account's assets balances.:metadatametada variables;:variablesvariables 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:
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:
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:
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:
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:
iex> Numscriptex.run(script, struct)
{:ok, result}Where result will be something like this:
iex> %{
...> postings: [
...> %Numscriptex.Posting{
...> amount: 100,
...> decimal_amount: 1.0,
...> asset: "USD/2",
...> destination: "bar",
...> source: "foo"
...> }
...> ],
...> balances: [
...> %Numscriptex.Balance{
...> account: "foo",
...> asset: "EUR/2",
...> final_balance: 300,
...> initial_balance: 300,
...> decimal_final_balance: 3.0,
...> decimal_initial_balance: 3.0,
...> },
...> %Numscriptex.Balance{
...> account: "foo",
...> asset: "USD/2",
...> final_balance: 400,
...> initial_balance: 500,
...> decimal_final_balance: 4.0,
...> decimal_initial_balance: 5.0,
...> },
...> %Numscriptex.Balance{
...> account: "bar",
...> asset: "USD/2",
...> final_balance: 100,
...> initial_balance: 0,
...> decimal_final_balance: 1.0,
...> decimal_initial_balance: 0.0,
...> }
...> ],
...> accountMeta: %{}
...> txMeta: %{}
...> }License
Copyright (c) 2025 MedFlow
This library is MIT licensed. See the LICENSE for details.