<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cardumen</title>
    <description>The latest articles on DEV Community by Cardumen (@cardumen_c23712b080b2a053).</description>
    <link>https://dev.to/cardumen_c23712b080b2a053</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3660593%2F96533b46-d31a-4007-8f06-4977dd5721b8.png</url>
      <title>DEV Community: Cardumen</title>
      <link>https://dev.to/cardumen_c23712b080b2a053</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cardumen_c23712b080b2a053"/>
    <language>en</language>
    <item>
      <title>Tutorial: Midnight Wallet SDK - Guía Completa para Desarrolladores</title>
      <dc:creator>Cardumen</dc:creator>
      <pubDate>Wed, 17 Dec 2025 18:17:32 +0000</pubDate>
      <link>https://dev.to/cardumen_c23712b080b2a053/tutorial-midnight-wallet-sdk-guia-completa-para-desarrolladores-20e7</link>
      <guid>https://dev.to/cardumen_c23712b080b2a053/tutorial-midnight-wallet-sdk-guia-completa-para-desarrolladores-20e7</guid>
      <description>&lt;p&gt;Índice&lt;/p&gt;

&lt;p&gt;Introducción&lt;br&gt;
Instalación&lt;br&gt;
Conceptos Clave&lt;br&gt;
Crear Instancias de Billetera&lt;br&gt;
Gestión de Direcciones&lt;br&gt;
Trabajar con Transacciones&lt;br&gt;
Conectar con DApps&lt;br&gt;
Ejemplos Prácticos&lt;/p&gt;

&lt;p&gt;Introducción&lt;br&gt;
Midnight Wallet SDK es un conjunto de herramientas que permite integrar funcionalidad de billetera en aplicaciones blockchain. El SDK proporciona abstracciones de alto nivel para gestionar operaciones comunes como:&lt;/p&gt;

&lt;p&gt;Derivación de claves - Generar claves criptográficas a partir de una semilla&lt;br&gt;
Balance de tokens - Obtener el saldo de tokens desde datos de la cadena&lt;br&gt;
Gestión de monedas - Administrar monedas y datos para pruebas de conocimiento cero&lt;br&gt;
Conexión con indexadores - Sincronizar estado con servidores indexadores&lt;br&gt;
Creación de transacciones - Preparar y enviar transferencias de tokens&lt;/p&gt;

&lt;p&gt;Paquetes Principales&lt;br&gt;
PaqueteDescripción@midnight-ntwrk/wallet-apiInterfaces de alto nivel para billeteras@midnight-ntwrk/walletImplementación de wallet-api con herramientas adicionales@midnight-ntwrk/zswapConstrucción de bloques de Zswap@midnight-ntwrk/wallet-sdk-hdSoporte para billeteras jerárquico-determinísticas (HD)@midnight-ntwrk/wallet-sdk-address-formatSoporte para formato de dirección Bech32m&lt;/p&gt;

&lt;p&gt;Instalación&lt;br&gt;
Requisitos Previos&lt;/p&gt;

&lt;p&gt;Node.js instalado&lt;br&gt;
Yarn o npm como gestor de paquetes&lt;/p&gt;

&lt;p&gt;Instalación del SDK&lt;br&gt;
Usa Yarn para instalar el paquete:&lt;br&gt;
bashyarn add @midnight-ntwrk/wallet/&lt;br&gt;
Reemplaza  con la versión requerida según la matriz de compatibilidad de versiones.&lt;br&gt;
⚠️ Importante: A partir de la versión 4.0.0 hay cambios significativos. Consulta la matriz de compatibilidad para asegurar que usas la versión correcta.&lt;br&gt;
Información Crítica sobre v4.0.0&lt;/p&gt;

&lt;p&gt;Cambio importante: Las claves secretas ya no se incluyen en el estado serializado de la billetera&lt;br&gt;
Restauración: Si restauras una billetera desde una instantánea, debes proporcionar la semilla original&lt;br&gt;
Seguridad: Asegúrate de gestionar datos sensibles de forma segura&lt;/p&gt;

&lt;p&gt;Conceptos Clave&lt;br&gt;
Semilla de Billetera (Wallet Seed)&lt;br&gt;
La semilla es un valor aleatorio (usualmente 256 bits) que se usa para derivar todas las claves criptográficas de tu billetera. Es esencial para:&lt;/p&gt;

&lt;p&gt;Recuperar una billetera&lt;br&gt;
Generar direcciones determinísticas&lt;br&gt;
Restaurar estado desde instantáneas&lt;/p&gt;

&lt;p&gt;Billeteras HD (Jerárquico-Determinísticas)&lt;br&gt;
El SDK utiliza derivación BIP-32 siguiendo la ruta:&lt;br&gt;
m / 44' / 2400' / account' / role / index&lt;br&gt;
Donde:&lt;/p&gt;

&lt;p&gt;account - Número de cuenta (recomendación BIP-44)&lt;br&gt;
role - Tipo de rol (3 para Zswap)&lt;br&gt;
index - Índice específico (recomendación BIP-44)&lt;/p&gt;

&lt;p&gt;Ciclo de Vida de la Billetera&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crear instancia → 2. Iniciar sincronización → 3. Usar billetera → 4. Cerrar
NetworkId (Identificador de Red)
Define la red blockchain objetivo:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NetworkId.TestNet - Red de prueba&lt;br&gt;
NetworkId.MainNet - Red principal&lt;br&gt;
NetworkId.DevNet - Red de desarrollo&lt;/p&gt;

&lt;p&gt;Crear Instancias de Billetera&lt;br&gt;
Paso 1: Importar el Constructor&lt;br&gt;
javascriptimport { WalletBuilder } from '@midnight-ntwrk/wallet';&lt;br&gt;
import { NetworkId } from '@midnight-ntwrk/zswap';&lt;br&gt;
Paso 2: Generar una Semilla&lt;br&gt;
Para testing, puedes generar una semilla aleatoria:&lt;br&gt;
javascriptimport { generateRandomSeed } from '@midnight-ntwrk/wallet-sdk-hd';&lt;/p&gt;

&lt;p&gt;const semillaAleatoria = generateRandomSeed();&lt;br&gt;
console.log('Semilla generada:', semillaAleatoria);&lt;br&gt;
Para producción, usa derivación HD:&lt;br&gt;
javascriptimport {&lt;br&gt;
  generateRandomSeed,&lt;br&gt;
  HDWallet,&lt;br&gt;
  Roles,&lt;br&gt;
} from "@midnight-ntwrk/wallet-sdk-hd";&lt;/p&gt;

&lt;p&gt;const semilla = generateRandomSeed();&lt;/p&gt;

