Skip to content

feat(ramps): Add TransakService for native deposit flow integration#7922

Open
georgeweiler wants to merge 16 commits intomainfrom
transak-service-spike
Open

feat(ramps): Add TransakService for native deposit flow integration#7922
georgeweiler wants to merge 16 commits intomainfrom
transak-service-spike

Conversation

@georgeweiler
Copy link
Contributor

@georgeweiler georgeweiler commented Feb 12, 2026

Explanation

Introduces a new TransakService class that provides a direct integration with the Transak API for native buy/deposit flows within the ramps-controller package. This enables a "V2" native flow where MetaMask communicates directly with Transak's APIs for authentication, KYC, quoting, and order management -- rather than routing everything through the aggregator widget.

What changed

New files:

  • TransakService.ts -- API client wrapping Transak's REST endpoints. Handles authentication (OTP login/verify), buy quotes, KYC requirements, order creation/cancellation, user management, payment confirmation, and a translation layer that maps generic ramps identifiers to Transak-specific ones. Uses the messenger pattern with registerMethodActionHandlers to expose 23 methods as callable actions.
  • TransakService-method-action-types.ts -- TypeScript action type definitions for each exposed messenger method.
  • TransakService.test.ts -- Comprehensive test suite using nock for HTTP mocking. Covers all public methods, error paths, authentication guards, environment switching (staging/production), order ID transformations, and retry logic.

Modified files:

  • RampsController.ts -- Adds nativeProviders.transak state (authentication status, user details, buy quote, KYC requirement) with loading/error tracking via ResourceState. Adds ~25 transak* methods that delegate to the TransakService via messenger calls, with stateful methods (transakGetUserDetails, transakGetBuyQuote, transakGetKycRequirement) managing loading and error state. Changes startQuotePolling to return early instead of throwing when no provider is selected.
  • RampsController.test.ts -- Adds tests for all new transak methods on the controller, including state management, error handling, and fallback error messages. Updates existing snapshot assertions to include the new nativeProviders state shape.
  • RampsService.ts -- Minor change (debug logging).
  • index.ts -- Exports all new types, classes (TransakService, TransakEnvironment, TransakOrderIdTransformer), and action types from the package.

Key design decisions

  • Messenger-based architecture: TransakService registers its methods as messenger actions, and RampsController calls them via this.messenger.call(...). This keeps the service decoupled from the controller and allows other consumers to call TransakService directly.
  • Translation layer: A getTranslation endpoint maps generic ramps identifiers (chain IDs, asset IDs, payment method paths) to Transak-native identifiers before calling Transak APIs. This keeps the controller's interface provider-agnostic.
  • Order ID transformation: TransakOrderIdTransformer converts between Transak's raw order IDs and the deposit-format IDs used by the ramps orders API (/providers/transak-native/orders/{id}).
  • Retry logic for order creation: If order creation fails with an "Order exists" error (code 4005), the service cancels all active orders and retries once.
  • State shape: Transak state is nested under nativeProviders.transak to support future native provider integrations under the same pattern. State is not persisted (persist: false) since it contains session-specific data.

Test plan

  • TransakService.test.ts -- all new service-level tests pass (auth flows, API calls, error handling, order ID transforms)
  • RampsController.test.ts -- all new controller-level tests pass (messenger delegation, state updates, error state management)
  • Existing RampsController tests continue to pass with updated snapshots
  • Verify staging and production environment URL routing works correctly

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Adds a new external-API integration (Transak) with authentication, quoting, KYC, and order lifecycle plus new controller state; mistakes could break deposit flows or leak error handling assumptions, but changes are isolated to the ramps controller/service layer with extensive tests.

Overview
Enables a native Transak deposit (buy) flow in @metamask/ramps-controller by introducing a new TransakService (OTP auth, KYC checks, quoting, order create/fetch/cancel, limits, OTT + payment widget URL generation, and identifier translation via the ramps orders API).

RampsController is extended with a new non-persisted nativeProviders.transak state slice (auth flag + userDetails/buyQuote/kycRequirement resource states) and a set of transak* convenience methods that delegate to TransakService and manage loading/error state where applicable. Quote polling behavior is tightened: startQuotePolling now throws when no payment method is selected (instead of returning early), and widget URL fetching no longer logs on failure.

Exports are updated to surface the new service/types/action types, PaymentMethod gains isManualBankTransfer?, and tests/snapshots are expanded substantially (including a comprehensive TransakService nock-based suite and controller coverage for the new Transak methods).

Written by Cursor Bugbot for commit 48b74cb. This will update automatically on new commits. Configure here.

