# Transaction Broadcasting

The `broadcast` action submits one or more fully signed transactions to the MultiversX P2P network via gossipsub, bypassing the public API. The relayer does not add its signature — the transaction must already carry a valid sender `signature`.

Use `broadcast` when:

* The transaction was signed by the user's own wallet
* You have pre-signed transactions from a backend signing service
* You want the fastest possible delivery path to validators

For transactions that require the relayer's co-signature (gasless / Relayer V3), use the [`relay` action](/developers/relayer-api/transaction-relaying.md) instead.

***

## Single Transaction

```javascript
ws.send(JSON.stringify({
  action: 'broadcast',
  requestId: 'tx-001',
  tx: {
    nonce: 42,
    value: '1000000000000000000',
    receiver: 'erd1qqqqqqqqqqqqqpgq...',
    sender: 'erd1abc...',
    gasPrice: 1000000000,
    gasLimit: 50000,
    data: 'c3dhcEV4YWN0QW1vdW50SW4=',
    chainID: '1',
    version: 2,
    signature: 'a1b2c3d4...'
  }
}));
```

## Batch Transactions

Send multiple transactions in a single message using the `txs` array. All transactions are delivered atomically to the P2P network, but each is evaluated and reported individually.

```javascript
ws.send(JSON.stringify({
  action: 'broadcast',
  requestId: 'batch-001',
  txs: [
    {
      nonce: 42,
      value: '0',
      receiver: 'erd1qqqqqqqqqqqqqpgq...',
      sender: 'erd1abc...',
      gasPrice: 1000000000,
      gasLimit: 50000,
      data: 'c3dhcA==',
      chainID: '1',
      version: 2,
      signature: 'sig1...'
    },
    {
      nonce: 43,
      value: '0',
      receiver: 'erd1qqqqqqqqqqqqqpgq...',
      sender: 'erd1abc...',
      gasPrice: 1000000000,
      gasLimit: 50000,
      data: 'c3dhcA==',
      chainID: '1',
      version: 2,
      signature: 'sig2...'
    }
  ]
}));
```

***

## Transaction Object Fields

| Field               | Type   | Required | Description                                                    |
| ------------------- | ------ | -------- | -------------------------------------------------------------- |
| `nonce`             | number | Yes      | Sender's current transaction counter                           |
| `value`             | string | Yes      | EGLD amount in atomic units (denominated in 10^-18 EGLD)       |
| `receiver`          | string | Yes      | Receiver's bech32 address                                      |
| `sender`            | string | Yes      | Sender's bech32 address                                        |
| `gasPrice`          | number | Yes      | Gas price in atomic units. Minimum: `1000000000`               |
| `gasLimit`          | number | Yes      | Maximum gas units the transaction may consume                  |
| `data`              | string | No       | Base64-encoded transaction payload (smart contract call data)  |
| `chainID`           | string | Yes      | `"1"` for mainnet                                              |
| `version`           | number | Yes      | Transaction version. Use `2`                                   |
| `options`           | number | No       | Transaction options bitmask                                    |
| `signature`         | string | Yes      | Hex-encoded Ed25519 signature over the canonical serialization |
| `guardian`          | string | No       | Guardian address for guarded accounts                          |
| `guardianSignature` | string | No       | Guardian's signature (required when `guardian` is set)         |

***

## Response

### Success

```json
{
  "action": "broadcast",
  "status": "completed",
  "requestId": "tx-001",
  "hashes": ["a1b2c3d4e5f6..."],
  "failed": []
}
```

### Partial Success (Batch)

When one or more batch transactions fail, the response reports each failure by zero-based index.

```json
{
  "action": "broadcast",
  "status": "partial",
  "requestId": "batch-001",
  "hashes": ["a1b2c3d4e5f6..."],
  "failed": [
    { "index": 1, "reason": "invalid signature" }
  ]
}
```

### Complete Failure

