FaRl Language

FaRl (Financial accounting Rule Language) est le langage de script embarqué dans Corren. Conçu pour exprimer les flux financiers clairement, avec sécurité, et validation à la compilation. Chaque script est atomique — si une instruction échoue, rien n'est committé.

Installation

Nécessite Go 1.16+. Buildez depuis les sources :

bash
# Cloner le dépôt
git clone https://github.com/amezianechayer/corren
cd corren

# Builder
go build -o corren .
sudo mv corren /usr/local/bin/

# Vérifier
corren --help

Quick Start

bash
# Terminal 1 — démarrer le serveur
corren server start

# Terminal 2 — exécuter un script
cat > script.farl << EOF
transfer [DZD.2 10000] (
  from @world
  to   @banque:reserve
)
EOF
corren run quickstart script.farl
200 {"ok":true}

# Vérifier le solde
curl http://localhost:3068/quickstart/accounts/@banque:reserve

Configuration

bash
corren config init

Génère le fichier de config à ~/.corren/corren.yaml :

yaml — ~/.corren/corren.yaml
server:
  http:
    bind_address: 127.0.0.1:3068
storage:
  driver: sqlite          # sqlite | postgres
  dir:    ~/.corren/data
  sqlite:
    db_name: corren
  postgres:
    conn_string: postgresql://localhost/postgres

CLI Reference

CommandeDescription
corren server startDémarre le serveur HTTP sur l'adresse configurée
corren run <ledger> <file>Compile et exécute un script FaRl contre un ledger
corren config initCrée le fichier de config par défaut
corren storage initInitialise le schéma de la base de données

Vue d'ensemble FaRl

Un script FaRl est une séquence d'instructions séparées par des sauts de ligne. Chaque instruction déplace de l'argent, affiche une valeur, ou définit des métadonnées. L'ensemble du script s'exécute comme une transaction atomique unique.

Sécurité à la compilation : Toutes les erreurs sont détectées avant l'exécution. Soldes insuffisants, incompatibilités de types, débordements d'allocation, et chaînes de sources invalides — détectés au compile. Jamais d'états partiels.

Types

TypeSyntaxeExemplesDescription
account@name@alice, @users:001Adresse de compte. Commence par @. Namespacé avec :
monetary[ASSET amount][DZD.2 5000], [COIN 100]Un montant spécifique d'un actif
assetASSETDZD, COIN, CREDITIdentifiant d'actif — majuscules uniquement
portionN/D ou N%85/100, 15%Fraction entre 0 et 1 exclusive
numberN42, 100Entier non signé
string"...""seller"Chaîne littérale. Utilisée dans les clés de metadata

Syntaxe monétaire

farl
# Avec précision — [ASSET.decimals montant]
[DZD.2 5000]     # Dinar algérien, 2 décimales
[EUR.2 10000]    # Euro, 2 décimales
[USD.2 10000]    # Dollar US, 2 décimales
[TND.3 5000]     # Dinar tunisien, 3 décimales

# Sans précision — [ASSET montant]
[COIN 100]        # Token COIN, pas de décimales

# Transférer tout le solde
[DZD.2 *]         # tout le solde DZD.2
Précision : Le numéro de précision (ex. .2 dans DZD.2) fait partie du nom de l'actif — il n'est pas utilisé pour l'arithmétique. DZD.2 et DZD sont deux actifs différents. Les montants sont toujours stockés comme des entiers.

Variables

Les variables sont déclarées en haut du script avant toute instruction. Elles sont injectées sous forme de JSON lors de l'exécution via l'API.

farl
# Déclaration simple
var $name: type

# Depuis les métadonnées
var $name: type = meta($account, "key")

Format JSON des variables

TypeFormat JSONExemple
accountstring"@alice"
monetaryobject{"asset":"DZD.2","amount":5000}
assetstring"DZD.2"
portionstring"15%" ou "3/20"
numberinteger42
stringstring"hello"

transfer

L'instruction transfer déplace un montant monétaire d'une source vers une destination.

farl — syntaxe
transfer <monetary | monetaryAll> (
  from <source>
  to   <destination>
)
Les clauses from et to peuvent être dans n'importe quel ordre. Corren suit les changements de solde dans le script — un transfer peut dépenser des fonds reçus par un transfer précédent dans le même script, même si le solde initial en base était 0.

Sources

Compte unique

farl
from @alice
from $vendeur    # variable

Cascade ordonnée

Draine chaque source dans l'ordre jusqu'à atteindre le montant cible :

farl
from {
  @wallet:ameziane    # drainé en premier
  @credit:ameziane    # puis ceci
  @world              # fallback illimité
}

Source capée (max)

farl
max [DZD.2 1000] from @alice

# Combinable avec cascade
from {
  max [DZD.2 500] from @alice
  @bob
}

Allotment source

farl
from {
  60%       from @alice
  35.5%     from @bob
  4.5%      from @charlie
}

Destinations

Compte unique

farl
to @bob
to $vendeur    # variable

