Skip to content

Mykobo Base EVM Integration#1159

Draft
ebma wants to merge 70 commits into
stagingfrom
mykobo-base-evm-api
Draft

Mykobo Base EVM Integration#1159
ebma wants to merge 70 commits into
stagingfrom
mykobo-base-evm-api

Conversation

@ebma
Copy link
Copy Markdown
Member

@ebma ebma commented May 22, 2026

Closes #1121.

ebma added 23 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.
@netlify
Copy link
Copy Markdown

netlify Bot commented May 22, 2026

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit 990e357
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a187b3b45a499000810863d
😎 Deploy Preview https://deploy-preview-1159--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
Copy link
Copy Markdown

netlify Bot commented May 22, 2026

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit 990e357
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a187b3b82e83d0008132193
😎 Deploy Preview https://deploy-preview-1159--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.

const TEST_IP_ADDRESS = "203.0.113.42";

const filePath = path.join(__dirname, "lastRampStateMykoboEurOnramp.json");
const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
ebma added 4 commits May 22, 2026 10:13
…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.
Sharqiewicz and others added 29 commits May 22, 2026 18:01
The OffRampFromEvmInitializeEngine is shared by three strategies:
- offrampToSepaEvm (Base → EUR via Mykobo)
- offrampToPixEvm (Base → BRL via Avenia)
- offrampEvmToAlfredpay (Polygon → BRL via Alfredpay)

All Base-targeting strategies feed OffRampSwapEngineEvm, whose Nabla pool
on Base only has USDC liquidity. The engine was unconditionally requesting
USDT (ALFREDPAY_EVM_TOKEN) as the Squid bridge target, producing quote
metadata (evmToEvm.toToken) that contradicts what the downstream Nabla
swap actually expects.

Derive the bridge target from the destination network: USDC for Base,
USDT for Polygon (where the downstream path is a direct Alfredpay payout
in USDT and Nabla is not involved).
Mykobo (EUR via SEPA) and Alfredpay/Avenia (BRL via PIX) offramps now
have dedicated initializer files matching the project's anchor-named
file convention. The Mykobo variant is fixed to Base+USDC since it has
no other deployment target; the alfredpay variant keeps its
network-aware USDC/USDT selection for Avenia (Base) and Alfredpay
(Polygon).
OnRampSquidRouterBrlToEvmEngineBase is used by both the Avenia BRL and
Mykobo EUR onramp strategies and does nothing currency-specific. Rename
to OnRampSquidRouterToBaseEngine to remove the misleading prefix.
…edicated files

Previously a single OffRampFromEvmInitializeEngine in
offramp-from-evm-alfredpay.ts served two anchors with a network
parameter:
- Avenia (Base \u2192 BRL via PIX) needed USDC as the Squid target
  because the downstream Nabla pool only has USDC liquidity.
- Alfredpay (Polygon \u2192 BRL) needed USDT because the partner pays
  out directly in USDT.

The shared class with a runtime branch obscured the anchor-specific
choices. Split into one file per anchor matching the existing
file-naming convention: each class hardcodes its target network and
token, removing the helper and the constructor parameter.
calculateSquidrouterNetworkFee was multiplying the route's native-token
value by the GLMR/USD price for every route, including Base, Polygon,
Ethereum, Avalanche and BSC routes. For Base->Polygon this under-reported
the network fee by roughly four orders of magnitude (ETH ~$2500 vs
GLMR ~$0.08).

Map the source network to the correct CoinGecko native-token id and use
a per-chain conservative fallback price so we never silently report a
near-zero fee when the price feed is unavailable.
…00 instead of 500

- register.actor.ts: replace duplicated inline additionalData dispatch with
  buildRegisterRampAdditionalData(); the previous version had no SELL+EURC
  branch, so Mykobo offramps fell through to the catch-all and never sent
  the user email, causing backend rejection.
- registerAdditionalData.ts + test: add the helper and unit coverage that
  cover BUY/SELL EURC, BRL, and Alfredpay paths.
- evm-to-mykobo.ts: throw APIError(400, isPublic) for missing email,
  ipAddress, and destinationAddress instead of plain Error so client gets
  a proper 4xx and a public error message.
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.

Migrate EURC ramp

3 participants