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

# Exploring account structures

> Compare FBO and omnibus account structures across disclosure and attribution models on both the crypto and USD rails.

export const SectionDrawer = ({title, items, current, sourceHref, outHref, outLabel}) => {
  const fallbackTitle = title || "Account structure";
  const fallbackItems = items || [{
    href: "/platform/developers/account-hierarchy",
    label: "Account hierarchy"
  }, {
    href: "/platform/developers/account-structure-explorer",
    label: "Exploring account structures"
  }, {
    href: "/platform/developers/b2b2x",
    label: "B2B2X accounts"
  }];
  const navSource = sourceHref || "/platform/developers/account-hierarchy";
  const backHref = outHref || "/platform/developers/setting-up";
  const backLabel = outLabel || "All developer docs";
  const [open, setOpen] = useState(false);
  const [path, setPath] = useState(current || "");
  const [drawerTitle, setDrawerTitle] = useState(fallbackTitle);
  const [drawerItems, setDrawerItems] = useState(fallbackItems);
  const panelRef = useRef(null);
  const loadedRef = useRef(false);
  const isCurrent = href => path === href || path.replace(/\/$/, "").endsWith(href);
  useEffect(() => {
    if (!current && typeof window !== "undefined") setPath(window.location.pathname);
  }, [current]);
  useEffect(() => {
    if (!open || loadedRef.current || items) return undefined;
    if (typeof window === "undefined" || typeof DOMParser === "undefined") return undefined;
    loadedRef.current = true;
    const controller = new AbortController();
    const marker = navSource.slice(0, navSource.indexOf("/", 1) + 1);
    const at = window.location.pathname.indexOf(marker);
    const base = at > 0 ? window.location.pathname.slice(0, at) : "";
    fetch(base + navSource, {
      signal: controller.signal,
      credentials: "same-origin"
    }).then(response => response.ok ? response.text() : Promise.reject(response.status)).then(html => {
      const doc = new DOMParser().parseFromString(html, "text/html");
      const self = doc.querySelector('li[id$="' + navSource + '"]');
      const list = self && self.parentElement;
      if (!list) return;
      const found = [];
      Array.prototype.forEach.call(list.children, child => {
        const label = child.getAttribute && child.getAttribute("data-title");
        const link = child.querySelector && child.querySelector("a[href]");
        if (label && link) found.push({
          href: link.getAttribute("href"),
          label: label
        });
      });
      if (found.length) setDrawerItems(found);
      const header = list.previousElementSibling;
      const heading = header && header.textContent ? header.textContent.trim() : "";
      if (heading && heading.length <= 40) setDrawerTitle(heading);
    }).catch(() => {});
    return () => controller.abort();
  }, [open, items, navSource]);
  useEffect(() => {
    if (!open) return undefined;
    const onKey = event => {
      if (event.key === "Escape") setOpen(false);
    };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open]);
  useEffect(() => {
    if (!open || !panelRef.current) return;
    const first = panelRef.current.querySelector("a");
    if (first) first.focus();
  }, [open]);
  return <div className="asx-drawer-root">
      <button type="button" className={"asx-drawer-toggle" + (open ? " asx-drawer-open" : "")} aria-expanded={open} aria-controls="asx-drawer-panel" onClick={() => setOpen(!open)}>
        <span className="asx-drawer-bars" aria-hidden="true">
          <span />
          <span />
          <span />
        </span>
        <span className="asx-drawer-toggle-label">{drawerTitle}</span>
      </button>

      <div className={"asx-drawer-scrim" + (open ? " asx-drawer-open" : "")} aria-hidden="true" onClick={() => setOpen(false)} />

      <nav id="asx-drawer-panel" ref={panelRef} className={"asx-drawer-panel" + (open ? " asx-drawer-open" : "")} aria-label={drawerTitle} aria-hidden={!open}>
        <div className="asx-drawer-head">
          <span className="asx-drawer-heading">{drawerTitle}</span>
          <button type="button" className="asx-drawer-close" aria-label="Close section navigation" tabIndex={open ? 0 : -1} onClick={() => setOpen(false)}>
            ×
          </button>
        </div>

        <ul className="asx-drawer-list">
          {drawerItems.map(item => <li key={item.href}>
              <a className={"asx-drawer-link" + (isCurrent(item.href) ? " asx-drawer-current" : "")} href={item.href} aria-current={isCurrent(item.href) ? "page" : undefined} tabIndex={open ? 0 : -1}>
                {item.label}
              </a>
            </li>)}
        </ul>

        <a className="asx-drawer-out" href={backHref} tabIndex={open ? 0 : -1}>
          {backLabel}
        </a>
      </nav>
    </div>;
};