@georgeweiler georgeweiler requested a review from a team as a code owner February 12, 2026 19:46
@georgeweiler georgeweiler marked this pull request as draft February 12, 2026 19:46
@georgeweiler georgeweiler changed the title feat: create transak service and add deposit state to ramps controller feat(ramps): Add TransakService for native deposit flow integration Feb 14, 2026
@georgeweiler georgeweiler marked this pull request as ready for review February 14, 2026 14:33
@georgeweiler
Copy link
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-eb9a46682",
  "@metamask-previews/accounts-controller": "36.0.0-preview-eb9a46682",
  "@metamask-previews/address-book-controller": "7.0.1-preview-eb9a46682",
  "@metamask-previews/ai-controllers": "0.0.0-preview-eb9a46682",
  "@metamask-previews/analytics-controller": "1.0.0-preview-eb9a46682",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-eb9a46682",
  "@metamask-previews/announcement-controller": "8.0.0-preview-eb9a46682",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-eb9a46682",
  "@metamask-previews/approval-controller": "8.0.0-preview-eb9a46682",
  "@metamask-previews/assets-controller": "1.0.0-preview-eb9a46682",
  "@metamask-previews/assets-controllers": "99.3.2-preview-eb9a46682",
  "@metamask-previews/base-controller": "9.0.0-preview-eb9a46682",
  "@metamask-previews/bridge-controller": "66.1.1-preview-eb9a46682",
  "@metamask-previews/bridge-status-controller": "66.0.2-preview-eb9a46682",
  "@metamask-previews/build-utils": "3.0.4-preview-eb9a46682",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-eb9a46682",
  "@metamask-previews/claims-controller": "0.4.2-preview-eb9a46682",
  "@metamask-previews/composable-controller": "12.0.0-preview-eb9a46682",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-eb9a46682",
  "@metamask-previews/controller-utils": "11.18.0-preview-eb9a46682",
  "@metamask-previews/core-backend": "5.1.1-preview-eb9a46682",
  "@metamask-previews/delegation-controller": "2.0.1-preview-eb9a46682",
  "@metamask-previews/earn-controller": "11.1.0-preview-eb9a46682",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-eb9a46682",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-eb9a46682",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-eb9a46682",
  "@metamask-previews/ens-controller": "19.0.2-preview-eb9a46682",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-eb9a46682",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-eb9a46682",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-eb9a46682",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-eb9a46682",
  "@metamask-previews/foundryup": "1.0.1-preview-eb9a46682",
  "@metamask-previews/gas-fee-controller": "26.0.2-preview-eb9a46682",
  "@metamask-previews/gator-permissions-controller": "1.1.2-preview-eb9a46682",
  "@metamask-previews/json-rpc-engine": "10.2.2-preview-eb9a46682",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-eb9a46682",
  "@metamask-previews/keyring-controller": "25.1.0-preview-eb9a46682",
  "@metamask-previews/logging-controller": "7.0.1-preview-eb9a46682",
  "@metamask-previews/message-manager": "14.1.0-preview-eb9a46682",
  "@metamask-previews/messenger": "0.3.0-preview-eb9a46682",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-eb9a46682",
  "@metamask-previews/multichain-api-middleware": "1.2.6-preview-eb9a46682",
  "@metamask-previews/multichain-network-controller": "3.0.3-preview-eb9a46682",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-eb9a46682",
  "@metamask-previews/name-controller": "9.0.0-preview-eb9a46682",
  "@metamask-previews/network-controller": "29.0.0-preview-eb9a46682",
  "@metamask-previews/network-enablement-controller": "4.1.0-preview-eb9a46682",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-eb9a46682",
  "@metamask-previews/permission-controller": "12.2.0-preview-eb9a46682",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-eb9a46682",
  "@metamask-previews/perps-controller": "0.0.0-preview-eb9a46682",
  "@metamask-previews/phishing-controller": "16.2.0-preview-eb9a46682",
  "@metamask-previews/polling-controller": "16.0.2-preview-eb9a46682",
  "@metamask-previews/preferences-controller": "22.1.0-preview-eb9a46682",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-eb9a46682",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-eb9a46682",
  "@metamask-previews/ramps-controller": "8.0.0-preview-eb9a46682",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-eb9a46682",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-eb9a46682",
  "@metamask-previews/sample-controllers": "4.0.2-preview-eb9a46682",
  "@metamask-previews/seedless-onboarding-controller": "8.0.0-preview-eb9a46682",
  "@metamask-previews/selected-network-controller": "26.0.2-preview-eb9a46682",
  "@metamask-previews/shield-controller": "5.0.1-preview-eb9a46682",
  "@metamask-previews/signature-controller": "39.0.2-preview-eb9a46682",
  "@metamask-previews/storage-service": "1.0.0-preview-eb9a46682",
  "@metamask-previews/subscription-controller": "6.0.0-preview-eb9a46682",
  "@metamask-previews/transaction-controller": "62.17.0-preview-eb9a46682",
  "@metamask-previews/transaction-pay-controller": "15.0.0-preview-eb9a46682",
  "@metamask-previews/user-operation-controller": "41.0.2-preview-eb9a46682"
}

@georgeweiler georgeweiler requested a review from a team as a code owner February 14, 2026 15:46
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

this.transakSetAuthenticated(false);
this.update((state) => {
state.nativeProviders.transak.userDetails.data = null;
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logout leaves token and state inconsistent

Medium Severity

transakLogout() always sets nativeProviders.transak.isAuthenticated to false in finally, but it never calls TransakService:clearAccessToken when TransakService:logout fails. This can leave a valid service token while controller state says logged out, causing auth state drift and inconsistent behavior across subsequent Transak calls.

Fix in Cursor Fix in Web

this.update((state) => {
state.nativeProviders.transak =
getDefaultRampsControllerState().nativeProviders.transak;
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reset does not clear service token

Medium Severity

transakResetState() only resets state.nativeProviders.transak and never calls TransakService:clearAccessToken. After reset, isAuthenticated becomes false while TransakService can still hold a valid token, so authenticated Transak requests may continue despite a reset state.

Fix in Cursor Fix in Web

} catch (error) {
if (error instanceof HttpError && error.httpStatus === 409) {
await this.cancelAllActiveOrders();
await new Promise((resolve) =>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order retry triggers on all conflicts

Medium Severity

createOrder() retries after cancelling active orders for any HttpError with status 409. The logic does not verify the Transak error code (for example 4005 “Order exists”), so unrelated conflict responses can still cancel active orders and trigger an unintended retry path.

Fix in Cursor Fix in Web

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.

1 participant