RED DE PRUEBA · XRPL TESTNET — sin dinero real

Contrato Épale Venezuela (EPV)

Expediente consultable del contrato, depositado como la misma copia en el SeixPanel de Seix y en el SeixPanel de Flyxs. La matriz vive en la cadena (XRPL); aquí vive su código fuente completo, su carta, su escalera y su registro. Generado: 2026-10-30T16:19:48.152Z.

🪙 La emisión de un vistazo

0 EPV

Emisión total del contrato, repartida en cuatro bolsas. Es la misma distribución escrita en la carta (§8), de un vistazo:

Pool de fundadores10 % · 600.000
Liquidez DEX15 % · 900.000
Tesorería / motor del ciclo45 % · 2.700.000
Reserva estratégica30 % · 1.800.000

Escalera de valor estipulada

Salida inicial a dinero/cripto (primeros 6 meses)US$0,10
Salida a los 6 meses (se libera trazar, intercambiar y vender)US$0,50
Reventa entre usuarios (P2P)US$0,50 – US$0,70
Reconocimiento dentro del ecosistema (minutos, productos, servicios, completar membresías)US$1,00

Emisión total: 6.000.000 EPV · Emisora: rfqoxpiU7QViZXSHQegqnFcHqm28ZyBEEx · Tesorería: raEhFHwegZGtMDNMYLMGcF6WLhdfV5Md8d · Emisión anclada: 170CF79DA826D889… · Carta SHA-256: 4265c4470bcf219f6e2f636a3dae0e5d96d1f694f7cb3f164005d1877dbdb36f · Disclaimer Universal v1.0 SHA-256: 7a5dbf830bb1aaeabb619182da50d1d80532fb187e120820c3ebd7898e71480c

Registro de asignaciones (sincronizado con el CRM/ERP de Épale)

El registro vivo está en el ERP de Épale (tablas epale_proyectos, epale_proyecto_membresias, epale_proyecto_tokens, epale_proyecto_pool). Esta es su foto al generarse el expediente; cada asignación nueva se asienta igual: fila en el ERP + ancla en cadena.

#Membresía (ERP)Usuario (SID)PaísTokensAncla en cadena
1ERP #1 · PREMIUM ANUAL · activaSID-DEMO-EPV-0001 (prueba)VE39,68 EPVbloqueo 26B4FFEDA086EF36… · liberación 4A72910B6E3B1F28…

Operaciones del contrato en el DEX (prueba): compra del usuario demo 57D54450750753B2… · venta 0A8F1AAC2EA5CB4D… · P2P dentro de la banda 6272B99ECEAF84EF…

Carta del contrato (texto completo)

CARTA DEL CONTRATO — Proyecto «Épale Venezuela»

Token: Épale Venezuela (EPV) · Emisión total: 6.000.000 de tokens · Versión de la carta: 1.0 · Fecha: 30 de octubre de 2026

Empresa emisora del proyecto: SEIX TECH LLC (Estados Unidos) · Cadena: XRPL (XRP Ledger)

Fase actual: red de prueba (testnet) con mecánica real. La red principal se activa por fases, solo con la estructura legal aprobada y la autorización expresa del CEO.


1. Nombre del proyecto

Épale Venezuela — la tokenización del proyecto de expansión de Épale Móvil en Venezuela, cuyo primer tramo es la entrada de los primeros 10.000 usuarios fundadores. Su token se llama igual que el proyecto: Épale Venezuela (EPV). Cada proyecto del ecosistema que se tokenice tendrá su propio contrato y su propio token, por país y por producto, nacidos de la misma fábrica de tokenización de la casa; este es el contrato del proyecto Épale Venezuela.

2. Para qué es

Para financiar y hacer crecer, con participación ordenada de su propia comunidad, la expansión de Épale Móvil en Venezuela: datos móviles (eSIM), minutos de intérprete en vivo, la plataforma y sus servicios. Los tokens representan la participación de los fundadores en el ciclo del proyecto dentro del ecosistema: quedan contabilizados, auditados y protegidos dentro del contrato, y se asignan a cada persona cuando su membresía del proyecto se vende y se libera, con auditoría en tiempo real de qué membresía, qué país y qué usuario (su identidad SID) los recibe.

3. La idea

Que las personas que hacen crecer el proyecto participen de su crecimiento con reglas claras y escritas desde el día uno: un token con un valor de referencia conocido dentro del ecosistema, una escalera de salidas estipulada, un registro público y verificable en la cadena, y un sistema que se autoalimenta — los flujos del propio sistema (emisión, operación, distribuciones) retroalimentan su tesorería bajo las reglas de custodia de la casa, sin tocar jamás el principal de los tenedores.

4. Misión

Llevar conectividad y comunicación sin fronteras a Venezuela — datos, interpretación en vivo y servicios digitales — con una participación de fundadores transparente, auditable en cadena y sincronizada con el CRM/ERP de Épale, donde cada asignación se puede verificar: qué membresía la originó, en qué país y a qué identidad SID pertenece.

5. Visión

Que Épale Venezuela sea el primer contrato de una red de proyectos tokenizados por país y por producto — comunicaciones, energía, salud y los que vengan — todos nacidos de la misma fábrica, con la misma carta, la misma cobertura legal y la misma disciplina: nada se promete por encima de lo que el ecosistema reconoce, y todo queda escrito y verificable.

6. Quién lo creó y qué empresa lo respalda operativamente

El proyecto fue creado por Jhonn Lenon Medina, fundador y CEO de SEIX TECH LLC, empresa de tecnología e intermediación tecnológica registrada en Estados Unidos, que opera el ecosistema Épale dentro de En5cronia y actúa como mandatario tecnológico del proyecto: pone la tecnología, la operación y la custodia distribuida; no es un banco ni una entidad financiera vigilada y no capta recursos del público.

7. Respaldo declarado

Este contrato declara como su respaldo toda la factibilidad del proyecto (su estudio de factibilidad y proyección, con supuestos explícitos), las proyecciones de venta del proyecto y el ecosistema completo de Épale dentro de En5cronia: software, asesoría, venta de empresas y servicios. Ese es el fundamento del ciclo del token: los ingresos reales del ecosistema alimentan la tesorería del sistema que sostiene las recompras, la liquidez y las distribuciones del proyecto. Como dice la parte legal de esta misma carta (sección 12), las proyecciones son escenarios construidos con supuestos — no promesas de rendimiento, de valor futuro ni de liquidez — y el valor del token nunca se reconoce por encima de US$1,00.

8. La emisión y su distribución

Emisión total: 6.000.000 EPV, contra el acta de emisión cuyo hash acompaña la transacción de emisión en cadena. Distribución según las proporciones de la fábrica:

Bolsa%TokensFunción
Pool de fundadores10 %600.000Asignaciones por membresía a los participantes del proyecto
Liquidez DEX15 %900.000Mercado ordenado del token en el DEX de XRPL
Tesorería / motor del ciclo45 %2.700.000Operación del ciclo, distribuciones y autoalimentación
Reserva estratégica30 %1.800.000Respaldo, contingencias y crecimiento por tramos

Tramo 1 — el piloto: los primeros 10.000 usuarios fundadores de Venezuela operan dentro de esta emisión con el sub-pool de fundadores ya dimensionado por el estudio de factibilidad (100.000 tokens de capacidad para el tramo; la captación base proyectada del tramo es de ≈US$100.000). Horizonte de la emisión: el fundador ha declarado la ambición de que la emisión completa acompañe una captación del ecosistema del orden de US$60.000.000 («Dios mediante»): es el horizonte de diseño de los 6.000.000 de tokens — una declaración de ambición del proyecto, no una promesa — y cada tramo futuro se dimensionará con su propio estudio antes de activarse.

9. Escalera de valor estipulada

El valor del token se reconoce por esta escalera, y por ninguna otra:

SalidaValor por token
Salida inicial a dinero/cripto (primeros 6 meses)US$0,10
Salida a dinero/cripto a partir de los 6 meses — cuando se libera la opción de trazar, intercambiar y venderUS$0,50
Reventa entre usuarios (P2P, acuerdo libre dentro de la banda)US$0,50 – US$0,70
Reconocimiento dentro del ecosistema (minutos de intérprete, productos, servicios, completar otras membresías)US$1,00

US$1,00 es el techo de reconocimiento del sistema: la plusvalía del proyecto se expresa en la solidez de su ciclo — cobertura de la reserva, distribuciones, servicios — nunca en un precio por encima de ese techo.

10. Asignación sincronizada con las membresías

Los tokens reposan en el contrato (la tesorería de la instancia), contabilizados, auditados y protegidos. Se asignan cuando cada membresía del proyecto se vende y se libera, aplicando la escala del sistema (1 token = US$1 de referencia al asignarse):

MembresíaPrecio% en tokensTokens
LITEUS$9,960 %0
PREMIUMUS$9610 %9,6
PREMIUM ANUALUS$4968 %39,68
JuniorUS$1.9967 %139,72
SeniorUS$2.9966 %179,76
MasterUS$8.9965 %449,8
Source CodeUS$26.9965 %1.349,8

El registro de asignaciones vive sincronizado con el CRM/ERP de Épale (proyecto, membresías del proyecto y tokens asignados por membresía son tablas del ERP) y cada asignación queda anclada y es verificable en XRPL: la operación en cadena lleva en su memo la membresía, el país y la referencia de identidad (SID) del receptor, y el hash de la transacción queda asentado en el registro del ERP. Auditoría en tiempo real: qué membresía, qué país, qué usuario.

Los tokens asignados se ven en el backoffice del usuario desde la asignación, bloqueados: su liberación ocurre por tramos dentro de la ventana entre los 90 días y los 6 meses, según el calendario publicado del proyecto, y exige la membresía activa — si la membresía vence, la liberación se pausa (los tokens no se pierden; esperan la reactivación). El bloqueo y la liberación se ejecutan en cadena con el mecanismo de escrow del token.

11. Custodia y reglas del contrato en cadena

12. Parte legal — Disclaimer Universal v1.0

La cobertura legal de este contrato es el Disclaimer General (Universal) de SeixTech LLC, versión 1.0, vigente desde el 30 de octubre de 2026, cuyo texto vivo se custodia en el servicio de disclaimers de la casa y cuya huella es:

Si el CEO ratifica el texto con cambios, el disclaimer se versiona (v1.1, v1.2…), la nueva huella se ancla en cadena y esta carta se actualiza con la nueva versión; las aceptaciones anteriores siguen valiendo para la versión aceptada.

Texto completo del Disclaimer Universal v1.0 (ES)


DISCLAIMER GENERAL — SEIXTECH LLC

Versión 1.0 · Vigente desde el 30 de octubre de 2026

Léelo completo antes de aceptar. Está escrito en lenguaje claro, sin letra pequeña escondida: lo que dice aquí es lo que aplica.

1. QUIÉNES SOMOS Y QUÉ NO SOMOS

SeixTech LLC es una empresa de tecnología e intermediación tecnológica registrada en Estados Unidos. SeixTech LLC NO es un banco, NO es una entidad financiera y NO es una institución vigilada por autoridades bancarias o financieras. SeixTech LLC NO capta recursos del público, NO ofrece cuentas de ahorro ni depósitos, NO otorga créditos y NO garantiza rendimientos a nadie.

2. LO QUE SÍ HACEMOS

Vendemos y operamos software, plataformas, aplicaciones y servicios digitales: sitios web, aplicaciones, asistentes de inteligencia artificial, herramientas de comunicación, identidad digital y servicios tecnológicos para personas y empresas.

3. LO REGULADO LO HACEN LOS SOCIOS

Cuando un servicio toca una actividad regulada —por ejemplo la emisión de tarjetas, la custodia regulada de fondos o las rampas de entrada y salida de dinero tradicional— esa parte la presta un socio que cuenta con la infraestructura y las autorizaciones correspondientes. Ese socio se nombra en los términos propios de ese servicio y responde por su parte. SeixTech pone la tecnología; no sustituye a la entidad regulada.

4. TU DINERO, TUS ACTIVOS, TUS LLAVES

Tu billetera (SeiXw, y PayW dentro de Épale) funciona con tus propios factores de seguridad: tu contraseña, tus códigos y tus confirmaciones. La empresa no conoce tu contraseña ni tus códigos, y ningún empleado te los pedirá jamás. En los módulos donde conectas tu propio broker o tu propia cuenta externa, la empresa solo muestra y transmite tus instrucciones: nunca custodia tu dinero ni tus posiciones.

5. ACTIVOS DIGITALES Y SU RIESGO

Los activos digitales tienen precios volátiles: su valor puede subir, bajar o llegar a cero, y puedes perder parte o todo lo que pongas en ellos. Las monedas estables, como RLUSD, son emitidas por terceros y tampoco están garantizadas por SeixTech. Nada de lo que veas en nuestras plataformas es una promesa de ganancia.

6. NADA DE ESTO ES ASESORÍA

La información, los contenidos, los asistentes y las herramientas del ecosistema son información general y herramientas de trabajo. No son asesoría financiera, legal, fiscal ni de inversión personalizada. Las decisiones sobre tu dinero y tu negocio son tuyas; cuando lo necesites, consulta a un profesional de tu confianza.

7. RECOMPENSAS, BENEFICIOS Y PROYECCIONES

