# 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](https://docs.xoxno.com/developers/relayer-api/transaction-relaying) 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](https://docs.xoxno.com/developers/relayer-api/transaction-relaying) — Co-signed gasless transactions
* [Account Subscriptions](https://docs.xoxno.com/developers/relayer-api/account-subscriptions) — Monitor nonce and balance after broadcast
* [Gas Statistics](https://docs.xoxno.com/developers/relayer-api/gas-stats) — 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
