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

# Quantum Intelligence

> Cross-cutting cryptographic risk context — exposure score, vault readiness, dormancy signals, migration readiness, and behavioral signals.

export const deriveDocsQuantumExposureScore = input => {
  const WEIGHTS = {
    signatureExposureObserved: 35,
    noOutgoingSignatureBaseline: 5,
    dormancy: {
      ancient_dormant_730d_plus: 20,
      cold_dormant_180_730d: 12,
      warm_dormant_30_180d: 5,
      active_0_30d: 0
    },
    valueAtRisk: {
      veryHigh: 25,
      high: 18,
      meaningful: 10,
      nonzero: 5
    },
    keySignatureAge: {
      ancient: 15,
      mature: 10,
      established: 5
    },
    migrationReadinessReduction: 10
  };
  const LABELS = {
    unknown: 'Unknown / insufficient data',
    low: 'Low exposure',
    moderate: 'Moderate exposure',
    elevated: 'Elevated exposure',
    migration_priority: 'Migration priority'
  };
  const BANDS = [{
    min: 75,
    key: 'migration_priority'
  }, {
    min: 50,
    key: 'elevated'
  }, {
    min: 25,
    key: 'moderate'
  }, {
    min: 0,
    key: 'low'
  }];
  const DORMANCY_ANCHORS = [[30, 0], [180, 5], [730, 12], [1825, 20]];
  const VALUE_ANCHORS = [[1, 0], [10000, 10], [100000, 18], [1000000, 25]];
  const AGE_ANCHORS = [[1, 0], [365, 5], [1095, 10], [1825, 15]];
  const LADDER = ['low', 'medium', 'high'];
  const interp = (x, anchors) => {
    const [firstX, firstY] = anchors[0];
    if (Number.isNaN(x) || typeof x !== 'number' || x <= 0 || x <= firstX) return firstY;
    const [lastX, lastY] = anchors[anchors.length - 1];
    if (x >= lastX) return lastY;
    const logX = Math.log2(x);
    for (let i = 1; i < anchors.length; i++) {
      const [x1, y1] = anchors[i];
      if (x <= x1) {
        const [x0, y0] = anchors[i - 1];
        return y0 + (y1 - y0) * ((logX - Math.log2(x0)) / (Math.log2(x1) - Math.log2(x0)));
      }
    }
    return lastY;
  };
  const scoreDormancy = d => Math.round(interp(d, DORMANCY_ANCHORS));
  const scoreValue = v => Math.round(interp(v, VALUE_ANCHORS));
  const scoreAge = a => Math.round(interp(a, AGE_ANCHORS));
  const asNumber = v => typeof v === 'number' && Number.isFinite(v) ? v : null;
  const bandFor = score => {
    for (const band of BANDS) if (score >= band.min) return band.key;
    return 'low';
  };
  const signatureSignal = (status, txSeen) => {
    if (status === 'signature_exposure_observed') {
      return {
        key: 'signature_exposure',
        basis: 'observed',
        label: 'Outgoing signature activity observed',
        weight: WEIGHTS.signatureExposureObserved,
        points: WEIGHTS.signatureExposureObserved,
        detail: 'The public key is recoverable once an account has signed an outgoing transaction.'
      };
    }
    if (status === 'no_outgoing_signature_observed') {
      return {
        key: 'signature_exposure',
        basis: txSeen ? 'observed' : 'unknown',
        label: 'No outgoing signature observed',
        weight: WEIGHTS.signatureExposureObserved,
        points: txSeen ? WEIGHTS.noOutgoingSignatureBaseline : 0,
        detail: txSeen ? 'History is visible and shows no outgoing signature so far — a small observed baseline.' : 'No transaction history was available to confirm signature exposure, so no points are added.'
      };
    }
    if (status === 'contract_wallet') {
      return {
        key: 'signature_exposure',
        basis: 'observed',
        label: 'Contract wallet signature model',
        weight: WEIGHTS.signatureExposureObserved,
        points: 0,
        detail: 'Contract wallets follow a different signature exposure model than EOAs.'
      };
    }
    return null;
  };
  const dormancySignal = days => days == null ? {
    key: 'dormancy',
    basis: 'unknown',
    label: 'Dormancy unavailable',
    weight: WEIGHTS.dormancy.ancient_dormant_730d_plus,
    points: 0,
    detail: 'Last-activity age is unknown, so no dormancy weight is applied.'
  } : {
    key: 'dormancy',
    basis: 'observed',
    label: 'Last-activity dormancy',
    weight: WEIGHTS.dormancy.ancient_dormant_730d_plus,
    points: scoreDormancy(days),
    detail: `Long-dormant wallets can delay owner attention and migration planning (${Math.round(days)} days since last activity).`
  };
  const valueSignal = usd => usd == null || usd < 0 ? {
    key: 'value_at_risk',
    basis: 'unknown',
    label: 'Value at risk unavailable',
    weight: WEIGHTS.valueAtRisk.veryHigh,
    points: 0,
    detail: 'Reported balance is unknown, so no value weight is applied.'
  } : {
    key: 'value_at_risk',
    basis: 'observed',
    label: 'Reported value at risk',
    weight: WEIGHTS.valueAtRisk.veryHigh,
    points: scoreValue(usd),
    detail: 'Higher reported value raises the consequence of delayed migration planning.'
  };
  const ageSignal = days => days == null ? {
    key: 'key_signature_age',
    basis: 'unknown',
    label: 'Key / signature age unavailable',
    weight: WEIGHTS.keySignatureAge.ancient,
    points: 0,
    detail: 'First-seen / first-signature timestamps are unknown, so no age weight is applied.'
  } : {
    key: 'key_signature_age',
    basis: 'observed',
    label: 'Key / signature age',
    weight: WEIGHTS.keySignatureAge.ancient,
    points: scoreAge(days),
    detail: `Older keys have a longer window of on-chain signature visibility (${Math.round(days)} days old).`
  };
  const exposureStatus = input?.exposureStatus ?? 'unknown';
  const txObserved = input?.txObserved === true;
  const daysDormant = asNumber(input?.daysDormant);
  const totalBalanceUsd = asNumber(input?.totalBalanceUsd);
  const keyAgeDays = asNumber(input?.keyAgeDays);
  const migrationPathPresent = input?.migrationPathPresent === true;
  if (!exposureStatus || exposureStatus === 'unknown') {
    return Object.freeze({
      score: null,
      status: 'insufficient_data',
      label: LABELS.unknown,
      bandKey: 'unknown',
      confidence: 'low',
      signals: Object.freeze([]),
      basePoints: 0,
      migrationOffset: 0,
      evidenceCount: 0,
      explanation: 'No chain identity or exposure classification is available, so no score is emitted. Unknown is a distinct outcome — it is never a synonym for a low score.'
    });
  }
  const signals = [signatureSignal(exposureStatus, txObserved), dormancySignal(daysDormant), valueSignal(totalBalanceUsd), ageSignal(keyAgeDays)].filter(Boolean);
  const basePoints = signals.reduce((sum, signal) => sum + signal.points, 0);
  const signatureDimObserved = exposureStatus === 'signature_exposure_observed' || exposureStatus === 'contract_wallet' || txObserved;
  const evidenceCount = [signatureDimObserved, daysDormant != null, totalBalanceUsd != null, keyAgeDays != null].filter(Boolean).length;
  if (evidenceCount === 0) {
    return Object.freeze({
      score: null,
      status: 'insufficient_data',
      label: LABELS.unknown,
      bandKey: 'unknown',
      confidence: 'low',
      signals: Object.freeze(signals),
      basePoints,
      migrationOffset: 0,
      evidenceCount: 0,
      explanation: 'No wallet-specific dimension is backed by observed evidence, so no score is emitted. Two unrelated wallets with no data both return Unknown rather than an identical confident baseline.'
    });
  }
  let points = basePoints;
  let migrationOffset = 0;
  if (migrationPathPresent) {
    const reduction = Math.min(WEIGHTS.migrationReadinessReduction, points);
    points = Math.max(0, points - reduction);
    migrationOffset = -reduction;
    signals.push({
      key: 'migration_readiness',
      basis: 'inferred',
      label: 'Migration-readiness offset',
      weight: WEIGHTS.migrationReadinessReduction,
      points: -reduction,
      detail: 'Observed migration-readiness signals reduce priority versus an equivalent EOA.'
    });
  }
  const score = Math.min(100, Math.max(0, points));
  const bandKey = bandFor(score);
  let confidence;
  if (exposureStatus === 'contract_wallet') {
    confidence = 'medium';
  } else if (keyAgeDays != null && daysDormant != null) {
    confidence = 'high';
  } else if (keyAgeDays != null || daysDormant != null || signatureDimObserved) {
    confidence = 'medium';
  } else {
    confidence = 'low';
  }
  if (totalBalanceUsd == null) {
    const rank = Math.max(0, LADDER.indexOf(confidence) - 1);
    confidence = LADDER[rank];
  }
  const unknownAxes = signals.filter(s => s.basis === 'unknown').map(s => s.key);
  const explanation = unknownAxes.length > 0 ? `Scored from ${evidenceCount} observed dimension(s). ${unknownAxes.length} axis/axes had no evidence and contributed 0 points — that lowers confidence and coverage; it does not mean the wallet is safer.` : `Every scored axis is backed by observed evidence, so the composite reflects full coverage at ${confidence} confidence.`;
  return Object.freeze({
    score,
    status: 'scored',
    label: LABELS[bandKey],
    bandKey,
    confidence,
    signals: Object.freeze(signals),
    basePoints,
    migrationOffset,
    evidenceCount,
    explanation
  });
};

