Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
183 commits
Select commit Hold shift + click to select a range
4ca87e8
fix(rebalancer): resume in-flight runs before sizing a fresh one
ebma Sep 19, 2026
58fa8b0
feat(rebalancer): add reset script for a stuck USDC base state
ebma Sep 19, 2026
ef6cc3c
fix(rebalancer): keep dry-run read-only and read coverage before resu…
ebma Sep 19, 2026
aab69b8
docs(rebalancer): sequence the state reset after pausing the cron
ebma Sep 19, 2026
ed4f56c
fix(rebalancer): end the invocation when dry-run pauses an in-flight run
ebma Sep 19, 2026
c290abd
fix(rebalancer): skip the off-mode balance read and test the cycle
ebma Sep 19, 2026
fe8d8b3
Merge pull request #1382 from pendulum-chain/fix/rebalancer-resume-in…
ebma Sep 19, 2026
a997fec
fix(shared): log Squidrouter error status and raw non-JSON body
ebma Sep 20, 2026
76fa569
fix(shared): retry Squidrouter route requests on gateway 5xx
ebma Sep 20, 2026
562abf4
fix(shared): keep the Squidrouter status when the error body cannot b…
ebma Sep 20, 2026
c2e894f
docs(repo): document the Squidrouter gateway 5xx retry in the securit…
ebma Sep 20, 2026
f8c6c61
docs(repo): align the Squid audit checklist with the single-retry con…
ebma Sep 20, 2026
0f77e06
Merge pull request #1383 from pendulum-chain/fix/squidrouter-gateway-…
ebma Sep 20, 2026
57ad76c
fix(rebalancer): drop the redundant husky prepare hook
ebma Sep 29, 2026
ab8afd5
Merge pull request #1385 from pendulum-chain/fix/rebalancer-husky-pre…
ebma Sep 29, 2026
bec6979
fix(sdk): map the api invalid pixKey message to InvalidPixKeyError
ebma Sep 29, 2026
4038d20
fix(api): apply the SELL release gate to the ramp status response
ebma Sep 29, 2026
ad8870c
Merge pull request #1386 from pendulum-chain/fix/sdk-invalid-pixkey-m…
ebma Sep 29, 2026
30ca30d
test(repo): run the gold app tests from the root test script
alexatsatoshi Sep 29, 2026
a1f95b4
fix(frontend): restore gold security headers lost in the netlify move
alexatsatoshi Sep 25, 2026
39cf116
perf(gold): serve the landing image as webp and shrink the wordmark
alexatsatoshi Sep 25, 2026
0a457a6
fix(gold): show a year of prices under 1A and each period's change
alexatsatoshi Sep 25, 2026
0fc26af
fix(gold): keep tracking a ramp through transient status errors
alexatsatoshi Sep 25, 2026
9aac903
fix(gold): count the pix deadline down from the clock
alexatsatoshi Sep 25, 2026
2fc5466
fix(gold): link the terms and privacy policy accepted at login
alexatsatoshi Sep 25, 2026
8080fca
fix(gold): raise legal and fee note contrast to wcag aa
alexatsatoshi Sep 25, 2026
f619dbd
fix(gold): read kyc readiness from the user's own avenia account
alexatsatoshi Sep 25, 2026
97548b2
fix(gold): resume a sell with the ramp's unsigned wallet transactions
alexatsatoshi Sep 25, 2026
8eef3db
fix(gold): stop abandoned ramps from blocking new operations
alexatsatoshi Sep 25, 2026
8a5c90e
fix(gold): count a resumed pix down to its real start deadline
alexatsatoshi Sep 25, 2026
8a0cf4a
fix(gold): request the e-mail code in brazilian portuguese
alexatsatoshi Sep 25, 2026
0f025e0
fix(gold): keep the vortex session on transient refresh failures
alexatsatoshi Sep 25, 2026
aeb4b15
fix(gold): submit avenia kyc with the alpha-3 country code
alexatsatoshi Sep 25, 2026
2ef6986
docs(repo): list the gold node tests in the testing strategy
alexatsatoshi Sep 25, 2026
f12a756
feat(gold): skip the e-mail code while the vortex session is valid
alexatsatoshi Sep 25, 2026
0ed289e
perf(gold): drop the landing png that the webp replaced
alexatsatoshi Sep 29, 2026
2463f7f
fix(api): skip presign validation on BUY ramp status requests
ebma Sep 29, 2026
7cd2850
docs(api): document the dashboard route to API keys
ebma Sep 29, 2026
6215421
test(api): cover the dynamic presign branch of the status release gate
ebma Sep 29, 2026
7a1c116
docs(api): show bank transfer instructions from update, not start
ebma Sep 29, 2026
9b2491e
docs(sdk): read bank transfer instructions from the registered ramp
ebma Sep 29, 2026
4518e63
docs(api): add an own-account path for bots and treasury jobs
ebma Sep 29, 2026
7a93582
docs(api): list the ramp status values the API returns
ebma Sep 29, 2026
1f6704d
docs(repo): use current SDK names in the vortex-integration skill
ebma Sep 29, 2026
206a11f
docs(sdk): drop the payment partner's name from the README
ebma Sep 29, 2026
82cff92
fix(gold): lock the buy amount while its quote is prepared
ebma Sep 29, 2026
7953652
fix(gold): clear the previous step's error when going back
ebma Sep 29, 2026
eef5e57
ci(repo): run the gold app tests in the test job
ebma Sep 29, 2026
abee2f1
Merge pull request #1387 from pendulum-chain/fix/ramp-status-release-…
ebma Sep 29, 2026
8d2b857
docs(api): state EUR availability once and name the provider neutrally
ebma Sep 29, 2026
70da011
docs(sdk): name the EUR provider neutrally in the README
ebma Sep 29, 2026
4435e36
docs(repo): align the integration skill with EUR availability
ebma Sep 29, 2026
94ed2d6
fix(gold): keep failed operations in the history with their code
ebma Sep 29, 2026
1a0d846
fix(gold): point the price change arrow down when the price fell
ebma Sep 29, 2026
14812f7
fix(gold): refuse wallet transactions that are not the quoted sell
ebma Sep 29, 2026
082d0fe
fix(gold): never broadcast a sell after its start window closed
ebma Sep 29, 2026
89a46d7
fix(shared): retry the Squid token list after a failed load
ebma Sep 29, 2026
ee0fc25
fix(api): retry the Squid token list in the background at boot
ebma Sep 29, 2026
9c1de90
docs(api): record the token-registry retry invariant in the Squid spec
ebma Sep 29, 2026
718e64c
docs(api): mark business EUR accounts as sandbox-only for now
ebma Sep 29, 2026
5120486
fix(gold): keep polling kyc through blips and restart expired attempts
ebma Sep 29, 2026
93be7a7
fix(gold): check the cpf digits before starting kyc
ebma Sep 29, 2026
4cc445c
fix(gold): delete a ramp's ephemeral keys once it completed
ebma Sep 29, 2026
d768185
perf(gold): render the landing before privy has loaded
ebma Sep 29, 2026
e8a13b5
docs(api): complete the own-account setup and signing steps
ebma Sep 29, 2026
9c12a05
docs(sdk): mark EUR buy as post-0.9.0 and add payout accounts first
ebma Sep 29, 2026
5818947
docs(api): route the EUR permit by its signer in the own-account path
ebma Sep 29, 2026
d85e1b1
docs(repo): give the skill's sell recipe a sell quote and payout guard
ebma Sep 29, 2026
be9b040
docs(api): pass the DomesticCountry enum in payout-account lookups
ebma Sep 29, 2026
58a0713
docs(sdk): note EUR is sandbox-only next to the SDK version requirement
ebma Sep 29, 2026
68ca928
Merge pull request #1389 from pendulum-chain/docs/api-dashboard-keys-…
ebma Sep 29, 2026
37cb4de
fix(frontend): never broadcast a sell after its start window closed
ebma Sep 29, 2026
6d1a0cf
fix(frontend): tell the user an expired sale sent nothing
ebma Sep 29, 2026
d254a5e
docs(frontend): document the sell start-deadline broadcast guard
ebma Sep 29, 2026
f915754
Merge pull request #1393 from pendulum-chain/fix/sell-start-deadline-…
ebma Sep 30, 2026
caa21cb
Merge pull request #1388 from pendulum-chain/fix/gold-review
ebma Sep 30, 2026
9de1835
fix(gold): keep focus in the field while the app refreshes
ebma Sep 30, 2026
551fe80
fix(gold): drop a kyc answer that arrives after the modal closed
ebma Sep 30, 2026
8ab5ee4
fix(gold): explain kyc status and resubmit failures in portuguese
ebma Sep 30, 2026
dc23f07
fix(gold): correct the kyc rejection copy
ebma Sep 30, 2026
fcacb56
fix(gold): show the kyc outcome where the user is looking
ebma Sep 30, 2026
1beff5e
fix(gold): reject run cpfs and tie the cpf error to its field
ebma Sep 30, 2026
70200ae
fix(gold): wait for privy on the google return and explain a failed load
ebma Sep 30, 2026
59147ba
fix(gold): close the ramp key store when a write fails
ebma Sep 30, 2026
39e4226
Merge pull request #1391 from pendulum-chain/fix/api-squid-token-list…
ebma Oct 1, 2026
591d11f
Merge pull request #1392 from pendulum-chain/fix/gold-followups
ebma Oct 1, 2026
6323d4d
chore(repo): remove applied one-off root scripts
ebma Oct 1, 2026
8889fc0
chore(repo): drop unused @packages tsconfig path alias
ebma Oct 1, 2026
b3edbce
ci(repo): drop the unused node-version matrix
ebma Oct 1, 2026
71ec2a2
chore(repo): remove relayer test:local script for a missing file
ebma Oct 1, 2026
736d78c
chore(rebalancer): remove unused Pendulum GraphQL indexer stack
ebma Oct 1, 2026
44de0d6
chore(rebalancer): remove the legacy brla-to-axlusdc flow
ebma Oct 1, 2026
2ca52e7
chore(shared): remove squidrouter helpers only the legacy rebalancer …
ebma Oct 1, 2026
3be29ec
chore(shared): remove dead XCM and substrate event code
ebma Oct 1, 2026
f95da92
refactor(rebalancer): drop unused compareRates and daily swap limit g…
ebma Oct 1, 2026
a83793b
refactor(rebalancer): share Base balance reads and Nabla config acros…
ebma Oct 1, 2026
2271248
refactor(rebalancer): extract the Nabla approve-then-swap send
ebma Oct 1, 2026
16a51f7
refactor(rebalancer): unify the Base flow state managers
ebma Oct 1, 2026
fcea6e5
refactor(rebalancer): inline the constant-argument amount policy
ebma Oct 1, 2026
7ee2ced
refactor(rebalancer): build the cost policy decision from shared fields
ebma Oct 1, 2026
e58c575
refactor(rebalancer): inline single-use daily limit wrappers
ebma Oct 1, 2026
ee26aab
chore(sdk): remove unused dev scripts and the prettier format script
ebma Oct 1, 2026
ee2bb45
test(sdk): characterize the requests ApiService sends
ebma Oct 1, 2026
9e7b154
refactor(sdk): route ApiService calls through one request helper
ebma Oct 1, 2026
130c6a2
test(sdk): characterize the BRL, domestic and EUR handler flows
ebma Oct 1, 2026
df09514
refactor(sdk): share the handler register and update flows
ebma Oct 1, 2026
3febad8
refactor(shared): build evm token entries with one helper
ebma Oct 1, 2026
9522710
chore(sdk): drop the unused stellar-sdk peer dependency
ebma Oct 1, 2026
3f4cd37
chore(shared): remove unused dependencies and tooling
ebma Oct 1, 2026
01b32f9
chore(api): remove unmounted subsidize, fundEphemeral and execute-xcm…
ebma Oct 1, 2026
37df9f2
chore(api): remove retired legacy-schema one-off scripts
ebma Oct 1, 2026
5487441
chore(api): remove orphaned files and the missing phase-metadata seed…
ebma Oct 1, 2026
080c814
chore(api): remove dead Hydration swap code and its SDK dependencies
ebma Oct 1, 2026
42d81bf
refactor(api): share one UUID_PATTERN across validators
ebma Oct 1, 2026
b554440
refactor(api): share one phase-to-transaction-status mapper
ebma Oct 1, 2026
64e5a9e
refactor(api): drop never-called stop() methods from workers
ebma Oct 1, 2026
aa48057
refactor(api): remove unused MaintenanceService schedule CRUD methods
ebma Oct 1, 2026
ce98dac
refactor(api): reuse shared Big helpers instead of local copies
ebma Oct 1, 2026
2c78517
chore(api): remove unused constants and config fields
ebma Oct 1, 2026
4c3b978
refactor(api): remove unused BaseRampService updateRampState and isQu…
ebma Oct 1, 2026
107f56a
refactor(api): merge ExtendableError into APIError
ebma Oct 1, 2026
834c085
refactor(api): drop the dead abort controller in webhook delivery
ebma Oct 1, 2026
7754567
refactor(api): remove unused spreadsheet service object, getUserProfi…
ebma Oct 1, 2026
4012b08
refactor(api): use crypto.randomUUID instead of the uuid package
ebma Oct 1, 2026
f80231c
refactor(api): drop the dead express-validation error branch and depe…
ebma Oct 1, 2026
5633038
chore(api): drop unused dependencies
ebma Oct 1, 2026
bd0dfe4
refactor(api): share one sendError helper for the error envelope
ebma Oct 1, 2026
bffaa07
refactor(api): build adminAuth and metricsDashboardAuth from one factory
ebma Oct 1, 2026
74fb6a3
refactor(api): collapse competitor price adapters to one file each
ebma Oct 1, 2026
abdac4c
refactor(dashboard): remove unused shadcn component exports
ebma Oct 1, 2026
11ecdbe
refactor(dashboard): drop the client-mocked notifications feed
ebma Oct 1, 2026
f00fee5
refactor(shared): share decodeJwtExpiryMs between frontend and dashboard
ebma Oct 1, 2026
f28257d
refactor(dashboard): drop legacy impersonation and transfer-state sto…
ebma Oct 1, 2026
7fb7e6a
refactor(dashboard): share the alfredpay document drop zone
ebma Oct 1, 2026
b9b280f
refactor(dashboard): share one onboarding TextField
ebma Oct 1, 2026
c122b5d
refactor(dashboard): derive corridor mappings from @vortexfi/shared
ebma Oct 1, 2026
f712084
refactor(dashboard): drop the always-false corridor flags
ebma Oct 1, 2026
ee96a0c
chore(gold): remove unused gold bar images
ebma Oct 1, 2026
c95f48d
chore(demo): remove the unused barrel and bscNetwork export
ebma Oct 1, 2026
c7c9c65
refactor(api): remove unreferenced block core helpers and handler met…
ebma Oct 1, 2026
f556744
refactor(api): delete inert and unregistered post-process handlers
ebma Oct 1, 2026
5a94ec6
refactor(api): drop unused StateMetadata fields
ebma Oct 1, 2026
83af011
refactor(api): share the EVM top-up between pre- and post-swap subsidies
ebma Oct 1, 2026
d0b381c
chore(frontend): remove components and hooks nothing imports
ebma Oct 1, 2026
fbe79e9
chore(frontend): remove unreferenced hooks, helpers and constants
ebma Oct 1, 2026
15ebac8
refactor(frontend): drop unused API client classes and methods
ebma Oct 1, 2026
10b0ec2
refactor(frontend): remove never-emitted events and unused store members
ebma Oct 1, 2026
ac15b43
refactor(frontend): drop leftover Nabla/solang helpers and unused config
ebma Oct 1, 2026
28275a3
refactor(frontend): reduce useLocalStorage to what its callers use
ebma Oct 1, 2026
fc63943
refactor(frontend): share createQuotePayload with the url params hook
ebma Oct 1, 2026
39e1adf
refactor(frontend): derive the ramp component from the active screen
ebma Oct 1, 2026
dda4f3f
refactor(frontend): remove dead ramp machine branches and context fields
ebma Oct 1, 2026
7bf992c
chore(frontend): remove unused translation keys
ebma Oct 1, 2026
5668c7d
chore(frontend): remove unreferenced assets
ebma Oct 1, 2026
e73db1f
refactor(frontend): validate the rating address with viem and drop web3
ebma Oct 1, 2026
8467394
refactor(frontend): use heroicons in the select and drop lucide-react
ebma Oct 1, 2026
800fa13
chore(frontend): drop dependencies nothing imports
ebma Oct 1, 2026
30d2e1c
chore(frontend): drop the dead shared alias from the vite config
ebma Oct 1, 2026
5e50907
chore(frontend): drop transitive @polkadot declarations
ebma Oct 1, 2026
f7569a0
chore(shared): remove types and ABI export orphaned by deleted legacy…
ebma Oct 1, 2026
1aa30da
refactor(api): import multiplyByPowerOfTen from shared in phases
ebma Oct 1, 2026
fa8f379
chore(repo): drop stellar-sdk, root big.js and unused catalog entries
ebma Oct 1, 2026
5776460
docs(dashboard): drop a comment pointing at a deleted frontend helper
ebma Oct 1, 2026
20d6f88
fix(frontend): poll the maintenance status from the layout again
ebma Oct 2, 2026
54d2912
fix(frontend): refetch the maintenance status on every poll
ebma Oct 5, 2026
b05595c
fix(frontend): lift the maintenance banner above the widget backdrop
ebma Oct 5, 2026
2a51fbc
feat(frontend): show the maintenance banner only in the widget
ebma Oct 5, 2026
3c478c9
feat(dashboard): show active maintenance windows in a banner
ebma Oct 5, 2026
54a527a
feat(dashboard): pause transfer actions during maintenance
ebma Oct 5, 2026
4de375f
feat(dashboard): say quotes are paused when they fail during maintenance
ebma Oct 5, 2026
3ab8583
fix(dashboard): refetch the maintenance status just after a window ends
ebma Oct 5, 2026
8148f0e
fix(dashboard): stop offramp signing once a maintenance window opens
ebma Oct 5, 2026
fe2df7b
fix(dashboard): keep senders from paying into a ramp paused by mainte…
ebma Oct 5, 2026
92e94d4
test(frontend): give e2e mock ramps an expiresAt for the sell deadlin…
ebma Oct 5, 2026
3b2f129
Merge pull request #1399 from pendulum-chain/fix/e2e-mock-ramp-expire…
ebma Oct 5, 2026
375dcf1
test(api): count only bearer auth's own log lines in its characteriza…
ebma Oct 5, 2026
cd1b22e
docs(shared): drop the deleted endpoint types from the wire-contract …
ebma Oct 5, 2026
93eeee2
docs(api): point stale security-spec lines at the code that enforces …
ebma Oct 5, 2026
932541e
Merge pull request #1397 from pendulum-chain/chore/repo-cleanup
ebma Oct 5, 2026
e4b3bb9
Merge pull request #1396 from pendulum-chain/fix/frontend-maintenance…
ebma Oct 5, 2026
83f7c67
fix(frontend): clear the maintenance store in tests with setState
ebma Oct 5, 2026
c243b16
Merge pull request #1400 from pendulum-chain/fix/frontend-maintenance…
ebma Oct 5, 2026
8c8d2a7
Merge pull request #1398 from pendulum-chain/feat/dashboard-maintenan…
ebma Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
54 changes: 35 additions & 19 deletions .agents/skills/vortex-integration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ A machine-loadable capability catalog for AI coding agents integrating Vortex in
- `pk_live_*` / `pk_test_*` — public value, sent as `X-Public-Key` for attribution and approved low-sensitivity reads. Quote/widget body `apiKey` remains compatibility transport; if both are present they must match.
- `sk_live_*` / `sk_test_*` — secret value, sent only in `X-API-Key`. **Never expose `sk_*` in a browser or mobile app.** It is returned only when the credential is created.
- If both values are configured, they must belong to the same credential or Vortex returns `403 CREDENTIAL_MISMATCH`. A valid secret may be used without a public value.
- **Ramp registration requires an authenticated profile in every corridor.** The SDK accepts either a secret credential for its bound profile or an `accessTokenProvider` for that profile's renewable Supabase Bearer session; raw API clients may use the secret credential or that Supabase Bearer session directly. Provider identity (BRL tax ID, Alfredpay customer, or Monerium profile) is derived from the authenticated profile, never from request fields. Shared dummy/ownerless profiles are invalid.
- **Ramp registration requires an authenticated profile in every corridor.** The SDK accepts either a secret credential for its bound profile or an `accessTokenProvider` for that profile's renewable Supabase Bearer session; raw API clients may use the secret credential or that Supabase Bearer session directly. Provider identity (BRL tax ID, bank-transfer customer, or EUR provider profile) is derived from the authenticated profile, never from request fields. Shared dummy/ownerless profiles are invalid.
- Profile-managed credentials use `POST/GET/DELETE /v1/api-credentials` with a Supabase Bearer session. One profile may have at most five active non-expired credentials; revoke by credential ID disables both values atomically with no DELETE body.
- **Decimals**: all amounts are strings. Never parse them through JS `Number` — use `BigInt`, `decimal.js`, or equivalent.
- **Quote TTL**: quotes expire (see `expiresAt`). Re-quote, never reuse stale quotes.
- **Presigned counts**: this is **per ephemeral-signed transaction, not per ramp**. Each transaction an ephemeral key signs must be submitted as 5 presigned variants — 1 primary plus exactly 4 backups with consecutive nonces in `meta.additionalTxs` (`NUMBER_OF_PRESIGNED_TXS = 5`); the API rejects any other backup count. A ramp can contain several ephemeral-signed transactions across its phases. (The SDK builds these for you; only raw-API integrations need to construct them.)
- **Currently implemented SDK corridors**: BRL via PIX, USD via ACH, MXN via SPEI, COP via ACH, and ARS via CBU support BUY and SELL; EUR via SEPA (Monerium) supports BUY only. EUR BUY needs `walletAddress` (the user's Monerium-linked wallet, linked in the Dashboard or Widget) and the returned owner permit signed through `submitUserTransactions`. Supply `customerType` to select the same individual or business Monerium profile used at onboarding; it is required when both types are bound (`MONERIUM_CUSTOMER_TYPE_REQUIRED` otherwise). `MONERIUM_ONBOARDING_REQUIRED` / `MONERIUM_REAUTHENTICATION_REQUIRED` mean the user must (re)connect Monerium first. These corridors deliver to EVM networks only (no AssetHub).
- **Currently implemented SDK corridors**: BRL via PIX, USD via ACH, MXN via SPEI, COP via ACH, and ARS via CBU support BUY and SELL; EUR via SEPA supports BUY only, is available in sandbox with production activation pending, and needs the next `@vortexfi/sdk` release (0.9.0 has no EUR support; use the direct API until then). EUR BUY needs `walletAddress` (the wallet linked with the EUR provider in the Dashboard or Widget) and the returned owner permit signed through `submitUserTransactions`. Supply `customerType` to select the same individual or business EUR provider profile used at onboarding; it is required when both types are bound (`MONERIUM_CUSTOMER_TYPE_REQUIRED` otherwise). `MONERIUM_ONBOARDING_REQUIRED` / `MONERIUM_REAUTHENTICATION_REQUIRED` mean the user must (re)connect the EUR provider first. These corridors deliver to EVM networks only (no AssetHub).
- **EUR currency value**: TypeScript uses the member `FiatToken.EURC`, which serializes to the wire value `"EUR"`. Raw JSON clients must send `"EUR"`, with `"sepa"` as the rail identifier.
- **taxId is deprecated for BRL**: the user's tax ID is derived server-side from the authenticated profile. Sending a `taxId` that mismatches the derived one is rejected; stop sending it in new integrations.
- **Deferred offramp funding**: the SDK checks the source wallet balance at `registerRamp` by default. Server integrations that register before funding a temporary wallet may configure `offrampFundingMode: "deferred"`. This skips only the SDK pre-flight; fund the exact `walletAddress` before signing/submitting user transactions, then update and start before the registration window expires. Backend execution-time balance checks remain authoritative.
Expand Down Expand Up @@ -267,22 +267,23 @@ triggers:
## When to use
The user wants to buy crypto with EUR and is already corridor-ready: an approved Vortex EUR provider binding, a live approved provider profile, exactly one existing Polygon EOA/IBAN destination, and access to that EOA for typed-data signing. Both individual and business legal entities may qualify. The active route delivers to supported EVM destinations, Polygon included.

Users become corridor-ready by completing Monerium OAuth onboarding in the Dashboard or Widget and linking the wallet they will pay in with (`POST /v1/monerium/wallet`); this flow does not cover onboarding, wallet linking, or IBAN provisioning. EUR SELL is unavailable.
EUR BUY is available in sandbox; production activation is pending. Users become corridor-ready by completing the EUR provider's OAuth onboarding in the Dashboard or Widget and linking the wallet they will pay in with (`POST /v1/monerium/wallet`); this flow does not cover onboarding, wallet linking, or IBAN provisioning. EUR SELL is unavailable.

## Prerequisites
- Quote with TypeScript member `inputCurrency: FiatToken.EURC` (raw JSON value `"EUR"`), `from: "sepa"`, and a supported EVM destination.
- A secret credential or Supabase session for the corridor-ready legal entity.
- `additionalData.destinationAddress`; do not submit profile, Monerium address, or IBAN identity.
- `additionalData.destinationAddress`; do not submit profile, provider address, or IBAN identity.
- `additionalData.customerType` (`"individual"` or `"business"`) when the user owns both legal profiles; use the same type as onboarding and wallet linking.
- A fresh EVM ephemeral key and a wallet-signing channel for the profile-linked Polygon owner.

## SDK recipe
Requires the next `@vortexfi/sdk` release; 0.9.0 has no EUR support.
```js
// walletAddress must be the wallet linked to the Monerium profile; a mismatch throws EurOnrampError.
// walletAddress must be the wallet linked to the EUR provider profile; a mismatch throws EurOnrampError.
const { rampProcess, unsignedTransactions } = await vortex.registerRamp(quote, {
customerType: "individual",
destinationAddress: "0xDestinationWallet",
walletAddress: "0xMoneriumLinkedWallet"
walletAddress: "0xProviderLinkedWallet"
});

// unsignedTransactions holds the owner's EIP-712 permit; the ephemeral txs are signed by the SDK.
Expand Down Expand Up @@ -313,7 +314,7 @@ The permit expires one week after preparation. If SEPA settlement arrives after
consumes its nonce, automatic execution stops for manual resolution.

## Common failures
- `400` approved-profile error: the effective legal entity has no approved local Monerium/EUR binding or the live provider profile is not approved.
- `400` approved-profile error: the effective legal entity has no approved local EUR provider binding or the live provider profile is not approved.
- `409 MONERIUM_CUSTOMER_TYPE_REQUIRED`: both legal types are bound; repeat registration with the type used for wallet linking.
- `409` expected-one-destination error: the profile does not have exactly one matching Polygon EOA/IBAN destination. Vortex does not create, select, or move one in this release.
- Contract-wallet error: the linked mint destination must be an EOA for the ERC-2612 handoff.
Expand Down Expand Up @@ -351,7 +352,7 @@ The user wants to ramp USD, MXN, COP, or ARS over their domestic banking rail. R
## Prerequisites
- The user completed KYC for the corridor's country via the Vortex app or Widget, and the SDK is authenticated with that user's own `sk_*` key or Supabase session.
- Buy: `destinationAddress` (required); `fiatAccountId`, `walletAddress` optional.
- Sell: `fiatAccountId` and `walletAddress` (both required). List saved accounts with `vortex.listAlfredpayFiatAccounts(country)`.
- Sell: `fiatAccountId` and `walletAddress` (both required). Verification does not create a pay-out account: the user adds one after verification (Dashboard **Add pay-out account**, the Widget, or `POST /v1/domestic/fiatAccounts`). List saved accounts with `vortex.listDomesticFiatAccounts(country)`.

## SDK recipe (onramp, MXN shown — substitute fiat + rail for USD/COP/ARS)
```js
Expand All @@ -369,20 +370,35 @@ const { rampProcess } = await vortex.registerRamp(quote, {
destinationAddress: "0xUserWalletAddress"
});

const started = await vortex.startRamp(rampProcess.id);
// Bank transfer instructions the user must pay arrive with the registered ramp
// (registerRamp performs the update that releases them); startRamp does not repeat them.
console.log(rampProcess.achPaymentData);

// Bank transfer instructions the user must pay are on the START response:
console.log(started.achPaymentData);
// After the user initiates the transfer, start before the ramp's expiresAt.
const started = await vortex.startRamp(rampProcess.id);
```

No user-signed on-chain transactions on buys. Unlike BRL there is no QR code — display the `achPaymentData` deposit instructions verbatim; the ramp continues automatically once the fiat deposit is confirmed.

## SDK recipe (offramp)
```js
const accounts = await vortex.listAlfredpayFiatAccounts("MEX");
const sellQuote = await vortex.createQuote({
rampType: RampDirection.SELL,
from: Networks.Polygon,
to: EPaymentMethod.SPEI,
network: Networks.Polygon,
inputAmount: "10",
inputCurrency: EvmToken.USDC,
outputCurrency: FiatToken.MXN
});

const { rampProcess, unsignedTransactions } = await vortex.registerRamp(quote, {
fiatAccountId: accounts[0].id,
const accounts = await vortex.listDomesticFiatAccounts(DomesticCountry.MX); // DomesticCountry from @vortexfi/sdk
if (accounts.length === 0) {
throw new Error("Add a pay-out account for MX before selling");
}

const { rampProcess, unsignedTransactions } = await vortex.registerRamp(sellQuote, {
fiatAccountId: accounts[0].fiatAccountId,
walletAddress: "0xUserWalletAddress"
});

Expand All @@ -393,11 +409,11 @@ await vortex.submitUserTransactions(rampProcess.id, unsignedTransactions, {
await vortex.startRamp(rampProcess.id);
```

The SDK cannot **create** fiat accounts; they are created during onboarding in the Vortex app or Widget. `fiatAccountId` is opaque to the SDK.
The SDK cannot **create** fiat accounts, and verification does not create one either: the user adds it in the Dashboard (**Add pay-out account**) or Widget, or a server calls `POST /v1/domestic/fiatAccounts`. `fiatAccountId` is opaque to the SDK.

## Common failures
- `MissingAlfredpayOnrampParametersError` / `MissingAlfredpayOfframpParametersError` — `destinationAddress`, `fiatAccountId`, or `walletAddress` missing.
- `AlfredpayOnrampKycRequiredError` — the authenticated user has no approved KYC for the corridor's country.
- `MissingDomesticOnrampParametersError` / `MissingDomesticOfframpParametersError` — `destinationAddress`, `fiatAccountId`, or `walletAddress` missing.
- `DomesticOnrampKycRequiredError` — the authenticated user has no approved KYC for the corridor's country.
- `400` "requires an API key linked to a user" on register — the supplied API credential or Bearer session is not bound to an eligible profile. Authenticate as the onboarded user or provision a managed profile and issue a credential for that explicit subject.
- `InsufficientBalanceError` — in the default `"prefunded"` mode, the offramp pre-flight found the source wallet balance below the quote's input amount. A deliberate register-then-fund integration may use `offrampFundingMode: "deferred"`; it must fund before submitting user transactions and starting the ramp.

Expand Down Expand Up @@ -695,7 +711,7 @@ try {

## Current corridor reality (August 2026)
- **BRL via PIX**: onramp and offramp both live. `taxId` deprecated — derived from the user-linked key.
- **EUR via SEPA**: BUY is active (`FiatToken.EURC`, rail `"sepa"`) for an approved Monerium user with one Polygon EOA/IBAN destination, through the SDK (`walletAddress` + `submitUserTransactions` for the owner permit), the Widget, the Dashboard, and the direct API. Onboarding and wallet linking happen in the Dashboard or Widget. Destinations: any supported EVM network. SELL is unavailable.
- **EUR via SEPA**: BUY is available in sandbox, production activation pending (`FiatToken.EURC`, rail `"sepa"`), for an approved EUR provider user with one Polygon EOA/IBAN destination, through the SDK from its next release (`walletAddress` + `submitUserTransactions` for the owner permit), the Widget, the Dashboard, and the direct API. Onboarding and wallet linking happen in the Dashboard or Widget. Destinations: any supported EVM network. SELL is unavailable.
- **USD (ACH) / MXN (SPEI) / COP (ACH) / ARS (CBU)**: onramp and offramp live via the AlfredPay corridor; registration requires an authenticated user identity. Route resolver determines availability per-combination.
- Live corridors deliver to EVM networks; AssetHub ramp execution is currently disabled.

Expand Down Expand Up @@ -745,7 +761,7 @@ Include this payload (with secrets redacted) in any support ticket.
| `InvalidNetworkError` | Network not in `Networks` enum | Use `discover-supported-corridors` |
| `MissingRequiredFieldsError` / `MissingBrlParametersError` / `MissingBrlOfframpParametersError` | Body field missing | Fill the missing field; do not retry blindly |
| `SubaccountNotFoundError` / `KycInvalidError` | BRL KYC issue | Direct user through KYC; do not retry programmatically |
| `AlfredpayOnrampKycRequiredError` | Bank-transfer-corridor KYC issue | Onboard or provision the credential's bound profile; do not retry programmatically |
| `DomesticOnrampKycRequiredError` | Bank-transfer-corridor KYC issue | Onboard or provision the credential's bound profile; do not retry programmatically |
| Raw EUR registration `400` / `409` | Missing approved binding/profile or not exactly one Polygon EOA/IBAN match | Provision or reconcile the user out of band; do not submit caller-selected provider identity |
| `VortexSdkError` with `code === "CREDENTIAL_MISMATCH"` | Configured public and secret values belong to different credentials | Load both values from the same credential; never infer pairing by name |
| `VortexSdkError` with `code === "provider_limit_exceeded"` | The provider account limit is exhausted | Stop retrying registration; wait for provider capacity to reset or contact Vortex support |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ jobs:
build:
name: Running ci
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18 ]
env:
CI: true

Expand Down Expand Up @@ -103,3 +100,6 @@ jobs:

- name: 🧪 Frontend tests (coverage-gated)
run: cd apps/frontend && bun run test:coverage

- name: 🧪 Gold tests
run: bun run test:gold
12 changes: 0 additions & 12 deletions apps/api/.dockerignore

This file was deleted.

3 changes: 0 additions & 3 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ DB_SSL_REQUIRED=false
DB_SSL_CA_CERT_PATH=

# Blockchain
AMPLITUDE_WSS=wss://rpc-amplitude.pendulumchain.tech
PENDULUM_WSS=wss://rpc-pendulum.prd.pendulumchain.tech
# Optional Substrate RPC overrides. Comma-separate multiple URLs for failover.
ASSETHUB_WSS=wss://dot-rpc.stakeworld.io/assethub
HYDRATION_WSS=wss://rpc.hydradx.cloud
Expand Down Expand Up @@ -194,7 +192,6 @@ BRLA_API_KEY=your-brla-api-key
BRLA_PRIVATE_KEY=your-brla-private-key

# Integration test helpers (only required for phase-processor integration tests)
# BACKEND_TEST_STARTER_ACCOUNT=
# TAX_ID=

# External API contract tests (RUN_LIVE_TESTS=1, see docs/operations-testing.md).
Expand Down
5 changes: 2 additions & 3 deletions apps/api/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ architecture and commands. Run commands from `apps/api/` unless noted.

### Ramp state machine

The ramping process runs through defined phases; metadata and valid transitions live in
PostgreSQL, seeded via `bun seed:phase-metadata`.
The ramping process runs through defined phases; the phase flows and their valid transitions
live in code under `src/api/services/phases/`.

- **Offramp**: prepareTransactions → squidRouter → pendulumFundEphemeral → subsidizePreSwap → nablaApprove → nablaSwap → subsidizePostSwap → performBrlaPayout → pendulumCleanup
- **Onramp**: brlaTeleport → createMoonbeamEphemeral → executeMoonbeamToPendulumXCM → subsidizePreSwap → nablaApprove → nablaSwap → executePendulumToAssetHubXCM → pendulumCleanup
Expand All @@ -31,7 +31,6 @@ bun test phase-processor.integration.test.ts --timeout X # integration test
bun migrate # run migrations
bun migrate:revert # revert ALL migrations (destructive — dev only)
bun migrate:revert-last # revert last migration
bun seed:phase-metadata # seed phase configuration
```

Lint with the repo Biome config: from repo root `bun lint:fix`, or target a path with
Expand Down
18 changes: 0 additions & 18 deletions apps/api/Dockerfile

This file was deleted.

1 change: 0 additions & 1 deletion apps/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Run from `apps/api/`:
```bash
bun migrate
bun migrate:revert-last
bun seed:phase-metadata
```

Do not run bulk migration reverts against shared or production databases. The production
Expand Down
Loading
Loading