# author_submitAndWatchExtrinsic - Enjin RPC Method

Submits a signed extrinsic to Enjin and returns a subscription that emits status updates as the transaction progresses through the lifecycle -- from entering the transaction pool, through block inclusion, to finalization. This is a WebSocket-only subscription method.

> **Why Enjin?** Build on the purpose-built NFT blockchain with protocol-level minting and $100M Metaverse Fund with NFT functions at protocol level, Fuel Tanks for subsidized fees, 700-1000 TPS, 6-second finality, and ERC-1155 standard pioneer.

## When to Use This Method

`author_submitAndWatchExtrinsic` is essential for game developers, NFT creators, and enterprises building cross-chain digital assets:

- **Transaction Lifecycle Tracking** -- Receive real-time status events as your extrinsic moves from the pool into a block and reaches finality on Enjin
- **Confirmation Waiting** -- Block until a transaction reaches a specific finality level (e.g., `inBlock` or `finalized`) before proceeding with dependent logic
- **Error Detection** -- Detect dropped, invalid, or usurped transactions immediately instead of polling, critical for high-volume NFT minting (2,000+ per tx), gaming assets, and cross-chain NFT transfers via Paratoken standard
- **User-Facing Feedback** -- Power progress indicators and toast notifications in dApp interfaces with granular status updates

## Best Practices

- Requires a WebSocket connection for real-time status updates
- Handles multiple status transitions: Ready, Broadcast, InBlock, Finalized
- Unsubscribe from the watch subscription when the extrinsic is confirmed
- Use `author_submitExtrinsic` with polling if WebSocket is unavailable

## Request Parameters

- `extrinsic` (`String, required`): Hex-encoded SCALE-serialized signed extrinsic (e.g., output of tx.toHex() or createSignedTx(...))

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}
```

## Response Fields

- `result` (`Unknown, required`): Extrinsic placed in the future queue because its nonce is higher than expected
- `field_2` (`Unknown, required`): Extrinsic is in the ready queue, waiting to be included in a block
- `field_3` (`Unknown, required`): Extrinsic has been broadcast to the listed peer IDs
- `field_4` (`Unknown, required`): Extrinsic has been included in the block with this hash (not yet finalized)
- `field_5` (`Unknown, required`): Block containing the extrinsic was retracted due to a chain reorganization
- `field_6` (`Unknown, required`): Finality could not be reached for the block within the expected timeframe
- `field_7` (`Unknown, required`): Extrinsic has been finalized in the block with this hash
- `field_8` (`Unknown, required`): Extrinsic was replaced by another extrinsic with the same nonce (hash of replacement)
- `field_9` (`Unknown, required`): Extrinsic was dropped from the transaction pool (e.g., pool is full or fee too low)
- `field_10` (`Unknown, required`): Extrinsic failed validation (bad signature, insufficient balance, wrong nonce, etc.)

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "bNxKoEf7t58opia1"
}
```

## Error Responses

### Error Response

- Code: `1002`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": 1002,
    "message": "Verification Error: Runtime error: Extrinsic has invalid signature"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
# author_submitAndWatchExtrinsic requires WebSocket.
# Use websocat to send the subscription request:
echo '{
  "jsonrpc": "2.0",
  "method": "author_submitAndWatchExtrinsic",
  "params": ["0x2d028400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01..."],
  "id": 1
}' | websocat wss://enjin-matrix-rpc.n.dwellir.com

# The connection stays open and prints status update messages as they arrive.
# For a fire-and-forget HTTP approach, use author_submitExtrinsic instead:
curl -X POST https://enjin-matrix-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "author_submitExtrinsic",
    "params": ["0x2d028400..."],
    "id": 1
  }'
```

```javascript
// Using @polkadot/api (recommended)
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

