# state_getMetadata - Asset Hub RPC Method

Returns the runtime metadata for Asset Hub as a SCALE-encoded hex string. Metadata describes all available pallets, storage items, calls, events, errors, and type definitions - everything needed to interact with the chain programmatically.

> **Why Asset Hub?** Build on Polkadot's system parachain managing $4.5B+ in DOT tokens, native USDC/USDT, and NFTs with 50-90% lower fees than Relay Chain, fee payment in any supported asset, 1.5M+ accounts migrated, and trustless Ethereum bridge access.

## When to Use This Method

`state_getMetadata` is essential for asset issuers, stablecoin integrators, and teams requiring low-cost token management on Polkadot:

- **Runtime Introspection** - Discover available pallets, calls, and storage items on Asset Hub
- **Extrinsic Building** - Get call signatures and type information for constructing transactions for native stablecoin transfers (USDC, USDT), DOT staking and governance, and cross-chain asset management via XCM
- **Storage Key Generation** - Build correct storage keys from metadata type definitions
- **Client Generation** - Auto-generate typed APIs and SDKs from the runtime metadata
- **Upgrade Awareness** - Detect metadata changes after runtime upgrades

## Best Practices

- Metadata is chain-specific and versioned -- cache for the duration of your session
- Metadata response can be large (500KB+ on complex chains) -- parse it once at startup
- Use metadata to build dynamic UIs that adapt to runtime changes
- The `specVersion` field changes on runtime upgrades -- monitor for incompatibility

## Request Parameters

- `blockHash` (`String, optional`): Block hash to query metadata at. If omitted, returns metadata for the current runtime

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "state_getMetadata",
  "params": [],
  "id": 1
}
```

## Response Fields

- `result` (`Bytes, required`): SCALE-encoded hex string containing the full runtime metadata

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x6d6574610e...truncated..."
}
```

## Error Responses

### Error Response

- Code: `-32603`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32603,
    "message": "Internal error"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Rust

```bash
curl -X POST https://asset-hub-polkadot-rpc.n.dwellir.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "state_getMetadata",
    "params": [],
    "id": 1
  }'
```

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

const provider = new WsProvider('wss://asset-hub-polkadot-rpc.n.dwellir.com');
const api = await ApiPromise.create({ provider });

// Get runtime metadata
const metadata = await api.rpc.state.getMetadata();

// List available pallets
const pallets = metadata.asLatest.pallets.map(p => p.name.toString());
console.log('Available pallets:', pallets);

// Get specific pallet info
const balancesPallet = metadata.asLatest.pallets.find(
  p => p.name.toString() === 'Balances'
);
console.log('Balances pallet index:', balancesPallet.index.toString());

// Check metadata version
console.log('Metadata version:', metadata.version);

await api.disconnect();
```

```python
import requests

def get_metadata(block_hash=None):
    url = 'https://asset-hub-polkadot-rpc.n.dwellir.com'
    params = [block_hash] if block_hash else []

    payload = {
        'jsonrpc': '2.0',
        'method': 'state_getMetadata',
        'params': params,
        'id': 1
    }

    response = requests.post(url, json=payload)
    return response.json()['result']

metadata_hex = get_metadata()
# Remove '0x' prefix for byte length calculation
byte_length = (len(metadata_hex) - 2) // 2
print(f'Metadata size: {byte_length} bytes ({byte_length / 1024:.1f} KB)')

# For full decoding, use the scalecodec library:
# from scalecodec import ScaleBytes
# from scalecodec.types import MetadataVersioned
# metadata = MetadataVersioned(ScaleBytes(metadata_hex))
```

```rust
use subxt::{OnlineClient, PolkadotConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api = OnlineClient::<PolkadotConfig>::from_url(
        "wss://asset-hub-polkadot-rpc.n.dwellir.com"
    ).await?;

    let metadata = api.rpc()
        .state_get_metadata(None)
        .await?;

    // Access pallet info through the metadata
    let pallets = metadata.pallets();
    for pallet in pallets {
        println!("Pallet: {} (index: {})", pallet.name(), pallet.index());
    }

    Ok(())
}
```

## Common Use Cases

### 1. Discover Available Pallets and Calls

Explore what functionality is available on Asset Hub:

```javascript
async function explorePallets(api) {
  const metadata = await api.rpc.state.getMetadata();
  const pallets = metadata.asLatest.pallets;

  for (const pallet of pallets) {
    const name = pallet.name.toString();
    const hasCalls = pallet.calls.isSome;
    const hasStorage = pallet.storage.isSome;
    const hasEvents = pallet.events.isSome;

    console.log(`${name}: calls=${hasCalls} storage=${hasStorage} events=${hasEvents}`);
  }
}
```

### 2. Build Storage Keys from Metadata

Generate correct storage keys for querying chain state:

```javascript
import { xxhashAsHex } from '@polkadot/util-crypto';

function buildStorageKey(palletName, storageName) {
  const palletHash = xxhashAsHex(palletName, 128);
  const storageHash = xxhashAsHex(storageName, 128);

  return palletHash + storageHash.slice(2); // Concatenate without duplicate 0x
}

// Example: Build key for System.Account storage
const key = buildStorageKey('System', 'Account');
console.log('Storage prefix key:', key);
```

### 3. Metadata Version Tracking

Track metadata changes across runtime upgrades on Asset Hub:

```javascript
async function compareMetadataVersions(api, blockA, blockB) {
  const hashA = await api.rpc.chain.getBlockHash(blockA);
  const hashB = await api.rpc.chain.getBlockHash(blockB);

  const metaA = await api.rpc.state.getMetadata(hashA);
  const metaB = await api.rpc.state.getMetadata(hashB);

  const palletsA = new Set(metaA.asLatest.pallets.map(p => p.name.toString()));
  const palletsB = new Set(metaB.asLatest.pallets.map(p => p.name.toString()));

  const added = [...palletsB].filter(p => !palletsA.has(p));
  const removed = [...palletsA].filter(p => !palletsB.has(p));

  console.log('Added pallets:', added);
  console.log('Removed pallets:', removed);
}
```

## Related Methods

- [`state_getRuntimeVersion`](https://www.dwellir.com/docs/asset-hub/state_getRuntimeVersion) - Get runtime version (check before re-fetching metadata)
- [`state_getStorage`](https://www.dwellir.com/docs/asset-hub/state_getStorage) - Query storage using keys derived from metadata
- [`state_call`](https://www.dwellir.com/docs/asset-hub/state_call) - Call runtime APIs described in metadata
