docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled

- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands
- CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround
- CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check
- NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere
- MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates
- LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-12 15:46:57 -08:00
parent cc8dcaf356
commit fbda1b4beb
5114 changed files with 498901 additions and 4567 deletions

32
scripts/omnl/README.md Normal file
View File

@@ -0,0 +1,32 @@
# OMNL Fineract scripts
Scripts for the **OMNL** tenancy ([omnl.hybxfinance.io](https://omnl.hybxfinance.io/)). Load env from `omnl-fineract/.env` or repo root `.env` (see [OMNL_FINERACT_CONFIGURATION.md](../../docs/04-configuration/OMNL_FINERACT_CONFIGURATION.md)).
| Script | Purpose |
|--------|---------|
| **omnl-gl-accounts-create.sh** | Create the five migration GL accounts (1000, 1050, 2000, 2100, 3000) via `POST /glaccounts`. Idempotent (skips if exists). Run **before** ledger post. |
| **omnl-discovery.sh** | GET offices, clients, savings/FD/RD products and accounts; output JSON. Set `OUT_DIR=<dir>` to write files. |
| **omnl-ledger-post.sh** | Post ledger allocation entries T-001, T-001B, T-002AT-008 per [LEDGER_ALLOCATION_POSTING_RUNBOOK.md](../../docs/04-configuration/mifos-omnl-central-bank/LEDGER_ALLOCATION_POSTING_RUNBOOK.md). Resolves GL account IDs from `GET /glaccounts`. Set `DRY_RUN=1` to print payloads only; `TRANSACTION_DATE=yyyy-MM-dd`, `OFFICE_ID=1` optional. |
| **omnl-deposit-one.sh** | Post a single deposit to an existing savings account. `ACCOUNT_ID=<id> AMOUNT=<number> [DATE=yyyy-MM-dd]`. Use discovery output for account IDs; for bulk, loop over a CSV or discovery JSON. |
**Run from repo root:**
```bash
# 1. Create GL accounts (run first; idempotent)
bash scripts/omnl/omnl-gl-accounts-create.sh
# 2. Post ledger entries (T-001T-008)
bash scripts/omnl/omnl-ledger-post.sh
# Discovery (list products, clients, accounts)
bash scripts/omnl/omnl-discovery.sh
OUT_DIR=./output/omnl-discovery bash scripts/omnl/omnl-discovery.sh
# Ledger dry run (print payloads only)
DRY_RUN=1 bash scripts/omnl/omnl-ledger-post.sh
# Single deposit (ACCOUNT_ID from discovery)
ACCOUNT_ID=1 AMOUNT=100 DATE=2026-02-10 bash scripts/omnl/omnl-deposit-one.sh
```
**Requirements:** `curl`, `jq` (for ledger posting and pretty-print in discovery).

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Post a single deposit to an existing savings account in OMNL Fineract.
# Usage: ACCOUNT_ID=<id> AMOUNT=<number> [DATE=yyyy-MM-dd] bash scripts/omnl/omnl-deposit-one.sh
# Get ACCOUNT_ID from: bash scripts/omnl/omnl-discovery.sh (savingsaccounts) or OUT_DIR output.
set -euo pipefail
REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
DATE="${DATE:-$(date +%Y-%m-%d)}"
if [ -f "${REPO_ROOT}/omnl-fineract/.env" ]; then
set +u
source "${REPO_ROOT}/omnl-fineract/.env" 2>/dev/null || true
set -u
elif [ -f "${REPO_ROOT}/.env" ]; then
set +u
source "${REPO_ROOT}/.env" 2>/dev/null || true
set -u
fi
BASE_URL="${OMNL_FINERACT_BASE_URL:-}"
TENANT="${OMNL_FINERACT_TENANT:-omnl}"
USER="${OMNL_FINERACT_USER:-app.omnl}"
PASS="${OMNL_FINERACT_PASSWORD:-}"
ACCOUNT_ID="${ACCOUNT_ID:-}"
AMOUNT="${AMOUNT:-}"
if [ -z "$BASE_URL" ] || [ -z "$PASS" ]; then
echo "Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD (e.g. omnl-fineract/.env)" >&2
exit 1
fi
if [ -z "$ACCOUNT_ID" ] || [ -z "$AMOUNT" ]; then
echo "Usage: ACCOUNT_ID=<savingsAccountId> AMOUNT=<number> [DATE=yyyy-MM-dd] $0" >&2
echo "Get ACCOUNT_ID from: bash scripts/omnl/omnl-discovery.sh (see savingsaccounts)" >&2
exit 1
fi
AUTH="${USER}:${PASS}"
BODY=$(jq -n \
--arg date "$DATE" \
--argjson amount "$AMOUNT" \
'{ transactionDate: $date, transactionAmount: $amount, dateFormat: "yyyy-MM-dd", locale: "en", paymentTypeId: 1, note: "Deposit via omnl-deposit-one.sh" }')
out=$(curl -s -S -w "\n%{http_code}" -X POST \
-H "Fineract-Platform-TenantId: ${TENANT}" -H "Content-Type: application/json" -u "$AUTH" \
-d "$BODY" "${BASE_URL}/savingsaccounts/${ACCOUNT_ID}/transactions?command=deposit")
code=$(echo "$out" | tail -n1)
resp=$(echo "$out" | sed '$d')
if [ "$code" = "200" ] || [ "${code:0:1}" = "2" ]; then
echo "OK Deposit $AMOUNT to account $ACCOUNT_ID (HTTP $code)"
else
echo "FAIL HTTP $code: $resp" >&2
exit 1
fi

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# OMNL Fineract — Discovery: offices, clients, savings/FD/RD products and accounts.
# Usage: run from repo root; sources omnl-fineract/.env or .env. Outputs JSON to stdout (or set OUT_DIR to write files).
# Requires: curl, jq (optional, for pretty-print).
set -euo pipefail
REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
OUT_DIR="${OUT_DIR:-}"
# Load OMNL env: prefer omnl-fineract/.env then root .env
if [ -f "${REPO_ROOT}/omnl-fineract/.env" ]; then
set +u
source "${REPO_ROOT}/omnl-fineract/.env" 2>/dev/null || true
set -u
elif [ -f "${REPO_ROOT}/.env" ]; then
set +u
source "${REPO_ROOT}/.env" 2>/dev/null || true
set -u
fi
BASE_URL="${OMNL_FINERACT_BASE_URL:-}"
TENANT="${OMNL_FINERACT_TENANT:-omnl}"
USER="${OMNL_FINERACT_USER:-app.omnl}"
PASS="${OMNL_FINERACT_PASSWORD:-}"
if [ -z "$BASE_URL" ] || [ -z "$PASS" ]; then
echo "Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD (e.g. in omnl-fineract/.env)" >&2
exit 1
fi
AUTH="${USER}:${PASS}"
CURL_OPTS=(-s -S -H "Fineract-Platform-TenantId: ${TENANT}" -H "Content-Type: application/json" -u "$AUTH")
api_get() {
curl "${CURL_OPTS[@]}" "${BASE_URL}/${1}"
}
maybe_save() {
local name="$1"
local body="$2"
if [ -n "$OUT_DIR" ] && [ -d "$OUT_DIR" ]; then
echo "$body" > "${OUT_DIR}/${name}.json"
fi
echo "$body"
}
echo "=== Offices ===" >&2
OFFICES=$(api_get "offices")
maybe_save "offices" "$OFFICES"
echo "$OFFICES" | jq '.' 2>/dev/null || echo "$OFFICES"
echo "=== Clients ===" >&2
CLIENTS=$(api_get "clients")
maybe_save "clients" "$CLIENTS"
echo "$CLIENTS" | jq '.' 2>/dev/null || echo "$CLIENTS"
echo "=== Savings products ===" >&2
SAVINGS_PRODUCTS=$(api_get "savingsproducts")
maybe_save "savingsproducts" "$SAVINGS_PRODUCTS"
echo "$SAVINGS_PRODUCTS" | jq '.' 2>/dev/null || echo "$SAVINGS_PRODUCTS"
echo "=== Savings accounts ===" >&2
SAVINGS_ACCOUNTS=$(api_get "savingsaccounts")
maybe_save "savingsaccounts" "$SAVINGS_ACCOUNTS"
echo "$SAVINGS_ACCOUNTS" | jq '.' 2>/dev/null || echo "$SAVINGS_ACCOUNTS"
echo "=== Fixed deposit products ===" >&2
FD_PRODUCTS=$(api_get "fixeddepositproducts")
maybe_save "fixeddepositproducts" "$FD_PRODUCTS"
echo "$FD_PRODUCTS" | jq '.' 2>/dev/null || echo "$FD_PRODUCTS"
echo "=== Recurring deposit products ===" >&2
RD_PRODUCTS=$(api_get "recurringdepositproducts")
maybe_save "recurringdepositproducts" "$RD_PRODUCTS"
echo "$RD_PRODUCTS" | jq '.' 2>/dev/null || echo "$RD_PRODUCTS"
echo "Discovery done. Use OUT_DIR=<dir> to write JSON files." >&2

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# Create the five migration GL accounts (1000, 1050, 2000, 2100, 3000) in OMNL Fineract via API.
# Idempotent: skips if glCode already exists. Run from repo root. Requires: curl, jq.
set -euo pipefail
REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
if [ -f "${REPO_ROOT}/omnl-fineract/.env" ]; then
set +u
source "${REPO_ROOT}/omnl-fineract/.env" 2>/dev/null || true
set -u
elif [ -f "${REPO_ROOT}/.env" ]; then
set +u
source "${REPO_ROOT}/.env" 2>/dev/null || true
set -u
fi
BASE_URL="${OMNL_FINERACT_BASE_URL:-}"
TENANT="${OMNL_FINERACT_TENANT:-omnl}"
USER="${OMNL_FINERACT_USER:-app.omnl}"
PASS="${OMNL_FINERACT_PASSWORD:-}"
if [ -z "$BASE_URL" ] || [ -z "$PASS" ]; then
echo "Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD (e.g. omnl-fineract/.env)" >&2
exit 1
fi
AUTH="${USER}:${PASS}"
CURL_OPTS=(-s -S -w "\n%{http_code}" -H "Fineract-Platform-TenantId: ${TENANT}" -H "Content-Type: application/json" -u "$AUTH")
api_get() {
curl "${CURL_OPTS[@]}" "${BASE_URL}/${1}"
}
api_post() {
local path="$1"
local body="$2"
local out
out=$(curl "${CURL_OPTS[@]}" -X POST -d "$body" "${BASE_URL}/${path}")
HTTP_CODE=$(echo "$out" | tail -n1)
echo "$out" | sed '$d'
}
# type: 1=ASSET, 2=LIABILITY, 3=EQUITY. usage: 1=DETAIL
create_gl() {
local gl_code="$1"
local name="$2"
local type_id="$3"
local description="$4"
local list
list=$(api_get "glaccounts" 2>/dev/null | sed '$d')
if echo "$list" | jq -e --arg c "$gl_code" '.[] | select(.glCode == $c) | .id' >/dev/null 2>&1; then
echo " [skip] $gl_code$name (exists)"
return 0
fi
# Handle both array and object response
if echo "$list" | grep -q "\"glCode\":\"${gl_code}\""; then
echo " [skip] $gl_code$name (exists)"
return 0
fi
local body
body=$(jq -n \
--arg code "$gl_code" \
--arg name "$name" \
--argjson type "$type_id" \
--arg desc "$description" \
'{glCode: $code, name: $name, type: $type, usage: 1, manualEntriesAllowed: true, description: $desc}')
api_post "glaccounts" "$body" >/dev/null
if [ "${HTTP_CODE:-0}" = "200" ] || [ "${HTTP_CODE:0:1}" = "2" ]; then
echo " [created] $gl_code$name"
else
echo " [fail] $gl_code HTTP ${HTTP_CODE}" >&2
return 1
fi
}
echo "=== OMNL Fineract — Create migration GL accounts ==="
echo "Base URL: $BASE_URL"
echo ""
create_gl "1000" "USD Settlement Reserves" 1 "USD Settlement & Reserve Assets"
create_gl "1050" "USD Treasury Conversion Reserve (M0)" 1 "Treasury Conversion Reserve (M0); backs M1 capacity at 1:5"
create_gl "2000" "USD Central Deposits" 2 "Central bank customer deposits; M1-denominated claims backed by 1050 where applicable"
create_gl "2100" "USD Restricted Liabilities" 2 "Restricted / held deposits"
create_gl "3000" "Opening Balance Control" 3 "Migration control account"
echo ""
echo "Done. Run scripts/omnl/omnl-ledger-post.sh to post T-001T-008."

View File

@@ -0,0 +1,116 @@
#!/usr/bin/env bash
# OMNL Fineract — Post ledger allocation entries (T-001, T-001B, T-002AT-008) per LEDGER_ALLOCATION_POSTING_RUNBOOK.
# Prerequisites: GL accounts 1000, 1050, 2000, 2100 exist; officeId known (e.g. 1). Uses glCode to resolve GL account IDs via GET /glaccounts.
# Usage: run from repo root. Set DRY_RUN=1 to print payloads only. Optional: TRANSACTION_DATE=yyyy-MM-dd (default today).
set -euo pipefail
REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
DRY_RUN="${DRY_RUN:-0}"
TRANSACTION_DATE="${TRANSACTION_DATE:-$(date +%Y-%m-%d)}"
OFFICE_ID="${OFFICE_ID:-1}"
if [ -f "${REPO_ROOT}/omnl-fineract/.env" ]; then
set +u
source "${REPO_ROOT}/omnl-fineract/.env" 2>/dev/null || true
set -u
elif [ -f "${REPO_ROOT}/.env" ]; then
set +u
source "${REPO_ROOT}/.env" 2>/dev/null || true
set -u
fi
BASE_URL="${OMNL_FINERACT_BASE_URL:-}"
TENANT="${OMNL_FINERACT_TENANT:-omnl}"
USER="${OMNL_FINERACT_USER:-app.omnl}"
PASS="${OMNL_FINERACT_PASSWORD:-}"
if [ -z "$BASE_URL" ] || [ -z "$PASS" ]; then
echo "Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD (e.g. omnl-fineract/.env)" >&2
exit 1
fi
AUTH="${USER}:${PASS}"
CURL_OPTS=(-s -S -w "\n%{http_code}" -H "Fineract-Platform-TenantId: ${TENANT}" -H "Content-Type: application/json" -u "$AUTH")
# Resolve glCode -> id from GET /glaccounts
get_gl_id() {
local code="$1"
local accs="$2"
echo "$accs" | jq -r --arg c "$code" '.[] | select(.glCode == $c) | .id // empty' 2>/dev/null || true
}
GL_ACCOUNTS=$(curl "${CURL_OPTS[@]}" "${BASE_URL}/glaccounts" 2>/dev/null | sed '$d')
# If no jq or empty, try without jq (grep/sed)
ID_1000=$(get_gl_id "1000" "$GL_ACCOUNTS")
ID_1050=$(get_gl_id "1050" "$GL_ACCOUNTS")
ID_2000=$(get_gl_id "2000" "$GL_ACCOUNTS")
ID_2100=$(get_gl_id "2100" "$GL_ACCOUNTS")
if [ -z "$ID_1000" ] || [ -z "$ID_2000" ]; then
if [ "$DRY_RUN" = "1" ]; then
echo "GL accounts not found; using placeholder IDs (1,2) for dry-run output only." >&2
ID_1000="${ID_1000:-1}"
ID_1050="${ID_1050:-1}"
ID_2000="${ID_2000:-2}"
ID_2100="${ID_2100:-2}"
else
echo "Could not resolve GL account IDs (1000, 2000). Create GL accounts first (see docs/04-configuration/mifos-omnl-central-bank/LEDGER_ALLOCATION_GL_MAPPING.md and POSTING_RUNBOOK)." >&2
echo "Test: curl -s -u \"\$OMNL_FINERACT_USER:***\" -H 'Fineract-Platform-TenantId: omnl' ${BASE_URL}/glaccounts" >&2
exit 1
fi
fi
# Build journal entry payload. Fineract 1.x style: officeId, transactionDate, comments, credits[], debits[] (each glAccountId, amount).
post_entry() {
local memo="$1"
local debit_id="$2"
local credit_id="$3"
local amount="$4"
local comments="$5"
local body
body=$(jq -n \
--argjson officeId "$OFFICE_ID" \
--arg transactionDate "$TRANSACTION_DATE" \
--arg comments "$comments" \
--argjson debitId "$debit_id" \
--argjson creditId "$credit_id" \
--argjson amount "$amount" \
'{ officeId: $officeId, transactionDate: $transactionDate, dateFormat: "yyyy-MM-dd", locale: "en", currencyCode: "USD", comments: $comments, credits: [ { glAccountId: $creditId, amount: $amount } ], debits: [ { glAccountId: $debitId, amount: $amount } ] }' 2>/dev/null)
if [ -z "$body" ]; then
echo "jq required for posting. Install jq or post manually. Memo=$memo debit=$debit_id credit=$credit_id amount=$amount" >&2
return 1
fi
if [ "$DRY_RUN" = "1" ]; then
echo "DRY_RUN: $memo -> $body"
return 0
fi
local out
out=$(curl "${CURL_OPTS[@]}" -X POST -d "$body" "${BASE_URL}/journalentries" 2>/dev/null)
local code
code=$(echo "$out" | tail -n1)
local resp
resp=$(echo "$out" | sed '$d')
if [ "$code" = "200" ] || [ "${code:0:1}" = "2" ]; then
echo "OK $memo (HTTP $code)"
else
echo "FAIL $memo HTTP $code: $resp" >&2
return 1
fi
}
# 1050 may not exist yet; use 1000 for T-001B debit if 1050 missing
[ -z "$ID_1050" ] && ID_1050="$ID_1000"
[ -z "$ID_2100" ] && ID_2100="$ID_2000"
echo "Posting ledger entries (officeId=$OFFICE_ID, date=$TRANSACTION_DATE)..." >&2
post_entry "T-001" "$ID_1000" "$ID_2000" 900000000000 "Opening Balance Migration"
post_entry "T-001B" "$ID_1050" "$ID_2000" 250000000000 "Treasury Conversion — Transfer to Reserve (M0)"
post_entry "T-002A" "$ID_2000" "$ID_2000" 2900000000 "Shamrayan Available (M1)"
post_entry "T-002B" "$ID_2000" "$ID_2100" 2100000000 "Shamrayan Restricted"
post_entry "T-003" "$ID_2000" "$ID_2100" 350000000000 "HYBX Capitalization Escrow"
post_entry "T-004" "$ID_2000" "$ID_2000" 5000000000 "TAJ Allocation (M1)"
post_entry "T-005" "$ID_2000" "$ID_2000" 5000000000 "Aseret Allocation (M1)"
post_entry "T-006" "$ID_2000" "$ID_2000" 5000000000 "Mann Li Allocation (M1)"
post_entry "T-007" "$ID_2000" "$ID_2000" 50000000000 "OSJ Allocation (M1)"
post_entry "T-008" "$ID_2000" "$ID_2000" 50000000000 "Alltra Allocation (M1)"
echo "Done." >&2