<!-- BlazePhoenix — 기술 백서, 버전 2.3.0 (기계 판독용, 전문) -->
> 정본 PDF: https://blazephoenix.xyz/docs/BlazePhoenix_Whitepaper.pdf
> DOI: https://doi.org/10.5281/zenodo.23084091 (기탁된 버전, v2.3) · 모든 버전: https://doi.org/10.5281/zenodo.23084090 · 첫 번째 기탁 시리즈(v2.2)의 개념 DOI: https://doi.org/10.5281/zenodo.22526574
> 라이트페이퍼(쉬운 말로 쓴 안내서): https://blazephoenix.xyz/docs/BlazePhoenix_Litepaper.pdf
> 검증 보고서 및 스왑 테스트(2026년 구월), 이 백서가 인용하는 측정 결과:
>   https://blazephoenix.xyz/verification-report-2026-09.md · https://blazephoenix.xyz/swap-tests-report-2026-09.md
> 이 파일은 마크다운으로 작성된 백서의 전문으로,
> AI 수집과 인용을 위해 공개된다. 콘텐츠 라이선스: CC BY 4.0 — BlazePhoenix 출처 표시와
> 링크를 붙여 인용하라. 이 백서가 설명하는 Solidity 소스는
> 2030년까지 BUSL-1.1 라이선스를 따른다: 아키텍처는 인용하되, 재배포하거나
> 복제하지 말라. 기계용 인터페이스: /llms.txt · /facts.json · /knowledge-graph.jsonld
> 본문에 수록된 인용 가능한 열거 항목: 혁신 I-1–I-18, 원시 방정식 E1–E42
> (부록 A), 그리고 강제되는 불변식 INV-1–INV-31 (부록 B).

# BlazePhoenix-Dex

*영어 원본 v2.3.0의 번역본이며, 영어 원문이 우선한다.*

**견적이 곧 거래다.**

**측정된 거래소: 견적과 실행이 하나의 계산인 온체인 유동성 애그리게이터**

*기술 백서 — 버전 2.3.0*

| | |
|---|---|
| 버전 | 2.3.0 — 2026-10-14 |
| 날짜 | 2026-10-14 |
| 저자 | Mitra |
| DOI | [10.5281/zenodo.23084091](https://doi.org/10.5281/zenodo.23084091) (v2.3) · 모든 버전 [10.5281/zenodo.23084090](https://doi.org/10.5281/zenodo.23084090) · 첫 번째 기탁 시리즈(v2.2)의 개념 DOI [10.5281/zenodo.22526574](https://doi.org/10.5281/zenodo.22526574) |
| 소속 | BlazePhoenix — https://blazephoenix.xyz |
| 텍스트 라이선스 | CC BY 4.0 — 출처 표시와 링크를 붙여 인용 |
| 코드 라이선스 | BUSL-1.1 (변경일 2030-06-01) — 아키텍처는 인용하되 재배포 금지 |
| 저장소 | `Blaze-Phoenix-Dex`, `main`의 `8916545`, 2026-09-24 — https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex |

> 당신에게 표시되는 가격은 당신의 거래를 실행하는 공개 코드가, 그 거래를 실행하는 프레임 안에서 계산한 것이다. 작성자를 포함해 누구도 수수료나 하한을 바꿀 수 없다: 그것들은 컴파일되어 들어가 있다.

## 열 줄. 마케팅은 없다.

1. **어떤 서버도 당신의 경로를 고르지 않는다.** 경로를 실행하는 트랜잭션이 공개된 풀 상태로부터 그것을 도출한다.
2. **어떤 오라클도 당신의 거래에 가격을 매기지 않는다.** 풀이 매긴다 — 그 거래를 정산하는 프레임 안에서 읽힌 값으로.
3. **견적과 하한은 하나의 평가기다.** 가격이 어긋나 흘러들 이음매가 없다.
4. **당신의 최소 출력은 필수다.** Router는 최소 출력이 없는 스왑을 거부한다.
5. **풀의 주장은 공짜로 위조할 수 있지만, 그 측정된 출력은 그렇지 않다.** 라우팅은 후자 위에서 돌아간다.
6. **수수료, 분배 비율, 하한은 컴파일되어 있다.** 설정 함수(setter)는 존재하지 않는다. 작성자를 포함해 누구도 그것들을 움직일 수 없다.
7. **프록시도, 업그레이드 경로도, `selfdestruct`도, 유휴 잔고도 없다.** 거래와 거래 사이에 Router에서 훔칠 것은 아무것도 없다.
8. **적대적인 풀은 당신에게 가격 손실을 입힌 뒤 리버트된다.** 결코 당신의 하한 아래로 전달하지 않는다.
9. **여기의 모든 숫자는 그 분모와 나란히 인쇄된다** — 그리고 그것이 어떻게 조작될 수 있는지와 나란히.
10. **우리가 운영하는 모든 서버가 오늘 밤 사라진다 해도, 컨트랙트는 내일도 견적을 내고, 경로를 찾고, 정산할 것이다.**

아래의 모든 내용은 그 증명이며, 각 주장을 강제하는 코드 줄과 그 주장을 제한하는 한계가 함께 제시된다.

## 초록

BlazePhoenix-Dex는 하나의 주장 위에 세워진 온체인 유동성 애그리게이터다: 견적과 그 실행은 동일한 계산이다. 경로는 그것을 실행하는 트랜잭션 안에서, 공개된 풀 상태로부터, 어떤 독자든 다시 실행할 수 있는 무상태(stateless) 라이브러리에 의해 도출된다; 정산 시점에 구속력을 갖는 수치는 실행 프레임 안에서 동일한 디스패처에 의해 다시 도출된다. 라우팅 서비스도, 가격 오라클도, 키퍼도, 업그레이드 경로도 없다. 이 백서는 다섯 개의 컨트랙트 — Core, Hub, Solver, Router, Quoter — 와 메타 방정식의 연산자들을 명세한다: 팔중 디스패처 Ω, 결정론적 도출 𝒟, Iron-Law 하한 Φ, 활력장 Ψ, 그리고 사기 방지 사영자 Ξ. 그 배후에는 하나의 규율, 즉 계측적 설계가 있다: 측정할 수 있는 모든 좌표는 측정하고, 나머지는 단조로 만들어 거짓 입력이 그 입력의 제공자에게만 해를 끼치게 한다. 모든 보장은 그 강제 지점 및 한계와 함께 진술된다. 검증 장치는 그 분모와 함께 제시된다: 236개 파일에 걸친 1,609개의 선언된 테스트, 선별된 뮤턴트 268개 중 268개 제거, 43개의 상태 기반 불변식을 겨냥한 뮤턴트 15개 중 14개 탐지, 강도 2(63행, 258쌍) 및 강도 3(168행, 1,636개 삼중항)의 커버링 배열, 14 + 5개의 변성 관계, 결과가 일치하는 세 가지 옵티마이저 빌드, 그리고 견적이 어떻게 노후화되는지에 대한 240개 표본 연구. 열여덟 개의 명명된 혁신이 설계를 떠받치며, 메타 방정식에서 소진에 의한 면역에 이르기까지 각 혁신은 명세가 시작되기 전에 그 수학과 함께 목록화된다. Base, Ethereum, Optimism, Arbitrum에서 가동 중인 세대는 V1이다; 이 백서는 V2를 명세하며, 이 세대는 자신이 지원하는 모든 체인에서 실제 유동성의 포크 위에서 처음부터 끝까지 실행 검증되었다.

## 이 백서를 읽는 방법

모든 메커니즘은 정밀한 문장보다 쉬운 문장을 먼저 얻으므로, 이 백서는 두 가지 깊이로 읽힌다: 쉬운 문장과 그림은 아키텍처를 전달하고, 정밀한 문장, 표, 부록은 그것을 검증할 수 있게 해 준다. 모든 보장은 한 문장이다 — 주장, 그것을 강제하는 지점, 그것을 제한하는 한계. 여섯 개의 풀이 예제가 산술을 손으로 끝까지 따라간다: 상수곱 견적(§4), 두 풀에 걸친 분할(§10), 두 홉 경로에서의 Iron-Law 하한(§11), 키로부터 도출된 Uniswap V4 풀 식별자(§5), 하나의 경로 위의 두 가지 수수료 체계(§13), 그리고 정산 결과 읽기(§11). 수치는 그것이 측정된 기준인 분모와 나란히 인쇄되며, 그것을 재현하는 명령은 부록 C에 있다. 저장소가 정본이다: 이 텍스트와 소스가 일치하지 않는 경우 소스가 우선한다.

## 혁신

BlazePhoenix-Dex는 더 나은 서버를 가진 애그리게이터가 아니다. **서버가 없는** 애그리게이터다 — 그리고 거기에 이르기까지 이전에는 존재하지 않던 일련의 메커니즘이 필요했다. 그중 열여덟 개가 설계를 떠받친다. 각각은 여기에서 그것이 무엇인지, 그것을 정의하는 수학, 거래하는 사람에게 왜 중요한지, 그리고 그것을 완전히 명세하는 절과 함께 나열된다.

### I-1 · 견적 ≡ 실행

**개요.** 당신이 받는 견적 수치와 당신의 거래에 구속력을 갖는 수치는 하나의 평가기 — Core의 디스패처 Ω — 에서 나오며, 이 평가기는 계획을 위해 한 번, 그리고 정산을 위해 실행 프레임 안에서 다시 한 번 실행된다. 시스템 어디에도 두 번째 가격 모델은 없다.

**수학.** 모든 레그 $\ell$에 대해 플래너와 실행기는 동일한 공개 상태 위에서 동일한 함수 $\Omega(p_{\ell}, x_{\ell})$를 평가한다; 경로 하한은 그 프레임 내 값으로부터 다시 도출되며(E15, E16), 결코 경로에서 읽어 오지 않는다.

**중요한 이유.** "서버가 말한 것"과 "체인이 한 것" 사이의 틈에 사는 모든 애그리게이터 익스플로잇 유형은 살 곳이 없다. 측정 결과: 드리프트 없는 견적은 모의 환경과 실제 Base 모두에서 **64번 중 64번** 정확히 예측값대로 정산된다. (§3, §11, §12)

### I-2 · 무료로 호출할 수 있는 온체인 솔버

**개요.** `Solver.findBestRoutePlan(tokenIn, tokenOut, amountIn)`은 `view` 함수다. 누구나 무료 `eth_call` 하나로 신뢰가 필요 없는 최적 경로를 얻는다 — API 키도, 릴레이어도, 서명도 필요 없다 — 그리고 `swapBestExactIn`은 하나의 트랜잭션 안에서 원자적으로 경로를 풀고 정산한다.

**수학.** Solver 전체는 경로 공간 $\mathcal{R}_{\mathcal{D}}$ 위에서 메타 방정식(E1)을 제약 조건하에 최대화하는 것이며, 결정론적 파이프라인으로 실행된다: 도출 → 순위화 → 탐침 → 필터 → 할당 → 게이트 → 하한.

**중요한 이유.** 업계의 라우팅 지능은 감사 가능한 코드 줄 수조차 없는 비공개 서버 위에서 돌아간다. 우리의 것은 누구나 다시 실행해 같은 답을 얻을 수 있는 29,183바이트의 공개 바이트코드다. (§2, §10, §11.1)

### I-3 · 메타 방정식

**개요.** 애그리게이터 전체를 하나의 식으로 쓴 것.

**수학.**
$$R^{\star} = \arg\max_{R}\ \mathrm{net}(R)\cdot\prod_{\ell \in R}\hat{\Psi}(p_{\ell})\cdot\mathbf{1}[\mathrm{net}(R)\ge\Phi(R)]\cdot\Xi(R)$$

**중요한 이유.** 곱의 형태는 안전성 속성이다: 실패하는 단 하나의 인자 — 견적이 영인 레그, 자격이 없는 풀, 하한 아래의 경로, 적대적인 훅 — 가 경로의 점수를 정확히 영으로 만든다. 부드러운 페널티도, 조정 가능한 가중치도, 슬쩍 통과하는 아슬아슬한 경로도 없다. (§3)

### I-4 · 팔중 디스패처 Ω

**개요.** 여섯 가지 베뉴 종류 — Uniswap V2, V3, V4, Algebra, Solidly 안정형, Solidly 변동형 — 가 하나의 속성 테이블과 하나의 디스패처 뒤에서 네 개의 수학적 계열로 수렴한다.

**수학.** 상수곱 $y = \lfloor x(\mathrm{BPS}-f)\,r_{\mathrm{out}} / (r_{\mathrm{in}}\mathrm{BPS} + x(\mathrm{BPS}-f)) \rfloor$ (E2); Q64.96 형식의 집중 유동성, $\sqrt{P}' = L\sqrt{P}/(L + x_{f}\sqrt{P}/Q_{96})$, $y = L(\sqrt{P}-\sqrt{P}')/Q_{96}$ (E3); 위험한 모든 곱은 512비트 `mulDiv`를 거쳐 계산된다.

**중요한 이유.** 파편화는 수학의 문제가 아니라 언어의 문제로 드러난다. 계열마다 감사된 공식 하나가 모든 체인의 모든 베뉴에 가격을 매긴다; 알려진 계열의 새 포크는 새 코드가 아니라 테이블의 한 행이다. (§4)

### I-5 · 결정론적 도출 𝒟

**개요.** 풀은 요청되는 것이 아니라 **계산된다**. Hub는 팩토리, 페어, init-code 해시로부터 각 후보 주소를 도출하고, 런타임 바이트코드를 가진 주소만 받아들인다.

**수학.** $\mathrm{pool} = \mathrm{addr}_{20}(\mathrm{keccak256}(\mathtt{0xff}\,\Vert\,\mathrm{origin}\,\Vert\,\mathrm{salt}\,\Vert\,\mathrm{initCodeHash}))$ (E8).

**중요한 이유.** 팩토리의 답은 위조될 수 있지만, 산술은 그렇지 않다. 탐색에는 후보당 외부 호출이 전혀 들지 않으며 모든 EVM 체인에서 동일하게 작동하고, 풀 매니저와 팩토리는 결코 하드코딩되지 않고 런타임 구성으로 주어진다. (§7)

### I-6 · 전적으로 프레임 내에서 처리되는 Uniswap V4

**개요.** V4 풀에는 주소가 없고 어떤 호출에도 응답하지 않는다. BlazePhoenix는 그 id를 도출하고, 각각이 실재함을 PoolManager의 스토리지에 대해 **증명하고**, `extsload`를 통해 가격과 유동성을 읽고, 최대 열여섯 구간에 걸쳐 틱 북을 순회하고, 싱글턴을 통해 — 네이티브 ETH를 포함해 — 정산하며, 산술만으로 훅을 선별한다.

**수학.** 스토리지 기준 $\mathrm{slot} = \mathrm{keccak256}(\mathrm{abi.encode}(\mathrm{poolId}, 6))$ 이며, $\sqrt{P}$는 비트 [0,160)에, LP 수수료는 [208,232)에 위치한다(E4); 훅 게이트 $(\mathrm{uint160}(\mathrm{hook})\,\&\,\mathtt{0x3FFF})\,\&\,((1\ll3)\mid(1\ll2)) \ne 0 \Rightarrow$ 거부하며, 호출은 하지 않는다(E18); 동적 수수료 게이트 E19.

**중요한 이유.** 베뉴의 답을 단 하나도 신뢰하지 않고 V4를 도출하고, 증명하고, 가격을 매기고, 선별하고, 정산한다 — 애그리게이터가 가질 수 있는 가장 깊은 V4 통합이며, 그 모든 것이 온체인에서 검증 가능하다. (§5)

### I-7 · Solidly 안정형 솔버

**개요.** Aerodrome 및 Velodrome 계열 베뉴가 사용하는 사차(quartic) 안정 곡선을 위한, 정확하고 실패 시 닫히는(fail-closed) 솔버.

**수학.** 불변식 $k(x,y) = x^{3}y + xy^{3}$ (E5)를, 소수 자릿수를 18자리로 정규화한 뒤(E7), 테이커에게 불리한 방향으로 한 단위를 올리는 보정과 64단계 상한을 둔 유계 뉴턴 반복으로 푼다(E6).

**중요한 이유.** 스테이블코인과 리퀴드 스테이킹 흐름은 이 곡선들에 몰려 있으며, 이 곡선들은 입력의 거의 전부를 출력으로 전달한다. 솔버는 이것들을 정확히 견적한다 — 그리고 한계를 증명할 수 없는 경우에는 추측 대신 영을 반환한다. (§6)

### I-8 · 모노슬롯

**개요.** 풀의 라우팅 상태 전체 — 활동성, 깊이 등급, 수수료, 종류, 플래그, 최신성 — 를 **하나의 256비트 워드**에 담는다.

**수학.** $s = a \,\vert\, f\!\ll\!8 \,\vert\, k\!\ll\!32 \,\vert\, r\!\ll\!40 \,\vert\, (c\wedge\mathtt{0xFFF})\!\ll\!48 \,\vert\, b\!\ll\!60 \,\vert\, \dots \,\vert\, B_{\mathrm{last}}\!\ll\!224$ (E9).

**중요한 이유.** `SLOAD` 한 번으로 풀의 순위를 매기고, `SLOAD` 한 번 위의 `SSTORE` 한 번으로 그것을 최신 상태로 유지한다. 그리고 이 워드에는 가격이 들어 있지 않으므로, 레지스트리의 기억은 구조적으로 거래에 잘못된 가격을 매길 수 없다. (§8)

### I-9 · 자가 치유 레지스트리와 활력장 Ψ

**개요.** 프로토콜은 유동성을 거래함으로써 유동성을 학습한다. 페어당 열여섯 개의 슬롯, 실제 스왑으로 얻는 적합도 점수, 산술에 의한 감쇠, 그리고 **페어 증명**: 풀은 저장되기 전에 자신이 어떤 토큰을 거래하는지 증명한다.

**수학.** $\Psi = V \cdot 2^{b} \cdot (1 + 0.25\,\mathbf{1}_{\mathrm{bridge}}) \cdot (1 + 0.05\,\mathbf{1}_{\mathrm{conc}})$ (E10); 활동성은 24,576초의 침묵마다 절반으로 줄어든다, $V = \mathrm{swapCount} \gg \lfloor (t - t_{\mathrm{last}})/24{,}576 \rfloor$ (E11); 깊이 등급 $b = \min(15, \lfloor \log_{10}(d/10^{15}) \rfloor)$ (E12); 신규 풀은 마진을 넘어설 때에만 가장 약한 풀을 밀어낸다, $2^{b(d_{\mathrm{new}})} > \Psi_{\mathrm{worst}} + \lfloor \Psi_{\mathrm{worst}}/4 \rfloor$ (E13).

**중요한 이유.** 키퍼도, 크론도, 어떤 풀이 중요한지 정하는 큐레이터도 없다: 레지스트리는 사용자들이 이미 보내던 흐름으로부터 스스로를 치유한다. (§9)

### I-10 · 자본 고정 분할

**개요.** 가격 품질과 풀 깊이는 따로 측정된다. 아주 작은 탐침이 각 풀의 한계 환율을 측정하고, 자본에 고정된 **신뢰 대역**이 이상치를 버리며, 살아남은 풀들이 측정된 깊이에 비례해 주문을 나눠 갖는다.

**수학.** 중심 = 깊이 가중 중앙값 — $\sum_{j \le i} d_{j} \ge \lceil \sum d / 2 \rceil$ 를 만족하는 첫 번째 $r_{i}$ — 이며, 대역은 $[0.95, 1.05]\times$ 중심이다(E25); 분할은 $\mathrm{out}_{\mathrm{split}} \ge \mathrm{out}_{\mathrm{single}}\cdot(1 + 25/10^{6})$ 일 때에만 살아남는다(E14).

**중요한 이유.** 앵커를 움직이려면 공격자는 페어의 실제 유동성 절반보다 더 많이 예치해야 한다 — 공짜 기부가 아니라 위험에 노출된 자본이다. 상시 보장: **분할은 결코 최적의 단일 풀보다 적게 지급하지 않는다.** Base에서 전수 그리드와 비교해 측정한 결과, Solver의 경로는 최적값의 0.13 bps 이내에 있다. (§10)

### I-11 · Iron-Law 하한 Φ

**개요.** 측정된 가격 영향과 경로 집중도로부터 **온체인에서, 실행 시점에** 다시 도출되는 최소 출력 — 호출자가 제공한 어떤 것과도 무관하다.

**수학.** $\mathtt{floorBps} = \max(9{,}600 - \mathtt{legShv} - \min(\delta, 10^{4}) - \sigma_{\ln}/10^{14},\ 8{,}000)$ (E15)이며, 집중도 차감은 $\mathtt{legShv} = \sum_{\mathrm{hops}} \lfloor 200\,((\sum a)^{2} - \sum a^{2})/\sum a^{2} \rfloor$ (E23)이다; 전달량은 $\mathtt{effMin} = \max(\mathtt{userMinOut},\ \mathtt{route.singleOutFloor},\ \lceil \mathtt{finalHopQuote}\cdot\mathtt{floorBps}/10^{4}\rceil)$ (E16)을 충족해야 한다.

**중요한 이유.** 호출자 데이터는 보호를 강화할 수만 있을 뿐 결코 약화할 수 없다. 깊이의 1 %인 거래에서 하한은 샌드위치를 거래의 약 2.7 %로 제한하고 그 이상은 **거부한다** — 그 경계를 넘으면 공격자의 왕복 거래는 손실로 돌아선다. (§11.5, §14.3)

### I-12 · 모든 이음매에서의 측정

**개요.** Router는 전송이 일어났다고 결코 믿지 않는다; 측정한다. 모든 인출, 각 홉의 도착, 모든 전달은 스왑의 첫 명령부터 수령인의 지갑에 이르기까지 실제 잔고 변화량이다.

**수학.** 각 홉은 측정된 도착량에 맞춰 재조정된다; 상수곱 레그 위의 전송 수수료(fee-on-transfer) 토큰은 풀이 실제로 보는 차이, 즉 풀 자신의 불변식이 검사할 바로 그 수량으로 가격이 매겨진다.

**중요한 이유.** 세금을 떼거나, 리베이스되거나, 잘못 보고하는 토큰도 Router가 보유하지 않은 것을 약속하도록 밀어붙일 수 없다 — 그리고 Router는 트랜잭션 사이에 **유휴 상태로 아무것도 보유하지 않는다**(INV-1). (§11.2, §11.4)

### I-13 · 단일 콜백 원칙

**개요.** 하나의 `fallback`이 모든 V3 형태 베뉴 — Uniswap V3, Algebra, Pancake V3, Sushi V3, 집중 유동성 Solidly 포크 — 의 플래시 회계 콜백에 응답하며, 진행 중인 레그의 컨텍스트는 EIP-1153 트랜지언트 스토리지에 보관된다.

**수학.** 지급해야 할 토큰은 두 델타의 부호로부터 읽힌다; 지급은 세 가지 방식으로 제한된다 — 커밋된 풀에만, 레그가 진행 중일 때에만, 그리고 레그에 기록된 예산을 결코 넘지 않게(INV-10).

**중요한 이유.** 작고 삼중으로 보호되는 하나의 진입점이 포크별 콜백의 잡다한 무리를 대체한다 — 더 적은 바이트코드, 더 적은 공격 표면, 검증할 대상은 하나. (§11.4)

### I-14 · 잉여 규칙과 소진에 의한 면역

**개요.** 컴파일되어 들어간 0.28 % 수수료로, Router가 보유하는 첫 번째 브리지 코인에 한 번 부과된다 — 그리고 **견적과 전달량 사이의 차이에 따라 커지는 수수료 항은 없다.**

**수학.** $\mathrm{fee} = \lceil 28 \cdot \mathrm{base} / 10^{4} \rceil$, 분배 $t_{1} = \lfloor 3{,}000\cdot\mathrm{fee}/10^{4} \rfloor$, $t_{2} = \mathrm{fee} - t_{1}$ (E17); 체계 술어 $\mathrm{feeHop} = \min\{h : \mathrm{isBridge}(\mathrm{hops}[h].\mathrm{tokenIn})\}$ (E26). 모든 브리지를 피하는 경로는 모든 홉에서 수수료를 지불하므로, 어떤 먼지(dust) 접두 구간도 수수료를 빼돌릴 수 없다.

**중요한 이유.** 풀이 견적을 초과해 전달하는 모든 것은 온전히 당신에게 도달한다. 수수료는 트레이더에게 불리하게 인상되거나 우대받는 통합자를 위해 인하될 수 없다 — 그 키를 가진 사람은 아무도 없다. (§13)

### I-15 · 바이트–가스 교환율 ρ*

**개요.** Router의 네 개의 문은 인터페이스로 구현된 아핀 비용 모델이다: 온체인에서 경로를 풀거나, 미리 풀어 둔 경로를 calldata에 담아 오거나 — 해당 체인에서 더 저렴한 쪽을 택한다.

**수학.** $p_{\mathrm{byte}} < \rho^{*}$ 일 때, 그리고 그때에만 경로를 calldata에 담으며, $\rho^{*}$는 경로 형태에 따라 바이트당 66.9–105.5 가스로 측정되었다(E22).

**중요한 이유.** 모든 체인에는 바이트의 가격과 계산의 가격이 있다. BlazePhoenix는 그 둘 사이의 교환율을 측정해 트레이더에게 더 저렴한 문을 건넨다. (§17.6)

### I-16 · 사기 방지 사영자 Ξ

**개요.** V4 훅이 델타를 변경하는 권한을 선언한 풀은, 그 훅이 한 번도 호출되지 않은 채 **순위화 이전에** 후보 집합에서 제거된다.

**수학.** 메타 방정식의 $\Xi(R) \in \{0,1\}$ 로, 훅 주소에 대한 E18 비트 검사로 평가된다.

**중요한 이유.** 적대적인 훅은 거짓말할 기회조차 얻지 못한다: 결코 질문받지 않기 때문이다. 세 개의 독립적인 계층 — 주소 비트, codehash로 고정된 허용 목록, 그리고 Ξ — 이 트레이더와 악의적인 베뉴 사이에 서 있다. (§5.5, §14.2)

### I-17 · 일방통행 문

**개요.** 관리 권한은 바이트코드 안에서 되돌릴 수 없게 종료된다. `renounceControl`은 어떤 코드 경로로도 해제할 수 없는 플래그를 기록한다; 그 이후에는 추가만 가능한(grow-only) 큐레이터 계층만 남는다.

**중요한 이유.** "우리는 건드리지 않겠다"가 "우리는 건드릴 수 없다"가 된다. 수수료, 분배 비율, 하한은 애초부터 설정 가능했던 적이 없다(INV-4); 문이 닫힌 뒤에는 그 밖의 어떤 것도 설정할 수 없다. (§14.7)

### I-18 · 계측적 설계와 공개된 분모

**개요.** 위의 모든 것의 배후에 있는 규율 — 측정할 수 있는 모든 좌표는 측정하고, 나머지는 단조로 만들어 거짓 입력이 그 제공자에게만 해를 끼치게 한다 — 그리고 **모든 숫자를 그 분모와 나란히** 보고하는 검증 장치.

**수학.** 236개 파일에 걸친 1,609개의 선언된 테스트; 선별된 뮤턴트 268개 중 268개 제거, 각각은 반드시 실패해야 하는 단 하나의 테스트와 짝지어짐; 체계 요인들에 대한 강도 2(258쌍 중 258쌍) 및 강도 3(1,636개 삼중항 중 1,636개)의 커버링 배열; 14 + 5개의 변성 관계; 비트 단위까지 일치하는 세 가지 옵티마이저 빌드; Halmos와 Certora Prover를 사용한 기호적 증명.

**중요한 이유.** 대부분의 프로젝트는 테스트 개수를 공개한다. BlazePhoenix는 자신의 증거가 어떻게 조작될 수 있는지와 무엇을 기준으로 측정하는지를 공개하며 — 모든 커밋마다 그것을 다시 계산한다. (§2.3, §15)

### 스코어보드

| 측정된 속성 | 결과 |
|---|---|
| 정확히 예측값대로 정산되는 드리프트 없는 견적(240개 표본 연구) | **64 / 64** |
| 베뉴 병리 × 문 조합 중 정산되거나 우리의 셀렉터로 거부되는 것 — 세 번째 방식은 결코 없음 | **20 / 20** |
| 제거된 선별 뮤턴트 | **268 / 268** |
| 거부가 정확한 코드로 실행 검증된 제어 동작 | **31 / 31** |
| 두 곳에서 계산되며 명명된 테스트가 서로 묶어 주는 수량 | **7 / 7** |
| 구성에 의해 실행된 체계 요인 값의 쌍 | **258 / 258** |
| 구성에 의해 실행된 체계 요인 값의 삼중항 | **1,636 / 1,636** |
| 결과가 일치하는 견적 수학의 옵티마이저 빌드(runs 1 / 300 / 20,000) | **3 / 3** |
| 네 체인에 걸친 미리보기 → 전달 | **0–1 bps** |
| Base에서 전수 그리드 대비 Solver 경로 | **0.13 bps 이내** |
| 깊이의 1 %인 거래에서 하한이 허용한 뒤 그 이상은 거부하는 최대 샌드위치 | **≈ 2.7 %** |
| 공로가 인정된 외부 보안 연구자 · 치명적(Critical) 발견 | **25 · 0** |


## 목차

- 혁신(I-1–I-18)과 스코어보드
- §1 문제: 견적은 다른 누군가가 계산한 약속이다
- §2 해답과 규율: 불변식 주도 설계와 계측적 설계
- §3 메타 방정식과 의존성 예산
- §4 팔중 디스패처 Ω — 종류, 계열, 닫힌 형식
- §5 Uniswap V4 처음부터 끝까지 — 싱글턴, 존재 증명, 수수료 게이트, 훅
- §6 Solidly 솔버
- §7 결정론적 도출 𝒟
- §8 모노슬롯
- §9 자가 치유 레지스트리와 활력장 Ψ
- §10 라우팅과 분할 할당기
- §11 실행 계층과 Iron-Law 하한 Φ
- §12 견적 인터페이스, 그리고 견적이 노후화되는 방식
- §13 수수료 정책: 두 가지 체계와 잉여 규칙
- §14 보안 모델
- §15 측정된 검증 장치
- §16 아키텍처와 크기
- §17 측정된 성능
- §18 뼈대만 남긴 DEX
- §19 배포 현황
- §20 토큰 — BZPX
- §21 연구 프로그램과 보안 기록
- §22 신뢰해야 하는 것과 신뢰할 필요가 없는 것
- §23 관련 연구
- §24 결론
- §25 스테이킹 V4: 설계
- 부록 A · 원시 방정식(E1–E42)
- 부록 B · 불변식(INV-1–INV-31)
- 부록 C · 재현 명령
- 용어집
- 참고문헌

---

## §1 문제: 견적은 다른 누군가가 계산한 약속이다

스무 명의 딜러가 있는 도시에서 자동차를 판다고 상상해 보라. 좋은 중개인은 스무 곳을 모두 방문해 최고가를 찾고, 경우에 따라 판매를 그중 두 곳에 나누기도 한다. 대부분의 온체인 "중개인"은 자체 데이터베이스 — 어쩌면 오래된 — 에서 가격을 확인하고 결과를 보여 준다. 사용자는 그들이 스무 곳을 모두 살펴보았는지, 혹은 그중 최선의 결과를 보여 주었는지 검증할 수 없다. 검증할 수 있는 것은 그들이 지금까지 정직했다는 사실뿐이다.

우리가 문서를 읽은 모든 애그리게이터 — 2026-09-07 기준 문서상의 1inch, CoW Swap, Velora (ParaSwap), KyberSwap, OpenOcean, Jupiter, UniswapX — 는 이렇게 작동한다: 서비스가 오프체인에서 경로를 계산하고, 컨트랙트는 전달받은 경로를 실행한다. 컨트랙트는 선택하지 않고 검사한다. 이는 건전한 엔지니어링이며 — 그 시스템들이 빠르고 저렴한 이유이기도 하다 — 좀처럼 분명히 언급되지 않는 한 가지 결과를 낳는다: **그러한 시스템이 할 수 있는 가장 강한 주장은 그 운영자가 정직하다는 것이다.**

그 간극은 대개 사기가 아니다. 그것은 세 가지 평범한 원인에서 비롯되는 표류다. 견적은 트랜잭션이 포함되기 전에 이미 움직인 준비금을 기준으로 계산되었다. 탐색은 운영자의 예산이 허락하는 만큼만 철저했다. 그리고 견적과 체결 사이의 모든 보정 — 슬리피지 버퍼, 도중에 붙는 수수료 — 은 사용자가 읽지 않은 코드에 의해, 사용자가 재현할 수 없는 숫자에 적용되었다. 그 어느 것도 악의를 필요로 하지 않는다. 그 모두가 보이지 않는다.

### §1.1 파편화는 실재하지만, 보이는 것보다 얕다

최고가를 원하는 트레이더는 역설에 직면한다. 유동성은 존재하며, 각자의 풀 컨트랙트, 각자의 가격 곡선, 각자의 주소 체계를 가진 수십 개의 탈중앙화 거래소에 흩어져 있다 — 그러나 어떤 단일 베뉴도 그 전부를 보유하지 않는다. 최선 실행의 대가는 모든 거래소의 언어를 동시에 구사하는 비용이며, 표시된 가격과 실제로 체결되는 거래 사이의 모든 번역자를 신뢰하는 비용이다.

기존 사업자들의 해법은 베뉴를 하나씩 통합하는 것이다: Uniswap용 커넥터 하나, Solidly 포크용 커넥터 하나, 집중 유동성 변종마다 또 하나의 커넥터를 두고, 각각 맞춤형 견적, 맞춤형 주소 조회, 맞춤형 실행을 갖춘다. 통합 표면은 베뉴 수에 비례하여 선형으로 증가하며, 모든 커넥터는 가격 결함이나 견적된 숫자와 실행된 숫자 사이의 조용한 불일치가 새로 생길 수 있는 장소다.

파편화는 보이는 것보다 얕다. 표면적 다양성 아래에서, 온체인 유동성 대부분을 보유한 자동화 마켓 메이커는 소수의 불변식에 지배된다. 상수곱 풀은 $x \cdot y = k$를 따른다. 집중 유동성 풀은 구간별 상수인 활성 유동성 위에서 고정소수점 제곱근 가격을 추적한다. Solidly 계열은 스테이블 쌍에서 사차식 $x^{3}y + xy^{3} = k$를, 변동성 쌍에서 상수곱을 따른다. 형태는 예순 가지가 아니라 몇 가지뿐이다.

형태가 적다면, 애그리게이션 비용은 베뉴 수에 따라 증가할 필요가 없다; 형태 수에 따라서만 증가하면 된다. 각 형태를 한 번, 올바르게 구현하고 모든 베뉴를 올바른 형태로 디스패치하는 프로토콜은 통합 표면을 선형에서 상수로 축소한다. 베뉴 추가는 더 이상 엔지니어링이 아니라 설정이 된다: 종류(kind)와 주소 도출 모드를 지정하면, 기존 수학이 가격을 매기고, 기존 라우터가 정산하며, 기존 레지스트리가 그것을 학습한다.

**설계의 핵심 주장:** BlazePhoenix가 경유하는 모든 베뉴는 소수의 출력 함수 계열 중 하나의 매개변수화이며 — 여섯 종류 분류 체계(§4)에 걸친 네 개의 작동 계열 — 이 함수들은 단일 무상태 라이브러리에 한 번 보유되고 결코 재정의되지 않는다. 베뉴를 추가한다는 것은 새로운 가격 코드를 작성하는 것이 아니라 종류와 도출 모드를 지정하는 것을 의미한다.

### §1.2 오프체인 비상구

지배적인 애그리게이터 아키텍처는 서버 플릿에서 경로 견적을 내고, 오프체인 솔버들 사이에서 경매를 돌리고, 키퍼 봇으로 풀 유동성을 인덱싱하며, 그 결과를 얇은 온체인 정산 컨트랙트로 보낸다 — 이 컨트랙트는 흔히 관리자 키가 로직을 통째로 교체할 수 있는 업그레이드 가능 프록시 뒤에 있다. 웹 서비스가 가격을 만들고, 데이터베이스가 유동성 그래프를 보유하며, 특권 운영자가 규칙을 바꿀 수 있고, 체인은 다른 곳에서, 사용자가 볼 수도 검증할 수도 없는 소프트웨어가 내린 결정을 정산하는 장소가 된다.

그러한 구성은 이 기술이 제거하려 했던 바로 그 신뢰를 다시 들여온다. 오프체인 견적은 온체인 체결과 다를 수 있으며, 사용자는 그래서는 안 되었다는 것을 증명할 수 없다. 키퍼가 멈추면 라우터는 눈이 먼다. 프록시 관리자는 단 한 번의 트랜잭션으로 정직한 컨트랙트를 적대적인 컨트랙트로 바꿀 수 있다.

| 관심사 | 오프체인 해결형 애그리게이터 | BlazePhoenix-Dex |
|---|---|---|
| 견적 | 오프체인 서버 또는 솔버 경매 | 미리보기와 프레임 내 실행이 공유하는 하나의 온체인 디스패처 |
| 유동성 발견 | 키퍼 봇이 인덱싱하고 목록을 푸시 | 자가 치유 레지스트리 — 거래를 통해 학습되는 유동성 — 와 결정론적 발견 |
| 경로 선택 | 독점적 오프체인 최적화기 | 누구나 공개 상태로부터 재현할 수 있는 온체인 Solver |
| 업그레이드 표면 | 관리자가 통제하는 프록시 | 무상태 Core; 프록시 없음, 업그레이드 경로 없음, `selfdestruct` 없음 |
| 자금에 대한 특권 | 흔히 존재 | Router는 유휴 상태에서 아무것도 보유하지 않으며, 사용자 원금을 옮기는 함수는 없다 |
| 체인 간 일관성 | 체인별 백엔드 로직 | 체인마다 하나의 바이트코드 |
| 신뢰 가정 | 운영자의 정직성 | 사용자 자신의 최소값 — 컨트랙트는 이를 영으로 받아들이기를 거부한다 |

BlazePhoenix는 이 거래의 양쪽 절반을 모두 거부한다. 표면이 선형이 아님을 보임으로써 선형 통합 표면을 거부하고, 발견, 견적, 점수 산정, 라우팅, 정산이라는 결정 전체를 누구나 읽을 수 있고 모든 체인에서 같은 답을 내는 컨트랙트 안에 둠으로써 오프체인 비상구를 거부한다. 비용은 실재한다: 집중 풀의 견적을 온체인에서 내는 것은 데이터베이스에서 읽는 것보다 비싸다. 그 비용으로 얻는 것은 한 부류의 실패 — 서버가 약속한 것과 체인이 전달한 것 사이의 조용하고 증명 불가능한 괴리 — 의 제거다. 뒤따르는 모든 설계 결정은 그 교환의 한 사례다.

## §2 해답과 규율

**선택하는 것이 곧 거래하는 것이다.**

BlazePhoenix-Dex에는 여섯 개의 값 — 두 토큰, 수량, 사용자의 최소값, 수령인, 기한 — 을 받는 진입점이 있으며, 이 진입점은 경로를 실행하는 트랜잭션 내부에서 공개 상태인 풀 준비금으로부터 경로를 산출한다: 어느 베뉴로, 각각 얼마를, 어떤 순서로. 경로가 공개 코드에 의해 공개 상태로부터 도출되었으므로 누구나 그것을 다시 도출할 수 있다. 정산 시 구속력을 갖는 숫자는 같은 디스패처가, 같은 프레임에서, 같은 실시간 상태로부터 다시 도출하므로, 견적과 실행은 서로 맞춰 보아야 할 두 개의 산출물이 아니라 하나의 계산이며, 미리보기와 체결을 가르는 것은 두 번째 모델이 아니라 시간이다.

추가로 세 개의 진입점은 calldata로부터 미리 계산된 경로를 받는데, 이는 더 저렴한 경로를 원하고 자신의 답을 직접 제공할 의향이 있는 호출자를 위한 것이다; 동일한 측정, 하한, 정산 메커니즘이 네 개의 문 모두에 적용된다(§11). 우리는 오프체인 접근 방식이 틀렸다고 주장하지 않으며, 더 저렴하다고 주장하지도 않는다. 우리가 주장하는 것은 다른 부류의 보장 — 약속된 것이 아니라 감사 가능한 것 — 이며, 이는 그것을 원하고 그 대가로 가스를 지불할 사용자를 위한 것이다.

이 설계의 네 가지 결정은, 우리가 확인할 수 있는 한, 이 분야에서 이례적이며, 각각은 그것을 정직하게 유지하는 경계와 함께 기술된다.

- **주소는 가져오는 것이 아니라 도출된다.** 베뉴 계열의 배포가 재현 가능한 곳이라면 어디서든, 풀의 주소는 팩토리의 스토리지에서 읽는 대신 CREATE2 산술 — 누구나 세 개의 공개 입력으로 재검증할 수 있는 정리 — 로 계산된다; 산술이 건전하지 않은 곳에서는 프로토콜이 질의하며, 질의된 풀은 목록에 오르기 전에 자신의 쌍을 스스로 증명한다(§7).
- **풀의 라우팅 상태는 하나의 워드다.** 모노슬롯은 베뉴의 순위를 매기는 데 필요한 모든 것을 단일 256비트 슬롯에 패킹하며 의도적으로 가격을 포함하지 않으므로, 레지스트리의 기억은 어떤 풀에 질의할지에 편향을 줄 수는 있어도 구조적으로 거래의 가격을 매길 수 없다(§8–§9).
- **하한은 실제로 일어난 거래로부터 재도출된다.** Router는 실행 프레임 내부에서 각 레그의 실현된 영향과 각 홉의 실현된 집중도를 측정하고 그 측정값으로부터 하한을 재계산하므로, 호출자가 제공한 필드는 보호를 강화할 수는 있어도 결코 완화할 수는 없다(§11).
- **주소가 없는 베뉴에서 발견은 배포자를 보지 않는다(deployer-blind).** Uniswap V4 싱글턴의 경우, 풀 식별자는 공개 매개변수로부터 도출된 뒤 매니저 자신의 스토리지에 대해 실재함이 증명된다 — 다른 누구도 써 넣을 수 없는 증명이다(§5).

이들 중 어느 것도 모든 애그리게이터가 안고 있는 잔여 위험 — 서명과 포함 사이에 상태가 움직일 수 있다는 것 — 을 제거하지 못하며, 그렇기 때문에 사용자가 직접 설정하는 최소값은 필수이며 최종적이다: 모든 문은 토큰이 움직이기 전에 `RouterE(10)`으로 영인 최소값을 거부한다.

### §2.1 불변식 주도 설계

해답 뒤에는 방법이 있다. 우리는 그것을 불변식 주도 설계라 부른다: 프로토콜은 일련의 연산이 아니라 도달 가능한 모든 상태에서 성립해야 하는 속성들의 집합으로 명세되며, 코드는 그 속성들이 구조적이도록 — 프로그래머의 경계심이 아니라 코드의 형태에 의해 강제되도록 — 조직된다.

대부분의 소프트웨어는 명령형으로 작성되며, 정확성은 창발적 희망이다. 일반 소프트웨어에서는 실패가 복구 가능하므로 이것이 용인된다. 공개 블록체인에서는 그 어느 것도 성립하지 않는다: 코드는 일단 배포되면 불변이고, 가치를 직접 수탁하며, 모든 가장자리가 사냥당하는 적대적 환경에서 실행되고, 단 하나의 산술 실수는 크래시가 아니라 되돌릴 수 없는 손실이다.

불변식은 시스템이 어떻게 그 상태에 도달했는지와 무관하게 참인 속성이다. *Router는 트랜잭션 사이에 자금을 보유하지 않는다.* *레지스트리 쌍은 최대 열여섯 개의 풀을 보유하며, 퇴출은 가장 적합한 열여섯 개를 남긴다.* *프로토콜 수수료를 지불하지 않은 정산은 존재하지 않는다.* 세 가지 원칙이 이러한 진술을 중심에 둔다:

| 원칙 | 진술 | 나타나는 곳 |
|---|---|---|
| 신뢰하지 말고 계산하라 | 값을 도출하거나 증명할 수 있는 곳에서는 결코 그것을 가져오거나 가정하지 않는다. | 𝒟가 풀 주소를 계산하고, Router는 측정된 잔액으로부터 수수료 기준과 하한을 프레임 내에서 재도출한다. |
| 단일 진실 공급원 | 모든 수학적 원시 연산은 한 번 정의되어 임포트되며, 결코 다시 기술되지 않는다. | Core가 모든 가격, 도출, 하한, 점수 산정 수학을 보유하고, Hub, Solver, Router, Quoter는 이를 임포트하며 어느 것도 원시 연산을 재정의하지 않는다. 공유 수량 레지스터(§15)는 생산자가 둘인 모든 수량에 등급을 매긴다. |
| 실패 시 닫힘(fail closed) | 속성을 보장할 수 없을 때는 추측에 기대어 진행하지 말고 정지한다. | Solver는 조작된 경로 대신 경로 없음을 반환하고(`SolverE(5)`); 스테이블 솔버는 포화된 견적 대신 영을 반환하며(§6); 레지스트리의 일관성 가드는 어떤 상태가 기록되기 전에 구조적으로 불가능한 베뉴를 되돌린다(§9). |

네 번째 원칙은 수탁을 지배한다: **자금으로 향하는 특권 경로는 없다.** Solver와 Quoter는 읽기 경로 컨트랙트이고; Router는 자금을 옮기는 유일한 컨트랙트이며, 사용자 자신의 한도 아래에서 원자적으로 옮기고, 이를 다른 곳으로 돌릴 수 있는 모든 관리 권한은 일방통행 문을 통해 영구적으로 포기될 수 있다(§14).

### §2.2 이 규율이 이 매체에 맞는 이유

명령형 코드를 위험하게 만드는 환경의 각 속성은 불변식 주도 코드가 이점으로 바꾸는 속성이다. 불변성은 증명 가능성에 보상한다: 배포된 코드는 패치할 수 없으므로 사전 증명의 가치는 그만큼 높다. 조합성은 단일 진실 공급원에 보상한다: 가격 함수는 그 작성자가 전혀 예상하지 못한 볼트 안에서 호출될 수 있으며, Core가 하나뿐이면 모든 호출자가 같은 답을 얻는다. 적대성은 실패 시 닫힘에 보상한다: 공격자에게는 작성자가 고려하지 않은 경로 하나만 있으면 되며, 가정이 깨질 때 정지하는 시스템은 공격자에게 즉흥의 여지를 주지 않는다. 걸려 있는 가치는 가독성에 보상한다: 하나의 수학적 코어 위의 다섯 개의 명명된 연산자로 표현된 프로토콜은 그것을 작성하지 않은 사람들이 한 줄씩 점검할 수 있다.

이 규율은 공짜가 아니며, 그 교환은 의도된 것이다: 오버플로 한계를 증명하는 것은 그것을 가정으로 치워 버리는 것보다 오래 걸리고; 모든 곱을 완전 정밀도 곱셈-나눗셈으로 처리하는 것은 단순한 곱셈이라면 들지 않을 가스를 소비하며; 주소를 도출하는 것은 주소를 가져오는 방식이라면 생략할 엔지니어링을 요구한다. 이는 배포 이후에는 복구할 수 없는 실패를 제거하는 대가로 배포 이전에 더 많은 비용을 치르는 것이다.

### §2.3 계측적 설계: 하나의 사상, 측정된 좌표, 단조 입력

불변식 주도 설계는 무엇이 항상 성립해야 하는지를 말한다. 추가적인 세 규칙은 그것을 지탱하는 코드가 어떻게 *형성*되는지를 말하며, 이들은 함께 우리가 이 분야의 다른 곳에서 이름 붙은 것을 찾지 못한 방법을 이루므로, 이 논문이 그것에 이름을 붙인다: **계측적 설계** — 컨트랙트를 하나의 *계측기*로 구축하는 것. 한 줄로 요약한 그 논지는 다음과 같다: *믿는 코드는 공격 표면이고, 측정하는 코드는 방어다.*

**한 점에서 평가되는 하나의 사상.** 애그리게이터는 베뉴별 통합을 포함하지 않는다; 그것은 하나의 매개변수화된 가격 사상을 공간(베뉴 형태, 체인, 수수료 모델)의 한 점에서 평가하는 것이다. 분기로 표현하면, 각자의 수수료 관례를 가진 여섯 개 남짓의 활성 베뉴 종류는 수백 개의 쌍별 조합으로 곱해진다 — 곱셈적인 바이트코드, 곱셈적인 감사 표면, 곱셈적인 표류. 사상으로 표현하면, 같은 범위가 소수의 *상태 형태*, 하나의 무상태 Core, 고유 단위로 운반되는 수수료 관례, 그리고 등록 테이블이 되며 — 바이트코드는 *가산적으로* 증가한다: 새로운 베뉴는 분기가 아니라 행이다. 이 축소를 지키는 규칙은 `KIND → SHAPE`이다: 베뉴 계열은 *데이터* — 기존 형태에 지정된 종류와 도출 모드 — 이며, 결코 적대적 입력이 선택할 수 있는 새로운 코드 경로가 아니다. Core는 형태를 비트 테이블(`THETA_ATTR`, §4)로 기술하므로 "이 종류는 준비금을 읽는가?"는 시프트 하나와 AND 하나이며, 누군가 잊어버릴 수 있는 기본 분기는 없다. 이 축소에는 제동 장치가 있다: 단일한 보편 공식은 없다. 두 개의 닫힌 형식 — 상수곱과 집중 유동성 — 은 정확히 평가되며, 복제가 부채가 될 모든 곡선에 대해서는 베뉴 자체의 바이트코드에 질의한다(§6). 사상은 *구성원 자격*을 축소할 뿐, 결코 *검증*을 축소하지 않는다.

**좌표는 측정되며, 결코 믿어지지 않는다.** 컨트랙트는 온체인 측정이 존재하는 곳에서 모델링된 값, 명목 값, 또는 호출자가 제공한 값에 근거해 행동하지 않으며 — 어떤 무허가 데이터도 어떤 코드가 실행될지를 선택하지 못한다. 신뢰할 수 없는 당사자가 제공하는 데이터 — 경로, 선언된 수수료, 주장된 깊이, 풀 id — 는 *좌표*다: 계측기를 겨눌 장소이며, 컨트랙트는 그곳을 스스로 읽고, 실패 시 닫힌다. 이 논문의 모든 핵심 방어는 그 한 사례다: 명목 수량 대신 측정된 인출(pull) 후 잔액(§11), calldata 대신 측정된 잔액 변화량에서 취한 수수료 기준(§13), 기부로 부풀릴 수 있는 원시 잔액 대신 측정된 깊이들의 깊이 가중 중앙값에 고정된 신뢰 대역(§10), 측정된 보유량에 대해 잘라 낸 용량 클램프(§10), 센티널을 신뢰하는 대신 실시간 상태를 읽는 V4 수수료 게이트(§5), 싱글턴 자체의 스토리지를 읽는 존재 증명 발견(§5), 그리고 발견이 목록에 올리기 전에 자신의 `token0`/`token1`을 증명하는 질의된 풀(§7). 저장소는 이 규칙 아래 호출자가 쓸 수 있는 모든 필드의 분류를 공개한다: 23개 필드 중 14개는 관측값과 대조해 확인되고, 5개는 조향용이며, 4개는 이유와 함께 선언된다(§15).

**측정이 끝나는 곳에서 단조성이 시작된다.** 어떤 숫자는 측정할 수도 재도출할 수도 없다 — 호출자의 하한, 호출자가 선언한 스케일, 호출자 자신의 최소값. 이런 숫자에 대해 설계는 그 숫자를 받아들이되 함수의 형태를 정하여 거짓말이 거짓말한 자에게만 해가 되도록 한다: 경로의 보증된 하한은 강제되는 하한을 *올릴* 수만 있다(§11); 레그의 보증된 견적이 측정된 견적을 충분히 덮지 못하면 측정된 견적으로 끌어올려진다(§11); 선언된 스케일링은 측정된 도착량으로 상한이 정해지므로, 과대 진술로 존재한 적 없는 자금을 라우팅할 수 없다; 그리고 프로토콜이 선택할 수 없는 유일한 숫자 — 사용자의 최소값 — 는 필수다. 단조 입력은 신뢰가 필요 없으므로, 누구든 오프체인에서 무료로 계산하여 calldata에 실어 보낼 수 있다 — 이것이 이 프로토콜이 *신뢰를 오프체인으로 옮기지 않고* 작업을 오프체인으로 옮기는 방법이며, Router가 하나의 해결형 문과 세 개의 calldata 문을 같은 보장과 함께 제공할 수 있는 이유다.

계측적 설계는 산출물을 둘러싼 엔지니어링도 지배하는데, 계측기는 그 교정만큼만 좋기 때문이다. 안전 마진은 측정된 위험으로부터 가격이 매겨진다: 분할 게이트가 25 ppm에 놓인 것은 레그 손익분기점이 3 ppm으로 측정되었기 때문이다(§10); 측정된 위험보다 27× 더 컸던 계획 헤어컷은 폐기되었다(§10). 외부 비교는 그 방법의 오차 막대를 동반하거나, 아니면 아예 게재되지 않는다(§17). 그리고 측정 장치 자체도 측정된다: 각 보증 계측기는 그 수치가 게재되기 전에 다른 경로로 이미 알려진 결함에 대해 교정되었다(§15). 이것이 이론의 전부이며, 이 문서의 나머지는 그 실천이다.

## §3 메타 방정식과 의존성 예산

애그리게이터 전체는 하나의 식의 최대화로 읽을 수 있다. 입력 토큰 $t_{\mathrm{in}}$, 출력 토큰 $t_{\mathrm{out}}$, 수량 $x$가 주어지면, Solver는 실행 가능성 하한과 사기 방지 게이트를 조건으로 하여 순수령 가치를 최대화하는 경로를 허용 가능한 경로들 가운데에서 탐색한다:

$$
R^{\star} \;=\; \mathrm{arg\,max}_{\;R \,\in\, \mathcal{R}_{\mathcal{D}}(t_{\mathrm{in}},\, t_{\mathrm{out}},\, x)} \;\; \mathrm{net}(R)\cdot\prod_{\ell\in R}\hat{\Psi}(p_{\ell})\cdot \mathbf{1}[\,\mathrm{net}(R) \ge \Phi(R)\,]\cdot \Xi(R)
$$
^e1

$$
\mathrm{net}(R) \;=\; \sum_{\ell\in R}\Omega(p_{\ell},\, x_{\ell})\;-\;f_{\mathrm{proto}}
$$

여기서 $\mathcal{R}_{\mathcal{D}}$는 결정론적 도출 𝒟와 레지스트리가 찾아낸 풀들 위의 경로 공간이고; $\Omega(p_{\ell}, x_{\ell})$는 풀 $p_{\ell}$을 통해 $x_{\ell}$을 밀어 넣는 레그 $\ell$에 대한 디스패처의 출력이며; $f_{\mathrm{proto}}$는 프로토콜 수수료이고; $\hat{\Psi}(p_{\ell})$는 레그의 정규화된 활력이며; $\Phi(R)$은 경로의 측정된 영향과 집중도에 대한 Iron-Law 하한이고; $\Xi(R) \in \{0, 1\}$은 사기 방지 사영자다.

| 연산자 | 이름 | 방정식에서의 역할 | 절 |
|---|---|---|---|
| Ω | 팔중 디스패처 | 각 레그의 가격을 매기고 — 풀 종류마다 하나의 분기 — 분할 할당기를 위해 토큰 단위 깊이를 보고한다 | §4 |
| 𝒟 | 결정론적 도출 | 경로가 사용하는 풀의 위치를 찾는다: 계열이 허용하는 곳에서는 계산된 주소로, V4 싱글턴에서는 증명된 풀 id로 | §5, §7 |
| Φ | Iron-Law 하한 | 경로를 실행 가능 집합으로 사영한다: 순출력 아래의 하한이며, 실행 프레임에서 재도출된다 | §11 |
| Ψ | 활력장 | 각 풀에 획득한 품질에 따라 가중치를 부여하며, 이는 패킹된 온체인 상태에서 읽힌다 | §9 |
| Ξ | 사기 방지 사영자 | 노출하기에 안전하지 않은 모든 경로의 가치를 정확히 영으로 매긴다 | §5, §14 |

이 인수분해는 의도된 것이다. Ω는 *풀이 얼마를 돌려주는가*에 답하고; Ψ는 *그것을 얼마나 신뢰할 것인가*에 답하며; Φ는 *경로가 허용 가능한가*에 답하고; 𝒟는 *풀이 어디에 있는가*에 답하며; Ξ는 *경로를 보여 주어도 안전한가*에 답한다. 곱의 형태는 실패하는 레그 하나가 경로의 점수를 붕괴시킴을 의미한다 — 영을 견적하는 레그, 또는 지위가 없는 풀은 곱을 할인하는 것이 아니라 영으로 만든다 — 그리고 지시 함수는 순출력이 하한 아래로 떨어지는 모든 경로를 강제로 영으로 만든다. 부드러운 페널티도, 조정 가능한 가중치도, 아슬아슬하게 빠져나가는 경로도 없다.

![FIG-1 — Solver의 파이프라인으로서의 메타 방정식: 𝒟와 레지스트리에서 나온 후보들이 Ψ에 의해 순위가 매겨지고 잘리며, Ω에 의해 견적되고, 레그들에 걸쳐 할당되고, Φ와 Ξ에 대해 검사된다; Router는 실행 프레임에서 Ω와 Φ를 재평가한다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-1.png)

온체인에서 Solver는 이 방정식을 파이프라인(`findBestRoutePlan`)으로 구현한다: 쌍별 후보는 레지스트리가, 그리고 레지스트리가 최신이 아닐 때는 𝒟가 수집하고; Ψ에 의해 순위가 매겨져 `MAX_CANDIDATES = 8`로 잘리며; Ω에 의해 탐침되고 견적되며; 신뢰 대역으로 필터링되고; 레그들에 걸쳐 할당되며; 조립된 경로는 Φ가 보증하는 하한을 지닌다. 그런 다음 Router는 실제로 도착한 수량에 대해 Ω와 Φ를 재평가한다. 이 방정식이 하는 주장 — 감사자가 코드에 요구할 수 있는 주장 — 은 그 파이프라인의 어떤 단계도 다섯 연산자 바깥의 어떤 것도 참조하지 않는다는 것이다. 어떤 수량이 다섯 연산자 중 하나의 인수가 아니라면, 그것은 경로에 영향을 줄 수 없다: 어떤 베뉴의 마케팅도, 어떤 오프체인 평판도, 어떤 운영자 선호도.

### §3.1 의존성 예산

형용사 대신 — *탈중앙화*, *서버리스*, *무신뢰*는 더 이상 정보를 전달하지 않는다 — 여기에 점검 가능한 목록이 있다: 하나의 거래가 정산되기 위해 존재해야 하고 계속 작동해야 하는 모든 것.

| 의존성 | 일반적인 오프체인 해결형 애그리게이터 | BlazePhoenix-Dex |
|---|---|---|
| 경로를 계산하는 서비스 | 필요 | 사용하지 않음 — Solver는 어떤 노드든 제공하는 `view` 함수다 |
| 가격 오라클 또는 피드 | 경우에 따라 다름 | 없음 — 다섯 컨트랙트에 피드 호출이 존재하지 않는다 |
| 솔버 네트워크 또는 경매 | 흔함 | 없음 |
| 키퍼, cron 또는 릴레이어 | 경우에 따라 다름 | 없음 — 활력은 다음에 읽힐 때 산술에 의해 감쇠하고, 레지스트리는 일반 거래로부터 학습한다 |
| 업그레이드 가능 프록시 | 흔함 | 없음 — 프록시 없음, `selfdestruct` 없음, 초기화 함수 없음; 유일한 `delegatecall`들은 컴파일러 자체가 공개 Core 라이브러리로 하는 호출로, 링크 시점에 고정된다 |
| 호스팅된 인터페이스 | 실질적으로 필요 | 편의 기능일 뿐 |

마지막 행은 대부분의 예산이 조용히 실패하는 곳이다. 호스팅된 인터페이스는 의존성이 아니라 편의 기능이다: `swapBestExactIn`은 사람이 손으로 조립할 수 있는 여섯 개의 값을 받으며, 통합자, 스크립트, 또는 경쟁자의 프런트엔드가 우리와 어떤 종류의 관계도 없이 이를 호출할 수 있다. 이 프로젝트가 운영하는 모든 서버가 오늘 밤 사라진다 해도, 컨트랙트는 내일도 오늘과 정확히 똑같이 견적하고, 라우팅하고, 정산할 것이다. 그 속성의 비용은 호출자가 탐색 비용을 가스로 지불한다는 것이다; 위의 예산은 그 가스로 없애는 것들의 목록이다.

불변성은 반대 방향으로도 작용한다. 결함은 패치될 수 없으며 — 한 번 감사하는 컨트랙트의 대가 — 그렇기 때문에 결함은 대신 공개되고 바운티가 걸린다(§21).

### §3.2 세 개의 경계

예산에는 세 개의 경계가 있으며, 각각은 그것이 한정하는 주장 옆에 기술된다. 인터페이스는 다른 모든 곳처럼 호스팅되며, 손상된 프런트엔드는 위협 모델에서 가장 큰 단일 노출이다: 컨트랙트는 전달받은 영이 아닌 최소값이 무엇이든 존중하므로, 사용자가 설정한 최소값은 어떤 인터페이스도 빼앗을 수 없는 유일한 한도다. 베뉴 발견은 이전 트랜잭션들이 기록한 상태를 읽는다 — 공개적이고 무허가이지만, 거래 시점에 만들어 낸 것은 아니다. 그리고 관리 권한은 일방통행 문을 통해 포기될 때까지 존재한다; 포기 이후 통제 계층 — 일시 중지, 역할, 트레저리, 배선, 제거 — 은 영원히 사라지고, 오직 증가 전용 큐레이터 계층만 남는다(§14). 무상태 코어, 서버 없는 운영, 증가 전용 거버넌스: 표가 곧 주장이며, 말은 그 그림자다.


## §4 팔중 디스패처 Ω

*쉽게 말하면:* 모든 종류의 풀에 가격을 매기는 방법을 아는 하나의 함수. *정확히 말하면:* Core의 `universalQuote(QuoteCtx, amountIn)`은 Solver와 Quoter 미리보기를 위한 견적 디스패처다; 이는 기대 출력과 분할 할당기를 위한 토큰 단위 깊이를 반환하며, 가격을 매길 수 없는 풀은 영을 반환하고 무엇이든 전송되기 전에 제외된다. Router는 동일한 Core 원시 연산을 자신의 프레임에서 레그별로 평가하며(`_hopScaleImpactAndQuote`), 이는 가스와 스택 깊이를 위해 유지되는 형제 함수이고, Quoter의 Exact Pass는 또 다른 질문을 던진다(§12); 셋은 저장소의 공유 수량 레지스터에 함께 기록되며, 그 일치는 가정되는 것이 아니라 명명된 테스트로 고정된다(§15). 이 내부 명칭은 디스패처가 출시될 당시의 여덟 종류 열거형에서 유래하여 유지되고 있다; 그 열거형은 이후 아홉 번째 구성원이 추가되었고, 세 개를 의도적 절제로 내주었다. 번호 매기기는 그 공백을 유지한다: 어제 기록된 모노슬롯은 영원히 동일하게 디코딩되어야 한다.

### §4.1 여섯 종류, 네 계열, 하나의 속성 테이블

| 종류 | 상수 | 계열 | 가격 분기 | Θ 속성 | 가스 사다리(Solver) |
|---|---|---|---|---|---|
| 0 | `KIND_V2` | 상수곱(Uniswap V2, Sushi, 포크) | `getReserves` 위의 닫힌 형식 `outV2` | 준비금 · 쌍 검증 가능 | 90,000 |
| 1 | `KIND_V3` | 집중 유동성(Uniswap V3 및 V3 형태의 포크) | `slot0` 위의 닫힌 형식 `outV3` | 풀에서 집중 · 쌍 검증 가능 | 110,000 |
| 4 | `KIND_V4` | Uniswap V4 싱글턴 | `extsload` 위의 닫힌 형식 `outV3` | 싱글턴 내 집중 | 180,000 |
| 5 | `KIND_SOLIDLY` | Solidly(Aerodrome, Velodrome, 포크), 스테이블 및 변동성 | 풀에 `getAmountOut` 질의; 복제된 곡선을 대체 수단으로 | 준비금 · 쌍 검증 가능 | 90,000 |
| 6 | `KIND_ALGEBRA` | Algebra(Camelot 계열), 동적 수수료 | `globalState` 위의 닫힌 형식 `outV3` | 풀에서 집중 · 쌍 검증 가능 | 110,000 |
| 8 | `KIND_V4_NATIVE` | 체인의 네이티브 통화를 보유한 V4 풀 | 종류 4와 같은 분기 | 싱글턴 내 집중 | 215,000 |

여섯 종류, 네 개의 작동 계열: 상수곱 {0}, 집중 {1, 6}, Solidly {5}, 그리고 V4 싱글턴 {4, 8}. 속성 열은 산문이 아니다: 그것은 Core의 비트 테이블 `THETA_ATTR = 0x040A9400A9`이며, 종류당 네 비트 — *준비금을 읽음*, *풀 주소에서 집중*, *싱글턴 내 집중*, *`token0()`/`token1()`을 노출함* — 로 되어 있으므로, 다섯 컨트랙트의 모든 구성원 자격 질문("이 종류는 준비금을 읽는가?")은 시프트 하나와 AND 하나(`kindHas`, `kindHasAny`)이며, 비트가 없는 종류는 누군가 잊어버릴 수 있는 기본 분기 없이 실패 시 닫힌다. 가스 열은 두 번째 테이블 `THETA_GAS`이며, Solver의 `estGas`만이 읽고, 단위는 레그당 5,000 가스다.

**종류 2, 3, 7은 의도적 절제로 인해 부재한다.** 가중 계열(3)은 라우팅 가능한 견적을 한 번도 산출하지 못했다. 두 Curve 계열(2, 7)은 작동했지만 그럼에도 2026년 팔월의 신뢰 경계 강화 과정에서 제거되었는데, 이를 지원하려면 Router의 유일한 토큰 `approve`, 풀에서 측정되는 대신 호출자가 보증하는 유일한 깊이 수치, 그리고 Hub의 쌍 진위 증명에서의 유일한 예외가 필요했기 때문이다 — 세 개의 공격 표면이며, 각각 그 부류의 마지막이었다. 폐기된 번호는 결코 재할당되지 않는다: `decodeKind`는 모노슬롯 비트에서 종류를 읽으므로, 2를 재사용하면 그 번호로 기록된 모든 풀이 재해석될 것이며, 절제된 심볼이 돌아오면 CI 가드가 빌드를 실패시킨다.

**네이티브 통화 종류(8)는 하나의 불변식이 그곳에서, 그리고 오직 그곳에서만 깨지기 때문에 존재한다.** Router의 작업 가정 — 모든 자산은 `balanceOf`를 가진 ERC-20이다 — 은 정확히 V4 풀의 통화가 체인의 네이티브 자산일 때 실패하며, 그 예외를 *타입*으로 인코딩하면 그것이 풀 레코드에서 보이고, 경로 계획에 실려 운반되며, 감사자가 grep으로 찾을 수 있게 된다. 가격 산정에는 분기가 필요 없다: 풀 식별자는 정렬된 두 통화로부터 도출되며, `address(0)`은 구조상 맨 앞에 정렬된다. 이 종류는 경험적으로 그 자리를 얻는다: 서비스되는 체인 전반에 걸쳐 2026-08-13에 측정한 결과, V4 내부의 ETH 표시 유동성 중 62.9 %가 네이티브 풀에 있었다(Arbitrum 99.6 %, Optimism 95.0 %, Base 48.9 %).

### §4.2 닫힌 형식

**상수곱.** 준비금은 $x \cdot y = k$를 만족하며, 베이시스 포인트 단위의 수수료 $f$가 입력에서 차감된다. Core는 다음을 계산한다

$$
y \;=\; \mathrm{outV2}(x,\, r_{\mathrm{in}},\, r_{\mathrm{out}},\, f) \;=\; \left\lfloor \frac{x\,(\mathrm{BPS}-f)\; r_{\mathrm{out}}}{\,r_{\mathrm{in}}\cdot \mathrm{BPS} \;+\; x\,(\mathrm{BPS}-f)\,} \right\rfloor
$$
^e2

마지막 단계 전까지 나눗셈이 없으므로 유일한 반올림은 마지막 몫의 내림이며, `BPS` 이상의 수수료는 풀을 견적 불가능하게 만든다(영을 반환). 이 공식에 들어가는 수수료는 `effV2Fee(declared)`다: 영이거나 `V2_FEE_CEILING_BPS = 100`을 초과하는 선언된 수수료는 표준 30 bps로 대체되므로, 등록은 비현실적인 수수료를 선언함으로써 자신의 견적 — 그리고 그 견적이 고정하는 하한 — 을 축소할 수 없다; 같은 규칙이 실행 경로에도 반영되어 있으므로, 거래의 양쪽이 하나의 숫자로 가격을 매긴다.

**풀이 예제 1 — 상수곱 견적.** 한 풀이 표준 30 bps에서 $r_{\mathrm{in}} = 1{,}000{,}000$ USDC와 $r_{\mathrm{out}} = 500$ WETH를 보유하고 있다; 트레이더가 $x = 10{,}000$ USDC를 매도한다. 풀 자체의 단위(USDC는 소수점 6자리, WETH는 18자리)로는 $x = 10^{10}$, $r_{\mathrm{in}} = 10^{12}$, $r_{\mathrm{out}} = 5 \times 10^{20}$이다:

1. 수수료 조정 입력: $x(\mathrm{BPS}-f) = 10^{10} \times 9{,}970 = 9.97 \times 10^{13}$.
2. 분자: $9.97 \times 10^{13} \times 5 \times 10^{20} = 4.985 \times 10^{34}$.
3. 분모: $10^{12} \times 10^{4} + 9.97 \times 10^{13} = 1.00997 \times 10^{16}$.
4. 출력: $\lfloor 4.985 \times 10^{34} / 1.00997 \times 10^{16} \rfloor = 4.935790 \times 10^{18}$ wei, 즉 **4.935790 WETH**(소수점 여섯째 자리까지; 컨트랙트는 마지막 몫을 wei 단위로 내림한다).

USDC당 0.0005 WETH라는 현물 가격에서라면 같은 10,000 USDC로 5 WETH를 얻을 것이다; 체결은 현물보다 1.28 % 낮으며, 그중 0.30 %가 수수료다. 이 레그에 대한 Core의 준비금 기반 영향 측정값은 $\mathrm{impactV2Bps} = \lceil x \cdot \mathrm{BPS} / (r_{\mathrm{in}} + x) \rceil = \lceil 10^{14} / 1.01 \times 10^{12} \rceil = 100$ bps이다 — Iron-Law 하한이 소비하는 바로 그 숫자다(§11).

**집중 유동성.** V3 형태의 풀은 준비금을 저장하지 않는다; 대신 Q64.96 고정소수점의 제곱근 가격 $\sqrt{P}$와 활성 유동성 $L$을 저장한다. 하나의 틱 안에서, 수수료 $f$가 백만분율 단위이고 $x_{f} = \lfloor x\,(10^{6} - f)/10^{6} \rfloor$일 때 token0 → token1에 대해:

$$
\sqrt{P}\,' \;=\; \frac{L\,\sqrt{P}}{\,L \;+\; x_{f}\,\sqrt{P}/Q_{96}\,},
\qquad
y \;=\; \frac{L\left(\sqrt{P}-\sqrt{P}\,'\right)}{Q_{96}}
$$
^e3

이고, token1 → token0에 대해서는 $\sqrt{P}\,' = \sqrt{P} + x_{f} Q_{96}/L$ 및 $y = L\,Q_{96}(\sqrt{P}\,' - \sqrt{P})/(\sqrt{P}\,\sqrt{P}\,')$이며, 나눗셈은 두 단계로 나뉜다. 단순한 계산은 오버플로한다: $L \cdot \sqrt{P}$는 $2^{288}$에 도달할 수 있다. Core는 이를 결코 형성하지 않는다 — 모든 곱은 완전 정밀도 `mulDiv`(512비트 중간값, 256비트 결과)를 통해 인수분해되므로 각 단계가 범위 안에 들어맞음이 증명 가능하며, 가격을 잘못된 방향으로 또는 영으로 움직일 단계는 숫자 대신 영을 반환한다. Algebra 풀(종류 6)은 이 분기를 공유하며, 분기가 처리하는 한 가지 측면에서만 다르다: 그 상태와 동적 수수료는 `globalState`에 있으며, 한 번 읽힌다(§5.4).

이 공식은 단일 틱 안에서는 정확하지만 틱 경계를 넘어서는 정확하지 않으며, 그곳에서 단일 틱 형식은 과대 추정한다. 디스패처는 틱 교차 산술을 복제하지 않는다: 베뉴의 내부 수학을 복제하는 것은 구조적 부채인데, 사본과 원본 사이의 어떤 괴리든 잘못된 가격의 견적으로 드러나기 때문이다. 그 자리에는 두 가지가 있다. Core는 경계 클램프(`sqrtBoundary`)를 지니며, 이는 현재 범위의 가장자리에서 단계를 잘라 낸다 — 범위 안에 들어맞는 부분에 대해서는 정확하고 나머지에 대해서는 엄격히 아래이며, 결코 위가 아니다 — 그리고 설계는 이를 순위 매기기가 아니라 *약속* 쪽에 두는데, 틱이 있는 계열만 클램프하면 두 가지 관례 아래에서 베뉴를 비교하게 되기 때문이다. 이 논문이 측정하는 트리에서 클램프는 정의되어 있지만 아직 연결되지 않았다: `Router._v4LegQuote`와 Core의 순위 매기기 분기는 모두 클램프되지 않은 단일 틱 형식을 호출하므로, 범위를 벗어나는 집중 레그는 레그별 하한(§11.3)과 홉 예산에 의해서만 한정되며, Iron-Law 하한이 최후의 방어선이 된다. 그리고 실행 시 Iron-Law 하한은 Router가 실제로 도착한 수량으로부터 프레임 내에서 재도출하는 견적에 대해 강제되므로, 틱 경계 과대 추정은 경로의 순위 정확도를 희생시킬 뿐, 결코 사용자의 하한을 희생시키지 않는다. Quoter의 Exact Pass(§12)는 통합자가 원할 때 풀 자체의 숫자를 제공한다.

**깊이.** 모든 분기는 토큰 단위의 깊이도 반환한다 — 준비금 계열에서는 더 짧은 준비금 쪽(`shortSide18`), 집중 계열에서는 $L$이 현재 가격에서 나타내는 가상 준비금 $L/\sqrt{P}$ 또는 $L\sqrt{P}$(`depthFromL18`) — 그래서 같은 깊이의 V3 풀과 V2 풀은 $\sqrt{P}$ 배만큼 차이 나는 대신 할당기에서 동일하게 비교된다.

### §4.3 두 가지 수수료 단위, 고유 단위로 운반

상수곱과 Solidly 베뉴는 수수료를 베이시스 포인트로 견적하고, 집중 베뉴는 백만분율로 견적한다. 디스패처는 각각을 고유 단위로 운반하며 결코 상호 변환하지 않으므로, 상수 하나 대신 둘이라는 대가로 한 부류의 반올림 실패를 제거한다. 두 베뉴 계열 — V4 동적 수수료 풀과 Algebra — 은 실행 시점에만 자신의 수수료를 알며, 둘 다 가격을 공급하는 것과 동일한 상태 읽기로부터 수수료를 측정하여 처리된다(§5.4).

### §4.4 하나의 디스패처, 두 개의 표면

| 종류 | Quoter 미리보기 | 프레임 내(Solver의 계획; Router의 재도출) |
|---|---|---|
| V2 (0) | 닫힌 형식 `outV2` | 닫힌 형식 `outV2` |
| V3 / Algebra (1, 6) | `slot0` / `globalState` 위의 닫힌 형식 `outV3`; Algebra의 실시간 수수료는 같은 읽기로부터 측정 | 동일 |
| Solidly (5) | 풀에 질의; 복제된 곡선 × 9,800 / 10,000을 대체 수단으로 | 동일 |
| V4 / V4 네이티브 (4, 8) | `extsload`를 통한 닫힌 형식 `outV3`, 실패 시 닫히는 수수료 게이트(E19) | 동일 |

따라서 미리보기와 체결을 가르는 것은 시간이다: 미리보기 블록과 실행 프레임 사이에 움직인 상태. 정확히 그 간극을 240개 샘플에서 측정한 결과는 §12 절에 제시된다.

## §5 Uniswap V4 전 과정

Uniswap V4는 새로운 풀 컨트랙트가 아니다. 그것은 새로운 형태의 베뉴이며, 2020년부터 2024년 사이에 만들어진 모든 라우터가 기대고 있는 네 가지 가정을 무효로 만든다. 풀에는 주소가 없다 — 풀은 하나의 싱글턴 스토리지 안의 항목이며, 해시를 키로 한다. 스왑 내부에서 임의의 코드가 실행된다 — 훅이 라우터가 수행 중인 정산의 정해진 지점에서 실행된다. 수수료는 상수일 필요가 없다. 그리고 풀은 `balanceOf`가 없는 체인의 네이티브 통화를 보유할 수 있다. 이 절은 네 가지 모두에 구조적으로 답하여, V4 레그가 Solver에게도, 하한에게도, 트레이더에게도 다른 어떤 레그와 구별되지 않도록 한다.

### §5.1 주소가 없는 풀 찾기: 도출한 뒤 증명한다

결정론적 도출 𝒟는 주소를 계산하지만(§7), V4 풀에는 계산할 주소가 없다. 풀이 가진 것은 식별자 — 그 `PoolKey`의 keccak 해시 — 이므로, 도출 표의 모드 9는 주소가 아니라 *poolId*를 도출하고, 이어서 어떤 주소 도출도 할 수 없는 일을 한다. 즉 해당 id에 대한 PoolManager 자체의 스토리지를 읽어 영이 아닌 제곱근 가격과 영이 아닌 유동성을 요구함으로써 풀이 존재함을 증명한다. 그 스토리지는 매니저 외에는 누구도 쓰지 않으므로, 누구도 이 증명을 위조할 수 없다. 조작된 id는 영으로 해석되어 폐기된다.

**풀이 예제 4 — 키로부터 얻는 V4 풀 식별자.** 정준 id는 $\mathrm{poolId} = \mathrm{keccak256}(\mathrm{abi.encode}(c_{0}, c_{1}, \mathrm{fee}, \mathrm{tickSpacing}, \mathrm{hooks}))$ (`computeV4PoolId`)이며, 32바이트 워드 다섯 개로 이루어진다. Base에서 0.05 % 등급의 네이티브 ETH / USDC 풀을 예로 든다: $c_{0} = \mathtt{0x0000…0000}$ (네이티브 통화가 먼저 정렬된다), $c_{1} = \mathtt{0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913}$ (USDC), fee $= 500$, tickSpacing $= 10$, hooks $= \mathtt{0x0}$.

1. `abi.encode`는 160바이트를 배치한다: 두 통화를 각각 32바이트로 왼쪽 패딩하고, 이어서 `500`, 이어서 `10`(`int24`이며 32바이트로 부호 확장된다), 마지막으로 영 훅이 온다.
2. $\mathrm{poolId} = \mathtt{0x96d4b53a38337a5733179751781178a2613306063c511b78cd02684739288c0a}$.
3. V4 행에 대한 레지스트리의 `pool` 필드는 id의 하위 160비트인 $\mathtt{0x781178a2613306063c511b78cd02684739288c0a}$이다 — 코드가 없는 주소이며, 이 때문에 V4 행에는 결코 `token0()`를 묻지 않고, 잔액을 읽지도 않으며, 대신 재도출로 스스로를 증명한다(§9).
4. 풀의 스토리지 기준점은 $\mathrm{keccak256}(\mathrm{abi.encode}(\mathrm{poolId}, 6)) = \mathtt{0x13a3a697…73b2893d}$이다: 매니저의 슬롯 6이 `pools` 매핑이다. Core는 그 슬롯에서 워드 0을, $\mathrm{base} + 3$에서 워드 3을 읽는다(`v4SqrtAndLiq`, 셀렉터 `extsload(bytes32)` = `0x1e2eaeaf`).
5. 워드 0은 비트 [0, 160)에 $\sqrt{P}$를, [160, 184)에 틱을, [184, 208)에 프로토콜 수수료를, [208, 232)에 LP 수수료를 담는다. 워드 3은 하위 128비트에 활성 유동성을 담는다. $\sqrt{P}$나 $L$ 중 하나라도 영이면, 이 프로토콜에서 그 풀은 존재하지 않는다.

같은 쌍의 래핑된 형제 — $c_{0} = \mathtt{0x4200…0006}$ (WETH), 같은 등급 — 는 id $\mathtt{0x90333bb0…ebd943a0}$를 갖는다: 깊이가 다른 별개의 풀이며, 이 때문에 스캔은 두 키를 모두 탐색하고 네이티브 키를 먼저 탐색한다(아래 참조). 5단계의 레이아웃은 문서에서 가져온 것이 아니다: 메인넷 포크에서 같은 풀을 이 레이아웃으로 읽은 결과와 정준 `StateView` 컨트랙트로 읽은 결과가 같은 숫자를 반환하며, Core의 주석이 인용하는 것이 바로 이 동일성이다.

**비용 사다리.** 후보 id를 만들어 내는 스캔은 가장 저렴한 단부터 시작하는 사다리이다. 모든 수수료–간격 조합을 순진하게 훑는 방식은 솔빙 경로에서 받아들일 수 없는 가스 비용을 치르게 하기 때문이다:

| 단 | 시도하는 것 | 적중 시 비용 |
|---|---|---|
| 1 | 이 토큰 쌍에 대해 이미 학습된 등급 패턴 | 한 번의 탐색 — 정상 상태 경로 |
| 2 | 콜드 스캔 비용을 치르기 전의 출처 확인 | 한 번의 읽기 |
| 3 | `extsload(bytes32[])`로 일괄 처리되는 정준 Uniswap 등급 | 일괄 처리당 한 번의 호출 |
| 4 | 등록 행이 선언한 `(fee, spacing)` 쌍 | 등록에 의해 상한이 정해짐 |
| 5 | 콜드 스타트 생성기 그리드 — fee $= 10{,}000\,j$, spacing $= 100\,j$, $j$는 `V4_GRID_MAX = 99`부터 내림차순 | 최대 `V4_GRID_PROBES = 40`개 탐색 |

전체 스캔은 증명된 풀이 `V4_CAP = 8`개에 이르면 멈춘다. 두 가지 속성은 검사가 아니라 구성 자체에서 나온다. 첫째, 훅이 있는 풀은 이 스캔에서 결코 방출될 수 없다 — 스캔은 *훅 없는* id를 도출하며, 훅이 있는 풀의 훅 없는 id는 매니저에 존재하지 않으므로, 어떤 정책 질문이 제기되기도 전에 생존 증명에서 실패한다. 둘째, 서로 다른 등급 추측에서 나온 중복 id는 하나의 후보로 합쳐지므로, 하나의 풀이 별칭을 통해 깔때기를 포화시킬 수 없다. 탐색 순서는 그 자체로 하나의 정책이다. 상한은 먼저 탐색된 것에 소모되기 때문이다: 체인의 네이티브 자산을 상대로 하는 쌍은 V4에서 네이티브와 래핑의 두 키를 가지며, 순서를 정할 당시 서비스하던 체인들에서 네이티브 키가 더 깊은 쪽으로 측정되었으므로(Base에서 3.76×, Robinhood Chain에서 292×) 먼저 탐색된다.

![FIG-2 — V4 도출–증명 사다리: 학습된 등급 패턴에서 상한이 정해진 생성기 그리드까지 다섯 단이 있으며, 각 후보 id는 여덟 개 상한에 계산되기 전에 매니저의 스토리지에 대해 생존이 증명된다. 훅이 있는 id는 이 경로에 결코 존재하지 않는다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-2.png)

**무허가 문.** 누구나 `claimV4(c0, c1, fee, tickSpacing)`를 호출하여 훅 없는 풀을 등록시킬 수 있다. 청구가 자체 증명을 지니기 때문이다: id는 제출된 키로부터 다시 계산되어 매니저의 스토리지에서 되읽히므로, 거짓 청구는 영으로 해석되어 아무것도 등록시키지 못한다(`HubE(9)`). 추가로 세 가지 조건이 문을 통제한다: 쌍의 한쪽은 라우팅 가능한 브리지 코인이어야 하므로, 상한이 있는 레지스트리의 자리가 라우터가 결코 건널 수 없는 쌍에 소모되지 않는다. 실효 수수료는 아래의 수수료 관문에서 견적 가능해야 한다. 그리고 청구는 스왑에 의한 등록이 통과하는 것과 같은 등록 마진(§9)을, 풀의 측정된 유동성을 깊이로 삼아 통과해야 한다. 레지스트리는 대중에게서 배울 수 있지만, 대중에게 속을 수는 없다.

### §5.2 어떤 호출에도 응답하지 않는 풀 읽기

생존이 증명된 id가 있으면 디스패처에게는 가격과 유동성이 필요하다 — 그런데 호출할 `slot0()`가 없다. Core는 위 예제에서처럼 `extsload`로 싱글턴의 스토리지를 직접 읽으며, V4 분기는 집중 유동성 닫힌 형태(§4.2)로 환원된다: 같은 수학, 같은 오버플로 규율이다. 싱글턴은 상태가 어디에 있는지를 바꿀 뿐, 상태가 무엇을 의미하는지는 바꾸지 않는다. 코드가 없거나 규격에 맞지 않는 매니저는 모두 영인 워드를 내놓는다 — 실패 시 닫힘이며, 결코 revert하지 않는다.

### §5.3 싱글턴과의 정산

실행은 일반적인 제어 흐름을 뒤집는다. 풀을 호출하고 지불을 위해 콜백을 받는 대신, Router는 매니저에서 `unlock`을 호출하고 매니저가 부여한 콜백 안에서 스왑 전체를 수행한다: 스왑하고, 이어서 갚아야 할 모든 통화를 `sync → settle → take`로 정산한 뒤, 락을 해제한다. 진행 중인 통화는 트랜지언트 스토리지(`TSLOT_V4IN`, `TSLOT_V4OUT`)에 담겨 unlock 경계를 넘나들므로, 프레임은 어떤 영구 상태도 더럽히지 않으며 스왑 사이에 컨텍스트가 비어 있음이 증명 가능하다. 매니저가 반환하는 `BalanceDelta`는 패킹되어 있다 — `amount0`은 상위 128비트에, `amount1`은 하위에 — 그리고 Router는 어떤 정산 산술보다도 먼저 그렇게 디코딩한다.

네이티브 통화는 중요한 불변식을 지킨다. Router는 모든 곳에서 WETH를 정준으로 삼는다 — `swapExactInNative`로 들어오는 네이티브 가치는 문에서 정확히 한 번 래핑되며, 네이티브 출력은 의도적으로 구현되지 않았다: 수령인은 WETH를 받으며, 어떤 경로도 그것을 조용히 가둘 수 없다. 네이티브 V4 레그는 자신의 unlock 프레임 안에서 *적시에* 언래핑한다 — `WETH.withdraw`, 이어서 가치를 실은 `settle`, 그리고 네이티브 `take`로 받은 ETH는 같은 프레임 안에서 다시 래핑된다 — 따라서 네이티브 ETH는 컨트랙트 안에 정지 상태로 결코 존재하지 않는다. 이를 위해 필요했던 유일한 새 표면은 요구 사항이 허용하는 한 가장 좁게 통제되는 `receive()`이다: 트랜지언트 슬롯(`TSLOT_ETHOK`)이 그 순간 Router에 원시 ETH를 보낼 수 있는 단 하나의 주소를 보관한다 — 언래핑 중에는 정준 WETH 컨트랙트, 네이티브 take 중에는 PoolManager, 그 밖의 모든 순간에는 영 — 따라서 다른 누구에게서 오는 날것의 ETH도 revert된다. 네이티브 문은 `weth`가 연결될 때까지 실패 시 닫힘이다(`RouterE(3)`).

### §5.4 실행 시점에만 존재하는 수수료

정적 수수료 V4 풀은 LP 수수료를 키에 담으며, 거기서 그것은 불변이다. 풀 매니저는 추가로 Uniswap 자체의 매니저 수수료 — 방향당 1,000 pips, 즉 십분의 일 퍼센트로 상한이 정해진 — 를 가져갈 수 있는데, Core의 정적 분기는 이를 더하지 않으므로, Uniswap의 수수료 컨트롤러가 이를 활성화하면 정적 키에 대한 견적은 전달되는 출력을 최대 그 매니저 수수료만큼 과대 계상할 수 있다(이 글을 쓰는 시점에 서비스하는 모든 체인에서 이 값은 영이다). 그 부족분은 하한 안에 떨어지며, 수수료 기반은 측정된 값이므로 이를 무시한다. 동적 수수료 풀은 대신 센티널 `0x800000`을 담으며, 실제 수수료는 `slot0`에 있다. 센티널을 액면 그대로 견적하면 실행은 실시간 요율을 지불하는데도 거래를 무료로 가격 책정하게 된다. 관문은 Core의 세 줄이다(`effV4Fee`):

$$
f_{\mathrm{eff}} \;=\; \begin{cases} f_{\mathrm{key}} & f_{\mathrm{key}} \ne \mathtt{0x800000} \\ \mathtt{0xFFFFFF}\ (\text{unquotable}) & f_{\mathrm{key}} = \mathtt{0x800000} \ \wedge\ f_{\mathrm{proto}} \ne 0 \\ f_{\mathrm{LP}}\ \text{read from } \mathtt{slot0} & \text{otherwise} \end{cases}
$$
^e19

영이 아닌 프로토콜 수수료와 함께 오는 동적 수수료 키는 $10^{6}$ 이상의 수수료를 반환하며, `outV3`는 이에 대해 영을 반환한다: 실패 시 닫힘이다. 프로토콜 수수료와 LP 수수료의 합성이 우리가 만족할 만큼 고정되어 있지 않기 때문이며, 이 글을 쓰는 시점에 Base에서 측정된 프로토콜 수수료는 영이므로 이 분기는 실제로는 아무 비용도 들지 않는다. 같은 규칙이 한 계열 건너에서 같은 이음매를 닫는다: Algebra 풀은 구성상 동적 수수료이며 영 센티널을 등록하므로, `quoteV3Fee`는 영이 아닌 설정 수수료를 진실로 받아들이고, 동적 풀에 대해서는 이미 가격을 제공한 `globalState` 읽기에서 나온 수수료를, 그리고 — 수수료가 틱 간격을 키로 하고 풀에 저장되는 집중 유동성 포크의 경우 — 풀 자체의 `fee()`를 받아들이며, 아무것도 응답하지 않으면 `0xFFFFFF`로 실패 시 닫힘이 된다. 정적 V3 풀은 추가 읽기를 하지 않으며 바이트 단위로 동일하게 견적된다.

하나의 이음매가 남아 있으며, 그것은 제거되는 대신 경계 지어진다: 동적 수수료 훅은 `beforeSwap` 안에서 스왑마다 수수료를 재정의할 수 있으므로, `slot0`에 저장된 수수료가 모든 경우에 실행된 수수료인 것은 아니다. 그 잔여분은 등록 시 Hub의 훅 허용 목록과, 실행 시 실현된 출력으로부터 재도출되는 Iron-Law 하한 Φ에 의해 억제되므로, 적대적인 재정의는 revert나 하한 이내의 부족분으로 끝나며, 결코 하한 아래의 체결이 되지 않는다(§14).

### §5.5 훅: 심문이 아니라 산술로 선별한다

훅은 Router가 수행 중인 정산 안에서 실행되는 임의의 코드이다. 직관적인 방어 — 훅에게 무엇을 할 의도인지 묻는 것 — 는 방어가 아니다. 적대적인 훅은 거짓말하거나 재진입하기 때문이다. BlazePhoenix는 훅에게 아무것도 묻지 않는다. 대신 세 개의 독립적인 계층이 가장 저렴한 것부터 버티고 있으며, 그 상세는 뒤에서 전개된다(§14): 훅의 권한 비트는 자신의 주소 하위 열네 비트에 있으며 배포 시 고정되고, 스왑 델타 변경을 허용하는 두 비트는 토큰이 움직이기 전에 한 번의 AND로 거부된다(`hookAltersDeltas`, `RouterE(9)`). 훅은 허용 목록에 있고 그 런타임 코드가 등록 시 고정된 codehash와 여전히 일치하는 동안에만 라우팅 가능하다(`isHookLive`). 그리고 사영자 Ξ는 순위 매기기 전에 델타를 변경하는 훅을 Solver의 후보 집합에서 제거하므로, 그 경로는 표현조차 될 수 없다. 잔여 위험은 메커니즘과 함께 명시된다: 허용 목록에 있고 델타를 변경하지 않는 훅도 여전히 스왑 도중 revert하여 시도에 든 가스를 낭비하게 할 수 있다 — 신뢰할 수 없는 코드가 무엇을 했을지 알아내기 위해 그것을 호출하는 일을 원칙적으로 거부하는 대가이다.

### §5.6 이 통합의 가치

위의 모든 메커니즘은 이미 다른 곳에서 쓰이는 Core 기본 요소를 새로운 상태 레이아웃에 적용한 것이거나, V4를 넘어 일반화되는 진정으로 새로운 기본 요소 — 존재 증명에 의한 발견 — 이다. 어떤 것도 풀별 통합이 아니며, 어떤 것도 사람이 최신으로 유지해야 하는 큐레이션 목록이 아니다. V4 풀은 디렉터리 없이 발견되고, 다른 누구도 쓸 수 없는 스토리지에 대해 증명되며, 다른 모든 집중 유동성 베뉴와 같은 닫힌 형태로 가격이 매겨지고, 측정이 정확하지 않은 곳에서는 실패 시 닫힘의 거부와 함께 측정에 의해 수수료 관문을 거치며, 불변의 주소 산술로 적대적 훅이 선별되고, 싱글턴 자체의 락 프로토콜을 통해 정산되며, Router가 정지 상태로 단 한 wei도 보유하지 않으면서 네이티브 ETH로 도달 가능하다. 측정 결과는 뒤에 제시된다(§17): Arbitrum의 팩토리 센서스에서 V4 계열은 다른 어떤 등록 베뉴보다 많은 홉 쌍을 차지하며, Optimism에서 센서스는 우리가 아는 한 그 체인 최초의 V4 라우팅 애그리게이션을 기록했다. 우리는 큐레이션된 풀 목록 없이, 네이티브 ETH를 포함하여, 실행 트랜잭션 안에서 V4 풀을 전적으로 도출하고, 증명하고, 가격을 매기고, 선별하고, 정산하는 다른 공개 애그리게이터를 알지 못한다. 그런 것을 찾는다면 이 주장은 틀린 것이며, 우리는 그것을 알고 싶다.

## §6 Solidly 솔버

어떤 불변식은 안전하게 재현할 수 있을 만큼 단순한 닫힌 형태가 없으며, 그것을 재현하는 일은 바로 프로토콜이 다른 곳에서 거부하는 복제 위험이다. 그러한 베뉴에 대해 디스패처는 베뉴가 노출하는 가장 좁고 정직한 채널을 통해 풀에게 묻는다 — 그리고 응답하지 않는 포크를 위해 복제본을 예비로 둔다.

Solidly 계열 — Aerodrome, Velodrome 및 그 포크들 — 은 스테이블 쌍에서 상관된 자산을 다루며, 그 사차 불변식은 상수곱보다 훨씬 더 대각선에 밀착하면서도 여전히 이탈을 허용한다:

$$
k(x, y) \;=\; x^{3}y \;+\; x\,y^{3} \;=\; x\,y\left(x^{2}+y^{2}\right)
$$
^e5

균형점 근처에서 이 곡선은 입력의 거의 전부를 출력으로 전달한다 — 스테이블코인과 리퀴드 스테이킹 흐름이 몰리는 바로 그곳에서 슬리피지가 최소이다. 변동성 Solidly 쌍은 상수곱이며, Core의 변동성 분기는 *바로* `outV2`이다 — 단일 생산자이며, 변성 관계 MR7이 이를 고정한다(§15).

![FIG-3 — 균형 잡힌 스테이블 쌍에서 입력 대비 출력: Core 자체의 공식으로 그린 Solidly 사차식, 상수곱, 슬리피지 영의 대각선 비교.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-3.png)

**기본 경로: 풀에게 묻는다.** `getAmountOut(amountIn, tokenIn)`은 쌍 자체의 바이트코드 — 실시간 수수료, 곡선, 반올림 포함 — 로 계산되므로, 정확히 그 수치를 요청하는 스왑은 구성상 풀의 불변식 검사를 통과하며, 어떤 마진도 필요하지 않다. 대체 경로의 발동 조건은 최대 1 wei의 응답이며, 정확히 1을 응답한 풀이 한때 묻는 세 채널(Solver, Router, Quoter) 중 둘을 서로 다른 분기로 보낸 뒤로 세 채널 전반에 걸쳐 일치시켰다.

**대체 경로: 복제한 뒤 적게 요청한다.** 포크가 셀렉터를 노출하지 않으면, 디스패처는 쌍의 팩토리에서 실시간 수수료를 읽고(`getFee(pool, stable)`; 선언된 대체 수수료는 V2 분기와 같은 100 bps 상한 이내에서만 신뢰되며, 그렇지 않으면 30 bps), 소수점 정규화된 준비금에서 불변식을 풀고, 결과에 9,800 / 10,000을 곱한다 — 복제본이 관찰할 수 없는 풀의 반올림이 항상 여유를 갖도록 하는 200 bps 헤어컷이다. 헤어컷은 대체 경로에서만 적용되며, 풀이 스스로 응답한 경우에는 결코 적용되지 않는다.

**풀이.** 거래 후 입력 잔액 $x$와 거래 전 준비금에서 얻은 목표 불변식 $K$가 주어지면, Core는 $f'(y) = x(x^{2} + 3y^{2}) > 0$인 $f(y) = x^{3}y + xy^{3} - K$의 근을, 반대편 거래 전 준비금인 $y_{0} = Y$에서 시작하는 뉴턴 반복으로 찾는다(`_solY`):

$$
k(x, y_{n}) < K:\ \ y_{n+1} = y_{n} + \max\!\left(1,\ \left\lfloor \tfrac{K - k(x,y_{n})}{f'(y_{n})} \right\rfloor\right); \qquad
k(x, y_{n}) \ge K:\ \ y_{n+1} = y_{n} - \delta_{n},\ \ \delta_{n} = \begin{cases} \lfloor y_{n}/2 \rfloor & d_{n} \ge y_{n} \\ \max(1, d_{n}) & \text{otherwise} \end{cases},\ \ d_{n} = \left\lfloor \tfrac{k(x,y_{n}) - K}{f'(y_{n})} \right\rfloor
$$
^e6

자연스러워 보이는 시작값 — 상수합 근사 — 은 가격을 조용히 오염시키는 방식으로 틀렸다: 준비금의 몇 배에 이르는 입력을 흡수하는 풀에서는 $f$가 극도로 가파른 영역에서 근보다 훨씬 위에 떨어지고, 스텝이 영으로 내림되며, 솔버는 확신에 찬, 그럴듯한, 틀린 숫자에 고정된다. 반대편 준비금에서 시작하여 양방향으로 스텝을 밟으면 균형 잡힌 풀과 심하게 치우친 풀 모두에서 수렴한다. 영을 넘어설 하향 스텝은 반복값의 절반으로 대체된다. 모든 스텝은 최소 한 단위의 진전을 강제한다. 반복은 연속된 반복값의 차이가 최대 한 단위일 때 종료되며, 테이커에게 불리한 최종 한 단위 조정으로 불변식이 안전한 쪽에 안착한다. 루프는 64회 반복으로 상한이 정해지며, 상한은 실패 시 닫힘이다: 솔버는 시작값 자체를 반환하고, 호출자의 $y \ge Y$ 가드가 이를 영 견적으로 매핑한다 — 풀은 가격 산정 불가로 취급되며, 이는 Aerodrome 자체가 잘못 가격을 매기기보다 거부하는 방식과 일치한다. 사라지는 도함수도 같은 출구로 나간다.

**소수점 정규화.** 불변식은 사차 동차식이므로, 두 토큰의 소수 자릿수가 같으면 스케일이 상쇄되어 원시 준비금이 그대로 통한다. DOLA(소수 18자리) 대 USDC(6) 같은 쌍은 그 불일치를 사차식 안으로 끌고 들어가므로, Core는 풀기 전에 양쪽을 공통의 $10^{18}$ 기준으로 스케일하고 이후에 스케일을 되돌린다:

$$
X = r_{\mathrm{in}}\cdot 10^{\,18-d_{\mathrm{in}}},\quad Y = r_{\mathrm{out}}\cdot 10^{\,18-d_{\mathrm{out}}},\quad A = \left\lfloor\frac{x\cdot 10^{\,18-d_{\mathrm{in}}}\,(\mathrm{BPS}-f)}{\mathrm{BPS}}\right\rfloor,\quad \mathrm{out} = \left\lfloor\frac{Y - y^{\star}(X+A,\,K)}{10^{\,18-d_{\mathrm{out}}}}\right\rfloor
$$
^e7

소수 자릿수가 열여덟을 넘는다고 보고하는 토큰은 스케일링에서 언더플로하는 대신 영을 견적한다. 상대 토큰의 소수 자릿수는 호출자에게서 받지 않고 풀에서 읽는다 — 호출자의 좌표를 받아들이는 단일 생산자 기본 요소는 그것이 닫기 위해 존재하는 바로 그 불일치를 다시 열게 될 것이다.

**전체 정의역에서 실패 시 닫힘.** 네 개의 가드가 곡선을 평가할지 여부 자체를 결정하며, 각각은 revert하는 대신 영을 반환한다. 견적 안에서의 revert는 해당 쌍에 대한 Solver의 계획 전체를 되감는 라이브러리 `DELEGATECALL`이기 때문이다: $X$와 $Y$는 $3.4 \times 10^{38}$을 넘어서는 안 되며, $X + A$도 마찬가지이다. 그리고 `_solKFits`는 불변식 $k = xy(x^{2}+y^{2})/\mathrm{WAD}^{3}$과 도함수 $x(x^{2}+3y^{2})/\mathrm{WAD}^{2}$가 모두 시작값 — 뉴턴이 방문하는 가장 큰 점 — 에서 256비트 워드에 들어맞을 것을 요구한다. 이 마지막 검사는 나눗셈 없이 이루어진다 — 512비트 곱의 상위 워드를 계산하여 WAD 하나와 비교한다(`_mulFitsWad`) — 이전 형태인 $a \le \max \cdot \mathrm{WAD} / b$ 자체가 토큰을 한 단위 미만으로 보유한 풀에서 오버플로했기 때문이다. N-버전 레인(§15)이 2026-09-05에 그 탈출을 발견했다. 수정은 대체한 코드보다 35바이트 작으며, 레드 퍼스트로 반영되었고, 전체 정의역에 대한 5,000회 실행 퍼즈, Python으로 같은 산술을 60,000개 샘플에 걸쳐 훑은 검사, 그리고 가드 안의 세 개의 뮤턴트를 갖추었다.

**측정된 잔차와 200 bps 마진.** 복제본은 풀 자체의 `getAmountOut`을 기준 진실로 삼아 실제 Aerodrome 풀에 대해 측정되었다:

| 풀 | 상태 | 기준 진실 | Core 출력 | 편차 |
|---|---|---|---|---|
| USDC / USDbC | 균형 (소수 6 / 6자리) | 999,534,071 | 999,534,071 | 0.000 % |
| DOLA / USDC | 치우침 (소수 18 / 6자리) | 985,528,437 | 987,998,286 | 0.251 % |
| USDz / USDC | 치우침 (소수 18 / 6자리) | 899,325,141 | 901,565,830 | 0.249 % |

균형 잡힌 쌍은 정수 단위까지 일치한다. 치우친 쌍은 높은 쪽으로 0.5 % 이내에 있는데, 풀의 수수료 차감 후 잔액 반올림이 외부에서 보이지 않기 때문이다. 그 잔차가 대체 경로가 200 bps만큼 적게 요청하는 이유이다: 마진은 실행 시 쌍의 불변식 검사에 여유가 있음을 보장하여, 과대 견적으로 인한 revert 가능성을 확실한 체결로 바꾸며, 미리보기 표면과 프레임 내 표면에 동일하게 적용되어 둘이 보조를 맞추도록 한다. 정준 오라클 레인(§15)은 곡선을 독립적으로 한정한다: 베뉴의 명세로부터 작성된 구현과 비교하여, Core의 스테이블 견적은 5,000개의 퍼즈 입력에 걸쳐 4 wei 이내에 있다.

## §7 결정론적 도출 𝒟

풀을 견적하려면 먼저 찾아야 한다. 업계의 답은 묻는 것이다: 베뉴의 팩토리를 호출하면 스토리지 매핑을 읽어 주소를 반환하고, 호출자는 팩토리의 말을 받아들인다. BlazePhoenix는 그 답을 지원하며 — 네 가지 팩토리 호출 모드가 배포를 재현할 수 없는 베뉴를 담당한다 — 산술이 허용하는 곳에서는 더 강한 답을 선호한다: 외부 호출 없이 주소를 계산하는 것이다. 계산된 주소는 같은 세 가지 입력을 가진 누구나 검증할 수 있다. 가져온 주소는 신뢰할 수밖에 없다.

그 메커니즘은 CREATE2이다. EVM은 컨트랙트의 주소를 배포자, 솔트, init 코드 해시의 순수 함수로 만든다:

$$
\mathrm{pool} \;=\; \mathrm{addr}_{20}\!\left(\mathrm{keccak256}(\mathtt{0xff}\,\Vert\,\mathrm{origin}\,\Vert\,\mathrm{salt}\,\Vert\,\mathrm{initCodeHash})\right)
$$
^e8

여기서 $\mathrm{addr}_{20}$은 해시의 마지막 스무 바이트를 취한다. Core는 85바이트 원상을 정확히 한 곳에서만 풀어 쓰며(`create2Address`), `deriveAddress`는 모드에 따라 솔트 다항식과 오리진을 선택하고, Hub의 증명된 Algebra 도출도 같은 생산자를 재사용한다.

| 모드 | 계열 | 해석 방식 | 솔트 / 탐색 세부 |
|---|---|---|---|
| 0 | V2 | 팩토리 호출 `getPair(t0,t1)` — 셀렉터 `0xe6a43905` | — |
| 1 | V3 / Algebra | 팩토리 호출 `getPool(t0,t1,fee)` — `0x1698ee82`; 실패 시 `poolByPair(t0,t1)` — `0xd9a641e1` | — |
| 2 | Solidly | 팩토리 호출 `getPool(t0,t1,stable)` — `0x79bc57d5` | — |
| 3 | 집중 유동성 Solidly 포크 (Slipstream, Velodrome CL) | 팩토리 호출 `getPool(t0,t1,tickSpacing)` — `0x28af8d0b` | — |
| 4 | V2 CREATE2 | 순수 산술 | `keccak256(packed(t0,t1))`, 오리진 = 팩토리 |
| 5 | V3 CREATE2 | 순수 산술 | `keccak256(encode(t0,t1,fee))`; `fee == 0`은 Algebra 센티널 → `keccak256(encode(t0,t1))`, 오리진 = 팩토리의 `poolDeployer()` (`0x3119049a`), 등록 시 증명됨 |
| 6 | Solidly 클론 CREATE2 | 순수 산술 | `keccak256(packed(t0,t1,stable))`, 오리진 = 팩토리 |
| 7 | CL CREATE2 | 순수 산술 | `keccak256(encode(t0,t1,tickSpacing))`, 오리진 = 팩토리 |
| 8 | *폐기됨* | Curve 제거로 비워짐; 묘비 — `MODES_VALID = 0x2FF`가 이를 거부한다 | — |
| 9 | V4 도출 스캔 | 훅 없는 poolId를 도출하고 PoolManager에서 생존을 증명한다 (§5.1) | 그리드 ≤ 99 등급, 콜드 탐색 ≤ 40, 8개 풀에서 중단 |

모드 0–3은 각각 한 번의 `staticcall`이다. 모드 4–7은 호출이 전혀 없으며 올바른 init 코드 해시만 필요하다. 모드 9는 어느 방식으로도 풀을 찾을 수 없는 유일한 계열을 담당한다.

**정리, 주장, 그리고 증명.** 도출된 주소는 쌍에 대한 정리이다: 오리진, 솔트 다항식, init 코드 해시가 주어지면 주소는 keccak만으로 따라 나오며, 이의를 제기하는 누구나 다시 계산할 수 있다. 질의된 주소는 팩토리가 자기 스토리지에 대해 하는 주장이다 — 그리고 팩토리는 다른 토큰의 풀로 응답할 수 있다. 2026-09 이후, 질의된 풀(모드 0–3)은 발견 목록에 오르기 전에 자신의 쌍을 증명한다: Hub는 그 풀의 `token0()`과 `token1()`을 읽으며 — 지불이 이루어지는 이음매에서 Router가 하는 것과 같은 두 번의 읽기이다 — 불일치하면 후보를 버리므로, 실행기가 거부할 풀은 결코 목록에 오르거나 계획되거나 순위가 매겨지지 않는다. 이를 바꾼 것은 적대적 베뉴 매트릭스(§15)이다: 그 첫 실행에서 큐레이터가 등록한 팩토리가 응답한 풀이 목록에 올랐고, 플래너가 그것의 순위를 매겼으며, 실행기가 지불이 이루어지는 이음매에서 그것을 거부했다 — 자금은 결코 위험에 처하지 않았지만, 사칭자가 분할을 차지하는 동안 그 쌍은 거부되었다.

**V3 형태 행의 두 가지 미묘한 점.** Algebra(Camelot 계열)는 동적 수수료를 부과하므로 수수료는 결코 그 정체성의 일부가 될 수 없다: Algebra 등록은 수수료 영을 센티널로 선언하고, 모드 5는 솔트에서 수수료를 빼며, CREATE2 오리진은 Algebra 팩토리가 노출하는 별도의 `poolDeployer` 컨트랙트이다. 그 오리진은 팩토리가 등록 이후에도 여전히 조종할 수 있는 유일한 도출 입력이다 — 프록시는 codehash를 그대로 둔 채 답을 바꾼다 — 그래서 Hub는 `addFactory` 시점에 이를 한 번 증명하고(`factoryDeployer`), 이후에는 증명된 오리진으로부터 도출한다. 통제권이 포기된 뒤에는 그 증명이 동결된다. 팩토리 호출 쪽에서는 Algebra 팩토리가 `getPool`이 아니라 `poolByPair`를 노출하므로, 아무것도 반환하지 않는 모드 1 조회는 포기하기 전에 그 셀렉터로 재시도한다.

**모든 도출은 하나의 최종 관문을 통과한다.** Hub는 런타임 바이트코드가 없는 도출 주소를 모두 버린다 — `extcodesize`가 검사의 전부이다(`hasCode`) — 따라서 잘못된 init 코드 해시, 낡은 등록, 혹은 어떤 계열이 배포된 적 없는 체인은 유령 베뉴가 아니라 아무것도 만들어 내지 않는다. 주소에 있는 바이트코드는 존재를 증명할 뿐 정직함을 증명하지 않으며, 이 때문에 찾아낸 풀과 체결된 거래 사이에는 여전히 등록, 신뢰 대역, 하한이 버티고 있다. 여러 `(fee, spacing)` 조합으로 도출된 같은 풀은 한 번만 목록에 오른다.

**도출과 질의의 경계는 경험적이다.** Velodrome과 Aerodrome 같은 클론 기반 베뉴는 init 코드 해시가 알려져 있음에도 팩토리 호출에 머문다. 그들의 CREATE2 오리진이 팩토리가 아니라 내부의 결정론적 클론 배포자 안에 있기 때문이다 — 주소를 공개 입력으로부터 재현할 수 없으므로, 프로토콜은 묻는다. Uniswap V3는 반대 방향의 대표 사례이다: 같은 풀 바이트코드가 모든 곳에 배포되므로 init 코드 해시는 모든 체인에서 하나의 상수이며, 동일성 `derived == getPool`은 Ethereum, Arbitrum, Optimism, Base의 포크에서 각 체인 자체의 팩토리에 대해 성립한다(§17). 산술이 건전한 곳에서는 도출하고, 그렇지 않은 곳에서는 묻고, 어느 쪽이든 모든 것을 검증한다.

**발견은 표를 행 단위로 소비한다.** 등록된 최대 `MAX_FACTORIES = 16`개의 팩토리 각각에 대해 Hub는 선언된 모든 `(fee, spacing)` 조합을 탐색한다 — Solidly 행은 스테이블과 변동성 두 가지 형태로, V4 행은 최대 여덟 개를 예약하여 — 따라서 스캔의 최악의 경우는 체인의 기분이 아니라 등록에 의해 고정된다. 설정 표면은 문에서 지켜진다(§9): 구조적으로 불가능한 베뉴는 저장될 수 없다.

## §8 모노슬롯

스토리지는 EVM에서 무엇을 하든 지배적인 비용이다: 콜드 `SLOAD`는 2,100 가스이며, 풀의 속성을 별도의 슬롯에 흩어 놓는 레지스트리는 평가되는 모든 경로에서, 모든 풀에 대해, 모든 속성마다 그 비용을 다시 치르게 된다. BlazePhoenix는 풀의 라우팅 상태 전체를 하나의 256비트 워드 — 모노슬롯 — 에 패킹하며, 이를 한 번 읽고 순수 산술로 디코딩한다.

| 비트 | 필드 | 폭 | 의미 |
|---|---|---|---|
| 0 | `active` | 1 | 슬롯이 활성 상태임 |
| 1–7 | 예약됨 | 7 | 비트 5 = 스테이블 곡선 (등록 시 풀 자체의 `stable()`에서 읽음); 비트 6 = 네이티브 쪽 교환됨; 비트 7은 이전에 브리지 플래그였으나 더 이상 기록되지 않음 — 브리지 보너스는 쌍에서 실시간으로 읽음 (§9) |
| 8–31 | `fee` | 24 | 베뉴 고유 단위의 수수료 등급 |
| 32–39 | `kind` | 8 | 베뉴 종류 (§4) |
| 40–47 | `tier` | 8 | 0 = 큐레이션 (운영자가 시드), 2 = 무허가 (거래로 학습되거나 청구됨) |
| 48–59 | `conc` | 12 | 집중도 필드, 패킹 전에 `0xFFF`로 마스킹됨 |
| 60–63 | `bucket` | 4 | 로그 깊이 버킷 |
| 64–95 | `lastUpdateTs` | 32 | 마지막 접촉의 실제 시각 타임스탬프 |
| 96–127 | `emaIn` | 32 | 입력 거래량 EMA용으로 예약됨; 등록 시 영으로 기록되며, 배포된 틱에 의해 갱신되지 않음 |
| 128–159 | `emaOut` | 32 | 출력 거래량 EMA용으로 예약됨; 위와 같음 |
| 160–191 | `swapCount` | 32 | 활동 횟수, $2^{32}-1$에서 포화 |
| 192–223 | `regBlk` | 32 | 등록 블록 |
| 224–255 | `lastBlk` | 32 | 마지막 접촉 블록 |

![FIG-4 — 모노슬롯 비트 레이아웃: 256비트 워드 하나, 이름 붙은 열네 개의 필드, 깊이 버킷으로 결코 번질 수 없도록 열두 비트로 마스킹된 집중도 필드.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-4.png)

인코딩은 시프트와 OR의 연속이다(`encodeSlot`):

$$
s \;=\; a \;\vert\; f\!\ll\!8 \;\vert\; k\!\ll\!32 \;\vert\; r\!\ll\!40 \;\vert\; (c \wedge \mathtt{0xFFF})\!\ll\!48 \;\vert\; b\!\ll\!60 \;\vert\; t\!\ll\!64 \;\vert\; e_{\mathrm{in}}\!\ll\!96 \;\vert\; e_{\mathrm{out}}\!\ll\!128 \;\vert\; n\!\ll\!160 \;\vert\; B_{\mathrm{reg}}\!\ll\!192 \;\vert\; B_{\mathrm{last}}\!\ll\!224
$$
^e9

한 가지 세부 사항은 편의가 아니라 가드이다: 집중도 필드는 시프트하기 *전에* 열두 비트로 마스킹되므로, 비트 [63:60]의 깊이 버킷으로 결코 번질 수 없다 — 패킹은 호출자가 범위 안에 머물기를 신뢰하는 대신, 패킹된 상태를 오염시키는 조용한 필드 중첩이라는 부류 자체를 금지한다. 디코딩도 마찬가지로 기계적이다: 각 접근자는 시프트 한 번과 절단 한 번이다. 버킷 기록자는 네 비트를 설정하기 전에 먼저 지운다. 스왑 횟수는 순환하지 않고 포화하므로, 가득 찬 슬롯이 바쁜 풀의 이력을 영으로 되돌리는 일은 결코 없다. 모든 틱과 등록 시 Hub가 찍는 실제 시각 타임스탬프는 활력 감쇠(§9)와 Solver의 발견 신선도 관문을 모두 구동한다.

이 워드가 담지 않는 것에 주목하라: 가격이다. 모노슬롯은 풀의 순위를 매기는 데 필요한 모든 것 — 활동, 깊이 등급, 수수료, 종류, 플래그, 최신성 — 을 담지만, 견적으로 오인될 수 있는 것은 아무것도 담지 않으므로, 레지스트리의 기억은 구조적으로 거래의 가격을 매길 수 없다. 가격은 그것으로 거래하는 프레임 안에서 실시간 준비금으로부터 새로 계산된다. 패킹된 워드는 어떤 풀에게 물어볼 가치가 있는지만을 결정할 뿐이다. 갱신 경로도 같은 절약을 따른다: 알려진 풀을 통한 스왑은 워드를 한 번 다시 쓴다 — 횟수, 버킷, 타임스탬프, 한 번의 `SLOAD`에 대한 한 번의 `SSTORE` — 따라서 레지스트리를 최신으로 유지하는 한계 비용은 사용자들이 어차피 보내고 있던 트랜잭션 안에 실려 간다.

하나의 워드가 왜 중요한지는 곱셈의 문제이다. Solver는 최대 세 가지 토폴로지에 걸쳐 쌍당 최대 `MAX_CANDIDATES = 8`개의 베뉴를 탐색하므로, 하나의 거래를 풀 때 토큰을 움직이기 전에 수십 개의 풀에 점수를 매길 수 있다. 콜드 슬롯당 2,100 가스에서, 풀당 다섯 슬롯 레이아웃은 그 점수 매기기 과정을 수십만 가스로 만든다. 모노슬롯은 이를 풀당 한 번의 읽기로 만들며, Hub의 `psisOf`는 모든 후보의 점수를 한 번의 일괄 호출로 반환한다. 모노슬롯은 온체인 라우팅에 적용된 최적화가 아니다. 그것은 온체인 라우팅이 애초에 감당 가능한 이유이다.

## §9 자가 치유 레지스트리와 활력장 Ψ

대부분의 애그리게이터는 키퍼가 오프체인에서 풀을 색인하여 그 목록을 온체인으로 밀어 넣기 때문에 자신의 베뉴를 안다 — 이는 신뢰된 프로세스, 신선도 지연 구간, 운영상의 의존성이라는 세 가지를 뜻하며, 경로를 온체인에서 푸는 프로토콜은 이 중 어느 것도 받아들일 수 없다. BlazePhoenix는 정반대의 입장을 취한다. 레지스트리는 유동성을 거래함으로써 유동성을 학습한다.

### §9.1 거래를 통한 학습과 페어 증명

실행된 모든 레그에서 Router는 Hub의 `recordSwap`(`onlyRouter`, `whenLive`)을 호출하여 풀, 그 종류(kind), 수수료, 페어, 금액, 그리고 자신이 측정한 깊이를 전달한다. Hub는 먼저 Router가 실행할 수 없었을 종류를 모두 거부하고(`KINDS_EXECUTABLE`) — 이는 Router가 배포 당시의 모습 그대로 남아 있는지에 의존하지 않는 국소적 방어이다 — 이어서 두 경로 중 하나를 택한다.

이미 알려진 풀의 모노슬롯은 갱신된다. 스왑 횟수는 *현재 감쇠된* 기준값에서 증가하고, 깊이 버킷이 재계산되며, 블록과 벽시계 타임스탬프가 전진한다 — 패킹된 워드 하나가 다시 쓰일 뿐 외부 호출은 없다. 알려지지 않은 풀은 콜드 경로를 탄다. 이 풀은 먼저 입장 심사(`_canInsert`, 아래 참조)를 통과해야 한다. 그다음 자신이 저장하려는 주장을 증명해야 한다. 페어 형태의 모든 종류(`KINDS_PAIR_PROOF` = V2, V3, Solidly, Algebra)에 대해 Hub는 풀 자신의 `token0()`과 `token1()`을 읽고 이것이 페어와 일치할 것을 요구한다 — 콜드 경로에서만, 최대 두 번의 `staticcall`로. `recordSwap`의 모든 인자 — 풀, 종류, 깊이 — 는 Router의 경로에 실려 오는, 호출자가 통제하는 calldata이므로, 이 증명이 없다면 공격자는 자신이 작성한 컨트랙트를 자신이 고른 페어 아래, 자신이 고른 깊이로, 두 토큰 중 어느 것도 보유하지 않은 채 등록할 수 있다. `pool` 필드가 바이트코드 없는 절단된 id인 두 V4 종류의 경우 증명은 재도출이다. 틱 간격을 복원하고 id를 재계산하여 대조하며, 훅 없는 풀만 허용하므로 훅이 달린 풀은 결코 이 문으로 등록될 수 없다. 네이티브 V4 풀은 `(address(0), T)`의 두 방향 모두로 대조된다. 불일치 시에는 등록을 *건너뛸* 뿐 결코 되돌리지(revert) 않는다. 사용자의 스왑은 이미 정산되었으며 레지스트리의 결정 때문에 실패해서는 결코 안 되기 때문이다.

등록 시점의 페어 증명은 2026년 구월에 새로 도입되었으며, 그것을 낳은 측정은 §15 절의 측정이다. 열다섯 개의 소스 변이를 모든 상태 기반 불변식에 대해 실행했는데, 이 증명을 제거한 뮤턴트는 첫 실행에서 모든 캠페인을 살아남았다. 어떤 테스트도 Hub에 다른 토큰을 거래하는 풀을 제시한 적이 없었기 때문이다. 이제 Hub 캠페인은 매 실행마다 그렇게 한다 — 페어를 뒤집은 경우, 토큰 하나만 공유하는 경우, 두 토큰 모두 외래인 경우 — 그리고 `invariant_ActiveEntriesTradeThePair`는 모든 활성 항목의 `token0`/`token1`을 읽는다. 해당 뮤턴트는 이 불변식에 의해 죽는 보호 항목이 되었다. 탐색(discovery)도 질의된(asked) 풀에 대해 같은 증명을 수행한다(§7).

운영해야 할 인덱서도 없고, 서버가 다운되어 프로토콜이 눈을 감게 되는 순간도 없다. 운영자용 `seedPool` 문은 배포 시점에 페어를 미리 채우며(티어 0, 큐레이션됨), 무허가 `claimV4` 문은 훅 없는 V4 풀을 그 자체의 증명 아래 받아들인다(§5.1). 둘 다 최적화일 뿐 의존성이 아니다 — 빈 레지스트리는 평범한 거래를 통해 채워진 레지스트리로 스스로 치유된다.

### §9.2 점수

활력장 Ψ는 품질과 용량을 분리하며, 모노슬롯과 페어의 실시간 브리지 상태만을 읽는다(Core `psi`):

$$
\Psi(s, t) \;=\; V(s, t)\cdot 2^{\,b(s)} \cdot \left(1 + \tfrac{2{,}500}{10^{4}}\,\mathbf{1}_{\mathrm{bridge}}\right)\cdot\left(1 + \tfrac{500}{10^{4}}\,\mathbf{1}_{\mathrm{conc}}\right)
$$
^e10

깊이 버킷 $b = \min(15, \lfloor \log_{10}(d / 10^{15}) \rfloor)$은 가중치 $2^{b}$를 통해 반영된다. 깊이가 열 배 늘 때마다 가중치가 두 배가 되므로 깊이는 준선형으로 반영된다 — 깊은 풀은 확실히 우대되지만, 그저 건전한 풀이 거대한 풀에 의해 지워지지는 않는다. 브리지 페어 위의 풀은 +25 %를 얻는다. 브리지는 유동성 그래프의 결합 조직이기 때문이다. 집중 유동성 풀은 추가로 +5 %를 얻는다. 브리지 보너스는 등록 시점에 고정된 비트가 아니라 단일 생산자(`_pairBridged`)가 페어로부터 실시간으로 읽으며, 한 페어 안에서는 균일하므로 같은 페어의 풀들 사이의 순서를 결코 바꿀 수 없다.

**감쇠 — 단 하나의 메커니즘.** 활동량은 유휴 상태에 따라 단일한 벽시계 규칙(`_decayedSwapCount`)으로 감쇠한다:

$$
V(s,t) \;=\; \mathrm{swapCount}(s) \;\gg\; \left\lfloor \frac{t - t_{\mathrm{last}}}{24{,}576\ \mathrm{s}} \right\rfloor, \qquad V = 0 \ \text{once the shift exceeds } 31.
$$
^e11

단계는 `VITALITY_DECAY_STEP_SECONDS = 24,576` — 약 6.8시간 — 이며, 서른두 번의 반감은 어떤 점수든 소멸시키므로, 침묵하는 풀은 786,432 s, 약 9.1일 후에 영점과 완전한 퇴출 가능 상태에 도달한다. 단 한 번의 스왑이 풀을 되살리며, 그 부활은 정직하다. 횟수는 감쇠 이전의 총계가 아니라 감쇠가 남겨 둔 값에서 재개되므로, 오랜 침묵 뒤의 먼지(dust) 스왑 한 번으로는 풀이 누적한 순위를 복원할 수 없다. 점수 산정기는 두 종류의 영을 구분한다. *죽은* 슬롯 — 비어 있거나, 한 번도 갱신되지 않았거나, 지평선을 넘어선 슬롯 — 은 진짜 영점을 받아 순위에서 빠지는 반면, 작은 횟수가 시프트에 의해 반올림되어 사라졌을 뿐인 *살아 있는* 슬롯은 하한인 하나로 끌어올려지므로, 최근에 접촉된 실제 풀이 반올림 오차 때문에 사라지는 일은 결코 없다. 이 창이 벽시계 초 단위인 것은 의도적이다. 블록 수로 센 창은 체인마다 다른 실제 기간을 의미하며, 16블록마다 시프트하던 이전 구현은 문서화된 것보다 약 128× 빠르게 영으로 감쇠했다.

![FIG-5 — 활력 감쇠: 횟수 4,096은 24,576 s마다 절반이 되어 32단계 후 영에 도달한다. 4일째의 스왑은 역사적 총계가 아니라 감쇠된 기준값에서 되살아난다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-5.png)

### §9.3 퇴출

각 페어는 최대 `MAX_SLOTS = 16`개의 풀을 보유한다. 열일곱 번째 풀이 들어오려 하면, Hub는 모든 기존 풀을 완전한 Ψ로 — 깊이와 보너스까지 모두 포함하여, 따라서 깊은 신규 풀이 얕지만 활발한 점유자를 밀어낼 수 있도록 — 점수화하고, 신규 풀의 예상 점수가 가장 약한 기존 풀을 일정 마진 이상 넘어설 때에만 신규 풀을 받아들인다(`_canInsert`):

$$
\Psi_{\mathrm{new}} \;>\; \Psi_{\mathrm{worst}} + \left\lfloor \Psi_{\mathrm{worst}}/4 \right\rfloor, \qquad \Psi_{\mathrm{new}} = 2^{\,b(d_{\mathrm{new}})}
$$
^e13

이때 신규 풀은 보수적으로 점수화된다 — 활력은 하나, 자신의 깊이 버킷, 보너스 없음 — 그리고 깊이가 영인 신규 풀은 즉시 거부된다. 이 마진은 괴롭힘(griefing) 벡터를 막는다. 단순히 최하위를 밀어내는 규칙 아래에서는 공격자가 겨우 활동하는 얕은 슬롯 열여섯 개를 붙잡아 깊은 경쟁자를 막아 둘 수 있지만, 마진과 함께 Ψ로 순위를 매기면 진정으로 더 깊은 풀이 여전히 이긴다. 이 상수의 한계는 그것이 강제되는 곳에 명시되어 있다. 신규 풀의 가중치는 이의 거듭제곱인 반면 기존 풀의 점수는 연속적이므로, 실효 마진은 약 100 %(점수가 하나를 갓 넘는 기존 풀)에서 약 25 %(점수 51인 기존 풀, 허용 가능한 최소 신규 풀 64)까지 이른다. 규칙의 25 %는 동작의 하한이기도 하며, 결코 그 아래로 내려가지 않는다. 거래가 활발한 기존 풀은 감쇠가 제 역할을 할 때까지 밀어내기 어렵다 — 이틀간의 유휴는 일곱 번의 반감을 치르게 한다. 무허가 V4 청구도 풀의 측정된 유동성을 깊이로 삼아 같은 마진을 통과해야 한다.

### §9.4 탐색과 그 가스 조절 손잡이

탐색은 공개 view이다. 어떤 페어에 대해서든 Hub는 자신의 팩토리 목록을 순회하며 §7 절의 모드로 후보를 도출하고, `hasCode`와 페어 증명을 적용하며, Solver는 그 결과를 등록된 집합과 주소 기준으로 중복 제거하여 병합하므로 어느 출처도 다른 출처를 억누를 수 없다. 가스를 제한하기 위해 Solver는 신선도 게이트(`_registryFresh`)를 적용한다. 해당 페어에 대해 *큐레이션된* 등록 베뉴 중 최소 `MIN_FRESH_VENUES = 3`개가 `DISCOVERY_TTL_SECONDS = 3,600` 이내에 접촉되었다면, 레지스트리를 신뢰하고 도출 탐색을 건너뛴다. 오직 큐레이션된 행(티어 0)만이 보증할 수 있다. 무허가 행은 먼저 스왑한 누구든 기록하는 것이며, 이를 셈에 넣었을 때에는 자가 등록된 먼지 페어 세 개가 시간당 세 번의 먼지 스왑으로 신선하게 유지되면서 한 페어의 탐색을 꺼 버리고 한 번도 등록되지 않은 모든 정직한 베뉴를 숨길 수 있었다. 무허가 행은 여전히 후보로 남지만 더 이상 경로를 선택하지 않는다. 이 게이트는 가스와 커버리지를 위한 조절 손잡이일 뿐 결코 안전 매개변수가 아니다 — Iron-Law 하한과 사용자의 최소 출력은 레지스트리가 얼마나 낡았든 모든 체결을 보호한다. 둘 다 레지스트리를 전혀 읽지 않기 때문이다.

### §9.5 구성 표면

Hub의 등록 문은 들어오는 길목에서 지켜진다. `addFactory`는 어떤 상태가 쓰이기도 전에(`HubE(5)`) 다음을 거부한다: `KINDS_ROUTABLE` 밖의 종류; `MODES_VALID` 밖의 모드; init-code 해시가 영인 CREATE2 모드; V2 종류 이외의 것에 묶인 V2-salt 슬롯; Solidly가 아닌 종류에 묶인 clone 슬롯; V3 또는 Algebra 이외의 것에 묶인 V3-salt 슬롯(Algebra 등록은 모든 수수료를 영 센티널로 선언해야 한다); 그리고 V4 이외의 종류에 묶이거나 수수료 목록과 간격 목록의 짝이 맞지 않는 V4 derive-scan 행. 이미 알려진 팩토리는 제자리에서 갱신될 뿐 두 번째 행을 받지 않으므로, 한 주소가 열여섯 개의 자리를 고갈시킬 수 없다(`HubE(4)`). 통제권이 포기된 후에는 런타임 코드가 바뀐 팩토리를 다시 활성화할 수 없으며, derive 행을 ask 행으로 변환할 수도 없다. `allowHook`은 입장 시점에 훅의 codehash를 고정하며, 훅은 허용 목록에 있고 *동시에* 변경되지 않은 동안에만 라우팅 가능하다(`isHookLive`). 포기 이후에는 목록 제외 기능이 사라지고, 바뀐 훅은 영원히 일시 중지 상태로 남는다. 브리지는 최대 `MAX_BRIDGES = 3`개이다. `addBridge`는 포기 이후에도 살아남는 큐레이터 권한이고, `removeBridge`는 포기와 함께 사라지는 통제 권한이다.

### §9.6 구조적 경계

한 문장이 위의 모든 것을 한정한다: **Ψ는 베뉴를 숨길 수는 있어도, 결코 잘못된 가격을 매길 수는 없다.** 점수는 후보 집합의 순위를 매기고 잘라낼 뿐이다 — 어떤 견적도, 어떤 하한도, 어떤 할당도 그것을 읽지 않는다. 그것들은 트랜잭션 내 측정으로부터 계산되기 때문이다. 기억은 오직 누락을 통해서만 경로 선택에 닿는다. 레지스트리가 무엇을 믿든 그것이 드러낼 수 있는 최악은 더 나쁘지만 실재하는 실시간 가격이며, 그러한 가격이 통과시킬 수 있는 가치는 신뢰 대역, 프레임 안에서 재도출되는 Iron-Law 하한, 사용자가 설정한 최소 출력 중 가장 엄격한 것에 의해 제한된다. 원금은 구조상 손이 닿지 않는다. 레지스트리는 무엇이 보이는지를 결정할 뿐, 무엇이 지불되는지에 대해서는 아무런 표결권이 없다.

## §10 라우팅과 분할 할당기

상수곱 풀은 하나의 규칙으로 가격을 매긴다. 두 준비금을 곱한 값은 결코 줄어들어서는 안 된다. 수수료 보유율 $\gamma$로 $\Delta$를 투입하면(0.30 % 베뉴 수수료는 $\gamma = 0.997$을 뜻한다) $y\,\gamma\Delta / (x + \gamma\Delta)$를 받게 되며, 분모에 자신의 입력이 들어 있다. 한 풀에 더 많이 밀어 넣을수록 추가 토큰 하나하나가 받는 대가는 더 나빠진다. 슬리피지는 산술이며, 그 치유책도 마찬가지이다. 같은 주문을 같은 페어의 여러 풀에 나누면 각 풀이 덜 움직인다.

**풀이 예제 2 — 두 풀에 걸친 분할.** 같은 페어의 두 풀, 둘 다 0.30 %인 풀에 대해 100 규모의 주문 하나를 낸다. 풀 A는 양쪽에 각각 1,000을, 풀 B는 양쪽에 각각 4,000을 보유한다.

| 경로 | 산술 | 받는 양 |
|---|---|---|
| 100 전부를 A로 | $1{,}000 \times 99.7 / 1{,}099.7$ | 90.661 |
| 100 전부를 B로 | $4{,}000 \times 99.7 / 4{,}099.7$ | 97.275 |
| A에 20, B에 80 | $1{,}000 \times 19.94 / 1{,}019.94 \;+\; 4{,}000 \times 79.76 / 4{,}079.76$ | $19.550 + 78.201 = 97.751$ |

20 / 80 분할은 추측이 아니라 할당기 자신의 선택이다. 가중치는 가장 깊은 후보 대비 측정된 깊이에 비례한다 — A의 가중치는 10,000 중 $1{,}000/4{,}000 = 2{,}500$, B의 가중치는 10,000 — 따라서 A는 주문의 $2{,}500 / 12{,}500 = 20$ %를 가져간다. 분할은 최선의 단일 풀보다 0.476 토큰, 즉 그 값의 48.9 bps만큼 낫다. 이어서 분할 게이트는 그 이득이 두 번째 레그의 값어치를 하는지 묻는다. 임계값은 $97.275 \times (1 + 25 / 10^{6}) = 97.278$이고 97.751은 이를 넘으므로 분할이 유지된다. 모든 행은 계산기로 확인할 수 있으며, 그 효과는 누적된다. 가격 영향은 각 풀이 *자기* 몫에 대해 보이는 반응이므로, 경로에 허용 가능한 레그가 하나 추가될 때마다 모든 풀이 덜 움직인다.

![FIG-6 — 두 풀 예제에서 깊은 풀로 보내는 비중에 따른 출력: 최댓값이 80 % 근처에 있는 오목 곡선이며, 할당기의 깊이 비례 지점이 표시되어 있다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-6.png)

### §10.1 경로 형태와 예산

Solver는 세 가지 토폴로지를 구성하고 최대화하는 것을 반환한다(`findBestRoutePlan`, 차순위는 대체 경로로 함께 반환): 한 홉의 *직접* 경로, 중간 토큰 하나를 거치는 *브리지* 경로, 두 개를 거치는 *이중 브리지* 경로 — 세 홉으로, Solver가 구성하는 가장 깊은 형태이자 Router가 받아들이는 최대치이다(`MAX_HOPS = 3`). 모든 홉은 의도적으로 분리된 두 예산 아래 병렬 레그로 분할된다. `MAX_LEGS_PER_STAGE = 4`는 *한 홉에 몇 개의 레그가 들어가는가*에 답하고, `MAX_LEGS = 11`은 *한 경로에 몇 개가 들어가는가*에 답하며, 둘이 합쳐 4, 4 + 4, 4 + 4 + 3을 낳는다 — 세 홉 경로의 마지막 홉이 세 개를 받는 것은 전역 상한이 그것을 압박하기 때문이지, 누군가 특수한 경우를 작성했기 때문이 아니다. 브리지는 Hub에서 운영자가 구성한 최대 세 개의 토큰 집합에서 나온다. Solver는 구성된 모든 브리지를 전개하고 거래마다 측정된 `totalOut`이 결정하게 한다. 측정된 영향이 `MAX_ROUTE_IMPACT_BPS = 9,000`을 초과하는 경로는 아예 반환되지 않으며, 허용 가능한 경로가 없는 페어는 경로를 지어내는 대신 `SolverE(5)`로 거부한다. Router는 호출자가 제공한 모든 경로에 대해 홉당 다섯 레그라는 자체 상한을 강제한다(`MAX_LEGS_PER_HOP = 5`, `RouterE(3)`).

### §10.2 깔때기

각 페어에 대해 Solver는 등록된 풀과, 레지스트리가 신선하지 않은 한(§9.4) 탐색된 풀을 모은다. 델타를 변경하는 훅을 가진 풀은 모두 제거하고(사영자 Ξ로, 계획된 문이 Router가 거부할 경로를 결코 조립할 수 없도록 여기서 적용된다), 합집합을 한 번의 일괄 호출로 Ψ에 따라 순위를 매겨 `MAX_CANDIDATES = 8`개를 유지한다 — 이는 의도적으로 어떤 홉의 레그 예산보다도 넓어서, 여러 얕은 베뉴 뒤에 나열된 깊은 풀도 보이고 가중되며, 목록 순서 때문에 굶주리는 대신 그것들을 밀어낼 수 있다. Ψ가 정확히 동점이면 더 낮은 수수료 쪽으로 결정되므로, 콜드 레지스트리에서 깔때기는 이력이 아니라 가격의 함수이다.

이어서 각 후보는 주문의 백분의 일로 한 번 탐침되며(`amountIn / 100`, 또는 그것이 영으로 내림될 경우 주문 전체), 같은 호출에서 한계 환율과 깊이를 함께 포착한다. 한계 환율은 전체 규모 환율이 뒤섞는 두 신호를 깔끔하게 분리한다. 가격이 낡은 풀은 규모와 상관없이 한계 환율이 틀리지만, 작지만 건전한 풀은 한계 환율이 정확하고 단지 전체 규모 출력이 나쁠 뿐이다. 페어 형태의 종류에 대해서는 선언된 깊이가 풀이 물리적으로 보유한 출력 토큰의 양으로 상한이 정해지므로, 양쪽에 토큰 하나씩만 보유하면서 $3 \times 10^{30}$을 선언하는 합성 페어는 실제 보유량으로 평가된다.

### §10.3 신뢰 대역

할당 이전에 후보는 신뢰받아야 하며, 그 신뢰는 위조하는 데 비용이 드는 양에 고정되어야 한다. 대역의 중심은 살아남은 후보들의 한계 환율의 **깊이 가중 중앙값**이다(`_depthWeightedMedian`). 환율은 깊이와 함께 정렬되며, 중앙값은 *깊이 질량*의 절반을 지나는 지점의 환율이다. 그 붕괴점은 질량의 50 %이다 — 이를 움직이려면 공격자는 페어의 실제 유동성의 절반보다 더 깊은 유동성을 대야 한다 — 그리고 이것이 가중하는 깊이는 준비금 풀의 경우 `getReserves`, 집중 유동성 풀의 경우 활성 유동성, V4의 경우 측정된 유동성으로, 모두 풀에 대한 단순한 기부(donation)로는 움직이지 않는 양이다. 이 마지막 성질이 앵커가 가장 큰 실제 잔고가 아닌 이유이다. 풀로의 전송은 준비금이나 가격을 움직이지 않으면서 `balanceOf`를 부풀리고 기부자가 회수할 수 있으므로 위험에 노출된 자본이 아니다 — 한 외부 연구자가 이전의 잔고 앵커가 기부에 의해 장악되는 것을 시연했고, 앵커는 교체되었다. 한계 환율이 중심에서 `MEDIAN_FILTER_BPS = 500`(±5 %)을 넘게 벗어난 후보는 무엇이든 전송되기 전에 제외된다. 후보가 하나뿐인 집합은 전혀 필터링되지 않으며, 중심이 영으로 반올림되는 집합은 단순 중앙값으로 대체되어, 다른 면에서는 라우팅 가능한 홉이 반올림 오차로 죽지 않도록 한다. 폭은 보정된 선택이다. 유동 스테이킹 페어를 견적하는 Solidly 안정 곡선은 대개 실제 환율에서 20–40 % 떨어져 있어 배제되며, 이번 거래 규모에 대한 판독값이 중심과 약간 다른 건전한 풀은 받아들여진다. §15 절의 변성 레인은 대역을 외부에서 측정했다. 한 풀이 형제 풀과 $10^{10}$만큼 떨어진 가격을 가질 때, 계획은 이상치 단독보다 35 % 덜 지불한다 — 중앙값이 그것을 믿기를 거부한 것이다.

### §10.4 할당기와 세 개의 가드

가중치는 측정된 깊이에 비례하며, 집합 내 가장 큰 깊이를 기준으로 정규화된다(`_weights`) — 정규화는 하나이고 모드는 없다. 모든 계열의 깊이가 토큰 단위로 표시되어(§4.2) 서로 비교 가능하기 때문이다. 비례 할당은 적대적 환경을 위한 견고한 선택이다. 반복도, 베뉴별 곡률 모델도 필요 없으며, 균등 분할이 낳는 파국적 실패를 피한다 — 측정된 WETH/USDC 사례에서 790-WETH 풀과 0.34-WETH 먼지 풀에 50 / 50으로 분할하면 1,302 USDC를 돌려준 반면, 깊은 풀에 집중하면 1,638을 돌려주었고, 깊이 가중 할당은 1,638을 돌려주었다. 깔때기에 홉의 예산보다 많은 생존 후보가 있을 때에는 *가중치 기준* 상위 예산 개수만큼의 생존 후보가 유지되므로, 늦게 나열된 깊은 풀이 일찍 나열된 얕은 풀을 밀어낸다. 그런 다음 세 개의 가드가 할당과 그것이 될 경로 사이에 선다.

- **용량 클램프.** 단일 틱 집중 유동성 공식은 현재 틱의 유동성이 모든 가격에 걸쳐 있는 것처럼 모델링하므로, 얕은 풀에서는 풀이 지금까지 보유했던 것보다 더 많은 것을 약속할 수 있다 — 관측된 한 사례에서는 117× 더 많이. 따라서 풀 형태 베뉴(종류 1과 6)의 집중 유동성 레그는 풀의 *측정된* 실제 출력 토큰 잔고의 `MAX_CONC_DRAIN_BPS = 3,000` — 30 % — 로 클램프된다. 클램프는 V4 레그에는 미치지 않는다. 싱글턴은 모든 풀의 토큰을 하나의 잔고로 보유하므로, Solver는 종류 4와 8에 대해 이 읽기를 건너뛰며, V4의 약속은 §11.3 절의 하한 외에는 아무런 물리적 한계를 갖지 않는다 — 하한이 곧 그 한계이다. 클램프는 두 단계로 되어 있다. 견적이 풀의 전체 보유량을 초과하면 그 약속은 물리적으로 불가능하므로, 클램프는 레그의 *확정 입력*을 같은 비율로 줄이고, 풀려난 입력을 나머지 레그로 연쇄시키며, 라우팅할 수 없는 나머지는 호출자에게 되돌려 보내도록 남긴다. 체결이 공격적이지만 가능한 경우에는 약속만 상한을 두는데, 그 경우 자본을 깎으면 잘 실행되는 풀에서 부분 체결을 강요한다는 것이 측정되었기 때문이다. 준비금으로 한정되는 공식은 과잉 약속을 할 수 없으므로 건드리지 않는다. 이 클램프는 대역과 달리 의도적으로 `balanceOf`를 읽는다. 대역은 기부가 왜곡할 수 있는 *상대적* 질문을 던지고, 클램프는 *물리적* 질문 — 이 풀이 이것을 지불할 수 있는가? — 을 던지며, 기부는 풀이 실제로 지불할 수 있는 양을 늘리기 때문이다.
- **분할 게이트.** 추가 레그 하나하나는 실제 가스를 소모하지만, 할당기는 의도적으로 가스 가격 항을 포함하지 않는다. 네이티브 토큰으로 표시된 가스를 거래 토큰으로 표시된 출력과 비교하려면 프로토콜이 갖기를 거부하는 바로 그 가격 오라클이 필요하기 때문이다. 오라클 없는 대리 지표는 임계값이다. 분할은 $\mathrm{out}_{\mathrm{split}} \ge \mathrm{out}_{\mathrm{single}} \cdot (1 + 25/10^{6})$ — `MIN_SPLIT_IMPROVEMENT_PPM = 25` — 일 때에만 유지되며, 그렇지 않으면 할당은 단일 레그로 축약된다. 임계값은 측정된 수치이다. 레그의 가스와 그 개선 사이의 실제 손익분기점은 Base에서 3 ppm으로 측정되었고(추가 레그당 calldata 320바이트, 바이트당 105.5 가스), 게이트는 그 약 여덟 배에 위치하며, 베이시스 포인트의 사분의 일은 베이시스 포인트로 존재하지 않으므로 백만분율로 표시된다. 그 이력 — 20 bps, 그다음 5, 그다음 0.25 — 은 측정에 대해 한 번도 보정된 적 없던 두 값의 기록이다. 게이트가 경로를 축약할 때에는 하나가 아니라 두 개의 단일 레그 후보를 전체 규모로 측정한다 — 가장 깊은 풀과 최선의 한계 환율을 가진 풀 — 한계 환율은 얕은 풀을 실제보다 좋아 보이게 하는 반면, 깊이는 전체 규모를 감당하는 능력의 대리 지표이기 때문이다. 더 나은 체결이 이긴다.
- **마진이 아닌 측정에 의한 보험.** 이전 설계는 레그 간 드리프트에 대한 보험으로, 계획 시점에 더 긴 경로의 모든 레그를 5 bps씩 깎았다. 그 후 드리프트를 같은 블록에서 아홉 개의 Base 페어에 대해 측정했는데, 최악의 경우가 −2 bps로 보험보다 한 자릿수 낮았으며, 그 보험은 모든 정직한 다중 레그 경로가 매 거래마다 지불하고 있었다 — 측정된 위험의 27×. 그 삭감은 폐지되었다. 실현 출력에 대해 강제되는 하한이 계획 비용 없이 그 위험을 떠맡는다. 측정은 평온한 시기에 이루어졌으며, 반론은 소스에 기록되어 있다. 보수성을 언젠가 다시 도입한다면 올바른 위치는 검사되는 하한이지, 순위만 매기는 추정치가 아니다.

조립된 경로는 Router가 재도출할 것들을 담는다. 각 레그의 확정 입력과 예상 출력, 경로의 비중 가중 영향, 그리고 Router가 강제하는 것과 같은 산술로부터 Solver가 증명(attest)하는 하한 Φ이다(§11). Solver는 또한 `THETA_GAS` 사다리로부터 종류별로 각 레그의 실행 비용을 매기고 그 합계를 `estGas`로 공개하는데, 이는 경로에 실려 있는 참고용 수치일 뿐 온체인의 어떤 것도 이를 읽지 않는다. 독자의 경험칙은 이 모든 기계 장치에도 변함없이 유효하다. 확인을 누르기 전에, 라우터가 주장하는 개선을 지갑이 보여 주는 가스와 비교하라.

## §11 실행 계층과 Iron-Law 하한 Φ

Solver는 제안하고, Router는 처분한다 — 원자적으로, 아니면 전혀 하지 않는다. Router는 자금을 움직이는 유일한 컨트랙트이며, 트랜잭션 사이에는 아무것도 보유하지 않고, 하나의 원칙 위에 구축된다. 관심사마다 하나의 메커니즘을 두고, 이를 보편적으로 적용한다. Router는 측정할 수 있는 것은 아무것도 가정하지 않는다.

### §11.1 네 개의 문, 하나의 실행기

| 문 | 시그니처(축약) | 인가 | 경로 |
|---|---|---|---|
| `swapExactIn` | `(route, amountIn, userMinOut, recipient, deadline)` | 고전적인 `approve` + `transferFrom` | calldata로부터 |
| `swapExactInWithPermit2` | `(route, amountIn, userMinOut, recipient, deadline, permit, signature)` | 정규 Permit2 컨트랙트를 통한 원스텝 Permit2 서명 전송; Router에 대한 상시 허용량 없음 | calldata로부터 |
| `swapExactInNative` | `(route, userMinOut, recipient, deadline)` payable | 네이티브 값, 문에서 한 번 래핑됨; `weth`가 연결될 때까지 실패 시 닫힘(`RouterE(3)`) | calldata로부터 |
| `swapBestExactIn` | `(tokenIn, tokenOut, amountIn, userMinOut, recipient, deadline)` | 고전적 방식 | Solver가 프레임 안에서 풀어낸 뒤(`findBestRoutePlan`), 지불자를 명시적으로 전달하는 자기 호출을 통해 실행 |

`swapBestExactIn`만이 트랜잭션 안에서 Solver를 호출한다. 나머지 세 문은 호출자가 Quoter를 통해 오프체인에서 무료로 계산한 경로를 받는다(§12). 네 문 모두 하나의 비공개 실행기(`_execute`)로 수렴하므로, 아래의 모든 검사는 모든 문에서 실행된다. EIP-7702 위임 계정은 컨트랙트와 똑같은 문을 호출하며, 이를 위한 별도의 진입점은 존재하지 않는다.

무엇이든 움직이기 전에 두 가지 거부가 실행된다. 영인 최소값은 모든 문에서 거부된다 — `RouterE(10)`, 예외 없음, 호환성 플래그 없음 — 최소값은 컨트랙트가 사용자를 대신해 계산할 수 없는 유일한 한계이며, 없을 수도 있는 가드는 가드가 아니기 때문이다. 그리고 세 홉을 넘거나, 한 홉에 다섯 레그를 넘거나, 입력 토큰이 이전 홉의 출력이 아닌 홉을 가진 경로는 `RouterE(3)`으로 거부된다. 홉 연속성이야말로 모든 중간 토큰의 스왑 전 "외래" 잔고를 알 수 있게 해 주는 것이며, 따라서 조작된 경로가 방치된 토큰을 입력으로 지명하여 Router가 보유한 그 토큰의 잔고 전체를 스왑에 끌어들일 수 없다.

### §11.2 첫 명령어부터의 측정

어느 문을 사용하든 Router가 가장 먼저 하는 일은 도착한 것을 측정하는 것이다. 명목 금액을 신뢰하는 대신 인출 후 자신의 잔고 변화량을 읽으므로, 전송 시 덜 전달되는 토큰은 실제 크기로 경로에 들어간다. 기준선은 한 번만 생성된다. 입력 토큰의 스왑 전 잔고(`baseIn`)와 출력 토큰의 스왑 전 잔고(`toutStart`)가 진입 시점에 측정되고, 이후의 모든 측정은 이에 대한 변화량이므로, 이미 컨트랙트에 놓여 있던 토큰 — 먼지, 기부, 무관한 이력에서 방치된 것들 — 은 스왑이 지급할 수 있는 대상에서 제외된다. 자신의 기준선 아래로 지출하는 데 성공한 경로는 검사된 산술(checked arithmetic)에서 실패한다 — 이는 안전한 결과이다. 그 자금은 애초에 이 스왑이 쓸 수 있는 것이 아니었기 때문이다.

다중 홉 경로는 계획이며, 계획은 모든 홉 경계에서 현실과 만난다. Router는 각 홉의 레그를 *실제로* 도착한 잔고에 맞게 재조정한다 — 홉 0은 측정된 인출 후 입력을 기준으로 하되 경로가 확정한 양으로 상한을 두고(따라서 Router는 미리 잘린 주문을 얇은 풀에 억지로 밀어 넣지 않는다), 이후의 모든 홉은 외래 기준선 위의 측정된 브리지 잔고를 기준으로 한다 — 각 레그는 $\mathrm{amt} = \mathrm{amountIn}_{\mathrm{leg}} \cdot \mathrm{real}/\mathrm{planned}$를 실행한다(`_hopScaleImpactAndQuote`). 영으로 조정된 레그는 건너뛰며, 사용되지 않은 입력과 브리지 잔여분은 Router가 아니라 지불자에게 되돌려진다. 같은 패스는 각 레그의 풀 상태를 한 번 읽고, 그 단일 읽기로부터 레그의 조정된 금액, 측정된 영향, 프레임 내 견적 — 하한이 소비하는 세 양 — 을 산출하므로, 준비금 계열과 집중 유동성 계열은 측정을 위해 추가 `staticcall`을 지불하지 않는다.

### §11.3 모든 레그 아래의 세 하한

**레그별 하한.** 각 레그가 Router의 출력 잔고에 기여한 측정값은 그 한계의 `LEG_FLOOR_BPS = 8,000` — 80 % — 에 도달해야 하며, 그렇지 않으면 스왑 전체가 `RouterE(5)`로 되돌려진다(`_execScaled`). 따라서 샌드위치당하거나 조작된 단일 풀은 그 손실을 다른 면에서는 건전한 합계 안에 숨기는 대신 즉시 트랜잭션을 실패시킨다. 한계는 호출자가 증명한 견적을 실제로 지출된 금액에 비례 배분한 값이며, 증명이 프레임 안에서 측정된 견적의 `MIN_QUOTE_COVERAGE_BPS = 5,000` 미만만을 포괄할 때에는 그 측정된 견적으로 *끌어올려진다*. 이것이 최소가 아니라 최대인 것은 의도적이다. 하한에서 축소된 주장과 함께 `min(claimed, measured)`를 쓰면 축소된 값이 반환되어 하한이 느슨해지며, 이것이 바로 이 게이트가 막는 공격이다. 가드는 하한의 근거가 하나라도 있을 때 — 호출자의 증명이든 프레임 내 견적이든 — 언제나 실행되므로, 경로에 영을 써 넣는다고 해서 더 이상 호출자 자신의 하한이 꺼지지 않는다. 그 한계: 측정 가능한 견적도 증명도 없는 베뉴는 집계 하한과 사용자 최소값으로 넘어가며, 이들이 어쨌든 그 베뉴를 한정한다.

**홉별 집계 하한.** 레그별 하한은 국소적인 반면 합성은 전역적이다. $L$개 레그 중 하나를 쥔 공격자는 어떤 단일 하한도 실패시키지 않고 홉의 약 $20\,\% \cdot (L-1)/L$를 추출할 수 있다. 따라서 각 홉은 *평균적인* 증명된 레그 하나가 정당하게 잃을 수 있는 만큼까지만 잃을 수 있다: $\sum \mathrm{got} + \lfloor (\mathrm{BPS} - \mathrm{LEG\_FLOOR\_BPS}) \cdot \lfloor \sum \mathrm{attested} / n \rfloor / \mathrm{BPS} \rfloor \ \ge\ \sum \mathrm{attested}$ — 여유분은 평균 증명 레그의 20 %이며, 그렇지 않으면 `RouterE(5)`이다. 최대가 아니라 평균인 이유는, 최대를 쓰면 공격자가 자신의 레그를 부풀려 공유 예산을 부풀리기 때문이다. 레그가 하나일 때 이 규칙은 정확히 레그별 하한으로 축약된다 — 구조상 새로운 경직성은 전혀 없다.

**경로 하한 Φ**는 §11.5 절에서 다룬다.

### §11.4 하나의 콜백, 하나의 잠금 해제

V3 형태의 모든 베뉴 — Uniswap V3, Algebra, 집중 유동성 Solidly 포크, Pancake V3, Sushi V3 — 는 서로 다른 콜백 이름 아래 동일한 플래시 회계 패턴을 사용한다. 풀은 스왑 도중 token0에 대한 델타, token1에 대한 델타, 임의의 데이터를 가지고 콜백하여 지불을 요구한다. 포크마다 이름 붙은 콜백을 하나씩 두는 대신, Router는 그 모두에 응답하는 단일 `fallback`을 노출한다 — 이것이 **단일 콜백 원칙**이다. 두 델타를 읽고, 그 부호로부터 어느 토큰을 빚지고 있는지 식별하고, 정확히 그 금액을 지불하며, 진행 중인 레그의 맥락은 transient storage(`TSLOT_POOL`, `TSLOT_TOKEN`, `TSLOT_AMT`)에서 가져온다. fallback은 누구나 도달할 수 있으므로 세 방면에서 보호된다. 현재 레그에 확정된 풀에만 지불하고, 진행 중인 레그가 없을 때에는 동작을 거부하며(스왑 사이에는 transient 맥락이 영이다), 레그에 기록된 예산보다 많이 내보내지 않는다 — 한 레그 안의 두 번째 요구는 `RouterE(6)`으로 거부된다. 새로운 콜백 이름을 가진 새로 출시된 V3 포크도 배포 당일부터 지원된다.

V4 싱글턴은 동일한 홉 인터페이스 뒤에 감싸진다: `unlock`, 그다음 스왑, `sync → settle → take`, 재잠금이며, 통화는 transient storage에 실리고 §5.3 절의 네이티브 이음새를 따른다. 어떤 V4 레그든 실행되기 전에 Router는 훅 주소를 읽고, 그 비트가 델타 변경 권한을 선언하면 레그를 거부한다(`RouterE(9)`). 체로 걸러진 훅이 곧 실행되는 훅이다. 정규 PoolManager가 unlock 데이터를 그대로 되돌려 주기 때문이다(`test/V4SievedHookIsTheExecutedHook.t.sol`). 경로 형태 규칙은 경로 전체에 걸친 정규 순서이다. 훅이 달린 레그가 한 번 실행되면, 이 홉이나 이후의 어떤 홉에서도 훅 없는 레그가 그 뒤에 실행되지 않는다(`sawHooked`). 따라서 훅은 같은 경로의 이후 훅 없는 레그를 그것이 정산되기 전에 관찰할 수 없다. 훅이 달린 레그는 그 뒤의 모든 레그도 훅이 달린 경우에만 더 앞선 홉에 위치할 수 있다.

**전송 수수료(fee-on-transfer), 이를 감당할 수 있는 레그에서.** 일부 토큰은 전송할 때마다 덜 전달된다. 상수곱 레그에서 Router는 이를 정규 패턴으로 처리한다 — 먼저 전송하고, 그다음 같은 전송 후 상태에서 풀의 준비금과 잔고를 읽어, 풀이 실제로 보는 차이에 대해 출력 가격을 매긴다 — 이는 구조상 정확한 양이다. 풀 자신의 불변식이 검사할 바로 그 양이기 때문이다. 이 분기는 토큰이 덜 측정될 때(`TSLOT_FOT`)에만 작동하며, 작동의 대가도 함께 명시된다. 견적에서 도출된 경로 하한은 세금을 모른 채 계산되었으므로 그 스왑에서는 제외되고, 프로토콜 하한은 측정된 순 비율로 재조정되며(보수적 방향인 올림으로), 레그별 한계는 같은 비율로 재산정되고, 사용자의 최소값이 구속 제약이 된다. 집중 유동성 레그는 설계상 전송 수수료를 지원하지 않는다 — 플래시 회계는 빚진 금액을 정확히 지불해야 한다 — 따라서 V3 전용 경로의 과세 토큰은 풀 안에서 되돌려지는 대신 처음부터 `RouterE(13)`으로 거부된다.

### §11.5 Iron-Law 하한 Φ의 전모

실행될 수 있는 모든 경로가 실행되어야 하는 것은 아니다 — 공정 가치의 60 %를 돌려주는 경로는 거래하지 않는 것보다 나쁘다 — 따라서 위의 모든 것 아래에는 프로토콜이 스스로 도출하는 하한이 놓인다. 이는 베이시스 포인트 단위의 보유 비율이다(Core `ironFloorBpsShv`, `legShaveBps`):

$$
\mathtt{floorBps} \;=\; \max\!\left(\;9{,}600 - \mathtt{legShv} - \min(\delta,\, 10^{4}) - \sigma_{\ln}/10^{14},\;\; 8{,}000\;\right)
$$
^e15

$$
\mathtt{legShv} \;=\; \sum_{\mathrm{hops}} \left\lfloor 200 \cdot \frac{(\sum_{i} a_{i})^{2} - \sum_{i} a_{i}^{2}}{\sum_{i} a_{i}^{2}} \right\rfloor \;=\; \sum_{\mathrm{hops}} \left\lfloor 200\,(n_{\mathrm{eff}} - 1) \right\rfloor
$$
^e23

세 상수가 이를 결정한다: `FLOOR_BASE_BPS = 9,600`, `FLOOR_PER_LEG_BPS = 200`, `FLOOR_HARD_MAX_LOSS_BPS = 2,000`. 하한은 깨끗한 단일 레그 스왑에서 96 %로 시작하여, 측정된 영향 $\delta$의 베이시스 포인트당 한 베이시스 포인트씩, 그리고 *실효* 추가 레그당 200 bps씩 느슨해지며, 8,000 bps 아래로 결코 떨어지지 않도록 클램프된다. 입력이 무엇이든, 프로토콜은 기준값의 80 % 미만을 보유하는 경로를 알면서 허용하는 일이 결코 없다. 레그 삭감은 레그 전반에 걸친 거래의 *집중도*로부터 계산된다 — $(\sum a)^{2}/\sum a^{2}$, 즉 실효 레그 수로, $N$개의 동일 비중이면 정확히 $N$이고 하나의 지배적인 레그면 정확히 1이다 — 따라서 분할은 실제로 얼마나 분할되었는지에 비례하여 완화를 얻고, 사라질 만큼 작은 비중을 나르는 레그는 영을 선언하든 한 wei를 선언하든 사라질 만큼 작은 양만 얻는다. 코시–슈바르츠 부등식에 의해 분자는 결코 언더플로하지 않는다. 영향 항은 경로에 걸친 레그들의 측정 영향의 비중 가중 평균이다. 두 성질이 정의의 일부로 명시된다. 변동성 항 $\sigma_{\ln}$은 시그니처에 존재하지만 모든 호출 지점에서 리터럴 영으로 전달된다 — 미래의 추정기를 위한 훅으로, 현재는 비활성이다 — 그리고 준비금 계열의 영향은 수수료를 제외한 값인 반면 집중 유동성 계열의 영향은 수수료를 포함한 값이며, 각각 해당 계열의 명세가 제시하는 형태를 따른다.

Φ를 휴리스틱이 아닌 강제로 만드는 것은 그것이 어디에서, 무엇에 대해 실행되는가이다. Router에 넘겨진 경로는 참고용 필드 — 견적된 합계, 증명된 하한 — 를 담고 있지만 Router는 어느 것도 신뢰하지 않는다. 실행 프레임 안에서 Router는 각 레그의 실제 영향을 측정하고, 넘겨받은 금액으로부터 레그 삭감을 계산하고, `floorBps`를 재계산하여, 이를 **최종 홉에 실제로 도착한 금액에서의 그 홉의 프레임 내 견적**에 적용한다 — 이는 출력 토큰으로 표시되고, 이번 실행 중에 읽은 풀 상태로부터 도출되며, calldata로는 위조할 수 없는 기준값이다. 기준값의 선택이야말로 가드가 애초에 발동할 수 있게 하는 것이다. *실현된* 출력의 일정 비율은 결코 실현된 출력을 초과할 수 없기 때문이다. 최종 홉을 프레임 안에서 견적할 수 없을 때 — 현재 틱의 유동성 공백, 수수료 센티널 — 기준값은 그 홉의 증명된 견적으로 대체되는데, 이는 커버리지 게이트가 이미 끌어올렸고 전송 수수료 측정이 재산정한 값이므로, 얇은 풀의 적시(JIT) 유동성 공백이 하한을 무력화할 수 없다. 하한의 앵커는 언제나 가치를 움직였고 경로의 출력을 산출하는 홉이다. 검사는 출력 측 수수료 이전의 총 출력에 대해 실행된다. 실효 최소값은 세 값의 최대이며,

$$
\mathtt{effMin} \;=\; \max\!\left(\mathtt{userMinOut},\;\; \mathtt{route.singleOutFloor},\;\; \left\lceil \mathtt{finalHopQuote} \cdot \mathtt{floorBps}/10^{4} \right\rceil\right)
$$
^e16

그 비대칭성이 핵심이다. 호출자가 제공한 필드는 보호를 강화할 수는 있어도 결코 완화할 수 없다. Solver는 같은 금액에 대해 같은 `legShaveBps`와 `ironFloorBpsShv`로 `singleOutFloor`를 증명하므로, Solver가 구성한 경로에서는 증명된 하한과 강제되는 하한이 구조상 일치하며(`test/FloorParitySolverRouter.t.sol`), §15 절의 체제 하네스는 생성된 모든 행에서 두 프레임을 고정한다. 수수료가 입력에서 떼어질 때 강제 하한은 $[\mathrm{attested} \times (1 - \mathrm{fee}),\ \mathrm{attested}]$ 안에 있고, 수수료가 출력에서 떼어질 때에는 증명된 하한과 같다.

![FIG-7 — Iron-Law 하한: 단일 레그, 동일한 두 레그, 동일한 네 레그에 대한 측정 영향 대비 보유율; 80 % 강제 클램프; 풀이 예제 3의 지점이 표시되어 있다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-7.png)

**풀이 예제 3 — 두 홉 경로에서의 하한.** 경로 A → WETH → B: 홉 0은 600 A를 풀 P로, 400 A를 풀 Q로 분할하고, 홉 1은 생산된 WETH를 하나의 풀 R로 보낸다. 프레임 안에서 측정된 값: 홉 0의 레그들은 100 및 60 bps의 영향을, 홉 1의 레그는 40 bps를 보이며, 경로 전체의 비중 가중 평균은 80 bps이다. 실제로 도착한 WETH 금액에서 홉 1의 프레임 내 견적은 1,000.000 B이다.

1. 홉 0 집중도: $(600 + 400)^{2} = 1{,}000{,}000$; $600^{2} + 400^{2} = 520{,}000$; $n_{\mathrm{eff}} = 1.923$; 삭감 $= \lfloor 200 \times 480{,}000 / 520{,}000 \rfloor = \lfloor 184.6 \rfloor = 184$ bps.
2. 홉 1은 레그가 하나이다: $(\sum a)^{2} - \sum a^{2} = 0$; 삭감 0. 총 `legShv` = 184.
3. $\mathtt{floorBps} = \max(9{,}600 - 184 - 80 - 0,\ 8{,}000) = 9{,}336$: 경로는 기준값의 93.36 %를 보유해야 한다.
4. $\mathtt{protocolFloorOut} = \lceil 1{,}000.000 \times 9{,}336 / 10{,}000 \rceil = 933.600$ B.
5. 트레이더가 설정한 `userMinOut` = 990 B와 Solver가 증명한 `singleOutFloor` = 933.6 B에서 `effMin` = 990 B이다 — 정직한 경로에서 마땅히 그래야 하듯, 사용자 자신의 한계가 구속력을 갖는 한계이다. 998.4 B의 전달은 정산되고, 985 B의 전달은 `RouterE(5)`로 거부되며, 모든 풀의 스왑을 포함한 트랜잭션 전체는 일어나지 않은 것이 된다.

트레이더가 `userMinOut` = 900 B로 설정했다면 프로토콜 하한이 933.6 B에서 구속력을 가졌을 것이다. 프레임 내 기준값보다 7 % 낮은 930 B의 체결은 사용자의 수치를 넘더라도 거부된다.

### §11.6 정산, 그리고 그것을 읽는 법

정산은 프레임을 열었던 방식 그대로, 측정으로 프레임을 닫는다. Router는 진입 기준선 대비 출력 토큰 변화량을 총 출력으로 취하고(영이면 `RouterE(8)`), 위의 하한 검사를 실행하고, 체제가 정한 곳에서 수수료를 부과하고(§13), 순액을 수령자에게 전송한 뒤 *수령자의* 잔고 변화량을 측정하고, 그 전달된 금액에 대해 `userMinOut`을 강제하며 — 따라서 전송 수수료가 있는 출력 토큰이 사용자를 그 한계 아래로 빠뜨릴 수 없다 — 수수료 원장을 한 번 읽는다. 프로토콜에 아무것도 지불하지 않은 정산은 거부되고(`RouterE(15)`), 고정(anchored) 경로에서의 두 번째 지불은 거부된다(`RouterE(16)`). 그런 다음, 그리고 성공 경로에서만, 실행된 레그마다 한 번씩 Hub의 `recordSwap`을 호출하는데(`_recordHits`), 이때 홉이 지출할 수 있었던 금액 — 각 레그에 측정 대비 선언 비율을 적용한 값 — 을 사용한다. 이것이 스왑이 수행하는 유일한 외부 쓰기이며, 하한이 거부하면 아무것도 쓰이지 않는다.

모든 정산은 `ExecutionProof(user, tokenOut, quoted, realized, floorUsed, blockNumber)`를 방출한다. 이는 실행과 같은 프레임에서 산출된 기준 견적으로, 누구나 해당 블록에서 `findBestRoutePlan`에 대한 `eth_call`로 재도출할 수 있는 온체인 시계열로 공개된다. 그 필드들을 과장 없이 설명하면 다음과 같다. `user`는 지불자이며 결코 `msg.sender`가 아니다. `quoted`는 최종 홉의 견적이다 — 이는 경로가 아니라 마지막 홉을 증명한다. `realized`는 전달된 양으로, 수령자 쪽에서 측정된다. `floorUsed`는 넘어서야 했던 프로토콜 하한이다. 각 `Fee(token, amount, toT1, toT2)` 이벤트는 토큰과 분배를 명시한다.

**풀이 예제 6 — 정산 읽기.** 예제 3의 경로가 정산되며 `ExecutionProof(0xUSER, B, 1,000.000, 998.400, 933.600, N)`과 하나의 `Fee(WETH, 0.014, 0.0042, 0.0098)`를 방출한다.

| 양 | 값 | 해석 |
|---|---|---|
| `quoted` | 1,000.000 B | 실제로 도착한 WETH에서의 최종 홉의 프레임 내 견적 |
| `realized` | 998.400 B | 수령자에게 전달되어 그곳에서 측정됨; 견적의 9,984 bps |
| `floorUsed` | 933.600 B | 견적에 `floorBps` 9,336을 적용한 값; 체결은 이보다 64.8 B 위에 있었다 |
| `Fee` | 하나의 이벤트에서 0.014 WETH | 고정 체제: 홉 0이 생산한 5.000 WETH의 28 bps로, 홉 1의 입력에서 지불되었으며 30 %는 첫 번째 트레저리로 갔다; B에서는 아무것도 떼지 않았다 |
| `userMinOut`까지의 거리 | 8.4 B | 사용자의 990 B 한계는 0.84 %의 여유를 남겼다 |

전달량에서 견적을 뺀 값은 프로토콜이 아니라 시장의 답이다. 수수료는 더 일찍, WETH로 빠져나갔으며, 이 경로에는 출력 측 공제가 존재하지 않는다. 출력 토큰으로 표시된 `Fee` 이벤트는 브리지 코인으로 들어가는 직접 경로에서만 나타난다(§13).

### §11.7 Router의 거부

| 코드 | 의미 | 위치 |
|---|---|---|
| `RouterE(1)` | 인가되지 않은 통제 호출 | `onlyControl` |
| `RouterE(2)` | 일시 중지됨, 또는 일시 중지 중 포기 시도 | `whenLive`, `renounceControl` |
| `RouterE(3)` | 잘못된 입력 — 영 주소, 연결되지 않은 네이티브 문, 홉 수, 홉당 레그 수, 홉 불연속 | 진입점, `_execute` |
| `RouterE(4)` | 기한 경과 | 진입점 |
| `RouterE(5)` | 하한 미달 출력 — 레그별, 홉별 집계, 경로 하한 또는 `userMinOut` | `_execScaled`, `_execute` |
| `RouterE(6)` | 인가되지 않은 콜백 — 잘못된 풀, 진행 중인 레그 없음, 두 번째 요구 | `fallback` |
| `RouterE(7)` | 재진입 | `nrEntrant`(transient 잠금) |
| `RouterE(8)` | 스왑 실패 — 영 출력, 알 수 없는 종류, 금액 이상인 수수료 | `_execute`, `_execScaled`, `_chargeHopFee` |
| `RouterE(9)` | V4 훅이 델타를 변경함 | V4 레그 사전 확정 |
| `RouterE(10)` | `amountIn > 0`인데 `userMinOut == 0` | 모든 문 |
| `RouterE(13)` | 집중 유동성 전용 경로의 전송 수수료 토큰 | `_execute` |
| `RouterE(14)` | 구조(rescue)가 대기열에 없거나 48시간 지연 기간 안에 있음 | `executeRescue` |
| `RouterE(15)` | 프로토콜 수수료를 지불하지 않고 정산됨 | 수수료 원장, `_execute`의 끝 |
| `RouterE(16)` | 고정 경로에서 수수료가 두 번 지불됨 | 수수료 원장, `_execute`의 끝 |

코드 11과 12는 할당되지 않았다. API만큼이나 열거 가능한 revert 표면이야말로 §15 절의 체제 배열이 모든 행을 하나의 규칙 — 정산하거나, 우리의 셀렉터로 거부하거나 — 으로 판정하고, 그 밖의 어떤 것이든 세 번째 길이라 부를 수 있게 해 주는 것이다.

## §12 견적 표면, 그리고 견적이 노화하는 방식

"당신의 견적"이라 불리는 숫자가 나올 수 있는 곳은 두 군데이며, 이 둘은 서로 다른 코드 경로이다. 하나는 당신이 서명하기 전에 인터페이스가 호출하는 읽기 전용 미리보기 컨트랙트인 **Quoter**이고, 다른 하나는 Solver와 Router가 실행 중인 트랜잭션 안에서 견적을 계산하는 **프레임 내 경로**이다. 종류별 진실은 앞서 §4.4 에서 제시했으며, 이 절은 그 표면들을 제시하고 그 사이의 간극을 측정한다.

### §12.1 Quoter의 표면

| 함수 | 반환값 | 비고 |
|---|---|---|
| `previewPlan(tIn, tOut, amountIn)` | `Preview` 하나 | 수수료와 안전 버퍼를 함께 담은 Solver의 계획 |
| `previewPlanWithMinOut(…, userMinOut)` | `Preview` 하나 | 위와 같으나, `canExecute`를 호출자의 최솟값 기준으로 판정 |
| `previewAndEncode(…)` / `previewAndEncodeWithMinOut(…)` | `Preview` 하나와 `swapExactIn`용 calldata | 통합자가 견적을 받는 방식 — §12.2 의 연구가 정확히 이것을 사용한다 |
| `batchQuote(entries[])` | 최대 `MAX_BATCH = 32`개의 미리보기 | 계획할 수 없는 쌍은 영으로 초기화된 항목을 남긴다 |
| `previewPlanExact(tIn, tOut, amountIn)` | 경로와 정확한 순출력 | 정밀 패스(Exact Pass): 모든 집중형 레그를 풀 자체의 스왑에 대해 모의 실행 |

**미리보기란 무엇인가.** `Preview` 구조체는 설계상 권고용이며 — 구속력 있는 검사는 Router가 프레임 내에서 재도출하는 검사들이다 — 그 필드들은 정의 지점에 문서화되어 있다. `grossOut`은 계획의 합계를 그대로 반영한다. `protocolFee`는 출력에 대한 수수료의 효과로, Router가 부과하는 방식(§13)과 정확히 같게 체제별로 모델링된다 — 고정 경로에서는 한 번, 소진 경로에서는 홉마다 한 번이며, Router가 반올림하는 대로 올림 처리된다. `safetyBuffer`는 다음과 같은 레그 간 버퍼이며

$$
s \;=\; \min\!\left(\max(0,\, n-2) + 5a,\;\; 10\right)\ \text{bps}
$$
^e21

이 값은 레그가 둘 이하이면 영이고, 둘을 넘는 레그 하나마다 한 베이시스 포인트이며, 여기에 *가정된* 수수료(Core가 자신의 상한 아래에서 대체한 선언 수수료)로 가격이 매겨진 상수곱 레그마다 다섯을 더하고, 이 모두에 열이라는 하나의 공통 상한이 적용된다. 상한 없는 버퍼는 더 이상 버퍼가 아니라 하한이 되기 때문이다. `netOut`은 $\mathrm{grossOut} - \mathrm{protocolFee} - s$ 이다. `ironFloor`는 증명된 하한을 그대로 반영한다. `topology`와 `bridgeUsed`는 홉 수와 Router가 사용할 수수료 앵커로부터 도출된다 — 같은 생산자의 같은 스캔이므로, 미리보기와 실행자가 서로 다른 브리지를 지목할 수 없다. 통합자 규칙은 한 문장에 담기며, 이 논문에서 가장 큰 하중을 지는 조언이다: 최솟값은 미리보기 필드가 아니라 당신 자신의 가격 기대로부터 도출하라.

**정밀 패스(Exact Pass).** 집중형 베뉴에 대한 풀 수준의 정확한 미리보기를 위해 Quoter는 풀의 실제 스왑 진입점을 호출한다. 풀은 지불을 요구하며 콜백하고, Quoter의 콜백은 지불하는 대신 revert하면서 계산된 델타를 revert 페이로드에 담아 보낸다. revert는 모든 상태를 되돌리고 아무것도 지불되지 않으며, 디코딩된 출력은 라이브 상태 위에서 실행되고 롤백된 베뉴 자신의 스왑이다. V4도 동일하게, 콜백이 패킹된 델타와 함께 revert하는 unlock을 통해 처리된다. 모의 실행을 거부하는 풀은 계획 자체의 주장을 그 아래의 현(chord)으로 스케일한 값을 유지한다 — AMM의 출력은 오목하므로 결코 그 위가 아니다. 홉 0은 계획이 약정한 양으로 상한이 걸리므로, 용량 제한된 경로가 Router가 결코 쓰지 않을 크기로 모의 실행되지 않는다. 2026-09-03 이후 반환되는 스칼라는 프로토콜 수수료를 뺀 순값으로, 미리보기가 공제하는 방식대로 한 번 공제되므로 이로부터 도출한 최솟값은 하한에 의해 지켜진다(`test/QuoterExactNetOut.t.sol`). 이것은 Quoter의 미리보기 측 계측 도구이며, 구속력 있는 표면은 프레임 내 재도출이다.

### §12.2 견적이 노화하는 방식

견적은 미래 블록에 대한 약속이다. 이 연구는 통합자가 하는 방식대로 견적을 받고 — `previewAndEncode`가 미리보기와 calldata를 반환한다 — 세상이 움직이게 하며(다른 누군가가 같은 풀들을 통해 영에서 세 건의 거래를 하고, 각각은 얕은 준비금의 최대 3 %이며, 모두 사용자와 같은 방향이므로 하나하나가 손해를 준다), 영에서 십 초가 지나게 한 뒤, calldata를 변경 없이 실행한다. 상수곱 목(mock)으로 이루어진 세 토큰 유니버스(A, B = 브리지 코인, C; 브리지로 직접, 브리지를 거치는 두 홉, 반대 방향의 두 홉)에서 240개 표본을 수집했으며, 모든 결과를 분류했다(`test/QuoteDelayStatistics.t.sol`):

| 견적과 실행 사이의 드리프트 | 표본 | 정산됨 | 하한에 의해 거부됨(`RouterE(5)`) | 전달/예측 — 평균 | 최소 |
|---|---|---|---|---|---|
| 없음 | 64 | 64 | 0 | 10,000 bps | 10,000 |
| 1 – 100 bps | 43 | 43 | 0 | 9,952 bps | 9,803 |
| 100 – 300 bps | 133 | 102 | 31 | 9,966 bps | 9,653 |

| 견적과 실행 사이의 지연 | 표본 | 정산됨 | 거부됨(`RouterE(5)`) | 전달/예측 — 평균 | 최소 |
|---|---|---|---|---|---|
| 0초 | 17 | 15 | 2 | 9,989 bps | 9,857 |
| 1 – 5초 | 99 | 89 | 10 | 9,959 bps | 9,653 |
| 6 – 10초 | 124 | 105 | 19 | 9,983 bps | 9,659 |

![FIG-8 — 견적이 노화하는 방식: 드리프트 구간별 정산 및 거부 표본과 모든 정산 표본의 전달/예측 비율; 같은 240개 표본을 지연별로 구간화하면 아무런 추세도 나타나지 않는다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-8.png)

시간은 견적을 움직이지 않는다. 드리프트가 움직인다. 모든 표본에서 단언된 것은 다음과 같다: 드리프트가 없는 견적은 모든 지연에서 정산되며 정확히 그 예측값을 전달한다(64 중 64). 정산은 미리보기가 증명한 하한 아래로 전달하는 일이 결코 없다. 세 번째 결과는 없다. 기한이 지난 뒤(`deadline = now + 5`, `+10`에 실행) 20 중 20이 기한 자체의 코드인 `RouterE(4)`로 거부되며, 어느 것도 정산되지 않는다. 최대 1 %의 불리한 드리프트 아래에서는 모든 견적이 여전히 체결되며 예측의 2 % 이내에 든다. 1–3 % 아래에서는 하한이 증명된 출력 아래로 체결하느니 네 건 중 한 건을 거부하며, 체결은 예측의 3.5 % 이내에 안착한다 — 견적 쪽에서 본 샌드위치 곡선(§14)이다.

**라이브 Base** (`test/fork/QuoteDelayFork.t.sol`, 1,000 USDC → WETH, 실제 풀, 현재 블록의 포크에 배포된 프로토콜): 다른 것이 전혀 움직이지 않은 상태에서 0, 3, 6, 10초 후에 실행했을 때, 견적은 네 번 모두 정확히 그 예측값을 전달했다. 같은 경로를 통해 10,000 · 50,000 · 200,000 · 1,000,000 · 5,000,000 USDC가 앞서 거래되고 오래된 calldata를 10초 후에 실행했을 때, 다섯 건 모두 하한 안에서 예측의 9,999 · 9,998 · 9,994 · 9,974 · 10,000 bps로 정산되었다: 천 달러 거래 앞의 백만 달러는 그 천 달러에 사분의 일 퍼센트의 비용을 치르게 하며, 오백만 달러 거래는 천 달러 경로가 건드리지 않는 풀들을 통해 라우팅되었다. 위의 분포는 목 위에서 얻은 것이다. 라이브 Base는 한 블록에서 한 쌍에 대한 도달 범위를 줄 뿐, 분포를 주지 않는다.

### §12.3 ABI를 통한 견적의 비용

외부 호출 주위에서 측정한 가스이며, 세 토큰 유니버스에서 각각 마흔 가지 크기(`test/QuoterGasStatistics.t.sol`), 라이브 Base에서 다섯 가지 크기(`test/fork/QuoterGasFork.t.sol`)로 측정했다:

| 세계 | `previewPlan` 평균 | 최소 | 최대 | σ | `previewAndEncode` 평균 | σ |
|---|---|---|---|---|---|---|
| 목 — 발견: 팩토리만 아는 쌍, 풀 하나 발견 | 121,313 | 116,468 | 123,491 | 1,433 | 123,317 | 1,851 |
| 목 — 새 레지스트리: 시드된 풀 세 개의 가격 산정 | 235,574 | 227,205 | 237,843 | 1,815 | 239,647 | 1,937 |
| 라이브 Base — 콜드: 레지스트리가 비어 있음, 승인된 팩토리 전수 탐색 | 1,524,221 | | | | 1,525,341 | |
| 라이브 Base — 웜: 한 번의 실행 직후의 새 상태, 풀 두 개 등록 | 1,414,633 | | | | 1,416,037 | |
| 라이브 Base — 발견 TTL 이후 다시 콜드 | 1,359,925 | | | | 1,361,582 | |

목에서 열 개 항목의 `batchQuote`: 2,055,390 가스, 항목당 205,539. 새 레지스트리 목이 발견보다 비용이 더 드는 것은 발견이 풀 하나를 찾은 곳에서 풀 세 개의 가격을 매기기 때문이다. 정직한 비교는 라이브 쪽이며, 거기서 발견 스윕은 견적의 약 7 %이다 — 그리고 견적은 통합자가 온체인에서 결코 비용을 지불하지 않는 view 호출이다.

## §13 수수료 정책: 두 체제와 잉여 규칙

*쉽게 말하면:* 프로토콜은 0.28 %를 한 번, 측정된 하나의 금액에서, 트레저리가 보유하고자 하는 코인으로 가져가며, 그렇게 했음을 센다. *정확히 말하면:* `PROTOCOL_FEE_BPS = 28`은 Core의 컴파일 타임 상수로, 배포된 바이트코드 어디에도 setter가 없다. 수수료는 `mulDivUp(base, 28, 10,000)`이며 — 올림 처리되므로 영이 아닌 어떤 기준액에서도 영의 수수료에 도달할 수 없다 — `TREASURY1_SHARE = 3,000` bps는 첫 번째 트레저리로, 나머지는 두 번째 트레저리로 분할된다(`fee − t1`로 계산되므로 둘의 합은 언제나 먼지 없이 수수료와 같다). 트레저리들은 생성 시에 고정되며 일방통행 문에 의해 영구히 동결될 수 있다. 어떤 키도 거래자에게 불리하게 요율을 올릴 수 없고, 어떤 키도 우대받는 통합자를 위해 요율을 낮출 수 없다: 수수료 문제는 컴파일 시에 답이 정해졌고, 키가 할 수 있는 일의 목록에서 제거되었다.

### §13.1 수수료가 부과되는 곳: 두 체제, 둘 다 명명됨

2026-08-22 이후의 규칙은 한 문장이다: **Router가 보유하는 첫 번째 브리지 코인에서 한 번 부과한다.** Router는 입력 토큰이 등록된 브리지인 첫 번째 홉을 찾아 경로를 스캔하며(`hub.isBridgeToken`, 홉 순서대로) — 그 홉이 `feeHop`이다 — 그 홉의 *측정된* 입력의 28 bps를 부과한다. 홉 0에서는 경로가 약정한 양으로 상한이 걸린 측정된 인출액이고, 이후 홉에서는 외래 기준선을 넘는 Router의 브리지 코인 잔액이며, 이는 이미 전송 수수료(fee-on-transfer)와 떠도는 잔액에 내성이 있는 양이다. 그런 다음 Router는 그 경로에서 다른 어떤 것도 부과하지 않는다. 목적지 자체가 브리지 코인인 직접 경로(TOKEN → WETH)는 대신 하한 검사 이후 출력에 부과한다 — 하한은 스왑 품질을 검증하고, 프로토콜의 몫은 이미 검증된 금액에서 나가며, 사용자의 최솟값은 실제로 받는 금액과 비교된다. 이것이 **고정 체제**이다. 왜 목적지가 아니라 브리지에서인가: 브리지는 WETH와 USDC이며, 트레저리는 목적지인 어떤 꼬리 토큰의 먼지 대신 보유하고 싶은 유동성 있는 코인을 받는다. 왜 홉 0의 출력이 아니라 홉 1의 입력에서인가: 둘은 같은 토큰, 같은 금액이지만, 홉 1의 입력은 실제 잔액으로 측정되며, 이는 Router가 이미 수행하는 측정이다.

이 규칙에는 2026-09-05까지 그 문장이 명명하지 않았던 두 번째 체제가 있다. 레지스트리가 보유하지 않을 풀들을 통해 손으로 만든 경로로서 어떤 홉의 입력도 브리지 코인이 아닌 경로는 결코 `feeHop`을 찾지 못한다. 이때 부과를 결정하는 술어는 *모든* 홉에 대해 참이 되며, 각 홉은 자신의 측정된 입력에 대해 28 bps를 지불한다. 이것이 **소진 체제**이며, 실수가 아니라 의도된 규칙이다: 그런 경로에 홉 0에서 한 번만 부과하는 방식이 그날 스위트 안에서 시도되었고, 소진 정책이 막기 위해 존재하는 탈출구를 다시 열었다 — 가치 없는 첫 홉이 수수료 지점을 먼지로 옮기고 실제 홉은 아무것도 지불하지 않는다(`test/FeeEscapeViaJunkPrefix.t.sol`). 고정된 다섯 개의 테스트가 그 실행 안에서 변경을 거부했다. 모든 홉을 벗어나도록 먼지를 끼워 넣을 수 있는 인덱스는 존재하지 않으며, 이것이 "소진에 의한 면역"의 의미이다. Solver는 모든 다중 홉 경로를 등록된 브리지를 통해 구성하므로, Solver가 만든 경로는 항상 정확히 한 번 지불한다. 소진 체제는 호출자가 레지스트리를 우회해 라우팅할 때 마주치는 것이며, Quoter는 이를 모델링한다(`_pack`은 어떤 홉 입력도 브리지가 아니면 `hops.length`번, 그렇지 않으면 한 번 부과한다). 따라서 두 체제 모두에서 미리보기와 전달이 일치한다(`test/ExhaustionRegimePreviewParity.t.sol`).

![FIG-9 — Router가 결정하는 두 수수료 체제: 홉들을 스캔해 첫 번째 브리지 코인 입력을 찾는다. 고정 경로는 그 홉에서 한 번 지불한다(또는 브리지 코인으로 가는 직접 경로의 출력에서). 브리지 입력이 없는 경로는 홉마다 한 번 지불한다. 원장은 횟수를 세어 영을 거부하고, 고정 경로에서는 둘을 거부한다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-9.png)

### §13.2 약정에는 생산자가 하나뿐이며, 컨트랙트가 센다

두 개의 봉인이 체제들에 수반된다. 한 홉의 선언된 레그 입력의 합 — 홉 0에서의 수수료 기준액이자 스케일의 분모 — 은 단 하나의 함수 `_legSum`이 계산한다. 2026-09-05 이전에는 각각이 자기 루프에서 레그를 합산했으며, 하나의 숫자에 생산자가 둘이었다. 그리고 수수료 원장: 수수료를 전송하는 유일한 함수인 `_payFee`는 일시(transient) 슬롯(`TSLOT_FEE`)에 횟수를 센다. 모든 정산은 그 슬롯을 한 번 읽고, 아무것도 지불하지 않은 전달(`RouterE(15)`)과, 고정 경로에서는 두 번째 지불(`RouterE(16)`)을 거부한 뒤, 슬롯을 비운다. 원장의 두 검사는 *벨트(belt)*이다: 그 앞의 술어가 지불 없이 정산하거나 고정 경로에서 두 번 지불하는 경로를 남기지 않으므로, 단독으로는 어떤 테스트도 이들을 작동시킬 수 없다. 이들은 테스트가 리뷰 시점에 거부하는 것을, 아직 존재하지 않는 경로에 대해 컨트랙트가 실행 시점에 거부하는 것이다. 이들은 벨트로 나열되며, 어디에서도 커버된 것으로 계산되지 않고, 이름 있는 테스트가 죽이는 뮤턴트만 받아들이는 변이 가드에서도 빠져 있다.

### §13.3 잉여 규칙

**잉여 규칙**은 수수료 설계의 정렬 논증을 코드가 구현하는 그대로 진술한 것이다: 프로토콜의 몫은 측정된 하나의 기준액의 28 bps로, 고정 경로에서는 한 번, 소진 경로에서는 홉마다 한 번이며, 정산 어디에도 견적된 것과 전달된 것의 차이에 비례하는 항은 없다. 기준액이 입력인 곳 — 모든 다중 홉 경로와 목적지가 브리지 코인이 아닌 모든 직접 경로 — 에서는 출력이 건드려지지 않는다: 풀들이 견적을 넘어 전달하는 모든 것이 수령인에게 온전히 도달하며, 체결이 유리하게 나왔다고 해서 수수료가 오를 수 없다. 기준액이 출력인 곳 — 브리지 코인으로 가는 직접 경로 — 에서는 수수료가 잉여를 포함한 총 전달액의 28 bps이며, 그 이상은 없다. 애그리게이터가 가장 떼어먹고 싶은 유혹을 받는 축은 견적과 체결 사이의 간극이다. 이 설계는 그 간극에 수수료 항을 전혀 두지 않으므로, 표기된 요율이 진짜이자 유일한 요율이다. 기준액은 결코 calldata에서 읽히지 않는다: `route.totalOut`은 낯선 자의 주장으로서 Router에 도달하며 아무것도 그것을 읽지 않는다(`test_FeeBase_IgnoresLiedAboutTotalOut`).

**풀이 예제 5 — 한 경로 위의 두 체제.** 브리지는 {WETH, USDC}이다. 거래자는 10,000 A를 판다.

*고정, 두 홉.* A → WETH → B. 홉 0은 부과되지 않는다(그 입력 A는 브리지가 아니다). 홉 0의 풀들은 5.000 WETH를 지급하며, 이는 외래 기준선을 넘는 Router의 WETH 잔액으로 측정된다. `feeHop` = 1: 수수료는 $\lceil 5.000 \times 28 / 10{,}000 \rceil = 0.014$ WETH이며, 0.0042는 첫 번째 트레저리로, 0.0098은 두 번째 트레저리로 분할되고, 홉 1은 4.986 WETH를 B로 투입한다. `Fee` 이벤트 하나. 원장은 1을 읽고 충족된다. 미리보기는 28 bps를 한 번 공제했다.

*고정, 브리지로 직접.* 한 홉으로 A → WETH. 입력에는 아무것도 부과되지 않는다. 풀들은 총 5.000 WETH를 전달한다. 하한은 5.000에 대해 검사되고, 그다음 출력 측 수수료 $\lceil 5.000 \times 0.0028 \rceil = 0.014$ WETH가 지불되며 4.986 WETH가 전달된다. `userMinOut`은 4.986과 비교된다. 출력 토큰으로 표시된 `Fee` 이벤트 하나.

*소진, 두 홉.* A, C, B 어느 것도 브리지가 아닌 A → C → B — 어떤 Solver도 만들지 않는 경로. 홉 0은 $\lceil 10{,}000 \times 0.0028 \rceil = 28$ A를 지불하고 9,972 A를 자신의 풀들로 보내며, 풀들은 2,000 C를 지급한다. 홉 1은 $\lceil 2{,}000 \times 0.0028 \rceil = 5.6$ C를 지불하고 1,994.4 C를 B로 보낸다. `Fee` 이벤트 둘. `feeHop`이 없는 상태에서 원장은 2를 읽으므로 `RouterE(16)`은 적용되지 않는다. 실효 요율은 $1 - 0.9972^{2} = 55.9$ bps이며, 미리보기도 그렇게 말했다.

### §13.4 Router 바깥에서 측정한 수수료

`test/FeeSeals.t.sol`은 Router의 숫자를 하나도 읽지 않는다: 수수료 토큰은 규칙과 브리지 목록에서, 기준액은 풀들의 잔액 델타(Router에서 수수료 홉으로 나간 것, 또는 이전 홉의 풀들이 지급한 것)나 수령인의 잔액 델타에서, 수수료는 트레저리들의 델타에서, 횟수는 `Fee` 이벤트에서 얻는다 — Router가 받아들이는 모든 형태(한 홉에서 세 홉; 브리지가 없음, 또는 첫째, 중간, 마지막 위치; 홉당 레그 하나 또는 둘)에 걸쳐, $10^{12}$ 에서 $10^{21}$ wei까지 퍼즈된 금액으로, 2,000회 실행, 실패 없음, 모든 정산 후 Router는 아무것도 보유하지 않았다.

| 경로 형태 | 체제 | 수수료가 부과되는 곳 | 측정값 |
|---|---|---|---|
| A → B (둘 다 브리지 아님) | 소진, 한 홉 | 홉 0의 입력, 한 번 | 수수료 = ⌈28 bps × 입력⌉, 이벤트 하나 |
| W → A (브리지 입력) | 고정 | 홉 0의 입력 | 동일 |
| A → W (브리지 출력, 직접) | 고정, 출력 측 | 출력 | 수수료 = ⌈28 bps × 총 출력⌉, 전달 = 총액 − 수수료 |
| A → C → B (브리지 없음) | 소진, 두 홉 | 각 홉의 측정된 입력 | 이벤트 둘 |
| W → A → B | 홉 0에서 고정 | 홉 0의 입력 | 이벤트 하나 |
| A → W → B | 홉 1에서 고정 | 홉 0이 생산한 브리지 코인 | 이벤트 하나, ⌈28 bps × 홉 0의 풀들이 지급한 양⌉ |
| A → C → W (두 홉의 출력으로만 브리지) | 소진 | 각 홉의 입력 | 이벤트 둘 |
| A → C → D → B · A → W → C → B · A → C → W → B | 위와 같음 | 위와 같음 | 이벤트 3 · 1 · 1 |

**수수료 테스트가 결함을 얼마나 자주 알아차리는가**는 가정하지 않고 측정했다(`docs/assurance/fee-seal-detection.json`): 모든 수수료 뮤턴트 — 어느 방향으로든 옮겨진 체제 술어, 약정 생산자, 원장의 두 검사, 그리고 불변식 연구의 세 수수료 뮤턴트 — 를 퍼즈 테스트마다 스무 개의 퍼즈 시드로, 결정론적 테스트마다 한 번 실행했다. 탐지율은 Wilson 95 % 구간과 함께 보고하며, 놓친 것이 없는 경우에는 놓칠 확률에 대한 삼의 법칙(rule of three) 상한을 함께 보고한다.

| 뮤턴트 | FeeSeals 퍼즈 | Router 캠페인 (2 홉, 2 레그) | 커버링 배열 t = 2 | 정크 접두사 탈출 | 소진 미리보기 동등성 |
|---|---|---|---|---|---|
| 소진 체제가 홉 0에만 부과 | 20/20 [0.84, 1.00] | 20/20 [0.84, 1.00] | 아니오 | 예 | 예 |
| 소진 체제가 홉 0을 건너뜀 | 20/20 [0.84, 1.00] | 20/20 [0.84, 1.00] | 아니오 | 예 | 예 |
| 약정이 첫 번째 레그만 셈 | 20/20 [0.84, 1.00] | 20/20 [0.84, 1.00] | 아니오 | 아니오 | 아니오 |
| 수수료 두 배 | 20/20 [0.84, 1.00] | 20/20 [0.84, 1.00] | 예 | 예 | 예 |
| 입력 측 수수료가 전혀 부과되지 않음 | 20/20 [0.84, 1.00] | 20/20 [0.84, 1.00] | 아니오 | 예 | 예 |
| 수수료가 양쪽 모두에 부과됨 | 20/20 [0.84, 1.00] | 20/20 [0.84, 1.00] | 아니오 | 예 | 예 |
| 벨트: 수수료 없는 정산이 더 이상 거부되지 않음 | 0/20 [0.00, 0.16] | 0/20 [0.00, 0.16] | 아니오 | 아니오 | 아니오 |
| 벨트: 고정 경로의 이중 지불이 더 이상 거부되지 않음 | 0/20 [0.00, 0.16] | 0/20 [0.00, 0.16] | 아니오 | 아니오 | 아니오 |

![FIG-10 — 각 테스트에 의한 각 수수료 뮤턴트의 탐지율을 나타낸 히트맵: Wilson 구간과 함께 표시한 실행 대비 사멸 수; 두 벨트는 아무것도 보지 못하는 행으로 드러난다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-10.png)

행을 가로로 읽으면: 어떤 테스트가 이 결함을 보는가. 열을 세로로 읽으면: 한 테스트가 무엇을 볼 수 있고 무엇을 볼 수 없는가. 첫 측정에서 Router 캠페인은 홉 0을 면제하는 소진 뮤턴트와 약정 생산자를 보지 못했는데, 직접 한 레그 경로만 만들었기 때문이다. 캠페인은 같은 날 두 홉을 걷고 홉 0을 두 풀로 분할하도록 확장되었으며, 하나의 캠페인이 모든 체제를 포괄하도록 유니버스에 브리지 코인을 두었고, 이제 둘 다 20 중 20으로 보인다. 커버링 배열은 두 배 수수료만 보는데, 그 행들이 언제나 브리지를 지나기 때문이다. 20 중 20을 기록한 스무 개 시드는 단일 캠페인의 놓칠 확률을 15 %로 제한하며, 이것이 변이 가드가 이름 있는 테스트와 캠페인을 둘 다 실행하는 이유이다.

이 논문이 사용하는 형식 — 주장, 강제 지점, 한계 — 으로 이 절을 요약하면 다음과 같다: 누구도 요율이나 분할을 움직일 수 없다. 배포된 바이트코드에 둘 다 setter가 없기 때문이다. 호출자는 견적을 축소 신고하여 수수료를 줄일 수 없다. 기준액은 측정된 잔액이고 `route.totalOut`은 아무것도 읽지 않기 때문이다. 호출자는 가치 없는 접두사로 수수료를 피할 수 없다. 브리지 입력이 없는 경로는 모든 홉에서 지불하기 때문이다. 그리고 정산은 아무것도 지불하지 않거나 고정 경로에서 두 번 지불할 수 없다. Router가 세기 때문이다 — 다만 카운터 자신의 두 검사는 어떤 테스트도 작동시킬 수 없는 벨트라는 한계가 있다.

## §14 보안 모델

*쉽게 말하면:* Router는 트랜잭션 사이에 아무것도 보유하지 않고, 모든 스왑은 전부 아니면 전무이며, 이 저장소가 고려한 각 공격 클래스는 이름 있는 검사로 답하거나 범위 밖으로 명시된다. *정확히 말하면:* 위협 카탈로그는 `docs/assurance/threats.json`으로, 공개된 익스플로잇 기록에서 추린 23개 클래스이다. 각 클래스는 그것을 거부하는 가드 심볼을 명시하며, 심볼이 `src/`를 떠나거나 이름 있는 테스트가 `test/`를 떠나면 CI 스크립트가 빌드를 실패시킨다. 카탈로그의 커버리지는 고려된 것에 대한 하한일 뿐, 존재하는 것에 대한 상한이 결코 아니다.

### §14.1 공격과 그것을 거부하는 검사

| 클래스 (id) | 그것을 거부하는 검사 | 강제되는 곳 | 상태 | 검사가 실패할 수 있음을 보여 주는 테스트 |
|---|---|---|---|---|
| 재진입 (DASP-1) | 가치를 이동시키는 모든 문에 일시 락 `nrEntrant`; 콜백 표면은 자신의 호출자를 인증한다 | Router `nrEntrant` → `RouterE(7)`; `fallback` → `RouterE(6)` | 차단됨 | `invariant_reentrancyBlocked` (`test/RouterAdversarialV4FromV1.t.sol`) |
| 접근 제어 (DASP-2) | 네 가지 역할; 모든 특권 문은 게이트됨; 제어 계층은 영구히 포기 가능 | Hub `onlyAdmin` / `onlyControl` → `HubE(1)`; Router `onlyControl` → `RouterE(1)` | 차단됨 | `test/HubAllowHookAfterRenounce.t.sol`, `test/ControlEvents.t.sol` |
| 산술 오버플로/언더플로 (DASP-3) | 전반에 걸친 검사된 산술; 모든 `unchecked` 블록에 서면 경계가 있음; 하드 곡선의 표현 가능성을 경계(`MAX_SQRT_PRICE_MINUS_ONE`)에서 테스트 | Core | 차단됨 | `test/BoundaryEquality.t.sol`, `test/CompilerPanicSurface.t.sol` |
| 무제한 반복에 의한 서비스 거부 (DASP-5) | 모든 경로 차원에 경계: `MAX_HOPS = 3`, `MAX_LEGS_PER_HOP = 5`, `MAX_LEGS = 11`, `MAX_CANDIDATES = 8`, `MAX_SLOTS = 16`, `MAX_FACTORIES = 16` | Router `_execute` → `RouterE(3)`; Solver; Hub → `HubE(4)` (팩토리), `_canInsert` (슬롯) | 차단됨 | `test/RouteHopCeiling.t.sol` |
| 트랜잭션 순서 의존 / 샌드위치 (DASP-7) | Iron-Law 하한으로, `FLOOR_HARD_MAX_LOSS_BPS = 2,000`에서 하드 클램프되며 호출자의 최솟값과 독립적으로 강제됨 — 최솟값은 하한을 더 조일 수만 있음 | Core `ironFloorBpsShv`; Router `_execute` → `RouterE(5)` | 차단됨 (제한됨, §14.3) | `test/regime/SandwichCurve.t.sol`; `invariant_DeliveredNeverBelowTheProtocolFloor` |
| 베뉴를 통한 교차 함수 재진입 (SWC-107) | 같은 일시 락이 풀 콜백을 포함한 스왑 전체에 걸침 | Router `nrEntrant` | 차단됨 | `test/V4LockedRegionReentrancy.t.sol` |
| 가격 오라클 조작 (SOK-ORACLE) | 피드가 없음: 기준 견적은 거래되는 베뉴들로부터 실행 프레임 안에서 생산되며, 하한은 그것에서 도출됨(`ironFloorBps`) | Core; Router `_execute` | 차단됨 | `test_Parity_SplitRoute_AttestedFloorEqualsEnforcedFloor` (`test/FloorParitySolverRouter.t.sol`) |
| 측정된 양을 호출자가 제공한 양으로 대체 (SOK-FEE-BASE) | 수수료 기준액은 측정된 잔액(`_chargeHopFee`); `route.totalOut`은 아무것도 읽지 않음 | Router `_chargeHopFee` | 차단됨 | `test_FeeBase_IgnoresLiedAboutTotalOut` (`test/BlazePhoenixRouter.t.sol`) |
| 전송 수수료 토큰과 리베이싱 토큰 (SOK-FOT) | 중요한 모든 금액은 측정된 잔액 델타(`balanceOf`)이며, 결코 가정된 전송 금액이 아님 | Router, 모든 이음매 | 차단됨 | `test/FotFloorReprice.t.sol`, `test/PartialFotAtPrePulledDoors.t.sol` |
| 레지스트리에 승인된 적대적 베뉴 (SOK-VENUE-ADMISSION) | 승인은 큐레이터 전용이며 주소당 한 행; 도출 원점은 승인 시 증명되고(`factoryDeployer`) 포기 후 동결됨 | Hub `addFactory`, `_probe` | 차단됨 | `test_C4_FirstPin_AfterRenounce_MustNotAttestTheLiveAnswer` (`test/T19ReadmissionEdge.t.sol`) |
| 프록시 뒤에서 로직이 바뀌는 승인된 베뉴 (SOK-PROXY-VENUE) | 런타임이 바뀐 경우 포기 후 재승인 거부(`factoryCodehash`); 프록시가 코드를 바꾸지 않고 답을 바꿀 수 있는 경우에는 대신 답을 고정 | Hub `addFactory` → `HubE(1)` | 차단됨 | `test/FactoryCodehashPin.t.sol`, `test/RenouncedFactoryRearm.t.sol` |
| 경직화 이후 도출에서 질의로 전환된 베뉴 행 (SOK-MODE-FLIP) | `controlRenounced` 이후 도출(derive) 행은 질의(ask) 행이 될 수 없음; 반대 방향의 강화는 계속 허용됨 | Hub `addFactory` | 차단됨 | `test_C4_S3_DeriveRowMustNotBecomeAnAskRowAfterRenounce` (`test/T19ReadmissionEdge.t.sol`) |
| 호출자의 선언을 보고하는 공개 지표 (SOK-VOLUME) | 레지스트리에는 측정 대 선언 비율(`hopScale`)로부터 홉이 실제로 쓸 수 있었던 금액이 전달됨 | Router `_recordHits` | 차단됨 | `test_INV_F4_VolumeInEqualsTheMeasuredPoolDelta` (`test/VolumeEventFidelity.t.sol`) |
| 실행을 예측하지 못하는 미리보기 (SOK-QUOTE-EXEC) | 두 수수료 체제 모두에서 미리보기와 전달이 같음을 단언(`previewRoute`, `_pack`) | Quoter; Router | 차단됨 | `test_INV_F2_PreviewPredictsDeliveryWithNoBridge` (`test/ExhaustionRegimePreviewParity.t.sol`) |
| 공격자의 주소로 유도된 결정론적 도출 (SOK-V4-DERIVE) | CREATE2 프리이미지는 생산자가 하나(`create2Address`); 풀 id는 자신의 키에서 도출됨; `hasCode`와 활성 증명이 해석되지 않는 것을 폐기 | Core; Hub `_probe`, `_scanV4` | 차단됨 | `test/ConditionAdequacyCore.t.sol`, `test/KindIsDerivedNotDeclared.t.sol` |
| 훅이 있는 베뉴의 악성 훅 (SOK-HOOK) | 세 계층, §14.2: 불변 주소 비트, 코드해시 고정이 있는 허용 목록(`hookAllowed`, `isHookLive`), 사영자 Ξ | Router → `RouterE(9)`; Hub `allowHook` → `HubE(8)`; Solver `_topKPools` | 차단됨 | `test/V4SievedHookIsTheExecutedHook.t.sol`, `test/SeedPoolHookMustBeAllowed.t.sol`, `test/RenouncedHookRearm.t.sol` |
| 반복 승인에 의한 레지스트리 고갈 (SOK-GRIEF-SEATS) | 주소당 한 행; 반복된 추가는 자리(`MAX_FACTORIES`)를 소모하지 않고 제자리에서 갱신 | Hub `addFactory` → `HubE(4)` | 차단됨 | `test/RenouncedFactoryRearm.t.sol` |
| 임의 외부 호출 또는 transfer-from 싱크 (SOK-ARBITRARY-CALL) | 실행자는 호출자가 제공한 호출 대상을 결코 받지 않음; 베뉴 상호작용은 종류별로 선택되는 닫힌 형태 집합에서 디스패치되며(`_execScaled`), 알 수 없는 종류는 revert됨 | Router `_execScaled` → `RouterE(8)` | 차단됨 | `test/regime/HostileVenueMatrix.t.sol` |
| 위임 승인에 대한 서명 재생 (SOK-SIG-REPLAY) | 위임 승인은 정규 Permit2(`permitTransferFrom`)가 처리; 시스템은 자체 서명 체계를 갖지 않음 | Router `swapExactInWithPermit2` | 차단됨 | `test/RouterPermit2OneStep.t.sol` |
| 거버넌스 장악 (SOK-GOV) | 거버넌스가 없음: 토큰 투표도, 타임락도, 프록시도 없음; 유일한 특권은 베뉴 승인과 일시 정지이며, 둘 다 포기 가능 | — | 범위 밖 | — |
| 악성 업그레이드 (SOK-UPGRADE) | 컨트랙트는 불변; 프록시도 구현 슬롯도 없음 | — | 범위 밖 | — |
| 외부 브리지 또는 베뉴의 침해 (SOK-BRIDGE-EXT) | 타사 베뉴는 조합될 뿐 통제되지 않음; 그 실패는 여기서 하한과 호출자의 최솟값으로 제한되며, 그 내부 보안은 주장하지 않음 | — | 범위 밖 | — |
| 사용자 개인 키의 침해 (SOK-KEY) | 신뢰 경계 밖; 공개된 사고 데이터가 손실의 큰 비중을 이것에 귀속시키므로 기록함 | — | 범위 밖 | — |

스물세 개 클래스 중 열아홉 개는 이름 있는 가드로 답한다. 네 개는 그 이유가 기록된 채 범위 밖이다. 카탈로그는 자산 폐포(`assets.json`)에 의해 반대 방향에서 교차 검증된다: 시스템이 보유하거나 결정하는 여섯 가지 — 이동 중인 자금, 위임된 허용량, 프로토콜 수수료, 레지스트리 무결성, 방치된 잔액, 호출자 가스 — 를 무언가를 잃을 수 있는 다섯 가지 방식과 교차한 서른 개의 셀이며, 각 셀은 위협 클래스, 범위 밖 근거, 또는 명시적인 *미해결(open)* 중 하나로 귀결된다. 열아홉 개 셀은 커버되고, 세 개는 범위 밖이며, 다섯 개는 해당 없음이고, 세 개는 미해결이며 그렇게 공개된다: 수수료의 표시 단위를 이용한 그리핑, 회수를 비경제적으로 만들기 위해 먼지를 방치하는 것, 그리고 호출자가 이름 있는 거부 대신 도달 가능한 세 가지 컴파일러 패닉 중 하나를 마주치는 것이다. 미해결 셀 중 어느 것도 다른 누군가의 자금으로 가는 경로가 아니다.

### §14.2 적대적 훅: 세 계층, 가장 저렴한 것부터

훅은 Router가 수행 중인 정산 안에서 실행되는 임의의 코드이며, 순진한 방어 — 훅에게 무엇을 할지 묻는 것 — 는 방어가 아니다. 적대적 훅은 거짓말하거나 재진입하기 때문이다. 대신 세 개의 독립적인 계층이 서 있으며, 가장 저렴하고 가장 확실한 것이 먼저 실행되도록 배열되어 있다. 이들은 델타 변경을 제한하며, 이들이 닿지 못하는 하나의 잔여 위험 — 델타를 건드리지 않는 훅이 자신의 동적 수수료를 덮어쓰는 것(§5.4) — 은 대신 Φ와 호출자의 최솟값으로 제한된다.

**계층 1 — 권한 비트는 주소 안에 있으며, 주소는 거짓말할 수 없다.** V4는 훅의 권한을 그 훅 자신의 컨트랙트 주소의 하위 14비트에 인코딩한다. 그 비트들은 배포 시 CREATE2 솔트에 의해 고정되며, PoolManager 자체가 훅을 호출하기 전에 같은 비트를 검사하므로, 훅은 그 주소가 선언하지 않은 권한을 가질 수 없다. 정확히 두 비트가 훅이 스왑 회계를 수정하도록 허용하며, Router는 어떤 토큰이 움직이기 전에 단 하나의 AND로 그것들을 마스킹한다(`hookAltersDeltas`):

$$
\left(\mathrm{uint160}(\mathrm{hook})\ \&\ \mathtt{0x3FFF}\right)\ \&\ \left((1 \ll 3) \mid (1 \ll 2)\right) \ne 0 \;\;\Longrightarrow\;\; \text{leg refused, } \mathtt{RouterE(9)}
$$
^e18

신뢰할 수 없는 코드로의 호출도 없고, 속일 대상도 없으며, 회피하려면 그 비트가 매니저 자신의 강제와 모순되는 주소가 필요할 것이다. 프록시는 로직의 모든 줄을 교체하더라도 그 주소가 원래 갖지 않았던 비트는 하나도 얻을 수 없다 — 이것이 이 거부가 사영 거리 영(§15.9)에 놓이고 그 아래의 코드해시 고정은 그렇지 않은 이유이다.

**계층 2 — 기본적으로 닫힌 허용 목록, 코드는 고정.** 깨끗한 비트를 가진 훅도 그리핑하거나 가스를 소모시키기 위해 악의적으로 revert할 수 있으므로, V4 풀은 그 훅이 큐레이터에 의해 허용 목록에 올라 있을 때만 라우팅 가능하며, Hub는 승인 시 훅의 코드해시를 고정한다: 이후 런타임이 바뀐 훅은 축출되지 않고 자동으로 일시 정지되며(`isHookLive` false) 재승인된 경우에만 재개된다 — 이 권한은 포기 후에는 코드가 바뀐 어떤 훅에 대해서도 거부된다. 고정의 한계는 그 지점에 명시되어 있다: 고정은 런타임 코드를 구속하므로 재배포나 직접 변이는 잡아내지만, 자신의 런타임은 바이트 단위로 동일하게 유지한 채 구현이 교체되는 위임 프록시는 잡아내지 못한다. EVM은 컨트랙트가 다른 컨트랙트의 스토리지를 읽을 방법을 주지 않기 때문이다. 업그레이드 가능한 훅을 승인하는 것은 이 고정이 대신할 수 없는 사람의 판단이다. 이 고정에 의존하지 않는 계층들은 그와 무관하게 유지된다.

**계층 3 — 경로는 아예 보이지도 않는다.** 사영자 Ξ는 순위를 매기기 전에 델타를 변경하는 훅을 가진 모든 풀을 Solver의 후보 집합에서 제거하며(`_topKPools`), Hub의 읽기 채널은 활성 상태인 훅만 제공한다. 승인될 수 없는 훅을 건드리는 경로는 최대화에 참여하지 않는다. 경로 형태 규칙이 이를 완성한다: 경로 전체에 걸친 정규 순서 — 훅이 실행된 뒤에는 훅 없는 레그가 결코 실행되지 않는다(§11.4).

![FIG-11 — 적대적 훅에 맞서는 세 계층, 가장 저렴한 것부터: 주소 비트 마스크(신뢰할 수 없는 코드로의 호출 없음), 코드해시 고정이 있는 허용 목록(기본적으로 닫힘), 사영자 Ξ(경로를 표현할 수 없음). 각 계층은 서로 다른 능력에 대응하며, 잔여 위험 — 가스를 낭비시키기 위해 revert하는, 깨끗하고 목록에 오른 훅 — 은 명시되어 있다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-11.png)

이 스택을 무력화하려면 델타 비트가 없고, 허용 목록에 변경 없이 올라 있으면서, 그럼에도 정산을 전복하는 훅이 필요하다 — 델타 변경에 대해서는 계층 1이 V4 자체의 불변 강제에 기대므로 이 조합은 배제된다. 수수료 덮어쓰기 잔여 위험(§5.4)은 남아 있으며, 하한이 이를 제한한다. 잔여 위험은 표에 명시되어 있다: 허용 목록에 오른, 델타를 건드리지 않는 훅은 여전히 스왑 도중 revert하여 시도에 든 가스를 낭비시킬 수 있다. 그리고 체(sieve)의 단일 레버도 명명된다: 체로 걸러진 훅이 실행된 스왑을 구속하는 것은 정규 PoolManager가 unlock 데이터를 그대로 되돌려 주고 자신의 unlock 호출자에게만 콜백하기 때문이다. 제어 권한인 Hub의 `setV4Manager`가 그 가정이 기대는 유일한 주소이며, 부정직한 매니저는 측정된 출력을 붕괴시켜 호출자 자신의 경계가 스왑을 거부하게 만든다(`HostileV4Manager` 캠페인) — 없는 것이 아니라 제한된 것이다.

### §14.3 제거가 아닌 제한: 오래됨, 순서, 그리고 샌드위치 곡선

견적은 과거에 대한 진술이고, 거래는 미래에 일어난다. 프레임 내 해결 문은 그 간극의 운영자 측 절반을 완전히 닫는다 — 경로는 그것을 실행하는 트랜잭션 안에서, 그 블록에 있는 그대로의 준비금으로부터 도출된다. 남는 것은 체인 자신의 절반이다: 당신이 서명하는 순간과 당신의 트랜잭션이 포함되는 순간 사이에 풀이 움직일 수 있다. 순서는 코드가 실행되기 전에 결정되므로, 누구도 컨트랙트 안에서 이를 막을 수 없다. 프런트러닝을 제거했다고 주장하는 프로토콜은 당신의 거래를 공개 대기열 밖으로 옮기고 이제 그것을 쥔 누군가를 신뢰하고 있거나, 들리는 것보다 좁은 무언가를 묘사하고 있는 것이다. 이 프로토콜이 하는 일은 이를 두 번, 독립적으로 제한하는 것이다: 당신의 최솟값은 피해를 당신이 고른 숫자로 상한 짓고, Iron-Law 하한 — 공격자나 경로가 제공한 어떤 것도 아닌, 측정된 영향과 마지막 홉의 프레임 내 견적으로부터 Router가 재도출하는 하한 — 은 프로토콜 자신의 책임으로 다시 상한을 건다.

이 경계는 공격자의 측에서 측정된다(`test/regime/SandwichCurve.t.sol`): 피해자의 경로와 하한은 대기 중인 트랜잭션에서처럼 견적 시점에 고정된다. 공격자는 피해자보다 먼저 풀 깊이의 일부를 거래하고, 피해자가 실행하며, 공격자가 되돌려 거래한다. 베뉴는 상수곱 쌍 — 모든 샌드위치 모델이 사용하는 형태 — 이므로 곡선은 하한의 속성이다. 깊이의 1 %인 거래(한쪽당 1,000,000에 대해 10,000)에서:

| 공격자가 움직이는 양 | 피해자 | 견적 대비 피해자의 손실 | 공격자의 왕복 손익 |
|---|---|---|---|
| 깊이의 0.1 % | 정산됨 | 0.47 % | +13.9 |
| 0.5 % | 정산됨 | 1.26 % | +68.9 |
| 1 % | 정산됨 | 2.22 % | +136.7 |
| 2 % | 정산됨 | 4.12 % | +268.7 |
| 3 % 이상 | **거부됨** | 0 | −174.5 … −544.9 |

![FIG-12 — 공격자 측에서 본 샌드위치 곡선: 피해자보다 먼저 움직인 깊이의 비율별로 나타낸 견적 대비 피해자의 손실과 공격자의 왕복 손익; 3 % 근처의 거부 경계는 위쪽으로 닫혀 있다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-12.png)

모든 지점에서 단언된 보장은 다음과 같다: 정산된 피해자는 견적 시점에 증명된 하한보다 적게 받는 일이 결코 없으므로, 손실은 증명된 견적과 증명된 하한 사이의 거리로 제한된다. 그리고 거부 영역은 위쪽으로 닫혀 있다 — 경계를 넘으면 더 큰 모든 조작이 거부되고 공격자는 자신이 움직인 가격을 떠안게 된다. 인용할 가치가 있는 숫자는 마지막 정산 행이다: 하한은 샌드위치가 1 % 거래에서 가져갈 수 있는 것을 그 거래의 약 2.7 %로 상한 짓고, 그 이상을 가져가려는 순간 공격자의 거래를 손실로 바꾼다. 프레임 내 경로는 더 조용한 억지력을 더한다: 서처가 연구할 사전 공개된 경로가 없다. 경로는 그것을 실행하는 블록 전까지 존재하지 않기 때문이다. 느슨한 최솟값은 초대장이다. 엄격한 최솟값은 이 절에서 당신에게 속한 가장 효과적인 단 하나의 통제 수단이다.

### §14.4 적대적 베뉴

베뉴의 병리에는 별도의 행렬이 있다(§15.5): 열 가지 오작동 — 아무것도 지급하지 않는 풀, 절반만 지급하는 풀, 64 KiB returndata 폭탄을 반환하는 풀, 읽기나 스왑에서 모든 가스를 소모하는 풀, `decimals()`가 결코 반환하지 않는 토큰, 두 번 발동하는 지불 콜백, 지불 전에 Router에 재진입하는 풀, revert하는 `slot0()`, 다른 토큰들의 풀로 답하는 팩토리 — 를 calldata 문 및 해결 문과 교차하면, 20 중 20 셀이 전달 금액이 잔액 델타와 같고 Router에 아무것도 남지 않은 채 정산되거나, 우리의 셀렉터로 거부된다. 전달된 모든 가스를 소모하는 스왑은 어떤 호출자도 피호출자를 대신해 결정할 수 없는 유일한 셀이다. 트랜잭션은 통째로 revert되고 사용자의 잔액은 건드려지지 않는다. Core가 베뉴에 대해 수행하는 모든 보호된 읽기는 `GAS_CAP = 100,000` 가스로 상한이 걸리므로, *읽기*에서 가스를 소모하는 베뉴는 제한된 양의 비용만 들고 제외될 뿐 치명적이지 않다.

### §14.5 적대적 프런트엔드

지금까지 만들어진 모든 애그리게이터에서 최솟값은 인터페이스가 제안하며, 부주의한 인터페이스는 그것을 잘못 제안한다 — 어떤 컨트랙트도 당신이 무엇을 골랐을지 알 수 없다. 컨트랙트가 할 수 있는 것은 그 제안을 증명 가능하게 만드는 것이다: 모든 스왑은 `ExecutionProof` — 사용된 견적, 전달된 금액, 적용된 하한 — 를 영구적으로 온체인에 방출하므로, 어떤 인터페이스의 실행 품질이든 당신을 포함해 누구나 감사할 수 있는 트랜잭션별 공개 기록이 된다. 일관되게 나쁜 최솟값을 제안하는 인터페이스는 자신의 기소장을 이벤트 로그에 스스로 써넣는다. 이 메커니즘을 완성하는 방어는 습관이다: 서명하기 전에 최솟값을 보라. 그것은 전적으로 당신의 것인 단 하나의 필드이다.

### §14.6 적대적 키가 할 수 있는 것과 할 수 없는 것

포기 이전에는 관리 키가 존재하며, 이를 정직하게 기술하는 방법은 보유자가 적대적이라고 가정하고 능력으로 기술하는 것이다. 권한의 전체 열거는 §16.4 참조.

**적대적 Router 키가 할 수 있는 것:** 수수료 목적지를 바꾸기(`setTreasuries`); 스왑을 일시 정지하기(`setPaused`); 네이티브 문과 Permit2 문이 호출하는 WETH 및 Permit2 컨트랙트를 다시 지정하기(`setWeth`, `setPermit2`) — 이로 인해 그 두 문은 설정된 주소만큼만 신뢰할 수 있게 된다; Router에 방치된 토큰의 구출을 대기열에 넣고 48시간 뒤 실행하기(`queueRescue`, `executeRescue`, `RESCUE_DELAY = 48 hours`, `RescueQueued`로 온체인 공지). **할 수 없는 것:** 수수료 요율, 분할, 하한을 움직이기 — 이들은 setter가 없는 컴파일 타임 상수이기 때문이다; 원금을 가져가기 — 프록시도, 업그레이드 경로도, `selfdestruct`도, Router에 대한 사용자 자금의 상시 승인도 없으며, Router는 정지 상태에서 아무것도 보유하지 않기 때문이다. 그런 일을 할 함수들은 비활성화된 것이 아니라 아예 존재하지 않는다.

**적대적 Hub 키가 할 수 있는 것:** 베뉴나 훅을 등재하기(`addFactory`, `allowHook`), 풀을 시드하기(`seedPool`, 운영자 자리를 통해), 레지스트리의 학습을 일시 정지하기(`setPaused`), Router, Solver, Quoter에 대한 Hub의 시야를 다시 지정하기(`setRoles`), V4 매니저를 다시 지정하기(`setV4Manager`, 체의 단일 레버). **할 수 없는 것:** 체결 가격을 잘못 매기기 — 레지스트리는 무엇이 보이는지만 결정하기 때문이다(§9.6). 적대적 등재가 도달하는 가장 날카로운 것은 견적과 *당신의 최솟값* 사이의 간극이다. 원금은 손이 닿지 않지만 당신의 슬리피지 허용치는 그렇지 않으며, 이것이 최솟값을 당신이 엄격하게 설정해야 하는 또 하나의 이유이다.

### §14.7 일방통행 문

관리 권한은 약속이 아니라 바이트코드 안에서, 되돌릴 수 없게 끝난다. Hub와 Router는 모두 `renounceControl`을 노출하며, 각각은 일시 정지 중에 호출되는 것을 거부한다(`RouterE(2)`, `HubE(2)`): 일시 정지된 뒤 포기된 프로토콜은 아무도 원하지 않고 아무도 벗어날 수 없는 종착 상태가 될 것이며, 일시 정지가 할 가치가 있는 것은 바로 행동하기 위해 제어권이 유지되기 때문이다. 포기 플래그는 각 컨트랙트에서 정확히 한 지점에서 `true`로 쓰이고 어느 지점에서도 `false`로 쓰이지 않으므로, 배포된 바이트코드의 어떤 함수도 그 문을 다시 열 수 없다 — "하지 않겠다"와 "할 수 없다"의 차이이다.

무엇이 닫히고 무엇이 열린 채 남는지는 요약하지 않고 열거한다. Router에서는 `onlyControl` 아래의 모든 것이 소멸한다: `setAdmin`, `setTreasuries`, `setPermit2`, `setWeth`, `setPaused`, 구출 경로 — 트레저리, 배선, 일시 정지 플래그는 현재 값에서 영구히 동결되며, Router는 그 고정된 구성 아래에서 스왑을 계속 실행한다. Hub에서는 **제어 계층**이 소멸한다: `setRoles`, `setOperator`, `setPaused`, `setV4Manager`, `removeBridge`, 그리고 훅 *등재 해제*. **큐레이터 계층**은 살아남으며, 레지스트리를 키우기만 한다: `addFactory`(포기 후에는 런타임이 바뀐 팩토리나 질의 행이 될 도출 행을 거부), `addBridge`(추가 전용, 최대 세 개, 멱등), `allowHook(h, true)`(코드가 바뀐 등재 훅을 거부). 기존 운영자 자리는 `seedPool`과 `addV4`를 유지하며, 새 자리는 부여될 수 없다. 포기 후의 악의적 등재는 자금을 빼낼 수 없다: 풀은 견적 시와 실행 시에 검증되며 하한과 호출자의 최솟값으로 제한된다. 레지스트리는 거래를 통해 계속 학습한다. 이 프로토콜에 대해 "포기되었다"를 되풀이하는 사람은 어떤 문이 열린 채 남는지 — 승인, 브리지, 훅 등재, 큐레이션된 시딩 — 그리고 어떤 문이 닫히는지 — 방향을 바꾸거나 동결하는 모든 권한 — 말할 수 있어야 한다.

### §14.8 이 모델이 당신에게 요구하는 것

프로토콜은 컨트랙트가 닫을 수 있는 것을 닫는다: 주소 위조, 콜백 스푸핑, 회계 위조, 적대적 훅, 산술. 어떤 컨트랙트도 제거할 수 없는 것은 제한한다: 오래됨과 순서를, 당신의 최솟값과 하한으로. 그리고 제한조차 할 수 없는 것에 대해서는 증거를 공개한다: 당신을 대신해 나쁜 최솟값을 제안하는 프런트엔드. 모든 행은 같은 필드로 수렴한다. 최솟값은 매번, 신중하게, 스스로 설정하라 — 그것은 이 절에서 그 품질이 당신 이외의 누구의 역량에도 의존하지 않는 단 하나의 보호 장치이다.

## §15 측정된 검증 장치

*쉽게 말하면:* 여기의 모든 수치는 그것이 측정된 분모와 나란히 제시되고, 스크립트 또는 이름이 명시된 `forge` 명령(부록 C)으로 깨끗한 체크아웃에서 다시 계산되며, 그 수치가 입증하지 않는 것이 뒤따른다. *정확히 말하면:* 측정 대상 트리는 `8949a9d` 시점의 `main`과 그것이 된 브랜치이며, 릴리스 프로필(`optimizer_runs = 300`, `via_ir`)에서 2026-09-05에 측정되었다.

### §15.1 수치로 본 테스트 스위트

| 항목 | 개수 | 계산 방법 |
|---|---|---|
| 선언된 테스트 | 236개 `.t.sol` 파일에 걸친 1,609개의 `test*` / `invariant*` / `check*` 함수 | `test/`에 대한 `grep` |
| 릴리스 프로필에서 통과, 포크 스위트 제외 | 1,320개 중 1,319개 통과 · 0개 실패 · 1개 건너뜀 | `forge test --no-match-path 'test/fork/**'` |
| 실제 유동성에서 통과 | 다섯 개 체인, 포크 레인의 39개 스위트에서 119개 중 119개; 여기에 실제 Base 테스트 세 개(§12) 추가 | 아카이브 RPC를 사용한 `forge test --match-path 'test/fork/**'` |
| 상태 기반 불변식 | 14개 캠페인의 43개 `invariant_*` 함수 | `grep -rc 'function invariant_' test` |
| 선별된 뮤턴트, 모두 제거됨 | 268개 중 268개 | `.github/scripts/mutants.py`; `check_targets.py` |
| 레짐 커버링 배열 | 강도 2: 63행, 258개 쌍 전부 · 강도 3: 168행, 1,636개 삼중항 전부 · 전체 5,184개 조합 중 | `covering_array.py --check` |
| 출시 크기(런타임 바이트; EIP-170 한도 24,576; 프로젝트 게이트 24,000) | Hub 23,648 · Router 23,781 · Solver 19,686 · Quoter 11,429 · Core 6,442 | `FOUNDRY_PROFILE=release forge build --sizes` |

### §15.2 증거 사슬과 선별된 변이 가드

하나의 속성은 위협에서 그 속성이 깨지면 실패할 무언가에 이르는 가장 짧은 경로만큼만 유효하다: `threat → property → guard → test → mutant`. 가드는 심볼로, 테스트는 이름으로, 뮤턴트는 그것이 바꾸는 정확한 줄로 지정된다. 이 셋 중 하나라도 트리에서 사라지면 빌드가 실패한다. 스위트의 모든 회귀 테스트는 그 이름이 가리키는 이유로 한 번 실패하는 것이 확인되었다 — 수정은 그 수정이 없는 코드에 대해 빨간색이었던 테스트와 함께 도착한다.

변이 가드는 손으로 작성한 268개의 뮤턴트를 보유하며, 각 뮤턴트는 소스의 정확한 한 줄을, 그 줄이 바뀌면 반드시 실패해야 하는 단 하나의 테스트와 짝짓는다 — 삭제된 가드, 거부를 결정하는 경계에서 뒤집힌 비교, 넓어진 권한 부여, 이웃 오류 코드와 뒤바뀐 오류 코드. 세 가지 속성이 이것을 점수가 아닌 가드로 만든다: 짝지어진 테스트를 먼저 변이되지 않은 트리에서 통과시킨다; 옵티마이저가 제거하는 변이 — 런타임 바이트코드가 동일한 변이 — 는 제거된 것이 아니라 *비활성*으로 보고된다; 그리고 일 초짜리 정적 검사가 모든 뮤턴트가 여전히 정확히 한 줄을 가리키는지 확인한다. 203개 중 203개가 제거되었다. 이 수치는 *이* 목록에 대한 적정성이다: 포화된 점수는 상한이 아니라 하한이다.

### §15.3 불변식을 겨냥한 뮤턴트

2026-09-05까지 선별된 뮤턴트 중 어느 것도 상태 기반 불변식을 자신에 의해 실패해야 하는 테스트로 지정하지 않았다. 14개 캠페인에 걸친 43개의 `invariant_*` 함수는 통과 상태였고, 그중 어느 것이 실패할 수 있는지 묻는 것은 아무것도 없었다: 속성이 보호하는 상태에 핸들러가 결코 도달하지 않는 캠페인은 그 속성을 빈 우주 위에서 인증하는 것이며, 바깥에서 보면 그 상태에 도달하는 캠페인과 똑같아 보인다. 측정은 가드의 것을 캠페인에 겨눈 것이다: 소스에서 가드 하나를 바꾸고, 하나의 시드로 모든 불변식을 실행하여, 어느 것이 알아차리는지 기록한다(`docs/assurance/invariant-mutants.json`).

| 뮤턴트 | 제거하는 것 | 알아차린 불변식 |
|---|---|---|
| 최종 전송이 한 wei 덜 지급한다 | 아무것도 보유하지 않음 | `RouterHoldsNothing`, `routerHoldsNothing`, `HoldsNothingBeyondTheSeed` |
| 프로토콜 수수료가 두 배가 된다 | 수수료 상한 | `FeeNeverExceedsProtocolMax` |
| 입력 측 수수료가 전혀 부과되지 않는다 | 수수료 하한 | `FeeNeverEscapes`, `DeliveredNeverBelowUserMinOut` |
| 수수료가 양쪽 모두에 부과된다 | 하나의 수수료, 한쪽 | `FeeIsChargedOnExactlyOneSide` |
| 모든 수수료의 30 %가 소각 주소로 간다 | 보존 | `conservationA/B/C/H`, `TokenConservation`, `LedgerConservationA` |
| 레그-페어 가드가 잘못된 페어의 레그를 허용한다 | 동질적인 홉 | `DivergentLegNeverSettles`, `HoldsNothingBeyondTheSeed`, `StrandedMoneyIsNeverSwept` |
| `whenLive`가 더 이상 `paused`를 확인하지 않는다 | 일시 정지 | `PausedRouterNeverSettles` |
| 재진입 락이 더 이상 거부하지 않는다 | 락 | `reentrancyBlocked` |
| 프로토콜 하한이 절반이 된다 | 하한 | `DeliveredNeverBelowTheProtocolFloor` — 2026-09-05에 해소 |
| `MAX_SLOTS`가 17이 된다 | 레지스트리 상한 | `neverExceedsMaxSlots` |
| 등록 페어 증명이 제거된다 | 페어 진정성 | `ActiveEntriesTradeThePair` — 2026-09-05에 해소 |
| 입력 잔여분 스윕이 기준선을 무시한다 | 묶인 자금 | `StrandedMoneyIsNeverSwept`, `HoldsNothingBeyondTheSeed` |
| 브리지 잔여분 스윕이 기준선을 무시한다 | 묶인 자금 | 같은 쌍 — 2026-09-05에 해소 |
| 출력 측정이 기준선을 무시한다 | 묶인 자금 | 같은 쌍 |
| 수수료 차감 후 `userMinOut` 검사가 절반이 된다 | — | 설계상 생존: 전송 시 수수료가 붙는 출력 토큰이 없는 캠페인 우주에서는 도달할 수 없는 회귀 감시자이며, 두 개의 단위 뮤턴트가 이를 감시한다 |

![FIG-13 — 불변식을 겨냥한 뮤턴트: 마흔세 개의 상태 기반 불변식에 대한 열다섯 개의 소스 변이, 뮤턴트별로 실패한 불변식, 캠페인에 부족했던 동작을 부여하여 2026-09-05에 해소된 세 건, 그리고 설계상 생존하는 하나의 감시자.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-13.png)

열다섯 개 중 열한 개가 첫 측정에서 감지되었다; 서로 다른 스무 개의 불변식 이름이 적어도 한 번 실패했다. 네 개의 생존자 중 세 개는 캠페인에 부족했던 동작을 부여함으로써 같은 날 해소되었다 — 해당 페어 아래 제시된 외부 풀, 미리 시딩된 중간 잔액 위의 두 홉 경로, 오래된 경로가 실행되기 전에 견적된 경로에 반하여 풀을 0–8 % 움직이는 고래 — 각각은 가드가 없을 때 실패하는 것이 확인되었다. 어떤 종류의 감시자도 없는 가드 두 개가 이 측정이 출력하기 위해 존재했던 숫자였고, 그것은 이제 영이다. 열다섯 개 중 열네 개가 감지된다; 그 열네 개는 자신에 의해 실패하는 불변식과 짝지어진 가드 항목이다. 감시되지 않는 가드를 포함한 통과 스위트는 이 코드베이스에서 문서화된 두 회귀 모두의 형태였다; 그런 가드를 찾아내는 측정은 이제 캠페인이 주장하는 모든 가드에 대해 실행된다.

### §15.4 레짐 커버링 배열

커버리지 기준은 코드를 색인하고, 변이는 주입된 결함을 색인한다; 어느 쪽도 호출 전에 *픽스처*가 고정하는 상태를 색인하지 않는다. 이러한 요인 열 가지가 열거된다 — 베뉴 계열(V2, V3, Solidly), 홉 수(1–3), 홉당 레그 수(1–2), 입력 토큰이 등록된 브리지인지 여부, 중간 토큰이 그러한지 여부, 전송 시 수수료의 형태(없음, pull 전용, 모든 전송), 입력 토큰의 소수 자릿수(18, 6), 진입 문(calldata 경로, 트랜잭션 내 풀이, Permit2), 제어(활성, 포기됨), 그리고 페어가 가득 찼는지 여부 — 5,184개 조합이다. `covering_array.py`는 요인 값의 모든 쌍(강도 2) 또는 모든 삼중항(강도 3)이 적어도 한 행에 나타나는 배열을 생성하며, 각 행은 하나의 하네스(`test/regime/RegimeHarness.sol`)를 통과하는 하나의 픽스처이고 단 하나의 단언을 갖는다: 스왑이 정산되되, 전달된 금액이 수신자의 잔액 변화량과 같고, Router가 발행한 하한 이상이며, Router에 아무것도 남지 않아야 한다 — 그렇지 않으면 우리 쪽 셀렉터로 거부되어야 한다. 패닉, 외부 셀렉터, 과소 전달 또는 묶인 잔액은 세 번째 경로이며 해당 행을 실패시킨다.

| 강도 | 행 수 | 충족된 튜플 | 정산됨 | 거부됨, 우리 쪽 | 구성 불가 | 세 번째 경로 |
|---|---|---|---|---|---|---|
| 2 | 63 | 258개 쌍 | 53 | 4 | 6 | 0 |
| 3 | 168 | 1,636개 삼중항 | 158 | 10 — `SolverE(5)` ×7, `RouterE(13)` ×3 | 0 | 0 |

![FIG-14 — 레짐 커버링 배열: 열 개 요인의 5,184개 조합에 걸쳐 강도 2(63행)와 강도 3(168행)에서 생성된 모든 행의 결과 — 정산됨, 우리 쪽 셀렉터로 거부됨, 구성 불가, 세 번째 경로.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-14.png)

거부는 플래너가 구성할 브리지 경로가 없는 경우와, 집중 유동성 전용 경로에서 실행기가 과세 토큰을 거절하는 경우이다; 구성할 수 없는 강도 2의 여섯 행(pull 전용 과세 토큰에는 소수 여섯 자리 형태가 없다)은 이름과 함께 출력되며 분모에 포함된다. 배열에 포함되지 않은 계열 — V4, 네이티브 V4, Algebra, 네이티브 진입 문, 훅 — 은 `regimes-covering.json`에 명시되어 있다. 배열의 첫 실행은 패리티 테스트가 한쪽에서만 고정해 두었던 프레임을 명시적으로 드러냈다: Router가 강제하는 하한은 수수료가 출력에서 차감될 때 증명된 하한과 같고, 입력에서 차감될 때는 $[\mathrm{attested} \times (1 - \mathrm{fee}), \mathrm{attested}]$ 안에 놓인다 — 두 프레임 모두 이제 모든 행에서 단언된다. 생성된 파일은 CI에서 생성기와 대조 검사되므로, 배열은 그것이 포괄한다고 주장하는 요인에서 벗어날 수 없다.

### §15.5 적대적 베뉴 행렬

토큰의 병리 현상에는 테스트가 있었지만, 베뉴의 병리 현상에는 행렬로 된 테스트가 없었다. `test/regime/HostileVenues.sol`은 베뉴마다 하나의 오작동을 담고 있으며, `HostileVenueMatrix.t.sol`은 커버링 배열의 규칙에 따라 각각을 calldata 진입 문 및 풀이 진입 문과 교차시킨다:

| 베뉴 | calldata 진입 문 | 풀이 진입 문 |
|---|---|---|
| 아무것도 지급하지 않음 / 절반만 지급 | `RouterE(5)`로 거부 | `RouterE(5)`로 거부 |
| 준비금 읽기에서 returndata 폭탄 | 정산됨 | 정산됨 |
| 준비금 읽기가 모든 가스를 소모 | `RouterE(8)`로 거부 | 플래너가 결코 선택하지 않음 |
| 스왑이 모든 가스를 소모 | 트랜잭션 전체가 되돌려지고 잔액은 그대로 | 트랜잭션 전체가 되돌려지고 잔액은 그대로 |
| `decimals()`가 모든 가스를 소모 | 정산됨 | 정산됨 |
| 지급 콜백이 두 번 호출됨 | `RouterE(6)`로 거부 | `RouterE(6)`로 거부 |
| 지급 전에 Router에 재진입 | 정산됨; 중첩된 스왑은 실행되지 않음 | 정산됨; 중첩된 스왑은 실행되지 않음 |
| `slot0()`가 되돌려짐 | 증명된 견적으로 정산됨 | 플래너가 결코 선택하지 않음 |
| 팩토리가 다른 토큰의 풀로 응답 | 결코 목록에 오르지 않음 | 결코 목록에 오르지 않음 |

스무 개 셀 중 스무 개: 정산되거나 우리 쪽 셀렉터로 거부되며, 세 번째 경로는 결코 없다. 마지막 행은 행렬의 첫 실행이 바꾼 것이다 — 탐색이 큐레이터가 승인한 팩토리가 응답한 풀을 목록에 올렸고, 실행기는 지급하는 접합부에서 그것을 거부했다; 이제 조회된 풀은 탐색이 목록에 올리기 전에 자신의 페어를 스스로 증명한다(§7).

### §15.6 정규 오라클

스위트의 모든 목(mock)은 Core 자신의 공식으로 견적을 내므로, 공식의 결함은 그 목을 사용하는 모든 패리티 테스트에서 보이지 않는다: 오라클이 곧 대상이다. `test/regime/CanonicalOracles.t.sol`은 베뉴들이 공개한 불변식으로부터 작성된 세 가지 구현을 담고 있다 — 입력에 수수료가 붙는 상수곱, 풀 자체의 트레이더 불리 방향 반올림을 따르는 V3 단일 틱 제곱근 가격 스텝, 뉴턴 방법으로 푼 Solidly 스테이블 곡선 — 그리고 Core를 이들에 대해 각각 5,000회 퍼즈하며, 먼저 방향(Core는 베뉴의 수학이 전달하는 것보다 더 많이 약속하지 않는다)을, 그다음 엄밀성을 단언한다.

| 계열 | 방향 | 측정된 엄밀성 |
|---|---|---|
| Uniswap V2 | Core는 명세를 결코 초과하지 않는다 | wei 단위까지 정확 |
| Solidly 스테이블 | Core는 솔버 자신의 마지막 스텝보다 더 많이 곡선을 초과하지 않는다 | 4 wei 이내 |
| Uniswap V3 | Core는 제곱근 가격의 마지막 자리 한 단위(ulp), 즉 $L / 2^{96}$ wei에 해당하는 양보다 더 많이 명세를 초과하지 않는다 | $L < 2^{96}$인 모든 풀에서 한 wei 미만 |

V3 경계는 그것을 유발하는 양으로 표현된다 — 풀은 새 가격을 트레이더에게 불리하게 반올림하고 Core는 이를 한 번 반올림하므로, 둘은 $\sqrt{P}$의 한 단위만큼 다를 수 있다 — 그리고 단언되는 것은 표본이 아니라 경계이다($L = 10^{30}$에서 $6.5 \times 10^{34}$ 출력에 대해 13 wei). 오라클은 베뉴의 바이트코드가 아니라 명세로부터 작성되었다; 그것이 오라클의 한계이다.

### §15.7 변성 관계

같은 공식으로 작성된 오라클은 그 공식을 잡아낼 수 없다. 변성 관계는 대신 입력이 움직일 때 출력이 어떻게 *움직여야* 하는지를 묻고, 베뉴 자신의 곡선은 참조 구현 없이도 그에 답한다. `test/CoreMetamorphicRelations.t.sol`은 Core의 견적 수학에 대한 열네 개의 관계를, `test/RouteMetamorphicRelations.t.sol`은 실제 Hub와 실제 Solver를 갖춘 두 풀 우주에서 Solver의 계획에 대한 다섯 개의 관계를 담고 있다:

| 수준 | 관계 | 의미 |
|---|---|---|
| Core, V2 · V3 · 스테이블 | MR1 단조성 | 더 많이 넣으면, 결코 더 적게 나오지 않는다 |
| Core, V2 · V3 · 스테이블 | MR2 준가법성 | 같은 풀에서 주문을 나누어도 결코 이득이 없다 |
| Core, V2 · 스테이블 | MR3 왕복 이득 없음 | 넣은 뒤, 갱신된 준비금으로 다시 되돌리면, 넣은 것보다 더 많이 돌려받지 못한다 |
| Core, V2 · V3 · 스테이블 | MR4 규모 등변성 | 주문과 풀을 함께 확대하면 출력도 반올림 범위 내에서 확대된다 |
| Core, V2 · V3 | MR5 수수료 단조성 | 더 높은 수수료가 더 많이 지급하는 일은 결코 없다 |
| Core, V3 | MR6 방향 대칭성 | 가격 1.0에서 `outV3`의 두 분기는 두 ulp 이내로 일치한다 |
| Core, Solidly | MR7 동일성 | 변동성 분기는 *곧* `outV2`이며, 생산자는 하나이다 |
| 계획 | MR-R1 | 분할은 최선의 단일 풀보다 결코 적게 지급하지 않는다 |
| 계획 | MR-R2 | `amountIn`에 대해 단조적이다 |
| 계획 | MR-R3 | 가격 대역 안에 풀을 추가해도 계획이 결코 낮아지지 않는다 |
| 계획 | MR-R4 | 등록 순서는 계획을 분할의 가중치 한 단위 이하로만 움직인다 |
| 계획 | MR-R5 | 증명된 하한은 기대 출력을 결코 초과하지 않는다 |

경계 중 세 가지는 작성되기 전에 측정되었으며, 단언에 들어간 숫자는 표본이 아니라 메커니즘이다. MR6: $L = 8.5 \times 10^{37}$이고 입력이 1,374 wei일 때, 한 분기는 1,073,741,823 wei를 견적했다 — 정확히 $L / 2^{96}$, 즉 한 ulp — 반면 다른 분기는 영을 견적했는데, 그 스텝이 반올림되어 아무것도 남지 않아 닫힌 쪽으로 실패한 것이다; 관계는 분기당 하나씩 두 ulp를 명시한다. MR-R1과 MR-R3: 한 풀의 가격이 다른 풀과 $10^{10}$만큼 떨어져 있을 때, 계획은 이상치 풀 단독보다 35 % 적게 지급했다 — 깊이 가중 중앙값이 그것을 믿기를 거부한 것이다 — 따라서 관계는 대역이 허용하는 풀에 대해서만 성립하며, 정의역은 두 가격을 ±4 % 이내로 유지한다. MR-R4: 깊이 가중치가 최솟값으로 내림되는 풀은 먼저 등록되면 먼지 레그로 유지되고 두 번째로 등록되면 잘려 나가며, 계획을 출력의 $3 \times 10^{-7}$만큼 움직인다 — 최소 가중치 풀에 대한 분할의 유지 또는 절단은 그 위치에 의존하며, 이 사실은 이제 발견된 곳에 기록되어 있다.

### §15.8 N-버전: 다른 코드 생성 아래의 같은 소스

스위트는 하나의 바이너리를 실행한다 — 릴리스 설정이 내보내는 바이너리이며, `profile_parity.py`가 스위트를 그 설정에 묶어 둔다. 다른 모든 옵티마이저 설정은 같은 소스에서 컴파일된 다른 프로그램이며, 컴파일러는 하나의 구성 요소이다. `test/nversion/`은 Core의 견적 수학을 두 개의 추가 프로필 — `optimizer_runs` 1과 20,000, 나머지는 모두 동일 — 아래에서 컴파일하고, `NVersionCore.t.sol`은 세 바이너리를 나란히 배포하여 퍼즈된 입력에 대해 동일성을 단언한다: `outV2`, `outV3`, `outSolidly`, `outSolidlyStable`, `mulDiv`, `mulDivUp`, 전체 입력 정의역에 걸쳐 각각 3,000회. 이 레인은 먼저 대체 바이트코드가 출시된 것과 *다르다*는 것을 단언하므로, 프로그램을 자기 자신과 비교해서 통과할 수 없으며, 누락된 아티팩트는 건너뜀이 아니라 실패이다.

**세 바이너리는 일치한다.** 발견된 것은 컴파일러가 아니었다. 레인의 첫 전체 정의역 실행은 *참조* 구현을 되돌리게 만들었다: 스테이블 곡선의 적합성 검사는 `max · WAD / b`를 나누었는데, 한 토큰을 한 단위 미만 보유한 풀에서 그 나눗셈 자체가 오버플로되었다; 두 번째 탈출 — 먼지 수준의 출력 준비금에 맞선 고래 규모의 입력 준비금 — 은 불변식 검사를 통과하고 도함수를 오버플로시켰다. 어느 것도 잘못된 숫자는 아니었다; 둘 다 견적 내부의 되돌림이었고, `universalQuote`는 라이브러리 `DELEGATECALL`이므로, 출력 토큰을 한 wei 보유한 등록된 풀 하나가 해당 페어에 대한 Solver의 계획을 무너뜨렸다. 수정(§6)은 그 나눗셈이 대신하던 질문을 직접 묻으며, 빨간색 테스트를 먼저 두고 들어갔다; 추가 가드 — 뉴턴 반복값에 대한 상한 — 도 작성되었으나, 두 검사가 성립하면 어떤 테스트로도 관찰할 수 없음이 밝혀져 유지하는 대신 제거되었다. 그 부류는 이 저장소가 가장 먼저 명명하는 것 — 열린 쪽으로의 실패(fail-open) — 이며, 그것을 찾고 있지 않던 도구에 의해 발견되었다.

### §15.9 사영 거리

이 저장소에서 확인된 모든 결함은, 내부에서 찾은 것이든 보고된 것이든, 하나의 형태를 가진다: 검사는 한 객체를 관찰하는데 *다른* 객체가 동작을 결정한다. 그렇기에 거부에서 흥미로운 속성은 그 복잡도가 아니라 거리 — 그것이 읽는 것이 결정하는 것으로부터 얼마나 떨어져 있는가 — 이다. `projection_distance.py`는 다섯 컨트랙트의 모든 `revert`를 지키는 술어를 분류한다: 상수, immutable, 잔액 변화량, 또는 그중 하나로부터 프레임 내에서 계산된 양은 거리 0이다; 호출자 경로의 필드, 또는 다른 컨트랙트의 변경 가능한 스토리지 조회는 거리 1이다; 자신의 동작을 위임할 수 있는 컨트랙트의 속성은 무한하다. 99개의 거부 지점에 대해: **거리 0이 86개, 거리 1이 11개, 무한이 2개.** 무한한 두 지점은 모두 `EXTCODEHASH` 고정 — 승인된 훅의 코드와 승인된 팩토리의 코드 — 이며, 둘은 대칭이 아니다: 훅의 경우 잔여 위험은 고정이 담지 않는 주소 비트에 의해 닫힌다(§14.2); 팩토리의 경우 그에 상응하는 것이 없으며, 잔여 위험은 풀 승인, 레그별 하한, 사용자의 최솟값으로 제한되므로, 피해는 자금 유출이 아니라 경로 품질 저하이고, 이는 해당 지점에 문서화되어 있다. 이 선별 방법의 한계: 하나의 술어만 읽으므로 어떤 값이 세 프레임 위에서 제약되었다는 것을 볼 수 없다; 호출자가 선언한 필드이면서 동시에 풀 선택자인 필드는 하나의 대입이 둘을 묶기 때문에만 거리 0이며, 그 대입은 `test/V4SievedHookIsTheExecutedHook.t.sol`에 의해 고정된다.

### §15.10 증거 독립성, 공유 수량 목록, 그리고 아티팩트

하중을 받는 각 가드에 대해 이 장치는 *독립적인* 확인이 몇 개 존재하는지 — 소스, 오라클, 도구, 환경, 방법론별로 — 기록하고 그 최솟값을 보고한다. 증거의 사슬은 가장 약한 고리에서 끊어지기 때문이다; 하중을 받는 속성은 어떤 가설도 공유하지 않는 최소 두 개의 경로로 확인된다. 공유 수량 목록(`SHARED_QUANTITIES.md`)은 둘 이상의 생산자 또는 소비자를 가진 모든 양을 열거하고, 그 양이 답하는 *질문*을 명시하며, 사본들을 묶는 것의 등급을 매긴다 — `SINGLE`, `PINNED`, `WEAK`, `OPEN`, `UNVERIFIED` — 그리고 테스트가 고정한다고 주장하는 대상에 도달하지 못하는 행을 강등하는 CI 규칙이 있다; 관계 목록의 생산자가 둘인 모든 양(7개 중 7개)은 이름이 지정된 테스트로 묶여 있다. 그리고 소스뿐 아니라 컴파일된 아티팩트도 읽는다: 결코 나타나서는 안 되는 옵코드(`SELFDESTRUCT`, `CALLCODE`, `ORIGIN`); 재진입 락이 조용히 영구 스토리지로 바뀌지 않았음을 보여 주는, 실행기에 여전히 존재하는 transient 옵코드; 디스패처에 존재하는 모든 선언된 함수; 스토리지 쓰기를 전혀 내보내지 않는 다섯 컨트랙트 중 세 개; 건전한 하한으로 실행이 증명된 출시 형태 명령어 스트림의 88.3 % — 도구가 수치를 출력하기 전에 올바르게 분류해야 하는 정답 컨트랙트에 대해 검증됨; 그리고 매 단계에서 옵코드를 확인하며 릴리스 Router에서 재생한 기록된 정직한 V2 스왑 하나에 대해, Router 명령어 4,382개와 Hub 명령어 407개가 실행되었고 불일치는 영이다 — 두 코드 섹션의 28.2 %와 2.5 %이며, 나머지는 죽은 코드가 아니라 기록된 어떤 시나리오도 실행하지 않은 코드이다.

### §15.11 이 모든 것이 입증하지 않는 것

- **정확성의 확률은 없다.** 초고신뢰성 검증에 관한 문헌은 테스트가 그러한 확률을 산출할 수 없다고 명시한다; 여기서도 어떤 확률도 제시하지 않는다.
- **변이 적정성은 이 목록에 대한 적정성이다.** 그것은 손으로 선별되었다; 포화된 점수는 하한이다.
- **위협 및 레짐 커버리지는 고려된 것에 대한 하한이다.** 아무도 명명하지 않은 부류와 요인은 구성상 분모 밖에 있다.
- **분포(§12)는 목(mock)에 대한 것이다.** 실제 Base는 도달 범위를 제공할 뿐, 분포를 제공하지 않는다.
- **탐지율은 시드당 256회 퍼즈 실행 기준의 테스트별 수치이다.** 그 예산에서 놓친 테스트가 더 큰 예산에서는 잡아낼 수 있다.
- **이 저장소는 V2이며, V2는 배포되지 않았다**(§19).

위의 모든 도구는 수치가 출력되기 전에 보정되었다: 다른 경로로 이미 알려진 결함에 대해 먼저 실행하고, 그것을 놓친 곳에서 수정하였다 — 주석 안의 텍스트에 일치했기 때문에 문서를 동작으로 읽은 텍스트 매처, 호출 옵션 블록을 고려하지 않은 커버리지 정규식, 패턴 목록이 비어 있던 대응물 검색. 도구는 이미 알려진 사례를 찾아낼 때까지 아무 가치가 없다; 탐색 없는 판정은 공허함이다; 그리고 컴파일러가 결정하는 것은 무엇이든 추정하지 않고 측정한다.

우리가 아는 한, 뮤턴트별 제거 테스트와 비활성 변이 보고를 갖춘 선별되고 이름 붙은 변이 가드; 그것을 강등하는 CI 규칙을 갖춘 공유 수량 목록; 생존자를 공개한, 상태 기반 불변식을 겨냥한 뮤턴트; 하나의 단언 아래 레짐 요인에 대한 강도 3 커버링 배열; 견적 수학의 N-버전 테스트; 견적이 시간에 따라 어떻게 낡아 가는지에 대한 측정된 곡선; 또는 신뢰 구간을 갖춘 자체 수수료 뮤턴트의 탐지율을 공개하는 다른 공개 DeFi 코드베이스는 없다. 아티팩트는 저장소에 있다; 선행 사례를 발견한다면 이 주장은 틀린 것이며, 우리는 그것을 알고 싶다.

## §16 아키텍처와 크기

*쉽게 말하면:* 다섯 개의 컨트랙트가 있고, 그중 하나는 나머지 넷이 가져다 쓰는 순수한 수학이며, 어떤 수학도 두 번 정의되지 않는다. *정확히 말하면:* Core는 스토리지도, 소유자도, 업그레이드 경로도 없는 무상태 라이브러리이다; Hub, Solver, Router, Quoter는 이를 가져다 쓰며 어느 것도 기본 연산을 재정의하지 않으므로, 수학에 대한 한 번의 감사가 모든 소비자를 포괄한다.

### §16.1 다섯 컨트랙트: 각각이 보유하는 것, 각각이 호출할 수 있는 것

| 컨트랙트 | 보유 | 호출 가능 대상 | 영구 상태 쓰기 | 크기(런타임 바이트) |
|---|---|---|---|---|
| **Core**(라이브러리) | 없음 — pure 및 view 함수: Ω, 𝒟, Φ, Ψ, 모노슬롯 코덱, 안전한 전송, `mulDiv` | 풀과 토큰, 읽기 전용, 모든 보호된 `staticcall`은 `GAS_CAP = 100,000`으로 상한 | 결코 없음 — `tstore`도, `sstore`도 없음 | 6,442 |
| **Hub** | 레지스트리(풀당 모노슬롯 하나, 페어당 ≤ 16), 팩토리 행(≤ 16), 브리지(≤ 3), codehash 고정이 있는 훅 허용 목록, V4 항목, 역할; ERC-7201 네임스페이스 | Core를 통한 풀과 팩토리, 읽기 전용; `extsload`를 통한 V4 PoolManager | `recordSwap` 시(Router만 호출), 승인 및 구성 시 | 23,648 |
| **Solver** | 없음 — `view` 전용 | Hub(레지스트리 읽기, 탐색), Core를 통한 풀 | 결코 없음 | 19,686 |
| **Router** | 관리자, 트레저리, `permit2`, `weth`, `paused`, `controlRenounced`, 구조 대기열; **유휴 상태의 토큰 잔액 없음** | 입력 토큰, 경로의 풀, 출력 토큰, Hub(`isBridgeToken`, `isHookLive`, `recordSwap`), Solver(`swapBestExactIn`만, `view`), V4용 WETH와 PoolManager, 해당 진입 문용 Permit2, 트레저리, 수신자 — 호출자가 제공한 대상은 결코 없음 | 자체 구성; 스왑당 유일한 외부 쓰기는 성공 경로의 `Hub.recordSwap` | 23,781 |
| **Quoter** | 없음 | Solver(`view`), Hub(`isBridgeToken`, 읽기 전용), Exact Pass 모의 실행을 위한 풀(호출 후 되돌림) | 결코 없음 | 11,429 |

![FIG-15 — 다섯 컨트랙트 토폴로지: Core(순수 수학: Ω · 𝒟 · Φ · Ψ)를 Hub(레지스트리, Ψ 상태, 브리지, 훅 고정), Solver(경로 최적화기, view), Router(원자적 실행, 자금을 움직이는 유일한 컨트랙트), Quoter(미리 보기, view 및 모의 실행)가 가져다 쓴다. 화살표는 호출이다; 스왑당 스토리지로 들어가는 유일한 실선 화살표는 Router → Hub.recordSwap이다; Solver와 Quoter에서 나가는 다른 모든 화살표는 view 전용이다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-15.png)

신뢰는 한 방향으로 흐른다. Hub는 Router가 `recordSwap`을 호출하는 것만 신뢰하고(`onlyRouter`) 다른 누구도 신뢰하지 않는다; Router는 경로에 필요한 세 가지 사실 — 이 토큰이 브리지인가, 이 훅이 활성인가, 그리고 등록 쓰기 — 에 대해서만 Hub를 신뢰하며, 거래 가격을 정하는 어떤 것에 대해서도 신뢰하지 않는다. Solver는 Hub의 레지스트리를 측정할 좌표 목록으로서만 신뢰한다. Quoter는 Solver의 계획을 참고용으로 신뢰하고 집중 유동성 레그를 풀 자체에 대해 다시 가격 산정한다. 어떤 것도 calldata를 신뢰하지 않는다: 경로는 낯선 사람의 주장으로서 Router에 도달한다.

### §16.2 스왑 하나의 생애

![FIG-16 — 스왑 하나의 생애: `previewAndEncode`(view) → calldata → 진입 문 → `_execute` → 베뉴 → 정산, 각 단계의 검사를 거부 코드로 명시: 진입 문에서 최솟값과 기한; 홉 형태와 연속성; 도착량 측정; 홉별로 재조정, 수수료 홉이면 수수료, 레그별 하한과 커버리지 게이트, 홉 합계; 스윕; 총 출력; 경로 하한 Φ; 있다면 출력 측 수수료; 수신자에서 측정한 전달량; `userMinOut`; 수수료 원장; 실행된 레그별 `recordSwap`; `ExecutionProof`.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-16.png)

1. **미리 보기**(오프체인, 무료): `previewAndEncode`는 `findBestRoutePlan`을 실행한다 — 레지스트리와, 최신이 아니면 탐색; Ξ; Ψ 순위; 탐침; 대역; 할당; 클램프; 분할 게이트; 증명된 하한 — 그리고 `Preview`와 calldata를 반환한다. 트레이더는 자신의 가격 기대에 따라 `userMinOut`을 설정한다.
2. **진입 문**: `swapExactIn`(또는 그 형제 함수)은 기한을 확인하고(`RouterE(4)`), 영인 최솟값을 거부하며(`RouterE(10)`), 입력을 끌어오고 변화량을 측정한다.
3. **`_execute`**: 홉 수, 홉당 레그 수, 홉 연속성(`RouterE(3)`); 수수료 스캔; 기준선은 한 번만 생성된다.
4. **홉별로**: 측정된 도착량에 맞추어 재조정한다; 이것이 수수료 홉이면(소진 시에는 모든 홉에서) 수수료를 부과한다; 각 풀을 한 번 읽어 금액, 영향, 견적을 얻는다; 각 레그를 그 종류가 선택하는 형태로 실행한다(알 수 없는 종류는 `RouterE(8)`, 변화량을 바꾸는 훅은 `RouterE(9)`); 커버리지 게이트를 포함한 레그별 하한과 홉 합계(`RouterE(5)`); 콜백은 약정된 풀에만 지급한다(`RouterE(6)`).
5. **스윕**: 기준선을 넘는 미사용 입력과 브리지 잔여분은 지급자에게 반환된다.
6. **정산**: 변화량으로서의 총 출력(영이면 `RouterE(8)`); 측정된 영향과 집중도로부터 `floorBps`; 마지막 홉의 프레임 내 견적과 세 방향 최댓값에 대한 경로 하한(`RouterE(5)`); 브리지 코인으로 들어가는 직접 경로에서의 출력 측 수수료; 전송, 수신자에서 측정한 전달량, 전달된 금액에 대한 `userMinOut`(`RouterE(5)`); 수수료 원장(`RouterE(15)`, `RouterE(16)`).
7. **유일한 쓰기**: 실행된 레그별로 측정된 금액과 함께 `Hub.recordSwap`; 그다음 `ExecutionProof`, `Swap`, `Fee` 이벤트. 어떤 검사라도 거부하면 아무것도 기록되지 않는다; 그 트랜잭션은, 모든 풀의 스왑을 포함하여, 일어나지 않은 것이 된다.

### §16.3 EIP-170 대비 크기

| 컨트랙트 | 런타임 바이트 | 24,000바이트 게이트까지의 여유 | EIP-170(24,576)까지의 여유 |
|---|---|---|---|
| Router | 23,781 | 219 | 795 |
| Hub | 23,648 | 352 | 928 |
| Solver | 19,686 | 4,314 | 4,890 |
| Quoter | 11,429 | 12,571 | 13,147 |
| Core | 6,442 | 17,558 | 18,134 |
| 합계 | 84,986 | | |

![FIG-17 — 릴리스 프로필에서 측정한 다섯 컨트랙트의 런타임 크기와 프로젝트의 24,000바이트 게이트 및 24,576바이트의 EIP-170 한도 비교; 비교 척도로 배포된 V1 라우터(14,624바이트).](https://blazephoenix.xyz/whitepaper/figs/ko/fig-17.png)

게이트는 스위트 자체 안에서(`DeployedSizeGate`) 스위트가 실행되는 것과 같은 프로필에서 단언되며, `profile_parity.py`가 그 프로필을 릴리스 프로필과 동일하게 유지한다 — 체인이 받아들일 수 없는 바이너리를 테스트하는 스위트는 잘못된 대상을 테스트하는 것이 된다. 크기는 추정하지 않고 측정한다: 수십 바이트로 추정되었던 한 가드는, 선택된 형태가 변수의 활성 범위를 늘렸기 때문에 측정해 보니 수백 바이트였다. Router와 Hub는 한계선에서 수백 바이트 이내에 있으며, 그래서 페어 증명(§9)은 179바이트를 차지하고 스테이블 곡선 수정(§6)은 그것이 대체한 코드보다 35바이트 *더 작다*: 추가되는 모든 바이트는 제거되는 바이트로 상쇄된다.

### §16.4 계수된 공격 표면

공격 표면이란 공격자가 쓸 수 있는 것, 컨트랙트가 보유하는 것, 그것이 호출하는 대상, 그리고 그것에 대한 권한을 가진 자이다. 여기서는 각각이 셀 수 있는 양이다.

**호출자가 쓸 수 있는 것.** 경로는 타입이 지정된 구조이다 — 최대 3홉 × 5레그, 각 레그는 열 개의 필드(`pool`, `hooks`, `kind`, `fee`, `tickSpacing`, `zeroForOne`, `stable`, `amountIn`, `expectedOut`, `auxId`)를 가진다 — 그리고 어떤 필드도 호출 대상을 지정하거나 calldata 덩어리를 담지 않는다. `docs/assurance/fields.json`은 상태, 이벤트 또는 가드에 도달하는 호출자 작성 가능 필드 전부, 총 23개를 분류한다:

| 등급 | 개수 | 의미 | 예시 |
|---|---|---|---|
| **확인됨** | 14 | 실행이 필드를 읽고 *또한* 검사가 그것을 관찰값과 비교한다 | 풀의 형태로부터 다시 도출되는 `leg.kind`(Hub `recordSwap`); 풀이 응답하는 집중 유동성 풀에서의 `leg.fee`; 측정된 도착량으로 재조정되는 `leg.amountIn`; 커버리지 게이트로 끌어올려지는 `leg.expectedOut`; 풀 자체의 `token0`/`token1`에 대해 게이트되는 `hop.tokenIn`/`tokenOut`; 실행되는 풀 id에 들어가는 V4의 `leg.hooks`와 `leg.auxId` |
| **조향** | 5 | 필드가 토큰 이동을 지시하므로, 거짓 값은 호출자를 처벌하며 별도의 검사가 필요 없다 | 페어 및 집중 유동성 베뉴의 `leg.pool`(토큰이 그곳으로 보내진다); V4의 `leg.zeroForOne`(잘못된 방향은 매니저의 회계에서 닫힌 쪽으로 실패한다); `route.singleOutFloor`(단조적 — 조일 수만 있다); 아무도 읽지 않는 참고용 경로 필드(`totalOut`, `estGas`, …) |
| **선언됨** | 4 | 상태, 이벤트 또는 가드에 도달하지만 아무것도 그것을 관찰하지 않는다 — 각각 그 이유가 기록되어 있다 | V2 페어의 `leg.fee`(어떤 페어도 `fee()`를 노출하지 않는다; 100 bps 상한으로 제한되고 견적과 실행이 똑같이 사용하므로 둘은 어긋날 수 없다); V4가 아닌 레그의 `leg.hooks`(훅 레그를 마지막에 두는 순서 지정에만 쓰인다; 거짓말은 호출자 자신의 경로를 대가로 치르게 한다); `Volume` 이벤트에 게시되는 `leg.expectedOut`(같은 필드를 소비하는 레그별 하한에 의해 약 +25 % 부근으로 제한된다; 온체인 소비자 없음); `leg.stable`(Hub가 등록 시 `stable()`을 측정하고 두 소비자가 같은 선언을 읽는다) |

레지스트리의 순위, 프로토콜 하한, 수수료 기준은 확인된 양으로만 계산된다. Hub는 `kind`를 확인한다; Router는 금액, 토큰, 집중 유동성 수수료를 확인한다; 도출 과정은 V4 필드를 구성상 확인한다.

**트랜잭션 사이에 Router가 보유하는 것.** 토큰 잔액 영 — `INV-1`, 캠페인 전반에서 `invariant_RouterHoldsNothing`, `invariant_routerHoldsNothing`, `invariant_HoldsNothingBeyondTheSeed`로 단언되며, 네 개의 가드 뮤턴트(한 wei 부족한 전송, 그리고 세 기준선 각각의 제거)가 감시한다. 수수료는 스왑 안에서 트레저리로 흘러간다; 잔여분은 스왑 안에서 지급자에게 돌아간다; Router에 머무를 수 있는 유일한 것은 잘못 보낸 자금이며, 48시간 구조 절차가 그것을 돌려주기 위해 존재하고 모든 스왑의 기준선은 그것을 지급에서 제외한다. 다섯 컨트랙트 중 세 개 — Core, Solver, Quoter — 는 스토리지 쓰기를 전혀 내보내지 않는다.

**스왑이 호출하는 대상.** 스왑 하나의 상대방은 여덟 부류의 닫힌 집합을 이룬다: 입력 토큰; 출력 토큰; 경로가 지정하는 풀(최대 15개); Hub; Solver, `swapBestExactIn`을 통해서만, 그리고 view로서만; WETH와 PoolManager, V4 및 네이티브 레그에 대해서만; Permit2, 해당 진입 문을 통해서만; 트레저리와 수신자. 아홉 번째는 없다: 실행기는 호출자가 제공한 호출 대상을 받지 않으며(SOK-ARBITRARY-CALL), 레지스트리가 보유한 종류가 선택하는 닫힌 형태 집합에서 베뉴 상호작용을 디스패치한다. 모든 보호된 읽기는 100,000 가스로 상한이 걸린다. 스왑의 정확한 명령어 수는 단언되는 것이 아니라 기록된다: 릴리스 바이너리에서 정직한 V2 스왑 하나에 대해 Router 명령어 4,382개이며, 매 단계에서 옵코드를 확인했다(§15.10).

**누가 권한을 가지는가.** Router에 아홉 개, Hub에 여섯 개의 제어 함수가 있으며, 모두 그것이 도달할 수 있는 것과 없는 것과 함께 열거되어 있다(§14.6–§14.7); 어느 것도 원금, 수수료, 분할 또는 하한을 움직일 수 없다; 모두 `renounceControl`에서 끝나며, 그 이후에 남는 것은 네 개 함수로 이루어진 증가 전용 큐레이터 계층과 기존 운영자 자리뿐이다. `selfdestruct`도, 프록시도, Hub의 일회성 `initialize` 이후의 이니셜라이저도 없으며, 아티팩트에 있는 유일한 `delegatecall`은 공개 Core 라이브러리로 들어가는 컴파일러 자체의 호출이다.

**거래가 정산되기 위해 존재해야 하는 것.** 예산(§3.1): 라우팅 서비스 없음, 오라클 없음, 솔버 네트워크 없음, 키퍼 없음, 프록시 없음 — 기존 설계가 "필수", "일반적" 또는 "경우에 따라 다름"인 곳에 다섯 개의 영.

**계수된 차이.** 경로를 오프체인에서 가져와 범용 호출 목록을 실행하거나 재고를 보유하는 설계와 비교하면:

| 표면 | 이 설계 | 오프체인 풀이 실행기 | 재고를 보유하는 베뉴 또는 라우터 |
|---|---|---|---|
| 가드에 도달하는 호출자 작성 가능 필드 | 23개, 그중 14개는 관찰값에 대해 확인되며 호출 대상을 지정하는 것은 0개 | `(target, calldata)` 쌍의 목록: 무제한이며, 각 대상은 컨트랙트가 호출자의 말만 믿고 수행하는 호출이다 | — |
| 실행기에 유휴 상태로 있는 토큰 잔액 | 0(`INV-1`) | 대개 0 | 재고 그 자체 |
| 스왑 중 스토리지에 쓰는 컨트랙트 | 1(`Hub.recordSwap`, 실행된 레그당 한 번의 쓰기, 성공 경로에서만) | 경우에 따라 다름 | 베뉴의 회계 |
| 경로를 계산하는 주체 | 체인(`findBestRoutePlan`, `view`) 또는 호출자, 어느 쪽이든 프레임 내에서 재측정 | 서버; 컨트랙트는 검사할 뿐 선택하지 않는다 | — |
| 오라클, 키퍼, 프록시, 업그레이드 경로 | 0 | 흔히 각각 ≥ 1 | 흔히 프록시 |
| 원금에 도달할 수 있는 특권 함수 | 0 | 프록시 관리자가 로직을 교체할 수 있다 | 경우에 따라 다름 |
| 감사자가 읽어야 하는 배포 바이트 | 84,986, 다섯 컨트랙트, 모두 EIP-170 이하 | 정산 컨트랙트, 그리고 감사 가능한 줄 수로 본 크기를 알 수 없는 서버 | — |

이 비교는 논증이 아니라 계수된 것이다: 그 차이는 무제한 목록에 맞선, 경계가 있고 타입이 지정되며 확인된 경로이다; 재고에 맞선 영이다; 다수의 쓰기에 맞선 한 번의 쓰기이다; 그리고 서버가 도출하고 체인은 단지 검사만 한 경로에 맞선, 체인이 도출한 — 또는 호출자가 도출하고 체인이 재측정한 — 경로이다.

## §17 측정된 성능

*쉽게 말하면:* 이 절의 모든 것은 실제 체인의 포크 위에서 — 실제 베뉴, 낯선 이들이 예치한 실제 준비금, 하나의 바이트코드로 — 측정되었으며, 정확히 그렇게 표시된다. *정확히 말하면:* 수치는 저장소의 공개 하네스가 그것을 재현하는 경우에만 여기에 나타나며, 하네스와 체인과 날짜가 그 옆에 명시된다. 포크 측정은 동작에 관한 증거이지 배포 기록이 아니며, 배포 기록은 §19 이다. §12 와 §15 의 측정은 2026-09-05의 것이고, 이 절의 측정 캠페인은 2026년 팔월에 수행되었으며 그 하네스는 `test/fork/`에 있다.

### §17.1 체인 간 동일성

동일한 바이트코드가 Ethereum, Arbitrum, Optimism, Base의 포크 위에서 실행되었고, 체인별 코드 변경 없이 각 체인의 베뉴 구성 — Base에서는 V4와 함께 Aerodrome과 Pancake, Arbitrum에서는 Camelot, Optimism에서는 Velodrome — 에 적응했다. 이 네 체인이 선택된 이유는 이들이 합쳐서 레지스트리가 허용하는 모든 베뉴 계열을 실행하기 때문이며, 따라서 체인별 특수 사례가 만약 존재했다면 드러날 기회가 네 번 있었다. 모든 체인에서 두 가지 동일성이 실측값에 대해 측정되었다. CREATE2 도출에 대해서는, 𝒟가 계산한 각 풀 주소를 같은 쌍과 매개변수에 대해 그 체인의 팩토리 자체가 보고하는 주소와 대조했고, `hasCode`로 도출된 모든 주소에 바이트코드가 있음을 확인했다. 정밀 패스(Exact Pass)에 대해서는, 베뉴 자체의 스왑을 라이브 상태 위에서 호출하고 그 델타를 revert 페이로드로 실어 냈으며, 체인마다 Algebra를 포함한 각 계열의 콜백 관례를 견뎌 냈다. 두 동일성은 네 체인 모두에서 성립하며, 표에 필요했을 차이 열은 비어 있다.

### §17.2 네 체인에 걸친 미리보기 → 전달 충실도

이 논지는 직접 측정을 허용한다. 스왑을 견적하고, 실행하고, 비교하는 것이다. `FidelityMatrix` 포크 하네스는 고정된 블록에서 Arbitrum, Base, Optimism, Robinhood Chain의 실제 쌍을 견적하고 실행하고 정산하며, 다루는 모든 쌍에 대해 미리보기와 전달을 나란히 출력한다. 이 하네스에는 50 bps의 영구 경보가 걸려 있어 충실도 퇴행이 스스로 드러난다. 2026-08-24 측정:

| 체인 | 미리보기 → 전달 | 함께 기록된 사항 |
|---|---|---|
| Arbitrum | 실행된 모든 쌍에서 0–1 bps | Solver의 경로가 10 % 단위의 전수 양방향 분할 그리드와 일치한다 |
| Base | 0–1 bps | 그리드 최댓값의 0.13 bps 이내; 실제 체결은 전부 포함 222,000–326,000 가스 |
| Optimism | 0–1 bps | Optimism에서 이 엔진이 수행한 최초의 포크 측정 V4 라우팅 실행 |
| Robinhood Chain | 0–1 bps | 그리드와 정확히 일치; 이 체인에서 최초로 측정된 실행 |

이 매트릭스는 상수곱, 집중형, V4 — 네이티브 포함 — 및 Solidly 레그를 처음부터 끝까지 실행한다. 대역은 실행된 모든 쌍에서 한 베이시스 포인트이며, 그 뒤에 50 bps 경보가 있다. §17.1 의 Solver 대 그리드 비교는 단위까지 일치하는 유일한 결과이며, 그렇게 명시된다. 두 가지 행동적 결과가 함께 따른다. 브리지 중개 토큰은 메타 방정식의 최대화자로서 쌍마다 적응적으로 선택되며, 의도적으로 고립된 쌍은 조작된 경로가 아니라 경로 없음을 반환한다.

### §17.3 팩토리 센서스

베뉴 등록은 하나의 주장이며, 그 주장에는 고유한 계측 도구가 있다. 센서스 하네스(`FactoryCensus`)는 프로덕션 배포 배선을 상속한다 — 체인들이 받는 것과 같은 스크립트를 통해 배포하며, 하네스와 배포가 어긋나면 실행을 거부하는 동등성 게이트 뒤에 있다. 이 하네스는 그 스크립트와 함께 비공개 배포 트리에 있으며 공개 저장소에는 없으므로, 아래 표는 보고된 것이지 이 체크아웃에서 재현할 수 있는 것이 아니다. 그 픽스처와 반공허성(anti-vacuity) 게이트를 공유하는 공개 스윕은 `test/fork/TokenSweep.t.sol`이다. 이 하네스는 고정된 토큰 코퍼스에 대해 모든 체인의 모든 등록된 팩토리마다 세 개의 열을 보고한다. 등록됨, 그 쌍의 풀을 보유함, 그 쌍을 획득함(승리 경로의 레그를 공급함)이다. 이 열들은 하나의 분모, 즉 홉-쌍을 공유하며, 명시적인 미해결 버킷을 두는데 이는 네 체인 모두에서 영으로 측정되었다. 엔진이 라우팅한 모든 레그는 이름 있는 팩토리에 귀속된다.

| Arbitrum, 88 홉-쌍 | 풀 보유 | 쌍 획득 |
|---|---|---|
| Uniswap V4 (네이티브 + 래핑) | 67 | 48 |
| Uniswap V3 | 86 | 33 |
| Pancake V3 | 77 | 13 |
| Camelot V3 | 66 | 10 |

하나의 쌍은 그 승리 경로의 레그를 공급한 모든 베뉴에 귀속되므로, 열의 합은 쌍의 수를 초과할 수 있다 — 분할이 아니라 귀속이다. Arbitrum에서 V4 계열은 표준 V3 팩토리를 포함하여 다른 어떤 등록된 베뉴보다 많은 쌍을 획득한다 — §5.1 의 네이티브 우선 탐침 순서를 외부에서 확인해 주는 결과이다. 센서스는 가지치기 도구이기도 하다. 등록되어 풀을 보유하면서도 수백 개의 측정된 쌍에 걸쳐 아무것도 획득하지 못하는 팩토리는 평판이 아니라 귀속으로 가격이 매겨진 퇴역 후보이며, 모든 스윕에는 반공허성 게이트가 있다 — 깊이가 있다고 보고된 쌍은 영이 아닌 견적도 내야 한다.

### §17.4 가스

모든 수치는 이름이 명시된 하네스로 추적 가능하며, 콜드와 웜은 따로 보고된다.

| 측정 항목 | 값 | 출처 |
|---|---|---|
| 콜드 솔브(최초 탐색, 팩토리 4개) | 169,093 가스 | `test/fork/DiscoveryColdWarm.t.sol`; `REPORTS.md` |
| 웜 솔브(캐시된 모노슬롯, 탐색 생략) | 132,691 가스 | 같은 실행 |
| 웜 절감 | −36,402 가스 (−21 %) | 차이 |
| 추가 레그당 한계 비용 | ≈ 28,700 가스 | `LifecycleMetrics` (2026년 팔월) |
| 브리지 경유 두 번째 홉 | ≈ 59,000 가스 | `LifecycleMetrics` (2026년 팔월) |
| 라이브 Base에서의 `previewPlan`, 콜드 / 웜 / TTL 이후 콜드 | 1,524,221 / 1,414,633 / 1,359,925 가스 | `test/fork/QuoterGasFork.t.sol` (§12.3) |

![FIG-18 — ABI를 통한 견적 비용: 목(mock) 위에서의 `previewPlan`과 `previewAndEncode`(탐색 대 새 레지스트리, 마흔 가지 크기에 걸친 평균과 분산) 및 라이브 Base 위에서의 비용(콜드, 웜, TTL 이후 콜드).](https://blazephoenix.xyz/whitepaper/figs/ko/fig-18.png)

세 가지 구조적 해석은 아키텍처에서 따라 나오므로 어떤 측정 없이도 성립한다. 웜 절감은 거래 규모가 아니라 레지스트리의 폭에 비례한다. 콜드 경로가 비용을 치르는 대상은 팩토리 스윕이며, 그 스윕을 대체하는 캐시 읽기는 어떤 경우든 `SLOAD` 하나이다. 레그는 한계 비용의 단위이며, 이것이 분할 게이트가 존재하는 이유이다(§10.4). 그리고 홉은 비용이 큰 차원이다. 브리지 경로는 펌프에서 자신의 비용을 치를 만큼 풀에서 충분히 이겨야 한다. 호출자는 거래마다 이 곡면 위의 자기 지점을 선택한다 — 프레임 안에서 솔브하고 탐색 비용을 치르거나, 경로를 공급하고 실행 비용만 치른다 — 그리고 §9.4 의 신선도 게이트는 프로토콜이 아직 모르는 쌍에만 스윕을 남겨 둔다. 라이브 견적의 비용은 1.4–1.5백만 가스의 *view* 연산이며, 체인 위에서는 아무도 이를 지불하지 않는다.

### §17.5 경쟁자들과 비교한 실행기

외부 기준이 없는 가스 표는 자화자찬이므로, 실행 계층은 같은 포크, 같은 블록에서 실제 프로덕션 calldata로 벤치마크되었다. 대상은 KyberSwap의 `MetaAggregationRouterV2`(`0x6131B5fae19EA4f9D964eAc0408E4408b66337b5`)였으며, 각 경로가 생성된 블록에 고정된 Base 포크 위에서 2026-08-22에 수행되었고, 두 번째 비교 대상으로 Paraswap의 견적도 함께 받았다. Kyber 자체가 인코딩한 경로를 공개 API에서 요청해 배포된 컨트랙트에 대해 재실행하고, 우리의 경로를 그 옆에서 실행하여 양측의 전달량을 잔액 델타로 측정했다. 하네스는 먼저 스스로를 검증한다. 경쟁자의 측정된 전달량이 그 API의 약속과 wei 단위까지 일치하므로, 어느 쪽의 revert도 결과로 위장할 수 없다. 레그당 기준으로 실행기는 기준 대비 2.4×에서 10.9× 더 가스 효율적인 것으로 측정되었다 — 가장 명확한 쌍에서 레그 네 개에 322,172 가스 대 레그 하나에 879,553 가스, 트랜잭션당 2.7× — 이 측정은 캡처된 calldata와 함께 비공개 트리에 있는 하네스(`BenchExec.t.sol`)로 수행되었으므로 수치는 보고된 것이지 이 체크아웃에서 재현할 수 있는 것이 아니며, 이 체크아웃은 `optimizer_runs = 999999`로 컴파일된다 — 따라서 차이는 컴파일러 설정이 아니라 설계에서 온다. 그들의 라우터는 서버가 결정한 범용 호출 목록을 해석하고, 우리 라우터는 타입이 지정된 구조를 실행하며, 해석에는 비용이 든다. 가장 명확한 쌍에서 그들의 단일 레그 경로는 879,553 가스가 들었고, 우리의 레그 네 개는 322,172 가스가 들었다. 측정된 아홉 쌍 전체에서 완전 솔브 문과 calldata 문은 동일한 출력을 전달했다. 방법의 오차 막대는 결과의 일부이다. 견적은 생성 대상 블록에 고정되었고, API 측 가스 추정치는 실제 실행에서 그중 하나가 148 % 낮게 측정된 뒤 폐기되었다.

### §17.6 바이트–가스 교환율 ρ*

호출자는 어느 문을 써야 하는가 — 경로를 calldata에 실어 보낼 것인가, 체인이 솔브하게 할 것인가? 트랜잭션의 비용은 아핀이고 분리 가능하다 — $21{,}000 + \mathrm{bytes} \cdot p_{\mathrm{byte}} + \mathrm{gas} \cdot p_{\mathrm{gas}}$ — 따라서 calldata와 실행 사이의 전체 교환은 하나의 스칼라로 환원된다. **ρ\***, 즉 calldata 한 바이트가 사는 실행 가스이다. 릴리스 프로파일에서 `test/EquationBench.t.sol`로 측정한 ρ*는 트랜잭션 전체 기준으로 단일 레그 경로의 바이트당 66.9 가스부터 레그 네 개 및 다섯 개 경로의 105.5까지 이른다. 이에 맞서는 것은 각 체인의 라이브 바이트 가격이며, 이는 키도 제삼자 피드도 없이 체인 자체의 수수료 프리디플로이 — OP-Stack 가스 가격 오라클, Arbitrum의 `ArbGasInfo` — 에서 읽는다.

| 체인, 가격은 2026-08 라이브로 읽음 | $p_{\mathrm{byte}}$ (가스/바이트) | 스왑에서 데이터가 차지하는 비중 | 저렴한 문 |
|---|---|---|---|
| Base | 7.2 | 0.9 % | calldata 경로 |
| Arbitrum | 12.8 | 3.4 % | calldata 경로 |
| Optimism | 33.9 | 14.0 % | calldata 경로 |
| calldata 가격의 데이터 가용성을 지불하는 ZK 롤업 | 243,256 | 99.9 % | 프레임 내 솔브 — 처음부터 끝까지 14× 저렴 |

$p_{\mathrm{byte}} < \rho^{*}$인 곳에서는 calldata 문이 저렴하고, $p_{\mathrm{byte}} > \rho^{*}$인 곳에서는 프레임 내 솔브가 저렴하다. OP-Stack과 Arbitrum 체인에서 데이터 항은 스왑 비용의 0.9–14 %이므로, 돈이 드는 곳은 실행이다 — 그리고 바로 거기서 위에서 본 실행기의 효율이 복리로 쌓인다. Router의 네 개의 문은 편의 기능이 아니라, 인터페이스로 만들어진 아핀 비용 모델이다.

## §18 뼈대만 남긴 DEX

열일곱 개 절의 메커니즘이 한 페이지로 환원된다. 다른 것을 읽지 않는다면 이것을 읽어라.

**하나의 일.** 두 토큰, 수량, 당신의 최솟값, 수령인, 기한을 받는다. 체인의 공개 유동성이 전달할 수 있는 최대의 출력을 반환하거나 — 그 이유를 말하는 코드와 함께 거부한다. 세 번째 결과는 없다. 적대적 베뉴 매트릭스에서 베뉴 병리와 문의 조합 픽스처 스무 개 중 스무 개가 하한 안에서 정산되거나 우리의 셀렉터로 거부된다(§15.5).

**다섯 개의 컨트랙트, 하나의 수학.**

| 컨트랙트 | 한 줄로 말하면 |
|---|---|
| **Core** | 상태 없는 라이브러리: 모든 베뉴의 닫힌 형식, 도출 𝒟, 하한 Φ, 활력 점수 Ψ. 한 번 정의되고 나머지 넷이 가져다 쓴다. |
| **Hub** | 유동성을 거래함으로써 학습하는 레지스트리 — 그리고 모든 쌍을 저장하기 전에 증명한다. 아무것도 가격을 매기지 않는다. |
| **Solver** | *측정된* 한계 출력과 깊이로부터 경로와 분할을 구성한다. `view` 전용이다. |
| **Router** | 자금을 옮기는 유일한 컨트랙트. 모든 잔액 델타를 측정하고, 하한을 강제하며, 정지 상태에서 아무것도 보유하지 않는다. |
| **Quoter** | 무료 미리보기 — Router가 정산에 쓰는 것과 같은 평가기이므로, 미리보기와 전달이 일치한다. |

**여섯 가지 베뉴 종류, 네 개의 계열.** Uniswap V2, V3, V4(네이티브 ETH 포함), Algebra 동적 수수료 풀, Solidly 안정형 및 변동형 쌍 — 각각 Core의 닫힌 형식 하나로 가격이 매겨지며, 이 모두에 디스패처 Ω 하나가 쓰인다(§4). Curve와 Balancer는 제거되었고, 그 종류 번호는 영구히 폐기되었다(§4.1).

**네 개의 문, 하나의 실행기.** `swapExactIn`, `swapExactInWithPermit2`, `swapExactInNative`는 calldata로 경로를 받고, `swapBestExactIn`은 체인 위에서 원자적으로 솔브하고 실행한다. 네 문 모두 같은 `_execute`, 같은 하한, 같은 수수료에 도달한다(§11.1).

**모든 거래 아래의 세 하한.** 레그별 하한, 홉 집계 하한, 그리고 실행 프레임에서 측정된 영향으로부터 재도출되는 경로 하한 Φ — 그다음 수령인에게 실제로 도착하는 것에 대한 *당신의* `userMinOut`. 전달량은 프로토콜의 하한과 당신의 하한 중 큰 쪽이다(§11.5).

**하나의 수수료, 컴파일되어 고정.** 0.28 %, Router가 보유하는 첫 번째 브리지 코인에 대해 한 번 부과되고, 결코 영이 되지 않도록 올림되며, 견적과 전달 사이의 격차에 따라 커지는 요소는 전혀 없다(§13).

**신뢰하기를 거부하는 것.** 광고된 유동성. 선언된 수수료. calldata의 깊이. 풀이 스스로 밝히는 거래 토큰. 이 모두는 공유 상태에 도달하기 전에 풀로부터 측정되거나 산술로 도출된다(§7, §9, §16.4).

**그 대가.** 가스: 프레임 안에서 경로를 도출하는 것은 다른 곳에서 계산된 답을 정산하는 것보다 비싸며, §17.6 은 체인마다 어느 문이 가장 저렴한지 알려 준다. 불변성: 결함은 공개되고 현상금이 걸리지만, 결코 패치되지 않는다(§21).

## §19 배포 현황

이 논문은 저장소를 문서화하며, 저장소가 정본이다. 이 글과 소스가 다를 경우 소스가 우선한다. 두 세대가 존재하며, 이들은 결코 혼동되지 않는다.

**V1이 체인 위에서 라이브로 운영되는 것이다.** 이는 다른, 더 이른 코드베이스로서 자체 저장소에 보관되어 있다 — 이 저장소의 이전 커밋이 아니다. 2026-09-04에 RPC로 측정한 결과, 배포된 V1 라우터는 Base, Ethereum, Optimism, Arbitrum에서 동일한 14,624바이트 컨트랙트이다. `test/fork/DeployedParity.t.sol`과 `test/fork/DeployedCodehashPin.t.sol`은 SDK가 지명하는 모든 네트워크에서 V1이 무엇인지를 — `test/fork/deployed-codehash.json`에 기록된 각 배포 컨트랙트의 코드해시로 — 고정하여 배포된 것의 변경이 감지되도록 하며, 세대와 무관하게 성립하는 속성을 단언한다. `SELFDESTRUCT` 없음, `CALLCODE` 없음, `tx.origin` 권한 없음, 일시적(transient) 재진입 락, 고정된 런타임 크기, 라이브 배선, 그리고 아직 포기되지 않은 제어권이다.

**이 저장소는 V2이며, V2는 배포되지 않았다.** 이 논문에서 측정한 트리는 23,781바이트의 Router를 빌드하며, V1에는 없는 외부 함수 — 그중에는 네이티브 값 문, 래핑/언래핑 이음매, 구조(rescue) 경로, Permit2 문이 있다 — 를 포함한다. V2 컨트랙트는 라이브 유동성의 포크 위에 배포되어 모든 포크 실행에서 처음부터 끝까지 실행된다(§15.1, §17). §15 나 §17 의 어떤 것도 배포된 V1 컨트랙트를 보호하지 않는다. 이 논문의 모든 주장은 `8949a9d` 시점의 V2에 관한 주장이다.

이에 따라 두 가지 정책이 독자를 보호한다. 이 논문에는 컨트랙트 주소가 없다 — 주소는 그것을 인용하는 문서보다 오래 살아남으며, 라이브 주소 표면은 갱신되고 기계적으로 검사될 수 있는 곳, 즉 저장소의 기록과 사이트의 검증 페이지 및 매니페스트에서 유지된다. 그리고 배포 기록에는 명세된 형식이 있다 — 체인, 주소, 배포 커밋, 런타임 바이트코드 해시를 브로드캐스트 시점에 기록하고, 깨끗한 빌드로부터 해시를 재도출하는 검증 단계를 둔다 — 그래서 V2가 배포될 때 모든 주소 주장은 신뢰가 아니라 산술이 된다.

## §20 토큰 — BZPX

십억 BZPX, 영구히 고정되며, 평범한 ERC-20이다. 18 소수 자릿수, 발행 경로 없음, 전송 수수료 없음, 리베이싱 없음, 전송 훅 없음, 일시 정지나 블랙리스트 없음 — 여섯 개의 배포 불변식이 배선 시점에 토큰 바이트코드에 대해 검사된다. 완전 희석 공급량은 제네시스부터 알려져 있다. 희소성은 정책이 아니라 컨트랙트의 속성이다.

| 배분 | 토큰 수 | 비중 | 비고 |
|---|---|---|---|
| 시장 유동성 | 520,000,000 | 52.0 % | 애그리게이터가 라우팅하는 유통량 |
| 스테이킹 발행(emission) | 180,000,000 | 18.0 % | 예비분; V4 발행 설계는 §25 에 있다 |
| 에어드롭 및 현상금 | 130,000,000 | 13.0 % | 하나의 풀: 기존 50,000,000 에어드롭, §21 의 기존 50,000,000 연구 풀, 그리고 시장 유동성에서 옮겨 온 30,000,000 |
| 팀 및 운영 | 103,000,000 | 10.3 % | 유일하게 락이 걸린 내부자 트랜치; 30개월 선형 베스팅 |
| 파트너 및 생태계 | 60,000,000 | 6.0 % | 통합, 베뉴 파트너십 |
| 마케팅 | 7,000,000 | 0.7 % | 출시 인지도 |

**2026년 구월에 바뀐 것.** 에어드롭과 보안 연구 풀은 같은 사람들에게 같은 일 — 프로토콜을 철저히 읽고 실제로 사용하는 것 — 에 대해 지불하는 두 개의 항목이었다. 이제 이들은 130,000,000 BZPX의 한 항목이다. 에어드롭에서 50,000,000, 연구 풀에서 50,000,000, 그리고 시장 유동성에서 가져온 30,000,000이며, 시장 유동성은 550,000,000에서 520,000,000으로 바뀐다. 다른 누구의 항목에서도 가져온 것은 없으며, 공급량은 여전히 정확히 십억이다.

토큰 컨트랙트의 온체인 기록이 공개되면, 그것이 이 표의 정본이다. 이 글을 쓰는 시점에 토큰에는 공급된 유동성도 토큰 생성 이벤트도 없다. 온체인 거래량 영(零)은 이 단계의 예상된 상태이지, 코드에 관한 신호가 아니다.

## §21 연구 프로그램과 보안 기록

이 프로젝트에 이름값을 하는 연구 프로그램이 생기기 전에, 독립 연구자들이 — 요청받지도, 약속받은 것도 없이 — 소스를 적대적으로 읽고 본 것을 비공개로 공개했다. 이 논문이 설명하는 코드는 그들의 독해를 견뎌 낸 코드이다. 그들 각자가 무엇을 보고했고 그것이 어떻게 되었는지는 그러한 기록이 있어야 할 곳, 즉 코드 옆의 저장소 `SECURITY.md`와 `SECURITY_HALL_OF_FAME.md`에 있으며, 거기서 모든 항목을 양방향으로 확인할 수 있다. 이 절은 그들이 누구인지, 적대적 독해가 무엇을 보상받는지, 그리고 그것이 발견한 것의 형태를 기록한다.

**연구자들.** NetGakarot · duxun · AmanDara1 · amitbhakar · auditor_1b3f2c · siam siddik · Thomas · llen · destinyae · superagent · Mohd Huzaifa · Raditya · bai bo · Josh W · Borutobro · mohaseenkatika · mohaseenbasha · Karan Rathod · Seavia Resources · acit aja · Brian Wahyu · M2000 Slash · Binod Bk · CHARIR ABDELHAMID · 그리고 익명으로 남기를 선택한 한 명의 연구자. 스물다섯 명의 연구자가 공로를 인정받았으며, Critical 등급에 이른 보고는 없다. 귀속은 결코 낮춰 반올림되지 않는다. 이름은 주어진 그대로 정확히 게시된다.

**풀: 에어드롭 및 현상금, 130,000,000 BZPX — 고정 공급량의 13 %**(§20). 보안 보상은 아래 대역에 따라 이 풀에서 지급되며, 대역은 변경되지 않았다.

| 심각도 | 보상 |
|---|---|
| Critical — 사용자 자금의 직접 탈취 또는 영구 동결; 도달 가능한 지급 불능 상태 | 2,500,000 – 7,500,000 BZPX |
| High — 특정 조건하의 탈취; 체계적으로 잘못된 당사자에게 지급하는 재분배 | 625,000 – 2,500,000 BZPX |
| Medium — 그리핑, 일시적 서비스 거부, 회수 가능한 잔여분 | 125,000 – 625,000 BZPX |
| Low — 위 등급 미만의 입증된 영향 | 최대 125,000 BZPX |

보상은 BZPX로 지급된다 — 수량과 일정은 약속되지만 시장 가치는 약속되지 않는다 — 지급은 2026년 시월 이후 시작되며 보고는 지금 분류된다. 한 조항은 의도된 것이다. 보고가 Critical이 되기 위해 보존 법칙을 깰 필요는 없다 — 장부가 맞는 동안 체계적으로 잘못된 당사자에게 지급하는 재분배는 탈취처럼 가격이 매겨지는데, 그것이 적대적 독자만이 잡아내는 등급이기 때문이다.

**기록에 담긴 것, 등급별.** 확인된 모든 발견은 이름 있는 속성, 수정 전 코드에 대해 실패하는 회귀 테스트, 그리고 그 테스트가 죽여야 하는 뮤턴트가 되었다. 각 등급을 한 문장씩, 레지스터를 참조하여 제시한다.

- *하나의 양을 두 곳에서 생산함* — 계획자와 실행기, 미리보기와 전달이 두 프레임에서 하나의 질문에 답하는 경우(하한의 영향 집계, 미리보기가 지명한 브리지 대 수수료가 고정되는 홉, 두 컨트랙트에 걸친 레그 예산, 고정된 브리지 비트 대 라이브 술어): 하나의 생산자로 축소하거나 이름 있는 동치성 테스트로 해소했으며, CI 규칙을 갖춘 레지스터로 열거했다.
- *측정값이 있었는데도 호출자가 공급한 양* — `route.totalOut`에서 읽은 수수료 기준액, calldata로부터 기록된 레지스트리의 수수료와 훅, 측정되지 않고 선언된 깊이, 선언을 그대로 보고한 공개 거래량: 측정으로, 그리고 §16.4 의 필드 분류로 해소했다.
- *부재를 허가로 읽음* — 프레임 안에서 견적할 수 없는 마지막 홉이 하한을 무력하게 남겨 두는 경우, 호출자가 영을 써 넣어 끌 수 있었던 커버리지 게이트, 한 계열 전체를 영으로 견적한 빈 수수료 목록: 결코 아무것도 아닌 것이 아니라 증명되거나 측정된 수치로 대체함으로써 해소했다.
- *잘못된 대상에 대한 고정* — 코드해시가 그대로인 채 프록시가 옮길 수 있었던 팩토리의 도출 원점, 구현이 옮겨질 수 있는데 런타임이 고정된 훅: 코드를 고정할 수 없는 곳에서는 답을 증명하고, 해소할 수 없는 곳에서는 그 지점에 잔여 위험을 명시함으로써 해소했다.
- *견적에서의 실패 시 개방(fail-open)* — 먼지 수량에서 그 자체가 오버플로되던 안정형 곡선의 정의역 가드로, N-version 레인이 발견했다: 레드 우선으로 해소했으며, 수정은 그것이 대체한 코드보다 작다.
- *두 대칭 채널 중 하나에만 적용된 수정* — 이 코드베이스가 가장 먼저 이름 붙인 결함 서명으로, 반복적으로 마주쳤다. 이제 모든 해소는 병합 전에 짝을 찾는다.

초대가 이 절의 요점이며, 그것은 상시적이다. 독립 독자들은 공개적으로 초대되며, 수량이 고정된 통화로 보상받는다. 저장소의 `SECURITY.md`에 있는 공개 채널을 통해 비공개로 보고하라.

## §22 신뢰해야 하는 것과 신뢰할 필요가 없는 것

모든 프로토콜은 무신뢰를 표방하지만, 그 예외를 열거할 수 있는 것은 거의 없다. 여기에 전체 목록이 있으며, 각 권한은 그것을 제한하는 문장과 함께 제시된다.

- **베뉴 레지스트리, 브리지 목록, V4 훅 허용 목록** — 큐레이션된다. 적대적 등재의 최악의 경우는 당신의 최솟값과 Iron-Law 하한 Φ 중 더 엄격한 쪽으로 제한되며, 원금에는 도달할 수 없다(§9.6, §14.6).
- **포기 이전의 Router 제어 키** — 수수료 수령처를 변경하고, 스왑을 일시 정지하고, 네이티브 문과 Permit2 문이 호출하는 WETH 및 Permit2 컨트랙트를 재지정하고, 48시간의 공개 지연 뒤에 잘못 보내진 토큰을 회수할 수 있다. 수수료율, 분할, 하한(컴파일 시점 상수, 세터 없음)을 바꾸거나 원금에 도달할 수는 없다(프록시 없음, 업그레이드 경로 없음, `selfdestruct` 없음, 정지 상태에서 보유하는 것 없음).
- **포기 이전의 Hub 제어 키** — Hub가 보는 Router, Solver, Quoter를 재지정하고, V4 매니저를 재지정하고, 레지스트리의 학습을 일시 정지하고, 운영자 좌석을 부여할 수 있다. 체결 가격을 잘못 매길 수는 없는데, 거래에 가격을 매기는 어떤 것도 레지스트리를 읽지 않기 때문이다.
- **트레저리** — 생성 시점에 고정되며, 30 / 70 분할에는 세터가 없다.
- **문들이 의존하는 표준 컨트랙트** — Permit2 문을 위한 Uniswap의 Permit2, 네이티브 문을 위한 체인의 WETH, V4 레그를 위한 PoolManager. 각각은 자신의 문이나 레그를 통해서만 호출되며, Router는 모든 레그의 가격을 자체 측정한 잔액 델타로 매기므로, 부정직한 상대방은 출력을 무너뜨리고 당신의 한계가 스왑을 거부한다.
- **토큰과 대응 관계** — BZPX가 여섯 개의 배포 불변식을 충족한다는 것, 그리고 당신이 호출하는 주소가 이 글과 대응한다는 것. §19 의 기록이 공개되는 순간, §19 가 명시하는 절차에 따라 확인할 수 있다.

신뢰할 필요가 없는 것: 당신의 거래에 가격을 매기는 누군가 — 경로는 실행 프레임에서 공개 상태로부터 도출되거나, 당신이 공급했다면 거기서 다시 측정된다. 그것을 공정하게 라우팅하는 누군가 — 하한은 측정된 출력으로부터 온체인에서 재도출되고, 수수료에는 견적과 전달 사이의 격차에 따라 커지는 항이 없다. 수수료를 세는 누군가 — Router는 수수료를 지불하지 않고는 정산을 거부한다. 그리고 계속 당신에게 서비스할 누군가 — 이 프로젝트가 운영하는 모든 서버가 오늘 밤 사라진다 해도, 컨트랙트는 내일도 오늘과 정확히 똑같이 견적하고, 라우팅하고, 정산할 것이다.

그리고 이 문서에서 하나의 습관만 간직한다면, 그것은 당신 자신의 거래에 설정하는 최솟값이어야 한다. 애그리게이터의 모든 한계는 결국 그 숫자로 귀결된다. 그것은 이 시스템에서 그 품질이 당신 외에 누구의 역량에도 의존하지 않는 유일한 보호 장치이다.

## §23 관련 연구

**라우팅.** Angeris, Evans, Chitra, Boyd는 상수 함수 시장 조성자(CFMM)에 걸친 라우팅을 최적점에서 베뉴 간 한계 가격을 균등화하는 볼록 계획 문제로 정식화했고, Diamandis, Resnick, Chitra, Angeris는 이에 대한 효율적인 쌍대 분해 알고리즘을 제시한다. 이 설계는 의도적으로 체인 위에서 그 최적점에 못 미치는 곳에 머문다. 분할 할당기는 한 번의 패스로 깊이에 비례하는 방식이며(§10.4), 반복도 베뉴별 곡률 모델도 필요 없고, 반복적 솔버가 가스 청구서이자 공격 표면이 되는 곳에서 견고하다. 변성 레인은 이 선택이 치르는 거리를 측정한다 — 분할은 결코 최선의 단일 풀보다 적게 지불하지 않으며(MR-R1), 충실도 매트릭스는 Base에서 Solver의 경로를 전수 그리드의 0.13 bps 이내에 위치시킨다. 상수곱 계열에 대해 한계 수익을 정확히 균등화하는 닫힌 형식은 베뉴당 두 개의 합과 하나의 제곱근이며 이후 릴리스의 후보이다. 목적 함수는 최적점 근처에서 평탄하며, 그래서 견고한 규칙이 먼저 출시된다. 오프체인 솔버 설계는 최적화를 서버로 옮기고 컨트랙트를 검사기로 축소한다. §3.1 의 의존성 예산은 그것이 치르는 비용에 대한 이 논문의 진술이다. 최근 연구는 모델을 가스로 확장한다. *Optimal Routing across Constant Function Market Makers with Gas Fees*는 가스가 무거래 영역을 넓히고 파편화와 비자명하게 상호작용함을 보이며, *Routing Frictions and Executable Liquidity in Fragmented Markets*는 광고된 유동성과 실행 가능한 유동성을 구분한다 — 이 설계가 모델링하는 대신 프레임 안에서 측정하는 바로 그 구분이다.

**견고한 집계.** §10.3 의 신뢰 중심은 깊이 가중 중앙값이며, 그 붕괴점은 가중치의 절반이다. Müller, Moumeni, Messaoudi의 *Cost of Manipulation in AMM-Based Oracles*(FC 2026)는 AMM 가격을 집계하는 오라클에 대해 대응하는 한계를 증명한다. 동일 가중치에서는 가장 저렴한 공격이 가장 얕은 풀들을 움직이는 반면, 유동성 가중치에서는 가중치의 절반을 장악하려면 전체 견적 깊이의 최소 절반이 필요하다. Ormer는 TWAP 평균 대신 중앙값을 추정하는 온체인 오라클을 제안하며, *From Means to Medians*는 어떤 벤치마크가 조작하기 가장 어려운지 연구한다. E25 는 같은 원리를 라우팅에 적용한다. 풀은 그 뒤에 서 있는 위험 자본만큼만 신뢰된다.

**베뉴.** Uniswap v2는 상수곱 기준과 V2 분기가 기본값으로 쓰는 30 bps 관례를 제공한다. Uniswap v3는 §4.2 의 닫힌 형식이 따르는 집중 유동성 모델, `slot0`, Q64.96 고정소수점을 제공한다. Uniswap v4는 §5 와 §14.2 가 기반하는 싱글턴, 플래시 회계, 훅 권한 주소 인코딩을 제공한다 — 여기의 설계는, 우리가 아는 한, V4 풀을 전적으로 프레임 안에서 도출하고, 증명하고, 가격을 매기고, 선별하고, 정산하는 최초의 설계이다. Egorov의 StableSwap은 이 프로토콜이 측정되지 않은 신뢰 가정 위에서 지원하는 대신 제거한 Curve 계열의 증폭된 불변식을 정의한다(§4.1). §6 의 Solidly 안정형 곡선에는 동료 심사 논문이 없으며, 배포된 구현으로 인용된다.

**MEV.** Daian 등은 순서 결정 적대자에 이름을 붙이고 그 규모를 보였으며, Zhou 등은 탈중앙화 거래소에 대한 샌드위치 공격을 측정하고 그 수익성을 정식화했다. 이 논문은 제거를 주장하지 않는다 — 순서는 컨트랙트 코드가 실행되기 전에 결정된다 — 대신 한계를 측정한다. §14.3 의 샌드위치 곡선을 공격자 쪽에서, 거부 경계와 그 너머에서 공격자가 겪는 음의 왕복 손익과 함께 측정한다.

**검증.** Clarkson과 Schneider의 하이퍼속성은 §21 의 재분배 발견이 속하는 등급 — 하나의 트레이스가 아니라 트레이스 집합의 속성 — 에 이름을 붙이며, 보존 법칙이 성립하는 동안에도 잘못된 당사자에게 지급될 수 있는 이유를 설명한다. Cousot와 Cousot의 추상 해석은 단일 틱 닫힌 형식의 틀이다. 이는 범위 안에서는 정확하고 범위 밖에서는 한 방향으로 건전한 베뉴 수학의 추상화이며, 하한이 구체화 검사 역할을 한다. Chen, Cheung, Yiu는 변성 테스트를 도입했으며, 이는 공식과 독립적인 오라클이 존재하지 않는 곳에서 §15.7 의 두 번째 심판이다. DeMillo, Lipton, Sayward는 변이 분석을 도입했으며, §15.2 의 큐레이션된 가드는 그 결합 가설에 의존한다. Avizienis의 N-version 프로그래밍은 §15.8 의 조상으로, 독립된 팀이 아니라 코드 생성에 적용되었다. Kuhn, Kacker, Lei의 조합 테스트는 §15.4 의 커버링 배열에 그 구성과 주장을 제공한다. 체계 요인의 모든 t-way 상호작용이 구성상 실행되며, 분모가 출력된다. Littlewood와 Strigini의 초고신뢰성 검증에 관한 결과는 이 논문 어디에서도 정확성의 확률을 명시하지 않는 이유이다.

**표준.** EIP-1153 일시적 저장소는 단일 콜백 원칙의 레그 컨텍스트, 수수료 원장, 재진입 락을 담는다. EIP-170은 Router와 Hub가 수백 바이트 이내로 근접해 있는 24,576바이트의 벽이다. EIP-1014는 𝒟의 CREATE2 산술이다. EIP-2612와 Uniswap의 Permit2는 두 번째 문의 서명 기반 전송을 정의한다. ERC-7201은 Hub의 저장소에 네임스페이스를 부여한다. EIP-7702 위임 계정은 다른 어떤 컨트랙트와도 같은 문을 호출한다.

## §24 결론

우리는 하나의 규율 — 믿지 말고 측정하라 — 위에 애그리게이터를 구축하고, 숫자가 누군가의 말을 그대로 받아들여질 수 있었던 모든 지점에서 그 규율을 지키고자 했다. 경로, 가격, 하한, 수수료 기준액, 베뉴 집합, 풀이 거래한다고 주장하는 쌍, 심지어 프로토콜 자신의 비용 모델까지.

결과는 강한 형태를 지닌 작은 시스템이다. 유동성 파편화는 수학의 문제가 아니라 언어의 문제로 드러났다. 베뉴의 증식 아래에는 작은 불변식 계열이 놓여 있으며, 각 불변식을 한 번씩 표현하는 프로토콜 — 하나의 디스패처, 하나의 도출, 하나의 하한, 하나의 활력장 — 은 베뉴별 특수 처리 없이 지형 전체의 가격을 매기고, 경로를 실행하는 트랜잭션 안에서 그 경로를 도출하며, 토큰이 당신에게 도달하기 전에 측정된 출력에 대해 자신의 약속을 다시 검사할 수 있다. 측정된 기록은 그것이 무엇을 사 주는지에 대한 가장 짧은 요약이다. 목(mock)과 라이브 Base에서 64번 중 64번 정확히 예측대로 정산되는 드리프트 없는 견적, 1 % 거래에 대한 샌드위치를 그 거래의 약 2.7 %로 제한하고 그 이상은 거부하는 하한, 네 체인에 걸쳐 0–1 베이시스 포인트만큼 떨어져 있는 미리보기와 전달, 그리고 자신이 어떻게 악용될 수 있는지와 무엇을 볼 수 없는지를 분모와 함께 공개하는 장치이다.

그 대가 역시 분명하다. 가스: 프레임 안에서 경로를 도출하는 것은 다른 곳에서 계산된 답을 정산하는 것보다 비싸다 — 누군가의 말을 그대로 받아들여야 하는 보장 대신 당신이 검사할 수 있는 보장의 가격이다 — 그리고 바이트–가스 교환율은 체인마다 어느 문이 그 가격을 가장 작게 만드는지 알려 준다. 불변성: 결함은 공개되고 현상금이 걸리지만, 패치되지 않는다 — 한 번 감사하면 되는 컨트랙트의 가격이며, §21 이 그 가격으로 사는 것이다.

논지를 다시 말하면: 선택하는 것이 곧 거래하는 것이다. 견적과 그 실행은 같은 계산이므로, 운영자의 정직성이 자리하던 틈은 산술로 닫힌다. 유동성을 거래함으로써 학습하고 저장하는 모든 쌍을 증명하는 레지스트리, 그리고 컴파일되어 고정되어 있기 때문에 저자들을 포함한 누구도 움직일 수 없는 수수료, 하한, 진입 표면. 이 프로토콜은 신뢰를 요구하지 않는다. 검증을 요구하며, 검증을 저렴하고, 공개적이며, 영구적으로 만들기 위해 작동한다.

## §25 Staking V4: 설계

Staking V4는 현재의 코드베이스이며, 주소는 출시 시 공개된다.

이 장은 스테이킹 엔진을 그 소스가 현재 서 있는 그대로 기술한다: `Blaze-Phoenix-Staking` 저장소의 `src/BlazePhoenixStaking.sol`, `cc614f9` 시점(2026-10-14)의 `main`이며, 이 파일은 `VERSION = "4.0.0"`을 선언한다(L169). 이 장의 모든 줄 번호(L*n*)는 그 커밋 시점의 해당 파일을 가리키며, 이 글과 소스가 어긋나는 곳에서는 소스가 우선한다. 이 장은 주소도, 배포 날짜도, 수익률도 담지 않는다: 이 장의 모든 수치는 컨트랙트에 컴파일된 상수이거나 그로부터 도출된 것이며, 그 도출은 수치 옆에 함께 제시된다.

이 엔진은 이 논문의 나머지 부분과 같은 규율을 따른다. 엔진은 하나의 토큰, BZPX만을 보유하며, 그 BZPX에 대한 모든 청구권은 원장 항목이고 컨트랙트는 각 상태 변경 전후로 그 합을 다시 검사한다(E35). 스테이커는 선택한 일수만큼 BZPX를 락하고 두 흐름 — 예정된 발행(emission)(E27–E29)과 차입자가 지불하는 이자(E31–E34) — 을 얻으며, 각 흐름은 약정에 따라 커지는 부스트로 가중된다(E30). 스테이커는 스테이크를 담보로 BZPX를 차입할 수 있다(E41). 손실이 발생하면 그 손실은 하나의 고정된 순서를 따르며(E37), 컨트랙트는 자신이 갚아야 할 것을 보유하고 있는지를 공개 뷰로 보고한다(E36).

### §25.1 아키텍처와 역할

하나의 컨트랙트 `BlazePhoenixStaking`이 `AccessControl`, `Pausable`, `ReentrancyGuard`를 상속한다(L167). 토큰, 트레저리, 발행 창(`emissionStart`, `emissionEnd`)은 불변값(immutable)이며(L266–269), 생성자는 코드가 없는 토큰 주소를 거부한다(L477). 오라클도, 업그레이드 경로도, 파라미터 설정 함수도 없다: 아래의 모든 상수는 컴파일되어 들어가 있다.

| 역할 | 보유자 | 허용 동작 | 줄 |
|---|---|---|---|
| `DEFAULT_ADMIN_ROLE` | 배포자, 생성 시 | 역할 부여 및 회수(`_setRoleAdmin` 호출이 존재하지 않으므로 이 역할이 모든 역할을 관리한다) | L485 |
| `ADMIN_ROLE` | 배포자, 생성 시 | `fundEmission`(누적 자금 공급 ≤ `TOTAL_REWARDS`); `withdrawReserve`(불변의 트레저리로만, 최대 `protocolReserve`까지, 하드 브리치 시 닫힘); `sweepUndistributedEmission`(`emissionEnd` 이후에만); `unpause`; `cancelEmergency`(지급 능력이 있는 장부에서만) | L486, L516, L533, L563, L580, L618 |
| `GUARDIAN_ROLE` | 생성 시에는 아무도 없음; 역할 관리자가 부여 | `pause`; `declareEmergency` — 둘 다 자금을 이동시키지 않는다 | L171, L578, L608 |
| 누구나 | — | `liquidate`; `tripBreaker`(장부가 하드 브리치 상태가 아니면 되돌려진다); `absorbBadDebt`; `lockStep` | L1066, L597, L639, L1291 |

어떤 역할도 스테이커의 원금을 이동시킬 수 없으며, 어떤 역할이든 수익을 보낼 수 있는 유일한 목적지는 불변의 트레저리다.

| 상수 | 값 | 의미 | 줄 |
|---|---|---|---|
| `TOTAL_REWARDS` | 180,000,000e18 | 발행 예산이자 누적 `fundEmission`의 상한 | L192 |
| `HALVING_PERIOD` | 2 × 365일 | 한 발행 기간의 길이 | L193 |
| `EMISSION_PERIODS` | 8 | 프로그램이 종료되기 전까지의 기간 수(`EMISSION_LENGTH` = 16년) | L194–195 |
| `INITIAL_REWARD_PER_SEC` | (`TOTAL_REWARDS` / 2) / `HALVING_PERIOD` | 기간 0의 발행률, $R_{0}$ | L196 |
| `MAX_STAKE_PER_WALLET` | 30,000,000e18 | 포지션당 스테이크 상한 | L198 |
| `MIN_EMISSION_WEIGHT` | 1,000e18 | 총 가중치가 이 값 미만이면 두 흐름 모두 비례적으로 억제된다 | L208 |
| `MAX_LTV` | 50 | 차입 상한, 순 스테이크 대비 % | L210 |
| `LIQ_THRESHOLD` | 95 | 부채 ≥ 스테이크의 95 %일 때 청산 가능 | L211 |
| `LIQ_BONUS_BPS` | 500 | 호출자에게 가는 청산 잉여, 부채 대비 bps | L212 |
| `RESERVE_FACTOR_BPS` | 300 | 각 이자 조각 중 `protocolReserve`에 남겨 두는 몫 | L213 |
| `MIN_LOCK_DAYS`, `MAX_LOCK_DAYS` | 90, 2,555 | 일 단위 락 한계 | L221–222 |
| `BOOST_BASE`, `BOOST_LINEAR`, `BOOST_QUAD` | 10,000, 750, 250 | 부스트 다항식, bps | L224–226 |
| `MIN_DEPOSIT_BLOCKS` | 10 | 출금, 순수 수익 청구 또는 락 변경 전에 예치가 경과해야 하는 블록 수 | L228 |
| `RATE_R0`, `RATE_RK`, `RATE_UK`, `RATE_S1`, `RATE_S2` | 100, 500, 0.8e18, 500, 72,500 | 꺾인 이자 곡선, 연 bps | L231–235 |
| `MAX_PROTOCOL_UTIL_WAD` | 0.75e18 | 새 차입에 대한 장부 이용률 상한 | L242 |
| `CONSERVATION_DUST` | 1e10 | 보존 가드의 허용 오차, wei 단위 | L247 |
| `MAINT_BASE`, `MAINT_DENSITY`, `MAINT_GAP_UNIT`, `MAINT_MAX_SCAN`, `MAINT_MAX_LOCK_ACTIONS` | 1, 50, 15분, 10, 4 | 자율 유지 관리 예산(E42) | L252–255, L263 |

### §25.2 흐름: 스테이크, 언스테이크, 청구, 차입

모든 사용자 진입점은 `deposit`이 정한 순서대로 하나의 파이프라인을 실행한다: 보존 항등식의 스냅숏을 찍고; 장부가 하드 브리치 상태이면 거부하고; 이자 인덱스와 발행 창을 전진시키고(`_updateGlobal`, L1428); 포지션이 보유했던 가중치로 두 흐름을 정산하고; 포지션이 지불해야 할 이자를 귀속시키고(`_accrueInterestFor`, L1627); 경과한 락을 해제하고; 동작을 수행하고; 단일 작성자를 통해 포지션의 가중치를 다시 계산하고(`_resync` → `_applyBoost` → `_checkpoint`, L1726–1756); 자율 유지 관리 창을 실행하고(E42); 보존 항등식을 다시 검사한다(E35). 문들은 각자가 덧붙이는 관문에서만 다르다.

| 진입점 | 일시 중지 | 비상 | 하드 브리치 | 추가 조건 |
|---|---|---|---|---|
| `deposit(amount, lockDays)` — 스테이크 | 닫힘 | 닫힘 | 닫힘 (L760) | $90 \le$ `lockDays` $\le \min(2{,}555,\ \text{days left to } \mathtt{emissionEnd})$; 포지션 스테이크 ≤ `MAX_STAKE_PER_WALLET`; `emissionEnd` 이전 (L743–760) |
| `lock(lockDays)` | 닫힘 | 닫힘 | — | 마지막 예치 이후 `MIN_DEPOSIT_BLOCKS` 경과 (L964–971) |
| `withdraw(amount)` — 언스테이크 | 열림 | 닫힘 | 닫힘 (L902) | 부채 없음; 락 경과; 마지막 예치 이후 `MIN_DEPOSIT_BLOCKS` 경과 (L890–895) |
| `claimRewards()` | 열림 | 닫힘 | 닫힘 (L932) | 발행과 순수 수익을 정산한다 (L930) |
| `claimPureYield()` | 열림 | 닫힘 | 닫힘 (L948) | 부채 없음; 마지막 예치 이후 `MIN_DEPOSIT_BLOCKS` 경과 (L944–948) |
| `borrow(amount)` | 닫힘 | 닫힘 | 닫힘 (L826) | E41 (L820–840) |
| `repay(amount)` | 열림 | 닫힘 | 열림 | — (L858) |
| `liquidate(user)` | 닫힘 | 닫힘 | 열림 | E39 (L1066) |
| `emergencyWithdraw()` — 출구 | — | 비상 시에만 | — | E40 (L658) |
| `absorbBadDebt(amount)` | 열림 | 열림 | 열림 | E38 (L639) |

**스테이크.** `deposit`은 BZPX를 포지션으로 끌어오고 그 락을 시작하거나 연장한다; 락은 결코 발행 프로그램의 종료 시점을 넘어설 수 없으며, 락으로 얻는 부스트(E30)는 락이 유효한 동안에만 지급된다. **언스테이크.** `withdraw`는 포지션이 갚을 것이 없고 락이 경과하면 원금을 돌려준다; 이 함수는 가디언의 일시 중지 중에도 열려 있으므로, 일시 중지는 누구도 가두지 않고 새로운 위험만 멈춘다. 비상 시 유일한 출구는 `emergencyWithdraw`이며, 이는 순자본을 지급하되 컨트랙트가 포지션들에 갚아야 할 것보다 적게 보유하고 있으면 비례적으로 축소하여 지급하고(E40), 포지션의 청구되지 않은 흐름은 몰수된다. **청구.** 수확은 두 청구 함수다: `claimRewards`는 두 흐름을 모두 지급하고, `claimPureYield`는 부채 없는 포지션에 대해 같은 것을 지급한다; 별도의 수확 함수는 없다. **차입, 상환, 청산.** 스테이커는 두 상한 안에서 자신의 스테이크를 담보로 BZPX를 차입할 수 있다(E41); 이자는 부채에 더해지는 것이 아니라 스테이크에서 차감된다(E34); `repay`는 일시 중지 중에도, 하드 브리치 중에도 열려 있는데, 부채를 청산하는 것은 장부를 더 안전하게 만들 뿐이기 때문이다. 부채가 스테이크의 95 %에 이른 포지션은 누구나 종료할 수 있으며, 종료한 자는 잉여를 받는다(E39).

![FIG-19 — 스테이킹 진입점들과 그 하나의 파이프라인: 각 문이 덧붙이는 관문, 이어서 보존 스냅숏, 브리치 관문, 전역 갱신, 정산, 이자 귀속, 락 만료, 동작, 단일 작성자 재동기화, 자율 유지 관리, 보존 검사; 비상 출구는 따로 서 있다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-19.png)

### §25.3 보상 회계

**발행.** $T$ = `TOTAL_REWARDS`, $P$ = `HALVING_PERIOD`, $R_{0}$ = `INITIAL_REWARD_PER_SEC`, $s$ = `emissionStart`라 하고, 시각 $t$에 대해 $\tau = t - s$, $p = \lfloor \tau / P \rfloor$라 하자. 누적 예정 발행량은 닫힌 형식이다(`_emittedAt`, L1458–1467):

$$
E(t) \;=\; \begin{cases} 0 & t \le s \\ \left(T - (T \gg p)\right) \;+\; (R_{0} \gg p)\,(\tau - pP) & s < t < s + 8P \\ T - (T \gg 8) & t \ge s + 8P \end{cases}
$$
^e27

*도출.* 정확한 산술에서 기간 $q$의 발행률은 $T/(2^{q+1}P)$이므로, 온전한 기간 $q$는 $T/2^{q+1}$을 발행하고 기간 $p$ 이전의 발행량은 $\sum_{q<p} T/2^{q+1} = T(1 - 2^{-p})$이다. $T = 1.8\times 10^{26}$은 $2^{8}$으로 나누어떨어지므로 $p \le 8$에 대해 $T \gg p = T/2^{p}$가 정확히 성립하고, 첫째 항은 반올림 없는 그 합이다. 발행률 항은 내림된다: $R_{0} \gg p = \lfloor\lfloor T/(2P)\rfloor / 2^{p}\rfloor = \lfloor T/(2^{p+1}P)\rfloor$이며, 정확한 발행률보다 초당 한 wei 미만만큼 모자란다. 따라서 한 기간 안에서 $E$는 선형으로 증가하고 정확한 곡선 아래에 머문다; 각 기간 전환 시점에는 $T/2^{p+1} - (R_{0} \gg p)\,P$만큼 계단식으로 올라가며, 이 도약은 음이 아니고 $P$ wei보다 작아서 곡선을 정확한 급수로 되돌린다. $E$는 단조 비감소이며 $T - T/2^{8} = \tfrac{255}{256}\,T$에서 종료된다: 마지막 $T/256$은 결코 예정되지 않으며, 비어 있었거나 자금이 공급되지 않은 창들이 남긴 몫과 함께, 종료 후 `sweepUndistributedEmission`에 의해서만 트레저리로 회수될 수 있다(L563). 여덟 번의 기간 전환에 대한 정확한 정수 평가는 모든 도약이 양수이고 $P$ 미만임을 확인한다.

![FIG-20 — 시간에 따른 예정 발행 E27: 발행률은 두 해마다 절반이 되고 누적 곡선은 열여섯 해 뒤 TOTAL_REWARDS의 255/256에서 종료된다; 예정된 것이지 지급된 것이 아니다 — 가중치가 없는 창은 아무것도 발행하지 않는다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-20.png)

**창.** 모든 진입점에서, $W$ = `totalBoostedEffective`, $W_{\min}$ = `MIN_EMISSION_WEIGHT`, $t_{\ell}$ = `lastRewardTime`, $t' = \min(t, \mathtt{emissionEnd})$로 두면, 경과한 창은 가중치당 누산기 $A$ = `accRewardPerShare`에 지급된다(`_updateGlobal`, L1428–1448):

$$
r \;=\; \min\!\left( \left\lfloor \frac{\left(E(t') - E(t_{\ell})\right)\cdot \min(W, W_{\min})}{W_{\min}} \right\rfloor,\ \mathtt{rewardReserve} \right), \qquad A \;\leftarrow\; A + \left\lfloor \frac{r \cdot 10^{18}}{W} \right\rfloor
$$
^e28

`rewardReserve`는 $r$만큼 줄고 `totalRewardDistributed`는 $r$만큼 늘어난다. $W = 0$이면 시계는 움직이지만 아무것도 포착되지 않으므로 누구도 밀린 몫을 물려받지 않는다; $W_{\min}$ 미만에서는 창이 비례적으로 억제되므로 먼지 수준의 포지션이 일정을 홀로 가져갈 수 없다; 그리고 각 창은 준비금으로 상한이 정해지므로 `fundEmission`은 새 자금이 들어오기 전에 시계를 정산하며(L523), 따라서 추가 공급이 지난 창을 소급하여 지급하는 일은 결코 없다.

**정산.** 발행 가중치 $w_{u}$와 체크포인트 $c_{u}$(`rewardDebt`)를 가진 포지션이 받을 몫은 다음과 같다(`_settlePendingRewards`, L1487–1497; 순수 수익 쪽의 쌍둥이 함수 L1499–1517은 $w^{\mathrm{p}}_{u}$, $A^{\mathrm{p}}$, `pureYieldDebt`를 사용한다):

$$
\mathrm{due}_{u} \;=\; \left(\left\lfloor \frac{w_{u}\,A}{10^{18}} \right\rfloor - c_{u}\right)^{+}, \qquad c_{u} \;\leftarrow\; \left\lfloor \frac{w_{u}\,A}{10^{18}} \right\rfloor
$$
^e29

그리고 가중치가 바뀔 때마다 단일 작성자가 $c_{u} = \lfloor w_{u}A/10^{18}\rfloor$로 체크포인트를 다시 찍으므로(`_checkpoint`, L1736), 포지션은 자신의 가중치가 자리 잡은 뒤에 닫히는 창에 대해서만 수익을 얻는다.

**부스트.** 가중치는 포지션의 스테이크에, 포지션이 *현재* 그 대가를 받고 있는 락에 대한 다항식인 부스트를 곱한 것이다(`boostByDays`, L496–505; `_effectiveLockDays`, L1687; `_computeBoost`, L1695–1700). $\ell$을 일 단위 락 — 락 해제 시각이 지나면 포지션이 정규화되었든 아니든 영 — 이라 하고 2,555로 상한을 두면:

$$
b(\ell) \;=\; 10^{4} + \left\lfloor \frac{750\,\ell}{365} \right\rfloor + \left\lfloor \frac{250\,\ell^{2}}{365^{2}} \right\rfloor \ \text{bps}, \qquad w_{u} = \left\lfloor \frac{(s_{u} - d_{u})^{+}\, b}{10^{4}} \right\rfloor, \qquad w^{\mathrm{p}}_{u} = \begin{cases} \lfloor s_{u}\, b / 10^{4} \rfloor & d_{u} = 0 \\ 0 & d_{u} > 0 \end{cases}
$$
^e30

여기서 $s_{u}$는 포지션의 스테이크, $d_{u}$는 그 부채다. 정확한 값: $b(90) = 10{,}199$, $b(365) = 11{,}000$, $b(730) = 12{,}500$, $b(1{,}825) = 20{,}000$, $b(2{,}555) = 27{,}500$ bps. 발행 가중치는 순 스테이크에 매겨진다; 순수 수익 가중치는 부채 없는 포지션에만 존재한다 — 차입자는 자신이 지불하는 이자를 벌지 않는다.

### §25.4 이자: 곡선과 인덱스

**곡선.** 이용률은 $U = \min(\lfloor D \cdot 10^{18} / S \rfloor, 10^{18})$이며, $D$ = `totalDebt`, $S$ = `totalStaked`이다($S = 0 < D$이면 $U = 10^{18}$; 둘 다 영이면 기본 이율). bps 단위의 연 이율은 80 %에서 꺾인다(`_interestRate`, L1768–1782):

$$
r(U) \;=\; \begin{cases} 100 + \left\lfloor 500\,U / 10^{18} \right\rfloor & U \le 0.8 \cdot 10^{18} \\ 500 + \left\lfloor 72{,}500\,(U - 0.8\cdot 10^{18}) / 10^{18} \right\rfloor & U > 0.8 \cdot 10^{18} \end{cases}
$$
^e31

두 분기는 꺾임점에서 만나며 — $100 + 0.8 \times 500 = 500$ — 곡선은 완전 이용률에서 $500 + 0.2 \times 72{,}500 = 15{,}000$ bps에 이른다. 차입은 장부를 75 % 위로 끌어올릴 수 없으므로(E41), 어떤 차입도 그것만으로는 가파른 분기에 들어서지 않는다.

**인덱스.** 경과한 각 조각 $\Delta t$에는 $D$나 $S$에 대한 어떤 쓰기보다 먼저 그 조각 전체에 걸쳐 유효했던 이율이 찍힌다(`_interestSlice`, L1589–1619; `_updateInterestIndex`, L1539–1583). $Y$ = 365일로 두면:

$$
\delta \;=\; \left\lfloor \frac{\lfloor 10^{18}\, r(U) / 10^{4} \rfloor \cdot \Delta t}{Y} \right\rfloor, \qquad \sigma \;=\; \left\lfloor \frac{D\,\delta}{10^{18}} \right\rfloor, \qquad I \leftarrow I + \delta, \quad S \leftarrow S - \sigma
$$
^e32

$\sigma$가 $S$를 초과하게 되면 인덱스 전진량은 $\delta = \lfloor S \cdot 10^{18}/D \rfloor$로 다시 도출되고 $\sigma$는 그로부터 재계산되므로, 포지션별 부과액의 합은 결코 전역 차감액을 넘지 않는다. 이 산술은 되돌리는 대신 영으로 포화되므로 그 아래에서 실행되는 진입점들이 멈출 수 없으며, 비상 시에는 시계가 부과 없이 미끄러진다(L1547). 하나의 함수가 실행과 모든 견적을 위해 조각을 계산하므로, 둘은 서로 다르게 반올림될 수 없다.

**분할.** 조각은 스테이크에서 빠져나가 같은 단계에서 나뉜다(L1560–1580; `_dampPure`, L1623–1625). $W_{\mathrm{p}}$ = `totalBoostedPure`, $A^{\mathrm{p}}$ = `accPureYieldPerShare`로 두면:

$$
\kappa_{r} = \left\lfloor \frac{300\,\sigma}{10^{4}} \right\rfloor, \quad \pi = \sigma - \kappa_{r}, \quad \eta = \left\lfloor \frac{\pi \cdot \min(W_{\mathrm{p}}, W_{\min})}{W_{\min}} \right\rfloor, \quad A^{\mathrm{p}} \leftarrow A^{\mathrm{p}} + \left\lfloor \frac{\eta \cdot 10^{18}}{W_{\mathrm{p}}} \right\rfloor, \quad \mathtt{protocolReserve} \mathrel{+}= \kappa_{r} + (\pi - \eta)
$$
^e33

따라서 $\sigma = \kappa_{r} + \eta + (\pi - \eta)$가 정확히 성립한다; 순수 가중치가 없으면 $\pi$ 전부가 준비금으로 간다. 억제는 발행의 억제(E28)와 같으며, 같은 상수를 쓴다.

**귀속.** 전역 쪽은 이자가 발생할 때 정산되었다; 포지션 자신의 몫은 그 포지션이 다음에 건드려질 때 귀속된다(`_accrueInterestFor`, L1627–1671). $I_{u}$를 그 포지션의 인덱스 체크포인트라 하면:

$$
i_{u} \;=\; \left\lfloor \frac{d_{u}\,(I - I_{u})}{10^{18}} \right\rfloor; \qquad i_{u} > s_{u} \;\Rightarrow\; \lambda = i_{u} - s_{u} \ \text{enters E37},\ S \mathrel{+}= \lambda,\ i_{u} \leftarrow s_{u}; \qquad s_{u} \leftarrow s_{u} - i_{u}, \quad I_{u} \leftarrow I
$$
^e34

이자는 스테이크에서 지급된다; 부채 자체는 늘어나지 않는다. 부채 없는 포지션은 체크포인트만 다시 찍으므로, 아무것도 빚지지 않았던 시간의 인덱스 증가를 결코 물려받지 않는다. 포지션이 지불할 수 없는 이자도 이미 분배되었다(E33); 그것을 $S$에 다시 더하면 $S = \sum_{u} s_{u}$가 유지되고, 손실은 그것이 알려지는 순간 기장된다(E37).

### §25.5 준비금, 지급 능력, 손실 워터폴

**보존.** $B$를 컨트랙트의 BZPX 잔액, $D$ = `totalDebt`, $\mathit{BD}$ = `totalBadDebt`, $S$ = `totalStaked`, $\mathit{RR}$ = `rewardReserve`, $\mathit{PR}$ = `protocolReserve`, $\Pi$ = `totalRewardDistributed − totalRewardsPaid`(분배되었으나 아직 지급되지 않은 것)라 하면, `conserves` 수정자를 단 모든 함수는 본문 실행 전에 양변의 스냅숏을 찍고, 실행 후 다음이 성립하지 않으면 되돌린다(L419–438; `_conservationSnapshot`, `_conservationCheck`, L449–457):

$$
\left|\, \Delta(B + D + \mathit{BD}) \;-\; \Delta(S + \mathit{RR} + \mathit{PR} + \Pi) \,\right| \;\le\; \varepsilon, \qquad \varepsilon = \mathtt{CONSERVATION\_DUST} = 10^{10}\ \text{wei}
$$
^e35

모든 항은 음이 아닌 단일 상태 변수이고 하한 없이 합산되므로, 항등식은 모든 체계에서 선형이다 — 회복 도구(`repay`, `liquidate`, `absorbBadDebt`)가 계속 작동해야 하는 말기 부실 상태를 포함해서 — 그리고 한 호출 전후의 변화만 비교되므로, 이력이 누적되어 거짓 경보가 될 수 없다.

**지급 능력.** 컨트랙트가 갚아야 할 것과, 그것을 보유하고 있는지에 대한 공개 검사(`_owed`, L1794–1798; `_hardBreach`, L1808–1810; `isSolvent`, L1824):

$$
\mathrm{owed} \;=\; (S + \mathit{RR} + \mathit{PR} - D)^{+} \;+\; \Pi, \qquad \mathrm{hardBreach} \iff B + \varepsilon < \mathrm{owed}, \qquad \mathtt{isSolvent}() = \neg\,\mathrm{hardBreach}
$$
^e36

*부실 채권과의 연결 도출.* $S + \mathit{RR} + \mathit{PR} \ge D$인 경우, 모든 보호된 호출에 걸쳐 $\Delta(B - \mathrm{owed} + \mathit{BD}) = 0$($\varepsilon$ 이내)이 성립하므로(E35), $c$를 원장 바깥에 보유된 잔액이라 할 때 $B - \mathrm{owed} = c - \mathit{BD}$이다. 따라서 기록된 부실 채권은 정확히 장부를 하드 브리치 쪽으로 미는 항이며, $\mathit{BD} > c + \varepsilon$이 되는 순간 장부는 지급 불능으로 읽힌다. 하드 브리치 하에서는 액면대로 지급하거나 부족한 자금 풀에 새 돈을 더하게 될 문들이 되돌려진다 — `deposit`, `borrow`, `withdraw`, `claimRewards`, `claimPureYield`와 그 안의 순수 수익 정산, `withdrawReserve`, `cancelEmergency`(L760, L826, L902, L932, L948, L1510, L539, L620) — 반면 `repay`, `liquidate`, `absorbBadDebt`는 열려 있으며, 누구나 `tripBreaker`를 호출하여 컨트랙트를 비상 상태로 전환할 수 있다(L597).

**워터폴.** 손실 $\lambda$는 오직 세 곳에서만 발생한다: 부채가 스테이크를 초과하는 청산(L1101–1106, L1128), 수면 아래의 비상 출구(L704–709), 그리고 포지션이 지불할 수 없는 이자(L1657–1663). 세 곳 모두 같은 순서를 적용한다:

$$
\kappa \;=\; \min(\lambda,\ \mathit{PR}), \qquad \mathit{PR} \leftarrow \mathit{PR} - \kappa, \qquad \mathit{BD} \leftarrow \mathit{BD} + (\lambda - \kappa)
$$
^e37

준비금 — 모든 이자 조각의 준비금 계수(E33)와 자기 청산 보너스로 쌓인 것 — 이 먼저 흡수한다; 덮이지 않은 나머지만 기록된 부실 채권이 된다. 재자본화는 공개된 문으로, 먼저 준비금을 사용하고, 그다음 호출자가 제공하는 만큼까지만, 그리고 결코 아직 덮이지 않은 것보다 많이는 받지 않는다(`absorbBadDebt`, L639–649). $a$를 호출자의 금액이라 하면:

$$
\rho \;=\; \min(\mathit{BD},\ \mathit{PR}), \qquad \gamma \;=\; \min(a,\ \mathit{BD} - \rho), \qquad \mathit{PR} \leftarrow \mathit{PR} - \rho, \qquad \mathit{BD} \leftarrow \mathit{BD} - \rho - \gamma, \qquad B \leftarrow B + \gamma
$$
^e38

이 함수는 $\rho + \gamma = 0$일 때 되돌려지고, 아무것도 밖으로 내보내지 않으며 호출자에게 아무것도 지급하지 않는다; E35 역시 구성상 성립한다($\Delta \mathrm{LHS} = \gamma - \rho - \gamma = -\rho = \Delta \mathrm{RHS}$). $\mathit{BD}$가 먼지 허용치 이내로 돌아오면 `isSolvent()`는 참을 읽고 관리자는 비상 상태를 취소할 수 있다.

**청산.** 포지션은 $d_{u} > 0$이고 ($s_{u} = 0$ 또는 $100\,d_{u} \ge 95\,s_{u}$)일 때 그리고 그때에만 청산 가능하다(`_isLiquidatable`, L1133–1137). 그것을 종료하는 것(`_executeLiquidation`, L1079–1131):

$$
\mathrm{seized} \;=\; \min\!\left(d_{u} + \left\lfloor \frac{500\,d_{u}}{10^{4}} \right\rfloor,\ s_{u}\right), \qquad \mathrm{bonus} \;=\; (\mathrm{seized} - d_{u})^{+}, \qquad \lambda \;=\; (d_{u} - s_{u})^{+} \ \text{to E37}
$$
^e39

보너스는 호출자에게 가지만, 차입자가 자신의 포지션을 종료하는 경우에는 `protocolReserve`에 남는다. 포지션이 벌어들인 흐름은 종료 전에 지급되며, 남은 스테이크는 락을 유지하는 부채 없는 포지션으로 남는다.

**비상 출구.** $\mathrm{pot} = B$, 그리고 추적되는 $E_{+}$ = `totalPositiveEquity` $= \sum_{u} \max(s_{u} - d_{u}, 0)$로 두면, 출구는 다음을 지급한다(`emergencyWithdraw`, L658–725; 축소 처리, L689–696):

$$
x_{0} \;=\; (s_{u} - d_{u})^{+}, \qquad E' \;=\; E_{+} - e_{u} + x_{0}, \qquad x \;=\; \min\!\left( \begin{cases} \lfloor x_{0}\,\mathrm{pot} / E' \rfloor & \mathrm{pot} < E' \\ x_{0} & \text{otherwise} \end{cases},\ \mathrm{pot} \right)
$$
^e40

여기서 $e_{u}$는 퇴장자의 추적 항으로, 이자 귀속 후 정확한 자본으로 대체된다. 지급 능력이 있는 장부에서는 축소가 결코 작동하지 않고 순자본이 전액 지급된다; 부족한 장부에서는 각 출구가 자신의 청구액에서 같은 비율을 가져가며, 수면 아래의 출구는 그 부족분을 기장한다(E37, L704–709).

![FIG-21 — 손실 워터폴 E37–E38: protocolReserve가 먼저 흡수하고, 나머지는 totalBadDebt로 기록되어 isSolvent()가 읽으며, absorbBadDebt는 준비금에서, 그다음 호출자로부터 재자본화하되 결코 덮이지 않은 것을 넘지 않는다; 비율은 예시이며 토큰 금액은 없다.](https://blazephoenix.xyz/whitepaper/figs/ko/fig-21.png)

**차입 상한.** 금액 $a$의 차입은 다음 둘을 모두 만족해야 한다(`borrow`, L834–840; `_ltvCap`, L1908–1909):

$$
d_{u} + a \;\le\; \left\lfloor \frac{50\,(s_{u} - d_{u})^{+}}{100} \right\rfloor, \qquad \left\lfloor \frac{(D + a)\cdot 10^{18}}{S} \right\rfloor \;\le\; 0.75 \cdot 10^{18}
$$
^e41

첫째는 포지션의 순 스테이크에 대한 것이고, 둘째는 장부 전체에 대한 것으로 80 % 꺾임점(E31) 아래로 유지된다.

**유지 관리.** 각 사용자 트랜잭션은, 컨트랙트가 일시 중지되었거나 비상 상태가 아닌 한, 차입자 레지스트리의 창(수면 아래에 있는 것을 청산)과 락 레지스트리의 창(만료된 락을 정규화)도 함께 스캔하며, 각 창의 크기는 다음과 같다(`_windowBudget`, L1148–1157; `_autoMaintain`, L1170–1178):

$$
c(n) \;=\; \min\!\left(1 + \left\lfloor \frac{n}{50} \right\rfloor + \left\lfloor \frac{t - t_{m}}{15\ \mathrm{min}} \right\rfloor,\ 10,\ n\right)
$$
^e42

여기서 $n$은 레지스트리 크기, $t_{m}$은 마지막 스윕 시각이다; 트랜잭션당 락 정규화는 최대 네 번 실행된다(L263). 이 일정은 온체인 상태의 순수 함수이며 — 키퍼도, 거버넌스도 없다 — 그 가스는 모든 경우에 유계이다.

**불변식.** 이 설계는 아홉 개의 불변식 INV-23 – INV-31(부록 B)을 추가한다: 보존 항등식, 발행 상한, 손실 순서, 브리치 하에서 닫힌 문들, 재자본화 한계, 가중치의 단일 작성자, 차입 상한, 유계의 비상 출구, 그리고 예치 경과 규칙.

### §25.6 V4의 강화

병합된 세 변경이 Base에 배포된 설계 이후의 V4 소스를 형성했다; 각각은 그것이 만들어 낸 설계의 수준에서 여기에 기술된다.

- **#22 (`b44e238`, 2026-10-11).** 이자는 실행과 모든 견적이 공유하는 하나의 함수에 의해 조각별로 찍히므로(E32), 미리 보기와 트랜잭션은 그 조각 전체에 걸쳐 유효한 이율로 같은 부과액을 계산한다. 순수 수익 채널은 발행과 같은 가중치 하한으로 억제되므로(E33), 잔여 분모가 흐름을 포착할 수 없다. 자신의 포지션을 종료하는 차입자는 청산 보너스를 받지 않는다. 읽기 전용 위험 뷰가 추가되었다(`healthFactor`, `daysToLiquidation`, `maxBorrowOf`).
- **#26 (`7fc0c2a`, 2026-10-13).** 세 손실 발생 지점 모두에 대한 하나의 손실 순서: 먼저 준비금, 그다음 기록된 부실 채권(E37); 공개된 재자본화 문(E38); 하드 브리치 하에서 닫히는 가치 이동 문들(E36); 코드가 없는 토큰 주소를 거부하는 생성자; 보호되는 모든 지점에 인라인되는 대신 두 개의 내부 헬퍼로 한 번 방출되는 보존 검사(항등식은 변경 없음); 그리고 보증 테스트 스위트.
- **#25 (`cc614f9`, 2026-10-14).** 문서 전용: 가이드와 테스트 헤더의 짧은 요약 줄. 컨트랙트 소스는 변경되지 않았다.

### §25.7 테스트 증거

`cc614f9` 시점의 `origin/main`에서, 실행이 아니라 소스 트리로부터 집계한 것: 37개의 `*.t.sol` 파일(`git ls-tree -r --name-only origin/main | grep -c '\.t\.sol$'`)이 256개의 `test*` 함수와 14개의 `invariant*` 함수를 선언한다(`git grep -E '^\s*function (test|invariant)' origin/main -- '*.t.sol'`, 접두사별로 분리). 저장소의 README(84번째 줄)는 그 옆에 아홉 개 스위트로 된 Node 하니스가 있다고 밝힌다. 이것들은 선언된 것의 개수이다; 이 논문은 스테이킹 스위트의 통과율을 보고하지 않는다.

---

## 개정 이력

### 버전 2.3.0 (2026-10-14)

2.3.0 — 스테이킹 V4 설계 장(§25)을 추가하며, 방정식 E27–E42, 불변식 INV-23–INV-31, 그림 FIG-19–FIG-21 이 함께 들어간다. 이는 스테이킹 엔진이 더 이상 이 논문에서 기술되지 않는다는 아래의 진술을 대체한다. 그 밖에는 버전 표기, 열거 범위, 부록 소개문, DOI 행만 바뀌며 나머지는 2.3 의 본문 그대로이다.

### 버전 2.3 (2026-09-24)

버전 2.3 은 2.2 계열을 잇는다. 이 본문의 초안에 잠시 쓰였던 3.0 번호 부여는 철회되며, 그보다 앞선 2026-09-01 의 3.0 번호 부여도 마찬가지이다. 2.2 와 달라진 점은 다음과 같다. 열여덟 개의 혁신에 이름을 붙이고 I-1 부터 I-18 까지 번호를 매겨 그 수학과 함께 논문 맨 앞에 서술하며, 측정된 기록의 스코어보드를 두었다. 모든 그림을 `8916545` 시점의 `main` 에 대해 다시 측정했다. 스테이킹 엔진은 이 논문에서 더 이상 기술되지 않는다(그 자체의 명세가 이를 담는다). 토큰 절은 130,000,000 BZPX 규모의 단일 Airdrop & Bounties 풀을 기록한다. 그리고 아래의 2.2 정오표는 그것이 바로잡던 본문에 반영되었다.

### 2.2 정오표 (2026-09-07)

2026-09-06 본문을 `main 8949a9d` 에 대해 적대적으로 읽은 뒤 적용되었으며, 버전 표기는 2.2 로 유지되었다. 각 항목은 본문이 말한 바와 체크아웃이 말하는 바를 밝힌다.

1. §4.2 — 경계 클램프 `sqrtBoundary` 는 Core 에 정의되어 있으나 순위 산정이나 프레임 내 약속에는 아직 연결되지 않았다. 본문은 이것이 약속 측에 적용된다고 적었다. 미결 항목: 이를 연결할 것(`Router._v4LegQuote`, Core 순위 산정 분기). V4 레그가 자신의 범위를 벗어날 때 E21 버퍼를 넘어 순 견적보다 낮게 인도되는 경우를 보이는 레드 우선(red-first) 테스트를 함께 둔다.
2. §10.4 — 용량 클램프는 V4 레그를 제외한다(Solver 는 종류 4 와 8 에 대해 잔액 읽기를 건너뛴다). 본문은 모든 집중형 레그가 클램프된다고 적었다. 미결 항목: 싱글턴에 대한 물리적 한계.
3. §5.4 — 정적 수수료 V4 키는 관리자의 프로토콜 수수료를 무시한다(현재 서비스되는 모든 체인에서 영). 본문은 키의 수수료가 모호하지 않다고 말했다.
4. §9.3 — 퇴출 마진은 3 % 가 아니라 약 25 % 까지 내려간다(기존 51, 신규 64). 앞선 수치는 E13 을 위반했으며 소스 주석에서 옮겨 온 것이었다.
5. §15 와 초록 — `invariant_*` 함수 43 개(출력된 명령이 43 을 반환한다), `.t.sol` 파일 217 개.
6. §14.1 — `HubE(6)` 는 존재하지 않는다. 팩토리 소진은 `HubE(4)` 이고, 슬롯 소진은 `_canInsert` 이다.
7. §14.2 — "어느 한 계층으로 충분하다"는 철회된다. 계층들은 델타 변경을 제한하며, §5.4 의 수수료 오버라이드 잔여분은 하한이 제한한다.
8. §11.4, INV-21 — 경로 형태 규칙은 "마지막 홉에만 훅"이 아니라 경로 전체에 걸친 정규 순서(`sawHooked`)이다.
9. §11.3, E24 — 집계 홉 하한의 여유분은 BPS 로 나뉜다. 즉 증명된 레그 평균의 20 % 이다.
10. §17.3 과 부록 C — `FactoryCensus` 하니스는 비공개 배포 트리에 있다. 이 표는 보고된 것이며 이 체크아웃에서는 재현할 수 없다.
11. §17.5 — 가스 기준이 명명되었고(KyberSwap `MetaAggregationRouterV2`, Base 포크, 2026-08-22, 두 번째 비교 대상으로 Paraswap) 레그당 비율 옆에 거래당 비율(2.7×)이 함께 인쇄된다. 하니스와 포착된 calldata 는 비공개이므로 수치는 보고된 것이며 이 체크아웃에서 재현할 수 없다.
12. §17.2 — "체인 최초의 V4 라우팅 애그리게이션"은 철회된다. "정확"은 측정된 대역(0–1 bps)으로 대체된다.
13. INV-8, INV-10, INV-15, INV-20, INV-22 — 시행 인용이 이를 담당하는 테스트와 코드로 정정되었다. INV-22 는 배포된 네 컨트랙트를 명시한다.
14. §1 — 애그리게이터 조사는 2026-09-07 에 문서화된 대로 명명된 일곱 곳으로 한정된다.
15. 참고문헌 16 과 20 — EIP-1153 은 A. Akhunov 와 M. Salem 의, EIP-7702 는 V. Buterin, S. Wilson, A. Dietrichs, M. Garnett 의 것이다.
16. §2.3, §15.11, §21 — 계측기 보정 및 결함 패턴의 개수는 보정 규칙 그 자체로 대체되었다.
- §14.2, 계층 2 (2026-09-07, 훅 허용): 허용 목록은 자동 문을 선별하고, 코드해시 고정은 코드가 바뀐 목록상의 훅을 모든 문에서 일시 중지시킨다. 주소 비트에 스왑 권한이 없는 훅은 스왑에서 결코 실행되지 않으므로 목록도 고정도 필요 없다. 스왑에서 실행되며 아무도 목록에 올리지 않은 훅은 명시적 문에서 호출자의 서명된 선택으로 수용되고, 모든 베뉴와 마찬가지로 프레임 내 약속, 증명 게이트, `userMinOut` 의 제한을 받으며, 자동 문이 이를 제안하는 일은 결코 없다. 훅이 달린 풀을 등록하면 그 훅이 허용되고 고정되므로, 훅이 달린 풀은 운영자의 한 단계로 끝난다. 세 계층은 유지되며, "기본 폐쇄"는 자동 문을 가리킨다.

### 2.2 정오표 (2026-09-24)

아홉 번째 바운티 파동 이후에 적용되었으며, 버전 표기는 2.2 로 유지되었다.

1. §4.2, 그리고 2026-09-07 정오표의 항목 1 — 종결. V4 풀은 자신의 장부를 따라 걸어서 견적한다. 가격, 유동성, 틱 비트맵과 틱을 `extsload` 로 읽고 PoolManager 자체의 스텝 산술로 계산하며, 순위 산정과 약속 모두에 같은 방식이 쓰인다(`Core.v4WalkOut`). 경계 클램프 `sqrtBoundary` 는 폐기된다. 디스패처가 틱 횡단 산술을 복제하지 않는다는 진술은 이제 V3 계열에만 해당한다.
2. §10.4, 그리고 2026-09-07 정오표의 항목 2 — 그것이 맡았던 목적에 한하여 종결. 이 걸음은 횡단하는 틱 안에서 장부가 보유한 유동성만을 계산하므로, V4 레그는 자신의 풀이 보유한 만큼으로 제한된다(`test_AOneTickRangeWithNothingBeyondPaysWhatItHolds`).
3. 2026-09-07 정오표의 항목 3 — 종결. 정적 수수료 키는 관리자의 프로토콜 수수료를 키의 LP 수수료와 스왑 방향에 맞춰 합성한다(`Core.effV4Fee`, `test_StaticKey_ProtocolFee_ReachesThePromise`).
4. §4 — 견적 경로가 풀에 대해 수행하는 모든 읽기는 가스 상한이 걸려 있고 고정된 수의 워드를 복사한다. 상위 수준 호출이던 두 가지(Solidly 페어의 `getAmountOut`, 그리고 `factory()` 다음의 `getFee`)는 `Core._askWord` 를 통해 묻는다(`test/APoolsAnswerIsBoundedByTheReader.t.sol`).

## 부록 A · 원시 방정식

이 논문의 모든 정량적 주장은 아래 방정식 중 하나로 환원된다. 각 방정식은 명시된 컨트랙트에서 단 한 번 구현되며 다른 모든 곳에서는 가져다 쓴다. E1–E22 의 번호는 이전 판에서 그대로 이어지며, E23–E26 은 새로 추가되어 뒤에 덧붙었다. E27–E42(2.3.0)는 스테이킹 엔진의 것으로, 스테이킹 저장소 `cc614f9` 의 `src/BlazePhoenixStaking.sol` 에 정의되어 있다(§25). 네 번째 열의 "Staking"은 해당 컨트랙트를 가리키며 그 줄 번호가 함께 적힌다.

| # | 이름 | 방정식 | 정의된 곳 | § |
|---|---|---|---|---|
| E1 | 메타 방정식 | $R^{\star} = \arg\max_{R}\ \mathrm{net}(R)\cdot\prod_{\ell}\hat{\Psi}(p_{\ell})\cdot\mathbf{1}[\mathrm{net}(R)\ge\Phi(R)]\cdot\Xi(R)$ | Solver 파이프라인 | §3 |
| E2 | 상수곱 출력 | $y = \lfloor x(\mathrm{BPS}-f)\,r_{\mathrm{out}} / (r_{\mathrm{in}}\,\mathrm{BPS} + x(\mathrm{BPS}-f)) \rfloor$ | Core `outV2` | §4.2 |
| E3 | 집중 유동성 출력 (틱 내부) | $\sqrt{P}' = L\sqrt{P}/(L + x_{f}\sqrt{P}/Q_{96})$, $y = L(\sqrt{P}-\sqrt{P}')/Q_{96}$; 반대 방향에는 쌍대 형태 | Core `outV3` | §4.2 |
| E4 | V4 저장소 기준 슬롯 | $\mathrm{slot} = \mathrm{keccak256}(\mathrm{abi.encode}(\mathrm{poolId}, 6))$; $\sqrt{P}$ 는 워드 0 의 비트 [0,160), LP 수수료는 [208,232), $L$ 은 워드 3 | Core `v4SqrtAndLiq` | §5 |
| E5 | Solidly 안정형 불변식 | $k(x,y) = x^{3}y + xy^{3}$ | Core `_solK` | §6 |
| E6 | Solidly 뉴턴 스텝 | 시드 $y_{0} = Y$; 상향 스텝 $\max(1, \lfloor (K-k)/f' \rfloor)$; 하향 스텝 $\max(1, \lfloor (k-K)/f' \rfloor)$, 영을 넘게 되면 $\lfloor y/2 \rfloor$ 로 대체; 종료 조건 $\lvert y_{n+1}-y_{n}\rvert \le 1$, 테이커에게 불리하게 한 단위 가산; 상한 64 → 시드 → 견적 0 | Core `_solY` | §6 |
| E7 | 소수 자릿수 정규화 | $X = r_{\mathrm{in}}10^{18-d_{\mathrm{in}}}$, $Y = r_{\mathrm{out}}10^{18-d_{\mathrm{out}}}$; 풀이 후 원래 스케일로 복원 | Core `_solidlyStable` | §6 |
| E8 | CREATE2 도출 | $\mathrm{pool} = \mathrm{addr}_{20}(\mathrm{keccak256}(\mathtt{0xff} \Vert \mathrm{origin} \Vert \mathrm{salt} \Vert \mathrm{initCodeHash}))$ | Core `create2Address`, `deriveAddress` | §7 |
| E9 | 모노슬롯 패킹 | $s = a \,\vert\, f\!\ll\!8 \,\vert\, k\!\ll\!32 \,\vert\, r\!\ll\!40 \,\vert\, (c\wedge\mathtt{0xFFF})\!\ll\!48 \,\vert\, b\!\ll\!60 \,\vert\, \dots \,\vert\, B_{\mathrm{last}}\!\ll\!224$ | Core `encodeSlot` | §8 |
| E10 | 활력장 | $\Psi = V \cdot 2^{b} \cdot (1 + 0.25\,\mathbf{1}_{\mathrm{bridge}}) \cdot (1 + 0.05\,\mathbf{1}_{\mathrm{conc}})$ | Core `psi` | §9.2 |
| E11 | 활동 감쇠 | $V = \mathrm{swapCount} \gg \lfloor (t - t_{\mathrm{last}})/24{,}576 \rfloor$; 시프트가 31 을 넘으면 0 | Core `_decayedSwapCount` | §9.2 |
| E12 | 깊이 버킷 | $b = \min(15, \lfloor \log_{10}(d/10^{15}) \rfloor)$ | Core `depthBucket` | §9.2 |
| E13 | 퇴출 허들 | $2^{b(d_{\mathrm{new}})} > \Psi_{\mathrm{worst}} + \lfloor \Psi_{\mathrm{worst}}/4 \rfloor$ | Hub `_canInsert` | §9.3 |
| E14 | 분할 게이트 | 분할을 유지하는 것은 다음일 때 그리고 그때에 한한다: $\mathrm{out}_{\mathrm{split}} \ge \mathrm{out}_{\mathrm{single}}\cdot(1 + 25/10^{6})$, 여기서 $\mathrm{out}_{\mathrm{single}}$ 은 전체 규모에서 가장 깊은 후보와 최고 환율 후보 중 더 나은 쪽 | Solver `_buildHop` | §10.4 |
| E15 | Iron-Law 하한 | $\mathtt{floorBps} = \max(9{,}600 - \mathtt{legShv} - \min(\delta, 10^{4}) - \sigma_{\ln}/10^{14},\ 8{,}000)$ | Core `ironFloorBpsShv` | §11.5 |
| E16 | 유효 최소값 | $\mathtt{effMin} = \max(\mathtt{userMinOut},\ \mathtt{route.singleOutFloor},\ \lceil \mathtt{finalHopQuote}\cdot\mathtt{floorBps}/10^{4}\rceil)$; 전송 수수료(fee-on-transfer)가 감지되면 둘째 항은 제거하고 셋째 항은 측정된 순 비율로 재조정 | Router `_execute` | §11.5 |
| E17 | 프로토콜 수수료 | $\mathrm{fee} = \lceil 28 \cdot \mathrm{base} / 10^{4} \rceil$, 기준 = 수수료 홉의 측정된 입력(앵커), 총 출력(브리지 코인으로 향하는 직접 경로), 또는 각 홉의 측정된 입력(소진); $t_{1} = \lfloor 3{,}000\cdot\mathrm{fee}/10^{4} \rfloor$, $t_{2} = \mathrm{fee} - t_{1}$ | Router `_chargeHopFee`, `_payFee` | §13 |
| E18 | 훅 게이트 | $(\mathrm{uint160}(\mathrm{hook}) \,\&\, \mathtt{0x3FFF}) \,\&\, ((1\ll 3)\mid(1\ll 2)) \ne 0 \Rightarrow$ 레그 거부, 호출은 이루어지지 않음 | Core `hookAltersDeltas`; Router | §14.2 |
| E19 | 동적 수수료 게이트 | $f_{\mathrm{eff}} = f_{\mathrm{key}}$ (정적인 경우); 센티널이 영이 아닌 프로토콜 수수료와 함께 오면 견적 불가; 그 외에는 다음에서 읽은 실시간 LP 수수료: `slot0` / `globalState` / 풀의 `fee()` | Core `effV4Fee`, `quoteV3Fee` | §5.4 |
| E20 | V2 수수료 상한 | $f_{\mathrm{eff}} = f_{\mathrm{decl}}$ 이면 $0 < f_{\mathrm{decl}} \le 100$ bps, 그렇지 않으면 30 | Core `effV2Fee`; Solidly 선언 폴백에도 같은 규칙 | §4.2 |
| E21 | 미리보기 안전 버퍼 | $s = \min(\max(0, n-2) + 5a,\ 10)$ bps | Quoter `_pack` | §12.1 |
| E22 | 표면 선택 | 경로를 calldata 에 싣는 것은 다음일 때 그리고 그때에 한한다: $p_{\mathrm{byte}} < \rho^{*}$; $\rho^{*}$ 는 경로 형태별로 66.9–105.5 가스/바이트로 측정됨 | `test/EquationBench.t.sol` | §17.6 |
| E23 | 집중에 따른 레그 삭감 | $\mathtt{legShv} = \sum_{\mathrm{hops}} \lfloor 200\,((\sum a)^{2} - \sum a^{2})/\sum a^{2} \rfloor$ | Core `legShaveBps` | §11.5 |
| E24 | 집계 홉 하한 | $\sum \mathrm{got} + \lfloor (\mathrm{BPS} - 8{,}000)\cdot \lfloor \sum \mathrm{attested}/n \rfloor / \mathrm{BPS} \rfloor \ge \sum \mathrm{attested}$ | Router `_execute` | §11.3 |
| E25 | 신뢰 중심 | 깊이 가중 중앙값: $(r_{i}, d_{i})$ 를 $r_{i}$ 로 정렬하고, 처음으로 다음이 성립하는 $r_{i}$ 를 반환: $\sum_{j \le i} d_{j} \ge \lceil \sum d / 2 \rceil$; 대역 $[0.95, 1.05] \times$ 중심 | Solver `_depthWeightedMedian` | §10.3 |
| E26 | 수수료 체제 술어 | $\mathrm{feeHop} = \min\{h : \mathrm{isBridge}(\mathrm{hops}[h].\mathrm{tokenIn})\}$; $h$ 에서 부과하는 것은 다음일 때 그리고 그때에 한한다: $\neg\mathrm{feeOnOut} \wedge (\mathrm{feeHop} = \infty \vee h = \mathrm{feeHop})$; $\mathrm{feeOnOut} \iff$ 홉이 하나이고 $\mathrm{isBridge}(\mathrm{tokenOut})$ | Router `_execute` | §13.1 |
| E27 | 발행 닫힌 형태 | $E(t) = (T - (T \gg p)) + (R_{0} \gg p)(t - s - pP)$, $p = \lfloor (t - s)/P \rfloor$; $s$ 까지는 $0$; $T - (T \gg 8)$ 는 $s + 8P$ 부터 | Staking `_emittedAt` (L1458–1467) | §25.3 |
| E28 | 발행 윈도 | $r = \min(\lfloor (E(t') - E(t_{\ell}))\min(W, W_{\min})/W_{\min} \rfloor,\ \mathit{RR})$; $A \mathrel{+}= \lfloor r \cdot 10^{18}/W \rfloor$ | Staking `_updateGlobal` (L1428–1448) | §25.3 |
| E29 | 정산 | $\mathrm{due}_{u} = (\lfloor w_{u}A/10^{18} \rfloor - c_{u})^{+}$, then $c_{u} \leftarrow \lfloor w_{u}A/10^{18} \rfloor$ | Staking `_settlePendingRewards`, `_settlePendingPureYield` (L1487–1517) | §25.3 |
| E30 | 락 부스트와 가중치 | $b(\ell) = 10^{4} + \lfloor 750\ell/365 \rfloor + \lfloor 250\ell^{2}/365^{2} \rfloor$ bps, $\ell$ 은 유효한 락; $w_{u} = \lfloor (s_{u} - d_{u})^{+} b/10^{4} \rfloor$; $w^{\mathrm{p}}_{u} = \lfloor s_{u} b/10^{4} \rfloor$ 은 $d_{u} = 0$ 일 때 그리고 그때에 한한다 | Staking `boostByDays`, `_computeBoost` (L496–505, L1695–1700) | §25.3 |
| E31 | 이자 곡선 | $r = 100 + \lfloor 500U/10^{18} \rfloor$ ($U \le 0.8 \cdot 10^{18}$ 일 때), 그 외에는 $500 + \lfloor 72{,}500(U - 0.8 \cdot 10^{18})/10^{18} \rfloor$ bps (연) | Staking `_interestRate` (L1768–1782) | §25.4 |
| E32 | 이자 지수와 조각 | $\delta = \lfloor \lfloor 10^{18} r/10^{4} \rfloor \Delta t / Y \rfloor$, $\sigma = \lfloor D\delta/10^{18} \rfloor$; $\sigma > S$ 이면 $\delta = \lfloor S \cdot 10^{18}/D \rfloor$ 이고 $\sigma$ 를 다시 계산 | Staking `_interestSlice`, `_updateInterestIndex` (L1539–1619) | §25.4 |
| E33 | 조각 분할 | $\kappa_{r} = \lfloor 300\sigma/10^{4} \rfloor$ 는 준비금으로; $\eta = \lfloor (\sigma - \kappa_{r})\min(W_{\mathrm{p}}, W_{\min})/W_{\min} \rfloor$ 는 순수 스테이커에게; 나머지는 준비금으로 | Staking `_updateInterestIndex`, `_dampPure` (L1560–1580, L1623–1625) | §25.4 |
| E34 | 이자 귀속 | $i_{u} = \lfloor d_{u}(I - I_{u})/10^{18} \rfloor$ 이며 $s_{u}$ 에서 차감된다; $s_{u}$ 를 넘는 초과분은 E37 로 가는 손실이다 | Staking `_accrueInterestFor` (L1627–1671) | §25.4 |
| E35 | 보존 가드 | $\lvert \Delta(B + D + \mathit{BD}) - \Delta(S + \mathit{RR} + \mathit{PR} + \Pi) \rvert \le 10^{10}$ | Staking `conserves` (L419–457) | §25.5 |
| E36 | 채무액과 지급 능력 | $\mathrm{owed} = (S + \mathit{RR} + \mathit{PR} - D)^{+} + \Pi$; 하드 위반은 다음일 때 그리고 그때에 한한다: $B + 10^{10} < \mathrm{owed}$ | Staking `_owed`, `_hardBreach`, `isSolvent` (L1794–1824) | §25.5 |
| E37 | 손실 워터폴 | $\kappa = \min(\lambda, \mathit{PR})$; $\mathit{PR} \mathrel{-}= \kappa$; $\mathit{BD} \mathrel{+}= \lambda - \kappa$ | Staking L1101–1106, L704–709, L1657–1663 | §25.5 |
| E38 | 재자본화 | $\rho = \min(\mathit{BD}, \mathit{PR})$, $\gamma = \min(a, \mathit{BD} - \rho)$; $\mathit{BD} \mathrel{-}= \rho + \gamma$ | Staking `absorbBadDebt` (L639–649) | §25.5 |
| E39 | 청산 | 청산 가능은 다음일 때 그리고 그때에 한한다: $d_{u} > 0 \wedge (s_{u} = 0 \vee 100 d_{u} \ge 95 s_{u})$; $\mathrm{seized} = \min(d_{u} + \lfloor 500 d_{u}/10^{4} \rfloor, s_{u})$; 손실 $(d_{u} - s_{u})^{+}$ | Staking `_executeLiquidation`, `_isLiquidatable` (L1079–1137) | §25.5 |
| E40 | 비상 헤어컷 | $x_{0} = (s_{u} - d_{u})^{+}$, $E' = E_{+} - e_{u} + x_{0}$; $x = \lfloor x_{0}\,\mathrm{pot}/E' \rfloor$ ($\mathrm{pot} < E'$ 일 때), 그 외에는 $x_{0}$; $x \le \mathrm{pot}$ | Staking `emergencyWithdraw` (L689–696) | §25.5 |
| E41 | 차입 상한 | $d_{u} + a \le \lfloor 50(s_{u} - d_{u})^{+}/100 \rfloor$ 그리고 $\lfloor (D + a) 10^{18}/S \rfloor \le 0.75 \cdot 10^{18}$ | Staking `borrow`, `_ltvCap` (L834–840, L1908–1909) | §25.5 |
| E42 | 유지보수 예산 | $c(n) = \min(1 + \lfloor n/50 \rfloor + \lfloor (t - t_{m})/900\ \mathrm{s} \rfloor,\ 10,\ n)$ (레지스트리당) | Staking `_windowBudget` (L1148–1157) | §25.5 |

## 부록 B · 불변식

이들은 도달 가능한 모든 상태에서 성립하는 성질이다. 각각은 명시된 지점의 코드 형태에 의해 시행되며, 이를 위반하게 될 트랜잭션은 실행되지 않는다. INV-1–INV-18 은 이전 판의 번호를 유지하되 시행 열을 코드에 맞게 정비했고, INV-19–INV-22 는 새것이다. INV-23–INV-31(2.3.0)은 스테이킹 엔진의 것으로(§25), 스테이킹 저장소 `cc614f9` 의 `src/BlazePhoenixStaking.sol` 에서 시행된다.

| # | 불변식 | 시행 |
|---|---|---|
| INV-1 | Router 는 트랜잭션 사이에 자금을 보유하지 않으며, 휴지 상태의 잔액은 영이다. | 기준선 범위의 스윕과 지급(§11); `invariant_RouterHoldsNothing`, `HoldsNothingBeyondTheSeed` |
| INV-2 | 모든 스왑은 원자적이다. 거래의 일부만 정산된 상태는 존재하지 않는다. | EVM 트랜잭션 의미론; 하나의 프레임 |
| INV-3 | 오라클이 없다. 어떤 외부 가격 피드도 허용, 순서 매김, 분할, 하한 설정, 수수료의 입력이 아니다. | 다섯 컨트랙트 어디에도 피드 호출이 없다 |
| INV-4 | 수수료율, 수수료 분배, 하한 상수에는 배포된 바이트코드 안에 세터가 없다. | Core 와 Router 의 컴파일 시점 상수(§13) |
| INV-5 | 호출자가 제공한 경로 필드는 보호를 강화할 수는 있어도 완화할 수는 없다. | E16 의 삼중 최댓값; 커버리지 게이트는 증명을 끌어올리기만 하고 낮추지 않는다(§11) |
| INV-6 | 최소값이 영인 스왑은 실행되지 않는다. | 모든 문에서의 `RouterE(10)` |
| INV-7 | Ψ 는 후보의 순위를 매기고 잘라낸다. 어떤 견적, 하한, 할당도 이를 읽지 않는다. | 데이터 흐름: 순위 산정 이후의 어떤 것도 Ψ 를 소비하지 않는다(§9.6) |
| INV-8 | 페어의 레지스트리는 최대 열여섯 개의 풀을 보유하며, 퇴출은 E13 의 마진으로 전체 Ψ 기준 가장 적합한 것을 남긴다. | Hub `_canInsert`; `invariant_neverExceedsMaxSlots` (상한); `test_RecordSwap_EvictsWeakestWhenFullAndNewcomerClearsMargin` (퇴출) |
| INV-9 | 런타임 바이트코드가 없는 주소는 경로에 들어가지 않는다. | 모든 도출에서의 `hasCode`(§7) |
| INV-10 | 범용 콜백은 현재 레그를 위해 약정된 풀에게만, 레그가 진행 중일 때만, 그리고 레그의 기록된 예산을 넘지 않는 범위에서만 지급한다. | 일시적 레그 컨텍스트; 풀에는 `RouterE(6)`, 예산에는 `RouterE(8)`(§11.4) |
| INV-11 | 주소가 델타 변경 권한을 선언하는 V4 훅은 어떤 토큰도 움직이기 전에, 호출하지 않고 거부된다. | E18; `RouterE(9)`; Solver `_topKPools` |
| INV-12 | 어떤 가격 산정 곱셈도 오버플로할 수 없다. 위험한 모든 곱셈은 512비트 `mulDiv` 로 인수분해되며, 한계를 둘 수 없는 상태는 영을 견적한다. | Core(§4.2, §6) |
| INV-13 | 반올림은 과잉 약속에 반하는 방향으로 이루어진다. 견적과 지급은 내림하고, 보호 하한과 수수료는 올림한다. | 각 지점의 Core `mulDiv` / `mulDivUp` |
| INV-14 | 한계를 증명할 수 없는 곳에서는 계산이 영을 반환하고 베뉴를 포기한다. 추측도, 견적 안에서 일정을 잡을 수 있는 리버트도 없다. | Core 전반의 실패 시 폐쇄 자세; `_solKFits`(§6) |
| INV-15 | 수수료 기준은 측정된 인출액이며 경로의 약정 입력으로 상한이 걸린다. 선언된 출력이 아니며, `route.totalOut` 은 어느 것도 읽지 않는다. | `_chargeHopFee`; `test_FeeBase_IgnoresLiedAboutTotalOut` |
| INV-16 | 포기는 일방향이다. 플래그는 한 지점에서 `true` 로 기록되고 어느 지점에서도 `false` 로 기록되지 않는다. 그 이후에는 증가만 가능한 큐레이터 계층만 남으며, 옮겨진 팩토리나 훅은 다시 무장될 수 없다. | Hub 와 Router 의 `renounceControl`(§14.7) |
| INV-17 | 선언된 상수곱 또는 Solidly 폴백 수수료는 100 bps 상한 안에서만 신뢰된다. 그 밖에서는 정규 30 bps 로 풀을 가격 산정한다. | E20 |
| INV-18 | Solver 의 계획에서 어떤 홉도 네 개를 넘는 레그를 갖지 않고 어떤 경로도 열한 개를 넘는 레그를 갖지 않는다. Router 는 최대 세 개의 홉과 홉당 다섯 개의 레그를 받아들인다. | Solver 예산; Router `_execute` → `RouterE(3)` |
| INV-19 | 풀은 자신이 그 페어를 거래함을 증명할 때만 레지스트리에 들어간다. 페어 형태의 종류에는 `token0()`/`token1()`, V4 에는 풀 id 의 재도출이며, 질의된 풀은 탐색이 목록에 올리기 전에 같은 증명을 한다. | Hub `recordSwap`, `_probe`; `invariant_ActiveEntriesTradeThePair` |
| INV-20 | 정산은 앵커 경로에서는 프로토콜 수수료를 정확히 한 번, 소진 경로에서는 홉마다 한 번 지급한다. 아무것도 지급하지 않은 정산은 존재하지 않는다. | 수수료 원장, `RouterE(15)`/`RouterE(16)`; `invariant_SettledSwapEmitsExactlyOneFee` (앵커); `test/FeeSeals.t.sol`, `test/ExhaustionRegimePreviewParity.t.sol` (소진) |
| INV-21 | 홉은 연쇄된다. 각 홉의 입력은 이전 홉의 출력이며, 훅이 없는 레그는 이 홉에서든 이후의 어느 홉에서든 훅이 달린 레그 뒤에 실행되지 않는다(`sawHooked` 는 경로 전체에 걸친다). | Router `_execute` → `RouterE(3)`; `sawHooked` |
| INV-22 | 배포된 네 컨트랙트 — Hub, Solver, Router, Quoter — 는 릴리스 프로파일에서 프로젝트의 24,000 바이트 게이트 아래로 출시되고, Core 라이브러리는 별도로 측정되며(24,576 중 6,477 바이트), 스위트는 그 프로파일에서 실행된다. | `DeployedSizeGate`; `profile_parity.py` |
| INV-23 | `conserves` 를 달고 있는 모든 스테이킹 진입점에서, 잔액에 부채와 기록된 부실 채권을 더한 값은 스테이크에 두 준비금과 분배되었으나 지급되지 않은 보상을 더한 값과 같은 양만큼 변하며, 그 오차는 1e10 wei 이내이다(E35). | Staking `conserves`, L419–457 → `Staking__InvariantBreached` |
| INV-24 | 예정된 발행은 `TOTAL_REWARDS − (TOTAL_REWARDS >> 8)` 을 결코 넘지 않는다. 누적 자금 조달은 `TOTAL_REWARDS` 를 결코 넘지 않는다. 어떤 발행 윈도도 `rewardReserve` 가 보유한 것보다 많이 지급하지 않는다. | `_emittedAt` L1458–1467; `fundEmission` L518 → `Staking__CapExceeded`; `_updateGlobal` L1442 |
| INV-25 | 실현된 모든 손실 — 청산 부족분, 물에 잠긴 비상 출구, 회수 불가능한 이자 — 은 먼저 `protocolReserve` 에서 충당된다. 충당되지 못한 나머지만이 `totalBadDebt` 에 더해진다(E37). | Staking L1101–1106, L704–709, L1657–1663 |
| INV-26 | 잔액에 1e10 을 더한 값이 `owed` 보다 작은 동안에는 예치, 차입, 인출, 보상 또는 순수 수익 청구, 준비금 인출, 비상 취소가 실행되지 않는다(E36). | `_hardBreach` 게이트 L760, L826, L902, L932, L948, L1510, L539, L620 → `Staking__InvariantBreached` |
| INV-27 | `absorbBadDebt` 는 BZPX 를 밖으로 옮기지 않고, 호출자에게 아무것도 지급하지 않으며, `totalBadDebt` 를 현재 값을 넘지 않는 만큼만 낮춘다. 준비금이 먼저이고, 호출자의 금액은 충당되지 못한 나머지로 상한이 걸린다(E38). | `absorbBadDebt` L639–649 |
| INV-28 | 발행 가중치, 순수 수익 가중치, 양의 지분의 합계는 `_applyBoost` 에서만 기록되며, 이 함수는 포지션의 추적된 항을 다시 계산한 항으로 교체한다. 락은 유효한 동안에만 부스트를 얻는다(E30). | `_applyBoost` L1726–1734; `_effectiveLockDays` L1687–1690 |
| INV-29 | 차입은 새 부채가 그 이전 포지션 순 스테이크의 절반 이하이고 그 이후 장부의 이용률이 75 % 이하일 때만 실행된다(E41). | `borrow` L834 → `Staking__LTVExceeded`; L838–840 → `Staking__UtilTooHigh` |
| INV-30 | 비상 출구는 포지션의 순 지분 이하, 그리고 컨트랙트의 잔액 이하로만 지급하며, 잔액이 추적된 지분에 못 미치면 pot/E′ 로 비례 축소된다(E40). | `emergencyWithdraw` L689–696 |
| INV-31 | 포지션의 마지막 예치로부터 `MIN_DEPOSIT_BLOCKS` = 10 블록 이내에는 어떤 인출, 순수 수익 청구, 락 변경도 실행되지 않는다. | L894, L947, L971 → `Staking__FlashLoanProtection` |

## 부록 C · 재현 명령

이 논문에서 저장소에서 나온 모든 숫자는, Foundry 와 Python 3 가 설치된 상태에서 `8949a9d` 시점의 `Blaze-Phoenix-Dex` 를 깨끗하게 체크아웃하여 아래 명령 중 하나로 재현된다. 포크 스위트에는 `DRPC_KEY` 에 아카이브 RPC 키가 필요하다.

```bash
forge test --no-match-path 'test/fork/**'                        # the suite, release settings (§15.1)
forge test --match-path 'test/fork/**'                           # the fork lane, five chains (§15.1, §17)
FOUNDRY_PROFILE=release forge build --sizes --skip test --skip script   # sizes against EIP-170 (§16.3)
python3 .github/scripts/mutants.py                                # the curated mutation guard, 268/268 (§15.2)
python3 .github/scripts/check_targets.py                          # every mutant still points at one line
python3 .github/scripts/assurance/covering_array.py --check       # both covering arrays current (§15.4)
forge test --match-contract RegimeCoverageTest -vv                # strength 2, 63 rows
forge test --match-contract RegimeCoverageT3Test -vv              # strength 3, 168 rows
forge test --match-path test/regime/HostileVenueMatrix.t.sol      # ten pathologies × two doors (§15.5)
forge test --match-path test/regime/SandwichCurve.t.sol -vv       # the sandwich curve (§14.3)
forge test --match-path test/regime/CanonicalOracles.t.sol        # the quote maths against the specifications (§15.6)
forge test --match-path 'test/*MetamorphicRelations.t.sol'        # 14 + 5 relations (§15.7)
FOUNDRY_PROFILE=nver1 forge build --skip '*.t.sol' && FOUNDRY_PROFILE=nver2 forge build --skip '*.t.sol'
NVERSION_LANE=1 forge test --match-path 'test/nversion/*'         # the three binaries agree (§15.8)
forge test --match-test invariant_ --fuzz-seed 0xb1a2ef00          # the stateful campaigns (§15.3)
forge test --match-path test/FeeSeals.t.sol                       # the fee rule, both regimes (§13.4)
forge test --match-path test/QuoteDelayStatistics.t.sol -vv       # how a quote ages, 240 samples (§12.2)
forge test --match-path test/QuoterGasStatistics.t.sol -vv        # what a quote costs (§12.3)
forge test --match-path test/fork/QuoteDelayFork.t.sol -vv        # live Base, 1,000 USDC → WETH (§12.2)
forge test --match-path test/fork/FidelityMatrix.t.sol -vv        # preview → delivery, four chains (§17.2)
forge test --match-path test/fork/DiscoveryColdWarm.t.sol -vv     # cold and warm solve (§17.4)
forge test --match-path test/EquationBench.t.sol -vv              # ρ* (§17.6)
forge test --match-path 'test/fork/Deployed*.t.sol'               # what V1 is, pinned by codehash (§19)
python3 .github/scripts/assurance/projection_distance.py          # 86 / 99 (§15.9)
python3 .github/scripts/assurance/metrics.py                      # every assurance number, with its denominator
bash .github/scripts/shared-quantities.sh                         # the register agrees with the tree
```

풀이 예제 4 의 해시는 `abi.encode(currency0, currency1, fee, tickSpacing, hooks)` 에 대한 어떤 keccak-256 구현으로도 재현된다. 논문의 수치는 순수 Python 의 Keccak-f[1600] 으로 만들어졌으며, 빈 문자열과 `"abc"` 의 테스트 벡터, 그리고 Core 가 하드코딩한 `extsload(bytes32)` 셀렉터에 대해 검증되었다.

## 용어집

**연산자.**

| 기호 | 이름 | 한 줄 설명 |
|---|---|---|
| Ω | 팔중 디스패처 | 모든 레그의 가격을 산정한다 — 닫힌 형태가 정확한 곳에서는 닫힌 형태로, 베뉴 자신의 바이트코드만이 정직한 원천인 곳에서는 베뉴에 묻는 방식으로. "팔중"은 역사적 명칭이며 오늘날 살아 있는 종류는 여섯이다 |
| 𝒟 | 결정론적 도출 | 배포가 재현 가능한 곳에서는 CREATE2 산술로 풀 주소를 계산하고, 그렇지 않은 곳에서는 묻고 나서 페어를 증명하며, V4 싱글턴에 대해서는 풀 id 를 도출하고 증명한다. `hasCode` 는 바이트코드가 없는 것을 버린다 |
| Φ | Iron-Law 하한 | 프로토콜 자신의 보유 하한: 기본 96 %, 측정된 충격과 측정된 집중에 따라 완화되며 80 % 에서 하드 클램프되고, 마지막 홉의 프레임 내 견적에 적용된다 |
| Ψ | 활력장 | 모노슬롯만으로 계산되는 베뉴의 품질. 후보 집합의 순위를 매기고 잘라낸다 — 베뉴를 숨길 수는 있어도 잘못 가격 산정하게 만들 수는 없다 |
| Ξ | 사기 방지 사영자 | 델타를 변경하거나 허용될 수 없는 훅을 건드리는 모든 경로를, 훅을 호출하지 않고 순위 산정 이전에 극대화 대상에서 제거한다 |

**용어.** **앵커 체제** — 수수료를 한 번, Router 가 보유한 첫 브리지 코인에 대해(또는 브리지 코인으로 향하는 직접 경로의 출력에 대해) 부과한다. **신뢰 대역** — 후보들의 한계 환율의 깊이 가중 중앙값 둘레 ±5 %. 후보가 하나뿐인 집합에서는 작동하지 않는다. **커버리지 게이트** — 증명이 견적의 절반에 못 미치게 덮을 때 레그의 증명된 한계를 측정된 견적으로 끌어올리는 것. **Exact Pass** — Quoter 의 풀 정확 미리보기: 베뉴 자신의 스왑을 실행한 뒤 리버트 추출로 되돌린다. 2026-09-03 부터 수수료를 뺀 순액이다. **소진 체제** — 어떤 홉의 입력으로도 브리지 코인이 없다. 모든 홉이 자신의 측정된 입력에 대해 28 bps 를 지급하며, 구조상 가치 없는 접두부에 면역이다. **수수료 원장** — 정산마다 수수료 지급 횟수를 세는 일시적 카운트로, 영을 거부하고 앵커 경로에서는 둘도 거부한다. **계측적 설계** — 컨트랙트를 계측기로 만든다: 다양성을 좌표로 붕괴시키고, 측정할 수 있는 모든 좌표를 측정하며, 나머지는 단조롭게 만든다. **메타 방정식** — 애그리게이터 전체를 하나의 극대화로 본 것으로, 곱이므로 실패하는 인자 하나가 경로를 영으로 만든다. **모노슬롯** — 풀의 라우팅 상태 전체를 하나의 256비트 워드에 담은 것으로, 안에 가격은 없다. **단일 콜백 원칙** — 하나의 `fallback` 이 모든 V3 형태 베뉴의 플래시 회계 콜백에 응답하며, 세 겹으로 보호된다. **일방통행 문** — 바이트코드로서의 포기: 제어 계층은 죽고, 증가만 가능한 큐레이터 계층은 남는다. **페어 증명** — 풀이 레지스트리가 저장하기 전, 또는 탐색이 목록에 올리기 전에 자신의 `token0`/`token1`(또는 재도출된 id)을 증명하는 것. **사영 거리** — 거부의 관측된 대상이 결정을 내리는 대상과 얼마나 떨어져 있는가. **자가 치유 레지스트리** — 프로토콜은 유동성을 거래함으로써 그것을 학습한다: 페어당 열여섯 슬롯, 적합도 순위 퇴출, 산술에 의한 감쇠. **잉여 규칙** — 프로토콜의 몫은 하나의 측정된 기준의 28 bps 이며, 견적과 인도 사이의 격차에 비례하는 것은 아무것도 없다. **세 번째 길** — Router 에 아무것도 남기지 않고 하한 안에서 이루어진 정산도, 우리 쪽 셀렉터를 가진 거부도 아닌 픽스처의 모든 결과.

## 참고문헌

1. G. Angeris, A. Evans, T. Chitra, S. Boyd. *Optimal Routing for Constant Function Market Makers.* arXiv:2204.05238, 2022 (ACM EC '22). — CFMM 위의 라우팅을 볼록 계획으로 본 것. §10.4 의 비례 할당기가 못 미치는 최적해이다.
2. T. Diamandis, M. Resnick, T. Chitra, G. Angeris. *An Efficient Algorithm for Optimal Routing Through Constant Function Market Makers.* arXiv:2302.04938, 2023 (Financial Cryptography 2023). — 같은 문제에 대한 쌍대 분해 알고리즘(§23).
3. H. Adams, N. Zinsmeister, D. Robinson. *Uniswap v2 Core.* 2020. https://uniswap.org/whitepaper.pdf — 상수곱 기준 문헌과 §4.2 의 기본값 30 bps.
4. H. Adams, N. Zinsmeister, M. Salem, R. Keefer, D. Robinson. *Uniswap v3 Core.* 2021. https://uniswap.org/whitepaper-v3.pdf — 집중 유동성, `slot0`, Q64.96 고정소수점(§4.2, §15.6).
5. Uniswap Labs (H. Adams et al.). *Uniswap v4 Core.* 2024. https://uniswap.org/whitepaper-v4.pdf — 싱글턴 PoolManager, 플래시 회계, 훅 권한 주소 인코딩(§5, §14.2).
6. M. Egorov. *StableSwap — Efficient Mechanism for Stablecoin Liquidity.* 2019. https://curve.fi/files/stableswap-paper.pdf — §4.1 에서 잘라낸 계열의 증폭된 불변식.
7. P. Daian, S. Goldfeder, T. Kell, Y. Li, X. Zhao, I. Bentov, L. Breidenbach, A. Juels. *Flash Boys 2.0: Frontrunning in Decentralized Exchanges, Miner Extractable Value, and Consensus Instability.* IEEE S&P 2020. DOI 10.1109/SP40000.2020.00040. — §14.3 이 제거한다고 주장하는 대신 한계를 두는 순서 조작 적대자.
8. L. Zhou, K. Qin, C. F. Torres, D. V. Le, A. Gervais. *High-Frequency Trading on Decentralized On-Chain Exchanges.* IEEE S&P 2021. DOI 10.1109/SP40001.2021.00027. — 샌드위치 공격의 측정과 형식화(§14.3).
9. P. Cousot, R. Cousot. *Abstract Interpretation: A Unified Lattice Model for Static Analysis of Programs by Construction or Approximation of Fixpoints.* POPL 1977. DOI 10.1145/512950.512973. — 단일 틱 닫힌 형태를 건전한 추상화로 보는 틀(§23).
10. M. R. Clarkson, F. B. Schneider. *Hyperproperties.* Journal of Computer Security 18(6), 2010. DOI 10.3233/JCS-2009-0393. — 트레이스 집합의 성질. 재분배 발견이 속하는 부류(§21, §23).
11. T. Y. Chen, S. C. Cheung, S. M. Yiu. *Metamorphic Testing: A New Approach for Generating Next Test Cases.* HKUST-CS98-01, 1998; arXiv:2002.12543. — §15.7 의 두 번째 판정자.
12. R. A. DeMillo, R. J. Lipton, F. G. Sayward. *Hints on Test Data Selection: Help for the Practicing Programmer.* IEEE Computer 11(4), 1978. DOI 10.1109/C-M.1978.218136. — 변이 분석과 결합 가설(§15.2).
13. A. Avizienis. *The N-Version Approach to Fault-Tolerant Software.* IEEE TSE SE-11(12), 1985. DOI 10.1109/TSE.1985.231893. — §15.8 의 조상.
14. D. R. Kuhn, R. N. Kacker, Y. Lei. *Practical Combinatorial Testing.* NIST SP 800-142, 2010. DOI 10.6028/NIST.SP.800-142. — 커버링 배열과 t-way 상호작용 커버리지(§15.4).
15. B. Littlewood, L. Strigini. *Validation of Ultrahigh Dependability for Software-Based Systems.* Communications of the ACM 36(11), 1993. DOI 10.1145/163359.163373. — 올바름의 확률이 진술되지 않는 이유(§15.11).
16. A. Akhunov, M. Salem. *EIP-1153: Transient Storage Opcodes.* https://eips.ethereum.org/EIPS/eip-1153 — 레그 컨텍스트, 수수료 원장, 재진입 락(§11.4, §13.2).
17. V. Buterin. *EIP-170: Contract Code Size Limit.* https://eips.ethereum.org/EIPS/eip-170 — 24,576 바이트의 벽(§16.3).
18. V. Buterin. *EIP-1014: Skinny CREATE2.* https://eips.ethereum.org/EIPS/eip-1014 — 𝒟 의 주소 산술(§7).
19. M. Lundfall et al. *EIP-2612: Permit Extension for EIP-20 Signed Approvals.* https://eips.ethereum.org/EIPS/eip-2612 · Uniswap Labs. *Permit2.* https://github.com/Uniswap/permit2 — Permit2 문(§11.1).
20. V. Buterin, S. Wilson, A. Dietrichs, M. Garnett. *EIP-7702: Set Code for EOAs.* https://eips.ethereum.org/EIPS/eip-7702 — 위임된 계정도 같은 문을 호출한다(§11.1).
21. *ERC-7201: Namespaced Storage Layout.* https://eips.ethereum.org/EIPS/eip-7201 — Hub 의 저장소 네임스페이스(§16.1).
22. MariaDB Corporation Ab. *Business Source License 1.1.* https://mariadb.com/bsl11/ — 코드 라이선스.
23. Fable & Mitra. *BlazePhoenix-Dex: an on-chain DEX aggregator with measured routing.* Repository, `main` 8949a9d, 2026-09-05. https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex — 이 논문의 모든 주장에 대한 정본 출처. 부록 C 는 여기서 숫자를 재현한다.
24. S. Müller, N. Moumeni, A. Messaoudi. *Cost of Manipulation in AMM-Based Oracles.* arXiv:2606.03548, 2026 (Financial Cryptography 2026, DeFi workshop). — E25 의 배후에 있는 유동성 가중 중앙값 한계(§10.3, §23).
25. *Ormer: A Manipulation-resistant and Gas-efficient Blockchain Pricing Oracle for DeFi.* arXiv:2410.07893. — 중앙값을 추정하는 온체인 오라클(§23).
26. *From Means to Medians: Optimal Benchmark Design.* arXiv:2506.22142. — 어떤 벤치마크가 가장 조작하기 어려운가(§23).
27. *Optimal Routing across Constant Function Market Makers with Gas Fees.* arXiv:2603.02844. — 가스를 고려한 라우팅(§23).
28. *Routing Frictions and Executable Liquidity in Fragmented Markets.* arXiv:2609.19013. — 광고된 유동성 대 실행 가능한 유동성(§23).

## 인용 방법

Mitra. *BlazePhoenix Technical Whitepaper: an on-chain liquidity aggregator whose quote and execution are one computation.* Version 2.3.0, 2026-10-14. Zenodo. DOI [10.5281/zenodo.23084091](https://doi.org/10.5281/zenodo.23084091) (기탁된 v2.3). 모든 버전: [10.5281/zenodo.23084090](https://doi.org/10.5281/zenodo.23084090). 첫 기탁 시리즈(v2.2)의 Concept DOI: [10.5281/zenodo.22526574](https://doi.org/10.5281/zenodo.22526574). BibTeX 및 기타 형식: https://blazephoenix.xyz/research/whitepaper