const provider = new WsProvider('wss://enjin-matrix-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });
const keyring = new Keyring({ type: 'sr25519' });

// Create and sign a transfer
const sender = keyring.addFromUri('//Alice');
const transfer = api.tx.balances.transferKeepAlive(
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
  1000000000000n
);

// Submit and watch -- signAndSend uses author_submitAndWatchExtrinsic internally
const unsub = await transfer.signAndSend(sender, ({ status, events, dispatchError }) => {
  console.log(`Status: ${status.type}`);

  if (status.isInBlock) {
    console.log(`Included in block: ${status.asInBlock.toHex()}`);

    // Check for dispatch errors in events
    if (dispatchError) {
      if (dispatchError.isModule) {
        const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
        console.error(`Error: ${section}.${name} -- ${docs.join(' ')}`);
      } else {
        console.error(`Error: ${dispatchError.toString()}`);
      }
    }
  }

  if (status.isFinalized) {
    console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
    unsub();
    api.disconnect();
  }
});

// Using raw WebSocket JSON-RPC
const ws = new WebSocket('wss://enjin-matrix-rpc.n.dwellir.com');

ws.onopen = () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    method: 'author_submitAndWatchExtrinsic',
    params: ['0x2d028400...signedExtrinsicHex'],
    id: 1
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.params) {
    console.log('Status update:', msg.params.result);
  } else {
    console.log('Subscription ID:', msg.result);
  }
};
```

```python
import asyncio
import websockets
import json

async def submit_and_watch(signed_extrinsic_hex):
    uri = 'wss://enjin-matrix-rpc.n.dwellir.com'

    async with websockets.connect(uri) as ws:
        # Submit and subscribe
        await ws.send(json.dumps({
            'jsonrpc': '2.0',
            'method': 'author_submitAndWatchExtrinsic',
            'params': [signed_extrinsic_hex],
            'id': 1
        }))

        # Get subscription ID
        response = json.loads(await ws.recv())
        if 'error' in response:
            print(f"Submission error: {response['error']['message']}")
            return None

        sub_id = response['result']
        print(f'Watching with subscription: {sub_id}')

        # Listen for status updates
        while True:
            message = json.loads(await ws.recv())
            if 'params' in message:
                status = message['params']['result']
                print(f'Status: {status}')

                # Handle terminal states
                if isinstance(status, dict):
                    if 'finalized' in status:
                        print(f"Finalized in: {status['finalized']}")
                        return status['finalized']
                    elif 'usurped' in status:
                        print(f"Usurped by: {status['usurped']}")
                        return None
                elif status in ('dropped', 'invalid', 'finalityTimeout'):
                    print(f'Transaction failed with status: {status}')
                    return None

# asyncio.run(submit_and_watch('0x2d028400...'))

# Using substrate-interface
from substrateinterface import SubstrateInterface, Keypair

substrate = SubstrateInterface(url='wss://enjin-matrix-rpc.n.dwellir.com')
keypair = Keypair.create_from_uri('//Alice')

call = substrate.compose_call(
    call_module='Balances',
    call_function='transfer_keep_alive',
    call_params={
        'dest': '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty',
        'value': 1000000000000
    }
)

extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_finalization=True)
print(f'Finalized in block: {receipt.block_hash}')
print(f'Extrinsic successful: {receipt.is_success}')
```

```rust
use futures::StreamExt;
use serde_json::json;
use tokio_tungstenite::{connect_async, tungstenite::Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let (mut ws_stream, _) = connect_async("https://enjin-matrix-rpc.n.dwellir.com").await?;

    // Send the subscription request
    let request = json!({
        "jsonrpc": "2.0",
        "method": "author_submitAndWatchExtrinsic",
        "params": ["0x2d028400...signedExtrinsicHex"],
        "id": 1
    });

    ws_stream
        .send(Message::Text(request.to_string()))
        .await?;

    // Listen for status updates
    while let Some(msg) = ws_stream.next().await {
        let msg = msg?;
        if let Message::Text(text) = msg {
            let value: serde_json::Value = serde_json::from_str(&text)?;

            if let Some(params) = value.get("params") {
                let status = &params["result"];
                println!("Status: {}", status);

                // Check for finalization
                if let Some(hash) = status.get("finalized") {
                    println!("Finalized in block: {}", hash);
                    break;
                }

                // Check for terminal failure states
                if status == "dropped" || status == "invalid" {
                    eprintln!("Transaction failed: {}", status);
                    break;
                }
            } else if let Some(error) = value.get("error") {
                eprintln!("Submission error: {}", error["message"]);
                break;
            } else {
                println!("Subscription ID: {}", value["result"]);
            }
        }
    }

    Ok(())
}
```

## Common Use Cases

### 1. Transaction Confirmation with Timeout

Wait for finalization with a configurable timeout to avoid hanging indefinitely:

```javascript
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';