Allotment destination

farl
to {
  85/100    to @vendeur:yanis
  10/100    to @plateforme:frais
  5/100     to @taxes
}

Avec remaining

farl
to {
  10%       to @plateforme
  5%        to @taxes
  remaining  to @vendeur    # reçoit 85%
}

Portion dynamique (variable)

farl
var $commission: portion

to {
  $commission  to @plateforme
  remaining    to @vendeur
}

Règles d'allotment

RègleValideInvalide
Portions fixes doivent sommer à 100% (sans remaining)50% + 50%50% + 30%
Un seul remaining par allotment20% + remainingremaining + remaining
Avec remaining, portions fixes < 100%80% + remaining100% + remaining
Portions variables nécessitent un remaining$p + remaining$p + 50%
Arrondi : Lors du fractionnement de montants non divisibles, Corren distribue le reste à la première portion. Le total est toujours exactement égal au montant original.

@world

@world est un compte intégré spécial représentant la création de monnaie. Il émet des fonds illimités sans vérification de solde — comme une banque centrale.

farl
# ✅ Émettre un montant fixe
transfer [DZD.2 10000] (
  from @world
  to   @banque:reserve
)

# ✅ Fallback dans une cascade
from {
  @wallet
  @world
}

# ❌ Impossible de transférer tout depuis @world
transfer [DZD.2 *] ( from @world  to @bob )

# ❌ Impossible d'utiliser @world dans allotment source
from { 50% from @world  50% from @alice }

Metadata (lecture)

Les variables peuvent être résolues depuis les métadonnées de compte à l'exécution via meta(account, "key") :

farl
var $sale:       account
var $seller:     account = meta($sale,   "seller")
var $commission: portion = meta($seller, "commission_rate")

transfer [DZD.2 1000] (
  from $sale
  to {
    $commission  to @plateforme
    remaining    to $seller
  }
)

set metadata

Métadonnées de transaction

farl
# Clé unique
set transaction metadata "reference" = "PAY-2026-001"
set transaction metadata "amount_dzd" = 5000

# Forme bloc
set transaction metadata {
  "reference" = "PAY-2026-001"
  "channel"   = "mobile"
  "region"    = "alger"
}

Métadonnées de compte

farl
set account metadata of @vendeur:yanis key "commission_rate" = 10/100
set account metadata of @vendeur:yanis key "tier"            = "gold"
farl
print 42
print 10 + 5 - 2
print @alice

# Abort immédiat — rollback tout
fail

Exemple — Marketplace split

marketplace.farl
transfer [DZD.2 5000] (
  from @client:ameziane
  to   @courses:0587:paiement
)

transfer [DZD.2 5000] (
  from @courses:0587:paiement
  to {
    90/100  to @chauffeur:yanis
    10/100  to @plateforme:frais
  }
)

set transaction metadata "reference" = "COURSE-0587"

Exemple — Source cascade

cascade.farl
transfer [DZD.2 3000] (
  from {
    @wallet:ameziane    # balance: 1000 → utilisé first
    @credit:ameziane    # balance: 2000 → utilisé next
    @world              # fallback illimité
  }
  to @commande:0089
)
# Résultat : 1000 wallet + 2000 credit = 3000

Exemple — Commission dynamique

commission.farl
var $sale:   account
var $seller: account = meta($sale,   "seller")
var $rate:   portion = meta($seller, "commission_rate")

transfer [DZD.2 10000] (
  from $sale
  to   {
    $rate      to @plateforme:commission
    remaining  to $seller
  }
)

Exemple — Allotment source

allotment.farl
transfer [DZD.2 1000] (
  from {
    60%  from @alice     # contribue 600
    35%  from @bob       # contribue 350
    5%   from @charlie   # contribue 50
  }
  to @destinataire
)

Exemple — Set metadata

metadata.farl
# Définir le taux de commission d'un vendeur
set account metadata of @vendeur:yanis key "commission_rate" = 12/100

# Utiliser dans un transfer
var $seller: account
var $rate:   portion = meta($seller, "commission_rate")

transfer [DZD.2 5000] (
  from @world
  to   {
    $rate     to @plateforme
    remaining to $seller
  }
)

HTTP API — Exécuter un script

bash
curl -X POST http://localhost:3068/quickstart/script \
  -H "Content-Type: application/json" \
  -d '{
    "plain": "transfer [DZD.2 1000] (\n  from @world\n  to @alice\n)",
    "vars": {}
  }'
ChampTypeRequisDescription
plainstringrequisLe contenu du script FaRl
varsobjectoptionnelValeurs des variables comme JSON

HTTP API — Transactions

MéthodeEndpointDescription
GET/:ledger/transactionsLister toutes les transactions. Params: after, account
POST/:ledger/transactionsCréer une transaction manuelle avec des postings explicites

HTTP API — Accounts

MéthodeEndpointDescription
GET/:ledger/accountsLister tous les comptes. Params: after
GET/:ledger/accounts/:addressObtenir le solde et infos d'un compte spécifique