export const AccountStructureExplorer = () => {
  const asxIsDark = () => typeof document !== "undefined" && (document.documentElement.classList.contains("dark") || document.documentElement.getAttribute("data-theme") === "dark");
  const [dark, setDark] = useState(asxIsDark);
  useEffect(() => {
    if (typeof document === "undefined") return;
    const root = document.documentElement;
    const read = () => setDark(asxIsDark());
    read();
    const obs = new MutationObserver(read);
    obs.observe(root, {
      attributes: true,
      attributeFilter: ["class", "data-theme"]
    });
    return () => obs.disconnect();
  }, []);
  const ASX_C = dark ? {
    bg: "#161617",
    panel: "#1f1f1f",
    ink: "#f4f5f5",
    body: "#cacbce",
    mut: "#9a9da2",
    line: "rgba(228, 229, 231, 0.16)",
    edge: "rgba(228, 229, 231, 0.42)",
    soft: "rgba(228, 229, 231, 0.07)",
    crypto: "#90acf9",
    fiat: "#e4b45c",
    onAccent: "#141415",
    termBg: "#0f0f10",
    termInk: "#f4f5f5",
    termMut: "#8b8f94",
    termDim: "#a3a7ac"
  } : {
    bg: "#f4f5f5",
    panel: "#ffffff",
    ink: "#141415",
    body: "#4e5055",
    mut: "#6b6e73",
    line: "#e4e5e7",
    edge: "#b3b7bd",
    soft: "#f4f5f5",
    crypto: "#5580f6",
    fiat: "#b45309",
    onAccent: "#ffffff",
    termBg: "#141415",
    termInk: "#f4f5f5",
    termMut: "#8b8f94",
    termDim: "#a3a7ac"
  };
  const ASX_FONT = "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif";
  const ASX_MONO = "ui-monospace, SFMono-Regular, Menlo, 'IBM Plex Mono', monospace";
  const ASX_CUSTS = [{
    short: "Ana",
    full: "Ana Torres",
    id: "214"
  }, {
    short: "Ben",
    full: "Ben Okafor",
    id: "587"
  }, {
    short: "Chloe",
    full: "Chloe Kim",
    id: "903"
  }];
  const asxComboSummary = (s, d, a) => {
    const key = `${s}.${d}.${a}`;
    const named = {
      "omnibus.undisclosed.virtual": "In the current product spec this is “pure omnibus” — commingled pool, client-held subledger as source of truth.",
      "omnibus.undisclosed.sub": "In the current product spec this is the “undisclosed standard-subaccount omnibus” — commingled base, but per-customer attribution kept at Anchorage Digital.",
      "fbo.disclosed.sub": "This is the fully-titled end of the spectrum — closest to a standard per-customer account."
    };
    const lines = [s === "fbo" ? "Structure — FBO: assets are held for the benefit of named beneficiaries; beneficial interest is recognized at the account level." : "Structure — omnibus: assets are commingled into one pool; the client is the customer of record and end users exist only as ledger entries.", d === "disclosed" ? "Disclosure — disclosed: end users are known to Anchorage Digital. We may perform some or all of their compliance ourselves, or a reliance agreement ensures the obligations are met by the client." : "Disclosure — undisclosed: end users are not disclosed to Anchorage Digital. Either we act purely as a technology provider, or a reliance agreement lets the client withhold end-user identity except in audit or investigation.", a === "sub" ? "Attribution — subaccounting (direct subledger): the per-customer balance at Anchorage Digital is the source of truth, and payouts are gated by it." : "Attribution — virtual accounting (passthrough subledger): the client's ledger is the source of truth for end-user balances; Anchorage Digital clears against the pool."];
    return {
      lines,
      named: named[key] || null
    };
  };
  const asxBuildCompliance = (s, d, a, rail) => {
    const isFiat = rail === "fiat";
    return [{
      k: "Legal title",
      v: isFiat ? s === "fbo" ? "Correspondent account titled “Anchorage Digital FBO Partner's Customers” — beneficial interest per end user." : "Correspondent account titled “Anchorage Digital — Partner Omnibus.” Client is customer of record; no titled interest per end user." : s === "fbo" ? "Vault held FBO the client's customers — beneficial interest recognized per end user in the custody agreement." : "Commingled custody pool. The client is our customer of record; end-user interest lives in a ledger, not in title."
    }, {
      k: "KYC · OFAC · AML/CTF",
      v: d === "disclosed" ? "End users are known to Anchorage Digital. We perform some or all of the compliance ourselves — or, under a reliance agreement, the client performs it and we verify the regulatory obligations are met." : "End users are not disclosed. Either Anchorage Digital is a technology provider only, or a reliance agreement lets the client keep them undisclosed (surfaced only for audit or investigation). The client owns compliance under its own license; we perform third-line oversight."
    }, {
      k: isFiat ? "Funds-transfer rule" : "Travel rule",
      v: d === "disclosed" ? "Anchorage Digital can collect and transmit originator and beneficiary information itself, or verify it is handled under the reliance agreement." : "Handled inside the client's program; end-user detail reaches Anchorage Digital only in an audit or investigation."
    }, {
      k: "Source of truth — end-user balances",
      v: a === "sub" ? "The Anchorage Digital subaccount ledger (direct subledger model). The per-customer balance we hold is authoritative." : "The client's subledger (passthrough model). Our per-customer references mirror activity, but the client's books are authoritative."
    }, {
      k: "Funds availability check",
      v: a === "sub" ? `At the subaccount. A payout exceeding ${d === "undisclosed" ? "subaccount #214's" : "Ana's subaccount"} balance is rejected, even if the pool has funds.` : "At the pool. A payout clears as long as the pool has funds — a per-customer reference can even run negative. Enforcing Ana's limit is the client's job."
    }, {
      k: "Deposit attribution",
      v: isFiat ? a === "sub" ? "Beneficiary and FFC reference resolves directly to a subaccount record at Anchorage Digital." : "Virtual account number (VAN) on the wire; the client's subledger maps VAN to customer." : a === "sub" ? "Unique deposit address per customer per asset — address identity auto-maps the deposit to the subaccount at Anchorage Digital. Never a memo." : "Unique deposit address per customer — never a memo. Once attributed by address, funds are swept to the shared liquidity address; the address-to-customer map lives with the client."
    }, {
      k: "Anchorage Digital's visibility",
      v: (a === "sub" ? "Per-customer balances and movement, in real time." : "Aggregate pool balance plus attribution references (VANs and deposit-address maps).") + (d === "undisclosed" ? " End users appear to us only as opaque IDs (#214, #587, #903); the ID-to-name map sits at the client, surfaced only for audit or investigation." : "")
    }];
  };
  const asxBuildSteps = (s, d, a, rail) => {
    const isFiat = rail === "fiat";
    const custBook = a === "sub" ? "Anchorage Digital subaccount ledger" : "Partner subledger";
    const dep = isFiat ? "$10,000" : "0.50 BTC";
    const wd = isFiat ? "$4,000" : "0.20 BTC";
    const railName = isFiat ? "Fedwire" : "the chain";
    const custodyName = isFiat ? "correspondent bank" : "Anchorage Digital vault";
    const undis = d === "undisclosed";
    const anaTag = undis ? "#214" : "Ana";
    const anaBook = a === "sub" && undis ? "Subacct #214" : "Ana Torres";
    const titleChip = isFiat ? s === "fbo" ? "Anchorage Digital FBO Partner's Customers" : "Anchorage Digital — Partner Omnibus" : s === "fbo" ? "Vault: FBO Partner's Customers" : "Vault: Partner Omnibus Pool";
    const steps = [{
      title: "Configuration",
      desc: `${s === "fbo" ? `The ${custodyName} account is titled “${titleChip}” — money in it legally belongs to the client's customers, not the client.` : `The ${custodyName} account is a single commingled pool titled “${titleChip}.” The client is the customer of record.`} ${d === "disclosed" ? "Anchorage Digital knows the end users — we ran KYC on them ourselves, or we hold a reliance agreement ensuring the client's KYC meets the obligations." : "End users are not disclosed to Anchorage Digital — the client KYC'd them under its own license, or we are only the technology layer."}`,
      edges: ["ovA", "ovC"],
      chip: null,
      ledger: []
    }, {
      title: `Deposit initiated — ${dep}`,
      desc: isFiat ? `Ana's bank sends ${dep} over Fedwire. ${a === "virtual" ? "The wire carries VAN 4402-0093 so the money can be told apart later." : undis ? "The wire's beneficiary and FFC line carries subaccount ID #214 — only the client's map ties #214 back to Ana." : "The wire's beneficiary and FFC line points at Ana's subaccount."}` : `Ana sends ${dep} from an external wallet. ${d === "disclosed" ? "Travel-rule data on the sender is available to Anchorage Digital — collected directly, or verified under the reliance agreement." : "Travel-rule data is collected inside the client's program."}`,
      edges: ["e1"],
      chip: null,
      ledger: []
    }, {
      title: "Funds land",
      desc: isFiat ? `${dep} settles into the ${s === "fbo" ? "FBO-titled" : "omnibus"} account at the correspondent bank. Physically, it is one balance either way — the difference is who the law says it belongs to.` : `${dep} arrives at Ana's own unique deposit address inside the vault. Attribution on-chain is always by address identity — never by memo. ${a === "sub" ? `In the subaccounting model, this address maps straight to ${undis ? "subaccount #214 at Anchorage Digital — the name never travels" : "Ana's subaccount at Anchorage Digital"}.` : "In the virtual model, this address is just a front door: once attributed, the funds won't stay here."}`,
      edges: ["e2"],
      chip: isFiat ? a === "sub" ? "A" : "pool" : "A",
      ledger: [isFiat ? {
        book: "Correspondent statement",
        lines: [{
          d: "Cr",
          acct: `${titleChip}`,
          amt: "+$10,000.00"
        }],
        note: a === "virtual" ? "ref VAN 4402-0093" : undis ? "FFC: Subacct #214" : "FFC: Subacct — Ana Torres"
      } : {
        book: "On-chain",
        lines: [{
          d: "+",
          acct: `dep addr bc1q…a9f2 (${anaTag})`,
          amt: "+0.50 BTC"
        }],
        note: undis ? "address ↦ cust #214" : "address ↦ Ana Torres"
      }]
    }, {
      title: "Attribution",
      desc: a === "sub" ? `Anchorage Digital auto-maps the ${isFiat ? "VAN or beneficiary reference" : "deposit address"} to ${undis ? "subaccount #214 and credits it on our own subaccount ledger — we see the ID, never the name; the #214-to-Ana map sits at the client" : "Ana's subaccount and credits her on our own subaccount ledger"}. Direct model: this balance is the source of truth — what our books say ${undis ? "#214" : "Ana"} has, Ana has.` : `Anchorage Digital books the deposit to the client's pooled balance. The client's subledger matches the ${isFiat ? "VAN" : "deposit address"} and credits Ana. Passthrough model: the client's ledger is the source of truth — our per-customer reference only mirrors it.`,
      edges: ["e3"],
      chip: a === "sub" ? "A" : "pool",
      ledger: [{
        book: custBook,
        lines: [{
          d: "Cr",
          acct: anaBook,
          amt: isFiat ? "+$10,000.00" : "+0.50 BTC"
        }],
        note: a === "sub" ? undis ? "booked at Anchorage Digital · ID only" : "booked at Anchorage Digital" : "booked at the client"
      }]
    }, {
      title: "Double-entry booking",
      desc: `Our general ledger records the position: an asset (${isFiat ? "due from the correspondent" : "custody assets on-chain"}) offset by a liability to the client${s === "fbo" ? " — held FBO its customers" : ""}. Assets always equal liabilities; the customer detail lives in the ${custBook.toLowerCase()}.`,
      edges: ["e4"],
      chip: null,
      ledger: [{
        book: "Anchorage Digital GL",
        lines: isFiat ? [{
          d: "Dr",
          acct: "Due from correspondent",
          amt: "$10,000.00"
        }, {
          d: "Cr",
          acct: s === "fbo" ? "FBO deposits payable" : "Client deposits payable",
          amt: "$10,000.00"
        }] : [{
          d: "Dr",
          acct: "Custody assets — BTC",
          amt: "0.50 BTC"
        }, {
          d: "Cr",
          acct: s === "fbo" ? "FBO custody liability" : "Client custody liability",
          amt: "0.50 BTC"
        }],
        note: "double entry — always balanced"
      }]
    }, {
      title: "Reconciliation",
      desc: isFiat ? `The general ledger is reconciled to the correspondent statement; balances rest overnight in the designated settlement account. ${a === "sub" ? `We can prove ${undis ? "#214's" : "Ana's"} balance from our own books.` : "We prove the pool; the client proves the people."}` : `The off-chain ownership record is reconciled to the on-chain balance. Any discrepancy is surfaced. ${a === "sub" ? "Per-address balances make this granular to the customer." : "The liquidity pool reconciles in aggregate; customer truth sits in the partner subledger."}`,
      edges: ["e5"],
      chip: null,
      ledger: []
    }, {
      title: `Withdrawal — ${wd}`,
      desc: `Ana withdraws ${wd}. ${a === "sub" ? `Anchorage Digital checks ${undis ? "subaccount #214's" : "Ana's own subaccount"} balance first — if ${wd} exceeded it, the payout would be rejected regardless of the pool.` : `Anchorage Digital checks the pool balance — the payout clears as long as the pool covers it; whether Ana actually has ${wd} is enforced by the client's ledger.`} ${d === "disclosed" ? "We screen the destination and — directly or under reliance — the customer" : "The client screens its customer; we screen the client-level instruction"}, then funds leave ${!isFiat && a === "virtual" ? "from the shared liquidity wallet " : ""}over ${railName} and every book reverses in step.`,
      edges: ["e6a", "e6b"],
      chip: a === "sub" ? "A" : "pool",
      ledger: [isFiat ? {
        book: "Correspondent statement",
        lines: [{
          d: "Dr",
          acct: titleChip,
          amt: "−$4,000.00"
        }],
        note: "Fedwire out"
      } : {
        book: "On-chain",
        lines: [{
          d: "−",
          acct: a === "sub" ? `dep addr bc1q…a9f2 (${anaTag})` : "liquidity bc1q…77e0",
          amt: "−0.20 BTC"
        }],
        note: "broadcast and confirmed"
      }, {
        book: "Anchorage Digital GL",
        lines: isFiat ? [{
          d: "Dr",
          acct: s === "fbo" ? "FBO deposits payable" : "Client deposits payable",
          amt: "$4,000.00"
        }, {
          d: "Cr",
          acct: "Due from correspondent",
          amt: "$4,000.00"
        }] : [{
          d: "Dr",
          acct: s === "fbo" ? "FBO custody liability" : "Client custody liability",
          amt: "0.20 BTC"
        }, {
          d: "Cr",
          acct: "Custody assets — BTC",
          amt: "0.20 BTC"
        }],
        note: "reversing entry"
      }, {
        book: custBook,
        lines: [{
          d: "Dr",
          acct: anaBook,
          amt: isFiat ? "−$4,000.00" : "−0.20 BTC"
        }],
        note: "customer balance reduced"
      }]
    }];
    if (!isFiat && a === "virtual") {
      steps.splice(4, 0, {
        title: "Sweep to liquidity",
        desc: "Now that the deposit is attributed, the 0.50 BTC is swept from Ana's deposit address into the shared liquidity wallet. From here on the coins are commingled — Ana's claim lives in the client's ledger, not at any particular address.",
        edges: ["esw"],
        chip: "pool",
        ledger: [{
          book: "On-chain",
          lines: [{
            d: "−",
            acct: `dep addr bc1q…a9f2 (${anaTag})`,
            amt: "−0.50 BTC"
          }, {
            d: "+",
            acct: "liquidity bc1q…77e0 (shared)",
            amt: "+0.50 BTC"
          }],
          note: "post-attribution sweep"
        }]
      });
    }
    return steps;
  };
  const AsxSeg = ({label, options, value, onChange, accent, note}) => {
    return <div style={{
      minWidth: 210,
      flex: "1 1 210px"
    }}>
        <div style={{
      fontSize: 11,
      letterSpacing: "0.06em",
      color: ASX_C.mut,
      marginBottom: 6,
      fontWeight: 600
    }}>
          {label}
        </div>
        <div style={{
      display: "flex",
      background: ASX_C.soft,
      border: `1px solid ${ASX_C.line}`,
      borderRadius: 10,
      padding: 3,
      gap: 3
    }}>
          {options.map(o => {
      const on = value === o.v;
      const off = !!o.disabled && !on;
      return <button key={o.v} onClick={() => {
        if (!off) onChange(o.v);
      }} aria-pressed={on} aria-disabled={off} title={off ? o.why : undefined} style={{
        flex: 1,
        border: "none",
        cursor: off ? "not-allowed" : "pointer",
        borderRadius: 8,
        padding: "8px 6px",
        fontFamily: ASX_FONT,
        fontSize: 13,
        fontWeight: on ? 700 : 500,
        color: on ? ASX_C.onAccent : ASX_C.ink,
        background: on ? accent || ASX_C.ink : "transparent",
        opacity: off ? 0.38 : 1,
        textDecoration: off ? "line-through" : "none",
        transition: "all 160ms ease"
      }}>
                {o.l}
              </button>;
    })}
        </div>
        {note && <div style={{
      fontSize: 10.5,
      lineHeight: 1.45,
      color: ASX_C.mut,
      marginTop: 6
    }}>
            {note}
          </div>}
      </div>;
  };
  const AsxNode = ({x, y, w, h, title, sub, ghost, accent, active, children}) => {
    return <g opacity={ghost ? 0.45 : 1}>
        <rect x={x} y={y} width={w} height={h} rx={10} fill={ASX_C.panel} stroke={active ? accent : ASX_C.line} strokeWidth={active ? 2 : 1.2} />
        <text x={x + 12} y={y + 22} fontFamily={ASX_FONT} fontSize={11.5} fontWeight={700} fill={ASX_C.ink}>
          {title}
        </text>
        {sub && <text x={x + 12} y={y + 38} fontFamily={ASX_FONT} fontSize={9.5} fill={ASX_C.mut}>
            {sub}
          </text>}
        {children}
      </g>;
  };
  const AsxChip = ({x, y, w, text, pulse, accent, mono}) => {
    return <g>
        <rect x={x} y={y} width={w} height={20} rx={10} fill={pulse ? accent : ASX_C.soft} stroke={pulse ? accent : ASX_C.line} strokeWidth={1} style={{
      transition: "all 200ms"
    }} />
        <text x={x + w / 2} y={y + 13.5} textAnchor="middle" fontFamily={mono ? ASX_MONO : ASX_FONT} fontSize={9} fontWeight={600} fill={pulse ? ASX_C.onAccent : ASX_C.ink}>
          {text}
        </text>
      </g>;
  };
  const AsxEdge = ({d, active, accent, dashed, dir = 1}) => {
    return <g>
        <path d={d} fill="none" stroke={active ? accent : ASX_C.edge} strokeWidth={active ? 2.4 : 1.6} strokeLinecap="round" strokeLinejoin="round" strokeDasharray={dashed ? "5 5" : active ? "6 6" : "none"} style={active && !dashed ? {
      animation: `asxflow 700ms linear infinite ${dir === -1 ? "reverse" : ""}`
    } : {}} markerEnd={active ? "url(#asxArrA)" : "url(#asxArr)"} />
      </g>;
  };
  const AsxDiagram = ({rail, s, d, a, step, chipPulse, edges}) => {
    const accent = rail === "fiat" ? ASX_C.fiat : ASX_C.crypto;
    const isFiat = rail === "fiat";
    const on = id => edges.indexOf(id) !== -1;
    const undis = d === "undisclosed";
    const tag = i => undis ? `#${ASX_CUSTS[i].id}` : ASX_CUSTS[i].short;
    const custodyTitle = isFiat ? "Correspondent bank" : "Anchorage Digital vault";
    const custodySub = isFiat ? s === "fbo" ? "Titled: Anchorage Digital FBO Partner's Customers" : "Titled: Anchorage Digital — Partner Omnibus" : s === "fbo" ? "Held FBO the client's customers" : "Commingled omnibus pool";
    const custBookTitle = a === "sub" ? "Subaccount ledger" : "Partner subledger";
    const custBookSub = a === "sub" ? "direct model" : "passthrough model";
    return <svg viewBox="0 0 1000 400" style={{
      width: "100%",
      height: "auto",
      display: "block"
    }} role="img" aria-label="Flow of funds diagram for the selected account structure">
        <defs>
          {}
          <marker id="asxArr" markerWidth="10" markerHeight="10" refX="7" refY="5" orient="auto">
            <path d="M2,2 L7,5 L2,8" fill="none" stroke={ASX_C.edge} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
          </marker>
          <marker id="asxArrA" markerWidth="10" markerHeight="10" refX="7" refY="5" orient="auto">
            <path d="M2,2 L7,5 L2,8" fill="none" stroke={accent} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
          </marker>
        </defs>

        {}
        {}
        <AsxEdge d="M432,42 C 360,42 180,56 180,122" active={on("ovA")} accent={accent} dashed />
        {}
        <AsxEdge d="M632,82 L632,116" active={on("ovC")} accent={accent} dashed />

        {}
        <AsxNode x={436} y={6} w={240} h={72} title="Client (partner institution)" sub={d === "disclosed" ? "introduces customers" : "licensed · reliance or tech-only"} accent={accent} active={step === 0} />
        <AsxChip x={446} y={48} w={220} text={d === "disclosed" ? "end users known to us" : "end users not disclosed"} pulse={step === 0} accent={accent} />

        {}
        <AsxNode x={24} y={126} w={180} h={64} title="Ana Torres" sub={isFiat ? "sends $10,000" : "sends 0.50 BTC"} accent={accent} active={on("e1") || on("e6b")} />
        {}
        <AsxNode x={24} y={26} w={130} h={34} title="Ben Okafor" ghost accent={accent} />
        <AsxNode x={24} y={76} w={130} h={34} title="Chloe Kim" ghost accent={accent} />

        {}
        {undis && <g>
            <path d="M676,42 L714,42" stroke={accent} strokeDasharray="5 5" strokeLinecap="round" fill="none" strokeWidth={1.4} />
            <rect x={716} y={6} width={260} height={90} rx={10} fill={ASX_C.panel} stroke={accent} strokeWidth={1.2} strokeDasharray="5 3" />
            <text x={728} y={28} fontFamily={ASX_MONO} fontSize={9.5} fontWeight={700} fill={accent}>
              ID map — held at the client
            </text>
            {ASX_CUSTS.map((c, i) => <text key={c.id} x={728} y={48 + i * 16} fontFamily={ASX_MONO} fontSize={9.5} fill={ASX_C.ink}>
                #{c.id} → {c.full}
              </text>)}
          </g>}

        {}
        <AsxNode x={272} y={129} w={156} h={58} title={isFiat ? "Fedwire" : "Blockchain"} sub={isFiat ? "USD wire rail" : "on-chain transfer"} accent={accent} active={on("e1") || on("e2") || on("e6a")} />

        {}
        <AsxNode x={496} y={120} w={272} h={230} title={custodyTitle} sub={custodySub} accent={accent} active={on("e2") || step === 0}>
          {isFiat ? a === "sub" ? <>
                <AsxChip x={508} y={176} w={248} mono text={`Subacct · ${tag(0)} · FFC ref`} pulse={chipPulse === "A"} accent={accent} />
                <AsxChip x={508} y={206} w={248} mono text={`Subacct · ${tag(1)} · FFC ref`} accent={accent} />
                <AsxChip x={508} y={236} w={248} mono text={`Subacct · ${tag(2)} · FFC ref`} accent={accent} />
                <text x={508} y={292} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                  one bank balance, real subaccount
                </text>
                <text x={508} y={310} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                  {undis ? "records per ID — names stay at client" : "records per customer"}
                </text>
              </> : <>
                <AsxChip x={508} y={176} w={248} mono text="one account · VAN routing" pulse={chipPulse === "pool"} accent={accent} />
                <AsxChip x={508} y={212} w={78} mono text={`VAN·${undis ? ASX_CUSTS[0].id : ASX_CUSTS[0].short}`} pulse={chipPulse === "pool"} accent={accent} />
                <AsxChip x={593} y={212} w={78} mono text={`VAN·${undis ? ASX_CUSTS[1].id : ASX_CUSTS[1].short}`} accent={accent} />
                <AsxChip x={678} y={212} w={78} mono text={`VAN·${undis ? ASX_CUSTS[2].id : ASX_CUSTS[2].short}`} accent={accent} />
                <text x={508} y={272} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                  everyone shares the account;
                </text>
                <text x={508} y={290} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                  {undis ? "the VAN carries only an ID" : "the VAN tells deposits apart"}
                </text>
              </> : a === "sub" ? <>
              <AsxChip x={508} y={176} w={248} mono text={`dep addr …a9f2 → ${tag(0)}`} pulse={chipPulse === "A"} accent={accent} />
              <AsxChip x={508} y={206} w={248} mono text={`dep addr …7c31 → ${tag(1)}`} accent={accent} />
              <AsxChip x={508} y={236} w={248} mono text={`dep addr …e08d → ${tag(2)}`} accent={accent} />
              <text x={508} y={292} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                unique address per customer;
              </text>
              <text x={508} y={310} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                {undis ? "address ↦ ID at Anchorage Digital" : "address ↦ subaccount with us"}
              </text>
            </> : <>
              <AsxChip x={508} y={170} w={248} mono text={`dep addr …a9f2 → ${tag(0)}`} pulse={chipPulse === "A"} accent={accent} />
              <AsxChip x={508} y={198} w={248} mono text={`dep addr …7c31 → ${tag(1)}`} accent={accent} />
              <AsxChip x={508} y={226} w={248} mono text={`dep addr …e08d → ${tag(2)}`} accent={accent} />
              {}
              <path d="M632,254 L632,272" stroke={on("esw") ? accent : ASX_C.edge} strokeWidth={on("esw") ? 2.4 : 1.6} strokeLinecap="round" strokeDasharray={on("esw") ? "5 5" : "none"} style={on("esw") ? {
      animation: "asxflow 700ms linear infinite"
    } : {}} markerEnd={on("esw") ? "url(#asxArrA)" : "url(#asxArr)"} />
              <text x={644} y={270} fontFamily={ASX_FONT} fontSize={9} fill={on("esw") ? accent : ASX_C.mut} fontWeight={on("esw") ? 700 : 400}>
                sweep
              </text>
              <AsxChip x={508} y={278} w={248} mono text="liquidity bc1q…77e0 · shared" pulse={chipPulse === "pool"} accent={accent} />
              <text x={508} y={322} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                unique address per deposit — never memo;
              </text>
              <text x={508} y={340} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
                attributed, then swept to the shared pool
              </text>
            </>}
        </AsxNode>

        {}
        <AsxNode x={836} y={120} w={140} h={86} title="General ledger" sub="double-entry" accent={accent} active={on("e4") || on("e5")} />
        <AsxNode x={836} y={226} w={140} h={124} title={custBookTitle} sub={custBookSub} accent={accent} active={on("e3")}>
          <AsxChip x={846} y={280} w={120} text="★ Source of truth" pulse accent={accent} />
          <AsxChip x={846} y={308} w={120} mono text={a === "sub" && undis ? "#214 · #587 · #903" : "Ana · Ben · Chloe"} pulse={on("e3")} accent={accent} />
          <text x={846} y={344} fontFamily={ASX_FONT} fontSize={9} fill={ASX_C.mut}>
            {a === "sub" ? "payout > balance rejected" : "mirrors · can go negative"}
          </text>
        </AsxNode>

        {}
        <AsxEdge d="M208,146 L268,146" active={on("e1")} accent={accent} />
        <AsxEdge d="M432,146 L492,146" active={on("e2")} accent={accent} />
        <AsxEdge d="M772,288 L832,288" active={on("e3")} accent={accent} />
        <AsxEdge d="M772,145 L832,145" active={on("e4")} accent={accent} />
        <AsxEdge d="M832,190 L772,190" active={on("e5")} accent={accent} dashed />
        <AsxEdge d="M492,170 L432,170" active={on("e6a")} accent={accent} />
        <AsxEdge d="M268,170 L208,170" active={on("e6b")} accent={accent} />

        {}
        <text x={24} y={360} fontFamily={ASX_FONT} fontSize={9.5} fill={ASX_C.mut}>
          dashed = oversight and reconciliation · solid = movement of value · animated = active this step
        </text>

        {}
        <text x={496} y={110} fontFamily={ASX_MONO} fontSize={10} fill={accent} fontWeight={700}>
          {isFiat ? "Fiat rail — USD" : "Crypto rail — BTC"}
        </text>
      </svg>;
  };
  const AsxLedgerPanel = ({entries, accent, maxHeight = 300}) => {
    const endRef = useRef(null);
    useEffect(() => {
      if (endRef.current) endRef.current.scrollIntoView({
        behavior: "smooth",
        block: "nearest"
      });
    }, [entries.length]);
    return <div style={{
      fontFamily: ASX_MONO,
      fontSize: 12,
      background: ASX_C.termBg,
      color: ASX_C.termInk,
      borderRadius: 12,
      padding: "14px 16px",
      maxHeight,
      overflowY: "auto",
      lineHeight: 1.55
    }}>
        {entries.length === 0 && <div style={{
      color: ASX_C.termMut
    }}>— no entries yet · select play —</div>}
        {entries.map((e, i) => <div key={i} style={{
      marginBottom: 12,
      animation: "asxrise 320ms ease both"
    }}>
            <div style={{
      color: accent,
      fontWeight: 700,
      fontSize: 10.5,
      letterSpacing: "0.04em"
    }}>
              {e.book}
              {e.note && <span style={{
      color: ASX_C.termMut,
      fontWeight: 400
    }}>  · {e.note}</span>}
            </div>
            {e.lines.map((l, j) => <div key={j} style={{
      display: "flex",
      gap: 10
    }}>
                <span style={{
      width: 22,
      color: ASX_C.termDim
    }}>{l.d}</span>
                <span style={{
      flex: 1
    }}>{l.acct}</span>
                <span>{l.amt}</span>
              </div>)}
          </div>)}
        <div ref={endRef} />
      </div>;
  };
  const [s, setS] = useState("omnibus");
  const [d, setD] = useState("undisclosed");
  const [a, setA] = useState("virtual");
  const [rail, setRail] = useState("crypto");
  const [step, setStep] = useState(0);
  const [playing, setPlaying] = useState(false);
  const [booksOpen, setBooksOpen] = useState(true);
  const accent = rail === "fiat" ? ASX_C.fiat : ASX_C.crypto;
  const steps = asxBuildSteps(s, d, a, rail);
  const compliance = asxBuildCompliance(s, d, a, rail);
  const summary = asxComboSummary(s, d, a);
  useEffect(() => {
    setStep(0);
    setPlaying(false);
  }, [s, d, a, rail]);
  useEffect(() => {
    if (!playing) return;
    const t = setInterval(() => {
      setStep(p => {
        if (p >= steps.length - 1) {
          setPlaying(false);
          return p;
        }
        return p + 1;
      });
    }, 2800);
    return () => clearInterval(t);
  }, [playing, steps.length]);
  const safeStep = Math.min(step, steps.length - 1);
  const cur = steps[safeStep];
  const ledgerEntries = steps.slice(0, safeStep + 1).flatMap(st => st.ledger);
  const btn = (extra = {}) => ({
    border: `1px solid ${ASX_C.line}`,
    background: ASX_C.panel,
    borderRadius: 9,
    padding: "8px 14px",
    fontFamily: ASX_FONT,
    fontSize: 13,
    fontWeight: 600,
    cursor: "pointer",
    color: ASX_C.ink,
    ...extra
  });
  const sectionLabel = {
    fontFamily: ASX_MONO,
    fontSize: 11,
    letterSpacing: "0.04em",
    color: ASX_C.mut,
    fontWeight: 600,
    marginBottom: 10
  };
  return <div className="asx-root" style={{
    background: ASX_C.bg,
    borderRadius: 14,
    border: `1px solid ${ASX_C.line}`,
    padding: 16,
    margin: "1.5rem 0",
    fontFamily: ASX_FONT,
    color: ASX_C.ink
  }}>
      {}
      <div style={{
    background: ASX_C.panel,
    border: `1px solid ${ASX_C.line}`,
    borderRadius: 14,
    padding: 16,
    display: "flex",
    gap: 18,
    flexWrap: "wrap",
    marginBottom: 14
  }}>
        {}
        <AsxSeg label="1 · Legal structure" value={s} onChange={v => {
    setS(v);
    if (v === "fbo") setA("sub");
  }} options={[{
    v: "fbo",
    l: "FBO"
  }, {
    v: "omnibus",
    l: "Omnibus"
  }]} />
        <AsxSeg label="2 · Customer disclosure" value={d} onChange={setD} options={[{
    v: "disclosed",
    l: "Disclosed"
  }, {
    v: "undisclosed",
    l: "Undisclosed"
  }]} />
        <AsxSeg label="3 · Attribution" value={a} onChange={setA} options={[{
    v: "sub",
    l: "Subaccounting"
  }, {
    v: "virtual",
    l: "Virtual",
    disabled: s === "fbo",
    why: "Unavailable under FBO: beneficial interest cannot pass through to a beneficiary whose share is tracked only on the client's ledger. Switch structure 1 to Omnibus."
  }]} note={s === "fbo" ? "Held at subaccounting under FBO — beneficial interest can only pass through a share we track. Switch 1 to Omnibus to compare." : null} />
        <AsxSeg label="Rail" value={rail} onChange={setRail} accent={accent} options={[{
    v: "crypto",
    l: "₿ Crypto"
  }, {
    v: "fiat",
    l: "$ Fiat (USD)"
  }]} />
      </div>

      {}
      <div>
        <div style={{
    position: "relative"
  }}>
          <div className="asx-chart-panel" style={{
    background: ASX_C.panel,
    border: `1px solid ${ASX_C.line}`,
    borderRadius: 14
  }}>
            {}
            <div style={{
    display: "flex",
    gap: 10,
    alignItems: "center",
    flexWrap: "wrap",
    padding: "4px 4px 12px",
    marginBottom: 12,
    borderBottom: `1px solid ${ASX_C.soft}`
  }}>
              <button style={btn()} onClick={() => {
    setPlaying(false);
    setStep(0);
  }}>⟲ Reset</button>
              <button style={btn()} onClick={() => {
    setPlaying(false);
    setStep(p => Math.max(0, p - 1));
  }}>◂ Back</button>
              <button style={btn({
    background: accent,
    color: ASX_C.onAccent,
    border: `1px solid ${accent}`,
    minWidth: 96
  })} onClick={() => {
    if (step >= steps.length - 1) {
      setStep(0);
      setPlaying(true);
    } else setPlaying(p => !p);
  }}>
                {playing ? "❚❚ Pause" : step >= steps.length - 1 ? "▶ Replay" : "▶ Play"}
              </button>
              <button style={btn()} onClick={() => {
    setPlaying(false);
    setStep(p => Math.min(steps.length - 1, p + 1));
  }}>Step ▸</button>
              {}
              <div style={{
    display: "flex",
    gap: 6,
    marginLeft: 4
  }}>
                {steps.map((_, i) => <button key={i} aria-label={`Go to step ${i}`} onClick={() => {
    setPlaying(false);
    setStep(i);
  }} style={{
    width: 10,
    height: 10,
    borderRadius: 5,
    border: "none",
    cursor: "pointer",
    background: i <= safeStep ? accent : ASX_C.line,
    padding: 0
  }} />)}
              </div>
              <div style={{
    marginLeft: "auto",
    fontSize: 12.5,
    color: ASX_C.mut
  }}>
                Step {safeStep} of {steps.length - 1}
              </div>
            </div>

            {}
            <div style={{
    position: "relative"
  }}>
              <AsxDiagram rail={rail} s={s} d={d} a={a} step={safeStep} chipPulse={cur.chip} edges={cur.edges} />

              <div key={safeStep} className="asx-step-callout" style={{
    borderLeftColor: accent
  }}>
                <div style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    marginBottom: 5
  }}>
                  <span style={{
    background: accent,
    color: ASX_C.onAccent,
    fontFamily: ASX_MONO,
    fontSize: 10.5,
    fontWeight: 700,
    letterSpacing: "0.04em",
    borderRadius: 999,
    padding: "2px 9px",
    whiteSpace: "nowrap"
  }}>
                    {safeStep === 0 ? "SETUP" : `STEP ${safeStep} / ${steps.length - 1}`}
                  </span>
                  <span style={{
    fontWeight: 800,
    fontSize: 13.5
  }}>{cur.title}</span>
                </div>
                <div style={{
    fontSize: 12.5,
    lineHeight: 1.5,
    color: ASX_C.body
  }}>{cur.desc}</div>
              </div>
            </div>
          </div>

          {}
          <div className="asx-summary-overlay" style={{
    borderLeft: `4px solid ${accent}`
  }}>
            <div style={{
    ...sectionLabel,
    marginBottom: 8
  }}>
              Structure · Disclosure · Attribution
            </div>
            {summary.lines.map((line, i) => <div key={i} style={{
    fontSize: 12,
    lineHeight: 1.5,
    color: ASX_C.body,
    marginBottom: i < summary.lines.length - 1 ? 5 : 0
  }}>
                {line}
              </div>)}
            {summary.named && <div style={{
    marginTop: 6,
    fontSize: 12,
    color: accent,
    fontWeight: 600
  }}>
                {summary.named}
              </div>}
          </div>

          <div className={booksOpen ? "asx-books-overlay asx-books-open" : "asx-books-overlay"}>
            <div style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    marginBottom: booksOpen ? 10 : 0
  }}>
              <div style={{
    ...sectionLabel,
    marginBottom: 0,
    flex: 1
  }}>
                The books — entries as they post
              </div>
              <button onClick={() => setBooksOpen(p => !p)} aria-expanded={booksOpen} style={btn({
    padding: "2px 10px",
    fontSize: 12
  })}>
                {booksOpen ? "Hide" : `Show${ledgerEntries.length ? ` (${ledgerEntries.length})` : ""}`}
              </button>
            </div>
            {}
            {booksOpen && <AsxLedgerPanel entries={ledgerEntries} accent={accent} maxHeight={122} />}
          </div>
        </div>


        {}
        <div style={{
    background: ASX_C.panel,
    border: `1px solid ${ASX_C.line}`,
    borderRadius: 14,
    padding: 16,
    marginTop: 14
  }}>
          <div style={sectionLabel}>Who holds what — this configuration</div>
          <div className="asx-holds-grid">
            {compliance.map((row, i) => <div key={i} style={{
    padding: "8px 0",
    borderTop: `1px solid ${ASX_C.soft}`
  }}>
                <div style={{
    fontSize: 11.5,
    fontWeight: 700,
    color: accent,
    marginBottom: 2
  }}>{row.k}</div>
                <div style={{
    fontSize: 12.5,
    lineHeight: 1.5,
    color: ASX_C.body
  }}>{row.v}</div>
              </div>)}
          </div>
        </div>
      </div>
    </div>;
};

