# Custom Tokens

Accept any ERC-20 token as payment by adding it to your custom token list.

***

## Overview

By default, MakaPay supports a set of built-in tokens (USDT, USDC, etc.) on each network. **Custom tokens** let you go beyond that list and accept any ERC-20 token that has a valid contract on a supported network.

### Supported Networks

Custom tokens are available on the following networks:

| Network       | Alchemy Price Support | Custom Price Required?                     |
| ------------- | --------------------- | ------------------------------------------ |
| **Ethereum**  | Yes                   | Only if Alchemy has no price               |
| **Polygon**   | Yes                   | Only if Alchemy has no price               |
| **BSC**       | Yes                   | Only if Alchemy has no price               |
| **Base**      | Yes                   | Only if Alchemy has no price               |
| **MakaChain** | No                    | Always (use TrustedOracle or manual price) |

**Why not MakaChain?** MakaPay uses the Alchemy Prices API to automatically fetch real-time USD prices for tokens. Alchemy does not support MakaChain, so tokens on MakaChain must rely on the TrustedOracle or a manually provided custom price.

***

## Adding a Custom Token

### Step 1: Open the Payment Link Form

1. Go to **Dashboard > Payments**
2. Click **Create Payment** to open the payment link creation form

### Step 2: Select a Network

Choose the network where your custom token is deployed (Ethereum, Polygon, BSC, or Base).

### Step 3: Open the Add Custom Token Modal

1. Click the **Token** dropdown
2. Scroll to the bottom of the token list
3. Click **"+ Add Custom Token"**

A dialog titled "Add Custom Token" will appear.

### Step 4: Enter the Token Contract Address

1. Paste the ERC-20 contract address into the **Token Contract Address** field (starts with `0x...`)
2. Click the **Lookup** button

The system will:

* Validate that the address is a valid Ethereum address
* Query the contract on-chain for its name, symbol, and decimals
* Check the Alchemy Prices API for an available USD price
* Display a token metadata card showing the token's logo, name, symbol, and decimal count

### Step 5: Review Token Metadata

After lookup, you will see a card displaying:

* **Token logo** (if available) or a colored placeholder circle
* **Symbol** and **Name** (e.g., "PEPE PepeCoin")
* **Decimals** (e.g., 18 decimals)
* **Price status**: Either "Current price: $X.XX (auto-updated)" in green, or a warning that no automatic price is available

### Step 6: Provide a Custom Price (If Required)

If the token has **no Alchemy price**, you will see an amber warning:

> "No automatic price available for this token. You must provide a USD price for accurate fee calculation. Update this price if the token value changes."

Enter the current USD price per token in the **Price (USD)** field (e.g., `0.00001234`). This value must be a positive number.

If the token **does** have an Alchemy price, the price field is not shown -- the system will use the auto-updated price.

### Step 7: Save

Click **"Add \[TOKEN\_SYMBOL]"** to save the token. It will now appear in your token dropdown under a **"Custom Tokens"** section with a **"Custom"** badge.

***

## Price Resolution

When you create a payment with a custom token, MakaPay resolves its USD price using a three-step waterfall:

### 1. Alchemy Prices API (Automatic)

For Ethereum, Polygon, BSC, and Base, MakaPay queries the Alchemy Prices API using the token's contract address and chain. If Alchemy returns a valid, non-zero price, it is used automatically. No action is required from you.

### 2. TrustedOracle (Automatic)

If Alchemy has no price, MakaPay checks the TrustedOracle smart contract on MakaChain. The oracle stores prices by token symbol. If a matching symbol is found with a non-zero price, it is used.

### 3. Custom Price USD (Manual Fallback)

If neither Alchemy nor the TrustedOracle returns a price, MakaPay uses the `customPriceUsd` value you provided when adding the token (or when creating the payment via API). If no custom price exists either, the payment creation will fail.

### Price Source in Payment Records

Every payment stores its `priceSource` field, which can be one of:

| Source           | Meaning                                    |
| ---------------- | ------------------------------------------ |
| `alchemy`        | Price from Alchemy Prices API              |
| `trusted_oracle` | Price from TrustedOracle on MakaChain      |
| `custom`         | Price provided manually by the merchant    |
| `stablecoin`     | Token is a known stablecoin; assumed $1.00 |

