> For the complete documentation index, see [llms.txt](https://docs-swap.crypto-chief.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-swap.crypto-chief.com/guides/processing-wallet.md).

# Swap from a Processing Wallet

## Swap from a Processing Wallet

The Swap API builds the steps. Crypto Processing signs each step with a wallet of your project and broadcasts it: [Sign Transaction](https://docs-processing.crypto-chief.com/blockchain-data/sign-transaction) and [Execute Transaction](https://docs-processing.crypto-chief.com/blockchain-data/execute-transaction). The private key stays on the platform.

Works on EVM networks, TRON, TON and Solana. On XRP Ledger the Processing API does not sign the swap's `tx_json`; sign it with your own key ([Signing on Each Network](/guides/signing.md#xrp-ledger)).

You need:

* `CC_MERCHANT_ID` — your project UUID, the Merchant ID from **Project Settings → Integration**. It is the Swap API key and the `Merchant` of the Processing API;
* `CC_API_KEY` — the project's secret API key. It signs Processing API requests; never put it in a URL;
* `CC_WALLET` — the address of a wallet of this project (master, static deposit or transit). It must hold the input token and the native coin for network fees: on this flow the platform does not top the wallet up.

```bash
npm install @cryptochiefs/cryptochief-crypto-processing-node
```

```javascript
import { CryptoChiefClient, TxType, TxStatus } from "@cryptochiefs/cryptochief-crypto-processing-node";

const MERCHANT_ID = process.env.CC_MERCHANT_ID;
const WALLET = process.env.CC_WALLET;
const processing = new CryptoChiefClient({ merchantId: MERCHANT_ID, apiKey: process.env.CC_API_KEY });

// Swap API call with retries for 429, 502, 503, 504 (as in Your First Swap).
async function swap(operation, body) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(`https://swap-api.crypto-chief.com/${MERCHANT_ID}/v1/${operation}`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body ?? {}),
    });
    const data = await res.json().catch(() => ({}));
    const out = { status: res.status, ...data };
    if (![429, 502, 503, 504].includes(out.status) || attempt >= 3) return out;
    await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
  }
}

const params = {
  network: "ETH_MAINNET",
  token_in: "USDC",
  token_out: "NATIVE",
  amount_in: "1000000000", // 1000 USDC, base units
  from_address: WALLET,
  slippage_bps: 50,
};

// Sign one step with the project wallet, send it, wait for the final status.
async function runStep(step) {
  const signed = await processing.transactions.sign({
    network: params.network,
    fromAddress: WALLET,
    type: TxType.Contract,
    calls: step.calls, // exactly as the build returned them
  });
  if (params.network.startsWith("SOLANA") && Buffer.from(signed.signedTxHex, "hex").length > 1232) {
    // Not sent; the signature expires by itself.
    throw new Error("the route does not fit one transaction: rebuild with a lower max_hops or max_splits");
  }
  // TRON signatures live 45 s, Solana 25 s: execute right away.
  const sent = await processing.transactions.execute({ uuid: signed.uuid });
  console.log(`${step.kind} sent: ${sent.txHash}`);
  const done = await processing.transactions.waitFor(signed.uuid, { intervalMs: 15_000, timeoutMs: 30 * 60_000 });
  if (done.status !== TxStatus.Confirmed) {
    throw new Error(`${step.kind} ${done.status}: ${done.errorReason ?? ""}`);
  }
}

const quote = await swap("quote", params);
if (quote.error) throw new Error(`${quote.status} ${quote.error}: ${quote.message}`);
console.log(`Expecting ${quote.amount_out_human}, at least ${quote.amount_out_min_human}`);
if (params.network.startsWith("SOLANA") && quote.fees.network.details.tx_version !== "legacy") {
  throw new Error("the route needs address lookup tables: the platform cannot sign it");
}

// On Solana build right away: the quote's route is kept for 5 s.
let build = await swap("build", { ...params, quote_id: quote.quote_id });
if (build.error) throw new Error(`${build.status} ${build.error}: ${build.message}`);
if (params.network.startsWith("SOLANA") && build.quote_id !== quote.quote_id) {
  throw new Error("the build searched a new route: quote again");
}

let i = 0;
let approved = false;
while (i < build.steps.length) {
  const step = build.steps[i];
  if (step.rebuild_after_previous && i > 0) {
    build = await swap("build", params);
    if (build.error) throw new Error(`${build.status} ${build.error}: ${build.message}`);
    i = 0;
    continue;
  }
  if (step.kind === "approve" && approved) throw new Error("approval not visible yet: rebuild in a few seconds");
  if (step.kind === "swap" && build.simulation.status !== "passed") {
    throw new Error(`simulation ${build.simulation.status}: ${build.simulation.reason ?? ""}`);
  }
  await runStep(step);
  if (step.kind === "approve") approved = true;
  i++;
}
console.log("Swap complete.");
```

The loop is the one from [Your First Swap](/guides/first-swap.md); only the signing changed. `step.calls` goes to Sign Transaction as is, in every family.

## What changes per network

| Network    | What the platform does when signing                                                                                                                                  | Limits                                                                                                                                                                 |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| EVM        | Gas limit from `eth_estimateGas` plus 20 %, EIP-1559 fees with a cap of about 3× the base fee; a call that reverts is refused at Sign with `SIMULATION_REVERTED`     | One unsent signature per address: execute a step before signing the next. Signature valid 10 min                                                                       |
| TRON       | Simulates the call and sets `fee_limit` from it, at most 100 TRX. Energy the wallet already has (staked, delegated or rented) is used first; the rest is paid in TRX | Signature valid 45 s                                                                                                                                                   |
| TON        | Signs a V4R2 wallet message with the step's body and attached TON                                                                                                    | Signature valid 5 min                                                                                                                                                  |
| Solana     | Builds a legacy transaction from the step's instructions, without address lookup tables, with the compute budget the build set                                       | The signed transaction must fit 1232 bytes: use a quote with `fees.network.details.tx_version` `legacy` and build with its `quote_id` within 5 s. Signature valid 25 s |
| XRP Ledger | Not supported                                                                                                                                                        | —                                                                                                                                                                      |

On TRON and TON a transaction that fails in a block stays `broadcasted`: `waitFor` then ends with a timeout. Check its `tx_hash` in a block explorer.

## Before Execute

Execute checks the wallet's native balance against the signed transaction's network fee and attached value. On TRON it also simulates the call again. If the check fails, nothing is sent: Execute returns `PREFLIGHT_FAILED` with a reason (`insufficient_native_for_gas`, `simulation_reverted`, `fee_limit_too_low`). Top up the wallet and call Execute again with the same `uuid` before `expires_at`. The full list is in [Execute Transaction](https://docs-processing.crypto-chief.com/blockchain-data/execute-transaction).

## Status without polling

Pass `urlCallback` to `sign` to receive `transaction.confirmed`, `transaction.failed`, `transaction.expired` or `transaction.cancelled` instead of calling `waitFor`. See [Transaction Webhook](https://docs-processing.crypto-chief.com/webhooks/transaction-webhook).

## Cost

| Call                                                   | Credits | USD     |
| ------------------------------------------------------ | ------- | ------- |
| Swap API quote                                         | 1,000   | $0.0001 |
| Swap API build                                         | 10,000  | $0.001  |
| Sign Transaction, per step                             | 60,000  | $0.006  |
| Execute Transaction, per step                          | 35,000  | $0.0035 |
| Transaction Information, per call (`waitFor` polls it) | 20,000  | $0.002  |

All calls are paid from the project's API credits. Network fees are paid by the wallet on chain.
