Repository navigation
Activación XRPL vía Xaman (300 cuentas), deploy real a mainnet, fix de red en el swap - #3
Merged
vallejoraul08-debug merged 16 commits intoSep 3, 2026
Conversation
… fuera mainnet NET estaba fijo a StellarSdk.Networks.TESTNET sin leer STELLAR_NETWORK. Con STELLAR_RPC_URL apuntando a mainnet (como en el deploy de hoy), la red del RPC y el passphrase de firma quedaban desincronizados, y el Asset.contractId() de USDC/XLM calculaba el SAC de la red que no es. Encontrado corriendo el smoke test de mainnet contra el contrato recién desplegado.
…ake Waves Endpoint público (sin login) que arma un EscrowCreate/EscrowCancel sin firmar y lo manda como payload de Xaman — el usuario firma con su propia wallet, el backend nunca toca una seed. Es la única forma que no pisa la cláusula anti-sybil del T&C (§7, scripted transactions), ver docs/ESTRATEGIA_300_CUENTAS.md. Incluye el sweeper que cancela solo los escrows vencidos (XRPL no lo hace automático) y el tab "Activar" en el dashboard.
…web) apps/web/Dockerfile pasa a Dockerfile.local — se conserva para build local, pero Railway construye api y web con Railpack (railway.json en cada uno), no con el Dockerfile. El proyecto real es micopaybridge-activation en Railway, no Render (render.yaml es aparte, para si el team decide usarlo después).
…y real de hoy atomic-swap ya está en Stellar mainnet (CB5TCVEBQDU...UGPU, ver render.yaml), y las dos piernas XRPL smoke-testeadas end-to-end contra mainnet real. live-swap-mainnet.ts es necesario aparte de live-swap.ts: ese es solo testnet (wallets XRPL efímeras del faucet, alias Stellar de testnet fijos) y no sirve para probar mainnet ni con las envs cambiadas. Este exige CONFIRM_MAINNET_SWAP=yes y imprime el plan completo antes de mover nada — mueve fondos reales.
Con la configuración de mainnet de este PR (STELLAR_NETWORK=PUBLIC en
render.yaml) el swap se iba a la red real y la capa de pago se quedaba en
testnet. Cada archivo interpretaba la variable por su cuenta y esperaba una
palabra distinta:
lib/soroban.ts STELLAR_NETWORK === "PUBLIC" → mainnet
middleware/x402.ts STELLAR_NETWORK === "MAINNET" → mainnet
No existía ningún valor que pusiera a los dos en la red real:
PUBLIC el swap movía fondos reales, pero verifyPayment enviaba el XDR a
horizon-testnet: se pagaba con USDC de faucet — gratis — y se
recibía un swap con dinero de verdad. Afecta a los trece endpoints
de pago, incluido swaps/execute (0.05) y fund_micopay (0.10).
MAINNET los pagos iban bien, pero el swap se firmaba con el passphrase de
testnet y la red lo rechazaba.
Ni el typecheck ni los tests lo veían, porque cada archivo es coherente
consigo mismo y las suites corren con el valor por defecto, donde ambos
coinciden en testnet. La contradicción solo aparecía con dinero real delante.
- Nuevo lib/stellarNetwork.ts: el único módulo que interpreta la variable.
Acepta las dos grafías a propósito — PUBLIC es como llama Stellar a su red
real y es lo que ya usa el deploy; MAINNET es lo que esperaba x402. Rechazar
una sería otra forma del mismo fallo.
- soroban.ts, x402.ts y scripts/live-swap-mainnet.ts pasan a usarlo. En el
script era un guardarraíl que clavaba la grafía y habría bloqueado el smoke
test de mainnet a quien configurara la otra.
- El nombre de red que se anuncia en el reto 402 se normaliza: dependía de
cómo se hubiera escrito la variable, así que el mismo despliegue podía
decirle "public" o "mainnet" a los agentes.
Tests: cuatro casos, incluido uno que recorre el árbol y falla si algún módulo
vuelve a comparar STELLAR_NETWORK por su cuenta. Ese caso ya encontró el
tercer sitio (el script del smoke test) que la revisión a mano se había
saltado.
Verificado sobre esta rama: typecheck limpio, npm test en verde.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neither failure came from production code. stellarNetwork.test.ts walked the source tree using `.pathname` of a file URL, which leaves spaces percent-encoded. The repo lives under "Micopay Bridge", so readdirSync got "Micopay%20Bridge" and threw ENOENT. Use fileURLToPath instead. x402.test.ts (SEC-C2) saved and restored X402_MOCK_MODE rather than clearing it. With X402_MOCK_MODE=true in a local .env the restore put the bypass back, so the test asserting that a mock payment header is rejected got 200 instead of 402 — green in CI, red locally. Delete the variable in beforeAll and restore the original in afterAll. The middleware gate itself was correct and is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every figure checked against chain and repo on 2026-08-26, not against earlier notes: contract storage and events, both XRPL balances and their zero owner count, the two source-tagged transactions, RPC health, repo visibility and license. Records what is still blocked — Railway trial expired so no public URL, the unset XRPL_SWEEPER_SEED that leaves user escrows waiting on a manual EscrowCancel, and the 5.23 XLM left on mainnet-bridge, which covers invocations but not another 7.29 XLM wasm upload. States the metrics summary honestly: the only two tagged addresses belong to the team, so nothing counts toward the 300 yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ted a tx mainnet.sorobanrpc.com was left in with a "verify before deploying" note. It was verified, and it failed: during the 2026-08-14 mainnet deploy it returned "transaction submission timeout" twice in a row, with the transaction never reaching the ledger (getTransaction returned NOT_FOUND and the account was never charged). The gateway.fm endpoint took it on the first try and still reports healthy. Leaving the failing value behind a TODO meant the next person to deploy would rediscover it the slow way. Also ignore .claude/, which holds the local launch.json used to start the dev servers — developer tooling, not part of the product. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El check de contratos usaba getLedgerEntry, que ya no existe: el RPC de mainnet (27.1.1, protocolo 27) responde -32601, y el catch lo traducía a "Empty response". El contrato SÍ estaba desplegado; /health?detailed=true devolvía 503 unhealthy por un método muerto. Ahora usa getLedgerEntries con la LedgerKey de instancia en XDR base64, que devuelve 1 entrada para CB5TCVEBQDUI2GSQZLMUA2H7FHFHCQLKVGZYJZBECPDVKZCI3PFZUGPU. `build: tsc` no copiaba src/db/migrations al dist, así que runMigrations tiraba ENOENT, el catch de arrancarMigraciones se lo tragaba y las tablas nunca se creaban. El build ahora las copia. Verificado en mainnet: 200 degraded, database up, escrow deployed:true, "✅ Migration 001_initial_schema.sql successful" en el arranque. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rippled rechaza el EscrowCreate si CancelAfter ya pasó cuando llega la firma, y el payload de Xaman vive bastante más que 300 s. Quien instalaba Xaman por primera vez firmaba una transacción muerta: fee quemado, sin escrow, y sin contar para las 300 cuentas del Mainnet Gate. El plazo solo marca desde cuándo se puede reclamar, así que alargarlo no le quita nada al usuario. El contador del panel iba con el mismo número. Verificado contra la API de Xaman sobre el payload real: SourceTag 2607170001, EscrowCreate, Destination == Account, ventana de 60 min. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El header decía "testnet live" fijo mientras la API corría en PUBLIC con el contrato de mainnet — lo primero que ve quien valide el Mainnet Gate. Ahora sale de /health, que es quien sabe en qué red está. El web no tenía config propia, así que heredaba el railway.json de la raíz y arrancaba con el startCommand del api: servía los 404 de Fastify en vez de la SPA. railway.web.json no define startCommand y deja que Railpack sirva los estáticos. Del lado de Railway (no versionado) quedan RAILPACK_SPA_OUTPUT_DIR y RAILPACK_STATIC_FILE_ROOT en apps/web/dist, y VITE_BASE=/ con VITE_OUT_DIR=dist para montar la SPA en la raíz en vez de /bridge/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The file was dated 2026-08-26, taken from the last upstream commit rather than from the day it was written. Renamed and redated to 2026-09-01. Two claims in it are no longer true and were worth more than a date fix: the Railway plan is paid and both services are up, so "app viva en mainnet" is met — /health reports network PUBLIC — and the pitch deck was found (10 slides), leaving the video as the only open §8 deliverable. The test count moves from 113 to 200 because the branch now sits on upstream/main and inherits the bazaar suite. Also notes that PR Micopay#3 is MERGEABLE and CLEAN: what holds it is review, not conflicts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…crow Two ways a person could sign, burn a fee, and end up with no escrow — and therefore no source-tagged transaction, so their address never counted toward the 300. CancelAfter is anchored when the payload is created, but Xaman keeps a payload for 24h by default. With the one-hour default that left 23 hours in which the QR still scanned and signed while the transaction was already expired; rippled rejects it. The comment in xrpl-leg.ts already described this failure from the 300s days — raising it to 3600s shrank the window without closing it. The payload's lifetime is now derived from the actual CancelAfter minus a signing margin, so it can never outlive its own deadline. Measured against Xaman: CancelAfter at +3599s, payload expiry at +3300s. The route also accepted any well-formed address without asking the ledger whether it exists. A wallet freshly created in Xaman and never funded is actNotFound and cannot sign anything — precisely the person this campaign is trying to reach. It now checks account_info first and says what to do, including the real reserve arithmetic. When the check itself fails the request is let through: an unreachable node should not block everyone. The minimum cancelAfterSeconds moves from 60 to 600, because below ten minutes the derived signing window leaves no honest room. minutosDeFirma returns <= 0 for those cases rather than rounding up to one minute — that clamp reintroduced the same bug at the lower bound, which the new test caught. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two changes aimed at the same funnel. The tab now reads from the URL hash, so #activate opens the activation panel directly instead of the agent demo terminal — without it, someone arriving from a campaign has to find the "Activar" tab among eight, and that row is cut off on a narrow screen. The sign step also led with a 220px QR code on every device. Nobody scans their own screen, so on a phone the only actionable control sat below a useless image. On touch devices the "Abrir en Xaman" link now comes first and the QR folds away behind a summary for anyone using a second device; desktop keeps the QR up top. Detection is pointer:coarse rather than width, because what decides whether a QR is useful is finger versus mouse, not how many pixels the window has. Deliberately not auto-navigating to the deep link on load: without Xaman installed that strands the user on a broken page, and iOS blocks navigation that does not originate in a user gesture. Verified by hand on a phone that the link does open the app when tapped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tab bar was a flex row with eight fixed-width items and no scrolling, so on a narrow screen it made the whole document wider than the viewport. Dragging sideways then revealed the body behind the dark wrapper, which was white: the console appeared to occupy half the screen with the tab row floating in a blank area beside it. Reported from a real phone, and reproduced at 375px once the emulation took. The nav now scrolls inside itself and its buttons stop shrinking, which keeps the document at the width of the screen. The body background also moves to the wrapper's own near-black instead of the light --color-surface, so overscroll and any future overflow can't flash white behind a dark page. Measured at 375px after deploying: document scrollWidth 375 against a 375 viewport, nav 375 visible over 1117 of content, body rgb(3, 7, 18). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Folding it behind a summary hid it from the two people who still need it — someone who opened the link on a desktop, and someone holding a second phone with Xaman on it. The deep-link button stays first, which was the point of the reorder; the code now sits open underneath it and can still be collapsed. Verified at 375px on the real sign step: order is link, summary, image, with the details open and the QR rendering at 200x200. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
vallejoraul08-debug
force-pushed
the
feat/xrpl-htlc-referencia
branch
from
September 3, 2026 04:19
3f8eb28 to
81a50aa
Compare
Collaborator
Author
|
Mergeo yo este PR, y dejo constancia de por qué en vez de que aparezca sin explicación. Lleva abierto desde el 14 de agosto sin review. Mientras tanto el Mainnet Gate de Make Waves sigue abierto y el panel pide agendar la reunión de validación; el reto cierra el 21 de septiembre. Sin este PR, Qué trae, verificado contra producción y no solo en local:
La rama se rebaseó sobre Si algo no te cuadra, revierto sin problema — prefiero eso a que el gate se nos pase esperando. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
atomic-swap(AtomicSwapHTLC) a Stellar mainnet:CB5TCVEBQDUI2GSQZLMUA2H7FHFHCQLKVGZYJZBECPDVKZCI3PFZUGPU. Smoke test end-to-end contra mainnet real (self-swap Soroban + 1 XRP real entre dos wallets XRPL propias) — las 4 tx verificadas on-chain, source tag2607170001presente.soroban.ts: el network passphrase estaba fijo a testnet sin leerSTELLAR_NETWORK, así que aunqueSTELLAR_RPC_URLapuntara a mainnet, las tx se firmaban con el passphrase equivocado.EscrowCreate/EscrowCancelsin firmar y lo manda como payload de Xaman — el usuario firma con su propia wallet, el backend nunca toca una seed (evita la cláusula anti-sybil del T&C §7). Incluye sweeper que cancela solo los escrows vencidos, y tab "Activar" en el dashboard.apiyweb.Test plan
npm run typecheck -w @micopay/apilimpionpm run test:live:mainnet -w @micopay/api) completó las 4 tx, verificadas independientemente en stellar.expert / livenet.xrpl.orgmicopaybridgees el repo que cuenta para el §8 del Mainnet Gate (repo público obligatorio) o si esto también debe reflejarse enmicopay-protocolXRPL_SWEEPER_SEEDsigue sin configurar — sin eso, los escrows de activación vencidos requieren reclamo manual del usuario🤖 Generated with Claude Code