Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 MedFlow

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
322 changes: 322 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
# Numscript WASM CLI

This project is a WebAssembly (WASM) port of the [formancehq/numscript](https://github.com/formancehq/numscript) CLI, enabling you to run Numscript execution in any WASM-compatible environment. Originally forked from the official repository, it now provides a standalone WASM binary that maintains full compatibility with the original CLI functionality.

## What is Numscript?

Numscript is a Domain-Specific Language (DSL) designed to help you model complex financial transactions, replacing complex and error-prone custom code with easy-to-read, declarative scripts. Originally developed by Formance, Numscript is used to express financial transactions within the Formance ledger system.

The language provides:
- **Declarative syntax** for modeling financial operations
- **Built-in validation** for transaction integrity
- **Account management** with metadata support
- **Variable substitution** for dynamic scripts
- **Feature flags** for experimental functionality

You can try Numscript online at [playground.numscript.org](https://playground.numscript.org/).

## Installation

### Prerequisites

You'll need a WebAssembly (WASM) runtime to execute the numscript-wasm binary. This guide uses Wasmtime, but you can also use other WASM runtimes like Wasmer.

**Install Wasmtime:**
```bash
curl https://wasmtime.dev/install.sh -sSf | bash
```

### Download the Binary

Download the WASM binary from the [latest release](https://github.com/PagoPlus/numscript-wasm/releases/latest) for your platform.

## Usage

The CLI provides two commands: `version` and `run`.

### Version Command

Display the current version of the CLI:

```bash
wasmtime numscript.wasm version
```

**Output:**
```bash
# If you are using the released binary this will print the real app version:
v0.1.0
# Otherwise will print dev:
dev
```

### Run Command

Execute a Numscript with the provided input data. The command reads JSON input from stdin containing the script, variables, account metadata, balances, and feature flags.

#### Input Format

The input JSON must follow this structure:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `script` | string | ✅ | The Numscript code to execute |
| `variables` | object | ❌ | [Variables](https://docs.formance.com/modules/numscript/reference/variables) |
| `metadata` | object | ❌ | [Account metadata](https://docs.formance.com/modules/numscript/reference/metadata) |
| `balances` | object | ✅ | Current account balances. [Example below](#balances). |
| `featureFlags` | object | ❌ | Experimental features to enable (empty objects as values). [Example below](#featureflags) |

##### balances
Is an object containing the account name and their respective assets and balances. Ex:
```json
"balances": {
"foo": {
"USD/2": 200,
"EUR/2": 100
},
"bar": {
"BRL/2": 200,
"JPY/2": 100
}
},
```
###### Asset Format
Assets use the format `CURRENCY/PRECISION`:
- `USD/2` = US Dollars with 2 decimal places
- `EUR/2` = Euros with 2 decimal places
- `BTC/8` = Bitcoin with 8 decimal places

##### featureFlags
Is an object containing the name of the feature flag you want to use with an empty object as their value. Ex:
```json
"featureFlags": {
"experimental-oneof": {},
"experimental-get-amount-function": {},
"experimental-mid-script-function-call": {}
}
```

Numscript does not have an official list of feature flags, but you can see the [source code](https://github.com/formancehq/numscript/blob/v0.0.17/internal/flags/flags.go) and the documentation with the [experimental features usage](https://github.com/formancehq/numscript/blob/main/differences-with-machine.md#new-functionalities-feature-flags)

#### Examples

##### Basic Transfer
Simple account-to-account transfer:

1. **Create an input file** (`example.json`):

```json
{
"script": "send [USD/2 100] (\n source = @foo\n destination = @bar\n)",
"balances": {
"foo": {
"USD/2": 200,
"EUR/2": 100
}
},
"variables": {},
"metadata": {},
"featureFlags": {}
}
```

2. **Execute the script**:

```bash
wasmtime numscript.wasm run < example.json
```

3. **Expected output** (JSON format):

```json
{
"postings": [
{
"source": "foo",
"destination": "bar",
"asset": "USD/2",
"amount": 100
}
],
"txMeta": {},
"accountsMeta": {}
}
```

#### Using variables

1. **Input File:** (`example.json`):
```json
{
"script": "vars { account $user monetary $fee portion $tax } send $fee (source = $user destination = { $tax to @platform:tax remaining to @platform:revenue })",
"balances": {
"users:1234": {
"USD/2": 10000
}
},
"variables": {
"user": "users:1234",
"fee": "USD/2 100",
"tax": "20%"
},
"metadata": {},
"featureFlags": {
"experimental-oneof": {},
"experimental-get-amount-function": {},
"experimental-mid-script-function-call": {}
}
}
```

2. **Execute the script**:
```bash
wasmtime numscript.wasm run < example.json
```

3. **Expected output** (JSON format):
```json
{
"postings": [
{
"source": "users:1234",
"destination": "platform:tax",
"amount": 20,
"asset": "USD/2"
},
{
"source": "users:1234",
"destination": "platform:revenue",
"amount": 80,
"asset": "USD/2"
}
],
"txMeta": {},
"accountsMeta": {}
}
```


#### Using metadata

1. **Input File:** (`example.json`):
```json
{
"script": "vars { account $order account $merchant = meta($order, \"merchant\") portion $commission = meta($merchant, \"commission\") } send [USD/2 *] ( source = $order destination = { $commission to @platform:fees remaining to $merchant })",
"balances": {
"orders:2345": {
"USD/2": 1000
}
},
"variables": {
"order": "orders:2345"
},
"metadata": {
"orders:2345": {
"merchant": "merchants:1234"
},
"merchants:1234": {
"commission": "15%"
}
},
"featureFlags": {
"experimental-oneof": {},
"experimental-get-amount-function": {},
"experimental-mid-script-function-call": {}
}
}
```

2. **Execute the script**:
```bash
wasmtime numscript.wasm run < example.json
```

3. **Expected output** (JSON format):
```json
{
"postings": [
{
"source": "orders:2345",
"destination": "platform:fees",
"amount": 150,
"asset": "USD/2"
},
{
"source": "orders:2345",
"destination": "merchants:1234",
"amount": 850,
"asset": "USD/2"
}
],
"txMeta": {},
"accountsMeta": {}
}
```

#### Allowing experimental features

If you want to use experimental features, you need to enable them by adding their respective feature flag:

1. **Input File:** (`example.json`):
```json
{
"script": "vars { monetary $mon = [USD/2 100] number $n = get_amount($mon) } send [USD/2 $n] (source = oneof { @foo @bar } destination = @baz)",
"balances": {
"foo": {
"USD/2": 99
},
"bar": {
"USD/2": 100
}
},
"variables": {},
"metadata": {},
"featureFlags": {
"experimental-oneof": {},
"experimental-get-amount-function": {},
"experimental-mid-script-function-call": {}
}
}
```

2. **Execute the script**:
```bash
wasmtime numscript.wasm run < example.json
```

3. **Expected output** (JSON format):
```json
{
"postings": [
{
"source": "bar",
"destination": "baz",
"amount": 100,
"asset": "USD/2"
}
],
"txMeta": {},
"accountsMeta": {}
}
```

## Error Handling

The CLI will exit with status code 1 and write error messages to stderr if:

- The input JSON is malformed
- The Numscript has parsing errors
- The execution fails due to insufficient balances or other runtime errors

Example error output:
```
Exception: Not enough funds. Needed [USD/2 100] (only [USD/2 99] available)
```

## License

This project maintains the same license as the original [formancehq/numscript](https://github.com/formancehq/numscript) repository.

## Contributing

This is a fork focused on WASM compatibility. For core Numscript language contributions, please consider contributing to the upstream [formancehq/numscript](https://github.com/formancehq/numscript) repository.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module numscript
module numscript-wasm

go 1.23.0

Expand Down
Loading