export const QuantumExposureCalculator = ({idPrefix = 'qec'}) => {
  const STATUS_OPTIONS = [{
    value: 'signature_exposure_observed',
    label: 'Outgoing signature observed (EOA)'
  }, {
    value: 'no_outgoing_signature_observed',
    label: 'No outgoing signature observed (EOA)'
  }, {
    value: 'contract_wallet',
    label: 'Contract wallet (Safe / multisig / AA)'
  }, {
    value: 'unknown',
    label: 'Unknown — cannot classify'
  }];
  const NON_SCORING_CONTEXT = ['Stablecoin concentration', 'Behavioral / adversarial signals', 'Counterparty relationship context', 'Protocol affinity'];
  const MIGRATION_OFFSET_POINTS = 10;
  const STYLES = `
.ww-qec{--ww-qec-bg:#FAFAF0;--ww-qec-panel:#FFF7E8;--ww-qec-ink:#2B2118;--ww-qec-muted:#59646A;--ww-qec-line:#B87333;--ww-qec-line-soft:#D9C7B3;--ww-qec-accent:#B84923;--ww-qec-ok:#526246;--ww-qec-warn:#8A5A1F;--ww-qec-risk:#8F2F1D;}
@media (prefers-color-scheme:dark){.ww-qec{--ww-qec-bg:#1B1512;--ww-qec-panel:#241C17;--ww-qec-ink:#F2E9DF;--ww-qec-muted:#A7B0B5;--ww-qec-line:#8A5A1F;--ww-qec-line-soft:#4A3B30;--ww-qec-accent:#D9714B;--ww-qec-ok:#9AAB89;--ww-qec-warn:#D6A85A;--ww-qec-risk:#E0836B;}}
.light .ww-qec,:root.light .ww-qec{--ww-qec-bg:#FAFAF0;--ww-qec-panel:#FFF7E8;--ww-qec-ink:#2B2118;--ww-qec-muted:#59646A;--ww-qec-line:#B87333;--ww-qec-line-soft:#D9C7B3;--ww-qec-accent:#B84923;--ww-qec-ok:#526246;--ww-qec-warn:#8A5A1F;--ww-qec-risk:#8F2F1D;}
.dark .ww-qec,:root.dark .ww-qec{--ww-qec-bg:#1B1512;--ww-qec-panel:#241C17;--ww-qec-ink:#F2E9DF;--ww-qec-muted:#A7B0B5;--ww-qec-line:#8A5A1F;--ww-qec-line-soft:#4A3B30;--ww-qec-accent:#D9714B;--ww-qec-ok:#9AAB89;--ww-qec-warn:#D6A85A;--ww-qec-risk:#E0836B;}
.ww-qec{background:var(--ww-qec-bg);color:var(--ww-qec-ink);border:2px solid var(--ww-qec-line);border-radius:4px;padding:1rem;font-size:0.875rem;line-height:1.5;}
.ww-qec *{box-sizing:border-box;}
.ww-qec-h{font-weight:600;font-size:0.75rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--ww-qec-muted);margin:0 0 0.5rem;}
.ww-qec-grid{display:grid;grid-template-columns:1fr;gap:0.75rem;}
@media (min-width:40rem){.ww-qec-grid{grid-template-columns:1fr 1fr;}}
.ww-qec-field{display:flex;flex-direction:column;gap:0.25rem;}
.ww-qec-field label{font-weight:600;}
.ww-qec-field select,.ww-qec-field input[type="number"]{width:100%;padding:0.4rem 0.5rem;border:1px solid var(--ww-qec-line-soft);border-radius:3px;background:var(--ww-qec-panel);color:var(--ww-qec-ink);font:inherit;}
.ww-qec-field input:disabled{opacity:0.5;}
.ww-qec-field select:focus-visible,.ww-qec-field input:focus-visible{outline:2px solid var(--ww-qec-accent);outline-offset:2px;}
.ww-qec-obs{display:flex;align-items:center;gap:0.4rem;font-weight:400;color:var(--ww-qec-muted);font-size:0.8125rem;}
.ww-qec-obs input{width:auto;}
.ww-qec-hint{color:var(--ww-qec-muted);font-size:0.8125rem;}
.ww-qec-out{margin-top:1rem;border:1px solid var(--ww-qec-line-soft);border-left:4px solid var(--ww-qec-accent);border-radius:3px;background:var(--ww-qec-panel);padding:0.75rem;}
.ww-qec-score{font-size:1.75rem;font-weight:700;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;}
.ww-qec-band{font-weight:600;color:var(--ww-qec-accent);}
.ww-qec-table{width:100%;border-collapse:collapse;margin-top:0.75rem;font-size:0.8125rem;}
.ww-qec-table caption{text-align:left;font-weight:600;color:var(--ww-qec-muted);padding-bottom:0.35rem;}
.ww-qec-table th,.ww-qec-table td{border-bottom:1px solid var(--ww-qec-line-soft);padding:0.35rem 0.4rem;text-align:left;vertical-align:top;}
.ww-qec-table th{color:var(--ww-qec-muted);font-weight:600;}
.ww-qec-num{text-align:right;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:nowrap;}
.ww-qec-basis{font-size:0.75rem;text-transform:uppercase;letter-spacing:0.04em;}
.ww-qec-basis[data-basis="observed"]{color:var(--ww-qec-ok);}
.ww-qec-basis[data-basis="inferred"]{color:var(--ww-qec-warn);}
.ww-qec-basis[data-basis="unknown"]{color:var(--ww-qec-risk);}
.ww-qec-scroll{overflow-x:auto;}
.ww-qec-note{margin-top:0.75rem;padding-top:0.6rem;border-top:1px dashed var(--ww-qec-line-soft);color:var(--ww-qec-muted);font-size:0.8125rem;}
.ww-qec-ctx{margin-top:0.75rem;border:1px dashed var(--ww-qec-line-soft);border-radius:3px;padding:0.6rem;color:var(--ww-qec-muted);font-size:0.8125rem;}
.ww-qec-ctx ul{margin:0.3rem 0 0;padding-left:1.1rem;}
.ww-qec-reset{margin-top:0.75rem;padding:0.35rem 0.7rem;border:1px solid var(--ww-qec-line);border-radius:3px;background:transparent;color:var(--ww-qec-ink);font:inherit;font-size:0.8125rem;cursor:pointer;}
.ww-qec-reset:focus-visible{outline:2px solid var(--ww-qec-accent);outline-offset:2px;}
`;
  const [exposureStatus, setExposureStatus] = useState('signature_exposure_observed');
  const [txObserved, setTxObserved] = useState(true);
  const [dormancyObserved, setDormancyObserved] = useState(true);
  const [dormancyDays, setDormancyDays] = useState('365');
  const [valueObserved, setValueObserved] = useState(true);
  const [valueUsd, setValueUsd] = useState('100000');
  const [ageObserved, setAgeObserved] = useState(true);
  const [ageDays, setAgeDays] = useState('1095');
  const [migrationPathPresent, setMigrationPathPresent] = useState(false);
  const toNumber = raw => {
    const parsed = Number.parseFloat(raw);
    return Number.isFinite(parsed) ? parsed : null;
  };
  const result = useMemo(() => deriveDocsQuantumExposureScore({
    exposureStatus,
    txObserved,
    daysDormant: dormancyObserved ? toNumber(dormancyDays) : null,
    totalBalanceUsd: valueObserved ? toNumber(valueUsd) : null,
    keyAgeDays: ageObserved ? toNumber(ageDays) : null,
    migrationPathPresent
  }), [exposureStatus, txObserved, dormancyObserved, dormancyDays, valueObserved, valueUsd, ageObserved, ageDays, migrationPathPresent]);
  const reset = () => {
    setExposureStatus('signature_exposure_observed');
    setTxObserved(true);
    setDormancyObserved(true);
    setDormancyDays('365');
    setValueObserved(true);
    setValueUsd('100000');
    setAgeObserved(true);
    setAgeDays('1095');
    setMigrationPathPresent(false);
  };
  const numberFields = [{
    name: 'value',
    label: 'Value at risk (USD)',
    hint: 'Anchors: $1 → 0, $10k → 10, $100k → 18, $1M → 25 points, log-interpolated between.',
    observed: valueObserved,
    onObservedChange: setValueObserved,
    value: valueUsd,
    onValueChange: setValueUsd,
    step: '1000'
  }, {
    name: 'dormancy',
    label: 'Days since last activity',
    hint: 'Anchors: 30d → 0, 180d → 5, 730d → 12, 1825d → 20 points, log-interpolated between.',
    observed: dormancyObserved,
    onObservedChange: setDormancyObserved,
    value: dormancyDays,
    onValueChange: setDormancyDays,
    step: '1'
  }, {
    name: 'age',
    label: 'Key / signature age (days)',
    hint: 'Anchors: 1d → 0, 365d → 5, 1095d → 10, 1825d → 15 points, log-interpolated between.',
    observed: ageObserved,
    onObservedChange: setAgeObserved,
    value: ageDays,
    onValueChange: setAgeDays,
    step: '1'
  }];
  return <div className="ww-qec not-prose">
      <style>{STYLES}</style>

      <p className="ww-qec-h">Quantum exposure calculator — educational, no wallet data</p>

      <div className="ww-qec-grid">
        <div className="ww-qec-field">
          <label htmlFor={`${idPrefix}-status`}>Signature exposure status</label>
          <select id={`${idPrefix}-status`} value={exposureStatus} onChange={e => setExposureStatus(e.target.value)} aria-describedby={`${idPrefix}-status-hint`}>
            {STATUS_OPTIONS.map(option => <option key={option.value} value={option.value}>
                {option.label}
              </option>)}
          </select>
          <span className="ww-qec-obs">
            <input id={`${idPrefix}-tx`} type="checkbox" checked={txObserved} onChange={e => setTxObserved(e.target.checked)} />
            <label htmlFor={`${idPrefix}-tx`}>Transaction history was observed</label>
          </span>
          <span id={`${idPrefix}-status-hint`} className="ww-qec-hint">
            Unknown emits no score at all. Without observed history, “no outgoing
            signature” adds 0, not the observed baseline of 5.
          </span>
        </div>

        {numberFields.map(field => <div className="ww-qec-field" key={field.name}>
            <label htmlFor={`${idPrefix}-${field.name}`}>{field.label}</label>
            <input id={`${idPrefix}-${field.name}`} type="number" inputMode="numeric" min="0" step={field.step} value={field.observed ? field.value : ''} disabled={!field.observed} onChange={e => field.onValueChange(e.target.value)} aria-describedby={`${idPrefix}-${field.name}-hint`} />
            <span className="ww-qec-obs">
              <input id={`${idPrefix}-${field.name}-observed`} type="checkbox" checked={field.observed} onChange={e => field.onObservedChange(e.target.checked)} />
              <label htmlFor={`${idPrefix}-${field.name}-observed`}>Observed for this wallet</label>
            </span>
            <span id={`${idPrefix}-${field.name}-hint`} className="ww-qec-hint">
              {field.hint}
            </span>
          </div>)}

        <div className="ww-qec-field">
          <span className="ww-qec-obs">
            <input id={`${idPrefix}-migration`} type="checkbox" checked={migrationPathPresent} onChange={e => setMigrationPathPresent(e.target.checked)} />
            <label htmlFor={`${idPrefix}-migration`}>
              Programmable upgrade path inferred (AA / multisig)
            </label>
          </span>
          <span className="ww-qec-hint">
            The only subtractive term: up to −{MIGRATION_OFFSET_POINTS} points,
            never below 0.
          </span>
        </div>
      </div>

      <div className="ww-qec-out" aria-live="polite">
        <p>
          <span className="ww-qec-score">{result.score === null ? 'null' : result.score}</span>{' '}
          <span className="ww-qec-band">{result.label}</span>
        </p>
        <p>
          Status <code>{result.status}</code> · confidence <code>{result.confidence}</code> ·{' '}
          {result.evidenceCount} observed evidence dimension(s)
        </p>

        <div className="ww-qec-scroll">
          <table className="ww-qec-table">
            <caption>Per-factor contribution to the numeric score</caption>
            <thead>
              <tr>
                <th scope="col">Factor</th>
                <th scope="col">Basis</th>
                <th scope="col" className="ww-qec-num">Max</th>
                <th scope="col" className="ww-qec-num">Points</th>
              </tr>
            </thead>
            <tbody>
              {result.signals.map(signal => <tr key={signal.key}>
                  <th scope="row">
                    {signal.label}
                    <br />
                    <span className="ww-qec-hint">{signal.detail}</span>
                  </th>
                  <td>
                    <span className="ww-qec-basis" data-basis={signal.basis}>
                      {signal.basis}
                    </span>
                  </td>
                  <td className="ww-qec-num">{signal.weight}</td>
                  <td className="ww-qec-num">{signal.points}</td>
                </tr>)}
              <tr>
                <th scope="row">Composite before offset</th>
                <td />
                <td className="ww-qec-num" />
                <td className="ww-qec-num">{result.basePoints}</td>
              </tr>
              <tr>
                <th scope="row">Migration-readiness offset</th>
                <td />
                <td className="ww-qec-num" />
                <td className="ww-qec-num">{result.migrationOffset}</td>
              </tr>
              <tr>
                <th scope="row">Score (clamped 0–100)</th>
                <td />
                <td className="ww-qec-num" />
                <td className="ww-qec-num">{result.score === null ? 'null' : result.score}</td>
              </tr>
            </tbody>
          </table>
        </div>

        <p className="ww-qec-note">{result.explanation}</p>
      </div>

      <div className="ww-qec-ctx">
        <strong>Observed but not included in the numeric score:</strong>
        <ul>
          {NON_SCORING_CONTEXT.map(item => <li key={item}>{item}</li>)}
        </ul>
        These are real WalletWall observations shown elsewhere in the product. They contribute
        exactly zero points here, because they contribute zero points in production.
      </div>

      <p className="ww-qec-note">
        Illustrative inputs only — no address is accepted and no data is fetched. This is a
        forward-looking heuristic for migration planning, not a statement of current risk, not a
        security assessment, and not financial advice. Quantum Exposure is a separate surface from
        Migration Readiness and from Stablecoin Vault readiness.
      </p>

      <button type="button" className="ww-qec-reset" onClick={reset}>
        Reset to the worked example
      </button>
    </div>;
};

