Zorrito

Cómo funciona
Zorrito

Ahorro premiado sin pérdida de capital, sobre Stellar.

💡 La idea es simple: ponés plata en un pozo, el pozo la presta en Blend y genera interés, y cada ronda ese interés se sortea entre los que están adentro. Uno se lleva el rendimiento de todos. Los demás no pierden nada: su capital sigue ahí y lo retiran cuando quieren.

Cómo funciona

1
Depositás XLM

Tu depósito va directo a Blend y empieza a generar al instante. No hay mínimo. Cada XLM, y cada segundo que está adentro, suma chances.

2
El pozo genera

El capital de todos se presta en Blend, el mercado de crédito de Stellar. El interés que pagan los que piden prestado se acumula como premio.

3
La ronda cierra

Al vencer el plazo (una semana; 10 minutos en el pozo de prueba), cualquiera puede cerrarla. El premio y las chances de cada uno quedan congelados en ese instante.

4
drand decide

El contrato fija de antemano qué número aleatorio de drand va a decidir: uno que todavía no existe. Cuando drand lo publica, cualquiera lo trae y el contrato verifica la firma antes de elegir.

5
Uno cobra, nadie pierde

El premio va entero a la wallet del ganador en la misma transacción. Todos los demás siguen con exactamente lo que pusieron, y una ronda nueva ya está corriendo.


🎯 Cómo se calculan las chances

Tu peso en el sorteo es cuánto pusiste por cuánto tiempo estuvo adentro durante la ronda. Es la misma proporción con la que tu plata generó el premio: quien más aportó al rendimiento, más chances tiene de llevárselo.

peso = Σ (depósito × segundos que estuvo en la ronda)
P(ganar) = tu peso ÷ peso total del pozo

Ejemplo: dos personas con 100 XLM. Una entra el lunes, la otra el sábado. La primera tiene 6 veces más chances, porque su plata generó 6 veces más. Entrar a último momento con mucha plata casi no suma, y eso es a propósito: no hay forma de "comprar" el sorteo sobre la hora.

Retirar no borra lo ya devengado. Si tuviste plata adentro tres días y la sacás, esos tres días siguen contando para el sorteo de esa ronda.

🔥 La racha diaria

Una vez por día (UTC) podés marcar "Ahorré hoy". Cada día seguido suma peso a la ronda en curso, y cada día suma más que el anterior:

bono del día k = depósito × (duración de la ronda) × k / 28

Como 1+2+…+7 = 28, los siete días seguidos suman exactamente una ronda entera de peso: quien marca todos los días duplica sus chances respecto de alguien con la misma plata que no marca ninguno. Saltear un día vuelve al día 1. El bono es de la ronda en que se marca; la ronda siguiente arranca de cero, como todo lo demás.

Días seguidosPeso extra acumuladoChances vs. no marcar
13,6 % de una ronda×1,04
321 %×1,21
554 %×1,54
7 🏆100 %×2

🤝 Referidos

Tu link de invitación es tu dirección. Quien entra con él, en su primer depósito, te declara como referente, y desde ese momento pesás como si tuvieras el 10 % de su capital, mientras esté adentro. Si retira, el bono baja con él.

peso base = tu capital + min(10 % del capital de tus referidos, 50 % de tu capital)

El tope de la mitad de tu propio capital es lo que hace que no convenga inventarse referidos: sin plata propia adentro no hay bono, y con plata propia el bono nunca vale más que la mitad de ella. Traer amigos suma; traer cuentas vacías no.


🎲 Cómo funciona el azar

Elegir un ganador on-chain es difícil: la cadena es determinística y pública, así que cualquier valor que se conozca al momento del sorteo puede ser influido por quien arma el bloque. Zorrito no usa nada de la cadena como fuente de azar. Usa drand, un beacon de aleatoriedad público operado por unas 20 organizaciones (Cloudflare, Protocol Labs, EPFL, Kudelski, entre otras) que publica un número firmado cada 3 segundos.

🔒 Fase 1 — Cierre: se fija un número que no existe

Al cerrar la ronda, el contrato calcula qué ronda de drand corresponde a 10 minutos en el futuro y la guarda. En ese momento nadie, ni drand, sabe qué valor va a tener. También congela el premio, el peso total y una entropía acumulada de todos los depósitos de la ronda.

✍️ Fase 2 — Sorteo: la firma se verifica on-chain

Cuando drand publica esa ronda, cualquiera trae la firma. El contrato verifica con BLS12-381, en el propio Soroban, que la firma es válida para esa ronda exacta y para la clave pública de drand que tiene guardada. Si no verifica, no sortea. Si verifica, la semilla es:

