Skip to content

Create new production release#1183

Merged
ebma merged 102 commits into
mainfrom
staging
Jun 1, 2026
Merged

Create new production release#1183
ebma merged 102 commits into
mainfrom
staging

Conversation

@ebma

@ebma ebma commented Jun 1, 2026

Copy link
Copy Markdown
Member

No description provided.

ebma added 30 commits May 21, 2026 10:54
Introduces the Mykobo anchor integration surface in the shared package:
singleton MykoboApiService with bearer auth + 401 retry, request/response
types, EURC EvmToken on Base, Mykobo env vars, and an email field on
RegisterRampRequest.additionalData to bind a ramp to a Mykobo profile.
Adds an EVM-to-SEPA quote strategy that swaps USDC to EURC on Base via
Nabla and reads the anchor fee dynamically from Mykobo's /fees endpoint,
mirroring the AlfredPay model (no static anchor table row). RouteResolver
sends sepa+EVM to the new strategy while sepa+AssetHub stays on the
existing Stellar path. Adds Mykobo-specific RampState meta fields and the
locked-in implementation plan doc.
Adds the execution slab for the Mykobo EUR offramp targeting Base (EVM):

- New evm-to-mykobo route prepares squidRouter (skipped when source is
  Base USDC), fee distribution, Nabla USDC->EURC swap, Mykobo intent
  creation, presigned EURC transfer to receivables, plus USDC/EURC/AXLUSDC
  cleanup approvals.
- New mykoboPayoutOnBase phase handler broadcasts the EURC transfer and
  polls Mykobo until the transaction reaches COMPLETED.
- subsidizePostSwap branches to mykoboPayoutOnBase for EUR offramps and
  routes EUR EVM offramps through the EVM subsidy path.
- fundEphemeral now provisions a Base ephemeral for EUR offramps and only
  verifies user-submitted squid hashes when a blueprint exists.
- base-chain post-process sweeps the new baseCleanupEurc phase alongside
  existing Base cleanups.
- ramp controller captures req.ip into additionalData so the Mykobo intent
  request carries the user IP required by the API.
- Add integration test hitting real Mykobo dev sandbox (fees, intent,
  quote creation, registerRamp) without on-chain submission.
- Fix OffRampFinalizeEngine to honor nablaSwapEvm output for SEPA path
  (not just PIX); previously SEPA quotes failed finalization.