<SectionDrawer />

<div className="asx-page">
  <div className="asx-page-topnav">
    <a className="asx-page-back" href="/knowledge-base/platform/developers/account-hierarchy">← Account hierarchy</a>
    <span className="asx-page-crumb">Developers · Account structure</span>
    <a className="asx-page-back" href="/knowledge-base/platform/developers/b2b2x">B2B2X accounts →</a>
  </div>

  <div className="asx-page-prose">
    # Exploring account structures

    Infrastructure Services account structures are usually described as single labels—"pure omnibus," "FBO," "virtual accounts"—but each label bundles three independent decisions. This explorer separates them so you can flip one at a time and watch the same deposit take a different path through the books.

    ## The three switches

    Each switch answers a different question, and you can move them almost independently.

    * **FBO or omnibus**—a question of legal title: who the money belongs to at the account level.
    * **Disclosed or undisclosed**—a question of compliance: who runs know your customer (KYC) checks on the end user.
    * **Subaccounting or virtual accounting**—a question of source of truth: whose ledger is authoritative for each customer's balance.

    Switches 1 and 3 have one dependency between them. FBO title passes beneficial interest through to named beneficiaries, and an ownership right can't pass through to a share we don't track. Virtual accounting puts that share on the client's own ledger, so there's nothing at Anchorage Digital for the titled interest to attach to. Selecting **FBO** therefore holds attribution at **Subaccounting**, and **Virtual** is unavailable until you switch back to **Omnibus**.

    The rail switch changes the mechanics that carry the money: a Fedwire transfer into a correspondent account for USD, or an on-chain transfer into a vault for crypto. Attribution differs by rail—USD deposits are told apart by a virtual account number or an FFC reference, while crypto deposits are told apart by deposit address, never by memo.
  </div>

  <AccountStructureExplorer />

  <div className="asx-page-prose">
    ## Reading the diagram

    Solid lines are movement of value. Dashed lines are oversight and reconciliation. The line that animates is the one active in the current step, so stepping through the flow shows the order in which each book posts. The deposit runs left to right along the upper track — customer, rail, custody — and the withdrawal returns along the lower one.

    Play, step, and reset sit directly above the chart. Three panels then overlay it as you move through the flow:

    * **The step callout** appears over the lower left, naming the current step and what it does. It changes as you step.
    * **The books** sits at the bottom right and appends entries as each step fires, so you can watch the same deposit hit the correspondent statement or chain, the general ledger, and the customer book in sequence. It covers the ledger boxes it describes, so **Hide** collapses it to a header bar when you want the chart unobstructed.
    * **Structure · Disclosure · Attribution** sits at the bottom left, restating what the three switches currently mean.

    Below the chart, **Who holds what** turns the same configuration into a set of obligations: legal title, compliance ownership, source of truth, and where the funds availability check happens.

    Ben Okafor and Chloe Kim are ghosted above Ana and never move. They are there because the pool holds their balances too — which is precisely what makes the attribution question matter.

    The starred **Source of truth** badge marks the switch that matters most operationally. Under subaccounting, a payout larger than the customer's own balance is rejected even when the pool has funds. Under virtual accounting, the pool clears the payout and enforcing each customer's limit stays with the client.

    ## What this page doesn't decide

    <Note>
      The seven coherent switch combinations render here for teaching purposes. Which structures Anchorage Digital actually offers, and in which phase, is a product decision rather than a property of the mechanics. The eighth combination is excluded for a mechanical reason, not a product one: FBO can't ride on virtual accounting.
    </Note>

    For the structures available today and the approvals each one needs, see [B2B2X accounts](/knowledge-base/platform/developers/b2b2x). For the client-facing version of the same material, see [B2B2X account structures](/knowledge-base/platform/users/b2b2x-accounts).
  </div>

  <div className="asx-pager">
    <a className="asx-pager-link asx-pager-prev" href="/knowledge-base/platform/developers/account-hierarchy">
      <span className="asx-pager-dir">← Previous</span>
      <span className="asx-pager-title">Account hierarchy</span>
    </a>

    <a className="asx-pager-link asx-pager-next" href="/knowledge-base/platform/developers/b2b2x">
      <span className="asx-pager-dir">Next →</span>
      <span className="asx-pager-title">B2B2X accounts</span>
    </a>
  </div>
</div>


## Related topics

- [B2B2X account structures](/knowledge-base/platform/users/b2b2x-accounts.md)
- [Account hierarchy](/knowledge-base/platform/developers/account-hierarchy.md)
- [Exploring settlement workflows](/knowledge-base/platform/users/atlas-settlement-explorer.md)
- [B2B2X accounts](/knowledge-base/platform/developers/b2b2x.md)
