> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privacyboost.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Portal deposits

# Portal Deposit Addresses

Give a user a reusable, public, exchange-style deposit address. Anyone can fund
it with standard ERC-20 transfers, and the funds are credited into the owner's
shielded balance without revealing which account was credited. See
[Portal Deposit Addresses](/sdk/concepts/portal-deposits) for the concept and
trust model.

<Warning>
  Preview feature — pending external audit, and enabled per deployment. Several
  fields are launch-limited; the limitations are called out inline below.
</Warning>

The TypeScript SDK exposes the full portal lifecycle through the `sdk.portal`
resource.

## Checking support

`delegateAddress()` returns the portal delegate address advertised by the
server's `/info`, or `undefined` when the server does not support portals:

```typescript theme={null}
if (!sdk.portal.delegateAddress()) {
  throw new Error('This server does not support portal deposits.');
}
```

## Creating a portal

`create` does everything in one call: it derives a fresh portal address `E` from
your seed, delegates it to the portal implementation, registers the on-chain
owner binding, and publishes the discovery registry entry.

```typescript theme={null}
const portal = await sdk.portal.create();
console.log('Deposit address:', portal.address); // share this E publicly
console.log('Derivation index:', portal.index);
```

Pin a derivation index to re-derive a specific portal deterministically:

```typescript theme={null}
const portal = await sdk.portal.create({ index: 0 });
```

## Listing your portals

```typescript theme={null}
const portals = await sdk.portal.list();
for (const p of portals) {
  console.log(p.address, p.status.registered, p.status.published);
}
```

## Checking a portal's status

```typescript theme={null}
const status = await sdk.portal.status(portal.address);
// { registered: boolean, delegated: boolean, published: boolean }
```

<Warning>
  **Launch limitation:** `status.delegated` always reports `false` — the
  discovery server does not yet compute on-chain delegation status. A portal you
  created with `create()` **is** delegated regardless. Do not gate a "ready"
  check on this field; use `registered` and `published`.
</Warning>

## Receiving deposits

A sender funds a portal with an ordinary ERC-20 transfer to `E` — they need
nothing from Privacy Boost. List the deposits observed at a portal:

```typescript theme={null}
const deposits = await sdk.portal.deposits(portal.address);
for (const d of deposits) {
  console.log(d.portalDepositId, d.state, d.netAmount);
}
```

A deposit moves through these states:

| `state`         | Meaning                                       |
| --------------- | --------------------------------------------- |
| `pending_sweep` | Funds are at `E`, not yet swept into the pool |
| `credited`      | Swept and credited to your shielded balance   |
| `cancellable`   | Eligible to be reclaimed                      |
| `cancelled`     | Reclaimed back to `E`                         |
| `unknown`       | State could not be determined                 |

<Warning>
  **Launch limitations:** `cancellableAtBlock` is always `0` (treat as
  *unknown*, not "reclaimable now"), and at launch the sweep fee is `0`, so
  `grossAmount` equals `netAmount`.
</Warning>

## Sweeping

Sweeping moves a portal's balance into the pool. At launch this is operator-run,
so you normally do not call it — but `sweep` is available as a self-service
backstop. It returns the sweep transaction hash:

```typescript theme={null}
const txHash = await sdk.portal.sweep(portal.address, tokenId);
```

## Reclaiming an un-credited deposit

If a swept deposit is never credited, reclaim it after the cancel delay. The
funds return to the portal address `E`, never to the caller:

```typescript theme={null}
const txHash = await sdk.portal.reclaim(depositId);
```

## Withdrawing raw funds from `E` (escape hatch)

For funds resting at `E` that can never be swept (for example a token that is not
registered with the protocol), `withdraw` builds a **signed raw EIP-1559
transaction** you broadcast yourself. Read the nonce and gas/fee values from your
own RPC:

```typescript theme={null}
const signedRawTx = sdk.portal.withdraw({
  index: portal.index,
  token: '0x...token-address',
  to: '0xDestination',
  amount: 1000000000000000000n,
  nonce: await publicClient.getTransactionCount({ address: portal.address }),
  gasLimit: 100000n,
  maxFeePerGas: 20000000000n,
  maxPriorityFeePerGas: 1000000000n,
});
// Broadcast signedRawTx yourself, e.g. publicClient.sendRawTransaction({ serializedTransaction: signedRawTx })
```

## Types

```typescript theme={null}
interface Portal {
  address: string; // the portal deposit address E
  index: number;   // seed-derivation index — re-derives E's key
  bindH: string;   // on-chain owner binding H (0x-prefixed 32-byte hex)
}

type PortalDepositState =
  | 'pending_sweep'
  | 'credited'
  | 'cancellable'
  | 'cancelled'
  | 'unknown';

interface PortalStatus {
  registered: boolean;
  delegated: boolean; // launch limitation: always false (see above)
  published: boolean;
}

interface PortalInfo {
  address: string;
  index?: number;
  status: PortalStatus;
}

interface PortalDeposit {
  portalDepositId: string; // key for reclaim()
  portal: string;
  tokenId: number;
  grossAmount: bigint;
  netAmount: bigint;
  state: PortalDepositState;
  requestBlock: number;
  cancellableAtBlock: number; // launch limitation: always 0 (treat as unknown)
}

interface CreatePortalOptions {
  index?: number;
}

interface WithdrawPortalParams {
  index: number;
  token: string;
  to: string;
  amount: bigint;
  nonce: number | bigint;
  gasLimit: bigint;
  maxFeePerGas: bigint;
  maxPriorityFeePerGas: bigint;
}
```
