> ## 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 settlement workflows

> Compare one-way send, one-way receive, and two-way settlement flows across leg scope and instruction channel.

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 AtlasSettlementExplorer = () => {
  const aseIsDark = () => typeof document !== "undefined" && (document.documentElement.classList.contains("dark") || document.documentElement.getAttribute("data-theme") === "dark");
  const [dark, setDark] = useState(aseIsDark);
  useEffect(() => {
    if (typeof document === "undefined") return;
    const root = document.documentElement;
    const read = () => setDark(aseIsDark());
    read();
    const obs = new MutationObserver(read);
    obs.observe(root, {
      attributes: true,
      attributeFilter: ["class", "data-theme"]
    });
    return () => obs.disconnect();
  }, []);
  const ASE_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)",
    proposer: "#90acf9",
    acceptor: "#e4b45c",
    network: "#5fd6e8",
    onAccent: "#141415",
    termBg: "#0f0f10",
    termInk: "#f4f5f5",
    termMut: "#8b8f94"
  } : {
    bg: "#f4f5f5",
    panel: "#ffffff",
    ink: "#141415",
    body: "#4e5055",
    mut: "#6b6e73",
    line: "#e4e5e7",
    edge: "#b3b7bd",
    soft: "#f4f5f5",
    proposer: "#5580f6",
    acceptor: "#b45309",
    network: "#0e8fa3",
    onAccent: "#ffffff",
    termBg: "#141415",
    termInk: "#f4f5f5",
    termMut: "#8b8f94"
  };
  const ASE_FONT = "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif";
  const ASE_MONO = "ui-monospace, SFMono-Regular, Menlo, 'IBM Plex Mono', monospace";
  const ASSETS = ["USD", "USDx_ETH", "USDx_SOL"];
  const fmt = n => Math.abs(n).toLocaleString("en-US");
  const legsFor = (type, scope) => {
    const multi = scope === "multi";
    if (type === "send") {
      return multi ? [{
        a: "USD",
        amt: -10000
      }, {
        a: "USDx_ETH",
        amt: -5000
      }] : [{
        a: "USD",
        amt: -10000
      }];
    }
    if (type === "receive") {
      return multi ? [{
        a: "USD",
        amt: 10000
      }, {
        a: "USDx_ETH",
        amt: 5000
      }] : [{
        a: "USD",
        amt: 10000
      }];
    }
    return multi ? [{
      a: "USD",
      amt: -10000
    }, {
      a: "USDx_ETH",
      amt: -5000
    }, {
      a: "USDx_SOL",
      amt: 15000
    }] : [{
      a: "USD",
      amt: -10000
    }, {
      a: "USDx_ETH",
      amt: 10000
    }];
  };
  const acceptorLegs = legs => legs.map(l => ({
    a: l.a,
    amt: -l.amt
  }));
  const legList = legs => legs.map(l => (l.amt < 0 ? "−" : "+") + fmt(l.amt) + " " + l.a).join(", ");
  const authText = (who, channel) => channel === "api" ? "API: POST /atlas/settlements/{settlementId}/authorize (requires the Authorize settlements permission)" : "UI: authorized in the app via quorum approval, per " + who + "'s organization policy";
  const acceptText = channel => channel === "api" ? "API: POST /atlas/settlements/{settlementId}/accept" : "UI: accepted from the web dashboard";
  const buildSteps = (type, scope, channel) => {
    const pLegs = legsFor(type, scope);
    const aLegs = acceptorLegs(pLegs);
    const needsAcceptorAuth = type !== "send";
    const receivingSide = type === "send" ? "acceptor" : type === "receive" ? "proposer" : "both";
    const chain = needsAcceptorAuth ? ["Proposed", "Authorized", "Accepted", "Authorized", "Executing", "Executed"] : ["Proposed", "Authorized", "Accepted", "Executing", "Executed"];
    const steps = [];
    steps.push({
      title: "Configuration",
      desc: type === "send" ? "Proposer collects the acceptor's participantId and confirms its Atlas participant vault is configured." : "Both sides collect each other's participantId and confirm their Atlas participant vaults are configured.",
      chan: null,
      network: null,
      chain,
      stateIndex: -1,
      edge: null,
      walletsActive: {
        proposer: [],
        acceptor: []
      },
      ledger: []
    });
    steps.push({
      title: "Propose settlement",
      desc: "Proposer calls Propose a settlement with " + legList(pLegs) + ". Amounts exclude fees: negative means sending, positive means receiving.",
      chan: "API: POST /atlas/settlements (no dashboard alternative is documented for proposing)",
      network: "Proposed",
      chain,
      stateIndex: 0,
      edge: "propose",
      walletsActive: {
        proposer: pLegs.map(l => l.a),
        acceptor: []
      },
      ledger: [{
        who: "Proposer",
        action: "Propose a settlement",
        note: "clientReferenceId set by proposer",
        lines: pLegs
      }]
    });
    steps.push({
      title: "Proposer authorizes",
      desc: "The proposed settlement must be authorized before it can move.",
      chan: authText("the proposer", channel),
      network: "Authorized",
      chain,
      stateIndex: 1,
      edge: "authP",
      walletsActive: {
        proposer: pLegs.map(l => l.a),
        acceptor: []
      },
      ledger: [{
        who: "Proposer",
        action: "Authorize settlement",
        note: null,
        lines: []
      }]
    });
    steps.push({
      title: "Acceptor detects the settlement",
      desc: type === "send" ? "Acceptor polls List settlements to find the pending proposal." : "Acceptor polls List settlements to find the pending proposal (having already registered its own participantId with the proposer).",
      chan: channel === "api" ? "API: GET /atlas/settlements (poll)" : "UI: appears in the acceptor's pending settlements queue",
      network: "Proposed",
      chain,
      stateIndex: 1,
      edge: "detect",
      walletsActive: {
        proposer: pLegs.map(l => l.a),
        acceptor: []
      },
      ledger: []
    });
    steps.push({
      title: "Accept",
      desc: "Acceptor selects its vault and wallet(s), then accepts with inverted amounts: " + legList(aLegs) + ". By asset, this must exactly net to zero against the proposer's amounts.",
      chan: acceptText(channel),
      network: "Accepted",
      chain,
      stateIndex: 2,
      edge: "accept",
      walletsActive: {
        proposer: pLegs.map(l => l.a),
        acceptor: aLegs.map(l => l.a)
      },
      ledger: [{
        who: "Acceptor",
        action: "Accept settlement",
        note: "createWallet available per-asset if needed",
        lines: aLegs
      }]
    });
    if (needsAcceptorAuth) {
      steps.push({
        title: "Acceptor authorizes",
        desc: "Because this settlement requires the acceptor to fund a leg, the acceptor must also authorize before execution.",
        chan: authText("the acceptor", channel),
        network: "Authorized",
        chain,
        stateIndex: 3,
        edge: "authA",
        walletsActive: {
          proposer: pLegs.map(l => l.a),
          acceptor: aLegs.map(l => l.a)
        },
        ledger: [{
          who: "Acceptor",
          action: "Authorize settlement",
          note: null,
          lines: []
        }]
      });
    }
    const condDesc = type === "twoway" ? "All four conditions are met: proposer authorized, acceptor accepted, acceptor authorized, and both sides fund their legs plus on-chain fees. Anchorage Digital moves both legs together." : "The proposer is authorized and funded, including on-chain fees. Anchorage Digital moves the funds.";
    steps.push({
      title: "Execute",
      desc: condDesc,
      chan: null,
      network: "Executing → Executed",
      chain,
      stateIndex: needsAcceptorAuth ? 4 : 3,
      edge: "execute",
      dirs: {
        toAcceptor: pLegs.some(l => l.amt < 0),
        toProposer: pLegs.some(l => l.amt > 0)
      },
      gasLegs: pLegs.map(l => ({
        asset: l.a,
        sender: l.amt < 0 ? "proposer" : "acceptor",
        native: nativeGas(l.a)
      })),
      walletsActive: {
        proposer: pLegs.map(l => l.a),
        acceptor: aLegs.map(l => l.a)
      },
      ledger: pLegs.map(l => ({
        who: l.amt < 0 ? "Proposer → Acceptor" : "Acceptor → Proposer",
        action: l.a + " leg settles",
        note: l.a === "USD" ? "Fedwire, no network fee" : "on-chain, network fee applies",
        lines: [l]
      }))
    });
    steps.push({
      title: "Confirm deposit",
      desc: (receivingSide === "both" ? "Both proposer and acceptor" : receivingSide === "acceptor" ? "Acceptor" : "Proposer") + " confirm receipt via List transactions, filtering on type: Deposit.",
      chan: null,
      network: "Executed",
      chain,
      stateIndex: needsAcceptorAuth ? 5 : 4,
      edge: "confirm",
      walletsActive: {
        proposer: pLegs.map(l => l.a),
        acceptor: aLegs.map(l => l.a)
      },
      ledger: []
    });
    steps.push({
      title: "Verify final state",
      desc: "Both proposer and acceptor verify the final state via List settlements.",
      chan: null,
      network: "Executed",
      chain,
      stateIndex: needsAcceptorAuth ? 5 : 4,
      edge: "verify",
      walletsActive: {
        proposer: [],
        acceptor: []
      },
      ledger: []
    });
    return steps;
  };
  const AseSeg = ({label, options, value, onChange, accent}) => <div style={{
    minWidth: 210,
    flex: "1 1 210px"
  }}>
      <div style={{
    fontSize: 11,
    letterSpacing: "0.06em",
    color: ASE_C.mut,
    marginBottom: 6,
    fontWeight: 600
  }}>{label}</div>
      <div style={{
    display: "flex",
    background: ASE_C.soft,
    border: `1px solid ${ASE_C.line}`,
    borderRadius: 10,
    padding: 3,
    gap: 3
  }}>
        {options.map(o => {
    const on = value === o.v;
    return <button key={o.v} onClick={() => onChange(o.v)} aria-pressed={on} style={{
      flex: 1,
      border: "none",
      cursor: "pointer",
      borderRadius: 8,
      padding: "8px 6px",
      fontFamily: ASE_FONT,
      fontSize: 13,
      fontWeight: on ? 700 : 500,
      color: on ? ASE_C.onAccent : ASE_C.ink,
      background: on ? accent || ASE_C.ink : "transparent",
      transition: "all 160ms ease"
    }}>
              {o.l}
            </button>;
  })}
      </div>
    </div>;
  const AseNode = ({x, y, w, h, title, sub, accent, active, children}) => <g>
      <rect x={x} y={y} width={w} height={h} rx={10} fill={ASE_C.panel} stroke={active ? accent : ASE_C.line} strokeWidth={active ? 2 : 1.2} />
      <text x={x + 14} y={y + 24} fontFamily={ASE_FONT} fontSize={12.5} fontWeight={700} fill={ASE_C.ink}>
        {title}
      </text>
      {sub && <text x={x + 14} y={y + 40} fontFamily={ASE_FONT} fontSize={9.5} fill={ASE_C.mut}>
          {sub}
        </text>}
      {children}
    </g>;
  const AseWalletChip = ({asset, active, x, y, accent}) => <g>
      <rect x={x} y={y} width={160} height={22} rx={11} fill={active ? accent : ASE_C.soft} stroke={active ? accent : ASE_C.line} strokeWidth={1} style={{
    transition: "all 200ms"
  }} />
      <text x={x + 80} y={y + 15} textAnchor="middle" fontFamily={ASE_MONO} fontSize={10} fontWeight={600} fill={active ? ASE_C.onAccent : ASE_C.ink}>
        {asset}
      </text>
    </g>;
  const AseEdge = ({d, active, accent, dashed}) => <path d={d} fill="none" stroke={active ? accent : ASE_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"
  } : {}} markerEnd={active ? "url(#aseArrA)" : "url(#aseArr)"} />;
  const AseChain = ({chain, idx, x0, y, xSpan}) => {
    const n = chain.length;
    const step = xSpan / (n - 1);
    return <g>
        <line x1={x0} y1={y} x2={x0 + xSpan} y2={y} stroke={ASE_C.line} strokeWidth={2} strokeLinecap="round" />
        {idx >= 0 && <line x1={x0} y1={y} x2={x0 + step * idx} y2={y} stroke={ASE_C.network} strokeWidth={2} strokeLinecap="round" />}
        {chain.map((label, i) => {
      const cx = x0 + step * i;
      const passed = idx >= 0 && i <= idx;
      const active = i === idx;
      return <g key={label + i}>
              <circle cx={cx} cy={y} r={5} fill={passed ? ASE_C.network : ASE_C.soft} stroke={passed ? ASE_C.network : ASE_C.edge} strokeWidth={1.2} filter={active ? "url(#aseGlow)" : undefined} />
              {active && <circle className="ase-pulse-sm" cx={cx} cy={y} r={5} fill="none" stroke={ASE_C.network} strokeWidth={1.6} />}
            </g>;
    })}
      </g>;
  };
  const AseMesh = ({active, cx, cy, r}) => {
    const pts = [];
    for (let i = 0; i < 6; i++) {
      const ang = Math.PI / 180 * (i * 60 - 90);
      pts.push([Math.round((cx + r * Math.cos(ang)) * 10) / 10, Math.round((cy + r * Math.sin(ang)) * 10) / 10]);
    }
    return <g>
        {pts.map((p, i) => {
      const b = pts[(i + 1) % 6];
      return <line key={"chord" + i} x1={p[0]} y1={p[1]} x2={b[0]} y2={b[1]} stroke={ASE_C.network} strokeWidth={1} opacity={0.18} />;
    })}
        {pts.map((p, i) => <line key={"spoke" + i} x1={cx} y1={cy} x2={p[0]} y2={p[1]} stroke={active ? ASE_C.network : ASE_C.edge} strokeWidth={active ? 1.6 : 1.2} opacity={active ? 0.85 : 0.35} strokeDasharray={active ? "4 4" : "none"} style={active ? {
      animation: "asxflow 900ms linear infinite"
    } : {}} />)}
        {pts.map((p, i) => <circle key={"node" + i} cx={p[0]} cy={p[1]} r={4} fill={active ? ASE_C.network : ASE_C.soft} stroke={active ? ASE_C.network : ASE_C.edge} strokeWidth={1} opacity={active ? 0.75 : 0.5} />)}
        <circle cx={cx} cy={cy} r={7} fill={active ? ASE_C.network : ASE_C.soft} stroke={active ? ASE_C.network : ASE_C.edge} strokeWidth={1.4} filter="url(#aseGlow)" />
        {active && <circle className="ase-pulse-lg" cx={cx} cy={cy} r={7} fill="none" stroke={ASE_C.network} strokeWidth={1.6} />}
      </g>;
  };
  const nativeGas = asset => asset === "USD" ? null : asset === "USDx_SOL" ? "SOL" : "ETH";
  const AseDiagram = ({step, gas}) => {
    const on = id => step.edge === id;
    const isConfig = step.title === "Configuration";
    const netActive = !!step.network;
    const dirs = step.dirs || ({});
    const isExecute = on("execute");
    const gasLegs = step.gasLegs || [];
    const sentBy = side => gasLegs.filter(g => g.sender === side);
    const gasFor = side => {
      const legs = sentBy(side);
      const onChain = legs.filter(g => g.native);
      return {
        sendsNothing: legs.length === 0,
        usdOnly: legs.length > 0 && onChain.length === 0,
        stationActive: gas === "station" && onChain.length > 0,
        natives: Array.from(new Set(onChain.map(g => g.native)))
      };
    };
    const pGas = isExecute ? gasFor("proposer") : null;
    const aGas = isExecute ? gasFor("acceptor") : null;
    const hexPath = (cx, cy, r) => {
      const pts = [];
      for (let i = 0; i < 6; i++) {
        const ang = Math.PI / 180 * (i * 60 - 90);
        pts.push(`${(cx + r * Math.cos(ang)).toFixed(1)},${(cy + r * Math.sin(ang)).toFixed(1)}`);
      }
      return `M${pts.join("L")}Z`;
    };
    const gasNode = (cx, accent, g) => {
      if (!g || g.sendsNothing) return null;
      if (g.usdOnly) {
        return <text x={cx} y={240} textAnchor="middle" fontFamily={ASE_FONT} fontSize={9} fill={ASE_C.mut}>
            USD leg, no gas needed
          </text>;
      }
      if (gas === "wallet") {
        return <text x={cx} y={240} textAnchor="middle" fontFamily={ASE_FONT} fontSize={9} fill={ASE_C.mut}>
            Gas: wallet-funded ({g.natives.join("/")})
          </text>;
      }
      const cy = 240;
      const r = 22;
      return <g>
          <path d={hexPath(cx, cy, r)} fill={g.stationActive ? accent : ASE_C.panel} stroke={g.stationActive ? accent : ASE_C.edge} strokeWidth={g.stationActive ? 2 : 1.2} filter={g.stationActive ? "url(#aseGlow)" : undefined} />
          <text x={cx} y={cy - 3} textAnchor="middle" fontFamily={ASE_MONO} fontSize={8.5} fontWeight={700} fill={g.stationActive ? ASE_C.onAccent : ASE_C.ink}>
            Gas
          </text>
          <text x={cx} y={cy + 8} textAnchor="middle" fontFamily={ASE_MONO} fontSize={7} fill={g.stationActive ? ASE_C.onAccent : ASE_C.mut}>
            station
          </text>
          {g.stationActive && <line x1={cx} y1={cy - r} x2={cx} y2={196} stroke={accent} strokeWidth={2.2} strokeDasharray="4 4" style={{
        animation: "asxflow 700ms linear infinite"
      }} markerEnd="url(#aseArrA)" />}
          <text x={cx} y={cy + r + 14} textAnchor="middle" fontFamily={ASE_FONT} fontSize={8.5} fontWeight={g.stationActive ? 700 : 400} fill={g.stationActive ? accent : ASE_C.mut}>
            {g.stationActive ? `funding ${g.natives.join("/")}` : "ETH · SOL"}
          </text>
        </g>;
    };
    return <svg viewBox="0 0 1000 300" style={{
      width: "100%",
      height: "auto",
      display: "block"
    }} role="img" aria-label="Flow of funds for the selected settlement">
        <defs>
          <marker id="aseArr" markerWidth="10" markerHeight="10" refX="7" refY="5" orient="auto">
            <path d="M2,2 L7,5 L2,8" fill="none" stroke={ASE_C.edge} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
          </marker>
          <marker id="aseArrA" markerWidth="10" markerHeight="10" refX="7" refY="5" orient="auto">
            <path d="M2,2 L7,5 L2,8" fill="none" stroke={ASE_C.proposer} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
          </marker>
          <filter id="aseGlow" x="-60%" y="-60%" width="220%" height="220%">
            <feGaussianBlur stdDeviation="2.2" result="b" />
            <feMerge>
              <feMergeNode in="b" />
              <feMergeNode in="SourceGraphic" />
            </feMerge>
          </filter>
        </defs>

        {}
        <AseNode x={20} y={60} w={210} h={150} title="Proposer" sub="participantId: PARTNER-A01" accent={ASE_C.proposer} active={on("propose") || on("authP") || isConfig}>
          {ASSETS.map((a, i) => <AseWalletChip key={a} asset={a} active={step.walletsActive.proposer.indexOf(a) !== -1} x={34} y={116 + i * 28} accent={ASE_C.proposer} />)}
        </AseNode>

        {}
        <AseNode x={770} y={60} w={210} h={150} title="Acceptor" sub="participantId: PARTNER-B07" accent={ASE_C.acceptor} active={on("accept") || on("authA") || isConfig}>
          {ASSETS.map((a, i) => <AseWalletChip key={a} asset={a} active={step.walletsActive.acceptor.indexOf(a) !== -1} x={784} y={116 + i * 28} accent={ASE_C.acceptor} />)}
        </AseNode>

        {}
        <rect x={330} y={90} width={340} height={120} rx={10} fill={ASE_C.panel} stroke={netActive ? ASE_C.network : ASE_C.line} strokeWidth={netActive ? 2 : 1.2} />
        <text x={344} y={112} fontFamily={ASE_FONT} fontSize={12.5} fontWeight={700} fill={ASE_C.ink}>
          Atlas settlement network
        </text>
        <AseChain chain={step.chain} idx={step.stateIndex} x0={344} y={136} xSpan={190} />
        <text x={344} y={154} fontFamily={ASE_MONO} fontSize={10} fill={step.stateIndex >= 0 ? ASE_C.network : ASE_C.mut}>
          state (illustrative): {step.stateIndex >= 0 ? step.chain[step.stateIndex] : "not yet proposed"}
        </text>
        <AseMesh active={netActive} cx={612} cy={172} r={26} />

        {}
        <AseEdge d="M232,110 C 280,110 300,120 328,135" active={on("propose") || on("authP")} accent={ASE_C.proposer} />
        <AseEdge d="M328,155 C 300,170 280,170 232,170" active={on("detect")} accent={ASE_C.proposer} dashed />
        <AseEdge d="M768,135 C 740,120 700,110 672,110" active={on("accept") || on("authA")} accent={ASE_C.acceptor} />

        {}
        <AseEdge d="M768,45 C 600,15 400,15 232,45" active={on("execute") && dirs.toProposer} accent={ASE_C.acceptor} />
        <AseEdge d="M232,225 C 400,250 600,250 768,225" active={on("execute") && dirs.toAcceptor} accent={ASE_C.proposer} />
        <AseEdge d="M232,238 C 400,265 600,265 768,238" active={on("confirm") || on("verify")} accent={ASE_C.edge} dashed />

        {}
        {gasNode(114, ASE_C.proposer, pGas)}
        {gasNode(864, ASE_C.acceptor, aGas)}
      </svg>;
  };
  const AseLedgerPanel = ({entries, maxHeight = 220}) => {
    const boxRef = useRef(null);
    useEffect(() => {
      if (boxRef.current) boxRef.current.scrollTop = boxRef.current.scrollHeight;
    }, [entries.length]);
    return <div ref={boxRef} style={{
      fontFamily: ASE_MONO,
      fontSize: 12,
      background: ASE_C.termBg,
      color: ASE_C.termInk,
      borderRadius: 12,
      padding: "12px 14px",
      maxHeight,
      overflowY: "auto",
      lineHeight: 1.5,
      scrollBehavior: "smooth"
    }}>
        {entries.length === 0 && <div style={{
      color: ASE_C.termMut
    }}>No requests yet. Select play.</div>}
        {entries.map((e, i) => <div key={i} style={{
      marginBottom: 10,
      animation: "asxrise 320ms ease both"
    }}>
            <div style={{
      fontSize: 10.5,
      fontWeight: 700,
      letterSpacing: "0.04em"
    }}>
              {e.action}
              {e.note && <span style={{
      color: ASE_C.termMut,
      fontWeight: 400
    }}>  · {e.note}</span>}
            </div>
            {e.lines.map((l, j) => <div key={j} style={{
      display: "flex",
      gap: 10
    }}>
                <span style={{
      width: 84,
      color: ASE_C.termMut,
      flexShrink: 0
    }}>{e.who}</span>
                <span style={{
      flex: 1
    }}>{l.a}</span>
                <span>{l.amt < 0 ? "−" : "+"}{fmt(l.amt)}</span>
              </div>)}
          </div>)}
      </div>;
  };
  const [type, setType] = useState("send");
  const [scope, setScope] = useState("single");
  const [channel, setChannel] = useState("api");
  const [gas, setGas] = useState("station");
  const [step, setStep] = useState(0);
  const [playing, setPlaying] = useState(false);
  const [ledgerOpen, setLedgerOpen] = useState(true);
  const steps = buildSteps(type, scope, channel);
  useEffect(() => {
    setStep(0);
    setPlaying(false);
  }, [type, scope, channel, gas]);
  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(s => s.ledger);
  const btn = (extra = {}) => ({
    border: `1px solid ${ASE_C.line}`,
    background: ASE_C.panel,
    borderRadius: 9,
    padding: "8px 14px",
    fontFamily: ASE_FONT,
    fontSize: 13,
    fontWeight: 600,
    cursor: "pointer",
    color: ASE_C.ink,
    ...extra
  });
  const sectionLabel = {
    fontFamily: ASE_MONO,
    fontSize: 11,
    letterSpacing: "0.04em",
    color: ASE_C.mut,
    fontWeight: 600,
    marginBottom: 10
  };
  return <div className="ase-root" style={{
    background: ASE_C.bg,
    borderRadius: 14,
    border: `1px solid ${ASE_C.line}`,
    padding: 16,
    margin: "1.5rem 0",
    fontFamily: ASE_FONT,
    color: ASE_C.ink
  }}>
      <div style={{
    background: ASE_C.panel,
    border: `1px solid ${ASE_C.line}`,
    borderRadius: 14,
    padding: 16,
    display: "flex",
    gap: 18,
    flexWrap: "wrap",
    marginBottom: 14
  }}>
        <AseSeg label="1 · Settlement type" value={type} onChange={setType} options={[{
    v: "send",
    l: "Send"
  }, {
    v: "receive",
    l: "Receive"
  }, {
    v: "twoway",
    l: "Two-way"
  }]} />
        <AseSeg label="2 · Leg scope" value={scope} onChange={setScope} options={[{
    v: "single",
    l: "Single-leg"
  }, {
    v: "multi",
    l: "Multi-leg"
  }]} />
        <AseSeg label="3 · Instruction channel" value={channel} onChange={setChannel} options={[{
    v: "api",
    l: "API"
  }, {
    v: "ui",
    l: "UI (quorum)"
  }]} />
        <AseSeg label="4 · Gas source" value={gas} onChange={setGas} options={[{
    v: "station",
    l: "Gas station"
  }, {
    v: "wallet",
    l: "Wallet-funded"
  }]} />
      </div>

      <div style={{
    background: ASE_C.panel,
    border: `1px solid ${ASE_C.line}`,
    borderRadius: 14,
    padding: 12
  }}>
        <div style={{
    display: "flex",
    gap: 10,
    alignItems: "center",
    flexWrap: "wrap",
    padding: "4px 4px 12px",
    marginBottom: 12,
    borderBottom: `1px solid ${ASE_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: ASE_C.network,
    color: ASE_C.onAccent,
    border: `1px solid ${ASE_C.network}`,
    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 ? ASE_C.network : ASE_C.line,
    padding: 0
  }} />)}
          </div>
          <div style={{
    marginLeft: "auto",
    fontSize: 12.5,
    color: ASE_C.mut
  }}>
            Step {safeStep} of {steps.length - 1}
          </div>
        </div>

        <AseDiagram step={cur} gas={gas} />

        {}
        <div key={safeStep} style={{
    marginTop: 12,
    background: ASE_C.panel,
    border: `1px solid ${ASE_C.line}`,
    borderLeft: `4px solid ${ASE_C.network}`,
    borderRadius: 10,
    padding: "12px 14px",
    animation: "asxnotify 260ms cubic-bezier(0.16, 1, 0.3, 1) both"
  }}>
          <div style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    marginBottom: 5
  }}>
            <span style={{
    background: ASE_C.network,
    color: ASE_C.onAccent,
    fontFamily: ASE_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: ASE_C.body
  }}>{cur.desc}</div>
          {cur.chan && <div style={{
    fontSize: 11.5,
    lineHeight: 1.5,
    color: ASE_C.mut,
    fontFamily: ASE_MONO,
    marginTop: 6
  }}>{cur.chan}</div>}
        </div>

        <div style={{
    marginTop: 10,
    fontSize: 9.5,
    color: ASE_C.mut
  }}>
          dashed = oversight/query · solid = movement of value or instruction · animated = active this step
        </div>
      </div>

      <div style={{
    background: ASE_C.panel,
    border: `1px solid ${ASE_C.line}`,
    borderLeft: `4px solid ${ASE_C.network}`,
    borderRadius: 14,
    padding: "12px 16px",
    marginTop: 14
  }}>
        <div style={{
    ...sectionLabel,
    marginBottom: 8
  }}>Settlement · leg scope · channel · gas</div>
        <div style={{
    fontSize: 12.5,
    lineHeight: 1.55,
    color: ASE_C.body,
    marginBottom: 6
  }}>
          <strong>Type:</strong> {type === "send" ? "Send (one-way)" : type === "receive" ? "Receive (one-way)" : "Two-way"}
        </div>
        <div style={{
    fontSize: 12.5,
    lineHeight: 1.55,
    color: ASE_C.body,
    marginBottom: 6
  }}>
          <strong>Leg scope:</strong> {scope === "single" ? "Single-leg, one asset amount per direction" : "Multi-leg, several asset amounts bundled into one settlement instruction"}
        </div>
        <div style={{
    fontSize: 12.5,
    lineHeight: 1.55,
    color: ASE_C.body,
    marginBottom: 6
  }}>
          <strong>Channel:</strong> {channel === "api" ? "API, authorize/accept called directly against the endpoints" : "UI, authorize/accept happen in the web dashboard, gated by org quorum policy"}
        </div>
        <div style={{
    fontSize: 12.5,
    lineHeight: 1.55,
    color: ASE_C.body
  }}>
          <strong>Gas source:</strong> {gas === "station" ? "Gas station, tops up ETH or SOL, whichever the moving leg needs; USD legs never need gas at all" : "Wallet-funded, every sending wallet covers its own ETH or SOL gas; USD legs never need gas at all"}
        </div>
      </div>

      <div style={{
    background: ASE_C.panel,
    border: `1px solid ${ASE_C.line}`,
    borderRadius: 14,
    padding: 16,
    marginTop: 14
  }}>
        <div style={{
    display: "flex",
    alignItems: "center",
    gap: 8,
    marginBottom: 10
  }}>
          <div style={{
    ...sectionLabel,
    marginBottom: 0,
    flex: 1
  }}>Requests as they post</div>
          <button onClick={() => setLedgerOpen(p => !p)} aria-expanded={ledgerOpen} style={btn({
    padding: "2px 10px",
    fontSize: 12
  })}>
            {ledgerOpen ? "Hide" : `Show${ledgerEntries.length ? ` (${ledgerEntries.length})` : ""}`}
          </button>
        </div>
        {ledgerOpen && <AseLedgerPanel entries={ledgerEntries} />}
      </div>

      <div style={{
    background: ASE_C.panel,
    border: `1px solid ${ASE_C.line}`,
    borderRadius: 14,
    padding: 16,
    marginTop: 14
  }}>
        <div style={sectionLabel}>Who does what: this configuration</div>
        <div className="asx-holds-grid">
          {[["Proposer authorization", "Required in every settlement type"], ["Acceptor authorization", type === "send" ? "Not required, accept only" : "Required, acceptor funds a leg"], ["Quorum: initiator org", "Quorum approval once initiated"], ["Quorum: acceptor org", type === "send" ? "Accept only" : type === "receive" ? "Accept + quorum approval" : "Accept and quorum approve to send"], ["Funding required before execution", type === "twoway" ? "Both sides: settlement amount plus on-chain fees" : "Proposer: settlement amount plus on-chain fees"], ["Instruction channel", channel === "api" ? "Authorizing via API requires the key to hold the Authorize settlements permission." : "Authorization happens in the app, according to your organization's policy."], ["Network fees", "USD legs are fee-exempt, per the integration guide. USDx_ETH and USDx_SOL legs are on-chain and incur a network fee."], ["Gas Station eligibility", "Gas Station covers both ETH and SOL: USDx_ETH tops up in ETH, USDx_SOL tops up in SOL. USD settles over Fedwire and needs no gas from either source, regardless of the switch."]].map((row, i) => <div key={i} style={{
    padding: "8px 0",
    borderTop: `1px solid ${ASE_C.soft}`
  }}>
              <div style={{
    fontSize: 11.5,
    fontWeight: 700,
    color: ASE_C.network,
    marginBottom: 2
  }}>{row[0]}</div>
              <div style={{
    fontSize: 12.5,
    lineHeight: 1.5,
    color: ASE_C.body
  }}>{row[1]}</div>
            </div>)}
        </div>
      </div>
    </div>;
};

<SectionDrawer
  title="Atlas"
  items={[
{ href: "/platform/developers/atlas/atlas-settlement-api", label: "Settlement API integration guide" },
{ href: "/platform/developers/atlas/atlas-collateral-management", label: "Atlas collateral management" }
]}
  sourceHref="/platform/developers/atlas/atlas-settlement-api"
  outHref="/platform/developers/setting-up"
  outLabel="All developer docs"
/>

<div className="asx-page">
  <div className="asx-page-topnav">
    <a className="asx-page-back" href="/knowledge-base/platform/developers/atlas/atlas-settlement-api">← Settlement API integration guide</a>
    <span className="asx-page-crumb">Developers · Atlas · Settlement explorer</span>
    <a className="asx-page-back" href="/knowledge-base/platform/developers/atlas/atlas-collateral-management">Atlas collateral management →</a>
  </div>

  <div className="asx-page-prose">
    # Exploring settlement workflows

    Send, receive, and two-way settlements share the same propose, authorize, and accept mechanics, but differ in who has to authorize and when execution can fire. This explorer walks through the exact sequence for any combination of settlement type, leg scope, and instruction channel.

    ## The four switches

    * **Settlement type**: send (one-way), receive (one-way), or two-way. See [Settlement workflows](/knowledge-base/platform/developers/atlas/atlas-settlement-api#settlement-workflows) for the source procedures.
    * **Leg scope**: single-leg or multi-leg, meaning how many asset amounts are bundled into one settlement instruction.
    * **Instruction channel**: API or UI, meaning whether authorize and accept happen directly against the endpoints, or in the web dashboard under your organization's quorum policy.
    * **Gas source**: gas station or wallet-funded, per [`useGasStation`](/knowledge-base/platform/developers/network-gas-fees#gas-station), meaning whether the on-chain gas for a leg is topped up from the organization's gas station, or paid from the sending wallet's own balance. USD legs never need gas at all; they settle over Fedwire, not a blockchain.
  </div>

  <AtlasSettlementExplorer />

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

    The proposer and acceptor sit on either side of the Atlas settlement network. Wallet chips light up for the asset or assets actually moving in the current step: one chip under single-leg, several under multi-leg. Solid lines are movement of value or an instruction call; dashed lines are polling or verification. The line that animates is the one active in the current step.

    The network node's connected-node chain is an illustrative stand-in for settlement state, not a documented enum. Only **Executing** and **Executed** are states the Settlement API integration guide actually names. Every earlier label just names the step most recently completed.

    <Note>
      This walkthrough uses the literal USD wallet alongside two illustrative on-chain stablecoins, USDx\_ETH and USDx\_SOL (not Anchorage Digital tickers). USD is fee-exempt and needs no gas, per the integration guide. It settles over Fedwire, not a blockchain. USDx\_ETH and USDx\_SOL are on-chain, so each incurs a network fee and draws gas from either the [gas station](/knowledge-base/platform/developers/network-gas-fees#gas-station) or the sending wallet, depending on the gas source switch: ETH for USDx\_ETH, SOL for USDx\_SOL.
    </Note>

    For the authoritative endpoint list, required permissions, and quorum table, see [Settlement API integration guide](/knowledge-base/platform/developers/atlas/atlas-settlement-api).
  </div>

  <div className="asx-pager">
    <a className="asx-pager-link asx-pager-prev" href="/knowledge-base/platform/developers/atlas/atlas-settlement-api">
      <span className="asx-pager-dir">← Previous</span>
      <span className="asx-pager-title">Settlement API integration guide</span>
    </a>

    <a className="asx-pager-link asx-pager-next" href="/knowledge-base/platform/developers/atlas/atlas-collateral-management">
      <span className="asx-pager-dir">Next →</span>
      <span className="asx-pager-title">Atlas collateral management</span>
    </a>
  </div>
</div>


## Related topics

- [Exploring settlement workflows](/knowledge-base/platform/users/atlas-settlement-explorer.md)
- [Settlement API integration guide](/knowledge-base/platform/developers/atlas/atlas-settlement-api.md)
- [Exploring account structures](/knowledge-base/platform/developers/account-structure-explorer.md)
- [API Changelog](/knowledge-base/porto/api-reference/changelog.md)