&lt;p&gt;function derivarSemillaDerivacion(semilla) {&lt;br&gt;
  const billetera = HDWallet.fromSeed(semilla);&lt;/p&gt;

&lt;p&gt;if (billetera.type != "seedOk") {&lt;br&gt;
    throw new Error("Error inicializando HD Wallet");&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;const zswapKey = billetera.hdWallet&lt;br&gt;
    .selectAccount(0)&lt;br&gt;
    .selectRole(Roles.Zswap)&lt;br&gt;
    .deriveKeyAt(0);&lt;/p&gt;

&lt;p&gt;if (zswapKey.type === "keyDerived") {&lt;br&gt;
    return zswapKey.key;&lt;br&gt;
  } else {&lt;br&gt;
    throw new Error("Error derivando clave");&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;const semillaFinal = derivarSemillaDerivacion(semilla);&lt;br&gt;
console.log('Semilla derivada:', semillaFinal);&lt;br&gt;
Paso 3: Construir la Instancia&lt;br&gt;
Opción A: Desde una Semilla Específica&lt;br&gt;
javascriptimport { WalletBuilder } from '@midnight-ntwrk/wallet';&lt;br&gt;
import { NetworkId } from '@midnight-ntwrk/zswap';&lt;/p&gt;

&lt;p&gt;const billetera = await WalletBuilder.build(&lt;br&gt;
  '&lt;a href="https://indexer.testnet-02.midnight.network/api/v1/graphql" rel="noopener noreferrer"&gt;https://indexer.testnet-02.midnight.network/api/v1/graphql&lt;/a&gt;',           // URL del Indexador&lt;br&gt;
  'wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws',          // WebSocket del Indexador&lt;br&gt;
  '&lt;a href="http://localhost:6300" rel="noopener noreferrer"&gt;http://localhost:6300&lt;/a&gt;',                                                  // URL del Servidor de Pruebas&lt;br&gt;
  '&lt;a href="https://rpc.testnet-02.midnight.network" rel="noopener noreferrer"&gt;https://rpc.testnet-02.midnight.network&lt;/a&gt;',                              // URL del Nodo&lt;br&gt;
  '0000000000000000000000000000000000000000000000000000000000000000',    // Semilla&lt;br&gt;
  NetworkId.TestNet,                                                       // Red&lt;br&gt;
  'error'                                                                   // Nivel de Log (opcional)&lt;br&gt;
);&lt;br&gt;
Opción B: Con Semilla Aleatoria (Testing)&lt;br&gt;
javascriptimport { WalletBuilder } from '@midnight-ntwrk/wallet';&lt;br&gt;
import { NetworkId } from '@midnight-ntwrk/zswap';&lt;br&gt;
import { generateRandomSeed } from '@midnight-ntwrk/wallet-sdk-hd';&lt;/p&gt;

&lt;p&gt;const semillaAleatoria = generateRandomSeed();&lt;/p&gt;

&lt;p&gt;const billetera = await WalletBuilder.build(&lt;br&gt;
  '&lt;a href="https://indexer.testnet-02.midnight.network/api/v1/graphql" rel="noopener noreferrer"&gt;https://indexer.testnet-02.midnight.network/api/v1/graphql&lt;/a&gt;',&lt;br&gt;
  'wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws',&lt;br&gt;
  '&lt;a href="http://localhost:6300" rel="noopener noreferrer"&gt;http://localhost:6300&lt;/a&gt;',&lt;br&gt;
  '&lt;a href="https://rpc.testnet-02.midnight.network" rel="noopener noreferrer"&gt;https://rpc.testnet-02.midnight.network&lt;/a&gt;',&lt;br&gt;
  semillaAleatoria,                                                         // Semilla aleatoria&lt;br&gt;
  NetworkId.TestNet,&lt;br&gt;
  'error'&lt;br&gt;
);&lt;br&gt;
Paso 4: Iniciar la Sincronización&lt;br&gt;
javascript// Inicia la sincronización con la cadena&lt;br&gt;
billetera.start();&lt;/p&gt;

&lt;p&gt;console.log('Billetera iniciada y sincronizando...');&lt;br&gt;
Paso 5: Cerrar la Billetera&lt;br&gt;
javascript// Cuando termines, cierra la billetera adecuadamente&lt;br&gt;
await billetera.close();&lt;/p&gt;

&lt;p&gt;console.log('Billetera cerrada y recursos liberados');&lt;/p&gt;

&lt;p&gt;Instantáneas de Estado (State Snapshots)&lt;br&gt;
Las instantáneas permiten guardar y restaurar el estado de una billetera rápidamente, ideal para extensiones de navegador o aplicaciones móviles.&lt;br&gt;
Crear una Instantánea&lt;br&gt;
javascriptconst estadoSerializado = await billetera.serialize();&lt;/p&gt;

&lt;p&gt;console.log('Instantánea creada:', estadoSerializado);&lt;br&gt;
Restaurar desde una Instantánea&lt;br&gt;
⚠️ Importante: Debes proporcionar la semilla original para restaurar.&lt;br&gt;
javascriptconst billetera_restaurada = await WalletBuilder.restore(&lt;br&gt;
  '&lt;a href="https://indexer.testnet-02.midnight.network/api/v1/graphql" rel="noopener noreferrer"&gt;https://indexer.testnet-02.midnight.network/api/v1/graphql&lt;/a&gt;',&lt;br&gt;
  'wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws',&lt;br&gt;
  '&lt;a href="http://localhost:6300" rel="noopener noreferrer"&gt;http://localhost:6300&lt;/a&gt;',&lt;br&gt;
  '&lt;a href="https://rpc.testnet-02.midnight.network" rel="noopener noreferrer"&gt;https://rpc.testnet-02.midnight.network&lt;/a&gt;',&lt;br&gt;
  '0000000000000000000000000000000000000000000000000000000000000000',    // DEBES proporcionar la semilla&lt;br&gt;
  estadoSerializado,&lt;br&gt;
  'error'&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;billetera_restaurada.start();&lt;/p&gt;

&lt;p&gt;Gestión de Direcciones&lt;br&gt;
Formato Bech32m&lt;br&gt;
Midnight utiliza direcciones en formato Bech32m con el prefijo personalizado mn_.&lt;br&gt;
La estructura del prefijo es:&lt;br&gt;
mn__&lt;br&gt;
Ejemplos:&lt;/p&gt;

&lt;p&gt;mn_shield-addr_test1... - Dirección de pago en TestNet&lt;br&gt;
mn_shield-addr_test1... - Dirección escudada en TestNet (ESK para indexador)&lt;/p&gt;

&lt;p&gt;Codificar una Dirección&lt;br&gt;
javascriptimport {&lt;br&gt;
  ShieldedAddress,&lt;br&gt;
  ShieldedCoinPublicKey,&lt;br&gt;
  ShieldedEncryptionPublicKey,&lt;br&gt;
  MidnightBech32m,&lt;br&gt;
} from "@midnight-ntwrk/wallet-sdk-address-format";&lt;br&gt;
import { NetworkId } from "@midnight-ntwrk/zswap";&lt;br&gt;
import { Buffer } from "buffer";&lt;/p&gt;

&lt;p&gt;// Crear claves públicas&lt;br&gt;
const claveCoinPublica = new ShieldedCoinPublicKey(&lt;br&gt;
  Buffer.from(&lt;br&gt;
    "064e092a80b33bee23404c46cfc48fec75a2356a9b01178dd6a62c29f5896f67",&lt;br&gt;
    "hex"&lt;br&gt;
  )&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;const claveEncriptacionPublica = new ShieldedEncryptionPublicKey(&lt;br&gt;
  Buffer.from(&lt;br&gt;
    "0300063c7753854aea18aa11f04d77b3c7eaa0918e4aa98d5eaf0704d8f4c2fc272899efbb8a71275f2a1aedd29f879021e0962b4730f9b47e1a",&lt;br&gt;
    "hex"&lt;br&gt;
  )&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;// Crear dirección&lt;br&gt;
const direccion = new ShieldedAddress(claveCoinPublica, claveEncriptacionPublica);&lt;/p&gt;

&lt;p&gt;// Codificar en Bech32m&lt;br&gt;
const direccionCodificada = ShieldedAddress.codec&lt;br&gt;
  .encode(NetworkId.TestNet, direccion)&lt;br&gt;
  .asString();&lt;/p&gt;

&lt;p&gt;console.log('Dirección codificada:', direccionCodificada);&lt;br&gt;
// Salida: mn_shield-addr_test1qe8qj25qkva7ug6qf3rvl3y0a366ydt2nvq30rwk5ckznavfdansxqqx83m48p22agv25y0sf4mm83l25zgcuj4f34027pcymr6v9lp89zv7lwu2wyn472s6ahfflpusy8sfv268xrumgls62hqz4u&lt;br&gt;
Decodificar una Dirección&lt;br&gt;
javascriptconst direccionCodificada = "mn_shield-addr_test1qe8qj25qkva7ug6qf3rvl3y0a366ydt2nvq30rwk5ckznavfdansxqqx83m48p22agv25y0sf4mm83l25zgcuj4f34027pcymr6v9lp89zv7lwu2wyn472s6ahfflpusy8sfv268xrumgls62hqz4u";&lt;/p&gt;

&lt;p&gt;// Parsear&lt;br&gt;
const direccionParseada = MidnightBech32m.parse(direccionCodificada);&lt;/p&gt;

&lt;p&gt;// Decodificar&lt;br&gt;
const direccionDecodificada = ShieldedAddress.codec.decode(&lt;br&gt;
  NetworkId.TestNet,&lt;br&gt;
  direccionParseada&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;console.log('Clave pública de coin:', direccionDecodificada.coinPublicKeyString());&lt;br&gt;
console.log('Clave pública de encriptación:', direccionDecodificada.encryptionPublicKeyString());&lt;/p&gt;

&lt;p&gt;Trabajar con Transacciones&lt;br&gt;
Ciclo de Vida de una Transacción&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Preparar transacción sin prueba
 ↓&lt;/li&gt;
&lt;li&gt;Calcular pruebas de conocimiento cero
 ↓&lt;/li&gt;
&lt;li&gt;Enviar transacción comprobada
Acceder al Estado de la Billetera
El estado se proporciona a través de un observable de RxJS:
javascriptimport { Observable } from 'rxjs';&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// Suscribirse a cambios de estado&lt;br&gt;
billetera.state().subscribe((estado) =&amp;gt; {&lt;br&gt;
  console.log('Estado actual:', estado);&lt;br&gt;
  console.log('Saldo:', estado.coins);&lt;br&gt;
  console.log('Historial de transacciones:', estado.transactionHistory);&lt;br&gt;
});&lt;br&gt;
Paso 1: Preparar una Transferencia&lt;br&gt;
javascriptimport { nativeToken } from '@midnight-ntwrk/zswap';&lt;/p&gt;

&lt;p&gt;// ⚠️ IMPORTANTE: Asegúrate que la billetera haya sincronizado completamente&lt;br&gt;
// antes de preparar una transacción. De lo contrario, podrías intentar gastar&lt;br&gt;
// una moneda que ya fue gastada pero tu billetera aún no lo sabe.&lt;/p&gt;

&lt;p&gt;const recetaTransferencia = await billetera.transferTransaction([&lt;br&gt;
  {&lt;br&gt;
    amount: 1n,                                          // Cantidad en satoshis (BigInt)&lt;br&gt;
    type: nativeToken(),                               // Tipo de token (tDUST)&lt;br&gt;
    receiverAddress: 'mn_shield-addr_test1kj...'      // Dirección del receptor&lt;br&gt;
  }&lt;br&gt;
]);&lt;/p&gt;

&lt;p&gt;console.log('Receta de transferencia preparada');&lt;br&gt;
Paso 2: Balancear una Transacción Existente&lt;br&gt;
Balancear una transacción significa ajustarla para cubrir las comisiones y equilibrar entradas/salidas:&lt;br&gt;
javascriptconst transaccionBalanceada = await billetera.balanceTransaction(&lt;br&gt;
  transaccionExistente,&lt;br&gt;
  []  // newCoins (opcional) - para monedas nuevas creadas por DApps&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;console.log('Transacción balanceada');&lt;br&gt;
Paso 3: Generar Pruebas de Conocimiento Cero&lt;br&gt;
Este paso es computacionalmente intensivo y puede tomar varios segundos:&lt;br&gt;
javascriptimport { TRANSACTION_TO_PROVE } from '@midnight-ntwrk/wallet-api';&lt;/p&gt;

&lt;p&gt;// Crear la receta (wrapper) de la transacción&lt;br&gt;
const receta = {&lt;br&gt;
  type: TRANSACTION_TO_PROVE,&lt;br&gt;
  transaction: recetaTransferencia&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;// Generar pruebas (⚠️ Esto es MUY lento - puede tomar decenas de segundos)&lt;br&gt;
console.time('Generación de pruebas');&lt;br&gt;
const transaccionComprobada = await billetera.proveTransaction(receta);&lt;br&gt;
console.timeEnd('Generación de pruebas');&lt;/p&gt;

&lt;p&gt;console.log('Pruebas generadas exitosamente');&lt;br&gt;
Paso 4: Enviar la Transacción&lt;br&gt;
javascript// La transacción debe estar:&lt;br&gt;
// 1. Balanceada (entradas ≥ salidas + comisiones)&lt;br&gt;
// 2. Comprobada (pruebas generadas)&lt;/p&gt;

&lt;p&gt;const transaccionEnviada = await billetera.submitTransaction(&lt;br&gt;
  transaccionComprobada&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;console.log('Transacción enviada:', transaccionEnviada);&lt;br&gt;
console.log('Hash de transacción:', transaccionEnviada.id);&lt;/p&gt;

&lt;p&gt;Conectar con DApps&lt;br&gt;
Las aplicaciones descentralizadas se conectan a la billetera a través del API de DApp Connector. La billetera se inyecta en el contexto del navegador, permitiendo que las DApps soliciten operaciones como:&lt;/p&gt;

&lt;p&gt;Obtener dirección del usuario&lt;br&gt;
Firmar transacciones&lt;br&gt;
Enviar tokens&lt;br&gt;
Ejecutar contratos inteligentes&lt;/p&gt;

&lt;p&gt;Consulta:&lt;/p&gt;

&lt;p&gt;DApp Connector Overview&lt;br&gt;
Wallet Provider para más detalles técnicos&lt;/p&gt;

&lt;p&gt;Ejemplos Prácticos&lt;br&gt;
Ejemplo 1: Transferencia Básica de tDUST&lt;br&gt;
Este ejemplo crea una billetera, sincroniza con la cadena y envía 1 tDUST a otra dirección:&lt;br&gt;
javascriptimport { WalletBuilder } from '@midnight-ntwrk/wallet';&lt;br&gt;
import { NetworkId, nativeToken } from '@midnight-ntwrk/zswap';&lt;/p&gt;

&lt;p&gt;async function transferirTDUST() {&lt;br&gt;
  try {&lt;br&gt;
    // 1. Crear billetera&lt;br&gt;
    console.log('📱 Creando billetera...');&lt;br&gt;
    const billetera = await WalletBuilder.build(&lt;br&gt;
      '&lt;a href="https://indexer.testnet-02.midnight.network/api/v1/graphql" rel="noopener noreferrer"&gt;https://indexer.testnet-02.midnight.network/api/v1/graphql&lt;/a&gt;',&lt;br&gt;
      'wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws',&lt;br&gt;
      '&lt;a href="http://localhost:6300" rel="noopener noreferrer"&gt;http://localhost:6300&lt;/a&gt;',&lt;br&gt;
      '&lt;a href="https://rpc.testnet-02.midnight.network" rel="noopener noreferrer"&gt;https://rpc.testnet-02.midnight.network&lt;/a&gt;',&lt;br&gt;
      '0000000000000000000000000000000000000000000000000000000000000000',&lt;br&gt;
      NetworkId.TestNet&lt;br&gt;
    );&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 2. Iniciar sincronización
console.log('🔄 Iniciando sincronización...');
billetera.start();

// Esperar a que sincronice (en producción, implementa mejor lógica de espera)
await new Promise(resolve =&amp;gt; setTimeout(resolve, 5000));

// 3. Preparar transferencia
console.log('💸 Preparando transferencia...');
const recetaTransferencia = await billetera.transferTransaction([
  {
    amount: 1n,
    type: nativeToken(),
    receiverAddress: 'mn_shield-addr_test1kjwksfp8x2tachehsfvufsdl35ljg5cxzdcysjdn6ntadspyxn3qxqrxypgjm055c2azrpuyn7un0ge2vm25vkfv38d24rj3ewcku5wmdc94gjr9'
  }
]);

// 4. Generar pruebas
console.log('⚙️ Generando pruebas de conocimiento cero (esto puede tomar un tiempo)...');
const transaccionComprobada = await billetera.proveTransaction(recetaTransferencia);

// 5. Enviar transacción
console.log('📤 Enviando transacción...');
const resultado = await billetera.submitTransaction(transaccionComprobada);

console.log('✅ ¡Transacción enviada exitosamente!');
console.log('ID de transacción:', resultado.id);

// 6. Cerrar billetera
await billetera.close();
console.log('🔐 Billetera cerrada');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (error) {&lt;br&gt;
    console.error('❌ Error:', error.message);&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// Ejecutar&lt;br&gt;
transferirTDUST();&lt;br&gt;
Ejemplo 2: Guardar y Restaurar Billetera&lt;br&gt;
javascriptimport { WalletBuilder } from '@midnight-ntwrk/wallet';&lt;br&gt;
import { NetworkId } from '@midnight-ntwrk/zswap';&lt;br&gt;
import { generateRandomSeed } from '@midnight-ntwrk/wallet-sdk-hd';&lt;br&gt;
import * as fs from 'fs';&lt;/p&gt;

&lt;p&gt;async function guardarYRestaurarBilletera() {&lt;br&gt;
  const ruutaArchivo = './billetera_snapshot.json';&lt;br&gt;
  const semilla = generateRandomSeed();&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
    // 1. Crear billetera original&lt;br&gt;
    console.log('📱 Creando billetera original...');&lt;br&gt;
    const billetera = await WalletBuilder.build(&lt;br&gt;
      '&lt;a href="https://indexer.testnet-02.midnight.network/api/v1/graphql" rel="noopener noreferrer"&gt;https://indexer.testnet-02.midnight.network/api/v1/graphql&lt;/a&gt;',&lt;br&gt;
      'wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws',&lt;br&gt;
      '&lt;a href="http://localhost:6300" rel="noopener noreferrer"&gt;http://localhost:6300&lt;/a&gt;',&lt;br&gt;
      '&lt;a href="https://rpc.testnet-02.midnight.network" rel="noopener noreferrer"&gt;https://rpc.testnet-02.midnight.network&lt;/a&gt;',&lt;br&gt;
      semilla,&lt;br&gt;
      NetworkId.TestNet&lt;br&gt;
    );&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;billetera.start();

// Esperar sincronización
await new Promise(resolve =&amp;gt; setTimeout(resolve, 3000));

// 2. Guardar instantánea
console.log('💾 Guardando instantánea...');
const estadoSerializado = await billetera.serialize();
fs.writeFileSync(ruutaArchivo, JSON.stringify({
  estado: estadoSerializado,
  semilla: Buffer.from(semilla).toString('hex')
}, null, 2));

await billetera.close();
console.log('✅ Instantánea guardada en:', ruutaArchivo);

// 3. Restaurar desde instantánea
console.log('📂 Leyendo instantánea...');
const datos = JSON.parse(fs.readFileSync(ruutaArchivo, 'utf-8'));

console.log('🔄 Restaurando billetera...');
const billetera_restaurada = await WalletBuilder.restore(
  'https://indexer.testnet-02.midnight.network/api/v1/graphql',
  'wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws',
  'http://localhost:6300',
  'https://rpc.testnet-02.midnight.network',
  Buffer.from(datos.semilla, 'hex'),
  datos.estado
);

billetera_restaurada.start();
console.log('✅ Billetera restaurada exitosamente');

// Verificar estado
billetera_restaurada.state().subscribe((estado) =&amp;gt; {
  console.log('Estado restaurado - Monedas:', estado.coins);
});

await billetera_restaurada.close();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (error) {&lt;br&gt;
    console.error('❌ Error:', error.message);&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;guardarYRestaurarBilletera();&lt;br&gt;
Ejemplo 3: Monitorear Estado en Tiempo Real&lt;br&gt;
javascriptimport { WalletBuilder } from '@midnight-ntwrk/wallet';&lt;br&gt;
import { NetworkId } from '@midnight-ntwrk/zswap';&lt;br&gt;
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';&lt;/p&gt;

&lt;p&gt;async function monitorearBilletera() {&lt;br&gt;
  try {&lt;br&gt;
    const billetera = await WalletBuilder.build(&lt;br&gt;
      '&lt;a href="https://indexer.testnet-02.midnight.network/api/v1/graphql" rel="noopener noreferrer"&gt;https://indexer.testnet-02.midnight.network/api/v1/graphql&lt;/a&gt;',&lt;br&gt;
      'wss://indexer.testnet-02.midnight.network/api/v1/graphql/ws',&lt;br&gt;
      '&lt;a href="http://localhost:6300" rel="noopener noreferrer"&gt;http://localhost:6300&lt;/a&gt;',&lt;br&gt;
      '&lt;a href="https://rpc.testnet-02.midnight.network" rel="noopener noreferrer"&gt;https://rpc.testnet-02.midnight.network&lt;/a&gt;',&lt;br&gt;
      '0000000000000000000000000000000000000000000000000000000000000000',&lt;br&gt;
      NetworkId.TestNet&lt;br&gt;
    );&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;billetera.start();

// Monitorear cambios de estado con debounce
billetera.state()
  .pipe(
    debounceTime(1000),                    // Esperar 1 segundo sin cambios
    distinctUntilChanged((a, b) =&amp;gt;         // Solo si realmente cambió
      JSON.stringify(a) === JSON.stringify(b)
    )
  )
  .subscribe((estado) =&amp;gt; {
    console.log('\n📊 === ESTADO ACTUALIZADO ===');
    console.log('Monedas disponibles:', estado.coins.length);
    console.log('Saldo total:', estado.coins.reduce((sum, c) =&amp;gt; sum + c.amount, 0n));
    console.log('Transacciones procesadas:', estado.transactionHistory.length);
    console.log('Última transacción:', 
      estado.transactionHistory[0]?.date || 'Ninguna');
  });

// Mantener proceso activo
console.log('👀 Monitoreando billetera (Ctrl+C para salir)...');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (error) {&lt;br&gt;
    console.error('❌ Error:', error.message);&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;monitorearBilletera();&lt;/p&gt;

&lt;p&gt;Buenas Prácticas y Recomendaciones&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sincronización Completa Antes de Transacciones
javascript// ❌ INCORRECTO
const receta = await billetera.transferTransaction([...]);&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// ✅ CORRECTO&lt;br&gt;
// Esperar a que la billetera esté sincronizada&lt;br&gt;
await new Promise(resolve =&amp;gt; &lt;br&gt;
  billetera.state()&lt;br&gt;
    .pipe(filter(s =&amp;gt; s.isSynced))&lt;br&gt;
    .subscribe(() =&amp;gt; resolve())&lt;br&gt;
);&lt;br&gt;
const receta = await billetera.transferTransaction([...]);&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gestión Segura de Semillas
javascript// ❌ INCORRECTO
const semilla = "0000000000..."; // Hardcodeada&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// ✅ CORRECTO&lt;br&gt;
const semilla = process.env.WALLET_SEED;  // Variables de entorno&lt;br&gt;
// O&lt;br&gt;
const semilla = await obtenerDelAlfiler(); // Sistema seguro de almacenamiento&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Manejo de Errores Robusto
javascripttry {
const transaccion = await billetera.proveTransaction(receta);
} catch (error) {
if (error.message.includes('insufficient')) {
console.error('Saldo insuficiente');
} else if (error.message.includes('timeout')) {
console.error('Servidor de pruebas no disponible');
} else {
console.error('Error desconocido:', error);
}
}&lt;/li&gt;
&lt;li&gt;Cierre Adecuado de Recursos
javascriptasync function operacionConBilletera() {
const billetera = await WalletBuilder.build(...);
try {
billetera.start();
// Tu código aquí
} finally {
await billetera.close(); // Siempre cerrar
}
}&lt;/li&gt;
&lt;li&gt;Timeouts en Operaciones Largas
javascriptimport { timeout } from 'rxjs/operators';&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;const transaccionConTimeout = billetera.proveTransaction(receta)&lt;br&gt;
  .pipe(timeout(60000)); // 60 segundos máximo&lt;/p&gt;

&lt;p&gt;Parámetros de Configuración&lt;br&gt;
WalletBuilder.build() - Parámetros Obligatorios&lt;br&gt;
ParámetroTipoDescripciónIndexer URLStringURL del API GraphQL del indexadorIndexer WebSocket URLStringURL WebSocket del indexadorProving Server URLStringURL del servidor de pruebasNode URLStringURL del nodo RPCSeedStringSemilla de 256 bits en hexNetwork IDNetworkIdTestNet, MainNet, etc.&lt;br&gt;
Parámetros Opcionales&lt;br&gt;
ParámetroTipoDefaultDescripciónLog LevelLogLevelwarnerror, warn, info, debugDiscard Transaction HistoryBooleanfalseDescartar historial de transacciones&lt;/p&gt;

&lt;p&gt;Troubleshooting&lt;br&gt;
Problema: "Error derivando clave"&lt;br&gt;
Solución: Verifica que la semilla sea de 256 bits (64 caracteres hex)&lt;br&gt;
Problema: Transacción rechazada por "double spend"&lt;br&gt;
Solución: Asegúrate de que la billetera esté completamente sincronizada&lt;br&gt;
antes de preparar la transacción&lt;br&gt;
Problema: Servidor de pruebas no disponible&lt;br&gt;
Solución: Verifica que el servidor de pruebas esté corriendo en localhost:6300&lt;br&gt;
o usa URL correcta en la configuración&lt;br&gt;
Problema: "secret keys are no longer included"&lt;br&gt;
Solución: Proporciona la semilla original al restaurar desde instantánea&lt;/p&gt;

&lt;p&gt;Recursos Adicionales&lt;/p&gt;

&lt;p&gt;Documentación Oficial: &lt;a href="https://docs.midnight.network" rel="noopener noreferrer"&gt;https://docs.midnight.network&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/midnight-ntwrk" rel="noopener noreferrer"&gt;https://github.com/midnight-ntwrk&lt;/a&gt;&lt;br&gt;
API Reference: Consulta la documentación de @midnight-ntwrk/wallet&lt;br&gt;
Ejemplos: Revisa los ejemplos incluidos en el repositorio&lt;/p&gt;

&lt;p&gt;Conclusión&lt;br&gt;
El Midnight Wallet SDK proporciona una forma robusta y segura de integrar funcionalidad de billetera en aplicaciones blockchain. Recuerda siempre:&lt;/p&gt;

&lt;p&gt;✅ Gestiona las semillas de forma segura&lt;br&gt;
✅ Sincroniza completamente antes de transacciones&lt;br&gt;
✅ Cierra recursos adecuadamente&lt;br&gt;
✅ Implementa manejo robusto de errores&lt;br&gt;
✅ Prueba ampliamente antes de producción&lt;/p&gt;

</description>
      <category>midnightfordevs</category>
      <category>blockchain</category>
      <category>cardano</category>
    </item>
    <item>
      <title>Kachina y el Dilema de la Composabilidad: Cuando la Privacidad Blockchain se Encuentra con la Realidad Técnica</title>
      <dc:creator>Cardumen</dc:creator>
      <pubDate>Tue, 16 Dec 2025 21:56:54 +0000</pubDate>
      <link>https://dev.to/cardumen_c23712b080b2a053/kachina-y-el-dilema-de-la-composabilidad-cuando-la-privacidad-blockchain-se-encuentra-con-la-2968</link>
      <guid>https://dev.to/cardumen_c23712b080b2a053/kachina-y-el-dilema-de-la-composabilidad-cuando-la-privacidad-blockchain-se-encuentra-con-la-2968</guid>
      <description>&lt;p&gt;Kachina y el Dilema de la Composabilidad: Cuando la Privacidad Blockchain se Encuentra con la Realidad Técnica&lt;br&gt;
El Problema que Nadie Quiere Admitir&lt;br&gt;
Hablemos claro: las blockchains públicas son como vivir en una casa de cristal. Cada transacción, cada saldo, cada movimiento está ahí para que cualquiera lo vea. Y aunque la transparencia es genial para la auditabilidad, es terrible para casi todo lo demás.&lt;br&gt;
Imagina tener que publicar tu salario cada vez que cobras. O que tus competidores puedan ver exactamente cuánto pagas a tus proveedores. Ese es el estado actual de las blockchains públicas.&lt;br&gt;
Kachina, el framework que está implementando Midnight (un sidechain de Cardano), promete resolver esto con contratos inteligentes privados. Pero como todo en cripto, la solución trae sus propios desafíos.&lt;br&gt;
Cómo Funciona Kachina: La Magia de los Dos Mundos&lt;br&gt;
Kachina divide la ejecución en dos capas que funcionan en paralelo:&lt;br&gt;
El Mundo Público (Compact Runtime)&lt;/p&gt;

&lt;p&gt;Vive en la blockchain&lt;br&gt;
Solo ve compromisos criptográficos (hashes)&lt;br&gt;
Verifica pruebas ZK&lt;br&gt;
Actualiza el estado global sin saber los detalles&lt;/p&gt;

&lt;p&gt;El Mundo Privado (Contract Runtime)&lt;/p&gt;

&lt;p&gt;Corre en tu computadora local&lt;br&gt;
Procesa la lógica real del contrato&lt;br&gt;
Genera pruebas de conocimiento cero&lt;br&gt;
Mantiene tus datos sensibles seguros&lt;/p&gt;

&lt;p&gt;Es como tener un notario que certifica que hiciste algo correctamente sin necesitar ver qué hiciste exactamente. Verificación sin revelación.&lt;br&gt;
El Elefante en la Habitación: La Composabilidad&lt;br&gt;
Aquí es donde las cosas se ponen interesantes (y complicadas).&lt;br&gt;
En blockchains tradicionales como Ethereum, la composabilidad es trivial. Un contrato puede:&lt;br&gt;
solidity// En Ethereum es así de simple&lt;br&gt;
uint balance = otherContract.getBalance(user);&lt;br&gt;
require(balance &amp;gt; minAmount);&lt;br&gt;
// Listo, ya tienes el dato&lt;br&gt;
Los contratos se hablan entre sí como piezas de LEGO. Uniswap puede llamar a un token ERC-20, que puede llamar a un oráculo, que puede llamar a otro protocolo. Todo fluye.&lt;br&gt;
El Nuevo Paradigma de Kachina&lt;br&gt;
En Kachina no puedes simplemente "leer" el estado de otro contrato porque... bueno, ese estado es privado. En lugar de eso:&lt;/p&gt;

&lt;p&gt;Contrato A genera una prueba ZK de su estado&lt;br&gt;
Contrato B verifica esa prueba&lt;br&gt;
Contrato B genera su propia prueba incluyendo la verificación anterior&lt;br&gt;
La blockchain solo ve pruebas verificándose, nunca los datos reales&lt;/p&gt;

&lt;p&gt;Es como si en lugar de mostrar tu saldo bancario para un préstamo, generas una prueba matemática que dice "sí, tengo más de X cantidad" sin revelar cuánto tienes exactamente.&lt;br&gt;
Los Trade-offs Reales&lt;br&gt;
Lo Bueno ✅&lt;/p&gt;

&lt;p&gt;Privacidad real: No más frontrunning, no más espionaje competitivo&lt;br&gt;
Cumplimiento regulatorio privado: Puedes cumplir regulaciones sin exponer datos sensibles&lt;br&gt;
Votaciones verdaderamente secretas: Governance sin presión social&lt;br&gt;
Finanzas corporativas viables: Las empresas pueden usar blockchain sin revelar secretos comerciales&lt;/p&gt;

&lt;p&gt;Lo Complicado ⚠️&lt;/p&gt;

&lt;p&gt;Complejidad de desarrollo: No es solo escribir Solidity y ya. Necesitas pensar en circuitos, pruebas, y verificaciones desde el día uno&lt;br&gt;
Costos computacionales: Generar pruebas ZK no es gratis. Tu computadora trabaja más&lt;br&gt;
Curva de aprendizaje empinada: ZK no es intuitivo. Requiere repensar cómo diseñas aplicaciones&lt;br&gt;
Composabilidad diferente: No imposible, pero definitivamente más compleja&lt;/p&gt;

&lt;p&gt;El Problema Técnico de Fondo&lt;br&gt;
Cuando Patricio López mencionó el tema de los circuitos algebraicos, dio en el clavo. En ZK, cada operación necesita ser expresada como un circuito. Si tienes:&lt;/p&gt;

&lt;p&gt;Prueba A: "Tengo más de 1000 tokens"&lt;br&gt;
Prueba B: "He estado activo por 30 días"&lt;/p&gt;

&lt;p&gt;Para crear una Prueba C que combine ambas condiciones, no puedes simplemente sumar A + B. Necesitas:&lt;/p&gt;

&lt;p&gt;Un circuito que verifique la Prueba A&lt;br&gt;
Un circuito que verifique la Prueba B&lt;br&gt;
Un nuevo circuito que implemente tu lógica usando ambas verificaciones&lt;/p&gt;

&lt;p&gt;Esto es lo que Kachina resuelve con "pruebas recursivas" - básicamente, pruebas que verifican otras pruebas. Pero no es magia; cada composición añade complejidad y costo computacional.&lt;br&gt;
¿Qué Significa Esto para el Ecosistema?&lt;br&gt;
Para Desarrolladores&lt;br&gt;
Preparense para una curva de aprendizaje brutal. No es solo aprender un nuevo lenguaje; es aprender una nueva forma de pensar sobre la computación. Los que dominen ZK serán los magos del próximo ciclo.&lt;br&gt;
Para Usuarios&lt;br&gt;
Finalmente podrán tener privacidad real. Pero probablemente pagarán más en fees y tendrán que esperar más tiempo para las transacciones (generar pruebas toma tiempo).&lt;br&gt;
Para Empresas&lt;br&gt;
Es la primera vez que blockchain es viable para casos de uso corporativos reales. No más excusas sobre "no podemos exponer nuestros datos".&lt;br&gt;
El Veredicto&lt;br&gt;
Kachina no es una bala de plata. Es un trade-off consciente: sacrificamos algo de la simplicidad y eficiencia de las blockchains públicas a cambio de privacidad real y verificable.&lt;br&gt;
La composabilidad no está muerta, solo es diferente. En lugar de un mundo donde los contratos leen libremente el estado de otros, tenemos un mundo donde los contratos prueban cosas sobre su estado sin revelarlo.&lt;br&gt;
¿Es más complejo? Absolutamente.&lt;br&gt;
¿Vale la pena? Para muchos casos de uso, definitivamente sí.&lt;br&gt;
Mirando al Futuro&lt;br&gt;
El éxito de Kachina y Midnight no dependerá solo de la tecnología, sino de:&lt;/p&gt;

&lt;p&gt;Herramientas de desarrollo: Necesitamos abstracciones que hagan ZK más accesible&lt;br&gt;
Educación: La comunidad necesita entender estos nuevos paradigmas&lt;br&gt;
Casos de uso killer: Aplicaciones que solo son posibles con privacidad&lt;br&gt;
Optimizaciones: Hacer la generación de pruebas más rápida y barata&lt;/p&gt;

&lt;p&gt;La privacidad en blockchain no es un lujo; es una necesidad para la adopción masiva. Kachina es un paso importante en esa dirección, incluso si significa repensar algunas de las suposiciones fundamentales sobre cómo funcionan los contratos inteligentes.&lt;br&gt;
Como siempre en cripto: no hay almuerzo gratis. Pero al menos con Kachina, nadie sabrá cuánto pagaste por él.&lt;/p&gt;

</description>
      <category>midnightfordevs</category>
      <category>blockchain</category>
      <category>zk</category>
    </item>
    <item>
      <title>Cómo escribir smart contracts en Midnight</title>
      <dc:creator>Cardumen</dc:creator>
      <pubDate>Sat, 13 Dec 2025 22:52:43 +0000</pubDate>
      <link>https://dev.to/cardumen_c23712b080b2a053/como-escribir-smart-contracts-en-midnight-h1j</link>
      <guid>https://dev.to/cardumen_c23712b080b2a053/como-escribir-smart-contracts-en-midnight-h1j</guid>
      <description>&lt;p&gt;Una introducción práctica al desarrollo privacy-first&lt;/p&gt;

&lt;p&gt;Midnight es una blockchain diseñada desde cero para resolver uno de los mayores problemas del ecosistema: cómo construir aplicaciones descentralizadas sin exponer datos sensibles. A diferencia de la mayoría de las blockchains —donde todo es público o todo es privado— Midnight introduce un modelo más fino: selective disclosure.&lt;/p&gt;

&lt;p&gt;En este artículo veremos cómo se escribe un smart contract en Midnight, qué lo hace distinto y cómo pensar el desarrollo cuando la privacidad no es un parche, sino una propiedad del lenguaje.&lt;/p&gt;

&lt;p&gt;El modelo mental: privacidad por defecto&lt;/p&gt;

&lt;p&gt;Antes de escribir una sola línea de código, hay que cambiar el enfoque.&lt;/p&gt;

&lt;p&gt;En Midnight:&lt;/p&gt;

&lt;p&gt;Todo lo que viene del usuario es privado por defecto&lt;/p&gt;

&lt;p&gt;Nada se puede hacer público “por accidente”&lt;/p&gt;

&lt;p&gt;La privacidad se controla explícitamente, no implícitamente&lt;/p&gt;

&lt;p&gt;Esto se logra gracias a Compact, el lenguaje de smart contracts de Midnight, y al uso de Zero-Knowledge Proofs (zkSNARKs) para validar transacciones sin revelar datos.&lt;/p&gt;

&lt;p&gt;¿Qué es Compact?&lt;/p&gt;

&lt;p&gt;Compact es un lenguaje declarativo diseñado específicamente para construir aplicaciones privacy-first.&lt;/p&gt;

&lt;p&gt;Cuando escribes un contrato en Compact:&lt;/p&gt;

&lt;p&gt;Defines la lógica de negocio&lt;/p&gt;

&lt;p&gt;Defines qué datos son públicos&lt;/p&gt;

&lt;p&gt;Defines qué datos permanecen privados&lt;/p&gt;

&lt;p&gt;El compilador genera:&lt;/p&gt;

&lt;p&gt;APIs en JavaScript / TypeScript para tu DApp&lt;/p&gt;

&lt;p&gt;Los artefactos criptográficos necesarios para generar pruebas ZK&lt;/p&gt;

&lt;p&gt;Todo esto ocurre localmente en el entorno del desarrollador.&lt;/p&gt;

&lt;p&gt;Componentes básicos de un contrato en Compact&lt;/p&gt;

&lt;p&gt;Todo contrato en Midnight se construye a partir de los mismos bloques:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ledger state (estado público)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Datos que viven en la cadena y son visibles para todos.&lt;/p&gt;

&lt;p&gt;export ledger value: Uint&amp;lt;64&amp;gt;;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Witnesses (entradas privadas)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Los witnesses representan datos que vienen del usuario o del frontend.&lt;br&gt;
Son privados por defecto y Compact no permite que se filtren accidentalmente.&lt;/p&gt;

&lt;p&gt;witness getSecret(): Bytes&amp;lt;32&amp;gt;;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Circuits (lógica verificable)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Los circuits definen qué puede hacer el contrato.&lt;br&gt;
Solo los circuits marcados como export pueden ser llamados desde fuera.&lt;/p&gt;

&lt;p&gt;export circuit get(): Uint&amp;lt;64&amp;gt; {&lt;br&gt;
  return value;&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Constructor&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Se ejecuta una sola vez, al crear el contrato.&lt;/p&gt;

&lt;p&gt;constructor(sk: Bytes&amp;lt;32&amp;gt;, v: Uint&amp;lt;64&amp;gt;) {&lt;br&gt;
  value = v;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Selective disclosure: disclose()&lt;/p&gt;

&lt;p&gt;Una de las reglas más importantes de Compact es esta:&lt;/p&gt;

&lt;p&gt;No puedes hacer público un dato privado sin decirlo explícitamente.&lt;/p&gt;

&lt;p&gt;Si intentas usar un witness directamente en el ledger o como retorno, el compilador falla.&lt;/p&gt;

&lt;p&gt;Forma correcta:&lt;/p&gt;

&lt;p&gt;export ledger publicData: Bytes&amp;lt;32&amp;gt;;&lt;/p&gt;

&lt;p&gt;export circuit record(): [] {&lt;br&gt;
  publicData = disclose(getSecret());&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Forma incorrecta (error de compilación):&lt;/p&gt;

&lt;p&gt;publicData = getSecret(); // ❌&lt;/p&gt;

&lt;p&gt;Esto elimina una clase completa de errores comunes en otros ecosistemas.&lt;/p&gt;

&lt;p&gt;Opaque types: cuando Compact no debe “mirar” el dato&lt;/p&gt;

&lt;p&gt;Compact permite tipos opacos como:&lt;/p&gt;

&lt;p&gt;Opaque&amp;lt;'string'&amp;gt;&lt;/p&gt;

&lt;p&gt;Estos tipos:&lt;/p&gt;

&lt;p&gt;Pueden almacenarse o transferirse&lt;/p&gt;

&lt;p&gt;No pueden ser inspeccionados dentro del contrato&lt;/p&gt;

&lt;p&gt;Son visibles en el frontend (TypeScript), pero no manipulables en Compact&lt;/p&gt;

&lt;p&gt;Son útiles para:&lt;/p&gt;

&lt;p&gt;Mensajes&lt;/p&gt;

&lt;p&gt;Metadatos&lt;/p&gt;

&lt;p&gt;Contenido arbitrario (ej. bulletin boards)&lt;/p&gt;

&lt;p&gt;Compromisos y hashing&lt;/p&gt;

&lt;p&gt;Para probar propiedades sobre datos sin revelarlos, Compact incluye primitivas criptográficas:&lt;/p&gt;

&lt;p&gt;persistentCommit(value, random);&lt;br&gt;
transientCommit(value);&lt;/p&gt;

&lt;p&gt;Estas funciones son clave para:&lt;/p&gt;

&lt;p&gt;Votaciones privadas&lt;/p&gt;

&lt;p&gt;Identidad verificable&lt;/p&gt;

&lt;p&gt;Autenticación sin revelar credenciales&lt;/p&gt;

&lt;p&gt;El rol del proof server&lt;/p&gt;

&lt;p&gt;Toda la magia ZK ocurre fuera de la blockchain.&lt;/p&gt;

&lt;p&gt;El proof server:&lt;/p&gt;

&lt;p&gt;Corre localmente o en infraestructura controlada&lt;/p&gt;

&lt;p&gt;Nunca expone datos privados a la red&lt;/p&gt;

&lt;p&gt;Solo devuelve pruebas criptográficas válidas&lt;/p&gt;

&lt;p&gt;Esto permite que:&lt;/p&gt;

&lt;p&gt;La blockchain valide hechos&lt;/p&gt;

&lt;p&gt;Sin conocer los datos subyacentes&lt;/p&gt;

&lt;p&gt;Cómo pensar un contrato en Midnight&lt;/p&gt;

&lt;p&gt;Al escribir en Midnight, conviene preguntarse:&lt;/p&gt;

&lt;p&gt;¿Este dato realmente debe ser público?&lt;/p&gt;

&lt;p&gt;¿Qué necesita verificar la red y qué no?&lt;/p&gt;

&lt;p&gt;¿Puedo probar esta condición sin revelar el valor?&lt;/p&gt;

&lt;p&gt;¿Quién controla el proof server?&lt;/p&gt;

&lt;p&gt;Compact no solo te da herramientas, te obliga a pensar bien el modelo de datos.&lt;/p&gt;

&lt;p&gt;Conclusión&lt;/p&gt;

&lt;p&gt;Escribir smart contracts en Midnight no es solo aprender un nuevo lenguaje.&lt;br&gt;
Es adoptar una forma distinta de diseñar aplicaciones descentralizadas:&lt;/p&gt;

&lt;p&gt;Privacidad por defecto&lt;/p&gt;

&lt;p&gt;Disclosure explícito&lt;/p&gt;

&lt;p&gt;Separación clara entre datos, lógica y pruebas&lt;/p&gt;

&lt;p&gt;Seguridad reforzada a nivel de compilador&lt;/p&gt;

&lt;p&gt;Si vienes de ecosistemas como Solidity o Plutus, Compact puede sentirse distinto al principio. Pero una vez que el modelo hace clic, es difícil volver atrás.&lt;/p&gt;

&lt;p&gt;La privacidad deja de ser un “feature” y pasa a ser parte del lenguaje.&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>tutorial</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
