11.10. Create a local currency (experimental)

This tutorial shows how to setup LibEuFin to act as the bank of a local currency. The main features include the registration and removal of user accounts (only) by the admin, and the possibility to convert the local currency into fiat (a.k.a. cashing out).

The banking capabilities are offered by a LibEuFin service called Sandbox. In particular, the tutorial relies on the Circuit API. More information about libEufin can be found in the How-To page.

The following sections show how to install and launch Sandbox either from sources, or with Docker.

11.10.1. Install and config from sources

First build LibEuFin.

If the installation succeeded, configure Sandbox with the following command.

$ export LIBEUFIN_SANDBOX_DB_CONNECTION=jdbc:sqlite:/tmp/libeufin.sqlite3
$ libeufin-sandbox config --currency NB --without-registrations default


The --without-registrations option allows only the administrator to add new accounts. Without this option, other APIs may offer unrestricted registrations.

If the configuration step succeeded, Sandbox should be ready to serve the bank for a currency named NB.

In following step, we launch Sandbox by setting the administrator password as secret.


The following command launches Sandbox so that it writes TANs on the filesystem to ease the cash-out operations without relying on an actual e-mail or SMS provider.

$ libeufin-sandbox serve --port 5016

If Sandbox is running, jump to this part.

11.10.2. Install and config with Docker

First, clone the deployment repository:

$ git clone git://git.taler.net/deployment

Then navigate to the Docker image location:

$ cd deployment/nlnet/task1

Now build the image with the following command.

$ docker build -t nlnet .

If the build step went well, the following command should suffice to start Sandbox and NGINX, by mapping the host’s 8080 port to the container’s 80.

$ docker run \
  -it nlnet

The previous command uses a default admin password of ‘admin’. Do CHANGE the admin password in a production scenario. The following command shows how to start the services with custom values.


Start the services this way to provide the environment suitable for this tutorial.

$ export MY_ADMIN_PASSWORD=secret
$ docker run \
  -v libeufin_data:/libeufin-data \
  -v /tmp:/tmp \
  -p 5016:5016 \
  -it nlnet

In the example above, Docker:

  1. Sets the admin password to secret
  2. Sets the currency to NB
  3. Stores the database in a volume. This helps to share the database between containers.
  4. Mounts container’s /tmp to the host’s, to let the reader obtain the file TAN in the same way the source-based installation does.
  5. Maps the host’s 8080 to the container’s 80 port.
  6. Maps the host’s 5016 to the container’s 5016 port. That lets the CLI reach Sandbox inside the container, and therefore run the tutorial.

By success, Web browsers get the UI by visiting http://localhost:8080

The following command shows how to delete the database, by deleting its volume.

$ docker volume rm libeufin_data

Note: the removal might fail because the exited containers are seen as still using the container. Please refer to the Docker documentation for further information.

11.10.3. If Sandbox is running

Sandbox should now be reachable on the port 5016. Check it with:

$ curl http://localhost:5016

If Sandbox is correctly running, it should respond with a greeting message. At this point, the administrator can add a new merchant to the bank with the following command.


Consult this document, to learn all the CLI commands that address the (Circuit) API used in this tutorial.

export LIBEUFIN_SANDBOX_URL=http://localhost:5016/

libeufin-cli \
  sandbox \
  demobank \
  circuit-register \
    --name "Circuit Shop" \
    --username circuit-shop \
    --cashout-address payto://iban/CH463312 \
    --internal-iban INT940993

If the previous step succeeded, the merchant should have access to their bank account with the circuit-shop username and shop-secret password.

Check it by asking the merchant balance with the following command. The two environment variables LIBEUFIN_SANDBOX_USERNAME and LIBEUFIN_SANDBOX_PASSWORD instruct the CLI to authenticate as the merchant.


libeufin-cli sandbox demobank info --bank-account circuit-shop