semilla = sha256(entropía ‖ firma ‖ ronda_drand)
ganador = el que cae en semilla mod peso_total
🛡️ Por qué nadie puede sesgarlo. Para elegir al ganador habría que conocer la firma de drand antes de que exista, y eso exige corromper a la mayoría de las organizaciones que operan la red. Un validador de Stellar no puede: el contrato no usa ningún dato del ledger como azar. Nosotros tampoco: no hay ninguna clave nuestra en el sorteo. Y cualquiera puede recomputarlo con lo que queda on-chain.

Comparación de enfoques

EnfoqueQuién puede influirEstado
Timestamp o hash del ledgerEl validador que arma el bloqueDescartado
Commit-reveal con un keeperEl keeper, si se calla la revelaciónDescartado: liveness
Oráculo VRFEl operador del oráculoNo hay uno en Stellar
drand + verificación BLS on-chain ✓Nadie por debajo de la mayoría de drandEs lo que corre

La verificación BLS12-381 dentro del contrato es posible porque Stellar la agregó como función nativa del host en el Protocolo 22. Zorrito la usa para verificar firmas de drand quicknet: un pairing_check entre la firma, el hash del mensaje, el generador de G2 y la clave pública del grupo.


🤖 Nadie tiene que estar

Cerrar la ronda y traer la firma son acciones que cualquiera puede hacer: no hay roles ni claves privilegiadas. El que las hace solo paga la fee. Hoy las dispara un keeper que corre como función serverless, y también cada visita a la app que encuentra una ronda vencida o un sorteo pendiente. Si el keeper desaparece, el pozo no se traba: el primero que abre la app lo destraba.

Y si drand dejara de publicar, lo único que no pasaría es el sorteo. El capital se retira igual, en cualquier momento, con o sin sorteo pendiente.


🧪 Probalo sin esperar una semana

El pozo de la home corre en mainnet con rondas semanales. Para ver el ciclo entero en minutos hay un pozo de prueba en testnet, con rondas de 10 minutos y XLM de prueba: zorritostellar.vercel.app/test. Es el mismo contrato, el mismo Blend (su pool de testnet) y el mismo drand. XLM de testnet gratis en el laboratorio de Stellar.


🌊 Blend: de dónde sale el premio

Blend es el protocolo de préstamos de Stellar. El pozo deposita el capital de todos como Supply en un pool de Blend v2 (Fixed en mainnet, TestnetV2 en testnet): una posición que presta y cobra interés pero que no se usa como colateral, así que no tiene deuda y no puede ser liquidada.

  • El interés lo pagan los que piden prestado en el pool. El APY que muestra la app es el que Blend está pagando ahora.
  • El contrato habla con Blend por su cuenta, a través de un adapter propio. Nadie tiene la llave de esa posición: solo el pozo puede depositar y retirar.
  • El premio es la diferencia entre lo que hay en Blend y el capital depositado. Si el pool no generó, el premio es 0 y nadie pierde.

📈 Hecho para un millón de cuentas

Sortear entre muchos participantes con pesos distintos es caro si hay que recorrerlos. Zorrito guarda los pesos en un Fenwick tree sobre el storage del contrato, con capacidad para 220 cuentas (algo más de un millón). Depositar, retirar y sortear tocan siempre ~20 entradas, sin importar si hay 2 participantes o 1.000.000.

El tiempo se resuelve con álgebra, no con recorridos: el peso de cada cuenta es depósito × T − b, donde b se ajusta en cada movimiento. Así el peso de todos avanza con el reloj sin que nadie tenga que actualizarlo.


🛠 Stack técnico

ContratosRust + Soroban (soroban-sdk 27). Tres contratos: el pozo, el adapter de Blend y un mock de rendimiento para tests. 58 tests, incluida la verificación BLS con claves propias, la racha, los referidos y el pozo operando contra el bytecode real de Blend.
Azardrand quicknet (bls-unchained-g1-rfc9380), verificado on-chain con las funciones BLS12-381 del host de Stellar. Descompresión de puntos en el cliente con @noble/curves.
RendimientoBlend v2, reserva XLM: pool Fixed en mainnet, TestnetV2 en testnet. Integrado con contractimport! de los WASM oficiales.
WebNext.js 16, Tailwind 4, @stellar/stellar-sdk 17, Stellar Wallets Kit (Freighter, xBull, Lobstr). Desplegada en Vercel.
KeeperFunción serverless en Vercel (/api/keeper), disparada por cron y por cada visita. Permissionless: cualquiera puede correr otro.