Si algún programa ofrece recompensas, bonos o beneficios, son discrecionales: la empresa puede crearlos, modificarlos o terminarlos, y nunca son intereses ni renta garantizada. Toda cifra proyectada —incluidas las de proyectos tokenizados y de activos del mundo real (RWA)— es un escenario construido con supuestos: no es una promesa de rendimiento, de valor futuro ni de liquidez. Participa solo con dinero cuya pérdida puedas asumir.

8. LA RED Y LAS COMISIONES

Participar en la red del ecosistema (Matriz ×6) es opcional. Las comisiones se calculan con el plan de compensación oficial vigente y nacen de ventas reales de productos y servicios. Nadie gana dinero solo por inscribirse, y ningún ingreso de red está garantizado: depende del trabajo y de las ventas reales de cada persona y de su equipo.

9. TU IDENTIDAD Y TU CUENTA

Una persona tiene una sola identidad verificada en el ecosistema (su SID). Debes registrar datos verdaderos y eres responsable de cuidar tu acceso y de lo que se haga desde tu cuenta.

10. TU ACEPTACIÓN DEJA HUELLA

Al decir «Sí, acepto», queda registrado de forma permanente: el texto exacto que aceptaste (su versión y su huella digital SHA-256), la fecha y la hora, tu cuenta o correo, tu dirección IP y el dispositivo que usaste. Guardamos ese registro como prueba de tu aceptación y puedes pedirnos una constancia de él escribiendo a [email protected].

11. CAMBIOS DE ESTE TEXTO

Si este disclaimer cambia de forma importante, publicaremos la nueva versión y te pediremos aceptarla de nuevo antes de seguir. Cada versión queda guardada con su huella, y tu aceptación anterior sigue valiendo para la versión que aceptaste.

12. CONTACTO

SeixTech LLC · [email protected] · Este disclaimer se complementa con los Términos del Servicio (seix.net/terms) y la Política de Privacidad (seix.net/privacy), que también aceptas al usar el ecosistema.


Firma de la carta: Jhonn Lenon M — Fundador y CEO, SEIX TECH LLC.

Anclaje: la huella SHA-256 de esta carta acompaña en cadena la transacción de emisión del token (memo SEIX-RWA-CARTA), de modo que la carta, su parte legal y la emisión quedan enlazadas de forma verificable por cualquiera.

Código fuente completo del contrato

En XRPL el contrato es la configuración de su emisora + las reglas del protocolo; este es el código íntegro que lo crea, lo opera y asigna sus tokens. Las llaves de las cuentas viven solo en el servidor (archivo 600) y no forman parte de este expediente.

bootstrap-epv.js — nacimiento del contrato (emisora, banderas, emisión 6.000.000, liquidez DEX)
// bootstrap-epv.js — CONTRATO REAL «Épale Venezuela» (EPV), Fábrica Seix/RWA
// Orden del CEO 2026-10-30 11:27 ET: emisión total 6.000.000 EPV (el piloto de 10.000
// usuarios VE es el tramo 1 dentro de esta emisión). La emisión se ancla en cadena a la
// CARTA DEL CONTRATO (memo con su SHA-256) y al Disclaimer Universal v1.0 (memo con su
// SHA-256). Fase: XRPL TESTNET con mecánica real; mainnet solo con palabra del CEO.
// Receta: la misma probada en la instancia de fábrica (EPM), con las correcciones
// aprendidas — banderas completas (incl. Allow Trust Line Locking, SetFlag 17 firmado a
// mano), NoRipple limpiado por línea tras autorizar, ofertas dimensionadas al saldo real
// y venta DEX por oferta en reposo + toma fondeada de la tesorería.
// Las llaves quedan SOLO en data/cuentas-epv.json (600) en el servidor.
import * as xrpl from "xrpl";
import { encode, encodeForSigning } from "ripple-binary-codec";
import { sign as firmarKP, deriveKeypair } from "ripple-keypairs";
import { writeFileSync, existsSync, mkdirSync } from "node:fs";
import { createHash } from "node:crypto";

const RED = "wss://s.altnet.rippletest.net:51233";
const DIR = "/srv/rwa-sistema/data";
const MONEDA = "EPV";
const EMISION = "6000000";
const LIMITE = "6000000";
const CARTA_SHA256 = "4265c4470bcf219f6e2f636a3dae0e5d96d1f694f7cb3f164005d1877dbdb36f";
const DISCLAIMER_SHA256 = "7a5dbf830bb1aaeabb619182da50d1d80532fb187e120820c3ebd7898e71480c";
const P_VENTA = 0.1;
const P_COMPRA = 0.095;
const drops = (xrp) => String(Math.round(xrp * 1_000_000));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const hex = (s) => xrpl.convertStringToHex(s);

mkdirSync(DIR, { recursive: true });
if (existsSync(`${DIR}/cuentas-epv.json`)) {
  console.log("YA_EXISTE cuentas-epv.json — el contrato Épale Venezuela ya nació. Abortando sin tocar nada.");
  process.exit(0);
}