# Quantum Intelligence

Quantum Intelligence is WalletWall's cross-cutting framework for estimating future cryptographic migration urgency for Ethereum wallets. It surfaces inside Holder Wall (wallet drawer), Whale Watcher (risk section), and Coinstellation (node detail panel).

## Framing

<Warning>
  A high Quantum Exposure Score does not mean a wallet is currently vulnerable to theft or exploitation by any existing technology. The score is a forward-looking heuristic for long-term cryptographic migration planning, not a statement of current risk.
</Warning>

* **Future-looking**: measures potential risk posed by future quantum computing advances to current cryptographic standards (ECDSA/secp256k1)
* **Not currently exploitable**: no practical quantum attack exists against these standards today
* **Informational only**: provided for cryptographic awareness and security planning
* **Not investment advice**: must not be used as a basis for financial decisions

## Quantum Exposure Score

The score is a normalized value from `0` to `100` — or `null` when there is not enough evidence to score. It is built from **four additive signal groups** plus one **subtractive migration-readiness offset**. Each group contributes at most the points shown; **missing data never adds points** — an unavailable signal is recorded as `unknown` (0 points) and lowers *confidence* instead of inflating the score.

| Signal group                   | Max points | What it measures                                                                                                                                                                                                                               |
| ------------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Signature exposure**         | 35         | Whether outgoing signature activity has been observed on-chain — the public key becomes recoverable after the first outgoing transaction. A wallet with observed history but *no* outgoing signature contributes a small baseline (5) instead. |
| **Value at risk**              | 25         | Reported balance tier: `≥ $1M` (25), `≥ $100k` (18), `≥ $10k` (10), `> $0` (5). The migration-urgency "bounty."                                                                                                                                |
| **Dormancy**                   | 20         | Observed last-activity bucket: `≥ 730d` (20), `180–730d` (12), `30–180d` (5), active (0).                                                                                                                                                      |
| **Key / signature age**        | 15         | Age of the exposed key/signature: `≥ 5y` (15), `≥ 3y` (10), `≥ 1y` (5).                                                                                                                                                                        |
| **Migration-readiness offset** | −10        | Subtracted when a programmable upgrade path (ERC-4337 AA / multisig) is inferred, reflecting lower migration friction.                                                                                                                         |

