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

# Exchange deposits

# Exchange Deposits

Centralized exchange deposit addresses are a top-three attack surface in crypto. Phishing pages clone exchange UIs and substitute attacker-controlled addresses. Malware swaps copied addresses at paste time. Support impersonators DM users with "deposit addresses". Every major exchange has lost customer funds to some variant of this attack, even though the exchange itself was never compromised. Notareum gives exchanges a way to publish deposit addresses as signed `.nota` files that wallets verify before a transaction is signed, closing the attack surface at the wallet layer.

## The problem

A user wants to deposit USDC to an exchange. The legitimate flow is:

1. User navigates to the exchange deposit page.
2. Exchange displays a deposit address (often per-user, rotated periodically).
3. User copies the address into their wallet.
4. User sends funds.

Each of steps 1–3 is attackable independently: DNS hijack, extension-injected UI, clipboard swap, screen-scraping malware. Users have no cryptographic anchor. The wallet sees a bare address and has no way to know it was produced by the exchange.

## The Notareum flow

```mermaid theme={"system"}
sequenceDiagram
    participant Exchange
    participant API
    participant Wallet
    participant Registry
    participant Chain

    Exchange->>Exchange: generate user deposit address (HD derivation)
    Exchange->>API: sign .nota (address, user, exchange identity)
    Exchange->>Wallet: deliver .nota (QR, deeplink, push)
    Wallet->>Wallet: parse + validate signature
    Wallet->>Registry: getResource(resourceId)
    Registry-->>Wallet: verified, level=institutional, not revoked
    Wallet->>Wallet: render "Deposit to CoolExchange (verified)"
    Wallet->>Chain: submit signed transaction
    Chain-->>Exchange: deposit credited
```

The wallet trusts only two things: the exchange's signing key and the on-chain record. It does not trust the page, the clipboard, the messaging channel, or the browser.

## Exchange-side implementation

Exchanges typically operate a single institutional signing key (HSM-backed) that signs all deposit address `.nota` files. The underlying deposit address is derived per user via HD wallets or allocated from a warm pool; the `.nota` wraps whatever scheme the exchange uses.

```typescript theme={"system"}
import { Notareum } from "@notareum/sdk";

const ntm = Notareum({ provider, signer: hsmSigner, contracts });

async function issueDepositNota(userId: string, userAddr: string, chainId: number) {
  const nota = await ntm.nota
    .create({
      type: "address",
      chainId,
      chainName: chainIdToName(chainId),
      identifier: userAddr,
      name: `CoolExchange deposit for ${userId}`,
      description: "Single-use deposit address. Valid 24 hours.",
      resourceMetadata: {
        exchangeUserId: userId,
        validFrom: Math.floor(Date.now() / 1000),
        validUntil: Math.floor(Date.now() / 1000) + 86400,
        depositChainIds: [chainId],
        memoRequired: false,
      },
      issuerName: "CoolExchange",
      issuerEntityType: "organization",
      issuerDid: "did:web:coolexchange.com",
    })
    .validate()
    .sign(hsmSigner);

  return nota.serialize();
}
```

The exchange registers its signing identity **once** at the institutional verification level. Every subsequent deposit address is validated by the wallet against that anchor, with no additional on-chain operations required per deposit. This keeps exchange operating costs negligible.

## Wallet-side verification

```typescript theme={"system"}
import { Notareum, VerificationLevel } from "@notareum/sdk";

const ntm = Notareum({ provider, contracts });

async function verifyDepositAddress(notaStr: string) {
  const nota = ntm.nota.parse(notaStr);
  ntm.nota.validate(nota);

  // The signing identity must be a registered, institutional, non-revoked issuer
  const issuerId = ntm.registry.computeIssuerId(nota.signature.signer);
  const issuer = await ntm.registry.getIssuer(issuerId);
  if (!issuer.exists || issuer.isRevoked) {
    throw new Error("Exchange identity not recognized");
  }
  if (VerificationLevel.rank(issuer.level) < VerificationLevel.rank("institutional")) {
    throw new Error("Exchange below required verification level");
  }

  // Validity window
  const now = Math.floor(Date.now() / 1000);
  const { validFrom, validUntil } = nota.resource.resourceMetadata;
  if (now < validFrom || now > validUntil) {
    throw new Error("Deposit address expired. Request a new one.");
  }

  return {
    address: nota.resource.identifier,
    exchange: nota.issuer.name,
    userId: nota.resource.resourceMetadata.exchangeUserId,
    expiresAt: validUntil,
  };
}
```

Wallets surface this to the user as:

> Depositing to **CoolExchange** (verified institutional)
> User: bob\@coolexchange
> Expires: 2026-04-23 14:30 UTC

## Delivery channels

The `.nota` file is payload, not transport. Exchanges can deliver via:

* **QR code** on the deposit page. Wallets scan and import directly.
* **Deep link** like `notareum://deposit?nota=<b64>`. Wallets register the URI scheme.
* **Push** via WalletConnect v2 sessions.
* **Email or SMS** as attachments (the file is small).

A compromised delivery channel cannot tamper with the payload without invalidating the signature. The wallet will reject anything that does not verify.

## Rotation and expiry

Exchanges rotate deposit addresses for privacy and accounting. Each rotation issues a new `.nota`:

* Short-lived (`validUntil` \< 24h) for per-transaction addresses.
* Long-lived (rolling 30 days) for reusable user addresses.
* Revocation via `NotaRegistry.revokeResource` on compromise.

Wallets enforcing the validity window prevent attackers from replaying a stolen but expired `.nota`.

## Travel Rule compliance

The `.nota` file is a natural carrier for Travel Rule originator/beneficiary data between exchanges. When Exchange A sends to Exchange B, the payment request `.nota` can include a `travelRule` block:

```json theme={"system"}
{
  "travelRule": {
    "originator": {
      "name": "Alice Chen",
      "vaspId": "vasp:coolexchange",
      "reference": "cx:user:12345"
    },
    "beneficiary": {
      "vaspId": "vasp:otherexchange",
      "reference": "oex:user:67890"
    }
  }
}
```

Both VASPs validate the signatures and the content satisfies FATF Recommendation 16 for covered transactions.

## Withdrawal allowlists

The inverse flow works too. A user submits a signed `.nota` of their personal withdrawal address; the exchange stores the `resourceId` as an allowlist entry. Any withdrawal request must resolve to an allowlisted `resourceId`. Because the binding is cryptographic, the exchange can offer "no-confirmation-email" withdrawals to addresses verified through Notareum.

## Integration patterns

* **Wallets** require verified issuer identity before accepting `.nota` imports.
* **Exchanges** publish a single institutional issuer `.nota` and a public directory of their operating subsidiaries.
* **Block explorers** tag known exchange deposit addresses with the issuing exchange identity.
* **Custodians** use the same pattern for cold-wallet receive flows.

## Related pages

* [Wallet Address Sharing](wallet-address-sharing.md)
* [Payment Requests](payment-requests.md)
* [Institutional Treasuries](institutional-treasuries.md)
* [Verification Engine](../protocol/verification-engine.md)