const evidencia = [];
function reg(paso, detalle, res, extra = {}) {
  const e = { paso, detalle, ...extra };
  if (res) {
    e.hash = res.result?.hash || res.hash || null;
    e.resultado = res.result?.meta?.TransactionResult || null;
    e.ledger = res.result?.ledger_index || null;
  }
  evidencia.push(e);
  console.log(`[${paso}] ${detalle} → ${e.resultado || ""} ${e.hash || ""}`);
  return e;
}
async function enviar(client, wallet, tx) {
  const prep = await client.autofill(tx);
  const firmado = wallet.sign(prep);
  const res = await client.submitAndWait(firmado.tx_blob);
  return { res, hash: firmado.hash };
}
async function firmarManual(client, wallet, seed, txBase) {
  const info = await client.request({ command: "account_info", account: wallet.address, ledger_index: "current" });
  const led = await client.request({ command: "ledger_current" });
  const tx = { ...txBase, Account: wallet.address, Fee: "12", Sequence: info.result.account_data.Sequence, LastLedgerSequence: led.result.ledger_current_index + 20, SigningPubKey: wallet.publicKey };
  const kp = deriveKeypair(seed);
  const blob = encode({ ...tx, TxnSignature: firmarKP(encodeForSigning(tx), kp.privateKey) });
  const envio = await client.submit(blob);
  const hash = envio.result.tx_json?.hash;
  for (let i = 0; i < 15; i++) {
    await sleep(3500);
    try { const t = await client.request({ command: "tx", transaction: hash }); if (t.result.meta?.TransactionResult) return { hash, resultado: t.result.meta.TransactionResult, sequence: tx.Sequence }; } catch { /* aún no validada */ }
  }
  return { hash, resultado: envio.result.engine_result, sequence: tx.Sequence };
}
async function fondear(client, nombre) {
  for (let i = 1; i <= 4; i++) {
    try {
      const { wallet } = await client.fundWallet();
      console.log(`[fondeo] ${nombre}: ${wallet.address}`);
      return wallet;
    } catch (e) {
      console.log(`[fondeo] ${nombre}: intento ${i} falló (${e.message}); reintentando…`);
      await sleep(5000);
    }
  }
  throw new Error("No se pudo fondear " + nombre);
}
async function linea(client, cuenta, emisor) {
  const r = await client.request({ command: "account_lines", account: cuenta, peer: emisor, ledger_index: "validated" });
  const l = (r.result.lines || []).find((x) => x.currency === MONEDA);
  return l ? Number(l.balance) : 0;
}
async function trustYAuth(client, emisorW, holderW, nombre) {
  const t = await enviar(client, holderW, {
    TransactionType: "TrustSet", Account: holderW.address,
    LimitAmount: { currency: MONEDA, issuer: emisorW.address, value: LIMITE },
  });
  reg("trustline-" + nombre, `Línea de confianza de ${nombre} hacia la emisora`, t.res, { hash: t.hash });
  const a = await enviar(client, emisorW, {
    TransactionType: "TrustSet", Account: emisorW.address,
    LimitAmount: { currency: MONEDA, issuer: holderW.address, value: "0" },
    Flags: xrpl.TrustSetFlags.tfSetfAuth,
  });
  reg("autorizacion-" + nombre, `Emisora autoriza la tenencia de ${nombre} (acto que sigue al veredicto SID/KYC)`, a.res, { hash: a.hash });
  const nr = await enviar(client, emisorW, {
    TransactionType: "TrustSet", Account: emisorW.address,
    LimitAmount: { currency: MONEDA, issuer: holderW.address, value: "0" },
    Flags: xrpl.TrustSetFlags.tfClearNoRipple,
  });
  reg("noripple-" + nombre, `Emisora limpia NoRipple en su lado de la línea de ${nombre} (habilita P2P entre autorizados)`, nr.res, { hash: nr.hash });
}

const client = new xrpl.Client(RED);
await client.connect();
console.log("Conectado a XRPL testnet.");

// ── 1. Cuentas del contrato ─────────────────────────────────────────────────
const emisor = await fondear(client, "emisora");
const tesoreria = await fondear(client, "tesoreria");
const inversionista = await fondear(client, "inversionista-demo");
const gobernanza = await fondear(client, "gobernanza-demo");

// ── 2. Banderas de la emisora (clawback ANTES de cualquier trustline) ──────
for (const [nombre, flag] of [
  ["RequireAuth", xrpl.AccountSetAsfFlags.asfRequireAuth],
  ["AllowTrustLineClawback", xrpl.AccountSetAsfFlags.asfAllowTrustLineClawback],
  ["DisallowXRP", xrpl.AccountSetAsfFlags.asfDisallowXRP],
]) {
  const r = await enviar(client, emisor, { TransactionType: "AccountSet", Account: emisor.address, SetFlag: flag });
  reg("emisora-" + nombre, `AccountSet ${nombre}`, r.res, { hash: r.hash });
}
const lock = await firmarManual(client, emisor, emisor.seed, { TransactionType: "AccountSet", SetFlag: 17 });
reg("emisora-AllowTrustLineLocking", "AccountSet Allow Trust Line Locking (SetFlag 17, firma manual): habilita los escrows del token", null, { hash: lock.hash, resultadoForzado: lock.resultado });

// ── 3. Trustlines + autorización + NoRipple ─────────────────────────────────
await trustYAuth(client, emisor, tesoreria, "tesoreria");
await trustYAuth(client, emisor, inversionista, "inversionista");
await trustYAuth(client, emisor, gobernanza, "gobernanza");

// ── 4. EMISIÓN 6.000.000 EPV anclada a la carta y al disclaimer ────────────
const acta = `SEIX-RWA|CONTRATO-EPALE-VENEZUELA|EPV|EMISION-6000000|CARTA-${CARTA_SHA256.slice(0, 16)}|2026-10-30`;
const hashActa = createHash("sha256").update(acta).digest("hex");
const emi = await enviar(client, emisor, {
  TransactionType: "Payment",
  Account: emisor.address,
  Destination: tesoreria.address,
  Amount: { currency: MONEDA, issuer: emisor.address, value: EMISION },
  Memos: [
    { Memo: { MemoType: hex("SEIX-RWA-CARTA"), MemoData: hex(CARTA_SHA256) } },
    { Memo: { MemoType: hex("SEIX-RWA-DISCLAIMER"), MemoData: hex(DISCLAIMER_SHA256) } },
    { Memo: { MemoType: hex("SEIX-RWA-ACTA"), MemoData: hex(hashActa) } },
  ],
});
reg("emision", `Emisión de ${EMISION} EPV a la tesorería, anclada a la CARTA (SHA-256 ${CARTA_SHA256.slice(0, 16)}…) y al Disclaimer Universal v1.0 (SHA-256 ${DISCLAIMER_SHA256.slice(0, 16)}…)`, emi.res, { hash: emi.hash, hashActa, cartaSha256: CARTA_SHA256, disclaimerSha256: DISCLAIMER_SHA256 });

const gb = await client.request({ command: "gateway_balances", account: emisor.address, ledger_index: "validated" });
reg("circulacion", `Suministro en circulación según gateway_balances: ${JSON.stringify(gb.result.obligations || {})}`, null);

// ── 5. Liquidez DEX dimensionada al saldo real ──────────────────────────────
const ov = await enviar(client, tesoreria, {
  TransactionType: "OfferCreate", Account: tesoreria.address,
  TakerGets: { currency: MONEDA, issuer: emisor.address, value: "50000" },
  TakerPays: drops(50000 * P_VENTA),
});
reg("dex-oferta-venta", `Tesorería ofrece vender 50.000 EPV a ${P_VENTA} XRP (fondeada con su saldo EPV)`, ov.res, { hash: ov.hash });
const oc = await enviar(client, tesoreria, {
  TransactionType: "OfferCreate", Account: tesoreria.address,
  TakerGets: drops(700 * P_COMPRA),
  TakerPays: { currency: MONEDA, issuer: emisor.address, value: "700" },
});
reg("dex-oferta-compra", `Tesorería ofrece recomprar 700 EPV a ${P_COMPRA} XRP (fondeada con su saldo XRP real)`, oc.res, { hash: oc.hash });

// ── 6. Compra DEX del inversionista demo (forma probada) ────────────────────
const compra = await enviar(client, inversionista, {
  TransactionType: "OfferCreate", Account: inversionista.address,
  TakerGets: drops(100 * P_VENTA),
  TakerPays: { currency: MONEDA, issuer: emisor.address, value: "100" },
  Flags: xrpl.OfferCreateFlags.tfImmediateOrCancel,
});
const invTrasCompra = await linea(client, inversionista.address, emisor.address);
reg("dex-compra-inversionista", `Compra DEX: el inversionista paga 10 XRP y recibe 100 EPV (saldo → ${invTrasCompra})`, compra.res, { hash: compra.hash });