These weights are the `SCORE_WEIGHTS` contract. The composite is bucketed into the labels below. See [Scoring Methodology](/concepts/scoring-methodology) for the plain-English factor walkthrough and worked example.

```mermaid theme={null}
flowchart TD
    accTitle: How the four Quantum Exposure signal groups and the migration-readiness offset combine into the composite score
    accDescr: Four additive signal groups feed a composite sum - signature exposure up to 35 points, value at risk up to 25 points, dormancy up to 20 points, and key or signature age up to 15 points. A migration-readiness offset of negative 10 points is subtracted when a programmable upgrade path is inferred. The composite normalizes to a 0 to 100 score, or null when evidence is insufficient.
    A["Signature exposure<br/>max 35"]:::input
    B["Value at risk<br/>max 25"]:::input
    C["Dormancy<br/>max 20"]:::input
    D["Key / signature age<br/>max 15"]:::input
    E["Migration-readiness offset<br/>-10 if upgrade path inferred"]:::decision
    S["Composite SCORE_WEIGHTS sum"]:::process
    O["0-100 exposure score,<br/>or null on insufficient evidence"]:::output
    A --> S
    B --> S
    C --> S
    D --> S
    E -. subtracted .-> S
    S --> O

    classDef input fill:#FAFAF0,stroke:#B87333,color:#2B2118,stroke-width:1.5px;
    classDef process fill:#B84923,stroke:#6B2412,color:#FFF7E8,stroke-width:1.5px;
    classDef decision fill:#D6A85A,stroke:#8A5A1F,color:#2B2118,stroke-width:1.5px;
    classDef output fill:#9AAB89,stroke:#526246,color:#172014,stroke-width:1.5px;
```

