feat(settlement): wire real Chain 138 contracts for 1,000 BTC ledger
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 52s
CI/CD Pipeline / Security Scanning (push) Successful in 2m40s
CI/CD Pipeline / Lint and Format (push) Failing after 49s
CI/CD Pipeline / Terraform Validation (push) Failing after 24s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 27s
Deploy ChainID 138 / Deploy ChainID 138 (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Verify Deployment / Verify Deployment (push) Has been cancelled
Validation / validate-smart-contracts (push) Failing after 14s
Validation / validate-security (push) Failing after 1m20s
Validation / validate-documentation (push) Failing after 18s
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 52s
CI/CD Pipeline / Security Scanning (push) Successful in 2m40s
CI/CD Pipeline / Lint and Format (push) Failing after 49s
CI/CD Pipeline / Terraform Validation (push) Failing after 24s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 27s
Deploy ChainID 138 / Deploy ChainID 138 (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Verify Deployment / Verify Deployment (push) Has been cancelled
Validation / validate-smart-contracts (push) Failing after 14s
Validation / validate-security (push) Failing after 1m20s
Validation / validate-documentation (push) Failing after 18s
Add canonical btc-l1-ledger config, /btc/contracts API, Python chain138-oracle service, and production deploy scripts so secure.omdnl.org can serve live cBTC, DODO, and PMM pool addresses with headroom-aware mint gating. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
172
scripts/deployment/cutover-chain138-oracle-python.sh
Normal file
172
scripts/deployment/cutover-chain138-oracle-python.sh
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env bash
|
||||
# Cut over Chain 138 oracle from legacy shell/Node mesh to Python chain138-oracle.
|
||||
#
|
||||
# Stops:
|
||||
# - scripts/reserve/pmm-mesh-6s-automation.sh (nohup/systemd legacy)
|
||||
# - scripts/reserve/keeper-service.js
|
||||
# - external oracle-publisher (systemd) if present
|
||||
#
|
||||
# Starts:
|
||||
# - chain138-oracle-api.service (FastAPI spot prices, port 3500)
|
||||
# - chain138-oracle-mesh.service (6s oracle + keeper + PMM loop)
|
||||
#
|
||||
# Updates .env:
|
||||
# CHAIN138_ORACLE_URL=http://127.0.0.1:3500
|
||||
# TOKEN_AGGREGATION_URL=http://127.0.0.1:3000 (OMNL mint/transfer unchanged)
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/deployment/cutover-chain138-oracle-python.sh [--dry-run]
|
||||
# RESTART_SETTLEMENT=1 ./scripts/deployment/cutover-chain138-oracle-python.sh
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
DRY_RUN=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
ORACLE_DIR="$REPO_ROOT/services/chain138-oracle"
|
||||
ENV_FILE="${OMNL_BANK_ENV:-$REPO_ROOT/.env}"
|
||||
SYSTEMD_DIR="/etc/systemd/system"
|
||||
API_UNIT="chain138-oracle-api.service"
|
||||
MESH_UNIT="chain138-oracle-mesh.service"
|
||||
LEGACY_UNITS=(chain138-pmm-mesh-automation.service price-feed-keeper.service oracle-publisher.service)
|
||||
RESTART_SETTLEMENT="${RESTART_SETTLEMENT:-1}"
|
||||
|
||||
log() { echo "[$(date -Iseconds)] $*"; }
|
||||
run() {
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
log "DRY-RUN: $*"
|
||||
else
|
||||
log "RUN: $*"
|
||||
eval "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
fi
|
||||
|
||||
log "=== Chain 138 oracle Python cutover ==="
|
||||
log "Repo: $REPO_ROOT"
|
||||
log "Oracle dir: $ORACLE_DIR"
|
||||
|
||||
# --- Stop legacy off-chain oracle processes ---
|
||||
stop_pattern() {
|
||||
local pattern="$1"
|
||||
local pids
|
||||
pids=$(pgrep -f "$pattern" 2>/dev/null || true)
|
||||
if [ -n "$pids" ]; then
|
||||
log "Stopping processes matching: $pattern ($pids)"
|
||||
run "pkill -f '$pattern' || true"
|
||||
fi
|
||||
}
|
||||
|
||||
for unit in "${LEGACY_UNITS[@]}"; do
|
||||
if systemctl is-active --quiet "$unit" 2>/dev/null; then
|
||||
run "systemctl stop '$unit'"
|
||||
fi
|
||||
if systemctl is-enabled --quiet "$unit" 2>/dev/null; then
|
||||
run "systemctl disable '$unit'"
|
||||
fi
|
||||
done
|
||||
|
||||
stop_pattern "pmm-mesh-6s-automation.sh"
|
||||
stop_pattern "keeper-service.js"
|
||||
stop_pattern "oracle_publisher"
|
||||
|
||||
# --- Install Python venv + deps ---
|
||||
if [ ! -d "$ORACLE_DIR/.venv" ]; then
|
||||
run "python3 -m venv '$ORACLE_DIR/.venv'"
|
||||
fi
|
||||
run "'$ORACLE_DIR/.venv/bin/pip' install -q -r '$ORACLE_DIR/requirements.txt'"
|
||||
|
||||
# --- Validate token coverage ---
|
||||
log "Validating M2 registry token price coverage..."
|
||||
if [ "$DRY_RUN" -eq 0 ]; then
|
||||
set -a
|
||||
# shellcheck source=/dev/null
|
||||
source "$ENV_FILE" 2>/dev/null || true
|
||||
set +a
|
||||
PYTHONPATH="$ORACLE_DIR" "$ORACLE_DIR/.venv/bin/python" -m chain138_oracle validate
|
||||
fi
|
||||
|
||||
# --- Patch .env ---
|
||||
merge_env_key() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
run "touch '$ENV_FILE'"
|
||||
fi
|
||||
if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
run "sed -i 's|^${key}=.*|${key}=${value}|' '$ENV_FILE'"
|
||||
else
|
||||
run "echo '${key}=${value}' >> '$ENV_FILE'"
|
||||
fi
|
||||
}
|
||||
|
||||
merge_env_key "CHAIN138_ORACLE_URL" "${CHAIN138_ORACLE_URL:-http://127.0.0.1:3500}"
|
||||
merge_env_key "CHAIN138_ORACLE_PORT" "${CHAIN138_ORACLE_PORT:-3500}"
|
||||
merge_env_key "TOKEN_AGGREGATION_URL" "${TOKEN_AGGREGATION_URL:-http://127.0.0.1:3000}"
|
||||
|
||||
# --- Install systemd units ---
|
||||
install_unit() {
|
||||
local example="$1"
|
||||
local unit="$2"
|
||||
local dest="$SYSTEMD_DIR/$unit"
|
||||
run "cp '$example' '$dest'"
|
||||
run "sed -i 's|/opt/smom-dbis-138|$REPO_ROOT|g' '$dest'"
|
||||
}
|
||||
|
||||
install_unit "$ORACLE_DIR/systemd/chain138-oracle-api.service.example" "$API_UNIT"
|
||||
install_unit "$ORACLE_DIR/systemd/chain138-oracle-mesh.service.example" "$MESH_UNIT"
|
||||
run "systemctl daemon-reload"
|
||||
run "systemctl enable '$API_UNIT' '$MESH_UNIT'"
|
||||
run "systemctl restart '$API_UNIT'"
|
||||
run "systemctl restart '$MESH_UNIT'"
|
||||
|
||||
# --- Smoke tests ---
|
||||
smoke() {
|
||||
local url="$1"
|
||||
local label="$2"
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
log "DRY-RUN smoke: $label $url"
|
||||
return 0
|
||||
fi
|
||||
curl -sf "$url" >/dev/null && log "OK: $label" || { log "FAIL: $label ($url)"; return 1; }
|
||||
}
|
||||
|
||||
ORACLE_BASE="${CHAIN138_ORACLE_URL:-http://127.0.0.1:3500}"
|
||||
CBTC=0xe94260c555ac1d9d3cc9e1632883452ebdf0082e
|
||||
sleep 2
|
||||
smoke "$ORACLE_BASE/health" "oracle health"
|
||||
smoke "$ORACLE_BASE/api/v1/prices/metamask/v2/chains/138/spot-prices?tokenAddresses=$CBTC" "cBTC spot price"
|
||||
|
||||
# --- Restart settlement middleware if requested ---
|
||||
if [ "$RESTART_SETTLEMENT" = "1" ]; then
|
||||
for unit in settlement-middleware omnl-settlement-middleware; do
|
||||
if systemctl is-active --quiet "$unit" 2>/dev/null; then
|
||||
run "systemctl restart '$unit'"
|
||||
log "Restarted $unit"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if docker compose -f "$REPO_ROOT/docker-compose.omnl-production.yml" ps settlement-middleware >/dev/null 2>&1; then
|
||||
run "docker compose -f '$REPO_ROOT/docker-compose.omnl-production.yml' up -d chain138-oracle settlement-middleware"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "=== Cutover complete ==="
|
||||
log "CHAIN138_ORACLE_URL=$ORACLE_BASE"
|
||||
log "TOKEN_AGGREGATION_URL=${TOKEN_AGGREGATION_URL:-http://127.0.0.1:3000}"
|
||||
log "Check: systemctl status $API_UNIT $MESH_UNIT"
|
||||
log "Check: curl -s $ORACLE_BASE/api/v1/oracle/tokens | head"
|
||||
134
scripts/deployment/deploy-btc-ledger-production.sh
Normal file
134
scripts/deployment/deploy-btc-ledger-production.sh
Normal file
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy 1,000 BTC ledger real contract wiring to production settlement middleware.
|
||||
#
|
||||
# Run on the OMNL bank host (dev-bis-ali / secure.omdnl.org backend):
|
||||
# OMNL_SKIP_GIT_PULL=1 bash scripts/deployment/deploy-btc-ledger-production.sh
|
||||
# Or after push to main:
|
||||
# bash scripts/deployment/deploy-btc-ledger-production.sh
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
REPO_DIR="${OMNL_BANK_ROOT:-$HOME/smom-dbis-138}"
|
||||
ENV_FILE="${OMNL_BANK_ENV:-$REPO_DIR/.env}"
|
||||
LOG_DIR="${OMNL_BANK_LOG_DIR:-$HOME/omnl-bank/logs}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
log() { echo "[$(date -Iseconds)] $*"; }
|
||||
|
||||
cd "$REPO_DIR"
|
||||
|
||||
if [[ "${OMNL_SKIP_GIT_PULL:-0}" != "1" ]]; then
|
||||
log "Pull latest main..."
|
||||
git fetch origin
|
||||
git checkout main
|
||||
git pull --ff-only origin main
|
||||
fi
|
||||
|
||||
export SETTLEMENT_MIDDLEWARE_CONFIG="${SETTLEMENT_MIDDLEWARE_CONFIG:-$REPO_DIR/config/settlement-middleware.production.v1.json}"
|
||||
export OMNL_BTC_LEDGER_CONFIG="${OMNL_BTC_LEDGER_CONFIG:-$REPO_DIR/config/btc-l1-ledger-1000.chain138.v1.json}"
|
||||
|
||||
if [[ ! -f "$OMNL_BTC_LEDGER_CONFIG" ]]; then
|
||||
echo "Missing BTC ledger config: $OMNL_BTC_LEDGER_CONFIG" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
set -a
|
||||
set +u
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set -u
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Ensure contract env keys are present (merge from canonical JSON if missing).
|
||||
if [[ -f "$REPO_DIR/scripts/deployment/sync-btc-ledger-contract-env.mjs" ]]; then
|
||||
FRAG="$(mktemp)"
|
||||
node "$REPO_DIR/scripts/deployment/sync-btc-ledger-contract-env.mjs" >"$FRAG"
|
||||
while IFS= read -r line; do
|
||||
key="${line%%=*}"
|
||||
if ! grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
||||
echo "$line" >>"$ENV_FILE"
|
||||
log "Added missing env: $key"
|
||||
fi
|
||||
done <"$FRAG"
|
||||
rm -f "$FRAG"
|
||||
set -a
|
||||
set +u
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set -u
|
||||
set +a
|
||||
fi
|
||||
|
||||
log "Building settlement-core + settlement-middleware..."
|
||||
for dir in packages/settlement-core services/settlement-middleware; do
|
||||
cd "$REPO_DIR/$dir"
|
||||
NODE_ENV=development npm install --no-fund --no-audit
|
||||
npm run build
|
||||
done
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
cd "$REPO_DIR/services/settlement-middleware"
|
||||
pkill -f "smom-dbis-138/services/settlement-middleware" 2>/dev/null || true
|
||||
fuser -k 3011/tcp 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
log "Starting settlement-middleware :3011 with BTC ledger config..."
|
||||
nohup env \
|
||||
SETTLEMENT_MIDDLEWARE_CONFIG="$SETTLEMENT_MIDDLEWARE_CONFIG" \
|
||||
OMNL_BTC_LEDGER_CONFIG="$OMNL_BTC_LEDGER_CONFIG" \
|
||||
OMNL_SUPPORTED_CHAINS_CONFIG="${OMNL_SUPPORTED_CHAINS_CONFIG:-$REPO_DIR/config/omnl-supported-chains.v1.json}" \
|
||||
OMNL_M2_TOKEN_REGISTRY="${OMNL_M2_TOKEN_REGISTRY:-$REPO_DIR/config/omnl-m2-token-registry.v1.json}" \
|
||||
OMNL_API_KEY="${OMNL_API_KEY:-${OMNL_SUPER_ADMIN_SECURE_KEY:-}}" \
|
||||
OMNL_SUPER_ADMIN_OFFICE24_KEY="${OMNL_SUPER_ADMIN_OFFICE24_KEY:-}" \
|
||||
OMNL_SUPER_ADMIN_EXCHANGE_KEY="${OMNL_SUPER_ADMIN_EXCHANGE_KEY:-}" \
|
||||
OMNL_SUPER_ADMIN_SECURE_KEY="${OMNL_SUPER_ADMIN_SECURE_KEY:-}" \
|
||||
OMNL_SUPER_ADMIN_FOREX_KEY="${OMNL_SUPER_ADMIN_FOREX_KEY:-}" \
|
||||
OMNL_FINERACT_BASE_URL="${OMNL_FINERACT_BASE_URL:-${FINERACT_BASE_URL:-}}" \
|
||||
OMNL_FINERACT_TENANT="${OMNL_FINERACT_TENANT:-${FINERACT_TENANT:-omnl}}" \
|
||||
OMNL_FINERACT_USER="${OMNL_FINERACT_USER:-}" \
|
||||
OMNL_FINERACT_USERNAME="${OMNL_FINERACT_USERNAME:-}" \
|
||||
OMNL_FINERACT_PASSWORD="${OMNL_FINERACT_PASSWORD:-}" \
|
||||
OMNL_PUBLIC_MONEY_SUPPLY="${OMNL_PUBLIC_MONEY_SUPPLY:-1}" \
|
||||
SETTLEMENT_ALLOW_HYBX_PRODUCTION="${SETTLEMENT_ALLOW_HYBX_PRODUCTION:-1}" \
|
||||
SETTLEMENT_ALLOW_CHAIN_MINT_EXECUTE="${SETTLEMENT_ALLOW_CHAIN_MINT_EXECUTE:-1}" \
|
||||
TOKEN_AGGREGATION_URL="${TOKEN_AGGREGATION_URL:-http://127.0.0.1:3000}" \
|
||||
CHAIN138_ORACLE_URL="${CHAIN138_ORACLE_URL:-http://127.0.0.1:3500}" \
|
||||
RPC_URL_138="${RPC_URL_138:-${CHAIN_138_RPC_URL:-}}" \
|
||||
CHAIN_138_RPC_URL="${CHAIN_138_RPC_URL:-${RPC_URL_138:-}}" \
|
||||
OMNL_MINT_OPERATOR_PRIVATE_KEY="${OMNL_MINT_OPERATOR_PRIVATE_KEY:-}" \
|
||||
OMNL_REQUIRE_LIVE_BTC_PRICE="${OMNL_REQUIRE_LIVE_BTC_PRICE:-1}" \
|
||||
CBTC_ADDRESS_138="${CBTC_ADDRESS_138:-}" \
|
||||
BTC_L1_RECIPIENT_ADDRESS="${BTC_L1_RECIPIENT_ADDRESS:-}" \
|
||||
BTC_EXISTING_MINT_HOLDER="${BTC_EXISTING_MINT_HOLDER:-}" \
|
||||
DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION:-${CHAIN_138_DODO_PMM_INTEGRATION:-}}" \
|
||||
CHAIN138_POOL_CBTC_CUSDT="${CHAIN138_POOL_CBTC_CUSDT:-}" \
|
||||
CHAIN138_POOL_CBTC_CUSDC="${CHAIN138_POOL_CBTC_CUSDC:-}" \
|
||||
CHAIN138_POOL_CBTC_CXAUC="${CHAIN138_POOL_CBTC_CXAUC:-}" \
|
||||
RESERVE_SYSTEM="${RESERVE_SYSTEM:-}" \
|
||||
ORACLE_PROXY_ADDRESS="${ORACLE_PROXY_ADDRESS:-}" \
|
||||
ORACLE_AGGREGATOR_ADDRESS="${ORACLE_AGGREGATOR_ADDRESS:-${AGGREGATOR_ADDRESS:-}}" \
|
||||
PRICE_FEED_KEEPER_ADDRESS="${PRICE_FEED_KEEPER_ADDRESS:-}" \
|
||||
HYBX_MIFOS_FINERACT_SIDECAR_URL="${HYBX_MIFOS_FINERACT_SIDECAR_URL:-}" \
|
||||
HYBX_BASE_URL="${HYBX_BASE_URL:-}" \
|
||||
HYBX_API="${HYBX_API:-}" \
|
||||
npm run start >"$LOG_DIR/settlement-middleware.log" 2>&1 &
|
||||
echo $! >"$LOG_DIR/settlement-middleware.pid"
|
||||
|
||||
sleep 4
|
||||
API_KEY="${OMNL_API_KEY:-${OMNL_SUPER_ADMIN_SECURE_KEY:-}}"
|
||||
log "Smoke: /btc/contracts"
|
||||
curl -sf -H "Authorization: Bearer ${API_KEY}" \
|
||||
"http://127.0.0.1:3011/api/v1/settlement/btc/contracts" | head -c 400 || {
|
||||
echo "FAIL: /btc/contracts not reachable" >&2
|
||||
tail -n 40 "$LOG_DIR/settlement-middleware.log" >&2 || true
|
||||
exit 1
|
||||
}
|
||||
echo
|
||||
log "Smoke: /btc/ledger headroom"
|
||||
curl -sf -H "Authorization: Bearer ${API_KEY}" \
|
||||
"http://127.0.0.1:3011/api/v1/settlement/btc/ledger" | head -c 500 || true
|
||||
echo
|
||||
log "BTC ledger production deploy complete"
|
||||
log "Public: https://secure.omdnl.org/settlement/btc/contracts"
|
||||
@@ -171,6 +171,19 @@ start_svc() {
|
||||
AZURE_OPENAI_API_VERSION="${AZURE_OPENAI_API_VERSION:-2024-02-15-preview}" \
|
||||
OMNL_MINT_OPERATOR_PRIVATE_KEY="${OMNL_MINT_OPERATOR_PRIVATE_KEY:-}" \
|
||||
OMNL_REQUIRE_LIVE_BTC_PRICE="${OMNL_REQUIRE_LIVE_BTC_PRICE:-1}" \
|
||||
OMNL_BTC_LEDGER_CONFIG="${OMNL_BTC_LEDGER_CONFIG:-$REPO_DIR/config/btc-l1-ledger-1000.chain138.v1.json}" \
|
||||
CBTC_ADDRESS_138="${CBTC_ADDRESS_138:-}" \
|
||||
BTC_L1_RECIPIENT_ADDRESS="${BTC_L1_RECIPIENT_ADDRESS:-}" \
|
||||
BTC_EXISTING_MINT_HOLDER="${BTC_EXISTING_MINT_HOLDER:-}" \
|
||||
DODO_PMM_INTEGRATION="${DODO_PMM_INTEGRATION:-${CHAIN_138_DODO_PMM_INTEGRATION:-}}" \
|
||||
CHAIN138_POOL_CBTC_CUSDT="${CHAIN138_POOL_CBTC_CUSDT:-}" \
|
||||
CHAIN138_POOL_CBTC_CUSDC="${CHAIN138_POOL_CBTC_CUSDC:-}" \
|
||||
CHAIN138_POOL_CBTC_CXAUC="${CHAIN138_POOL_CBTC_CXAUC:-}" \
|
||||
CHAIN138_ORACLE_URL="${CHAIN138_ORACLE_URL:-http://127.0.0.1:3500}" \
|
||||
RESERVE_SYSTEM="${RESERVE_SYSTEM:-}" \
|
||||
ORACLE_PROXY_ADDRESS="${ORACLE_PROXY_ADDRESS:-}" \
|
||||
ORACLE_AGGREGATOR_ADDRESS="${ORACLE_AGGREGATOR_ADDRESS:-${AGGREGATOR_ADDRESS:-}}" \
|
||||
PRICE_FEED_KEEPER_ADDRESS="${PRICE_FEED_KEEPER_ADDRESS:-}" \
|
||||
BITCOIND_RPC_URL="${BITCOIND_RPC_URL:-}" \
|
||||
BITCOIND_RPC_USER="${BITCOIND_RPC_USER:-}" \
|
||||
BITCOIND_RPC_PASS="${BITCOIND_RPC_PASS:-}" \
|
||||
|
||||
114
scripts/deployment/post-btc-m2-headroom-adjustment.mjs
Normal file
114
scripts/deployment/post-btc-m2-headroom-adjustment.mjs
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Post three-leg BTC M2 headroom policy adjustment to Fineract:
|
||||
* Leg 1 (Office 1): Dr 2100 / Cr 2410
|
||||
* Leg 2 (Office 24): Dr 1410 / Cr 2100
|
||||
* Leg 3 (Office 24): Dr 2100 / Cr 2200
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, '../..');
|
||||
const configPath = path.join(repoRoot, 'config/offices/btc-m2-headroom-adjustment-20260720.v1.json');
|
||||
|
||||
const dryRun = process.argv.includes('--dry-run');
|
||||
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
|
||||
const base = (process.env.OMNL_FINERACT_BASE_URL || process.env.FINERACT_BASE_URL || '').replace(/\/$/, '');
|
||||
const tenant = process.env.OMNL_FINERACT_TENANT || process.env.FINERACT_TENANT || 'omnl';
|
||||
const user =
|
||||
process.env.OMNL_FINERACT_USER?.trim() ||
|
||||
process.env.OMNL_FINERACT_USERNAME?.trim() ||
|
||||
'ali_hospitallers_tenant';
|
||||
const pass = process.env.OMNL_FINERACT_PASSWORD || '';
|
||||
const amountRaw = process.env[cfg.amountEnv] || String(cfg.amountUsd || '');
|
||||
const amount = parseFloat(amountRaw);
|
||||
|
||||
if (!base || !pass) {
|
||||
console.error('Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
console.error(`Set ${cfg.amountEnv} or amountUsd in config to a positive USD amount`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const auth = Buffer.from(`${user}:${pass}`).toString('base64');
|
||||
const headers = {
|
||||
Authorization: `Basic ${auth}`,
|
||||
'Fineract-Platform-TenantId': tenant,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
};
|
||||
|
||||
const glCache = new Map();
|
||||
|
||||
async function glIdForCode(glCode) {
|
||||
const code = String(glCode).trim();
|
||||
if (glCache.has(code)) return glCache.get(code);
|
||||
const res = await fetch(`${base}/glaccounts?glCode=${encodeURIComponent(code)}`, { headers });
|
||||
if (!res.ok) throw new Error(`GL lookup failed for ${code} (${res.status})`);
|
||||
const data = await res.json();
|
||||
const list = Array.isArray(data) ? data : data?.pageItems ?? [];
|
||||
const match = list.find((row) => String(row.glCode) === code);
|
||||
if (!match?.id) throw new Error(`GL code ${code} not found in Fineract tenant ${tenant}`);
|
||||
glCache.set(code, Number(match.id));
|
||||
return Number(match.id);
|
||||
}
|
||||
|
||||
const txDate = cfg.transactionDate || new Date().toISOString().slice(0, 10);
|
||||
const results = [];
|
||||
|
||||
for (const leg of cfg.entries) {
|
||||
const debitGlAccountId = await glIdForCode(leg.debitGlCode);
|
||||
const creditGlAccountId = await glIdForCode(leg.creditGlCode);
|
||||
const entry = {
|
||||
officeId: leg.officeId,
|
||||
transactionDate: txDate,
|
||||
referenceNumber: leg.memo,
|
||||
comments: leg.comments,
|
||||
currencyCode: cfg.currencyCode || 'USD',
|
||||
dateFormat: 'yyyy-MM-dd',
|
||||
locale: 'en',
|
||||
debits: [{ glAccountId: debitGlAccountId, amount }],
|
||||
credits: [{ glAccountId: creditGlAccountId, amount }],
|
||||
};
|
||||
const summary = {
|
||||
memo: leg.memo,
|
||||
officeId: leg.officeId,
|
||||
amount,
|
||||
debitGlCode: leg.debitGlCode,
|
||||
creditGlCode: leg.creditGlCode,
|
||||
debitGlAccountId,
|
||||
creditGlAccountId,
|
||||
};
|
||||
console.log(JSON.stringify({ dryRun, ...summary }, null, 2));
|
||||
|
||||
if (dryRun) {
|
||||
results.push({ ...summary, status: 'dry-run' });
|
||||
continue;
|
||||
}
|
||||
|
||||
const res = await fetch(`${base}/journalentries`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(entry),
|
||||
});
|
||||
const body = await res.text();
|
||||
if (!res.ok) {
|
||||
console.error(`Fineract POST failed for ${leg.memo} (${res.status}): ${body}`);
|
||||
process.exit(1);
|
||||
}
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(body);
|
||||
} catch {
|
||||
parsed = { raw: body };
|
||||
}
|
||||
results.push({ ...summary, status: 'posted', response: parsed });
|
||||
console.log(`Posted ${leg.memo}:`, body);
|
||||
}
|
||||
|
||||
console.log(JSON.stringify({ dryRun, amount, legs: results.length, results }, null, 2));
|
||||
@@ -26,7 +26,7 @@ set -u
|
||||
set +a
|
||||
|
||||
FRAG="$(mktemp)"
|
||||
grep -E '^(OMNL_FINERACT_|OMNL_PUBLIC_MONEY_SUPPLY=|OMNL_REQUIRE_API_KEY=|OMNL_CUSTOMER_SECURITY_PRODUCTION=|OMNL_CUSTOMER_API_KEYS=|OFFICE24_OPENING_M1_USD=|OMNL_PORTAL_INTERNAL_SECRET=|JWT_SECRET=|DATABASE_URL=|RPC_URL_138=|CHAIN_138_RPC_URL=|SETTLEMENT_ALLOW_CHAIN_MINT_EXECUTE=|SETTLEMENT_ALLOW_HYBX_PRODUCTION=|OMNL_ALLOW_CHAIN_MINT_EXECUTE=|OMNL_MINT_OPERATOR_PRIVATE_KEY=|TOKEN_AGGREGATION_URL=|SWIFT_LISTENER_URL=|HYBX_MIFOS_FINERACT_SIDECAR_URL=|INFURA_PROJECT_ID=|INFURA_PROJECT_SECRET=)' "$ENV_FILE" >"$FRAG" || true
|
||||
grep -E '^(OMNL_FINERACT_|OMNL_PUBLIC_MONEY_SUPPLY=|OMNL_REQUIRE_API_KEY=|OMNL_CUSTOMER_SECURITY_PRODUCTION=|OMNL_CUSTOMER_API_KEYS=|OFFICE24_OPENING_M1_USD=|OMNL_PORTAL_INTERNAL_SECRET=|JWT_SECRET=|DATABASE_URL=|RPC_URL_138=|CHAIN_138_RPC_URL=|SETTLEMENT_ALLOW_CHAIN_MINT_EXECUTE=|SETTLEMENT_ALLOW_HYBX_PRODUCTION=|OMNL_ALLOW_CHAIN_MINT_EXECUTE=|OMNL_MINT_OPERATOR_PRIVATE_KEY=|OMNL_BTC_LEDGER_CONFIG=|CBTC_ADDRESS_138=|BTC_L1_RECIPIENT_ADDRESS=|BTC_EXISTING_MINT_HOLDER=|DODO_PMM_INTEGRATION=|CHAIN_138_DODO_PMM_INTEGRATION=|RESERVE_SYSTEM=|ORACLE_PROXY_ADDRESS=|CHAIN138_POOL_CBTC_CUSDT=|CHAIN138_POOL_CBTC_CUSDC=|CHAIN138_POOL_CBTC_CXAUC=|CHAIN138_ORACLE_URL=|CHAIN138_ORACLE_PORT=|TOKEN_AGGREGATION_URL=|OMNL_REQUIRE_LIVE_BTC_PRICE=|KEEPER_PRIVATE_KEY=|PRICE_FEED_KEEPER_ADDRESS=|AGGREGATOR_ADDRESS=|SWIFT_LISTENER_URL=|HYBX_BASE_URL=|HYBX_API=|HYBX_ENVIRONMENT=|HYBX_API_KEY=|HYBX_CLIENT_ID=|HYBX_CLIENT_SECRET=|HYBX_WEBHOOK_SECRET=|HYBX_MIFOS_FINERACT_SIDECAR_URL=|HYBX_ALLOW_LAN_SIDECAR=|INFURA_PROJECT_ID=|INFURA_PROJECT_SECRET=)' "$ENV_FILE" >"$FRAG" || true
|
||||
|
||||
ADMIN_ENV_KEY="$(node -e "
|
||||
const fs = require('fs');
|
||||
|
||||
31
scripts/deployment/smoke-chain138-oracle-pricing.mjs
Normal file
31
scripts/deployment/smoke-chain138-oracle-pricing.mjs
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Smoke: settlement middleware can fetch live cBTC/USD from chain138-oracle.
|
||||
*
|
||||
* CHAIN138_ORACLE_URL=http://127.0.0.1:3500 node scripts/deployment/smoke-chain138-oracle-pricing.mjs
|
||||
*/
|
||||
const oracleBase = (process.env.CHAIN138_ORACLE_URL || 'http://127.0.0.1:3500').replace(/\/$/, '');
|
||||
const cbtc = '0xe94260c555ac1d9d3cc9e1632883452ebdf0082e';
|
||||
|
||||
async function main() {
|
||||
const health = await fetch(`${oracleBase}/health`);
|
||||
console.log('health', health.status, await health.json());
|
||||
|
||||
const url =
|
||||
`${oracleBase}/api/v1/prices/metamask/v2/chains/138/spot-prices` +
|
||||
`?tokenAddresses=${cbtc}&vsCurrency=usd`;
|
||||
const spot = await fetch(url);
|
||||
const body = await spot.json();
|
||||
console.log('cBTC spot', spot.status, body);
|
||||
const rate = body[cbtc]?.usd ?? body[cbtc.toLowerCase()]?.usd;
|
||||
if (!(rate > 0)) {
|
||||
console.error('FAIL: no live cBTC/USD from oracle');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('OK: live BTC/USD =', rate);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
43
scripts/deployment/sync-btc-ledger-contract-env.mjs
Normal file
43
scripts/deployment/sync-btc-ledger-contract-env.mjs
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Emit .env lines for 1,000 BTC ledger real contract addresses from
|
||||
* config/btc-l1-ledger-1000.chain138.v1.json
|
||||
*
|
||||
* node scripts/deployment/sync-btc-ledger-contract-env.mjs
|
||||
* node scripts/deployment/sync-btc-ledger-contract-env.mjs --write .env.fragment
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, '../..');
|
||||
const cfgPath = path.join(repoRoot, 'config/btc-l1-ledger-1000.chain138.v1.json');
|
||||
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
||||
|
||||
const lines = [
|
||||
`OMNL_BTC_LEDGER_CONFIG=config/btc-l1-ledger-1000.chain138.v1.json`,
|
||||
`CBTC_ADDRESS_138=${cfg.token.address}`,
|
||||
`BTC_L1_RECIPIENT_ADDRESS=${cfg.recipients.primaryMintTarget}`,
|
||||
`BTC_EXISTING_MINT_HOLDER=${cfg.recipients.existingMintHolder1000}`,
|
||||
`DODO_PMM_INTEGRATION=${cfg.dodoEvm.pmmIntegration}`,
|
||||
`CHAIN_138_DODO_PMM_INTEGRATION=${cfg.dodoEvm.pmmIntegration}`,
|
||||
`RESERVE_SYSTEM=${cfg.oracle.reserveSystem}`,
|
||||
`ORACLE_PRICE_FEED=${cfg.oracle.oraclePriceFeed}`,
|
||||
`ORACLE_AGGREGATOR_ADDRESS=${cfg.oracle.aggregator}`,
|
||||
`AGGREGATOR_ADDRESS=${cfg.oracle.aggregator}`,
|
||||
`ORACLE_PROXY_ADDRESS=${cfg.oracle.proxy}`,
|
||||
`PRICE_FEED_KEEPER_ADDRESS=${cfg.oracle.priceFeedKeeper}`,
|
||||
`CHAIN138_POOL_CBTC_CUSDT=${cfg.pmmPools.cBTC_cUSDT}`,
|
||||
`CHAIN138_POOL_CBTC_CUSDC=${cfg.pmmPools.cBTC_cUSDC}`,
|
||||
`CHAIN138_POOL_CBTC_CXAUC=${cfg.pmmPools.cBTC_cXAUC}`,
|
||||
];
|
||||
|
||||
const writeArg = process.argv.indexOf('--write');
|
||||
if (writeArg >= 0) {
|
||||
const out = process.argv[writeArg + 1] || path.join(repoRoot, '.env.btc-ledger.fragment');
|
||||
fs.writeFileSync(out, `${lines.join('\n')}\n`, 'utf8');
|
||||
console.log(`Wrote ${out}`);
|
||||
} else {
|
||||
console.log(lines.join('\n'));
|
||||
}
|
||||
Reference in New Issue
Block a user