// ── 7. Venta DEX del inversionista (oferta en reposo + toma fondeada) ───────
const ofertaVenta = await enviar(client, inversionista, {
  TransactionType: "OfferCreate", Account: inversionista.address,
  TakerGets: { currency: MONEDA, issuer: emisor.address, value: "25" },
  TakerPays: drops(25 * P_COMPRA),
});
reg("dex-venta-oferta", "El inversionista coloca su oferta de venta de 25 EPV en el libro", ofertaVenta.res, { hash: ofertaVenta.hash });
const toma = await enviar(client, tesoreria, {
  TransactionType: "OfferCreate", Account: tesoreria.address,
  TakerGets: drops(25 * P_COMPRA),
  TakerPays: { currency: MONEDA, issuer: emisor.address, value: "25" },
});
const invTrasVenta = await linea(client, inversionista.address, emisor.address);
reg("dex-venta-inversionista", `Venta DEX ejecutada: la tesorería toma la oferta del inversionista (saldo → ${invTrasVenta} EPV)`, toma.res, { hash: toma.hash });

// ── 8. Foto final y custodia de llaves (solo servidor) ──────────────────────
const foto = {
  tesoreria_EPV: await linea(client, tesoreria.address, emisor.address),
  inversionista_EPV: await linea(client, inversionista.address, emisor.address),
  gobernanza_EPV: await linea(client, gobernanza.address, emisor.address),
};
reg("foto-final", `Saldos finales: ${JSON.stringify(foto)}`, null);

const cuentas = {
  red: "XRPL TESTNET",
  instancia: "EPALE-VENEZUELA",
  nombre: "Épale Venezuela",
  moneda: MONEDA,
  emision: EMISION,
  carta_sha256: CARTA_SHA256,
  disclaimer_sha256: DISCLAIMER_SHA256,
  precios_demo: { venta: P_VENTA, compra: P_COMPRA },
  emisor: { address: emisor.address, seed: emisor.seed },
  tesoreria: { address: tesoreria.address, seed: tesoreria.seed },
  inversionista: { address: inversionista.address, seed: inversionista.seed },
  gobernanza: { address: gobernanza.address, seed: gobernanza.seed },
  creado: new Date().toISOString(),
};
writeFileSync(`${DIR}/cuentas-epv.json`, JSON.stringify(cuentas, null, 2), { mode: 0o600 });
writeFileSync(`${DIR}/evidencia-epv.json`, JSON.stringify({ instancia: cuentas.instancia, moneda: MONEDA, direcciones: { emisor: emisor.address, tesoreria: tesoreria.address, inversionista: inversionista.address, gobernanza: gobernanza.address }, evidencia }, null, 2), { mode: 0o644 });
console.log("LISTO. cuentas-epv.json (600) y evidencia-epv.json escritos en " + DIR);
await client.disconnect();
asignar-epv.js — asignación sincronizada con el CRM/ERP (escrow del lote por membresía)
// asignar-epv.js — Asignación de prueba sincronizada con el CRM/ERP de Épale.
// Uso: bun run asignar-epv.js <id_membresia_erp>
// Ejecuta la asignación del lote de una membresía PREMIUM ANUAL (39,68 EPV por la escala)
// desde la tesorería del contrato Épale Venezuela hacia la wallet del usuario demo,
// BLOQUEADA en un escrow del token (en producción el FinishAfter es el primer tramo del
// calendario 90 días–6 meses; en esta prueba de testnet el tramo se comprime a ~90 s y el
// memo lo declara). El memo de la operación ancla: membresía ERP, nivel, país, SID y la
// huella de la carta. Al final imprime una línea JSON con los hashes para asentar el
// registro en epale_proyecto_tokens (la sincronización CRM ↔ cadena).
import * as xrpl from "xrpl";
import { encode, encodeForSigning } from "ripple-binary-codec";
import { sign as firmarKP, deriveKeypair } from "ripple-keypairs";
import { readFileSync, writeFileSync } from "node:fs";

const DIR = "/srv/rwa-sistema/data";
const MONEDA = "EPV";
const MEMBRESIA_ID = process.argv[2];
if (!MEMBRESIA_ID) { console.error("Falta el id de la membresía del ERP."); process.exit(1); }
const NIVEL = "PREMIUM ANUAL";
const PAIS = "VE";
const SID = "SID-DEMO-EPV-0001";
const CANTIDAD = "39.68";
const c = JSON.parse(readFileSync(`${DIR}/cuentas-epv.json`, "utf8"));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const rippleNow = () => Math.floor(Date.now() / 1000) - 946684800;
const hex = (s) => xrpl.convertStringToHex(s);

const client = new xrpl.Client("wss://s.altnet.rippletest.net:51233");
await client.connect();
const tesoreria = xrpl.Wallet.fromSeed(c.tesoreria.seed);
const inversionista = xrpl.Wallet.fromSeed(c.inversionista.seed);

async function firmarManual(wallet, seed, txBase) {
  const info = await client.request({ command: "account_info", account: wallet.address, ledger_index: "current" });
  const led = await client.request({ command: "ledger_current" });
  const tx = { ...txBase, Account: wallet.address, Fee: "12", Sequence: info.result.account_data.Sequence, LastLedgerSequence: led.result.ledger_current_index + 20, SigningPubKey: wallet.publicKey };
  const kp = deriveKeypair(seed);
  const blob = encode({ ...tx, TxnSignature: firmarKP(encodeForSigning(tx), kp.privateKey) });
  const envio = await client.submit(blob);
  const hash = envio.result.tx_json?.hash;
  for (let i = 0; i < 15; i++) {
    await sleep(3500);
    try { const t = await client.request({ command: "tx", transaction: hash }); if (t.result.meta?.TransactionResult) return { hash, resultado: t.result.meta.TransactionResult, sequence: tx.Sequence }; } catch { /* aún no validada */ }
  }
  return { hash, resultado: envio.result.engine_result, sequence: tx.Sequence };
}
async function enviar(wallet, tx) {
  const prep = await client.autofill(tx);
  const f = wallet.sign(prep);
  const res = await client.submitAndWait(f.tx_blob);
  return { hash: f.hash, resultado: res.result.meta.TransactionResult };
}
async function linea(cuenta) {
  const r = await client.request({ command: "account_lines", account: cuenta, peer: c.emisor.address, ledger_index: "validated" });
  const l = (r.result.lines || []).find((x) => x.currency === MONEDA);
  return l ? l.balance : "0";
}

