# Intent Relay API

> **Error handling conventions:** Failures from `submitTransaction` / `relayTxAndWaitPacket` follow the **relay-layer contract**: `error.message` is one of the literal strings exported as `RELAY_ERROR_CODES` (`'SUBMIT_TX_FAILED'`, `'RELAY_TIMEOUT'`). Modules other than swap (moneyMarket, bridge, dex, migration, staking) propagate these errors raw. The **swap module** wraps them into `SodaxError<SwapErrorCode>` with `context.relayCode` (see [SWAPS.md](https://github.com/icon-project/sodax-document/blob/main/developers/packages/foundation/sdk/tooling-modules/SWAPS.md) Error Handling).

The Intent Relay API Service provides functionality for submitting transactions and retrieving transaction packets across different chains. This service is part of the cross-chain communication infrastructure.

Source: `packages/sdk/src/shared/services/intentRelay/IntentRelayApiService.ts`

## Available Actions

1. `submit` — Submit a transaction to the intent relay service.
2. `get_transaction_packets` — Get all packets associated with a transaction.
3. `get_packet` — Get a specific packet by connection sequence number.

## Transaction Status Types

* `pending` — No signatures yet.
* `validating` — Not enough signatures collected.
* `executing` — Enough signatures collected, no confirmed destination tx hash yet.
* `executed` — Has a confirmed destination transaction hash.

## Chain IDs vs Chain Keys

The relay API uses its own numeric chain ID space (`IntentRelayChainId`) — bigint values defined in `RelayChainIdMap` — that is **distinct** from the `SpokeChainKey` string keys used everywhere else in the SDK. For example, Sonic's relay chain ID is `146n` while its chain key is `ChainKeys.SONIC_MAINNET`.

Use `getIntentRelayChainId(chainKey)` (from `@sodax/sdk`) to convert a `SpokeChainKey` to its `IntentRelayChainId`. The relay API wire format expects these IDs serialized as strings (call `.toString()` before passing them in `chain_id` fields).

`PacketData` fields `src_chain_id` and `dst_chain_id` are returned as `number` by the relay API; use `getChainKeyFromRelayChainId()` to convert back to a `SpokeChainKey` if needed.

## Result\<T> Return Types

All public functions in this module return `Promise<Result<T>>`:

```ts
type Result<T> = { ok: true; value: T } | { ok: false; error: Error | unknown };
```

On failure, check `result.error.message` for CODE-form errors such as `'SUBMIT_TX_FAILED'` or `'RELAY_TIMEOUT'`. Check `result.error.cause` for the underlying error when present. There are no typed error discriminators (`RelayError`, etc.) — those have been removed.

## High-Level Entry Point: `relayTxAndWaitPacket`

For most use cases, call `relayTxAndWaitPacket` rather than invoking `submitTransaction` / `waitUntilIntentExecuted` separately. It submits the transaction and polls until the relay packet reaches `'executed'` status.

```typescript
import { relayTxAndWaitPacket } from '@sodax/sdk';
import { ChainKeys } from '@sodax/sdk';

// relayData ({ address, payload }) comes from the preceding spoke operation,
// e.g. createIntent() or createBridgeIntent() returns { tx, relayData }.
// It is always required — pass it through even for non-Solana/Bitcoin chains.
const result = await relayTxAndWaitPacket({
  srcTxHash: '0x123...',
  data: relayData,          // RelayExtraData from the preceding spoke operation
  chainKey: ChainKeys.ETHEREUM_MAINNET,
  relayerApiEndpoint: 'https://api.example.com/relay' as HttpUrl,
  timeout: 120_000,         // optional; defaults to DEFAULT_RELAY_TX_TIMEOUT (120 s)
});

if (!result.ok) {
  if (result.error instanceof Error && result.error.message === 'RELAY_TIMEOUT') {
    // packet did not arrive within timeout
  }
  // handle other errors via result.error.message / result.error.cause
  return;
}

const packet = result.value; // PacketData — status === 'executed'
console.log('hub tx hash:', packet.dst_tx_hash);
```

**Solana and Bitcoin note:** These chains use split transactions — the on-chain tx carries only a verification hash, while the full call data is submitted off-chain via the relayer. Pass a `RelayExtraData` object as `data`:

```typescript
const result = await relayTxAndWaitPacket({
  srcTxHash: '5xL...',
  data: { address: '0xabc...', payload: '0xcafe...' }, // required for Solana/Bitcoin
  chainKey: ChainKeys.SOLANA_MAINNET,
  relayerApiEndpoint: 'https://api.example.com/relay' as HttpUrl,
  timeout: undefined, // use default
});
```

## Low-Level API Examples

### Submit Transaction

```typescript
import { submitTransaction } from '@sodax/sdk';

const request = {
  action: 'submit',
  params: {
    chain_id: '146',    // IntentRelayChainId as string — use getIntentRelayChainId(chainKey).toString()
    tx_hash: '0x123',
  },
} satisfies IntentRelayRequest<'submit'>;

const result = await submitTransaction(request, 'https://api.example.com/relay' as HttpUrl);

if (!result.ok) {
  // result.error.message === 'SUBMIT_TX_FAILED' on relay rejection
  // result.error.cause contains the underlying relay error message
  return;
}
// result.value: SubmitTxResponse
// { success: true, message: 'Transaction registered' }
```

### Get Transaction Packets

```typescript
import { getTransactionPackets } from '@sodax/sdk';

const request = {
  action: 'get_transaction_packets',
  params: {
    chain_id: '146',
    tx_hash: '0x123',
  },
} satisfies IntentRelayRequest<'get_transaction_packets'>;

const result = await getTransactionPackets(request, 'https://api.example.com/relay' as HttpUrl);

if (!result.ok) return;
// result.value: GetTransactionPacketsResponse
// {
//   "success": true,
//   "data": [
//     {
//       "src_chain_id": 6,
//       "src_tx_hash": "0x23a7eae34f6acf5cfadc43e714a4d188b0d6526b95c82c9b969e69d7222df5de",
//       "src_address": "a8e168789b1fa96de2fb816df56757ad950438a4",
//       "status": "executed",
//       "dst_chain_id": 146,
//       "conn_sn": 54,
//       "dst_address": "67a8cf2543a30b292a443430df213983951dca08",
//       "dst_tx_hash": "0xd7f1cf40154d3123eda3a94622bae13d879307fd3526cb45dd50951fee9cd244",
//       "signatures": [
//         "c172723dba3aec0f98d6602fcfbbcae9873ce3f4fc0eded70d64b6ad3f7806aa0b22d0fa3ea57679ec05f8c51a8562c9c979d247330966e9aaaf34a4dfae64e001"
//       ],
//       "payload": "cafebabe"
//     }
//   ]
// }
```

### Get Packet

```typescript
import { getPacket } from '@sodax/sdk';

const request = {
  action: 'get_packet',
  params: {
    chain_id: '146',
    tx_hash: '0x123...abc',
    conn_sn: '54',
  },
} satisfies IntentRelayRequest<'get_packet'>;

const result = await getPacket(request, 'https://api.example.com/relay' as HttpUrl);

if (!result.ok) return;
// result.value: GetPacketResponse
// On success:
// {
//   "success": true,
//   "data": {
//     "src_chain_id": 6,
//     "src_tx_hash": "0x781554a94bbd2ebd79ebaa01c645781ddf46610e5f1af8e5735d58b95ca6fbd6",
//     "src_address": "1d790ac96a0da4c249fd8838a7cc46b91fee3c5a",
//     "status": "executing",
//     "dst_chain_id": 21,
//     "dst_address": "0x26f83c5996f79229ef16cf7ca49eeb8682535e81ab59c30e561cc317bcc96a4a::sampledapp::...",
//     "conn_sn": 14,
//     "signatures": ["c172723dba3aec0f98d6602fcfbbcae9873ce3f4fc0eded70d64b6ad3f7806aa0b22d0fa3ea57679ec05f8c51a8562c9c979d247330966e9aaaf34a4dfae64e001"],
//     "payload": "cafebabe"
//   }
// }
```

## Type Definitions

All types are exported from `packages/sdk/src/shared/services/intentRelay/IntentRelayApiService.ts`.

```typescript
export type RelayTxStatus = 'pending' | 'validating' | 'executing' | 'executed';

export type RelayAction = 'submit' | 'get_transaction_packets' | 'get_packet';

// chain_id is always a string representation of an IntentRelayChainId bigint value
export type IntentRelayRequest<T extends RelayAction> = {
  action: T;
  params: T extends 'submit'
    ? { chain_id: string; tx_hash: string; data?: RelayExtraData }
    : T extends 'get_transaction_packets'
      ? { chain_id: string; tx_hash: string }
      : T extends 'get_packet'
        ? { chain_id: string; tx_hash: string; conn_sn: string }
        : never;
};

// Extra data required for Solana and Bitcoin split-tx chains
export type RelayExtraData = { address: Hex; payload: Hex };

export type PacketData = {
  src_chain_id: number;    // IntentRelayChainId as number (not a SpokeChainKey)
  src_tx_hash: string;
  src_address: string;
  status: RelayTxStatus;
  dst_chain_id: number;
  conn_sn: number;
  dst_address: string;
  dst_tx_hash: string;
  signatures: string[];
  payload: string;
};

export type SubmitTxResponse = {
  success: boolean;
  message: string;
};

export type GetTransactionPacketsResponse = {
  success: boolean;
  data: PacketData[];
};

export type GetPacketResponse =
  | { success: true; data: PacketData }
  | { success: false; message: string };

export type RelayAndWaitParams = {
  srcTxHash: string;
  data: RelayExtraData;         // always required; only used in submit payload for Solana/Bitcoin
  chainKey: SpokeChainKey;
  relayerApiEndpoint: HttpUrl;
  timeout: number | undefined;
};

export type IntentDeliveryInfo = {
  srcChainKey: SpokeChainKey;
  srcTxHash: string;
  srcAddress: string;
  dstChainKey: SpokeChainKey;
  dstTxHash: string;
  dstAddress: string;
};

export type WaitUntilIntentExecutedPayload = {
  intentRelayChainId: string;   // IntentRelayChainId serialized as string
  srcTxHash: string;
  timeout?: number;
  apiUrl: HttpUrl;
};
```


---

# 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.sodax.com/developers/packages/foundation/sdk/tooling-modules/intent_relay_api.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.