- Strip /v1 from default MYKOBO_BASE_URL; client appends it itself, so
  env var is now the bare host (https://api-dev.mykobo.app).
- Ignore lastRampStateMykoboEur.json test artifact.
Introduces the mykoboOnrampDeposit RampPhase for the upcoming EVM ephemeral flow, an IbanPaymentData.reference field for SCOR payment references, EURC Base ERC20 constants, a free-token config for Mykobo EUR, and wires the new phase into the frontend progress page maps. Reuses the unified subsidizePreSwap / subsidizePostSwap / distributeFees handlers for the EVM branch instead of introducing suffixed phase names.
The unified subsidizePreSwap, subsidizePostSwap, nablaApprove, nablaSwap, and distributeFees handlers previously branched into the EVM path when the input or output currency was BRL (and a partial EURC special-case for offramp post-swap). This hardcoded the BRL flow into every handler and required a new conditional for each future EVM-ephemeral route. Switch the dispatch to check quote.metadata.nablaSwapEvm, which is set by the quote engine whenever the EVM-ephemeral Nabla path is selected (BRL today, Mykobo EUR onramp + offramp next). Removes the FiatToken import where it is no longer used.
Adds OnRampInitializeMykoboEngine which estimates EURC delivered on the Base ephemeral after Mykobo's deposit fee, populating ctx.mykoboMint for downstream stages. Introduces a Mykobo onramp validation helper that checks for an EVM ephemeral and EURC on Base, a MykoboApiService.defaultDepositFee shortcut to lookup MykoboFeeKind.DEPOSIT fees, the MykoboOnrampTransactionParams type carrying the user email, and a parallel ctx.mykoboMint shape next to ctx.aveniaMint / ctx.aveniaTransfer in the quote context.
Adds OnRampSwapEngineMykoboEvm, an EVM Nabla swap variant that consumes ctx.mykoboMint and swaps EURC -> USDC on Base, OnRampMykoboToEvmFeeEngine which carries the Mykobo deposit fee as the anchor fee and computes the Squidrouter network fee for Base USDC -> destination, and the onrampMykoboToEvmStrategy route strategy that chains Initialize -> Fee -> NablaSwap -> Discount -> SquidRouter -> Finalize. Reuses OnRampSquidRouterBrlToEvmEngineBase since it operates on USDC on Base regardless of upstream fiat origin.
Replace Monerium with Mykobo as the EUR→EVM onramp path:
- RouteResolver: EURC + EVM destination → onrampMykoboToEvmStrategy
- InitialPhaseHandler: EURC BUY → mykoboOnrampDeposit phase

Monerium AssetHub strategy is retained for the substrate path.
Add OnrampMykobo kind to ramp-transaction-preparation that routes EURC BUY
on EVM destinations to a new prepareMykoboToEvmOnrampTransactions route.
RampService.prepareMykoboOnrampTransactions creates the Mykobo deposit
intent (EURC, DEPOSIT, evm ephemeral address) and surfaces IBAN +
reference via ibanPaymentData. Monerium remains for EURC -> AssetHub.
Mykobo on Base replaces Monerium for EUR onramps. EUR onramps now route
exclusively through the Mykobo EVM-ephemeral flow; EUR→AssetHub is no
longer supported and returns HTTP 400.

- Delete Monerium controller, route, service, handlers, quote engines,
  strategies, transaction routes, and shared endpoint module
- Drop `moneriumAuthToken`, `moneriumMint`, `moneriumOnrampSelfTransfer`,
  `moneriumOnrampMint` from phase/quote/meta types and validation
- Remove EURC→Monerium branch from fund-ephemeral nextPhaseSelector and
  ramp helpers explorer-link builder
- Drop `onrampMoneriumToAssethubStrategy` import and SEPA→AssetHub branch
  from route-resolver; EURC+AssetHub BUY now hard 400
- Drop `createOnrampSquidrouterTransactionsFromPolygonToMoonbeam` from
  shared squidrouter; retain Polygon→EVM path for Alfredpay USDT
- Inline `Signature` type into ramp.endpoints (was re-exported from
  deleted Monerium endpoint module)
- Update validation.test fixtures to use generic squid/nabla phases
Sweep removal of Monerium frontend integration: KYC machine, redirect
step, AssetHub form step, auth service, hook, stories, schema fields,
URL params, translations, and progress flow entries. Mykobo replacement
UI will be added in a dedicated slab.

- Delete moneriumKyc.machine, moneriumAuth, monerium.service, useMoneriumFlow,
  MoneriumRedirectStep, MoneriumAssethubFormStep, related stories
- Remove ERC2612 permit branch and SEPA SELL message-signature branch
  from sign.actor; drop `moneriumOfframpSignature` and `moneriumOnrampPermit`
  from additionalData
- Drop `moneriumWalletAddress` from useRampSubmission, schema, register
  payload, types.phases; EUR onramps deposit directly to evmEphemeral
- Remove `useMoneriumKycActor`/`Selector`, MoneriumKyc types from contexts
  and machines/types
- Simplify `getRampFlow`: EURC BUY always uses `onramp_eur_evm` (Mykobo
  flow). Drop `onramp_eur_assethub` and `onramp_eur_assethub_via_hydration`
- Replace `onramp_eur_evm` flow with Mykobo: initial → mykoboOnrampDeposit
  → subsidizePreSwap → nablaApprove → nablaSwap → subsidizePostSwap →
  squidRouter* → distributeFees → destinationTransfer → complete
- Drop `MoneriumErrors.USER_MINT_ADDRESS_NOT_FOUND` branch from
  useGetRampRegistrationErrorMessage and SummaryStep banner
- Drop `ERC20_EURE_POLYGON_*` constants from shared tokens; remove all
  Monerium translation keys from en.json and pt.json
Poll the Base ephemeral for EURC arrival from Mykobo's SEPA settlement,
then transition to subsidizePreSwap so the Nabla EURC→USDC swap can run.

- Mirror BRLA/Alfredpay mint-handler structure: `checkEvmBalancePeriodically`
  on Base for EURC against `evmEphemeralAddress`, expected raw amount taken
  from `quote.metadata.mykoboMint.outputAmountRaw`
- Recovery shortcut: skip the wait if the ephemeral already holds \u226595% of
  the expected amount (handles fee variance between quote and settlement)
- 24h payment timeout reflects SEPA business-day cutoffs; on timeout the
  phase transitions to `failed`. Inner balance-check timeout (5 min) bubbles
  up as a recoverable error so the outer process loop can retry
- Register `mykoboOnrampDepositHandler` alongside Mykobo payout handler

Flow: initial \u2192 mykoboOnrampDeposit \u2192 subsidizePreSwap \u2192 nablaApprove \u2192
nablaSwap \u2192 subsidizePostSwap \u2192 squidRouter* \u2192 destinationTransfer
…ture

EUR offramp migrated to Mykobo on Base/EVM; ARS to be rebuilt with AlfredPay
later. This removes all Stellar token configs, Spacewalk redeem paths, SEP-10
auth, Stellar/Spacewalk phase handlers, Stellar tx builders/validation,
StellarTokenConfig/Details types, Stellar ephemeral helpers, and stellar_eurc
/stellar_ars anchor fee branches. FiatToken.ARS preserved for the frontend
selector. ARS via 'cbu' now throws explicit 'temporarily unavailable' error
in route-resolver. PaymentData.anchorTargetAccount removed (only Stellar
callsites). XCM event parser moved to xcmParsers.ts so substrate event
listener compiles without redeem parsers.
Deletes stellar_eurc and stellar_ars rows from anchors table and removes
XLM-token subsidies, completing the Stellar removal. New migration (028);
prior migrations 005/008/022 are left untouched.
…ring

Deletes stellarKyc XState machine + SEP-10/24 actors and services, the
useSignChallenge hook, and stellar.service. Strips stellar ephemeral creation
from useRampSubmission, register.actor, and ramp.machine; removes stellarKyc
branch from kyc.states; removes spacewalkRedeem/stellarPayment/stellarCreate
Account from phaseFlows and phaseMessages; cleans up rampState context, ramp
page, RampSubmitButton, NetworkSelector, TokenSelection helpers, summary
TransactionTokensDisplay, storage.service, services/api/index re-export,
phases types, localStorage keys, and progress flow resolver. ARS coin icon
and FiatToken.ARS preserved for the token selector.
Drops createStellarEphemeral usage from VortexSdk, the stellarEphemeral
field from SDK input/internal types, and BrlHandler's stellar ephemeral
plumbing. SDK no longer signs Stellar transactions since the backend has
no Stellar offramp paths.
…m fallback

Refactors the bridged-token-for-fallback ternary into an explicit if/else
that throws if Ethereum USDC config is missing. Removes the only Mykobo-scoped
biome lint error introduced in this branch.
…hemeral from mykobo offramp test

Slab 7 removed stellarTokenConfig, which was the only place FiatToken.EURC
had fiat metadata registered, causing getAnyFiatTokenDetails(EURC) to throw
'Token type is not Moonbeam' in the offramp quote validation path.

- Register FiatToken.EURC in freeTokenConfig with decimals=6 (Circle's EVM
  EURC on Base) and matching min/max amount caps.
- Remove the leftover Stellar ephemeral from the mykobo-eur-offramp
  integration test now that the Mykobo+Base flow only needs EVM+Substrate
  ephemerals (the normalize step now rejects Stellar entries).

Mykobo offramp integration test: 5/5 passing against real sandbox.
Mirrors mykobo-eur-offramp.integration.test.ts for BUY direction.
Covers Mykobo /fees DEPOSIT lookup, createTransactionIntent DEPOSIT
returning IBAN instructions, EUR onramp quote on Base, and ramp
registration which calls Mykobo at registration time and populates
ibanPaymentData + state.mykoboTransactionId/Reference.

Asserts Base-destination phase set: nablaApprove, nablaSwap,
destinationTransfer, baseCleanupEurc, baseCleanupUsdc; no SquidRouter.

Adds gitignore entries for the test scratch file. All 5/5 tests pass
against the real Mykobo sandbox.
…e flows

Adds 05-integrations/mykobo.md covering the active EUR rail (SEPA <-> EURC
on Base, mirroring BRLA-on-Base topology) with invariants, threat vectors,
and audit checklist. Updates ramp-phase-flows.md corridor list, mermaids,
handler-registry table, and audit checklist to reflect Mykobo on/offramp
and the removal of the Stellar-EUR offramp and Monerium-EUR onramp.
…cated

Adds deprecation banners pointing to mykobo.md. Stellar anchors remain
active for ARS-only off-ramp; Monerium is fully removed from active
corridors. Files retained for audit lineage of F-023 / F-024.
Updates module index, glossary, system architecture, ephemeral-account
inventory, discount/validation notes, integration corridor lists,
Spacewalk bridge intro, fund-routing F-029 (Mykobo Base ops added to
MOONBEAM_FUNDING_PRIVATE_KEY blast radius), secret-management inventory
(MYKOBO_* env vars added), and release-readiness env-var list (replace
MONERIUM_* with MYKOBO_*). No code paths reference Monerium for new
ramps; banners preserve audit lineage.
AUDIT-RESULTS.md: marks Section 5.2 Monerium DEPRECATED, adds Section
5.2b Mykobo with 21 checks (20 PASS, 1 PARTIAL log redaction, 1 FAIL
on HTTP-client timeout per F-014), updates F-014 location list, marks
F-023 SUPERSEDED and F-024 carried forward, retargets top-10 action
item #9 to a per-user concurrent Mykobo-ramp limit.

FINDINGS.md: F-014 service list now includes Mykobo+BRLA and notes the
Monerium gap was inherited by the Mykobo client; F-023 status set to
SUPERSEDED with resolution explaining the 24h Mykobo outer timeout;
F-024 carried forward as DEFERRED — STILL APPLIES with the impact
rewritten for the larger 24h exposure window per pending unpaid ramp.
- Rewrite ramp-phase-flows on-ramp and off-ramp mermaid diagrams as
  unified funnel-after-fiat: per-corridor fiat-watch entry collapses
  into the shared Pendulum/Nabla chain, then splits again at payout.
- Correct EUR on-ramp prose: mykoboOnrampDeposit transitions directly
  to subsidizePreSwap, skipping fundEphemeral.
- Remove ARS as an active corridor: drop ARS row from cross-chain
  delivery, drop ARS off-ramp branch from the diagram, and downgrade
  stellar-anchors.md from 'ARS-only' to fully deprecated.
- Fix mykobo.md endpoint typo: /v1/mykobo/profile -> /v1/mykobo/profiles
  (matches mykobo.route.ts).
File four open findings raised during the Mykobo-on-Base EUR rail audit:

- F-068 (Critical): /v1/mykobo/profiles GET/POST have no requireAuth;
  anonymous callers can enumerate KYC profiles by wallet address and
  POST KYC documents (ID, source-of-funds, selfie) against arbitrary
  wallet addresses. Parent mount at routes/v1/index.ts is also
  unwrapped. Violates mykobo.md Invariant 15. Fix: add requireAuth to
  both routes (mirroring alfredpay.route.ts) plus wallet-ownership
  check.

- F-069 (High): fund-ephemeral-handler.nextPhaseSelector has no EURC
  SELL branch; EUR off-ramps fall through to 'moonbeamToPendulum',
  which has no role in Base-only EUR off-ramp routing
  (evm-to-mykobo.ts). Latent stuck-phase bug. Not surfaced by tests
  because integration tests only call registerRamp, not startRamp on
  this path. Fix: add explicit EURC -> distributeFees branch mirroring
  BRL.

- F-070 (Medium): MykoboApiService constructor accepts any URL scheme
  for MYKOBO_BASE_URL. A misconfigured deployment with http:// would
  silently transmit bearer tokens, KYC document references, and IBAN
  payment instructions in cleartext. Fix: enforce
  URL.protocol === 'https:' in production.

- F-071 (Low): MykoboApiService.handleAuthFailure is not debounced.
  Concurrent 401s trigger concurrent refresh/acquire calls (thundering
  herd), with this.cachedToken writes racing. getToken is correctly
  debounced via tokenPromise; apply the same pattern to the 401 path.

Also flip the corresponding rows in AUDIT-RESULTS.md (Mykobo subsection
5.2b checks #2, #14, #17, #19) from PASS to FAIL/PARTIAL, update the
summary tables in both files to reflect 4 open findings, and add the
remediation entries to the Recommended Remediation Order.
Both GET and POST /v1/mykobo/profiles were unprotected, allowing anonymous
callers to enumerate KYC profiles and submit arbitrary KYC documents.
Adds requireAuth middleware on both routes, mirroring the alfredpay pattern.
No wallet-ownership check needed: wallet_address in the Mykobo flow always
refers to an ephemeral, not a user-owned wallet.
ebma added 27 commits May 29, 2026 20:33
EUR→EURC on Base is degenerate: Mykobo settles the SEPA deposit directly
as EURC on the Base ephemeral, so the generic Nabla(EURC→USDC)+Squid(USDC→EURC)
path swaps a token for itself, wasting fees and exposing the ramp to a
settlement-subsidy race.

Add isEurToEurcBaseDirect() and short-circuit this corridor end to end:
- quote: Nabla engine emits a 1:1 EURC passthrough; Squid engine emits a
  same-token same-chain record with skipRouteCalculation.
- tx builder: emit only the destinationTransfer (and mark isDirectTransfer),
  skipping nabla/squid/cleanup txs.
- fund-ephemeral: route the direct corridor straight to destinationTransfer.
The finalSettlementSubsidy handler returned as soon as any balance (incl.
leftover dust from the Nabla swap) appeared on the ephemeral, then computed
subsidy = expected - totalBalance. When the Squid bridge output had not yet
landed, it over-subsidized: a EUR→EURC Base ramp received ~29.36 EURC from
the funder on top of the bridge delivery, stranding ~59 EURC.

- snapshot destination-token balance (preSettlementBalance) right after the
  squidRouter swap confirms, before squidRouterPay, so the subsidy reflects
  actual bridge delivery (actualBalance - preSettlementBalance) rather than
  total balance.
- wait for >=90% of expected delivery before computing the subsidy.
- defensively skip the subsidy/squidRouter phases for direct-transfer ramps.
When the source and destination chains are the same (EURC/BRLA on Base,
AlfredPay token on Polygon), destinationTransfer was presigned with a
nonce after the post-complete cleanup approvals. Since cleanups only
broadcast at the 'complete' phase, the presigned nonce sat ahead of the
ephemeral's live nonce and could never be mined, stranding user funds
(0 delivered).

Place destinationTransfer at the first executable nonce after the swap
for same-chain routes, followed by the cleanup approvals. Backup re-swap
txs are omitted on same-chain paths (no handler executes them and they
would re-introduce the nonce gap).
Add a best-effort guard in DestinationTransferHandler that compares the
presigned transaction nonce against the ephemeral's live on-chain nonce
before broadcasting. If the presigned nonce is ahead of the live nonce,
the transfer can never be mined, so raise an unrecoverable error for
manual review instead of silently retrying until funds are stranded.

RPC failures while reading the live nonce are non-blocking and fall
through to the normal happy path.
Reframe the same-chain destination nonce invariant/threat from the
misleading "nonce-0 collision" to the real failure mode — a nonce gap
that leaves destinationTransfer permanently un-mineable and strands
funds (0-delivery). Add the new destination-transfer fail-fast guard
invariant, the finalSettlementSubsidy over-subsidy race fix, and the
EUR→EURC-on-Base direct-transfer bypass across ramp-phase-flows,
fund-routing, and mykobo specs.
The squidRouter phase handler skipped the swap for any AlfredPay onramp
delivering to Polygon, keyed only on metadata.to (the destination
network). Since AlfredPay mints USDT on Polygon, a USDC (or other token)
output requires a real USDT->output swap; skipping it stranded funds.

Gate the skip additionally on quote.outputCurrency === ALFREDPAY_EVM_TOKEN
so only the direct-token (USDT/Polygon) case short-circuits, mirroring the
quote engine's skipRouteCalculation condition. USDC/Polygon now falls
through to execute the swap; USDT/Polygon and cross-chain are unaffected.
Avenia mints BRLA on Base, but the BRL→BRLA/Base corridor still forced an
unconditional BRLA→USDC Nabla swap (a degenerate self-swap) that fails.

Mirror the existing EUR→EURC Base direct path for BRL→BRLA: add an
isBrlToBrlaBaseDirect predicate and wire it through the quote engines and
phase handlers. The Avenia Nabla engine now bypasses the swap with 1:1 BRLA
passthrough metadata, the shared Base SquidRouter engine emits a 1:1
passthrough bridge record, and the Avenia route emits a single
destinationTransfer (nonce 0) with isDirectTransfer set — skipping the
Nabla swap, fee-token distribution, SquidRouter, and both Base cleanups
that only make sense when a BRLA→USDC swap actually happened.

The fund-ephemeral, squidRouter, and finalSettlement handlers recognise the
new direct predicate alongside the persisted isDirectTransfer flag, keeping
BRL→BRLA structurally identical to the proven EUR→EURC corridor.
…ge cases

Document the two degenerate no-swap on-ramp fixes alongside the existing
EUR→EURC bypass:

- Bug 1 (AlfredPay): the Polygon swap short-circuit must be gated on the
  output token (`quote.outputCurrency === ALFREDPAY_EVM_TOKEN`), not on the
  destination network alone — otherwise a non-USDT Polygon output (e.g.
  USDC) is mis-delivered as the minted USDT. Added invariant, threat, and
  checklist item to alfredpay.md; note in ramp-phase-flows.md.
- Bug 2 (Avenia/BRLA): BRL→BRLA-on-Base must take the direct-transfer
  bypass (`isBrlToBrlaBaseDirect`) — single destinationTransfer, no Nabla/
  distributeFees/Squid/finalSettlementSubsidy/cleanup. Added flow
  subsection, invariant, threat, and checklist items to brla.md; note in
  ramp-phase-flows.md; extended the degenerate-route invariant/checklist in
  fund-routing.md.
…ges-for-order-limits

[READY] Improve Error Messages
Change subsidy guard from targetDiscount > 0 to targetDiscount !== 0
in all four discount engines so that negative configured targets
(e.g., -0.001 for reference rate minus 10 bps) correctly compute
and apply subsidies when actual output falls below the floor.

- onramp.ts
- offramp.ts
- onramp-alfredpay.ts
- offramp-alfredpay.ts

Add unit tests for calculateSubsidyAmount covering:
- Positive/negative/zero targetDiscount scenarios
- Max subsidy cap behavior
- Actual output meeting or exceeding target
Persists ephemeral keypairs to a separate localStorage item keyed by ramp ID, so they survive rampState resets. Includes security-spec documentation.
…alstorage-backup

Add rampEphemerals localStorage backup for ephemeral key failsafe
…count

Allow negative targetDiscount as rate floor
Delete expired quotes that are older than 90 days
@netlify

netlify Bot commented Jun 1, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit ba4bd85
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a1dcf4120b356000937e2a8
😎 Deploy Preview https://deploy-preview-1183--vortexfi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 1, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit ba4bd85
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a1dcf414b4aa7000701cc9f
😎 Deploy Preview https://deploy-preview-1183--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@ebma ebma merged commit afb255f into main Jun 1, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants