# DApps Browser Integration

Back an in-app dApp browser with Aurea: the tenant's dApp catalogue, EIP-191 message signing, nonce and gas lookups, and raw-transaction relay.

## Overview

Five endpoints under `/v1/dapps` support a dApp browser. All require a Bearer token and work with EVM chains. The API has no per-dApp routes and no WalletConnect endpoints: the bridge between the dApp page and the wallet (for example handling `personal_sign` or `eth_sendTransaction` requests) lives in your app, which calls these endpoints.

|  |  |  |
| --- | --- | --- |
| [GET /v1/dapps/](https://docs.aureahub.com/docs/dapps-list.md) | — | Catalogue of the tenant's active dApps |
| [POST /v1/dapps/sign-personal-message](https://docs.aureahub.com/docs/dapps-sign.md) | — | EIP-191 signature (custodial) or digest to sign (other wallets) |
| [GET /v1/dapps/nonce](https://docs.aureahub.com/docs/dapps-nonce.md) | — | Pending nonce of an address |
| [GET /v1/dapps/estimate-gas](https://docs.aureahub.com/docs/dapps-gas.md) | — | Gas limit with a 20% buffer |
| [POST /v1/dapps/broadcast](https://docs.aureahub.com/docs/dapps-broadcast.md) | — | Relay a signed raw transaction |

## Enable and List

The dApps browser is enabled per tenant. Available Features (`GET /v1/tenant/available-features`) reports it as `dappsEnabled`; when it is off, `GET /v1/dapps/` returns an empty list.

```typescript
const API = 'https://api.aureahub.com';
const bearer = { Authorization: `Bearer ${accessToken}` };

const { dappsEnabled } = await fetch(`${API}/v1/tenant/available-features`, { headers: bearer })
  .then(r => r.json());

if (dappsEnabled) {
  const { dapps } = await fetch(`${API}/v1/dapps/`, { headers: bearer }).then(r => r.json());
  // [{ id, name, url, iconUrl, description, category, sortOrder, ... }]
}
```

## Message Signing

When a dApp asks for `personal_sign`, forward the message and the selected address. Custodial wallets come back signed; `client_side` and MPC wallets come back with `requiresClientSigning: true` and a `messageHash` that already includes the EIP-191 prefix, which must be signed as a raw digest.

```typescript
const result = await fetch(`${API}/v1/dapps/sign-personal-message`, {
  method: 'POST',
  headers: { ...bearer, 'Content-Type': 'application/json' },
  body: JSON.stringify({ message, walletAddress })   // message: 0x-hex or UTF-8 text
}).then(r => r.json());

const signature = result.signature                       // custodial: signed by the server
  ?? device.signingKey.sign(result.messageHash).serialized; // client_side: sign the digest locally

// Return signature to the dApp
```

## Sending a Transaction

The dApps endpoints do not sign transactions. For a wallet whose key is on the device, build and sign the transaction locally, using the nonce and gas limit from Aurea, then relay it. The numeric `chainId` is available from [Supported Chains](https://docs.aureahub.com/docs/tokens-chains.md); fee fields are not returned by these endpoints.

```typescript
const chainSlug = 'gnosis';
// tx comes from the dApp's eth_sendTransaction request: { to, data, value }

const nonceQuery = new URLSearchParams({ chainSlug, address: device.address });
const { nonce } = await fetch(`${API}/v1/dapps/nonce?${nonceQuery}`, { headers: bearer })
  .then(r => r.json());

const gasQuery = new URLSearchParams({ chainSlug, to: tx.to, data: tx.data ?? '0x', value: tx.value ?? '0x0' });
const { gasLimit } = await fetch(`${API}/v1/dapps/estimate-gas?${gasQuery}`, { headers: bearer })
  .then(r => r.json());

const signedTransaction = await device.signTransaction({
  to: tx.to, data: tx.data, value: tx.value, nonce, gasLimit,
  chainId,                            // e.g. from GET /v1/tokens/meta/chains
  maxFeePerGas, maxPriorityFeePerGas  // from your own fee source
});

const res = await fetch(`${API}/v1/dapps/broadcast`, {
  method: 'POST',
  headers: { ...bearer, 'Content-Type': 'application/json' },
  body: JSON.stringify({ chainSlug, signedTransaction })
});
if (!res.ok) throw new Error((await res.json()).message);
const { txHash } = await res.json();
```

## Limits and Errors

|  |  |  |
| --- | --- | --- |
| GET /v1/dapps/nonce | 30 / minute | 400 `NO_RPC_URL` · 404 `WALLET_NOT_FOUND` · 422 `RPC_ERROR` · 504 `RPC_TIMEOUT` |
| GET /v1/dapps/estimate-gas | No route-specific limit | 400 `NO_RPC_URL` · 422 `RPC_ERROR` · 504 `RPC_TIMEOUT` |
| POST /v1/dapps/sign-personal-message | 20 / minute | 404 `WALLET_NOT_FOUND` · 500 `KEY_DECRYPTION_FAILED` |
| POST /v1/dapps/broadcast | 10 / 10 seconds | 400 `UNKNOWN_CHAIN` or `NO_RPC_URL` · 422 `RPC_ERROR` · 504 `RPC_TIMEOUT` |

Error bodies on these routes have the shape `{ "error": "…", "message": "…" }`; RPC timeouts apply after 15 seconds.

---

Web version: https://docs.aureahub.com/#guide-dapps