*Each signal group contributes independently, up to its own maximum; the migration-readiness offset is the only subtractive term. The same five weights from the table above — no others — drive the composite.*

### Try it — exposure calculator

The table gives each group's maximum. Between the anchor values, the value-at-risk,
dormancy, and key-age axes interpolate continuously rather than stepping, so a wallet
between two tiers scores between two tiers. This calculator applies the same
`SCORE_WEIGHTS` and the same calibration curves as production — the weights and anchors
are pinned to the canonical modules by test, so it cannot drift from the app.

Set an axis to *not observed* to see the rule that matters most: missing evidence
contributes **0 points and lowers confidence** — it never makes a wallet look safer.

<QuantumExposureCalculator idPrefix="quantum-intel" />

### Score labels

| Label                       | Score band | Meaning                                                                     |
| --------------------------- | ---------- | --------------------------------------------------------------------------- |
| Low exposure                | below 25   | Minimal priority; public key may not be exposed, or value is low.           |
| Moderate exposure           | ≥ 25       | Standard EOA risk; public key is exposed but migration is not yet urgent.   |
| Elevated exposure           | ≥ 50       | Significant value held in a classical EOA with a revealed public key.       |
| Migration priority          | ≥ 75       | High-value, exposed wallet with no clear path to automated rotation.        |
| Unknown / insufficient data | `null`     | Signature info or transaction history is unavailable — no score is emitted. |