📋 Contratos

Pool de Blend v2 · mainnet (Fixed)CAJJZSGMMM3PD7N33TAPHGBUGTB43OC73HVIK2L2G6BNGGGYOSSYBXBD
Ver en stellar.expert ↗
Pozo de prueba · testnet · 10 minCCAM3QUEKETEFA4TL27TD63ZD5LB646NJJF4OOWRQBP2RZ6O4Q7HWHRZ
Ver en stellar.expert ↗
Pool de Blend v2 · testnet (TestnetV2)CCEBVDYM32YNYCVNRXQKDFFPISJJCV557CDZEIRBEE4NCV4KHPQ44HGF
Ver en stellar.expert ↗
drand quicknet · clave pública del grupo (G2)83cf0f2896adee7eb8b5f01fcad39122a6e9c76a4bc09e76eae8991ef5ece45a
Ver en drand ↗

Código abierto: github.com/artugrande/zorrito. Los tests, los scripts de deploy y este mismo sitio están ahí.


🛡️ Seguridad y riesgos, sin maquillaje

BAJO
Nadie puede elegir al ganador

El azar viene de drand y se verifica en el contrato. No hay clave de admin en el pozo: ningún rol puede pausar, cambiar el sorteo ni tocar fondos. El adapter tiene un admin que solo sirve para fijar, una única vez, quién es el pozo.

BAJO
El capital no depende de nadie

Retirar no necesita permiso, no tiene penalidad y funciona aunque haya un sorteo pendiente, aunque el keeper esté caído y aunque drand deje de publicar.

MEDIO
Riesgo de Blend: liquidez

El capital está prestado. Si el pool de Blend tiene casi toda su liquidez tomada, un retiro puede fallar hasta que alguien devuelva o deposite. Es el riesgo real de "nadie pierde": no se pierde capital, pero puede haber que esperar. Blend lo mitiga con tasas que suben con la utilización.

MEDIO
Riesgo de Blend: protocolo

Un bug en Blend afecta al pozo como a cualquier prestamista. Blend v2 está auditado y es el protocolo de crédito con más uso en Stellar, pero el riesgo no es cero.

BAJO
Liveness de drand

Si drand no publica, no hay sorteo hasta que vuelva. El capital se retira igual. drand lleva años publicando cada 3 segundos con más de 20 operadores.

BAJO
Renta de storage en Stellar

Las entradas persistentes vencen si nadie las extiende. El contrato extiende las suyas en cada uso; para cuentas inactivas durante meses hace falta que alguien las extienda (cualquiera puede). Pendiente de automatizar en el keeper.

MEDIO
Contrato nuevo, sin auditoría

Construido desde cero durante el hackathon. El pozo de mainnet tiene un tope de capital fijo en el contrato justamente por esto: limita cuánto puede haber adentro hasta que una auditoría diga que se puede subir. No pongas plata que no puedas perder.


❓ Preguntas

¿Puedo perder plata?

No. El sorteo reparte solo el interés que generó el pozo. Tu depósito es tuyo y lo retirás cuando quieras. Lo único que puede pasar es tener que esperar si Blend está sin liquidez en ese momento.

¿Qué gano si no salgo sorteado?

Nada, y no perdés nada. Es la misma plata que tenías, pero con una chance de premio cada ronda en vez de un interés chico. Si preferís el interés seguro, Blend está ahí directo.

¿Por qué no ganó el que más plata tiene?

Porque el peso es plata por tiempo, no solo plata. Alguien con menos plata que entró antes puede pesar más. Y aun con más peso, es un sorteo: más chances no es certeza.

¿Puedo verificar un sorteo?

Sí. Cada sorteo emite un evento con la ronda de drand, la firma y el peso total. Con eso y los depósitos de la ronda, cualquiera recomputa la semilla y el ganador.

¿Qué pasa si nadie cierra la ronda a tiempo?

Nada malo. Se cierra cuando alguien la cierra, y hasta entonces el pozo sigue generando. El keeper y cada visita a la app la cierran solas.

¿Por qué XLM y no un stablecoin?

Porque no necesita trustline: cualquier cuenta de Stellar ya lo tiene, y eso saca un paso del camino de entrada. El contrato es agnóstico: se construye con cualquier token que Blend acepte como reserva, y un pozo en USDC es el mismo código con otro token.

¿Puedo tener referidos sin poner plata?

Podés, pero no te suman: el bono de referidos vale como mucho la mitad de tu propio capital, y sin capital vale cero.

← Volver a la app