const memoTxt = `SEIX-RWA-ASIG|ERP-MEMBRESIA-${MEMBRESIA_ID}|${NIVEL}|${PAIS}|${SID}|${CANTIDAD} EPV|CARTA-${c.carta_sha256.slice(0, 16)}|TRAMO-DEMO-COMPRIMIDO`;
const fin = rippleNow() + 90;
const crear = await firmarManual(tesoreria, c.tesoreria.seed, {
  TransactionType: "EscrowCreate",
  Destination: inversionista.address,
  Amount: { currency: MONEDA, issuer: c.emisor.address, value: CANTIDAD },
  FinishAfter: fin,
  CancelAfter: fin + 86400,
  Memos: [{ Memo: { MemoType: hex("SEIX-RWA-ASIG"), MemoData: hex(memoTxt) } }],
});
console.log(`[asignacion-crear] Escrow del lote asignado (${CANTIDAD} EPV bloqueados) → ${crear.resultado} ${crear.hash}`);
if (crear.resultado !== "tesSUCCESS") { console.error("La asignación no se creó; no se asienta registro."); process.exit(1); }
console.log("[asignacion] esperando el vencimiento del tramo demo (~95 s)…");
await sleep(95000);
const finTx = await enviar(inversionista, { TransactionType: "EscrowFinish", Account: inversionista.address, Owner: tesoreria.address, OfferSequence: crear.sequence });
const saldo = await linea(inversionista.address);
console.log(`[asignacion-finalizar] Liberación del tramo al usuario → ${finTx.resultado} ${finTx.hash} · saldo del usuario: ${saldo} EPV`);
const salida = {
  membresia_erp_id: Number(MEMBRESIA_ID), nivel: NIVEL, pais: PAIS, sid: SID, cantidad: Number(CANTIDAD),
  escrow_crear_hash: crear.hash, escrow_finalizar_hash: finTx.hash, resultado: finTx.resultado,
  saldo_usuario_tras_asignacion: saldo, memo: memoTxt, fecha: new Date().toISOString(),
};
writeFileSync(`${DIR}/asignacion-epv.json`, JSON.stringify(salida, null, 2), { mode: 0o644 });
console.log("ASIGNACION_JSON " + JSON.stringify(salida));
await client.disconnect();
server.js — servicio del sistema (rwa-sistema: proyectos, tenencias, DEX, P2P con la banda)
// server.js — Servicio del Sistema de Tokenización Seix/RWA (instancia n.º 1: EPM, XRPL TESTNET)
// Sirve la interfaz del inversionista: proyectos de la fábrica, estado en vivo de la instancia,
// tenencias de la cuenta demo y operaciones DEX/P2P de demostración contra testnet.
// Las llaves viven SOLO en /srv/rwa-sistema/data/cuentas.json (600); jamás salen del servidor.
// Reglas aplicadas (órdenes del CEO 2026-10-30): canje en ecosistema $1,00 · P2P $0,50–$0,75 ·
// salida a cash $0,10 (<6 meses) / $0,50 (>6 meses) · liberación 90 días–6 meses con membresía
// activa · las ofertas de liquidez se dimensionan al saldo real (hallazgo de la prueba viva).
import * as xrpl from "xrpl";
import { readFileSync } from "node:fs";

const PUERTO = 3179;
const RED = "wss://s.altnet.rippletest.net:51233";
// Contrato vigente: «Épale Venezuela» (EPV, 6.000.000) — orden del CEO 2026-10-30 11:27 ET.
const CUENTAS = JSON.parse(readFileSync("/srv/rwa-sistema/data/cuentas-epv.json", "utf8"));
const MONEDA = CUENTAS.moneda || "EPV";
const NOMBRE_TOKEN = CUENTAS.nombre || "Épale Venezuela";
let ASIGNACION_CRM = null;
try { ASIGNACION_CRM = JSON.parse(readFileSync("/srv/rwa-sistema/data/asignacion-epv.json", "utf8")); } catch { /* aún sin asignación */ }
const EXPLORER = "https://testnet.xrpl.org/transactions/";

const P_VENTA_XRP = 0.1;    // precio demo en testnet (XRP por EPV)
const P_RECOMPRA_XRP = 0.095;

const PROYECTOS = [
  { id: "epv", codigo: "EPV", nombre: "Épale Venezuela — contrato del proyecto", vertical: "Comunicaciones", pais: "Venezuela", estado: "activa-testnet", nota: "El contrato real del sistema (orden del CEO 2026-10-30): emisión 6.000.000 EPV anclada a su carta y al Disclaimer Universal v1.0. Tramo 1: piloto de 10.000 usuarios fundadores de Épale Móvil." },
  { id: "epm-ve", codigo: "EPM", nombre: "Épale Móvil — Expansión Venezuela (prueba de fábrica)", vertical: "Comunicaciones", pais: "Venezuela", estado: "historica", nota: "Instancia inicial con la que se probó la fábrica (ejemplo de 1.000.000, reemplazado por el contrato Épale Venezuela de 6.000.000)." },
  { id: "epc-co", codigo: "EPC", nombre: "Épale Móvil — Colombia", vertical: "Comunicaciones", pais: "Colombia", estado: "disenada", nota: "Réplica n.º 2: diseñada con factibilidad propia. No lanzada." },
  { id: "epe-ec", codigo: "EPE", nombre: "Épale Móvil — Ecuador", vertical: "Comunicaciones", pais: "Ecuador", estado: "disenada", nota: "Réplica n.º 3: diseñada con su envoltura país. No lanzada." },
  { id: "solar", codigo: "SOL", nombre: "Energía solar (plantilla)", vertical: "Energía", pais: "Por instancia", estado: "plantilla", nota: "Plantilla de la fábrica: tokeniza flujos del proyecto, nunca promesas de rendimiento." },
  { id: "salud", codigo: "SAL", nombre: "Salud — MiCardio (plantilla)", vertical: "Salud", pais: "Por instancia", estado: "plantilla", nota: "Plantilla de la fábrica: los datos clínicos viven fuera de cadena; en cadena solo hashes de actas." },
];