**Prohibited terminology**: "Quantum vulnerable", "Quantum hacked soon", "Q-Day target", "Unsafe wallet".

```mermaid theme={null}
stateDiagram-v2
    accTitle: Quantum Exposure Score bands
    accDescr: The composite score maps to one of five labels. Unknown is emitted with no numeric score when signature or transaction history is unavailable. Otherwise the 0 to 100 composite is bucketed as Low below 25, Moderate at 25 or above, Elevated at 50 or above, and Migration priority at 75 or above.
    [*] --> Unknown: insufficient evidence
    [*] --> Low: score computed
    Low --> Moderate: score >= 25
    Moderate --> Elevated: score >= 50
    Elevated --> MigrationPriority: score >= 75

    Unknown: Unknown (null, no score)
    Low: Low exposure (< 25)
    Moderate: Moderate exposure (>= 25)
    Elevated: Elevated exposure (>= 50)
    MigrationPriority: Migration priority (>= 75)

    classDef input fill:#FAFAF0,stroke:#B87333,color:#2B2118,stroke-width:1.5px;
    classDef process fill:#B84923,stroke:#6B2412,color:#FFF7E8,stroke-width:1.5px;
    classDef decision fill:#D6A85A,stroke:#8A5A1F,color:#2B2118,stroke-width:1.5px;
    classDef output fill:#9AAB89,stroke:#526246,color:#172014,stroke-width:1.5px;
    classDef risk fill:#8F2F1D,stroke:#5E1D12,color:#FFF7E8,stroke-width:1.5px;
    classDef datastore fill:#6F7068,stroke:#3C3D38,color:#FFF7E8,stroke-width:1.5px;

    class Unknown datastore
    class Low output
    class Moderate decision
    class Elevated process
    class MigrationPriority risk
```