The expected response looks like the following one:

  "balance" : {
    "amount" : "NB:0",
    "credit_debit_indicator" : "credit"
  "paytoUri" : "payto://iban/SANDBOXX/INT940993?receiver-name=Circuit+Shop",
  "iban" : "INT940993"

In the following example, the merchant creates a cash-out operation, trying to convert 1 NB back to the fiat currency. The --amount-debit option takes the amount that the merchant wants to be debited in their local currency bank account, whereas the --amount-credit option is the calculation of the conversion rates as expected by the merchant. The two values will be checked by the bank along this request, to make sure that both parties agree.


The current version has a fixed 0.95 conversion rate for cashing out. Future version will make this value configurable.

libeufin-cli \
  sandbox \
  demobank \
  circuit-cashout \
    --amount-debit=NB:1 \
    --amount-credit=CHF:0.95 \

If the previous command succeeded, it returned a JSON looking like the following, although most likely having a different UUID.

  "uuid" : "de12389b-e477-4a0c-829e-f779c1cfb3a0"

The uuid represents the cash-out operation being just created and waiting to be confirmed with a TAN.


The current version provides only the local currency side of such operation. In other words, the merchant can now only see a deduction on their local currency bank account but no incoming payment in their fiat bank account. Future versions will implement this.

Confirm now the cash-out operation by sending to the bank the TAN found in the file /tmp/libeufin-cashout-tan.txt. Assuming that the TAN for it is WXYZ, the next command will confirm it to the bank (please, use your UUID).

libeufin-cli \
  sandbox demobank circuit-cashout-confirm \
    --uuid de12389b-e477-4a0c-829e-f779c1cfb3a0 \
    --tan WXYZ

Check now that the cash-out operation appears as a outgoing transaction for the merchant:

libeufin-cli \
  sandbox demobank list-transactions \
    --bank-account circuit-shop

The expected output should contain one line like the following. That witnesses that the cash-out was correctly confirmed and scheduled to be transferred to the fiat account.

"subject" : "Cash-out of NB:1 to CHF:0.95"

The next commands show how to delete one account from the local currency circuit. For the deletion to succeed, the account must have a balance of zero NB. Because of the cash-out operation, the merchant has now -1 NB to their account, therefore the deletion will fail. Check it, as the administrator, with the following command


libeufin-cli \
  sandbox demobank \
  circuit-delete-account --username circuit-shop

The expected output is:

Unexpected response status: 412
Response: {
  "error" : {
    "type" : "sandbox-error",
    "description" : "Account circuit-shop doesn't have zero balance.  Won't delete it"

The bank may now award 1 NB to the merchant to bring their balance to zero and finally delete the account. With the following command, the administrator awards 1 NB to the merchant.

libeufin-cli \
  sandbox demobank new-transaction \
    --bank-account admin \
    --payto-with-subject "payto://iban/SANDBOXX/INT940993?message=bring-to-zero" \
    --amount NB:1

Check if the balance returned to zero with this command, and try again to delete the account like already done

Now the deletion command should have succeeded! Try to ask the balance again, and expect one error like the following:

  "error" : {
    "type" : "util-error",
    "description" : "Customer 'circuit-shop' not found"


Every Circuit API endpoint is addressed by a CLI subcommand whose name starts with circuit-. The following command shows them.

libeufin-cli sandbox demobank | grep circuit

E-mail or SMS TAN setup

SMS and E-mail TANs get sent via commands that are invoked by the Sandbox. Sandbox learns about those commands via optional parameters to the serve command.


Future versions will allow setting the external commands via the configuration; follow #7527.

Hence, starting Sandbox as shown in the following commands is the only requirement to use the aforementioned TAN channels.

For the SMS TAN:

libeufin-sandbox serve --sms-tan sms_provider_command

Alternatively, for the e-mail TAN:

libeufin-sandbox serve --email-tan email_provider_command

Both commands will be passed the TAN to STDIN and the target phone number / e-mail address as their first command line argument.


The way the invoked commands interact with their providers is out of the Sandbox scope.

Finally, Libeufin ships two TAN commands as example. The e-mail command relies on GNU mail and the SMS command on the service offered by telesign.com. Check contrib/libeufin-tan-sms.sh and contrib/libeufin-tan-email.sh in the Libeufin repository.