let client = null;
let conectando = null;
async function xrplClient() {
  if (client && client.isConnected()) return client;
  if (conectando) return conectando;
  conectando = (async () => {
    client = new xrpl.Client(RED);
    await client.connect();
    conectando = null;
    return client;
  })();
  return conectando;
}
const wallets = {};
function wallet(nombre) {
  if (!wallets[nombre]) wallets[nombre] = xrpl.Wallet.fromSeed(CUENTAS[nombre].seed);
  return wallets[nombre];
}
async function linea(cuenta) {
  const c = await xrplClient();
  const r = await c.request({ command: "account_lines", account: cuenta, peer: CUENTAS.emisor.address, ledger_index: "validated" });
  const l = (r.result.lines || []).find((x) => x.currency === MONEDA);
  return l ? Number(l.balance) : 0;
}
async function xrpDe(cuenta) {
  const c = await xrplClient();
  const r = await c.request({ command: "account_info", account: cuenta, ledger_index: "validated" });
  return Number(r.result.account_data.Balance) / 1e6;
}
async function enviar(nombre, tx) {
  const c = await xrplClient();
  const w = wallet(nombre);
  const prep = await c.autofill(tx);
  const f = w.sign(prep);
  const res = await c.submitAndWait(f.tx_blob);
  return { hash: f.hash, resultado: res.result.meta.TransactionResult };
}
const drops = (x) => String(Math.round(x * 1e6));

// Serializa las operaciones firmadas (las secuencias no deben chocar).
let candado = Promise.resolve();
function conCandado(fn) {
  const run = candado.then(fn, fn);
  candado = run.then(() => {}, () => {});
  return run;
}
// Liquidez de la tesorería dimensionada a su saldo real (regla de la fábrica).
async function asegurarLiquidez() {
  const c = await xrplClient();
  const tes = CUENTAS.tesoreria.address;
  const [saldoXRP, saldoEPM, ofertas] = await Promise.all([
    xrpDe(tes), linea(tes),
    c.request({ command: "account_offers", account: tes, ledger_index: "validated" }),
  ]);
  const lista = ofertas.result.offers || [];
  const esVenta = (o) => typeof o.taker_gets === "object"; // entrega EPM
  const esCompra = (o) => typeof o.taker_gets === "string"; // entrega XRP
  const dispXRP = Math.max(0, saldoXRP - 12); // reserva de la cuenta
  let comprometido = 0;
  for (const o of lista.filter(esCompra)) {
    const xrp = Number(o.taker_gets) / 1e6;
    if (xrp <= dispXRP) { comprometido += xrp; continue; }
    await enviar("tesoreria", { TransactionType: "OfferCancel", Account: tes, OfferSequence: o.seq });
  }
  if (comprometido < 1 && dispXRP >= 1) {
    const xrpOferta = Math.min(dispXRP * 0.9, 50);
    await enviar("tesoreria", {
      TransactionType: "OfferCreate", Account: tes,
      TakerGets: drops(xrpOferta),
      TakerPays: { currency: MONEDA, issuer: CUENTAS.emisor.address, value: String(Math.floor(xrpOferta / P_RECOMPRA_XRP)) },
    });
  }
  const ventaEPM = lista.filter(esVenta).reduce((s, o) => s + Number(o.taker_gets.value), 0);
  if (ventaEPM < 5000 && saldoEPM > 10000) {
    await enviar("tesoreria", {
      TransactionType: "OfferCreate", Account: tes,
      TakerGets: { currency: MONEDA, issuer: CUENTAS.emisor.address, value: "50000" },
      TakerPays: drops(50000 * P_VENTA_XRP),
    });
  }
}

const limitePorIP = new Map();
function limitado(ip) {
  const ahora = Date.now();
  const arr = (limitePorIP.get(ip) || []).filter((t) => ahora - t < 60000);
  arr.push(ahora);
  limitePorIP.set(ip, arr);
  return arr.length > 30;
}
const json = (obj, code = 200) => new Response(JSON.stringify(obj), { status: code, headers: { "Content-Type": "application/json; charset=utf-8", "Access-Control-Allow-Origin": "https://seix.net", "Cache-Control": "no-store" } });

let cacheProyecto = { t: 0, datos: null };
async function datosProyecto() {
  if (Date.now() - cacheProyecto.t < 15000 && cacheProyecto.datos) return cacheProyecto.datos;
  const c = await xrplClient();
  const [gb, ofertasTes, libroVenta, libroCompra, saldoTes] = await Promise.all([
    c.request({ command: "gateway_balances", account: CUENTAS.emisor.address, ledger_index: "validated" }),
    c.request({ command: "account_offers", account: CUENTAS.tesoreria.address, ledger_index: "validated" }),
    c.request({ command: "book_offers", taker_gets: { currency: MONEDA, issuer: CUENTAS.emisor.address }, taker_pays: { currency: "XRP" }, limit: 5, ledger_index: "validated" }),
    c.request({ command: "book_offers", taker_gets: { currency: "XRP" }, taker_pays: { currency: MONEDA, issuer: CUENTAS.emisor.address }, limit: 5, ledger_index: "validated" }),
    linea(CUENTAS.tesoreria.address),
  ]);
  const obligaciones = gb.result.obligations?.[MONEDA] || "0";
  const fmtOferta = (o, dueno) => ({
    dueno,
    entrega: typeof o.TakerGets === "string" ? { moneda: "XRP", valor: Number(o.TakerGets) / 1e6 } : { moneda: MONEDA, valor: Number(o.TakerGets.value) },
    recibe: typeof o.TakerPays === "string" ? { moneda: "XRP", valor: Number(o.TakerPays) / 1e6 } : { moneda: MONEDA, valor: Number(o.TakerPays.value) },
  });
  const datos = {
    emisora: CUENTAS.emisor.address,
    tesoreria: CUENTAS.tesoreria.address,
    moneda: MONEDA,
    nombreToken: NOMBRE_TOKEN,
    emisionTotal: Number(CUENTAS.emision || 6000000),
    cartaSha256: CUENTAS.carta_sha256 || null,
    disclaimerSha256: CUENTAS.disclaimer_sha256 || null,
    enCirculacion: Number(obligaciones),
    enTesoreria: saldoTes,
    precioVentaDemoXRP: P_VENTA_XRP,
    precioRecompraDemoXRP: P_RECOMPRA_XRP,
    libro: [
      ...(libroVenta.result.offers || []).map((o) => fmtOferta(o, o.Account)),
      ...(libroCompra.result.offers || []).map((o) => fmtOferta(o, o.Account)),
    ].slice(0, 8),
    ofertasTesoreria: (ofertasTes.result.offers || []).length,
    actualizado: new Date().toISOString(),
  };
  cacheProyecto = { t: Date.now(), datos };
  return datos;
}