async function sendAndConfirm(api, sender, tx, timeoutMs = 120000) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      reject(new Error('Transaction confirmation timed out'));
    }, timeoutMs);

    tx.signAndSend(sender, ({ status, dispatchError, events }) => {
      if (dispatchError) {
        clearTimeout(timer);
        if (dispatchError.isModule) {
          const { docs, name, section } = api.registry.findMetaError(dispatchError.asModule);
          reject(new Error(`${section}.${name}: ${docs.join(' ')}`));
        } else {
          reject(new Error(dispatchError.toString()));
        }
      }

      if (status.isFinalized) {
        clearTimeout(timer);
        resolve({
          blockHash: status.asFinalized.toHex(),
          events: events.map((e) => `${e.event.section}.${e.event.method}`)
        });
      }
    }).catch((err) => {
      clearTimeout(timer);
      reject(err);
    });
  });
}
```

### 2. Batch Transaction Pipeline

Submit multiple extrinsics sequentially and track each one through finalization:

```javascript
async function submitBatch(api, sender, calls) {
  const results = [];
  let nonce = (await api.rpc.system.accountNextIndex(sender.address)).toNumber();

  for (const call of calls) {
    const result = await new Promise((resolve, reject) => {
      call.signAndSend(sender, { nonce: nonce++ }, ({ status, dispatchError }) => {
        if (dispatchError) {
          const decoded = dispatchError.isModule
            ? api.registry.findMetaError(dispatchError.asModule)
            : { name: dispatchError.toString() };
          reject(new Error(`Dispatch error: ${decoded.name}`));
        }

        if (status.isFinalized) {
          resolve({ blockHash: status.asFinalized.toHex(), nonce: nonce - 1 });
        }
      });
    });
    results.push(result);
    console.log(`Tx nonce=${result.nonce} finalized in ${result.blockHash}`);
  }

  return results;
}
```

### 3. Reorg-Aware Event Handling

Handle block retractions gracefully, re-evaluating transaction inclusion after reorganizations:

```javascript
async function sendWithReorgHandling(api, sender, tx) {
  let includedBlock = null;

  return new Promise((resolve, reject) => {
    tx.signAndSend(sender, ({ status, events }) => {
      if (status.isReady) {
        console.log('Transaction in ready queue');
      }

      if (status.isInBlock) {
        includedBlock = status.asInBlock.toHex();
        console.log(`Included in block: ${includedBlock}`);
      }

      if (status.isRetracted) {
        console.warn(`Block retracted: ${status.asRetracted.toHex()} -- waiting for re-inclusion`);
        includedBlock = null;
      }

      if (status.isFinalized) {
        console.log(`Finalized in block: ${status.asFinalized.toHex()}`);
        resolve({ finalized: status.asFinalized.toHex(), events });
      }

      if (status.isDropped || status.isInvalid) {
        reject(new Error(`Transaction ${status.type}`));
      }

      if (status.isUsurped) {
        reject(new Error(`Transaction usurped by ${status.asUsurped.toHex()}`));
      }
    });
  });
}
```

## Status Flow

```
              ┌─────────────────────────────────────┐
              │          future (nonce gap)          │
              └──────────────┬──────────────────────┘
                             │ nonce becomes current
                             ▼
 submit ──► ready ──► broadcast ──► inBlock ──► finalized ✓
              │                       │
              ├──► dropped ✗          ├──► retracted (reorg) ──► inBlock (re-included)
              ├──► invalid ✗          └──► finalityTimeout ✗
              └──► usurped ✗
```

## Related Methods

- [`author_submitExtrinsic`](https://www.dwellir.com/docs/enjin/author_submitExtrinsic) -- Submit an extrinsic without subscribing to status updates (fire-and-forget)
- `system_accountNextIndex` -- Get the next valid nonce for an account, including pending pool transactions
- [`author_pendingExtrinsics`](https://www.dwellir.com/docs/enjin/author_pendingExtrinsics) -- List all extrinsics currently in the transaction pool
- [`payment_queryInfo`](https://www.dwellir.com/docs/enjin/payment_queryInfo) -- Estimate the fee for an extrinsic before submission
- [`chain_getFinalizedHead`](https://www.dwellir.com/docs/enjin/chain_getFinalizedHead) -- Get the hash of the latest finalized block