*Bands are read from the same score bounds listed in the table above (below 25 / ≥ 25 / ≥ 50 / ≥ 75). `Unknown` is a distinct, non-numeric outcome — it is never a synonym for a low score.*

### Source caveats

Every score carries a `caveats` array built from up to three layers:

| Layer                                 | When added                                                                                 |
| ------------------------------------- | ------------------------------------------------------------------------------------------ |
| Base caveat (`SCORE_CAVEAT` constant) | Always                                                                                     |
| Coverage caveat                       | When key facts (chain profile, tx history, dormancy) are unavailable                       |
| Insufficient-data caveat              | When `exposureStatus === 'unknown'`                                                        |
| Stale-data caveat                     | When scheduled Dune data is older than that query's **cadence-scaled** staleness threshold |
| Missing-source caveat                 | When a required source alias is not configured                                             |
| Live-only caveat                      | When a Dune API call failed or was skipped                                                 |

Staleness is judged **per query cadence**, not by one flat threshold: queries on a
daily refresh cadence flag at roughly 30 hours, while the migration-readiness query —
which refreshes weekly by design — uses a weekly-scaled threshold. A weekly snapshot a
few days old is on schedule, not late, and is no longer misreported as "Dune auto-run
may be delayed."

<Note>
  Exposure and dormancy facts are **stablecoin-scoped**: the underlying Dune queries
  read curated stablecoin tables (`stablecoins_multichain.*`), so surfaces describe
  USD-stablecoin balances and flows on Ethereum — the labels state that scope rather
  than implying all-token coverage.
</Note>

## Chain and signature assumptions

| Chain type                  | Notes                                                                                                                                                                   |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| EVM / Ethereum EOAs         | Use ECDSA (secp256k1). Public key is recoverable from signature after first outgoing transaction. Accounts that have only received funds have lower immediate exposure. |
| Smart contract wallets / AA | ERC-4337 or multisig (Gnosis Safe) wallets may support future upgrades to quantum-resistant signatures. If the upgrade path is unknown, confidence is reduced.          |
| Solana                      | Uses Ed25519. WalletWall does not support Solana holder analytics; Solana quantum risk is documented as a general category only.                                        |

## Vault Readiness Card

The Vault Readiness Card renders the full score breakdown:

* Composite score and risk band
* Per-component breakdown with values
* Behavioral exposure signals (adversarial signals)
* Migration-readiness hints from Dune scheduled data
* Source caveats and confidence level

Source provenance is shown via a data-source badge for each contributing data source.

## Dormancy signal

Wallets that have not sent any outgoing transaction are classified as dormant. Dormancy increases migration urgency because:

* The wallet may not be actively monitored
* The holder may not respond to future migration requirements
* Long-dormant wallets represent "cold" exposure that is hard to remediate

Dormancy is measured from the `last_active_at` timestamp. The **Dormancy exposure feed** provides source-backed dormancy facts with bucket classification:

| Bucket                      | Duration                                    |
| --------------------------- | ------------------------------------------- |
| `warm_dormant_30_180d`      | 30–180 days since last outgoing transaction |
| `cold_dormant_180_730d`     | 180–730 days (roughly 6 months to 2 years)  |
| `ancient_dormant_730d_plus` | More than 730 days (2+ years)               |

## Migration readiness

Migration readiness indicates whether a wallet has a viable path to adopt post-quantum cryptography (PQC) signatures. The **Migration readiness feed** provides:

* `is_contract_wallet`: true if the address is a contract-based wallet (Safe, multisig, ERC-4337 AA)
* `contract_wallet_type`: `safe`, `gnosis_multisig`, `erc4337_aa`, or `other_contract`
* `recent_migration_signal`: true if a token migration or key-rotation event was observed
* `risky_approval_count`: count of active ERC-20 approvals with unlimited or large allowances

<Note>
  `is_contract_wallet: true` does not mean the wallet is safe — it means it has a programmable upgrade path. The absence of a known-safe upgrade path should still yield low readiness.
</Note>

### How the three concepts relate

WalletWall keeps three distinct ideas separate so users don't confuse "risk" with "a fix":

<Tabs>
  <Tab title="Quantum Exposure">
    **Question it answers:** How urgent is the wallet-level risk?

    The composite `0`–`100` score (or `null`) defined above, built from the four signal groups and the migration-readiness offset.
  </Tab>

  <Tab title="Migration Readiness">
    **Question it answers:** How feasible is it to move safely?

    Recommends a migration **path** — not a separate score — from the wallet's existing signals. Detailed below.
  </Tab>

  <Tab title="Stablecoin Vault">
    **Question it answers:** What's the destination readiness journey?

    The destination readiness journey — and, for vault candidates, the Vault Simulator testnet rehearsal. See [Stablecoin Vault & Vault Simulator](/features/vault).
  </Tab>
</Tabs>