async function datosInversionista() {
  const c = await xrplClient();
  const dir = CUENTAS.inversionista.address;
  const [epm, xrp, txs] = await Promise.all([
    linea(dir), xrpDe(dir),
    c.request({ command: "account_tx", account: dir, limit: 8, ledger_index_min: -1, ledger_index_max: -1 }),
  ]);
  const ops = (txs.result.transactions || []).map(({ tx, meta }) => ({
    tipo: tx.TransactionType,
    hash: tx.hash,
    resultado: meta?.TransactionResult || null,
    fecha: tx.date ? new Date((tx.date + 946684800) * 1000).toISOString() : null,
    enlace: EXPLORER + tx.hash,
  }));
  return {
    direccion: dir,
    moneda: MONEDA,
    sid: "verificado (demo)",
    tenenciaEPM: epm,
    saldoXRP: xrp,
    valorEcosistemaUSD: epm * 1,
    valorCashMayor6mUSD: epm * 0.5,
    valorCashMenor6mUSD: epm * 0.1,
    operaciones: ops,
    asignacionCRM: ASIGNACION_CRM ? {
      membresiaERP: ASIGNACION_CRM.membresia_erp_id, nivel: ASIGNACION_CRM.nivel, pais: ASIGNACION_CRM.pais,
      sid: ASIGNACION_CRM.sid, cantidad: ASIGNACION_CRM.cantidad,
      escrowCrear: ASIGNACION_CRM.escrow_crear_hash, escrowFinalizar: ASIGNACION_CRM.escrow_finalizar_hash,
      enlaceCrear: EXPLORER + ASIGNACION_CRM.escrow_crear_hash, enlaceFinalizar: EXPLORER + ASIGNACION_CRM.escrow_finalizar_hash,
    } : null,
    vestingDemo: {
      nota: "Calendario ilustrativo del sistema (propuesta de 4 tramos, pendiente de aprobación del CEO): un otorgamiento por la escala queda bloqueado y libera 25 % a los 90 días y 25 % en los meses 4, 5 y 6, solo con la membresía activa.",
      ejemplo: "PREMIUM ANUAL $496 → 39,68 EPV: 9,92 EPV por tramo.",
    },
  };
}

Bun.serve({
  port: PUERTO,
  hostname: "127.0.0.1",
  async fetch(req) {
    const url = new URL(req.url);
    const ruta = url.pathname.replace(/^\/rwa-sistema/, "") || "/";
    const ip = req.headers.get("x-forwarded-for") || "local";
    try {
      if (ruta === "/salud") return json({ ok: true, servicio: "rwa-sistema", red: "XRPL TESTNET", instancia: "EPV · Épale Venezuela (contrato, 6.000.000)" });
      if (ruta === "/proyectos") return json({ proyectos: PROYECTOS });
      if (ruta === "/proyecto") return json(await datosProyecto());
      if (ruta === "/inversionista") return json(await datosInversionista());
      if (req.method === "POST" && ["/comprar", "/vender", "/p2p"].includes(ruta)) {
        if (limitado(ip)) return json({ error: "Demasiadas operaciones. Espera un minuto." }, 429);
        const body = await req.json().catch(() => ({}));
        const cantidad = Number(body.cantidad);
        if (!Number.isFinite(cantidad) || cantidad < 1 || cantidad > 5000) return json({ error: "Cantidad fuera del rango de demostración (1 a 5.000 EPM)." }, 400);
        return await conCandado(async () => {
          if (ruta === "/comprar") {
            const antes = await linea(CUENTAS.inversionista.address);
            const r = await enviar("inversionista", {
              TransactionType: "OfferCreate", Account: CUENTAS.inversionista.address,
              TakerGets: drops(cantidad * P_VENTA_XRP),
              TakerPays: { currency: MONEDA, issuer: CUENTAS.emisor.address, value: String(cantidad) },
              Flags: xrpl.OfferCreateFlags.tfImmediateOrCancel,
            });
            const despues = await linea(CUENTAS.inversionista.address);
            await asegurarLiquidez();
            return json({ ok: r.resultado === "tesSUCCESS" && despues > antes, hash: r.hash, resultado: r.resultado, comprados: Math.max(0, despues - antes), enlace: EXPLORER + r.hash });
          }
          if (ruta === "/vender") {
            const antes = await linea(CUENTAS.inversionista.address);
            if (antes < cantidad) return json({ error: "La cuenta demo no tiene esa cantidad de EPM." }, 400);
            // Forma probada en cadena: oferta en reposo del inversionista + toma fondeada de la tesorería.
            const oferta = await enviar("inversionista", {
              TransactionType: "OfferCreate", Account: CUENTAS.inversionista.address,
              TakerGets: { currency: MONEDA, issuer: CUENTAS.emisor.address, value: String(cantidad) },
              TakerPays: drops(cantidad * P_RECOMPRA_XRP),
            });
            const toma = await enviar("tesoreria", {
              TransactionType: "OfferCreate", Account: CUENTAS.tesoreria.address,
              TakerGets: drops(cantidad * P_RECOMPRA_XRP),
              TakerPays: { currency: MONEDA, issuer: CUENTAS.emisor.address, value: String(cantidad) },
            });
            const despues = await linea(CUENTAS.inversionista.address);
            await asegurarLiquidez();
            return json({ ok: despues < antes, hash: toma.hash, hashOferta: oferta.hash, resultado: toma.resultado, vendidos: Math.max(0, antes - despues), enlace: EXPLORER + toma.hash });
          }
          // /p2p — transferencia entre personas con el precio acordado en la banda, registrado en memo.
          const precio = Number(body.precio);
          if (!Number.isFinite(precio) || precio < 0.5 || precio > 0.7) return json({ error: "El precio P2P debe estar dentro de la banda de la plataforma: US$0,50 a US$0,70 por token." }, 400);
          const antes = await linea(CUENTAS.inversionista.address);
          if (antes < cantidad) return json({ error: "La cuenta demo no tiene esa cantidad de EPM." }, 400);
          const memo = xrpl.convertStringToHex(`SEIX-RWA-P2P|${cantidad} EPM|${precio.toFixed(2)} USD/EPM|DEMO-TESTNET`);
          const r = await enviar("inversionista", {
            TransactionType: "Payment", Account: CUENTAS.inversionista.address,
            Destination: CUENTAS.gobernanza.address,
            Amount: { currency: MONEDA, issuer: CUENTAS.emisor.address, value: String(cantidad) },
            Memos: [{ Memo: { MemoType: xrpl.convertStringToHex("SEIX-RWA-P2P"), MemoData: memo } }],
          });
          return json({ ok: r.resultado === "tesSUCCESS", hash: r.hash, resultado: r.resultado, enlace: EXPLORER + r.hash, nota: "Transferencia de demostración entre dos cuentas verificadas (demo). En producción el precio acordado se liquida por SeiXw/RLUSD y el token se mueve igual, en cadena." });
        });
      }
      return json({ error: "Ruta no encontrada" }, 404);
    } catch (e) {
      return json({ error: "El servicio de testnet no respondió: " + e.message }, 502);
    }
  },
});
console.log(`rwa-sistema escuchando en 127.0.0.1:${PUERTO}`);

SEIX TECH LLC · Sistema de Tokenización Seix/RWA · Fase testnet con mecánica real — la red principal se activa por fases, solo con la estructura legal aprobada y la autorización expresa del CEO.