# Gas Drop On Destination
Source: https://docs.mayan.finance/application/gas-on-destination
Delivering native gas tokens on the destination chain to enable immediate post-swap transactions.
The "Gas drop on destination" feature automatically provides native gas tokens along with the output assets on the destination blockchain, enabling users to immediately perform transactions after a cross-chain swap without needing to fund gas separately.
If the destination wallet already holds sufficient native gas tokens, this feature will not be triggered by default.
Users can adjust the amount of gas they receive within predefined limits specific to each supported blockchain:
### Gas drop on destination default and max values:
| Destination Chain | Token | Default Gas | Max Gas |
| ----------------- | :---- | ----------- | ------- |
| Solana | SOL | 0.01 | 0.1 |
| Ethereum | ETH | 0.01 | 0.05 |
| BSC | BNB | 0.001 | 0.02 |
| Polygon | MATIC | 0.1 | 0.2 |
| Avalanche | AVAX | 0.01 | 0.1 |
| Arbitrum | ETH | 0.002 | 0.01 |
# Mayan Explorer
Source: https://docs.mayan.finance/application/mayan-explorer
Real-time tracking and advanced search for complete transparency on Mayan transactions.
Mayan’s dedicated explorer provides full transparency and real-time visibility into all aspects of cross-chain swaps. Users can monitor protocol statistics, track transfer volumes, and follow the progress of individual transactions step-by-step.
The explorer includes a powerful search function where users can input transaction IDs or wallet addresses to view detailed histories and track specific transactions in real time. This makes it an essential tool for both users and developers seeking to audit swap activity or verify transaction statuses.
Visit the [**Mayan Explorer**](https://explorer.mayan.finance/) to gain complete oversight of your cross-chain swaps and the overall health and performance of the Mayan protocol.
# Relayer Fees
Source: https://docs.mayan.finance/application/relayer-fees
Learn how relayer fees work and how transactions are processed by Mayan.
The relayer fee is paid to relayers who cover the gas costs and execute transactions on behalf of users during a cross-chain swap.
**Transaction Process**
Performing a cross-chain swap involves three transactions:
1. **Initial Swap Instruction:**\
The user initiates a transaction to send tokens, along with the necessary swap details, using the [**Swap Bridge**](https://www.perplexity.ai/architecture/wh-swap). The user pays the transaction fee in the native token of the source chain.
2. **Swap Registration:**\
Relayers receive a signed message from Wormhole guardians and register the swap for auction on Solana. This transaction is carried out by the relayers on behalf of the user.
3. **Redeem Transaction:**\
Relayers complete the process by sending the output tokens to the user’s wallet, finalizing the swap.
Users pay relayer fees so that relayers can perform the registration and redeem transactions on their behalf. Without paying the relayer fee, these transactions need to be executed manually by the user.
# User Manual
Source: https://docs.mayan.finance/application/user-manual
# How to Swap Tokens with Mayan
Swapping tokens on Mayan is designed to be as simple and seamless as possible. Whether performing a cross-chain swap or a same-chain swap, the process is straightforward:
1. Choose your **input token** and the blockchain it’s on.
2. Select your **destination chain** and the desired **output token**.
3. Mayan handles everything behind the scenes — from finding the best route and managing transactions to delivering the exact amount of tokens quoted to your wallet.
No complex steps or manual operations are required. Just select your tokens and chains, confirm the quoted amount, and let Mayan do the rest.
## Wallet Support
To use Mayan, all that’s needed is any major wallet. Mayan supports all leading wallets to ensure a smooth, seamless swapping experience across chains.
## Example: Cross-Chain Swap from Solana to Base
* Connect your preferred wallet
* Select Solana as the source chain and your input token
* Select Base as the destination chain and your output token
* Confirm and execute the swap
Your tokens will arrive on Base once the swap completes, with the exact amount quoted at the start.
Please pay attention to the price impact percentage, it shows how much your swap affects the price of tokens on that market, so if it's high, it means you are not getting an ideal deal.
# Auction
Source: https://docs.mayan.finance/architecture/auction
Discover how the Mayan onchain auction delivers the best swap rates for every user.
**What it is:**\
A transparent, on-chain auction on Solana where drivers compete to offer users the most favorable swap price for cross-chain transfers.
**How it works:**
* Each swap request triggers a 3-second English auction.
* Drivers submit bids focused on offering the best rate, not the fastest response.
* The best price wins, and the winner executes the swap at their quoted rate.
**Why it matters:**\
Unlike typical limit order models—which lead to gas wars and MEV extraction—Mayan’s auction system is designed to maximize value for users through true price competition.
**Get involved:**\
To become a solver (“driver”), open a support ticket in the Mayan Discord and request access. Applications are evaluated on a case-by-case basis.
**Design note:**\
Mayan refers to solvers as “drivers,” it's a nod to the Mayan team’s roots in building ride-sharing routing algorithms.
# MCTP
Source: https://docs.mayan.finance/architecture/mctp
A seamless way to move tokens cross-chain using native USDC powered by Circle CCTP.
**What it is:**\
The MCTP (Mayan-Circle Transfer Protocol) method converts input tokens to USDC and sends them to the destination chain via Circle’s CCTP. Drivers then compete in an on-chain auction on Solana to deliver the best rate for swapping USDC into the user’s requested output token.
**How it works:**
* Input tokens are swapped to USDC on the source chain, then forwarded using Circle CCTP.
* An auction is held on Solana. The winning driver converts USDC on the destination chain to the requested output token.
* Output tokens are delivered directly to the user’s wallet.
* The protocol fee is zero if the output token is USDC and 3 basis points for other tokens.
### MCTP Contract Addresses
| Network | Wormhole Chain ID | Contract Address |
| --------- | ----------------- | ---------------------------------------------------------------------------------- |
| Solana | 1 | `dkpZqrxHFrhziEMQ931GLtfy11nFkCsfMftH9u6QwBU` |
| Ethereum | 2 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| BSC | 4 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| Polygon | 5 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| Avalanche | 6 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| Arbitrum | 23 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| Optimism | 24 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| Base | 30 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| Unichain | 44 | `0x875d6d37EC55c8cF220B9E5080717549d8Aa8EcA` |
| Sui | 21 | `0xb787fe0f7530b4fd2162fa0cc92f4f6c5a97c54b4c5c55eb04ab29f4b803ac9c`(shared state) |
**Fast MCTP:**\
A variant using Circle CCTPv2 for faster settlement and more efficient messaging. Flow and auction structure are identical; finality is faster and latency reduced. Protocol fee is 3 basis points.
| Network | Wormhole Chain Id | Contract Address |
| --------- | ----------------- | ---------------------------------------------- |
| Solana | 1 | `Gx9rivpS3YR8pBFwMuP6omYqVxunpLvLkNn7ubNyuZZ5` |
| Ethereum | 2 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Polygon | 5 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Avalanche | 6 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Arbitrum | 23 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Optimism | 24 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Base | 30 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Linea | 38 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Unichain | 44 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| HyperEVM | 47 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
| Monad | 48 | `0xC1062b7C5Dc8E4b1Df9F200fe360cDc0eD6e7741` |
# Relayers
Source: https://docs.mayan.finance/architecture/relayers
How relayers keep Mayan transactions moving securely and efficiently across chains.
**What it is:**\
Relayers are independent entities that deliver authenticated messages between blockchains on Mayan, playing a key role in cross-chain swaps.
**How it works:**
* Relayers monitor supported blockchains for Mayan transactions.
* When a swap is detected, they retrieve Verifiable Action Approvals (VAAs) from Wormhole guardians.
* Using the VAA, relayers commit the message on Solana or another target network, completing the transaction.
* Relayers earn a fee for covering network costs and acting on behalf of users.
**Why it matters:**\
Relayers never access or control users’ funds. The Solana program is trustless, enabling anyone to run their own relayer and participate in message transfer—no permissions needed. This ensures swaps remain secure and decentralized for all users.
# Swift
Source: https://docs.mayan.finance/architecture/swift
Lightning fast, intent-based bridging and cross-chain swaps with Mayan.
Swift v1 will not be supported in the long term. New integrations should use [Swift v2](/architecture/swift-v2), and existing integrations are strongly encouraged to migrate.
**What it is:**\
Swift is Mayan’s intent-based protocol designed for rapid bridging and swaps. Users simply specify their input token and source chain, along with their desired output token and destination chain. All underlying complexities such as order creation and asset locking are fully handled by the protocol and hidden from users and integrators.
**How it works:**
* When a cross-chain swap is initiated, an auction is triggered onchain.
* Drivers (solvers) bid competitively in this auction.
* The highest bidder fulfills the order on the destination chain exactly as specified by the user, using liquidity (inventory) they already hold on that chain. This results in near-instant settlement with guaranteed output amount.
* Upon fulfillment, the winning driver receives a receipt that enables the secure unlocking and transfer of assets on the source chain.
* The entire process from order initiation to final settlement typically completes within seconds.
**User experience:**\
Swift abstracts all technical steps—and there is no need for users or integrators to manually interact with contracts or manage asset locking. Mayan automatically orchestrates the flow to ensure a smooth experience.
**Protocol fee:**\
Swift charges a fee of 3 basis points (0.03%).
### Swift Contract Addresses
| Network | Wormhole Chain ID | Contract Address |
| --------- | ----------------- | ---------------------------------------------- |
| Solana | 1 | `BLZRi6frs4X4DNLw56V4EXai1b6QVESN1BhHBTYM9VcY` |
| Ethereum | 2 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| BSC | 4 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Polygon | 5 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Avalanche | 6 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Arbitrum | 23 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Optimism | 24 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Base | 30 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Linea | 38 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Unichain | 44 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| HyperEVM | 47 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
| Monad | 48 | `0xC38e4e6A15593f908255214653d3D947CA1c2338` |
# Swift v2
Source: https://docs.mayan.finance/architecture/swift-v2
Lightning fast, intent-based bridging and cross-chain swaps with Mayan’s Swift v2.
**What it is:**\
Swift v2 is Mayan's upgraded intent-based protocol for cross-chain swaps. It uses the driver liquidity already available on the destination chain and introduces a cleaner two-contract architecture for more predictable settlement. Instead of bridging tokens or minting wrapped assets, drivers compete to deliver the user's output directly on the destination chain, and Swift v2 later unlocks the user's original funds on the source chain.
This model removes the need for users to route through bridges or supply destination gas. Swift focuses on fast execution, guaranteed output amounts, and a simple integration path through the [Mayan SDK](https://github.com/mayan-finance/swap-sdk).
**What's New in Swift v2:**
| Aspect | Swift v1 | Swift v2 |
| ------------------------ | ------------------------------------------------ | ----------------------------------------------------------------------------------- |
| **Architecture** | Single contract per chain | Two-contract structure (source + destination) |
| **Settlement Flow** | Unlock tied directly to fulfillment | Clearer, more consistent settlement across chains |
| **Payload Handling** | Basic support | Staging + settle step for payload swaps to validate the payload before final payout |
| **Referrer Fees** | Collected from output token on destination chain | Collected from locked source assets and paid on source chain |
| **Integration Clarity** | Some logic shared in one contract | Cleaner separation of responsibilities |
| **Indexing / Analytics** | Less standardized | Destination-side reporting improved |
**How it works:**
* A user starts a swap on the source chain, and the Swift v2 source contract locks the input tokens. The input may first be swapped into the primary locked asset on the source chain (e.g., USDC or ETH); if the swap is later refunded, the user receives that converted asset rather than the original token.
* An [auction](/architecture/auction) is run on Solana, where drivers submit bids based on the output they can deliver on the destination chain.
* The winning driver fulfills the swap directly on the destination chain using their own liquidity, giving the user an instant payout.
* The destination contract posts fulfillment data that is executed on the source chain, and Swift v2 emits an unlock message releasing the locked funds.
* For payload-enabled swaps, the destination contract stages the payout and completes it after a settlement step. The source unlock still occurs after the VAA, as usual.
**Key advantages:**
* Fast, predictable cross-chain settlement powered by driver liquidity on the destination chain.
* No bridge liquidity requirements, wrapping, or mint/burn mechanics.
* Competitive driver participation ensures reliable execution and guaranteed output amounts.
* Simple integration handled entirely through the SDK, with minimal on-chain assumptions.
* Optional referrer-fee support for integrators.
**Protocol fee:**\
Swift v2 keeps the same protocol fee: 3 basis points (0.03%). This value may change over time at the guardian's discretion; the live protocol bps for any quote is returned in the quote response as `mayanBps`.
### Swift v2 Contract Addresses
| Network | Wormhole Chain ID | Source Contract Address |
| ------------- | ----------------- | --------------------------------------------- |
| **Solana** | 1 | `mayan34VedncxdK2XobtvWFDXQASUTBXhUVzt2kKgny` |
| **Ethereum** | 2 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **BSC** | 4 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Polygon** | 5 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Avalanche** | 6 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Arbitrum** | 23 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Optimism** | 24 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Base** | 30 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Linea** | 38 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Unichain** | 44 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **HyperEVM** | 47 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| **Monad** | 48 | `0x40fFE85A28DC9993541449464d7529a922142960` |
| Network | Wormhole Chain ID | Destination Contract Address |
| ------------- | ----------------- | --------------------------------------------- |
| **Solana** | 1 | `mayan34VedncxdK2XobtvWFDXQASUTBXhUVzt2kKgny` |
| **Ethereum** | 2 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **BSC** | 4 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Polygon** | 5 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Avalanche** | 6 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Arbitrum** | 23 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Optimism** | 24 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Base** | 30 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Linea** | 38 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Unichain** | 44 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **HyperEVM** | 47 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
| **Monad** | 48 | `0xD78D199f8C402e7B5Cc2abE278dF0412400a3BAe` |
# Wormhole Swap
Source: https://docs.mayan.finance/architecture/wh-swap
Permissionless, open-source cross-chain token swaps built on Wormhole primitives and powered by Mayan.
**What it is:**\
Wormhole Swap leverages the Wormhole Token Bridge’s mint-and-burn architecture enhanced by Wormhole’s generic message-passing capabilities to enable seamless cross-chain, one-click token swaps. Developed by Mayan, this protocol embeds swap-specific metadata alongside Token Bridge messages, making cross-chain swaps efficient and trustless.
**How it works:**
* Users initiate a swap that sends tokens via the Wormhole Token Bridge using mint-and-burn.
* Swap details are embedded in generic Wormhole messages delivered by relayers on Solana.
* Drivers receive all swap information and participate in a Mayan on-chain auction to determine the best execution price.
* The auction winner performs a flash swap on Solana, obtaining the funds and atomically sending the promised output amount within the same transaction.
* Post-swap, if the destination address is on Solana, the relayer directly transfers output tokens to the user's wallet. Otherwise, relayers send tokens to destination chains via the Token Bridge.
**Key advantages:**
* Atomic flash swaps ensure secure, simultaneous execution without intermediary risk.
* Fully permissionless and open-source.
* Efficient use of Wormhole messaging coupled with Mayan’s competitive auction to optimize pricing and execution.
Here is an overview of what happens when a user executes a cross-chain transaction on Mayan:
**Protocol fee:**\
The Wormhole Swap fees are set at 10 basis points (0.10%).
### WH Swap Contract Addresses
| Network | Wormhole Chain ID | Contract Address |
| --------- | ----------------- | ---------------------------------------------- |
| Solana | 1 | `FC4eXxkyrMPTjiYUpp4EAnkmwMbQyZ6NDCh1kfLn6vsf` |
| Ethereum | 2 | `0xBF5f3f65102aE745A48BD521d10BaB5BF02A9eF4` |
| BSC | 4 | `0xBF5f3f65102aE745A48BD521d10BaB5BF02A9eF4` |
| Polygon | 5 | `0xBF5f3f65102aE745A48BD521d10BaB5BF02A9eF4` |
| Avalanche | 6 | `0xBF5f3f65102aE745A48BD521d10BaB5BF02A9eF4` |
| Arbitrum | 23 | `0xBF5f3f65102aE745A48BD521d10BaB5BF02A9eF4` |
| Optimism | 24 | `0xBF5f3f65102aE745A48BD521d10BaB5BF02A9eF4` |
| Base | 30 | `0x11AA521C888d84f374B63823d9b873CAa3591f55` |
# What is Mayan?
Source: https://docs.mayan.finance/index
Mayan is how crypto moves. Through a simple swap interface and a powerful set of developer tools, it handles routing and execution behind the scenes so users get fast, competitive swaps and teams can build cross-chain movement into their products.
With over **\$18 billion** in total volume processed, **8 million swaps completed**, and **3 million unique wallets** served, Mayan supports transfers between the major blockchains like Solana, Ethereum, Monad, BNB Chain, Base, Arbitrum, Optimism, Sui, HyperEVM, and many more.
## Key Features
* **One place, three swap methods:** Swift, MCTP, and Wormhole Swap — whether you want to transfer pennies or millions of dollars, Mayan adapts to deliver an optimized experience balancing speed and price.
* **Best rates, always:** Onchain auctions continuously find the optimal swap and bridge routes for maximum value.
* **Blazing speed:** Swaps complete in just seconds — not minutes or hours.
* **Developer-friendly:** Easy to integrate using the Mayan SDK and widget, with flexible options for fees.
* **Secure and trustworthy:** Trustless, permissionless smart contracts, audited regularly and designed to keep assets safe.
## Transfer Methods
* ⚡ **Swift:** Our newest, fastest method. Intent-based transfers that complete at lightning speed with the best prices, thanks to a competitive auction system.
* 🌐 **MCTP (Mayan-Circle Transfer Protocol):** Uses Circle's CCTP highway for value transfer, combined with Mayan's auction model for ideal pricing — perfect for high-value and stablecoin transfers.
* 🔄 **Wormhole Swap:** Employs Wormhole’s Token Bridge for low-slippage, high-value transfers of BTC, SOL, and ETH.
## Use Cases
* Swap any asset to any asset on supported major chains effortlessly and instantly.
* Bridge assets quickly with the best prices available.
* Power wallets, dApps, and marketplaces with seamless cross-chain liquidity.
## The Mayan Product Suite
* **Mayan App:** Simple, intuitive interface to execute swaps with a few clicks.
* **Mayan API:** Comprehensive tools for developers to embed cross-chain capabilities into their platforms.
* **Mayan Protocol:** Robust smart contracts and auction mechanisms ensuring trustless, efficient, and transparent swaps.
## Trust and Reliability
Mayan provides industry-leading uptime, rigorous audits, and security measures designed to ensure your assets are safe, and your trades are executed smoothly — whether pennies or millions.
## Get Started
* Try the [**Mayan app**](https://mayan.finance/) for instant, cross-chain swaps.
* Explore our [**developer guides**](https://docs.mayan.finance/) to build your own integrations.
# Zaps (Custom Payloads)
Source: https://docs.mayan.finance/integration/custom-payload
Learn how to attach custom payloads, to Mayan swaps, which routes support them, and how to pass raw payload bytes through the EVM, Solana, and Sui SDK integrations.
## Overview
Custom payloads let you attach a small piece of data to a cross-chain swap. This data travels with the transfer and can be read by your destination contract or backend once the funds arrive.
The [Swap SDK](https://github.com/mayan-finance/swap-sdk) sends this payload as raw bytes. It does not interpret or modify the contents, and your destination logic is responsible for decoding them. Some routes, such as HyperCore deposits, construct their own fixed payload instead.
This page explains what custom payloads are, why they are useful, and how to include them when calling the Swap SDK.
## Definition and Guarantees
A custom payload is an arbitrary sequence of bytes attached to a supported swap route. When a route accepts payloads, the SDK forwards the raw bytes exactly as provided.
### Core guarantees
* Payloads are forwarded verbatim; the SDK does not transform, validate, or decode them. Some routes hash the payload into an order key for verification or indexing, but the raw bytes sent cross-chain are unchanged.
* When a route supports payloads (e.g., [MCTP](/architecture/mctp), Fast MCTP, Swift), it sets the `payloadType` enum accordingly.
* The raw payload bytes are included in the cross-chain message or transaction and reach the destination exactly as provided. Your destination logic is responsible for parsing them.
* Some routes do not accept caller-supplied payloads, may ignore them, or construct their own fixed payload (such as HyperCore or specific lock-fee/auction paths).
## Why use custom payloads?
Custom payloads let you attach additional context to a transfer, such as user metadata, routing parameters, or application-specific instructions for your destination logic. If your integration does not require extra data on the destination chain, you can omit it.
Only specific swap routes support caller-supplied payloads; others will reject or override the bytes.
## Use custom payloads in the Swap SDK
You can attach a custom payload from EVM, Solana, or Sui by passing a `Buffer` or `Uint8Array` to the relevant SDK call. Payload support depends on the chain and the route type, so refer to the sections below for precise behavior.
Payloads should stay small. Each chain and route has its own message-size limits, and larger payloads may increase fees or cause the swap to fail if the underlying protocol rejects the message.
### Fetching quote
To ensure you receive a quote that supports custom payloads, you must pass `payload: 'true'` in the `quoteOptions` parameter when using `fetchQuote` or `generateFetchQuoteUrl`.
> **Important**
>
> * Custom payloads are not supported by all route types provided by Mayan. Therefore, it is important to explicitly indicate that you intend to use custom payloads so the quote service can return only routes that support them.
### EVM
On EVM, a [custom payload](https://github.com/mayan-finance/swap-sdk/blob/main/src/evm/evmSwap.ts#L365) can be included in calls such as `swapFromEvm`, or `getSwapFromEvmTxPayload`.
**Example:**
```ts theme={null}
import { swapFromEvm } from '@mayanfinance/swap-sdk';
const customPayload = Buffer.from('hello-world');
const tx = await swapFromEvm(
quote,
fromAddress,
destAddr,
null, // optional referrer
signer,
undefined, // optional permit
undefined, // optional overrides (gas settings)
customPayload
);
```
### Solana
On Solana, a [custom payload](https://github.com/mayan-finance/swap-sdk/blob/main/src/solana/solanaSwap.ts#L328) is supported on specific routes, including MCTP, Fast MCTP, and Swift v2, under route-specific conditions.
**Example:**
```ts theme={null}
import { swapFromSolana } from '@mayanfinance/swap-sdk';
const customPayload = Buffer.from('hello-world');
const result = await swapFromSolana(
quote,
swapperWalletAddress,
destinationAddress,
referrerAddresses,
signTransaction,
connection,
undefined, // extraRpcs
undefined, // sendOptions
undefined, // jitoOptions
{ customPayload } // forwarded only for supported routes
);
```
### Sui
On Sui, a [custom payload](https://github.com/mayan-finance/swap-sdk/blob/main/src/sui/suiSwap.ts#L19) is passed directly as the payload argument to `createSwapFromSuiMoveCalls`.
**Example:**
```ts theme={null}
import { createSwapFromSuiMoveCalls } from '@mayanfinance/swap-sdk';
const customPayload = Buffer.from('hello-world');
const tx = await createSwapFromSuiMoveCalls(
quote,
swapperWalletAddress,
destinationAddress,
referrerAddresses,
customPayload,
suiClient
);
```
## Summary
Zaps let you attach application-specific data to supported Mayan swap routes. Only specific paths accept payloads, and each chain has its own conditions. Always confirm the quote type and route before attaching a payload, and ensure your destination logic is prepared to decode the data you send.
# Explorer API
Source: https://docs.mayan.finance/integration/explorer-api
Track the full lifecycle and details of your Mayan transactions with the Explorer API.
## Overview
The Mayan Explorer API provides a powerful interface to track and monitor cross-chain swaps executed on the Mayan protocol. It delivers comprehensive transaction data for users and developers seeking detailed insights into swap lifecycle and status.
## Interactive API Documentation
For users and developers who want to explore and test the Mayan API endpoints directly, we provide a [**Swagger UI**](https://price-api.mayan.finance/swagger) portal. This user-friendly interface allows you to view all available endpoints, try out queries in real-time, and understand request and response formats without writing any code.
## Querying Transaction Status
Using the Explorer API, you can query transaction progress and details by providing the swap transaction hash. The API returns current statuses reflecting each step of the swap process.
## Real-Time Monitoring and Auditing
This API enables real-time visibility into the entire swap journey, supporting auditing, error handling, and enriched user experience for frontends or backend integrations.
### Example API Request
Here’s a sample request illustrating how to retrieve swap details using a transaction hash:
```powershell theme={null}
curl 'https://explorer-api.mayan.finance/v3/swap/trx/0x232bf1d5dd6e340a2e036ed0b58dbb444b56d3770d5543c7d55fecebbf0ad65e'
```
## Detailed Swap Information
The API response includes all relevant fields such as swap status, involved tokens and chains, amounts transferred, timestamps, fees, and verification receipts. Null values are omitted for cleaner data consumption.
```json theme={null}
{
"id":"a8054a9c-6141-491a-80c3-8dd7ff88e638",
"trader":"0xeE1A58EafE1977A3D2ae59E2897Bb815054f6D58",
"sourceTxHash":"0x232bf1d5dd6e340a2e036ed0b58dbb444b56d3770d5543c7d55fecebbf0ad65e",
"sourceTxBlockNo":34244833,
"status":"SETTLED_ON_SOLANA",
"transferSequence":"77090",
"swapSequence":"0",
"deadline":"2022-10-12T09:27:45.000Z",
"sourceChain":"5",
"swapChain":"1",
"destChain":"1",
"destAddress":"EU8z368kxJ4VzLfdpNG774L6DpAnav9d9cBBpbyH9Rr2",
"fromTokenAddress":"0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270",
"fromTokenChain":"5",
"fromTokenSymbol":"WMATIC",
"fromAmount":"9.99611386",
"toTokenAddress":"mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So",
"toTokenChain":"1",
"toTokenSymbol":"MSOL",
"toAmount":"0.231",
"transferSignedVaa":"01000000020d00aab8719f49c4c8bb89e6d705d279fc9a8ee7a35e4ff8cce223e2c97970c19c3405e7fd0f938c4e626284dff68bb21381979053b8415a201ed1d0a6e74c18aef001028027fa1479db229ba36a73801754f23a1cafb72a51d6d542ae50971233193aa966ea1c358bebd11cda30616b8ba54abf980f7cac2945e75442cfca0dace2d2a901032add4e30f42d1f8a7259887d6a66a3c84d70dc6069873d99b089263566b95da519259abe330aa736cfd626ce4e18c0e8bc4ee1c4b37d50b8f4d790f5395761fe01044f38137d47f85156d9cb0151a6aba6e9ef04499bea96737ec8a4674916e55689742867f2b7bc90101b22943e1c5ede43346318dae7b8ba38b5d884ecf4d0b2590106168151effac6eecc77a930dfc3b53a30f6ca5ce513449ae11bec0380626324b3624c3ada89b7a5fc736ae2505cedb330abafee60a712df5101098960e101ccb20007f796a00e8c3514551dd526265aebf558b4b03b72cdeb7a9b0ba3ccc399133f162a2f84260aabc6158ea8e6b69ccb19ea99011e576c84b26cef71563b24f12cf6010ac14e56b157b625404aee331233f8c658d8edd4879815b197245e9501d05f89e910685955d9f6330e42f609d1caaf445e27f0ded0064f386c013e0a3983cd33fd000b3f4aec5c048486620b2558d16a3d11cff060fe50ad5e2f89ba105c62b1f00e33064cd41824ad2916d217a0f9e6bd334804434dc26491387843da14da6b8fd2e7000ca44a8bacf1107b08158b301188126508377716bd37f6d0062231ab73594f7999151efd117138f3b5c7fc33122c7f2cfc4c5f65c4b91e8e1b9579f964daf14529000d1a3159f13eba61ce37a45e5e0c56c1c5c75e44dcd9bd9511955ebd5367fccbcc7ada7f68241454edb504d3d2b8f33384b64a688a88bee66e044cdf155f20d021000eb617d77ae2b498591bec60ff3a89c9a25d8854850ed6b3793cbbdae803ec2e43776f029ec6e31c2f0a1973bb8fa220bf3206c0c0c148120ace02cb06b35366cf0011979eea8ec0b1fa9a80a96981441eda2d3d760d39c103d4de440e45d917c7d9c526c2163aadfc1892cdb35e50629fef15651c65e4546215f65ba9cc0722db4a550112ac48a08541a9fd14d0eae20bb80e910b332dc117556ab2da9caa7c0b177e5a5d534baa50dac75715df918bc6f9185dfff37485ffb49a6badb3f7d6884e4b4953016346819500012af700050000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde0000000000012d220f01000000000000000000000000000000000000000000000000000000003b94dbfa0000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf127000057290becfd5df48290fc36fc8597f04cc79c7c122a5389214ee897f3557cd30ba00010000000000000000000000000000000000000000000000000000000000000000",
"swapSignedVaa":"01000000020d003cb77de662828e1455a9ae190ed928eb6c89f7a99e2b4599c7e7c96286461cbe518f1f51624abe5ae129e22607f69b467fe1f37cc0639eefa5d679c22bad6fea0002ca3095e347aa3efed7136169130f34f848fd0bf930c6e234926371dee73c396d003bc747d93a359adc697abacd372882955dfd1eea6f3b1cda83566b34a854a90003b3b500b752b99c36c81d1a3e05e930167d21471bf0b414049c1f4615c22b52826e41c46adace3bf86ec7541d00576235db1a297bd48234d81f841cf899da94570104ea6cdd63d2cd5eae3cd3ead80fea8656617ea15c6354817fc4a437c49028e6bf690782d95f2c37d921d841d86fe7b8e3e13809359436d1d78747b3361ea896ed0106959131aec94f6e26c46e1755a1ba18bf5f2c6f73f02276d97b46e547f52a9dfe05d807435797969bb3678b2334b0513fb5f42f1c590d87d9dc7489b0b1d2caa201071fca6db97d946efb454ad7e88c58dbf1a18f5dbb31842f3711aaafaadeac97834ae99bf5274a3802757eac7495513e980c9cbec9b7ba898eb4994089bfd3492c010885ab047bf6bd72f9760b60e246a835457dc0b0cf8240dd6ef810a6951623a8f84690f280b7861e45d5885ab9726cd5a0fb47eaf87dc692465cb1caea6a171457010aebb44ec2fa0ed1b6a2a7f1485d9887e1c158bfe479f741055b15b5ba9034f81453d6c8d91a14ed90dc324e64a7645170d7ea8bd7b67c42193c895ff7e3e68595000b8e1acd9c77f642bdf5114becfddec283f80f199cd7b1ba0a47fcea3bee669dbe4e0c4e0ec02a02b78a3c09023b46ba4777498efeb6439f044cc9d86f4f64db42000c6d363a69cbeac8d037aa8e5cb802102473d0dc61d2b102bf3cd3d4f2ae2cdde56b096372fd52e7088cda9050a25acb67371d67c6f6d89553f28b9b385c36a50a000d1193dd6415f01bb5b21b5a2207134a7f13be5d3c762c01bb64e5a08dcf22a3425f6439f63f33ec9eed3a6d72d2405f982057793456becad61e32ce490f9bfc2b01113ac90f24dff1d129f8bf8030cea54fee37fc522a2e1cecd425da254de1374e8543fe907e60e336e48bf77c04e0603672bd16592857a22dea9879434277791061001284150c97369be0f71316be004db4604ea7c666747eca0dd1b18314a7d35ac9171f46456a82f73081baa6adfd749bad936cba9a2b5bb83651f917449d4d2b61ba006346819500012af80005000000000000000000000000eae8425c60b12d09d12d02ff981f85b34f2dcfcc00000000000000000f01000000000000000000000000000000000000000000000000000000003b94dbfa0b62ba074f722c9d4114f2d8f70a00c66002337b9bf90c873657a6d201db4c800001c81ba38362db3567aaf37cb0e8ff25e91c669c2f17fbf5c872f847c8c1a7bfbb0001000000000000000000000000ee1a58eafe1977a3d2ae59e2897bb815054f6d5800050000000000012d220000000000000000000000000000000000000000000000000000000001504dc0000000006346889100000000012b3efa000000000000000000000000000a8751",
"savedAt":"2022-10-12T08:57:59.565Z",
"initiatedAt":"2022-10-12T08:57:57.000Z",
"completedAt":"2022-10-12T09:16:26.852Z",
"insufficientFees":false,
"retries":0,
"swapRelayerFee":"0.19611386",
"redeemRelayerFee":"0.0",
"refundRelayerFee":"0.00690001",
"statusUpdatedAt":"2022-10-12T09:16:26.852Z",
"bridgeFee":"0",
"sourceTxFee":"4915861714160382",
"fromTokenLogoUri":"https://assets.coingecko.com/coins/images/14073/small/matic.png?1628852392",
"toTokenLogoUri":"https://assets.coingecko.com/coins/images/17752/small/mSOL.png?1644541955",
"fromTokenScannerUrl":"https://polygonscan.com/token/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270",
"toTokenScanner":"https://solscan.io/token/mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So"
}
```
# Mayan Forwarder Contract
Source: https://docs.mayan.finance/integration/forwarder-contract
Unified entry point for secure cross-chain swaps across EVM networks.
## Overview
Mayan Forwarder serves as the unified entry point for interacting with Mayan’s cross-chain swap methods—Swift, MCTP, and Wormhole Swap. It streamlines integration by providing a single, secure interface and maintains a whitelist of trusted protocol addresses to mitigate risks from compromised quote data.
The Forwarder contract is deployed across multiple EVM-compatible chains—including Ethereum, Arbitrum, Base, Optimism, Avalanche, Polygon, BSC, Unichain, Linea, HyperEVM and Monad. All share the same contract address:
```js theme={null}
0x337685fdaB40D39bd02028545a4FfA7D287cC3E2
```
### Swap From EVM
To initiate a swap using an ERC20 token as input, you must first approve the required allowance for the Mayan Forwarder contract or alternatively provide a permit object to enable spending.
```js theme={null}
tokenContract.approve(forwarderContract, amountIn);
```
After approval, you can generate the complete transaction payload using the `getSwapFromEvmTxPayload` function from the [Mayan SDK](https://github.com/mayan-finance/swap-sdk) and then pass it to the Mayan Forwarder contract.
If you need to build the payload manually and use Mayan Forwarder, you should choose the right method based on your input token:
* `forwardERC20`: For input tokens that are ERC-20.
| Parameter | Type | Descrption |
| ------------- | ------------ | ------------------------------------------------------------------------------ |
| tokenIn | address | Input token address |
| amountIn | uint256 | Input amount |
| permitParams | PermitParams | Signed permission (eip-2612) Pass zero for all values if you don't want to use |
| mayanProtocol | address | Address of Mayan final contract |
| protocolData | bytes | Bytes data for Mayan final contract |
* `swapAndForwardERC20`: Same as `forwardERC20` but performs a swap on the source chain before bridging.
| Parameter | Type | Description |
| --------------- | ------------ | ------------------------------------------------------------------------------ |
| tokenIn | address | Input token address |
| amountIn | uint256 | Input amount |
| permitParams | PermitParams | Signed permission (eip-2612) Pass zero for all values if you don't want to use |
| swapProtocol | address | Contract address of swap protocol |
| swapData | bytes | Bytes data that is needed by swap protocol |
| middleToken | address | The output token of swap protocol |
| minMiddleAmount | uint256 | Minimum output of swap step or the transaction will revert |
| mayanProtocol | address | Address of Mayan final contract |
| mayanData | bytes | Bytes data for Mayan final contract |
* `forwardEth`: For input tokens that are native tokens of the chain.
| Parameter | Type | Description |
| ------------- | ------- | ----------------------------------- |
| mayanProtocol | address | Address of Mayan final contract |
| mayanData | bytes | Bytes data for Mayan final contract |
* `swapAndForwardEth`: Similar to `forwardEth` but performs a swap before bridging.
| Parameter | Type | Description |
| --------------- | ------- | ---------------------------------------------------------- |
| amountIn | unit256 | Input amount of native token |
| swapProtocol | address | Contract address of swap protocol |
| swapData | bytes | Data that is needed by swap protocol |
| middleToken | address | The output token of swap protocol |
| minMiddleAmount | uint256 | Minimum output of swap step or the transaction will revert |
| mayanProtocol | address | Address of Mayan final contract |
| mayanData | address | Bytes data for Mayan final contract |
# Hyperliquid (HyperCore)
Source: https://docs.mayan.finance/integration/hyperliquid
Deposit USDC into a Hyperliquid (HyperCore) spot or perps balance from any supported chain, and withdraw USDC back out to an EVM chain, using the Mayan Swap SDK.
## Overview
Mayan supports **direct Hyperliquid (HyperCore) deposits and withdrawals** through the
[Swap SDK](https://github.com/mayan-finance/swap-sdk). You can:
* **Deposit** — swap from any supported token on any supported chain into a HyperCore **USDC** balance.
* **Withdraw** — move **USDC** out of a HyperCore balance back to an EVM chain.
A HyperCore account is identified by an ordinary EVM-style `0x…` address — the **same address** the
user signs with. HyperCore holds USDC in two distinct balances, and every deposit/withdrawal targets one of them:
* **USDC (spot)** — the Hyperliquid spot balance.
* **USDC (perps)** — the Hyperliquid perpetuals (collateral) balance.
Both directions are built on Mayan **Swift v2**. Deposits and withdrawals construct their own fixed
payloads internally (see [Zaps / Custom Payloads](/integration/custom-payload)); you do not need to
craft them. Only **USDC** is supported on the HyperCore side today.
From an integrator's perspective, HyperCore deposits and withdrawals are **just another cross-chain
swap route**: you call the same `fetchQuote` and `swapFromEvm` / `swapFromSolana` entry points with
`hypercore` as the `toChain` (deposit) or `fromChain` (withdraw), and consume the resulting `Quote`
exactly as you would for any other pair.
Requires `@mayanfinance/swap-sdk` **v14.3.0** or later.
### HyperCore token identifiers
When you fetch a quote, the HyperCore side is addressed with these `toToken` / `fromToken` values:
| Balance | Quote token name | Identifier (contract) |
| ------------ | ---------------- | -------------------------------------------- |
| USDC (spot) | `USDC (spot)` | `0x000000000000000000000000000000000000ffff` |
| USDC (perps) | `USDC (perps)` | `0x0000000000000000000000000000000000000000` |
You can always discover these dynamically from the [Tokens API](https://price-api.mayan.finance/swagger/)
(`fetchTokenList('hypercore')`) instead of hard-coding them.
***
## Deposit (into HyperCore)
Bridge/swap from a source chain into a HyperCore USDC balance. This is a normal signed transaction on
the **source** chain (so the user needs gas there); the SDK and Mayan relayer handle the rest, crediting
the chosen HyperCore balance.
### 1. Fetch a quote
Set `toChain: 'hypercore'` and `toToken` to the spot or perps identifier. The returned quote is a
`SWIFT` quote whose `toToken.name` is `'USDC (spot)'` or `'USDC (perps)'`.
```ts theme={null}
import { fetchQuote } from '@mayanfinance/swap-sdk';
const HC_USDC = {
spot: '0x000000000000000000000000000000000000ffff',
perps: '0x0000000000000000000000000000000000000000',
};
const quotes = await fetchQuote({
amountIn64: '3800000000000000', // 0.0038 ETH (wei)
fromToken: '0x0000000000000000000000000000000000000000', // ETH on Base
fromChain: 'base',
toToken: HC_USDC.spot, // or HC_USDC.perps
toChain: 'hypercore',
slippageBps: 'auto',
});
const quote = quotes[0];
```
* The input token can be anything Mayan supports on the source chain (native ETH, USDC, etc.); Mayan
swaps it to USDC and deposits it.
* `gasDrop` is **not supported** for HyperCore deposits and is ignored.
* HyperCore deposits **do not accept a caller-supplied payload** — passing one throws. The route
builds its own fixed payload.
* HyperCore enforces a minimum deposit (around **5 USDC** of output). Size `amountIn64` so the
expected output clears it.
### 2. Execute the deposit
Use the regular `swapFromEvm` (or `swapFromSolana`) — there is **no extra signing step**. Pass the
user's HyperCore address (the `0x…` account) as `destinationAddress`.
```ts theme={null}
import { swapFromEvm } from '@mayanfinance/swap-sdk';
import type { TransactionResponse } from 'ethers';
const hyperCoreAddress = wallet.address; // the 0x account that owns the HyperCore balance
const res = (await swapFromEvm(
quote,
wallet.address, // swapperAddress (must match the signer)
hyperCoreAddress, // destinationAddress = HyperCore account
null, // referrerAddresses
signer, // ethers Signer on the source chain
null, // permit
null, // overrides
null, // payload — must be null for HyperCore deposits
)) as TransactionResponse;
console.log('source tx:', res.hash);
await res.wait();
```
For a contract-level integration, build the unsigned transaction with `getSwapFromEvmTxPayload`
instead. Solana sources are supported via `swapFromSolana`; depositing from HyperEVM uses a
mono-chain quote and the same `swapFromEvm` entry point.
**Sui → HyperCore is temporarily disabled.** Calling `createSwapFromSuiMoveCalls` with
`toChain: 'hypercore'` throws. A dedicated entry point will ship in a future release.
### 3. Track the deposit
A deposit returns a normal `TransactionResponse`. Track it on the
[Explorer API](https://explorer-api.mayan.finance/swagger/) by transaction hash and watch
`clientStatus` (`INPROGRESS` → `COMPLETED` / `REFUNDED`):
```ts theme={null}
const res = await fetch(`https://explorer-api.mayan.finance/v3/swap/trx/${txHash}`);
const { clientStatus } = await res.json();
```
***
## Withdraw (out of HyperCore)
Move USDC out of a HyperCore balance to an EVM chain. Withdrawals are **gasless**: the user signs an
EIP-712 typed-data message instead of sending an on-chain transaction, and the SDK submits it to the
Mayan relayer. **No source-chain gas is required.**
### 1. Fetch a quote
Set `fromChain: 'hypercore'` and `fromToken` to the spot or perps identifier, and pass
`{ gasless: true }`. HyperCore withdrawals **must** be gasless `SWIFT` quotes — the returned quote
carries an `hcSwiftWithdraw` object.
```ts theme={null}
import { fetchQuote } from '@mayanfinance/swap-sdk';
const quotes = await fetchQuote(
{
amountIn64: '5000000', // 5 USDC (6 decimals)
fromToken: HC_USDC.spot, // or HC_USDC.perps
fromChain: 'hypercore',
toToken: '0x0000000000000000000000000000000000000000', // ETH on Base
toChain: 'base',
slippageBps: 'auto',
},
{ gasless: true },
);
const quote = quotes[0];
// quote.gasless === true, quote.type === 'SWIFT'
// quote.hcSwiftWithdraw = { gasLimit, relayerFee64, maxFee64, minFinalityThreshold }
```
You can withdraw to any supported destination token/chain (e.g. ETH or USDC on Base).
### 2. Execute the withdrawal
Call the **same** `swapFromEvm`. Because `quote.fromChain === 'hypercore'` and the quote is a gasless
SWIFT quote, the SDK builds the `HyperliquidTransaction:SendToEvmWithData` EIP-712 message, has the
signer sign it, submits it to the relayer, and returns an **`orderId` string** (no on-chain tx).
```ts theme={null}
import { swapFromEvm } from '@mayanfinance/swap-sdk';
const orderId = (await swapFromEvm(
quote,
wallet.address, // swapperAddress = HyperCore account (must match the signer)
destinationAddr, // where the funds land on the destination chain
null, // referrerAddresses
signer, // ethers Signer — signs the EIP-712 message
null, // permit
null, // overrides
null, // payload — optional, supported for withdrawals
)) as string;
console.log('withdraw orderId:', orderId);
```
* Withdrawals **must** use a gasless SWIFT quote. Passing a non-gasless quote (or a non-SWIFT
quote) throws `Only SWIFT gasless quotes are supported from hypercore withdraw`.
* Unlike deposits, a custom `payload` **is** supported and is appended to the order's hook data.
* You only specify the final `toChain` / `toToken` in the quote; the SDK and relayer handle delivery
to that destination.
### 3. Track the withdrawal
`swapFromEvm` returns an `orderId` (e.g. `HCS_WITHDRAW_0x…`) for HyperCore withdrawals. Track it on the
[Explorer API](https://explorer-api.mayan.finance/swagger/) via the **order-id** endpoint (not the
`trx` endpoint used for deposits) and watch `clientStatus`:
```ts theme={null}
const res = await fetch(`https://explorer-api.mayan.finance/v3/swap/order-id/${orderId}`);
const { clientStatus } = await res.json(); // INPROGRESS -> COMPLETED / REFUNDED
```
# Quote API
Source: https://docs.mayan.finance/integration/quote-api
We highly recommend using [Mayan SDK](https://github.com/mayan-finance/swap-sdk) for integration as it simplifies the integration process.
Before performing a swap we need find the best route and get the swap rate for the token pair using quote API.
### API Reference
### Example:
The request to get the quote for swapping 100 USDC on Avalanche to receive SOL on Solana would be like this:
#### Request:
```powershell theme={null}
curl -X 'GET' 'https://price-api.mayan.finance/v3/quote?amountIn=100&fromToken=0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e&fromChain=avalanche&toToken=0x0000000000000000000000000000000000000000&toChain=solana&slippageBps=300&gasDrop=0&swift=true&mctp=true&fastMctp=true&wormhole=true&solanaProgram=FC4eXxkyrMPTjiYUpp4EAnkmwMbQyZ6NDCh1kfLn6vsf&forwarderAddress=0x337685fdaB40D39bd02028545a4FfA7D287cC3E2&sdkVersion=13_1_0'
```
#### List of request parameters:
| Parameter | Type | Required | Description |
| ------------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| amountIn | Number | Yes\* | Input amount in human-readable format (e.g. `100` for 100 USDC). Either `amountIn` or `amountIn64` must be provided. |
| amountIn64 | String | Yes\* | Input amount in base units (smallest denomination, e.g. `100000000` for 100 USDC). Either `amountIn` or `amountIn64` must be provided. |
| fromToken | String | Yes | Token address on the source chain (use `0x0000000000000000000000000000000000000000` for native tokens) |
| fromChain | String | Yes | Source chain name (e.g. `solana`, `ethereum`, `bsc`, `avalanche`, `arbitrum`, `base`, `optimism`, `polygon`, `sui`) |
| toToken | String | Yes | Token address on the destination chain |
| toChain | String | Yes | Destination chain name |
| slippageBps | Number | Yes\* | Maximum slippage in basis points (e.g. `300` = 3%, max `500`). Either `slippageBps` or `slippage` must be provided. |
| slippage | Number | Yes\* | Maximum slippage as a decimal (e.g. `0.03` = 3%). Either `slippageBps` or `slippage` must be provided. |
| swift | Boolean | No | Enable [Swift](/architecture/swift) routes |
| mctp | Boolean | No | Enable [MCTP](/architecture/mctp) routes |
| fastMctp | Boolean | No | Enable Fast MCTP routes |
| wormhole | Boolean | No | Enable [Wormhole Swap](/architecture/wh-swap) routes |
| gasless | Boolean | No | Enable gasless swap mode |
| solanaProgram | String | No | Mayan Solana program address |
| forwarderAddress | String | No | Mayan Forwarder contract address |
| sdkVersion | String | No | SDK version in `major_minor_patch` format (e.g. `13_1_0`) |
| referrer | String | No | Referrer Solana address that receives the referrer fee |
| referrerBps | Number | No | Default is 0. The basis points the integrator earns through the referral program |
| gasDrop | Number | No | Default is 0. Amount of native gas to deliver to the user on the destination chain |
| fullList | Boolean | No | Default is `false`. Enables returning all available quote options instead of the default limited (fastest and best-return) results |
| destinationAddress | String | No | Helps provide a more accurate quote, for example by checking whether the Solana ATA exists. |
| apiKey | String | No | Defines an API key that prevents the **rate-limit-exceeded** error on a per-IP basis. |
| mpsDeposit | Boolean | No | Default is `false`. When `true`, attaches an [MPS deposit address](#mps-deposit-address-mpsdeposit) (`mpsDepositAddress`) to each eligible quote so a payer can fund the swap with a plain transfer instead of signing a transaction. Requires `destinationAddress`. See [MPS deposit address](#mps-deposit-address-mpsdeposit). |
| mpsUserId | String | No | Integrator-scoped identity that keys the MPS deposit address — a `0x`-prefixed hex string whose value fits in 20 bytes. Only used when `mpsDeposit` is `true`; when omitted it is derived from your `apiKey`. |
#### Response:
> The quote service returns an array of quotes. By default, it includes at most two items: the first is the fastest option, and the second is the best-return quote. If the fastest option also provides the best return, only one quote is returned.
>
> It is possible to set the `fullList` request parameter to `true` to retrieve all available quote options. In this case, more than two items may be returned, and no sorting is applied. Interfaces can apply their own sorting logic by considering the `etaSeconds` and `expectedAmountOut` fields in each item.
```json theme={null}
{
"quotes": [
{
"meta": {
"advertisedDescription": "Cheapest and Fastest",
"advertisedTitle": "Best",
"icon": "https://cdn.mayan.finance/fast_icon.png",
"switchText": "Switch to the best route",
"title": "Best"
},
"sendTransactionCost": 0,
"gasless": false,
"slippageBps": 300,
"effectiveAmountIn": 100,
"effectiveAmountIn64": "100000000",
"expectedAmountOut": 1.201839546,
"price": 0.01201963167985677,
"minAmountOut": 1.16578436,
"minReceived": 1.16578436,
"solanaRelayerFee": null,
"solanaRelayerFee64": null,
"redeemRelayerFee": null,
"redeemRelayerFee64": null,
"refundRelayerFee": null,
"refundRelayerFee64": "711",
"cancelRelayerFee64": "9147",
"submitRelayerFee64": "0",
"clientRelayerFeeSuccess": null,
"clientRelayerFeeRefund": 0.00902389942474755,
"deadline64": "1774621897",
"fromToken": {
"name": "USD Coin",
"symbol": "USDC",
"mint": "FHfba3ov5P3RjaiLVgh8FTv4oirxQDoVXuoUUDvHuXax",
"contract": "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
"chainId": 43114,
"wChainId": 6,
"decimals": 6,
"logoURI": "https://assets.coingecko.com/coins/images/6319/small/USD_Coin_icon.png?1547042389",
"coingeckoId": "usd-coin",
"realOriginContractAddress": "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e",
"realOriginChainId": 6,
"supportsPermit": true
},
"fromChain": "avalanche",
"toToken": {
"name": "SOL",
"symbol": "SOL",
"mint": "So11111111111111111111111111111111111111112",
"contract": "0x0000000000000000000000000000000000000000",
"chainId": 0,
"wChainId": 1,
"decimals": 9,
"logoURI": "https://statics.mayan.finance/SOL.png",
"wrappedAddress": "So11111111111111111111111111111111111111112",
"coingeckoId": "solana",
"realOriginContractAddress": "So11111111111111111111111111111111111111112",
"realOriginChainId": 1,
"supportsPermit": false
},
"toChain": "solana",
"gasDrop": 0,
"eta": 1,
"etaSeconds": 3,
"clientEta": "3s",
"bridgeFee": 0,
"suggestedPriorityFee": 0,
"type": "SWIFT",
"priceStat": {
"ratio": 0.9996,
"status": "GOOD"
},
"referrerBps": 0,
"protocolBps": 3,
"onlyBridging": false,
"minMiddleAmount": 100,
"sourceSwapExpense": 0,
"swiftVersion": "V2",
"swiftMayanContract": "0x40fFE85A28DC9993541449464d7529a922142960",
"swiftAuctionMode": 2
}
],
"minimumSdkVersion": [
7,
0,
0
]
}
```
### Response fields:
The following table shows the common fields of response:
| Field | Type | Description |
| ----------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | String | Determines the bridge method which can be "WH", "SWIFT", "MCTP" or "FAST\_MCTP" |
| effectiveAmountIn | Number | The actual input amount that will be deducted from user's wallet |
| expectedAmountOut | Number | Expected output amount that user receives |
| minAmountOut | Number | Minimum output amount of auction |
| minReceived | Number | The minimum amount that user receives after deducting relayer fees |
| price | Number | The amount of output token that user receives per 1 unit of input token |
| solanaRelayerFee | Number | For "WH" type this fee is denominated in input token. For "MCTP" type the fee is denominated in USDC. For "SWIFT" type the fee is zero. |
| redeemRelayerFee | Number | For "WH" type this fee is denominated in output token. For "MCTP" type the fee is denominated in USDC. For "SWIFT" type the fee is zero. |
| RefundRelayerFee | Number | For "WH" and "SWIFT" types this fee is denominated in input token. For "MCTP" type the fee is denominated in USDC. |
| clientRelayerFeeSuccess | Number | Total dollar value of relayer fees in the success scenario |
| clientRelayerFeeRefund | Number | Total dollar value of relayer fee in the refund scenario |
| eta | Number | estimated time of arrival in minutes |
| client eta | String | human readable string of eta |
| fromToken | Object | Input token details |
| fromChain | String | Source network name |
| toToken | Object | Output token details |
| toChain | String | Destination network name |
| mpsDepositAddress | String | Returned when `mpsDeposit` was requested and the quote is eligible — a deterministic deposit address on the **source** chain (`null` otherwise). Sending the input token to it settles the swap via the [Mayan Payment Service](/payment-service/overview), no signature required. See [MPS deposit address](#mps-deposit-address-mpsdeposit). |
| mpsIntegratorId | String | The MPS integrator the deposit address was minted under, keyed to your API key. `null` unless `mpsDepositAddress` is set. |
| mpsUserId | String | Echoes the integrator-scoped `mpsUserId` used to derive the address (the value you sent, or the one derived from your API key). `null` unless `mpsDepositAddress` is set. |
| swiftAuctionMode | Number | Swift quotes only. Auction mode of the order (see note below). |
**Auction modes** (Swift quotes only, `type = "SWIFT"`)
* `2`: English auction. The user receives at least `minAmountOut`, typically around `expectedAmountOut` and possibly higher or lower.
* `3`: Exact-out. The user is guaranteed to receive exactly `expectedAmountOut`.
### MPS deposit address (`mpsDeposit`)
Pass `mpsDeposit: true` to make a quote **fundable by a plain transfer**. On eligible quotes the
response carries an `mpsDepositAddress` — a deterministic address on the *source* chain — plus
`mpsIntegratorId` and `mpsUserId`. When the payer simply sends the input token to that address, the
[Mayan Payment Service (MPS)](/payment-service/overview) detects the deposit and settles the swap to
`destinationAddress` on the destination chain, with **no transaction to sign** and no SDK swap call.
This is the recommended way to obtain an MPS deposit address — the quote endpoint derives it for you
inline, so there is no separate address-generation call to make.
Requirements and behavior:
* **`destinationAddress` is required.** Without it `mpsDepositAddress` is `null` (the address is keyed
to the final recipient).
* **MPS must be enabled for your API key.** The address is minted under your integrator identity, so
pass your `apiKey`; without an MPS-enabled key the field comes back `null`. Contact
[support@mayan.finance](mailto:support@mayan.finance) to enable it.
* **`mpsUserId` is optional.** It's an integrator-scoped identity (a `0x`-prefixed hex string whose
value fits in 20 bytes, e.g. `0x1`); the same id always yields the same deposit address. When
omitted, it is derived from your `apiKey`, so deposits still bucket by caller. The value used is
echoed back as `mpsUserId`.
* **Only eligible quotes get an address.** MPS attaches it to Swift, mono-chain, and *direct* (no
source swap) Fast MCTP quotes whose source chain and token are indexed by MPS (see the
[deposit source chains](/payment-service/overview#deposit-source-chains) and
[supported tokens](/payment-service/overview#supported-tokens)) — and only when the amount clears
the token's minimum. Other quotes in the same response return `mpsDepositAddress: null`.
* **Best-effort.** If the address can't be derived for any reason, the quote is still returned with
`mpsDepositAddress: null`. Never treat a `null` as an error.
The [Mayan SDK](https://github.com/mayan-finance/swap-sdk) (v15+) exposes this as `fetchQuote`
options `mpsDeposit` and `mpsUserId`, and reads it back as `quote.mpsDepositAddress`. Passing
`mpsDeposit` forces the SDK's POST-body request path.
Track a deposit made to an `mpsDepositAddress` — detection, settlement, and delivery — with the MPS
[events stream](/payment-service/events) or by polling
[`GET /swaps`](/payment-service/api-reference#list-swaps).
### **Supported Tokens**
Mayan is an intent-based protocol and supports any token as input or output, provided there is sufficient liquidity on the source or destination chains. We also maintain an approved, whitelisted token list for convenience:
#### Example:
```powershell theme={null}
curl -X 'GET' \
'https://price-api.mayan.finance/v3/tokens?chain=solana' \
-H 'accept: application/json'
```
To get the aggregated list of tokens from all chains remove `chain` from in the query .
### Supported Chains
#### `GET` `sia.mayan.finance/v10/init`
Returns configuration for every supported chain, including whether it can be used as a origin or destination\*\* \*\*chain.
#### Example:
```powershell theme={null}
curl -X 'GET' \
'https://sia.mayan.finance/v10/init' \
-H 'accept: application/json'
```
This endpoint provides a JSON response containing a comprehensive list of all chains supported by the platform. Each chain entry includes detailed metadata such as the chain's name, chain ID, and other relevant attributes.
Two key fields indicate the chain's functionality within the platform:
* `originActive`: Specifies whether the chain is supported as a source chain.
* `destinationActive`: Specifies whether the chain is supported as a destination chain.
Use this endpoint to determine compatibility and availability of specific chains for your operations.
### API Key:
The `apiKey` parameter is optional. If you are using the Mayan SDK on a frontend and your request volume is low or comes from multiple origins, you can omit the `apiKey` to use the public endpoint. We recommend starting without an API key and only requesting one if you hit rate limits. To obtain an API key, email [**support@mayan.finance**](mailto:support@mayan.finance).
# Referral Program
Source: https://docs.mayan.finance/integration/referral
The Mayan Referrer Program lets Integrators of the Mayan SDK and widget earn referral fees on transactions by setting their wallet address as the referrer.
Each bridging method within the Mayan protocol has its own referral fee behavior and collection mechanism. This document explains how referral fees work across all methods and how to configure them in the SDK.
***
## **Swift V2**
**Fee Collection**
* Fees are collected on the **source chain** once the bridge is successfully completed.
* Deposited into the [fee manager contract](https://etherscan.io/address/0x26227ACE40de5671e8355fCAFf65a0522aa7b303)\*\*.
* Fees are collected in the fee manager contract and can be withdrawn using this tool:\
[https://explorer.mayan.finance/withdraw-fee-swift](https://explorer.mayan.finance/withdraw-fee-swift)
* Collected via the **locked assets** (e.g., USDC, ETH).
**Fee Rate**
* Maximum referral fee for all chains: **200 bps**
***
## **MCTP Method**
**Fee Collection**
* Fees are collected on the **destination chain** only if:
1. The bridge includes a **swap on the destination chain**. (e.g., USDC → USDC transfers have no fee)
2. The bridge completes **successfully**.
* Fees are collected in the **Circle-issued stablecoin** minted during bridging (USDC or EURC).
**Fee Rate**
* Maximum referral fee:
* **From Solana:** up to **100 bps**
* **From other chains:** up to **50 bps**
***
## **FastMCTP Method**
**Fee Collection**
* Fees are collected on the **destination chain** only if the bridge is successfully completed.
* Unlike MCTP, the existence of a swap on the destination chain **does not matter**.
* Fees are collected in the **Circle-issued stablecoin** (USDC or EURC).
**Fee Rate**
* Maximum referral fee for all chains: **100 bps**
***
## **WH Method**
**Fee Collection**
* All referral fees are received on the **Solana chain**.
* Fees are only distributed if the **bridge is successfully completed** and **includes a swap**.\
(Example: a bridge from Ethereum → Solana where ETH is swapped to WETH will **not** generate referral fees.)
* The fees are collected in the **output token** and automatically transferred to the **referrer Solana address**.
**Fee Rate**
* The **default referral fee rate** is **10 bps (0.1%)**.
* Referrers can configure their preferred rate from **0 to 50 bps**.
* Use the [Referrer Fee Management](https://explorer.mayan.finance/referrer-fee) page to set your on-chain referral fee.
* You must specify a **valid Solana wallet address** as your referral address.
***
## **Notes**
* For all methods **except WH**, you can set your referral fee rate by including `referrerBps` in the quote request parameters.
* For all methods **except Swift V2 with EVM source chains**, referral fees are **automatically transferred** to the referrer’s address.
***
## **How to Enable Referral Fees in the SDK**
### Step 1 — Prepare Referrer Addresses
You must prepare a referral address for each VM type:
```text theme={null}
const referrerAddresses = {
solana: 'VALID_SOLANA_WALLET_ADDRESS',
evm: 'VALID_EVM_WALLET_ADDRESS',
sui: 'VALID_SUI_WALLET_ADDRESS'
};
```
### Step 2 — During Quote Fetching
* Always pass your **Solana referral address** as the `"referrer"` parameter.
* Set your desired referral fee (in bps) as `"referrerBps"`.
* **Note:** Always use the Solana address as your referrer, regardless of source or destination chain (even for EVM → EVM routes).
```text theme={null}
const quote = await fetchQuote({
...
referrer: referrerAddresses.solana,
referrerBps: 25, // example: 25 bps = 0.25%
...
});
```
### Step 3 — During Transaction Building
All transaction or bridge functions in the SDK accept an optional `referrerAddresses` parameter:
```text theme={null}
type ReferrerAddresses = {
solana: string;
evm: string;
sui: string;
};
```
Always pass the **full object** prepared in Step 1.\
The SDK will automatically determine which wallet to use based on the selected bridge method, source, and destination.
This ensures referral fees are correctly routed, regardless of the bridge type or source/destination chains.
***
Please note that your Solana referrer address must have associated token accounts (ATAs) for USDC, USDT, and WETH. You can create these ATAs by sending a small (dust) amount of each SPL token to your referrer address.
If these ATAs are not initialized, you may lose your referral bps.
SPL token mint addresses:
* USDC: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
* USDT: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
* WETH: 7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs
Please check [Mayan SDK](https://github.com/mayan-finance/swap-sdk) and [widget](/integration/swap-widget) to see how you can set your referrer address
# Swap Widget
Source: https://docs.mayan.finance/integration/swap-widget
You can add the Mayan cross-chain swap widget to your website by including a few lines of code:
(example: [buybonk.com](https://buybonk.com))
```php theme={null}
```
That's it! You now have a fully-functional bridge on your website.
### Customizing Your Widget
We provide a dashboard where you can fully customize your widget, including:
* Supported chains and token lists
* Referrer fees and referrer addresses for monetization
* Colors and overall appearance of the widget
* And more
Please visit the Mayan widget builder: [https://widget.mayan.finance](https://widget.mayan.finance)
### Full Example
For a full example of the integration, you can check the source code of [buybonk.com](https://buybonk.com) on [GitHub](https://github.com/mayan-finance/buybonk). This repository provides a comprehensive example of how to integrate the Mayan Swap Widget into a project.
# Brand Kit
Source: https://docs.mayan.finance/link/brand
# Github
Source: https://docs.mayan.finance/link/github
# Swap SDK
Source: https://docs.mayan.finance/link/swap
# API Reference
Source: https://docs.mayan.finance/payment-service/api-reference
Every MPS REST endpoint, request/response shape, pagination, and the structured error model.
## Base URL
```
https://mps-api.mayan.finance
```
## Authentication
Every authenticated request must include your API key (a UUID) in the `x-api-key` header:
```
x-api-key: your-uuid-api-key
```
`GET /chains` and `GET /source-config` are public and do not require a key. A missing key
returns `401 UNAUTHORIZED`; an invalid key returns `403 FORBIDDEN`.
## Pagination
List endpoints are cursor-paginated by creation time (newest first).
| Query param | Description |
| ----------- | ----------------------------------------------------------- |
| `limit` | Page size. Default `20`, max `100`. |
| `cursor` | ISO-8601 timestamp from a previous response's `nextCursor`. |
Each response includes `nextCursor`; pass it back as `cursor` for the next page. When `nextCursor`
is `null`, there are no more pages.
## Errors
### Error shape
All API errors — plus the `error` field on a swap and on a `status_changed` event — share one
structured shape:
```json theme={null}
{
"code": "VALIDATION_ERROR",
"message": "Human-readable, safe to surface.",
"retryable": false,
"details": { "field": "evm" }
}
```
API error responses wrap it as `{ "error": { ... } }` with an appropriate HTTP status. Always branch
on `code` (a stable enum) rather than `message` (which may be reworded). `details`, when present,
holds only safe, parameterized context (numbers, ids, thresholds) — never raw internal exception
text.
### Error codes
| Code | Retryable | Meaning |
| ---------------------- | --------- | ---------------------------------------------------------------------- |
| `VALIDATION_ERROR` | no | Bad or missing request parameters |
| `UNAUTHORIZED` | no | Missing `x-api-key` |
| `FORBIDDEN` | no | Invalid API key |
| `UNSUPPORTED_CHAIN` | no | Unknown chain id in the request |
| `RESOLVE_FAILED` | yes | Could not fetch/decode the transaction (e.g. RPC) |
| `NOT_CONFIGURED` | no | A server feature is not configured |
| `NOT_FOUND` | no | Resource does not exist |
| `INSUFFICIENT_BALANCE` | yes | Relayer/wallet lacked funds to send the tx |
| `AMOUNT_TOO_SMALL` | yes | Deposit value below what the route/fees allow |
| `FEE_NOT_COVERED` | yes | Collected fee does not cover settlement cost |
| `NO_ROUTE` | yes | No quote/route available for this pair right now |
| `QUOTE_INVALID` | yes | A quote was returned but failed validation |
| `UNSUPPORTED_ROUTE` | no | This source/destination combination is not supported |
| `UNSUPPORTED_DEST` | no | Destination chain is not supported |
| `TX_REVERTED` | yes | An on-chain transaction reverted |
| `SIMULATION_REVERTED` | yes | Pre-flight simulation reverted |
| `RPC_UNAVAILABLE` | yes | RPC timeout / connection / rate-limit |
| `PRICE_UNAVAILABLE` | yes | Token could not be priced |
| `REORG` | yes | A prior on-chain step was re-orged away |
| `CONFIG_MISSING` | yes | A required config is missing (recovers once set) |
| `INTERNAL` | yes | Unclassified failure (message is generic; cause is logged server-side) |
On a swap, `error.retryable` also reflects state: a `dead` (terminal) item always reports
`retryable: false`, because the worker will not try it again regardless of the code.
***
## Get a deposit address
You don't call MPS to mint a deposit address — you get one from the **Mayan Quote API**. Request a
quote with `mpsDeposit: true` and a `destinationAddress`, then read `mpsDepositAddress` off any
eligible quote. The address is deterministic and permanent for a given recipient, and MPS settles
anything sent to it automatically.
See [MPS deposit address on the Quote API](/integration/quote-api#mps-deposit-address-mpsdeposit) for
the request/response shape and eligibility rules.
***
## List deposit addresses
Paginated deposit addresses that have been created for the authenticated integrator (each is minted
the first time you request a quote with `mpsDeposit: true` for a new recipient).
```
GET /deposit-addresses?limit=20&cursor=
```
**Response**
```json theme={null}
{
"items": [
{
"id": "uuid",
"walletAddress": "0x...",
"userId": "0x...",
"destChain": "30",
"destWallet": "0x...",
"destToken": "0x...",
"chainType": "evm",
"createdAt": "2026-04-03T00:00:00.000Z"
}
],
"nextCursor": "2026-04-02T23:59:00.000Z"
}
```
***
## List swaps
Paginated settlement status for your deposit addresses. This is the endpoint to poll for payment
progress. Each item is a settlement request: MPS sweeps the wallet's balance of `tokenAddress` on
`chainId` and delivers it to the destination.
```
GET /swaps?limit=20&cursor=
```
**Response**
```json theme={null}
{
"items": [
{
"id": "uuid",
"status": "failed_swap",
"phase": "swap",
"category": "retrying",
"terminal": false,
"chainId": 6,
"tokenAddress": "0x...",
"amount": "1000000",
"swapTxHash": null,
"error": {
"code": "RPC_UNAVAILABLE",
"message": "Upstream RPC is temporarily unavailable.",
"retryable": true,
"details": { "reason": "fetch failed" }
},
"retryCount": 2,
"walletAddress": "0x...",
"userId": "0x...",
"destChain": "30",
"destWallet": "0x...",
"destToken": "0x...",
"createdAt": "2026-04-03T00:00:00.000Z",
"updatedAt": "2026-04-03T00:01:00.000Z"
}
],
"nextCursor": "2026-04-02T23:59:00.000Z"
}
```
* `chainId` is the **Wormhole chain ID** the funds are on; `tokenAddress` is the token being settled.
* `amount` is a **best-effort snapshot** for display (raw smallest-unit string, or `null` for a
request-indexed settlement). The actual settled amount is the wallet's live balance at swap time.
* `error` is `null` unless the item is failing/deferred — a non-null `error` never means success.
* See [Swap Statuses](/payment-service/statuses) for `status`/`phase`/`category`/`terminal`, and
`swapTxHash` for the settlement transaction (view it on
[Mayan Explorer](https://explorer.mayan.finance/)).
***
## Request index
Trigger settlement for a token the scanner doesn't auto-detect. Only [whitelisted tokens](/payment-service/overview#supported-tokens)
(native + USDC on EVM; USDC/USDT/WETH on Solana) are detected automatically. For any other token, call
this with one of **your deposit addresses** (an `mpsDepositAddress` from a quote), the token, and the
chain — MPS queues a settlement and the worker sweeps the wallet's live balance. If a settlement for
that wallet+token is already active, the call is deduped.
```
POST /request-index
```
**Request body**
| Field | Type | Description |
| --------------- | ------ | ----------------------------------------------------------------------------- |
| `walletAddress` | string | one of your deposit addresses (an `mpsDepositAddress` from a quote) |
| `tokenAddress` | string | the token to index — a contract/mint address, or `"native"` for the gas token |
| `chain` | number | the **Wormhole chain ID** the wallet holds the token on |
**Example**
```bash theme={null}
curl -X POST https://mps-api.mayan.finance/request-index \
-H "Content-Type: application/json" \
-H "x-api-key: $MAYAN_API_KEY" \
-d '{
"walletAddress": "0xff129358605c18388f527d97dbf20e30ea6ddaea",
"tokenAddress": "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7",
"chain": 6
}'
```
**Response**
```json theme={null}
{
"chainId": 6,
"walletAddress": "0xff129358605c18388f527d97dbf20e30ea6ddaea",
"tokenAddress": "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7",
"queued": true,
"queueItemId": "uuid",
"reason": null
}
```
* `queueItemId` is the settlement id (`null` when not queued) — track it via [`GET /swaps`](#list-swaps)
and `status_changed` events.
* When `queued` is `false`, `reason` explains why: `"already_queued"` (a settlement for this
wallet+token is already active — the in-flight one will sweep the new balance too),
`"below_token_min"` (below the token's configured minimum), or `"below_min"` (below the chain's USD
floor).
* Returns `404 NOT_FOUND` if `walletAddress` is not one of your registered deposit addresses.
***
## Chain info
Supported chains with their block-explorer URLs. **No authentication required.**
```
GET /chains
```
**Response**
```json theme={null}
[
{ "chainId": 6, "name": "avalanche", "explorerTxUrl": "https://snowtrace.io/tx/", "kind": "evm" },
{ "chainId": 1, "name": "solana", "explorerTxUrl": "https://solscan.io/tx/", "kind": "svm" }
]
```
`kind` is `"evm"` or `"svm"`. This lists **every** chain (as a destination or explorer target); to
discover which source chains and tokens the scanner actually **detects and settles**, use
[`GET /source-config`](#source-config).
***
## Source config
The **source chains and tokens the scanner auto-detects and settles**, with the minimum deposit
advertised per token. **No authentication required.** Poll this instead of hardcoding a list — the
minimums here are the same ones the deposit gate enforces, so they never drift. Any token *not* listed
can still be settled on demand via [`POST /request-index`](#request-index).
```
GET /source-config
```
**Response**
```json theme={null}
{
"chains": [
{
"chainId": 30,
"name": "base",
"kind": "evm",
"tokens": [
{ "address": "0x0000000000000000000000000000000000000000", "symbol": "BASE", "standard": "native", "minDeposit": "0.0003", "decimals": 18 },
{ "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "symbol": "USDC", "standard": "erc20", "minDeposit": "1", "decimals": 6 }
]
},
{
"chainId": 1,
"name": "solana",
"kind": "svm",
"tokens": [
{ "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "symbol": "USDC", "standard": "spl", "minDeposit": "1", "decimals": 6 },
{ "address": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", "symbol": "USDT", "standard": "spl", "minDeposit": "1", "decimals": 6 },
{ "address": "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs", "symbol": "WETH", "standard": "spl", "minDeposit": "0.0003", "decimals": 8 }
]
}
]
}
```
| Field | Type | Description |
| ------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------- |
| `chains[].chainId` | number | Source **Wormhole chain ID** |
| `chains[].name` | string | Chain name |
| `chains[].kind` | string | `"evm"` or `"svm"` |
| `chains[].tokens[].address` | string | Contract/mint address; the native coin uses the zero address `0x0000…0000` |
| `chains[].tokens[].symbol` | string? | Token symbol, when known (the native coin's symbol is the chain name uppercased) |
| `chains[].tokens[].standard` | string | `"native"`, `"erc20"`, or `"spl"` |
| `chains[].tokens[].minDeposit` | string? | Minimum deposit in the token's own units (human-readable, e.g. `"10"` USDC). Present only when a minimum is configured. |
| `chains[].tokens[].decimals` | number? | Token decimals, paired with `minDeposit` to convert an on-chain (base-unit) balance for comparison. |
A deposit below the advertised minimum is detected but not settled — you still get a
`deposit_detected` event with `queued: false` and `ignoredReason: "below_token_min"` (or
`"below_min"` for the chain-level USD floor). See
[deposit\_detected](/payment-service/events#deposit_detected).
***
## Event replay
Fetch this integrator's events in chronological order — used to catch up after a WebSocket
disconnect. Events are retained for 30 days.
```
GET /events?since=&limit=50&type=status_changed
```
| Query param | Description |
| ----------- | -------------------------------------------------------------------------------------------------- |
| `since` | Return events created strictly after this timestamp. Omit to start from the oldest retained event. |
| `type` | Optional filter: `deposit_detected` or `status_changed`. |
| `limit` | Page size. Default `50`, max `200`. |
**Response** (ascending by time):
```json theme={null}
{
"items": [
{ "id": "uuid", "type": "status_changed", "data": { "...": "..." }, "timestamp": "2026-04-03T00:00:00.000Z" }
],
"nextCursor": "2026-04-03T00:00:00.000Z"
}
```
Page forward with `nextCursor` until it is `null`. See
[Delivery & replay](/payment-service/events#delivery-and-replay) for the full pattern.
# Events
Source: https://docs.mayan.finance/payment-service/events
Real-time Socket.IO events across the settlement lifecycle, plus replay after disconnects.
MPS emits two event types across its whole lifecycle. Consume them live over **Socket.IO**, and catch
up on anything missed after a disconnect with the [`GET /events`](#delivery-and-replay) replay
endpoint.
## Event types
| Type | Trigger |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `deposit_detected` | A deposit was observed on-chain (fires whether or not it gets queued) |
| `status_changed` | A swap moved between [statuses](/payment-service/statuses) — covers deploy/swap/settle progress, **every retry, deferrals, success, and terminal failure** |
`status_changed` is the single lifecycle signal: a completed payment is `status_changed` with
`status: "completed"`, and a freshly deployed wallet rides on the `status_changed` into
`pending_swap` (carrying its deploy `txHash`).
## Socket.IO
The live stream is served over [Socket.IO](https://socket.io) (`bun add socket.io-client`). Connect
to the base URL and pass your API key in the handshake `auth`:
```typescript theme={null}
import * as io from "socket.io-client";
const socket = io.connect("https://mps-api.mayan.finance", {
transports: ["websocket"],
auth: { apiKey: process.env.MAYAN_API_KEY }, // or query: { apiKey } for browser clients
});
socket.on("connect", () => console.log("connected"));
socket.on("connected", ({ integratorName }) => console.log("hello", integratorName));
// Every event arrives on the "event" channel as { type, data, timestamp }.
socket.on("event", (evt) => {
if (evt.type === "status_changed" && evt.data.terminal) {
console.log(evt.data.status, evt.data.queueItemId, evt.data.txHash);
}
});
```
On a successful connection the server emits a `connected` event with your integrator name:
```json theme={null}
{ "integratorName": "your-name" }
```
Socket.IO keeps the connection alive with its own heartbeat and **auto-reconnects** on a drop, so you
don't manage pings yourself. Fan-out is still best-effort — after a reconnect, catch up on anything
missed via [`GET /events`](#delivery-and-replay).
### deposit\_detected
```json theme={null}
{
"type": "deposit_detected",
"data": {
"walletAddress": "0x...",
"chainId": 6,
"tokenAddress": "0x...",
"queued": false,
"ignoredReason": "below_min"
},
"timestamp": "2026-04-03T00:00:00.000Z"
}
```
`queued` tells you whether the deposit entered the settlement queue. If `false`, `ignoredReason`
explains why (`below_token_min`, `below_min`, `no_adapter`, `enqueue_error`) — so a dust/spam deposit
that will never produce a swap is an explicit signal, not a detection with no follow-up. `below_token_min`
is the per-token minimum (see [`GET /source-config`](/payment-service/api-reference#source-config));
`below_min` is the chain-level USD floor.
### status\_changed
```json theme={null}
{
"type": "status_changed",
"data": {
"queueItemId": "uuid",
"chainId": 6,
"tokenAddress": "0x...",
"walletAddress": "0x...",
"status": "failed_swap",
"prevStatus": "swapping",
"phase": "swap",
"category": "retrying",
"terminal": false,
"retryCount": 2,
"txHash": null,
"error": {
"code": "RPC_UNAVAILABLE",
"message": "Upstream RPC is temporarily unavailable.",
"retryable": true,
"details": { "reason": "fetch failed" }
}
},
"timestamp": "2026-04-03T00:00:00.000Z"
}
```
* `queueItemId` is the settlement id (matches [`GET /swaps`](/payment-service/api-reference#list-swaps) `id`).
* `error` is `null` except on failing/deferred states (see the [error model](/payment-service/api-reference#errors)).
* `txHash` carries the wallet-deployment tx on the transition into `pending_swap`, and the
swap/settle tx on the transition into `completed`.
* To react only to final outcomes, filter on `terminal: true` (`completed` = success, `dead` = failure).
## Delivery and replay
Socket.IO fan-out is **live and best-effort**: events are pushed only to currently-connected sockets
and are not buffered per connection. Socket.IO auto-reconnects, but events that fired while you were
disconnected are not resent — **catch up with the replay endpoint** rather than relying on the socket.
```
GET /events?since=&limit=50&type=status_changed
```
Returns your events in **chronological (ascending)** order strictly after `since` (omit `since` to
start from the oldest retained event). Page forward with `nextCursor` until it is `null`.
```typescript TypeScript theme={null}
async function drain(sinceISO?: string) {
let cursor = sinceISO;
while (true) {
const url = new URL("https://mps-api.mayan.finance/events");
url.searchParams.set("limit", "200");
if (cursor) url.searchParams.set("since", cursor);
const { items, nextCursor } = await fetch(url, {
headers: { "x-api-key": process.env.MAYAN_API_KEY! },
}).then((r) => r.json());
for (const evt of items) handleEvent(evt);
if (!nextCursor) break;
cursor = nextCursor;
}
}
```
```python Python theme={null}
import os, requests
def drain(since_iso: str | None = None):
cursor = since_iso
while True:
params = {"limit": 200}
if cursor:
params["since"] = cursor
data = requests.get(
"https://mps-api.mayan.finance/events",
headers={"x-api-key": os.environ["MAYAN_API_KEY"]},
params=params,
).json()
for evt in data["items"]:
handle_event(evt)
if not data["nextCursor"]:
break
cursor = data["nextCursor"]
```
Events are retained for **30 days** and pruned afterwards, so replay only reaches back over the
retention window. Treat events as **idempotent** — dedupe on the event `id` (from `GET /events`),
or on `data.queueItemId` + `data.status`.
# Examples
Source: https://docs.mayan.finance/payment-service/examples
Complete, runnable end-to-end examples — deposit ~2 USDC and watch it settle to the destination over the WebSocket.
These are full, copy-paste examples: get a deposit address from a quote, have a user send **\~2 USDC**,
and watch it settle to the destination over the WebSocket.
## Setup
```bash theme={null}
export MAYAN_API_KEY=your-mps-enabled-api-key
export MPS_URL=https://mps-api.mayan.finance # optional (default) — event tracking
export PRICE_URL=https://price-api.mayan.finance # optional (default) — quotes
export DEST_WALLET=0xYourMerchantWalletOnArbitrum # optional — where you receive USDC on Arbitrum
```
Need a key? Email [support@mayan.finance](mailto:support@mayan.finance) and ask to have **MPS enabled**
on it — the quote returns `mpsDepositAddress` only for MPS-enabled keys.
## Shared listener (Socket.IO)
The event stream is served over **Socket.IO**. The listener below resolves once a settlement is
terminal (`completed` / `dead`) for a given wallet. Both examples import it. Install the client once:
```bash theme={null}
bun add socket.io-client # or: npm i socket.io-client
```
Socket.IO handles the connection for you — a built-in heartbeat keeps it alive through idle gaps and it auto-reconnects on a drop, so
there's no manual keepalive to maintain.
```typescript listen.ts theme={null}
// Connects to the MPS event stream (Socket.IO) and calls `onEvent` for every
// event, then resolves once a settlement reaches a terminal state (completed /
// dead) for the given wallet.
import * as io from "socket.io-client";
export interface SettlementResult {
status: "completed" | "dead" | string;
queueItemId: string;
txHash: string | null;
}
export function watchSettlement(opts: {
mpsUrl: string; // e.g. https://mps-api.mayan.finance
apiKey: string;
walletAddress: string; // resolve when this wallet reaches a terminal status
onEvent?: (evt: any) => void;
}): Promise {
const target = opts.walletAddress.toLowerCase();
return new Promise((resolve, reject) => {
const socket = io.connect(opts.mpsUrl, {
transports: ["websocket"],
extraHeaders: {
'x-api-key': opts.apiKey,
}
});
socket.on("connect", () => console.log("WS connected"));
socket.on("connect_error", (err: Error) => reject(new Error(`WebSocket error: ${err.message}`)));
// Every event arrives on the "event" channel as { type, data, timestamp }.
socket.on("event", (evt: any) => {
opts.onEvent?.(evt);
// Only react to this wallet's terminal transition.
const wallet = (evt.data?.walletAddress ?? "").toLowerCase();
if (evt.type === "status_changed" && evt.data?.terminal && wallet === target) {
socket.disconnect();
resolve({ status: evt.data.status, queueItemId: evt.data.queueItemId, txHash: evt.data.txHash ?? null });
}
});
});
}
```
## Shared quote helper
The deposit address comes from a **Mayan quote** with `mpsDeposit` enabled — not a separate MPS call.
The helper below fetches a quote and returns the `mpsDepositAddress` (the deterministic address on the
quote's source chain). Both examples import it.
```typescript mps-quote.ts theme={null}
// Fetches a Mayan quote with mpsDeposit enabled and returns the MPS deposit
// address on the source chain. Send the input token to it and MPS settles the
// swap to your destination — no signature required.
export async function getMpsDepositAddress(opts: {
priceUrl: string; // e.g. https://price-api.mayan.finance
apiKey: string; // your MPS-enabled Mayan API key
amountIn64: string; // input amount in base units
fromChain: string; // source chain name, e.g. "base" | "solana"
fromToken: string; // input token address/mint
toChain: string; // destination chain name
toToken: string; // destination token address
destinationAddress: string; // final recipient
mpsUserId?: string; // optional integrator-scoped id (else derived from your key)
}): Promise {
const res = await fetch(`${opts.priceUrl}/v3/quote?apiKey=${opts.apiKey}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amountIn64: opts.amountIn64,
fromChain: opts.fromChain,
fromToken: opts.fromToken,
toChain: opts.toChain,
toToken: opts.toToken,
slippageBps: "auto",
destinationAddress: opts.destinationAddress,
mpsDeposit: true,
...(opts.mpsUserId ? { mpsUserId: opts.mpsUserId } : {}),
swift: true, mctp: true, fastMctp: true, monoChain: true, fullList: true,
sdkVersion: "15_1_0",
}),
});
if (!res.ok) throw new Error(`quote failed: ${res.status} ${await res.text()}`);
const { quotes } = await res.json();
// All eligible quotes carry the same address; take the first non-null one.
const address = quotes?.map((q: any) => q.mpsDepositAddress).find(Boolean);
if (!address) {
throw new Error(
"No mpsDepositAddress in quotes — is MPS enabled for your API key, and is the amount above the token's minimum deposit?",
);
}
return address as string;
}
```
## Base (EVM): deposit USDC on Base
The user sends \~2 USDC to the quote's deposit address on Base; it's settled as USDC to your
destination wallet on Arbitrum.
```typescript evm-base.ts theme={null}
import { getMpsDepositAddress } from "./mps-quote";
import { watchSettlement } from "./listen";
const MPS_URL = process.env.MPS_URL || "https://mps-api.mayan.finance";
const PRICE_URL = process.env.PRICE_URL || "https://price-api.mayan.finance";
const API_KEY = process.env.MAYAN_API_KEY;
if (!API_KEY) throw new Error("Set MAYAN_API_KEY");
const USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
const USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"; // USDC on Arbitrum (dest)
const DEST_WALLET = process.env.DEST_WALLET || "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f";
// 1. Get the MPS deposit address from a quote (source = Base).
const depositAddress = await getMpsDepositAddress({
priceUrl: PRICE_URL,
apiKey: API_KEY,
amountIn64: "2000000", // ~2 USDC (6 decimals)
fromChain: "base",
fromToken: USDC_BASE,
toChain: "arbitrum",
toToken: USDC_ARB,
destinationAddress: DEST_WALLET,
});
console.log(`\n➡️ Send ~2 USDC to this address on BASE:\n ${depositAddress}\n`);
console.log("Waiting for the deposit and settlement (Ctrl-C to stop)...\n");
// 2. Watch the event stream until the settlement is terminal.
const result = await watchSettlement({
mpsUrl: MPS_URL,
apiKey: API_KEY,
walletAddress: depositAddress,
onEvent: (evt) => {
if (evt.type === "deposit_detected") {
console.log(`💰 deposit detected — queued=${evt.data.queued}${evt.data.ignoredReason ? ` (${evt.data.ignoredReason})` : ""}`);
} else if (evt.type === "status_changed") {
const err = evt.data.error ? ` [${evt.data.error.code}]` : "";
console.log(` → ${evt.data.status}${err} (retry ${evt.data.retryCount})`);
}
},
});
// 3. Report the outcome.
if (result.status === "completed") {
console.log(`\n✅ Settled — USDC delivered on Arbitrum.`);
if (result.txHash) console.log(` Explorer: https://explorer.mayan.finance/tx/${result.txHash}`);
} else {
console.log(`\n❌ Ended as "${result.status}". Check GET /swaps for the error.`);
}
process.exit(0);
```
Run it, then send \~2 USDC to the printed address:
```bash theme={null}
bun run evm-base.ts
```
Sample output:
```text theme={null}
➡️ Send ~2 USDC to this address on BASE:
0xff129358605c18388f527d97dbf20e30ea6ddaea
WS connected
💰 deposit detected — queued=true
→ pending_deploy (retry 0)
→ deploying (retry 0)
→ pending_swap (retry 0)
→ swapping (retry 0)
→ completed (retry 0)
✅ Settled — USDC delivered on Arbitrum.
Explorer: https://explorer.mayan.finance/tx/0x...
```
## Solana: deposit USDC on Solana
A quote from a Solana source returns a **Solana vault** as its `mpsDepositAddress`. The user sends
\~2 USDC to it on Solana; it's settled as USDC to your destination wallet on Arbitrum.
```typescript solana.ts theme={null}
import { getMpsDepositAddress } from "./mps-quote";
import { watchSettlement } from "./listen";
const MPS_URL = process.env.MPS_URL || "https://mps-api.mayan.finance";
const PRICE_URL = process.env.PRICE_URL || "https://price-api.mayan.finance";
const API_KEY = process.env.MAYAN_API_KEY;
if (!API_KEY) throw new Error("Set MAYAN_API_KEY");
const USDC_SOLANA = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"; // USDC on Arbitrum (dest)
const DEST_WALLET = process.env.DEST_WALLET || "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f";
// 1. Get the MPS deposit address from a quote (source = Solana → a Solana vault).
// The destination can be any Mayan-supported chain; here it's Arbitrum.
const depositAddress = await getMpsDepositAddress({
priceUrl: PRICE_URL,
apiKey: API_KEY,
amountIn64: "2000000", // ~2 USDC (6 decimals)
fromChain: "solana",
fromToken: USDC_SOLANA,
toChain: "arbitrum",
toToken: USDC_ARB,
destinationAddress: DEST_WALLET,
});
console.log(`\n➡️ Send ~2 USDC to this vault on SOLANA:\n ${depositAddress}\n`);
console.log("Waiting for the deposit and settlement (Ctrl-C to stop)...\n");
// 2. Watch the event stream until the settlement is terminal.
const result = await watchSettlement({
mpsUrl: MPS_URL,
apiKey: API_KEY,
walletAddress: depositAddress,
onEvent: (evt) => {
if (evt.type === "deposit_detected") {
console.log(`💰 deposit detected — queued=${evt.data.queued}${evt.data.ignoredReason ? ` (${evt.data.ignoredReason})` : ""}`);
} else if (evt.type === "status_changed") {
const err = evt.data.error ? ` [${evt.data.error.code}]` : "";
console.log(` → ${evt.data.status}${err} (retry ${evt.data.retryCount})`);
}
},
});
// 3. Report the outcome.
if (result.status === "completed") {
console.log(`\n✅ Settled — USDC delivered on Arbitrum.`);
if (result.txHash) console.log(` Explorer: https://explorer.mayan.finance/tx/${result.txHash}`);
} else {
console.log(`\n❌ Ended as "${result.status}". Check GET /swaps for the error.`);
}
process.exit(0);
```
Run it, then send \~2 USDC to the printed vault:
```bash theme={null}
bun run solana.ts
```
Sample output:
```text theme={null}
➡️ Send ~2 USDC to this vault on SOLANA:
9xQe...PnRk
WS connected
💰 deposit detected — queued=true
→ pending_settle (retry 0)
→ settling (retry 0)
→ completed (retry 0)
✅ Settled — USDC delivered on Arbitrum.
Explorer: https://explorer.mayan.finance/tx/...
```
The status chain differs by source chain — EVM runs `pending_deploy → deploying → pending_swap →
swapping → completed`; Solana runs `pending_settle → settling → completed`. See
[Swap Statuses](/payment-service/statuses) for the full lifecycle and the `error` field.
# Payment Service Overview
Source: https://docs.mayan.finance/payment-service/overview
Accept any token on any chain with deterministic deposit addresses — MPS bridges and swaps it to the token and chain you want, automatically.
## What is the Mayan Payment Service?
The **Mayan Payment Service (MPS)** is cross-chain payment infrastructure. You get a
**deterministic deposit address** for each of your users from a [Mayan quote](/integration/quote-api#mps-deposit-address-mpsdeposit);
when they send *any* token on *any* supported chain to that address, MPS automatically detects the
deposit, bridges and swaps it via Mayan Protocol, and delivers the **destination token on the
destination chain** you specified.
It turns "accept a specific token on a specific chain" into "accept anything, settle in what you
want" — ideal for checkout, on-ramps, treasury top-ups, and wallet funding.
MPS is a hosted backend on top of Mayan. You integrate over a simple REST API plus real-time
WebSocket events — no smart-contract work required.
## How it works
Request a [Mayan quote](/integration/quote-api#mps-deposit-address-mpsdeposit) with
`mpsDeposit: true` and a `destinationAddress`, then read `mpsDepositAddress` off an eligible
quote. It's a deterministic address on the source chain of that quote — permanent and reusable
for the same recipient.
Show the address to your user. They send the input token on the source chain — no signature, no
transaction to build.
The scanner detects the deposit and checks its USD value. The worker deploys the user's smart
wallet (first time only), then executes the bridge/swap through Mayan Protocol.
The destination token arrives at the destination wallet on the destination chain, and the swap
reaches `status: "completed"`.
You track every step in real time via [events](/payment-service/events) or by polling
the [`/swaps`](/payment-service/api-reference#list-swaps) endpoint.
The deposit address comes from the **Quote API**: pass `mpsDeposit: true` (with `destinationAddress`)
and read `mpsDepositAddress` off each eligible quote. See
[MPS deposit address on the Quote API](/integration/quote-api#mps-deposit-address-mpsdeposit).
## Key concepts
* **Deterministic & permanent addresses** — the same parameters always produce the same address.
Addresses never expire and can be reused for repeat payments from the same user.
* **One address, many chains** — an EVM deposit address is valid on every supported EVM chain (quote
from an EVM source chain to get it). A Solana source quote returns a Solana vault for SPL /
Token-2022 deposits (USDC, USDT, and WETH are auto-detected).
* **Any token in, one token out** — users pay in whatever they hold; you always receive the
destination token you configured. Common tokens are detected automatically; anything else can be
indexed on demand (see [Supported tokens](#supported-tokens)).
* **Structured, real-time lifecycle** — a single `status_changed` event stream reports progress,
retries, success, and terminal failure, each with a stable
[error model](/payment-service/api-reference#errors).
## Supported chains
MPS accepts **deposits** on a fixed set of source chains, and settles to **any chain Mayan supports**
as the destination. Chains are identified by their **Wormhole chain ID** (the EVM chain ID is shown
for reference only).
### Deposit (source) chains
Users can deposit on these chains:
| Chain | Wormhole chain ID | EVM chain ID | Native token |
| --------- | ----------------- | ------------ | ------------ |
| Solana | 1 | — | SOL |
| Ethereum | 2 | 1 | ETH |
| BSC | 4 | 56 | BNB |
| Polygon | 5 | 137 | POL |
| Avalanche | 6 | 43114 | AVAX |
| Arbitrum | 23 | 42161 | ETH |
| Optimism | 24 | 10 | ETH |
| Base | 30 | 8453 | ETH |
| Monad | 48 | 143 | MON |
### Destination chains
The destination (`destChain`) can be **any chain Mayan supports** — including chains beyond the
deposit set above (e.g. Sui and many more). See
[Mayan's supported chains](https://docs.mayan.finance/) for the full list.
Always pass the **Wormhole chain ID** in `destChain` and read it in `chainId` — e.g. Base is `30`,
not `8453`. Passing an EVM chain ID will produce a different (wrong) deposit address or be rejected.
## Supported tokens
A deposit address can receive **any** token, but MPS's scanner **auto-detects** (and settles with no
action from you) this whitelist per source chain. `Min deposit` is in the token's own units — a
deposit below it is detected but not settled.
| Chain | Token | Contract / mint address | Min deposit |
| --------- | ----- | ---------------------------------------------- | ----------- |
| Solana | USDC | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | 1 |
| Solana | USDT | `Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB` | 1 |
| Solana | WETH | `7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs` | 0.0003 |
| Ethereum | ETH | native | 0.003 |
| Ethereum | USDC | `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` | 10 |
| BSC | BNB | native | 0.002 |
| BSC | USDC | `0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d` | 1 |
| Polygon | POL | native | 2 |
| Polygon | USDC | `0x3c499c542cef5e3811e1192ce70d8cc03d5c3359` | 1 |
| Avalanche | AVAX | native | 0.03 |
| Avalanche | USDC | `0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e` | 1 |
| Arbitrum | ETH | native | 0.0003 |
| Arbitrum | USDC | `0xaf88d065e77c8cc2239327c5edb3a432268e5831` | 1 |
| Optimism | ETH | native | 0.0003 |
| Optimism | USDC | `0x0b2c639c533813f4aa9d7837caf62653d097ff85` | 1 |
| Base | ETH | native | 0.0003 |
| Base | USDC | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` | 1 |
| Monad | MON | native | 1 |
| Monad | USDC | `0x754704bc059f8c67012fed69bc8a327a5aafb603` | 1 |
The table above is a convenience snapshot. The **authoritative, live** list — with the current
per-token minimums — is [`GET /source-config`](/payment-service/api-reference#source-config). Poll it
rather than hardcoding, and surface each token's `minDeposit` to your users before they pay.
**Need another token whitelisted?** Email [support@mayan.finance](mailto:support@mayan.finance) with
the chain and token contract — we add tokens in **under 12 hours, 24/7**. Until then, any other token
can still be settled on demand via
[`POST /request-index`](/payment-service/api-reference#request-index) (MPS sweeps the wallet's balance
and settles it if Mayan can route the token).
## Next steps
Get your first deposit address from a quote and receive events end-to-end.
Every endpoint, request/response shape, and the structured error model.
The full status lifecycle: `phase`, `category`, `terminal`, and retries.
Real-time WebSocket events and replay after disconnects.
## Getting access
You need an **API key** (a UUID) to call the API and connect to the event stream. To receive one,
email [support@mayan.finance](mailto:support@mayan.finance) or reach out to the Mayan team in
[Discord](https://discord.com/invite/MayanFinance).
A web dashboard is also available — log in with your API key to browse generated addresses and
settlement statuses.
# Quickstart
Source: https://docs.mayan.finance/payment-service/quickstart
Get a deposit address from a quote, accept a payment, and receive the settlement — end to end.
This guide takes you from zero to a working cross-chain payment: get a deposit address from a quote,
let a user deposit, and get notified when funds are delivered.
## Prerequisites
* An **API key** (UUID) from the Mayan team — see [Getting access](/payment-service/overview#getting-access).
* The base URL: `https://mps-api.mayan.finance`.
All authenticated requests send your key in the `x-api-key` header.
## 1. Get a deposit address from a quote
Request a [Mayan quote](/integration/quote-api#mps-deposit-address-mpsdeposit) with `mpsDeposit: true`
and a `destinationAddress` (where the funds settle), then read `mpsDepositAddress` from an eligible
quote. It's a deterministic address on the quote's **source** chain — in the example below the payer
funds on Arbitrum and the merchant receives USDC on Base. The address is minted under your API key, so
MPS must be enabled for it.
```bash curl theme={null}
curl -X POST "https://price-api.mayan.finance/v3/quote?apiKey=$MAYAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amountIn64": "2000000",
"fromChain": "arbitrum",
"fromToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
"toChain": "base",
"toToken": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"slippageBps": "auto",
"destinationAddress": "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f",
"mpsDeposit": true,
"swift": true, "mctp": true, "fastMctp": true, "monoChain": true, "fullList": true,
"sdkVersion": "15_0_0"
}'
```
```typescript TypeScript theme={null}
const PRICE_URL = "https://price-api.mayan.finance";
const API_KEY = process.env.MAYAN_API_KEY!;
const res = await fetch(`${PRICE_URL}/v3/quote?apiKey=${API_KEY}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amountIn64: "2000000", // ~2 USDC (6 decimals)
fromChain: "arbitrum",
fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum (source)
toChain: "base",
toToken: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", // USDC on Base (destination)
slippageBps: "auto",
destinationAddress: "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f",
mpsDeposit: true,
swift: true, mctp: true, fastMctp: true, monoChain: true, fullList: true,
sdkVersion: "15_0_0",
}),
});
const { quotes } = await res.json();
// All eligible quotes carry the same address; take the first non-null one.
const depositAddress = quotes.map((q) => q.mpsDepositAddress).find(Boolean);
console.log(depositAddress); // address on Arbitrum (the source chain)
```
```python Python theme={null}
import os, requests
PRICE_URL = "https://price-api.mayan.finance"
API_KEY = os.environ["MAYAN_API_KEY"]
res = requests.post(
f"{PRICE_URL}/v3/quote",
params={"apiKey": API_KEY},
json={
"amountIn64": "2000000", # ~2 USDC (6 decimals)
"fromChain": "arbitrum",
"fromToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", # USDC on Arbitrum (source)
"toChain": "base",
"toToken": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", # USDC on Base (destination)
"slippageBps": "auto",
"destinationAddress": "0xEab4Fb2De5a05Ba392eB2614bd2293592d455A4f",
"mpsDeposit": True,
"swift": True, "mctp": True, "fastMctp": True, "monoChain": True, "fullList": True,
"sdkVersion": "15_0_0",
},
)
quotes = res.json()["quotes"]
deposit_address = next((q["mpsDepositAddress"] for q in quotes if q.get("mpsDepositAddress")), None)
print(deposit_address) # address on Arbitrum (the source chain)
```
**Response** (abridged — one quote shown):
```json theme={null}
{
"quotes": [
{
"type": "SWIFT",
"mpsDepositAddress": "0xafb7e27de13e435a7f1d1a21d33b26edff17bc8b",
"mpsIntegratorId": "12",
"mpsUserId": "0x3a1f…",
"expectedAmountOut": 1.999
}
]
}
```
Read `mpsDepositAddress` from any eligible quote (`SWIFT`, `MONO_CHAIN`, or a direct `FAST_MCTP`) —
they all share the same address. Quotes that aren't eligible return `mpsDepositAddress: null`.
The address is deterministic and permanent for a given recipient + `mpsUserId` — requesting another
quote returns the same address, so it's safe to call on every checkout. It's `null` unless MPS is
enabled for your key, `destinationAddress` is set, and the amount clears the token's
[minimum](/payment-service/api-reference#source-config).
## 2. Let the user deposit
Show `mpsDepositAddress` to your user and have them send the input token on the **source** chain
(Arbitrum, in the example above). MPS detects the deposit and settles it to your destination token
automatically — no signature, no transaction to build.
* A quote from an **EVM** source chain returns an EVM address (valid on that chain).
* A quote from **Solana** returns a Solana vault for SPL / Token-2022 deposits (USDC, USDT, WETH).
Whitelisted tokens — the native coin and **USDC** on EVM, and **USDC / USDT / WETH** on Solana
— are detected automatically. For any other token, trigger indexing yourself with
[`POST /request-index`](/payment-service/api-reference#request-index). See
[Supported tokens](/payment-service/overview#supported-tokens).
## 3. Receive the settlement
You have two ways to track a payment — use whichever fits your stack:
The stream is served over **Socket.IO** (`bun add socket.io-client`). Connect and receive
`deposit_detected` and `status_changed` events as they happen.
```typescript theme={null}
import * as io from "socket.io-client";
const socket = io.connect("https://mps-api.mayan.finance", {
transports: ["websocket"],
auth: { apiKey: API_KEY },
});
socket.on("event", (evt) => {
if (evt.type === "status_changed" && evt.data.terminal) {
// completed = success, dead = gave up
console.log(evt.data.status, evt.data.queueItemId, evt.data.txHash);
}
});
```
See [Events](/payment-service/events) for the full payloads and reconnection/replay guidance.
Poll [`GET /swaps`](/payment-service/api-reference#list-swaps) and read each item's `status`,
`terminal`, and `error`. A payment is delivered when `status` is `completed`.
```bash theme={null}
curl "https://mps-api.mayan.finance/swaps?limit=20" -H "x-api-key: $MAYAN_API_KEY"
```
A payment is **done** when a swap reaches `status: "completed"` (`terminal: true`, `category: "success"`).
Most transfers settle in seconds. See [Swap Statuses](/payment-service/statuses) for the full lifecycle.
## Full example: e-commerce checkout
Map your order ID to a stable `mpsUserId` so the same order always maps to the same deposit address,
then watch for the terminal event.
```typescript TypeScript theme={null}
import { createHash } from "node:crypto";
const PRICE_URL = "https://price-api.mayan.finance";
const API_KEY = process.env.MAYAN_API_KEY!;
const USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"; // payer deposits USDC on Arbitrum
const USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // merchant receives USDC on Base
// Map any order id to a stable 20-byte mpsUserId (sha256 → first 20 bytes).
const orderIdToUserId = (orderId: string) =>
"0x" + createHash("sha256").update(orderId).digest("hex").slice(0, 40);
export async function createPaymentAddress(orderId: string, merchantWallet: string) {
const res = await fetch(`${PRICE_URL}/v3/quote?apiKey=${API_KEY}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amountIn64: "2000000", // ~2 USDC
fromChain: "arbitrum",
fromToken: USDC_ARB,
toChain: "base",
toToken: USDC_BASE,
slippageBps: "auto",
destinationAddress: merchantWallet,
mpsDeposit: true,
mpsUserId: orderIdToUserId(orderId),
swift: true, mctp: true, fastMctp: true, monoChain: true, fullList: true,
sdkVersion: "15_0_0",
}),
});
const { quotes } = await res.json();
const depositAddress = quotes.map((q: any) => q.mpsDepositAddress).find(Boolean);
if (!depositAddress) throw new Error("MPS deposit address unavailable — enable MPS on your key and check the token minimum.");
return { depositAddress };
}
```
```python Python theme={null}
import os, hashlib, requests
PRICE_URL = "https://price-api.mayan.finance"
API_KEY = os.environ["MAYAN_API_KEY"]
USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" # payer deposits USDC on Arbitrum
USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" # merchant receives USDC on Base
# Map any order id to a stable 20-byte mpsUserId (sha256 → first 20 bytes).
def order_id_to_user_id(order_id: str) -> str:
return "0x" + hashlib.sha256(order_id.encode()).hexdigest()[:40]
def create_payment_address(order_id: str, merchant_wallet: str):
res = requests.post(
f"{PRICE_URL}/v3/quote",
params={"apiKey": API_KEY},
json={
"amountIn64": "2000000", # ~2 USDC
"fromChain": "arbitrum",
"fromToken": USDC_ARB,
"toChain": "base",
"toToken": USDC_BASE,
"slippageBps": "auto",
"destinationAddress": merchant_wallet,
"mpsDeposit": True,
"mpsUserId": order_id_to_user_id(order_id),
"swift": True, "mctp": True, "fastMctp": True, "monoChain": True, "fullList": True,
"sdkVersion": "15_0_0",
},
)
quotes = res.json()["quotes"]
deposit_address = next((q["mpsDepositAddress"] for q in quotes if q.get("mpsDepositAddress")), None)
if not deposit_address:
raise RuntimeError("MPS deposit address unavailable")
return {"depositAddress": deposit_address}
```
Show the deposit address to the customer. They send \~2 USDC on Arbitrum; the merchant receives USDC on
Base, and you get a `status_changed` → `completed` event to fulfil the order.
Deposits below the per-chain minimum USD value are detected but not settled. You still receive a
`deposit_detected` event with `queued: false` and `ignoredReason: "below_min"`, so it's never a
silent gap.
## Watch a payment end-to-end
Get an address from a quote (step 1), then subscribe to the event stream (**Socket.IO**) and wait
until the settlement is terminal. This is the snippet most integrations start from — it prints each
event and resolves on `completed` (success) or `dead` (gave up).
```typescript TypeScript theme={null}
import * as io from "socket.io-client"; // bun add socket.io-client
const MPS_URL = "https://mps-api.mayan.finance";
const API_KEY = process.env.MAYAN_API_KEY!;
function watchSettlement(walletAddress: string) {
const target = walletAddress.toLowerCase();
return new Promise<{ status: string; txHash: string | null }>((resolve) => {
const socket = io.connect(MPS_URL, { transports: ["websocket"], auth: { apiKey: API_KEY } });
socket.on("event", (evt) => {
if (evt.type === "deposit_detected") console.log("deposit detected, queued:", evt.data.queued);
if (evt.type === "status_changed" && evt.data.walletAddress.toLowerCase() === target) {
console.log("→", evt.data.status, evt.data.error?.code ?? "");
if (evt.data.terminal) {
socket.disconnect();
resolve({ status: evt.data.status, txHash: evt.data.txHash });
}
}
});
});
}
// const { depositAddress } = await createPaymentAddress(orderId, merchantWallet); // from the checkout example
const result = await watchSettlement(depositAddress);
console.log(result.status === "completed" ? `✅ delivered: ${result.txHash}` : `❌ ${result.status}`);
```
```python Python theme={null}
# pip install "python-socketio[client]"
import os, socketio
MPS_URL = "https://mps-api.mayan.finance"
API_KEY = os.environ["MAYAN_API_KEY"]
def watch_settlement(wallet_address: str):
target = wallet_address.lower()
sio = socketio.Client()
result = {}
@sio.on("event")
def on_event(evt):
if evt["type"] == "deposit_detected":
print("deposit detected, queued:", evt["data"]["queued"])
if evt["type"] == "status_changed" and evt["data"]["walletAddress"].lower() == target:
print("→", evt["data"]["status"], (evt["data"].get("error") or {}).get("code", ""))
if evt["data"]["terminal"]:
result.update(status=evt["data"]["status"], txHash=evt["data"].get("txHash"))
sio.disconnect()
sio.connect(MPS_URL, transports=["websocket"], auth={"apiKey": API_KEY})
sio.wait()
return result
# deposit_address = create_payment_address(order_id, merchant_wallet)["depositAddress"] # from checkout
result = watch_settlement(deposit_address)
print("✅" if result["status"] == "completed" else "❌", result["status"], result.get("txHash"))
```
Full runnable examples (deposit **\~2 USDC** on Base or Solana → receive USDC on Arbitrum) are on the
[Examples](/payment-service/examples) page.
# Swap Statuses
Source: https://docs.mayan.finance/payment-service/statuses
The full settlement lifecycle — phase, category, terminal, retries, and how to react to each state.
Every deposit that enters the settlement queue moves through a status machine. The current state is
on each [`/swaps`](/payment-service/api-reference#list-swaps) item and on every
[`status_changed`](/payment-service/events#status_changed) event.
## Don't hardcode status strings
Depending on the source chain, a deposit flows through one of two machines — **EVM** (deploy → swap)
or **Solana** (settle). So each item carries three fields that let you react to *meaning* rather than
memorizing every string:
* **`phase`** — which leg of the lifecycle: `deploy` | `swap` | `settle` | `done`
* **`category`** — the integrator-facing meaning, independent of chain:
* `waiting` — queued, will be picked up automatically
* `active` — a transaction is in flight right now
* `retrying` — the last attempt failed or was deferred; will retry after backoff
* `success` — terminal, funds delivered
* `failed` — terminal, gave up
* **`terminal`** — `true` once no further automatic transition will happen (`completed` or `dead`)
The simplest integration: **watch for `terminal: true`** — `completed` is success, `dead` is
failure. Everything else is in-progress.
## Status reference
| Status | Chain | phase | category | terminal | Description |
| ---------------- | ------ | ------ | ----------- | -------- | --------------------------------------------------------------------- |
| `pending_deploy` | EVM | deploy | waiting | no | Queued; waiting for smart-wallet deployment |
| `deploying` | EVM | deploy | active | no | Wallet deployment transaction in flight |
| `pending_swap` | EVM | swap | waiting | no | Wallet deployed; waiting for swap execution |
| `swapping` | EVM | swap | active | no | Swap transaction in flight |
| `failed_deploy` | EVM | deploy | retrying | no | Deployment attempt failed; will retry after backoff |
| `failed_swap` | EVM | swap | retrying | no | Swap attempt failed; will retry after backoff |
| `pending_settle` | Solana | settle | waiting | no | Queued; waiting for settlement |
| `settling` | Solana | settle | active | no | Settlement transactions in flight |
| `deferred` | Solana | settle | retrying | no | Temporarily blocked (e.g. no quote yet, amount too small); will retry |
| `failed_settle` | Solana | settle | retrying | no | Settlement attempt failed; will retry after backoff |
| `completed` | both | done | **success** | **yes** | Funds delivered to the destination |
| `dead` | both | done | **failed** | **yes** | Gave up after exhausting retries (or a non-retryable error) |
## Happy paths
```
EVM: pending_deploy → deploying → pending_swap → swapping → completed
Solana: pending_settle → settling → completed
```
## The `error` field
When a swap is in a `retrying` state (or `dead`), its `error` is a
[structured error](/payment-service/api-reference#errors):
```json theme={null}
{ "code": "NO_ROUTE", "message": "No route available for this pair right now.", "retryable": true }
```
* `error` is `null` on any non-failing state. A **non-null `error` never means success** — success is
always `status: "completed"` with `error: null`.
* Branch on `error.code` (stable), not on `message`.
* `error.retryable` reflects whether the worker will try again. A `dead` item always reports
`retryable: false`.
## Retries & backoff
Failed attempts are retried automatically with exponential backoff:
```
5s → 15s → 30s → 45s → 1m → 5m → 15m
```
After **7** attempts — or immediately for a non-retryable error such as `UNSUPPORTED_DEST` — the item
becomes `dead`. Every attempt emits a `status_changed` event carrying the incremented `retryCount`
and the current `error`, so you can surface retry progress in real time.
`deferred` (Solana) is not a failure — it means settlement is temporarily blocked (e.g. no quote
yet, or the accumulated balance is still too small) and will be retried. It shares the same
backoff and `dead` cap as the failing states.
## Reacting to states
```typescript TypeScript theme={null}
function onSwapUpdate(s: { status: string; terminal: boolean; category: string; error?: { code: string } | null }) {
if (!s.terminal) return; // still in progress
if (s.status === "completed") {
fulfilOrder(); // success
} else {
// status === "dead"
flagForReview(s.error?.code); // permanent failure
}
}
```
```python Python theme={null}
def on_swap_update(s: dict) -> None:
if not s["terminal"]:
return # still in progress
if s["status"] == "completed":
fulfil_order() # success
else: # "dead"
flag_for_review((s.get("error") or {}).get("code"))
```
# Security & Audits
Source: https://docs.mayan.finance/resources/audits
Information about Mayan security and smart contract audits.
Mayan smart contracts and Solana programs place a strong emphasis on security. Our smart contracts have undergone thorough audits by leading security firms, including [**OtterSec**](https://osec.io/) and [**Sec3**](https://www.sec3.dev/), ensuring robust protection of user assets and protocol reliability.
Mayan operates on trustless, permissionless smart contracts, meaning no single party has control over or can freeze assets — guaranteeing true user sovereignty.
The protocol benefits from Wormhole’s secure and decentralized messaging network, which validates cross-chain messages with high reliability.