```json
{
  "action": "broadcast",
  "status": "failed",
  "requestId": "batch-001",
  "hashes": [],
  "failed": [
    { "index": 0, "reason": "invalid nonce" },
    { "index": 1, "reason": "insufficient balance" }
  ]
}
```

### Response Fields

| Field       | Type   | Description                                                                       |
| ----------- | ------ | --------------------------------------------------------------------------------- |
| `action`    | string | Always `"broadcast"`                                                              |
| `status`    | string | `"completed"` — all succeeded; `"partial"` — some failed; `"failed"` — all failed |
| `requestId` | string | The `requestId` provided in your request                                          |
| `hashes`    | array  | Transaction hashes of successfully broadcast transactions                         |
| `failed`    | array  | Objects containing `index` (zero-based position in batch) and `reason`            |

***

## Example: Promise-Based Client

```javascript
class TransactionBroadcaster {
  constructor() {
    this.ws = new WebSocket('wss://relayer.xoxno.com/ws');
    this.pending = new Map();
    this.counter = 0;

    this.ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      if (msg.action === 'broadcast' && msg.requestId) {
        const resolve = this.pending.get(msg.requestId);
        if (resolve) {
          this.pending.delete(msg.requestId);
          resolve(msg);
        }
      }
    };
  }

  ready() {
    return new Promise((resolve) => {
      if (this.ws.readyState === WebSocket.OPEN) return resolve();
      this.ws.onopen = resolve;
    });
  }

  async broadcast(transactions, timeoutMs = 30000) {
    const requestId = `req-${++this.counter}`;
    const txs = Array.isArray(transactions) ? transactions : [transactions];

    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        this.pending.delete(requestId);
        reject(new Error('Broadcast timeout'));
      }, timeoutMs);

      this.pending.set(requestId, (result) => {
        clearTimeout(timer);
        resolve(result);
      });

      this.ws.send(JSON.stringify({ action: 'broadcast', requestId, txs }));
    });
  }

  async broadcastOne(tx) {
    const result = await this.broadcast(tx);
    if (result.status === 'failed') {
      throw new Error(result.failed[0]?.reason ?? 'Broadcast failed');
    }
    return result.hashes[0];
  }
}

// Usage
const broadcaster = new TransactionBroadcaster();
await broadcaster.ready();

const hash = await broadcaster.broadcastOne({
  nonce: 42,
  value: '1000000000000000000',
  receiver: 'erd1qqqqqqqqqqqqqpgq...',
  sender: 'erd1abc...',
  gasPrice: 1000000000,
  gasLimit: 50000,
  chainID: '1',
  version: 2,
  signature: 'hex-signature...'
});

console.log('Broadcast hash:', hash);
```

***

## Broadcast vs Relay

| Aspect                     | Broadcast                         | Relay                                  |
| -------------------------- | --------------------------------- | -------------------------------------- |
| Relayer co-signing         | No                                | Yes (Ed25519, shard-specific key)      |
| `relayer` field required   | No                                | Yes                                    |
| `signature` field required | Yes                               | Yes (user signature)                   |
| Gas paid by                | User                              | Relayer                                |
| Use case                   | Standard self-signed transactions | Gasless / Relayer V3 meta-transactions |

{% hint style="info" %}
To track a transaction after broadcasting, subscribe to `tx-status/{hash}`. The subscription auto-expires 60 seconds after the first status update is delivered.
{% endhint %}

***

## Related Pages

* [Transaction Relaying](/developers/relayer-api/transaction-relaying.md) — Co-signed gasless transactions
* [Account Subscriptions](/developers/relayer-api/account-subscriptions.md) — Monitor nonce and balance after broadcast
* [Gas Statistics](/developers/relayer-api/gas-stats.md) — Determine a competitive gas price before sending
* [Error Reference](https://github.com/XOXNO/docs/blob/main/developers/relayer-api/error-reference.md) — Transaction-level error codes and resolution


---

# 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.xoxno.com/developers/relayer-api/transaction-broadcasting.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.
