> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sodax.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> SODAX is mainnet-only (no testnet chains or RPC endpoints exist) with a hub-and-spoke architecture; Sonic is the hub. SODAX is non-custodial protocol/software: it routes and settles; independent solvers on the marketplace fill — never say 'our solver', 'the SODAX solver', that SODAX trades, takes custody, or fills orders. For frontend/React integrations, prefer @sodax/dapp-kit hooks over calling @sodax/sdk directly. SDK operations — the methods that build, submit or await a transaction, and the API/quote calls — return Result<T, E> ({ ok: true, value } or { ok: false, error }): check result.ok, never wrap them in try/catch or branch on error.message; discriminate on the narrow error.code union instead. Synchronous config getters (getPartnerFee, getSupportedSwapTokens, getVault, ...) return their value directly, not a Result.

# Swaps on Hedera

> Intent-based cross-network swaps from a Hedera perspective: one signed EVM transaction, solver-filled, settled across networks.

SODAX swaps are intents, not bridges. Your user signs one transaction on Hedera that locks the input and declares what they want on the destination network. Solvers fill it; the SODAX relayer carries proof between Hedera, the hub (Sonic), and the destination. If nothing fills, the intent can be cancelled and funds recovered.

From your side it is one SDK call. From your user's side it is one wallet prompt, plus an approval the first time they swap an HTS token.

## What this looks like on Hedera

* **Source**: the intent transaction is an EVM transaction sent through Hedera's JSON-RPC relay and signed by the user's ECDSA key. `ChainKeys.HEDERA_MAINNET` (`'hedera'`) identifies the network everywhere in the SDK.
* **Reach**: any solver-compatible asset on any SODAX network is a valid destination, <span data-sodax-config="count" data-metric="swap-tokens">166</span> assets today. The [on-Hedera asset list](/hedera/networks-and-assets) only bounds what users hold on Hedera itself, not what they can swap into.
* **Tokens**: Hedera assets are native HTS tokens, addressed by the EVM form of their token ID. USDC is token `0.0.456858`, which the SDK lists as `0x000000000000000000000000000000000006f89a`. Read addresses from the SDK config; never convert them by hand.
* **Settlement**: output lands on the destination network at `dstAddress`. Swaps into Hedera settle as HTS balances on the recipient's EVM address, which must be able to receive the token: see [Receiving tokens on Hedera](#receiving-tokens-on-hedera).

<h2 id="approvals">
  Approvals
</h2>

`swap()` does not approve the input token for you. HBAR is the native token and needs no approval. Every HTS token does: SODAX's asset manager pulls it through the token's ERC-20 interface, so check the allowance first and approve when it falls short.

```typescript theme={null}
const allowance = await sodax.swaps.isAllowanceValid({ params, walletProvider });
if (!allowance.ok) throw allowance.error;

if (!allowance.value) {
  const approval = await sodax.swaps.approve({ params, walletProvider });
  if (!approval.ok) throw approval.error;
  // wait for the approval to be mined before calling swap()
}
```

`params` is the same intent params object you pass to `swap()`.

## Quoting

The solver API quotes both directions:

```typescript theme={null}
const quoteResult = await sodax.swaps.getQuote({
  token_src: hbar.address,            // token on Hedera
  token_dst: usdc.address,            // token on the destination network
  token_src_blockchain_id: ChainKeys.HEDERA_MAINNET,
  token_dst_blockchain_id: ChainKeys.ARBITRUM_MAINNET,
  amount: 10n * 10n ** BigInt(hbar.decimals), // 10 HBAR
  quote_type: 'exact_input',          // the only supported quote type
});
```

`getQuote` deducts any configured partner fee before quoting, so `quoted_amount` is the net output the user actually receives.

## Fees

Using SODAX is free to integrate. Two fees can apply to a trade:

* **Base fee**: a fixed 0.1% of the input, taken by the protocol. Not configurable; compute it ahead of time with `getSolverFee(inputAmount)`.
* **Your fee**: optional platform fee on top, set by you and paid to you. It is the only fee you control. Configure it at SDK setup and check it with `getPartnerFee(inputAmount)`. See [Monetize SDK](/developers/how-to/monetize_sdk).

The user also pays Hedera's network fee for the intent transaction, in HBAR, like any other Hedera EVM transaction.

## Executing

Prefer `swap()`. It creates the intent on Hedera, verifies it landed, submits it to the relay, waits for the packet on the hub, and notifies the solver. The [Quickstart](/hedera/quickstart#4-quote-and-swap) has the complete call. Track progress with `getStatus(request)`, and cancel unfilled intents with `cancelIntent`. Limit orders (intents without deadlines) work from Hedera too, via `createLimitOrder`.

Orchestrating manually with `createIntent` and `submitIntent` works the same as on any EVM network: Hedera intents need no relay extra data.

<h2 id="receiving-tokens-on-hedera">
  Receiving tokens on Hedera
</h2>

On most EVM networks any address can receive any token. On Hedera an account receives an HTS token only if it is **associated** with that token, or still has a free **automatic association** slot. A delivery to an account that is neither fails. This applies whenever Hedera is the destination: a swap into Hedera, or a money market borrow or withdraw delivered to Hedera. HBAR itself needs no association.

Whether an account qualifies depends on how it was created:

| `max_automatic_token_associations` | What the account can receive |
| - | - |
| `-1` | Any token. The default for accounts auto-created by sending to an EVM address, and for hollow accounts once completed. Most accounts made by EVM wallets are in this group. |
| `> 0` | New tokens until that many slots are used, then only tokens it is already associated with. |
| `0` | Only tokens it is already associated with. |

Check before you submit, using Hedera's public mirror node. It accepts an EVM address directly:

```typescript theme={null}
const MIRROR = 'https://mainnet-public.mirrornode.hedera.com/api/v1';

// tokenAddress is the SDK's EVM address for the HTS token, e.g. usdc.address
async function canReceiveOnHedera(evmAddress: string, tokenAddress: string): Promise<boolean> {
  const tokenId = `0.0.${BigInt(tokenAddress)}`;

  const res = await fetch(`${MIRROR}/accounts/${evmAddress}`);
  if (!res.ok) return false; // no Hedera account at this address yet
  const account: { max_automatic_token_associations: number } = await res.json();
  if (account.max_automatic_token_associations === -1) return true;

  const rel = await fetch(`${MIRROR}/accounts/${evmAddress}/tokens?token.id=${tokenId}`);
  const { tokens }: { tokens: { automatic_association: boolean }[] } = await rel.json();
  if (tokens.length > 0) return true; // already associated

  // Count used automatic slots; this reads the first page, which covers most accounts
  const all = await fetch(`${MIRROR}/accounts/${evmAddress}/tokens?limit=100`);
  const { tokens: held }: { tokens: { automatic_association: boolean }[] } = await all.json();
  const usedSlots = held.filter(t => t.automatic_association).length;
  return usedSlots < account.max_automatic_token_associations;
}
```

When it returns `false`, ask the user to associate the token before you submit. From an EVM wallet that is one transaction: every HTS token exposes `associate()` at its own address ([HIP-719](https://hips.hedera.com/hip/hip-719)), signed by the account that will receive it.

```typescript theme={null}
import { parseAbi } from 'viem';

// walletClient: a viem wallet client for the receiving account on Hedera mainnet
const hash = await walletClient.writeContract({
  address: usdc.address as `0x${string}`,
  abi: parseAbi(['function associate() returns (uint256)']),
  functionName: 'associate',
});
```

## Error handling

Operations return `Result<T>` instead of throwing. The core methods (`swap`, `createIntent`, `postExecution`) return a typed `SodaxError` union you can `switch` on by `error.code`.

***

Full reference, including raw mode, limit orders, cancellation, and the complete error-code table: [Swaps](/developers/packages/foundation/sdk/functional-modules/swaps).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.