***

## Custom Price USD

### When Is It Needed?

You must provide a custom USD price when:

* The token is **not listed** on Alchemy's pricing index (most small-cap or newly deployed tokens)
* The **TrustedOracle** does not have a price entry for the token's symbol
* You are using a token on **MakaChain**, which is not covered by Alchemy

### How to Set It

**In the dashboard**: Enter the price in the **"Price (USD)"** field when adding a custom token, or in the **"Token Price (USD)"** field that appears on the payment form when a custom token without automatic pricing is selected.

**Via API**: Include the `customPriceUsd` field in the payment creation request body (see [API Reference](#api-reference) below).

### Updating the Price

If the token's market value changes significantly, update the stored price:

1. Remove the existing custom token from your list
2. Re-add it with the updated price

Alternatively, provide an updated `customPriceUsd` directly in each API payment request -- this overrides the stored value.

### Volatility Warning

Custom prices are **static snapshots**. They do not update automatically. If you accept a volatile token, the price you set when creating the payment may be outdated by the time the customer pays.

**Recommendation**: For volatile custom tokens, use a **1-hour expiration** on payment links. This limits the window during which the token's price could drift significantly from the value used to calculate fees and amounts.

***

## Fee Calculation for Custom Tokens

Custom tokens use the **same fee structure** as all other tokens (platform fee, processing fee, gas fee). The difference is in how the non-percentage fees are converted to token units.

### How It Works

For **stablecoins** (USDT, USDC, etc.), the system assumes 1 token = $1 USD, so a $0.10 processing fee equals 0.10 tokens.

For **custom tokens**, the system converts USD fees to token units using the resolved token price:

```
Fee in token units = Fee in USD / Token Price in USD
```

### Example: Token Worth $0.05 Each

| Fee Component     | USD Value    | Token Units         |
| ----------------- | ------------ | ------------------- |
| Platform Fee (1%) | 1% of amount | 1% of amount (same) |
| Processing Fee    | $0.10        | 2.00 tokens         |
| Gas Fee (est.)    | $0.03        | 0.60 tokens         |

**Platform fee** is always a percentage of the requested amount, so it works identically regardless of token price.

**Processing fee** and **gas fee** are fixed USD amounts that must be converted using the token's price. If the token is worth $0.05, then $0.10 = 2.00 tokens.

### Fee Modes

All three fee modes (Gas Tank, Gasless, Payer Covers Fee) work with custom tokens:

| Mode                 | Behavior with Custom Tokens                                                                                       |
| -------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Gas Tank**         | Fees deducted from your Gas Tank in USDT. Token price is used to calculate the USDT equivalent.                   |
| **Gasless**          | Fees deducted on-chain from the payment amount in the custom token. Token price converts USD fees to token units. |
| **Payer Covers Fee** | Fees added on top of the payment. Customer sends extra tokens to cover fees.                                      |

### Gasless Restriction

Gasless mode requires the system to know the token's USD price so it can calculate how many tokens to deduct as fees. If a custom token has **no price available** (no Alchemy price, no oracle price, and no custom price), MakaPay automatically disables gasless mode and switches to Gas Tank mode.

***

## Expiration Recommendations

| Token Type                                      | Recommended Expiration    |
| ----------------------------------------------- | ------------------------- |
| Stablecoins (USDT, USDC)                        | 24 hours or no expiration |
| Major tokens with Alchemy price (WETH, WBTC)    | 24 hours                  |
| Volatile custom tokens (meme coins, new tokens) | **1 hour**                |
| Tokens with a custom price (no auto-update)     | **1 hour**                |

For tokens with a manually set custom price, the price does not update between payment creation and settlement. A shorter expiration window reduces the risk of a significant price change making the fee calculation inaccurate.

***

## POS Custom Tokens

Custom tokens are fully supported in the Point of Sale (POS) flow.

### How It Works

1. When you open the POS and select a network, the POS loads both built-in tokens and your custom tokens for that network
2. Custom tokens appear alongside built-in tokens in the token selection grid
3. Each custom token displays a **"Custom"** badge to distinguish it from built-in tokens
4. When you select a custom token, the POS fetches its current price to calculate the equivalent token amount for the USD payment

### Price Calculation in POS

For non-stablecoin custom tokens, the POS:

1. First tries the oracle price API (`/api/oracle/price`)
2. Falls back to the token metadata API (`/api/tokens/metadata`) which queries Alchemy for the current price
3. Displays the amount as "Approx" rather than "Equals" since the price may fluctuate

If no price can be resolved, the POS shows an error and does not allow continuing to payment.

***

## Troubleshooting

### "Invalid contract address"

**Cause**: The address you entered is not a valid Ethereum address format.

**Fix**: Ensure the address starts with `0x` and is exactly 42 characters long (including the `0x` prefix). Copy the address directly from a block explorer (Etherscan, Polygonscan, BscScan, or Basescan).

### "Cannot read token contract"

**Cause**: The address does not point to a valid ERC-20 contract on the selected network, or the contract does not implement the standard `decimals()` function.

**Fix**:

* Verify the contract address is correct
* Confirm you selected the **correct network** (e.g., if the token is on Polygon, do not try to add it on Ethereum)
* Check that the contract is a standard ERC-20 token by viewing it on a block explorer

### "No automatic price available for this token"

**Cause**: Alchemy does not have pricing data for this token. This is common for small-cap tokens, newly deployed contracts, or tokens on MakaChain.

**Fix**: Enter a USD price manually in the **"Price (USD)"** field. You can find the token's current market price on CoinGecko, CoinMarketCap, or a DEX aggregator.

### "This token is already in your custom tokens list"

**Cause**: You already added this exact token (same contract address, same chain).

**Fix**: The token is already available in your token dropdown. Look for it in the **"Custom Tokens"** section.

### "Price is required for this token"

**Cause**: You tried to save a custom token that has no automatic price without entering a custom USD price.

**Fix**: Enter the token's current USD price in the **"Price (USD)"** field before saving.

### Wrong Decimal Display

**Cause**: The token's `decimals()` function returns a non-standard value.

**Fix**: MakaPay reads decimals directly from the smart contract. If the displayed decimal count looks wrong, the contract itself may have a non-standard implementation. Verify on a block explorer that the token uses standard ERC-20 decimals.

### Fees Exceed Payment Amount

**Cause**: For very low-value tokens, the converted fee amounts (in token units) can exceed the payment amount itself, especially in gasless mode.

**Fix**: Either increase the payment amount or enable **"Payer Covers Fee"** so fees are added on top rather than deducted from the payment.

***

## API Reference

### Creating a Payment with a Custom Token

Use the standard `POST /payments` endpoint. For custom tokens, include the `customPriceUsd` field:

```bash
curl -X POST https://app.makapay.io/api/payments \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant": "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00",
    "chainId": "137",
    "token": "0xYOUR_CUSTOM_TOKEN_CONTRACT_ADDRESS",
    "amount": "1000.00",
    "orderId": "order-custom-1",
    "customPriceUsd": "0.00001234",
    "gasless": false,
    "payerCoversFee": false
  }'
```

### `customPriceUsd` Field

| Field            | Type   | Required | Description                                                                                                                                                        |
| ---------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `customPriceUsd` | string | No       | USD price per token (e.g., `"0.00001234"`). Used as a fallback if Alchemy and TrustedOracle have no price. **Required** for tokens with no automatic price source. |

### Behavior

* If the token has an Alchemy price, `customPriceUsd` is ignored (Alchemy takes priority)
* If the token has a TrustedOracle price but no Alchemy price, the oracle price is used and `customPriceUsd` is ignored
* If neither automatic source has a price, `customPriceUsd` is used
* If no price is available from any source and the token is not a stablecoin, gasless mode is automatically disabled (falls back to Gas Tank mode)

### Response

The response is the same as any other payment creation -- a `url` field with the payment page link:

```json
{
  "url": "https://app.makapay.io/pay/01234567-89ab-cdef-0123-456789abcdef"
}
```

***

## Contact

Questions about custom tokens?

* Email: <support@makapay.io>
* Dashboard: Use the support chat


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.makapay.io/guides/custom-tokens.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