Migration Readiness can recommend a single migration **path** from existing wallet signals (value at risk, signature exposure, dormancy, wallet structure). It does **not** add a separate score. The migration-readiness recommender returns `level`, `urgency`, `difficulty`, `recommendedPath`, `blockers`, and `nextAction`.

| `recommendedPath`  | When it is suggested                                                             |
| ------------------ | -------------------------------------------------------------------------------- |
| `monitor`          | Small wallet, low exposure — no action needed yet                                |
| `fresh-wallet`     | Older exposed wallet with medium value                                           |
| `split-wallet`     | Very high value concentrated in one address                                      |
| `multisig`         | High-value exposed wallet with no upgrade path                                   |
| `treasury-custody` | DAO / treasury-like (Safe / multisig) wallet needing a signer-led migration path |
| `vault-prototype`  | Long-horizon, high-value, quantum-exposed vault candidate                        |

### Stablecoin Vault & Vault Simulator (research prototypes)

When Migration Readiness recommends `vault-prototype`, Quantum Intelligence surfaces the wallet as a **vault candidate** and can route to the **Stablecoin Vault** readiness journey at `vault.walletwall.org` / `/stablecoin-vault`.

The Stablecoin Vault readiness journey synthesizes Quantum Intelligence exposure signals, Holder Wall holder context, and Stable Seer stablecoin exposure to produce one of four outcomes: Monitor, Prepare, Testnet Rehearsal, or Not Enough Data.

The **Vault Simulator** (`/vault`) is the Sepolia testnet rehearsal detail — surfaced from the Testnet Rehearsal outcome. It demonstrates a **hybrid classical + post-quantum authorization** model: an Ethereum ECDSA signature plus an ML-DSA (FIPS 204, formerly CRYSTALS-Dilithium) signature. Related signature research includes SLH-DSA (FIPS 205, formerly SPHINCS+).

<Warning>
  Both vault surfaces are research prototypes. The app does not store keys, does not ask for seed phrases, and does not implement recovery flows. The on-chain verifier is an architectural placeholder, not production-grade cryptographic verification. vault.walletwall.org is Stablecoin Vault (the readiness journey); /vault is the Vault Simulator (the Sepolia testnet detail route).
</Warning>

**Approved framing:** post-quantum migration research, hybrid authorization prototype, experimental migration path, vault candidate, Stablecoin Vault readiness, Vault Simulator (for /vault). Avoid absolute safety, recovery, or asset-control claims.

See [Stablecoin Vault & Vault Simulator](/features/vault) for full product documentation.

## Behavioral exposure signals

Behavioral signals (`adversarialSignals`) are deterministic heuristics derived from wallet transaction history and 12-week Dune activity data. They are computed by the behavioral-signals engine.

<AccordionGroup>
  <Accordion title="Signal definitions">
    | Key                             | Approved label             | What it observes                                                   |
    | ------------------------------- | -------------------------- | ------------------------------------------------------------------ |
    | `extractionStyleActivityRisk`   | extraction-style activity  | Single large outgoing movement dominates outgoing volume           |
    | `counterpartyConcentrationRisk` | counterparty concentration | Activity concentrated among one or few counterparties              |
    | `relayRoutingExposure`          | relay routing exposure     | Incoming value closely followed by outgoing to different addresses |
    | `activityRampRisk`              | activity ramp              | Recent activity sharply elevated vs. prior baseline                |
    | `assetValueAmbiguityRisk`       | asset/value ambiguity      | Token or value fields are ambiguous, missing, or unverifiable      |
  </Accordion>
</AccordionGroup>

Each signal carries `score`, `confidence`, `reason`, and `evidence`. Signals with `score < 0.3` are not rendered.

`confidence` reflects data completeness, not accusatory certainty. These signals are not findings of wrongdoing and do not imply intent, identity, or legal status.

## Policy simulator

The policy simulator allows exploring how score components would change under different wallet configuration assumptions (e.g., migrating to a Safe multisig, rotating keys). It operates on the current score breakdown and produces a hypothetical output — no on-chain action is taken.

## Dune source aliases

<AccordionGroup>
  <Accordion title="Public alias, query name, and cadence">
    | Public alias             | Query name                                 | Cadence           |
    | ------------------------ | ------------------------------------------ | ----------------- |
    | Dormancy exposure feed   | `walletwall_dormant_quantum_exposure_v1`   | Weekly            |
    | Signature exposure feed  | `walletwall_wallet_signature_exposure_v1`  | Daily / on-demand |
    | Value-at-risk feed       | `walletwall_quantum_value_at_risk_v1`      | Daily             |
    | Migration readiness feed | `walletwall_wallet_migration_readiness_v1` | Weekly            |
  </Accordion>
</AccordionGroup>

See [Dune Queries](/development/dune-queries) for full field contracts and staleness thresholds.

## Related

<Columns cols={2}>
  <Card title="Migration Readiness" icon="route" href="/concepts/migration-readiness">
    How exposure becomes a recommended path — the shared Monitor / Review / Migrate / Vault Candidate tiers.
  </Card>

  <Card title="Quantum Exposure Clock" icon="clock" href="/concepts/quantum-exposure-clock">
    Why exposure is wallet-specific — key/signature age and value at risk, without a universal Q-Day countdown.
  </Card>
</Columns>
