並非所有國家都是平等的
以太坊的狀態並不均衡:少數合約佔據了大部分存儲空間,而大多數賬戶的生命週期都很短——有時甚至只有一個區塊。研究狀態訪問模式可以揭示哪些狀態保持活躍,哪些狀態變得冷淡。如果我們針對實際使用情況進行優化,就可以提高執行層的運行速度。
分析
本研究按賬戶類型、字節碼使用情況、部署器和工廠模式、代碼大小以及插槽活動分析了狀態使用情況。研究涵蓋了從創世區塊到Pectra 區塊 22,431,083(2025 年 5 月)之前的所有區塊。
EOA 與合同 — 哪一個持續時間更長?
活動跨度= 從特定狀態的第一次訪問到最後一次訪問(讀取和寫入)的塊距離。
0 活動跨度= 僅在單個塊中訪問狀態。
假設1 個區塊 = 12 秒。
合同 | 緊急行動 | |
---|---|---|
賬戶總數 | 50,119,846 | 243,161,178 |
中位活動跨度(區塊) | 0 | 22,317(約3.1天) |
P75 活動跨度(塊) | 966,579(約4.5個月) | 1,274,601(約5.8個月) |
P95 活動跨度(塊) | 4,857,691(約1.85年) | 6,800,324(~2.59年) |
零活動跨度份額 | 55.17% | 4.50% |
EOA 顯然比合同持續時間更長。
EOA 的中位壽命跨度不為零(約 3.1 天),而合同壽命跨度的中位值為零。EOA 的右尾在每個分位數(q75、q95)處都較重,表明其長壽群體較大。大多數合同在設計或意圖上都是短暫的。
約55%的合同為零跨度合同。可能的驅動因素包括:- 工廠垃圾郵件和大規模鑄造模板(例如,代幣克隆、永遠不會看到後續的配對合約)。
- 為單個交易創建的MEV/實用程序部署。
- 自毀模式(尤其是 EIP-6780 之前),其中創建和銷燬發生在同一個區塊/交易內。
“短暫的 EOA” 也很常見。
從首次觀察到最後一次觀察,平均 EOA 持續時間僅為約 3.1 天, 75% 的EOA 會在約 6 個月內結束。這可能包括一次性認領者/鑄幣者、交易所充值地址和機器人錢包。長期合同確實存在,但並不典型。
跨度多年的合約往往由標準驅動或基於基礎設施:代幣合約(ERC-20/721)、註冊中心、路由器、多重簽名/代理管理合約以及其他核心原語。許多應用程序團隊會在升級時輪換地址,這會將活動分散到新部署中,並縮短每個地址的測量跨度。
合同有何不同?
一份合約 ≠ 一個獨立的應用程序。事實上,許多合約會重用現有的字節碼,或者使用相同的模板(通過工廠)進行部署。我們可以根據不同的類別來分析這些合約:
- 模板與唯一字節碼
- 按部署者地址
- 按工廠地址
- 按代碼大小
- 有狀態(帶槽) vs. 無狀態(不帶槽)
模板與唯一字節碼
模板= 由多個合約地址部署的字節碼哈希。
唯一= 字節碼哈希只部署一次。
字節碼計數 | 合約數量 | 平均中位活動跨度(塊) | |
---|---|---|---|
模板 | 150,587 | 48,663,814 | 468,852(約2.1個月) |
獨特的 | 1,456,032 | 1,456,032 | 905,439(~4.1個月) |
全部的 | 1,606,619 | 50,119,846 |
數據顯示:
- 合約高度集中:約 15 萬個模板字節碼佔所有合約的97% 。僅Top 1代碼哈希就覆蓋了14.4%的合約; Top 10 代碼哈希覆蓋了51% ; Top 100 代碼哈希覆蓋了81.8% 。這體現了嚴重的長尾模板重用,而非多樣化的實現。
- 插槽不在克隆所在的地方:儘管只佔合約的2.9% ,但唯一字節碼擁有約 875.2M 個存儲槽,而模板則擁有約 429.1M 個存儲槽——約 67% 的狀態位於一次性實現中。
獨立合約的中位活動週期(約 4.1 個月)幾乎是模板(約 2.1 個月)的兩倍。這符合直覺:許多模板都是極簡代理/克隆、代幣工廠或臨時部署(垃圾郵件/模因幣、MEV 腳手架、EIP-6780 之前的自毀模式),這些部署壽命很短,或者在創建後從未被使用過。
獨特的合約狀態負載更重,這可能反映了管理更大規模持久數據的複雜系統(DEX、Staking、橋接、Rollup 基礎設施)。另一方面,合約中大量的模板份額解釋了零活動跨度部署的長尾現象——許多克隆合約在創建後從未真正執行過。
按部署者地址
Single = 部署者僅部署一個合約。
多個= 部署多個合約的部署者。
部署者數量 | 合同總數 | 每份合約的平均槽位 | 平均中位活動跨度(塊) | |
---|---|---|---|---|
單身的 | 4,793,357 | 4,793,357 | 30.65 | 197,134(~0.9個月) |
多種的 | 850,064 | 45,326,489 | 193.28 | 687,037(約3.1個月) |
全部的 | 5,643,421 | 50,119,846 |
數據顯示:
- 一小部分部署者幾乎承擔了所有的部署工作——僅15.1%的部署者就承擔了90.4%的合約。集中度極高:僅排名前 500 位的部署者就承擔了所有合約的57.4% ;而最大的單個部署者僅佔3.15% 。
- 活躍的部署者平均會生成更多持久且佔用更多 slot 的合約。來自“多個”部署者的合約壽命約為 3.5 倍(687k 個區塊 vs 197k 個區塊),每個合約佔用的slot 數量約為 6 倍(193 個 vs 31 個)。這種模式適合那些在鏈上維護產品(並且取得成功)的團隊/服務,而不是一次性的實驗。
- 但最頂級的部署者傾向於短暫型部署。在部署量排名前十的合約中,它們的活躍期極短(平均數百個區塊),存儲空間極小——這與大規模生產的克隆、工廠垃圾和 MEV 腳手架的情況一致。換句話說:在“多部署”組中,分為構建者(持久、有狀態部署)和噴霧者(高容量、短期部署)。
合約部署高度集中。大多數持久且狀態密集型的合約來自重複部署者,而部署量最大的部署者則在區塊鏈上部署了大量短期合約。
對於多個合約部署者來說,也可能是他們犯了一個錯誤,所以他們必須重新部署合約。
按工廠地址
非工廠= 由 EOA 直接創建(無中介合同)。
工廠 – 個人= 由一份生產一個孩子的合同創建。
工廠 – 多= 由生產多個孩子的合同創建。
數數 | 合同總數 | 每份合約的平均槽位 | 平均中位活動跨度(塊) | |
---|---|---|---|---|
非工廠 | 808,188 | 5,470,844 | 313.14 | 768,567(約3.6個月) |
個人 | 66,900 | 66,900 | 196.58 | 431,153(約2個月) |
多 | 32,929 | 44,582,102 | 177.58 | 510,060(~2.36個月) |
全部的 | 908,017 | 50,119,846 |
數據顯示:
- 大多數合約都是通過合約工廠鑄造的克隆合約。約89.1%的合約來自多合約工廠(32,929 個地址),而只有10.9% 的合約是在沒有合約工廠的情況下部署的。然而,非合約工廠合約的平均負載(約313 個存儲槽/合約)比合約工廠子合約(約178-197 個存儲槽/合約;中位數為431-510,000個區塊)更重,活躍時間更長(活躍時間中位數約為 769,000 個區塊)。
- 集中度極高。排名前五的工廠佔所有部署的約43% ,排名前一百的工廠佔所有部署的約89% 。一半的工廠部署的合同不超過5份, 99%的工廠部署的合同不超過4,978份。
工廠可擴展廉價、輕量級的代碼。單個和多個工廠組中每個合約的插槽數量較少,平均活動跨度較短,這與代理模式、代幣/交易對垃圾郵件、空投鑄幣以及 MEV/腳手架合約相符——這些合約易於量產,通常壽命較短,而且許多合約從未積累過有意義的插槽。
非工廠部署更傾向於基礎設施。更大的存儲空間和更長的跨度意味著定製系統(治理、保險庫、路由器、網橋、彙總基礎設施)由 EOA/多重簽名直接部署,並持續保持活躍。
“獨立工廠”≠ 持久。只生產一個子進程的工廠比非工廠部署的跨度更短,槽位也更少。許多工廠看起來像是工廠的引導程序或虛榮用途,而不是長期運行的應用程序。
按代碼大小
年齡= 最新區塊(22431083) - 首次看到該狀態的區塊。
尺寸類別 | 合約數量 | 零活動跨度(%) | 非零中值活動跨度(塊) | 非零 P99 活動跨度(塊) | 平均年齡(街區) |
---|---|---|---|---|---|
微小(<1KiB) | 44,298,982 | 57.9 | 2,192,443(約10個月) | 8,958,543(約3年) | 6,318,754(~2.4年) |
小(1-5KiB) | 4,333,667 | 41.4 | 205,882(約28天) | 11,690,310(~4年) | 11,044,783(~4.2年) |
中等(5-10KiB) | 592,936 | 23.4 | 58,613(約8天) | 11,417,402(~4年) | 7,283,986(~2.8年) |
大(10-20KiB) | 794,012 | 8.6 | 8,302(約1天) | 10,871,423(~4年) | 5,297,655(~2年) |
非常大(20-24KiB) | 100,249 | 11.1 | 273,516(約1個月) | 11,276,444(~4年) | 5,973,078(~2.3年) |
數據顯示:
- Tiny 占主導地位,但這是兩個不同的世界。Tiny 合約約佔所有合約的 88% ,但超過一半(57.9%)是零跨度合約,這與大規模鑄造的克隆合約和存根合約一致。然而,在那些活躍的合約中,Tiny 的跨度中位數是所有合約中最長的(約 219 萬個區塊,約 10 個月),這表明存在第二個長期使用的子群體。
- 活動跨度與規模的關係並非單調變化,而是呈 U 形。以非零活動量為條件,中值跨度從極小 → 小型 → 中型 →大型(最短,約 8.3k 個區塊 ≈ 1 天)下降,然後反彈至非常大型(約 27.3k 個區塊 ≈ 1 個月) 。因此,“大型代碼運行時間更長”是錯誤的,因為中等規模的代碼變動速度最快。
- 每個規模類別都有一個長尾。P99 集群跨越所有規模的約 1000 萬到 1170 萬個區塊(約 3-4 年),這意味著每個區塊中都有一小部分合約會持續存在數年。
微型合約通常充斥著代理和工廠克隆。大多數合約價格低廉、一次性使用且從未使用過。然而,真正重要且有用的合約運行時間更長,因此非零合約群體的跨度也更長。另一種可能性是,它們是用於升級的網關合約。
小型/中型/大型合約專注於定製化、炒作週期合約(例如代幣/NFT 鑄幣/挖礦、一次性應用邏輯)。團隊還會在升級時輪換地址,因此活動範圍涵蓋各個細分領域。
超大型合同通常預示著系統複雜。這類合同比中型合同更為罕見,且持續時間更長,但並非總是成功——一些大型部署從未被採用。
有狀態合約 vs. 無狀態合約
有狀態= 至少有 1 個存儲槽的合約
無狀態= 合約存儲槽為 0
類型 | 合同總數 | 零壽命(%) | 中位非零活動跨度(塊) |
---|---|---|---|
有狀態的 | 23,127,186 | 50.8 | 105,112(約14.6天) |
無國籍 | 26,992,660 | 58.91 | 3,185,332(約1.2年) |
數據顯示:
- 大多數合約都是無狀態的。53.9 % 的合約從未接觸過存儲;46.1% 的合約接觸過存儲。
- 零活動在無狀態合約中更為常見。58.9 % vs. 有狀態合約為 50.8%。
- 無狀態合約的運行時間更長。它們的中位非零跨度比有狀態合約長約30 倍(1.2 年 vs. 14.6 天)。
為什麼這種情況可能發生
- 無狀態 = 廉價耐用的實用程序。許多都是簡單的轉發器、代理或助手。它們易於批量部署(因此很多最終都無法實現跨度),但有用的程序可以運行數年,因為它們不保存數據,也不需要遷移。
- 有狀態 = 升級或替換。代幣、礦池、保險庫以及存儲數據的應用程序更有可能升級或更換為新版本。這將活動分散到新地址,並縮短每個舊地址的跨度。
存儲槽的使用頻率如何?
圖 20:大多數存儲槽位被寫入一次後就不再使用,超過 60% 的存儲槽位處於零活動跨度。圖 21(洛倫茲曲線):存儲使用情況極度傾斜,基尼係數為 0.973。數據顯示:
- 超過一半的存儲槽位(63.3%)是臨時的。這些槽位一旦設置就永遠不會被訪問。
- 集中度極高。單個合約佔據總插槽的約 6%(參見XEN Crypto );排名前 1000 的合約佔據所有插槽的約 51%。
零活動槽常見於一次性標誌、空投聲明、初始化記錄、廢棄餘額或快速替換的合同中的數據。
一小部分大型系統承載了以太坊大部分的活躍存儲槽位。這就是為什麼一小部分合約佔據了總存儲槽位。
需要注意的是,代理模式通常會將邏輯和存儲拆分。邏輯合約通常呈現無狀態且長期有效,而存儲合約則保存狀態並可能輪換。僅查看地址(而非項目)會發現有狀態代理的跨度較短。
一些標記為“零活動”的 slot 可能仍會在鏈下讀取(通過 RPC 調用)。這些讀取操作不會出現在鏈上。
開放思想
狀態到期
大多數狀態很快就會冷卻。一個務實的方向是定期修剪非活動狀態,並採用復活方案:
- 窗口: 12-18 個月的活動窗口適合分佈:許多州的中位非零跨度遠低於一年,而長壽州則高於一年。
- 優點:保持熱狀態較小,減緩狀態增長,減少磁盤 I/O,減少存儲大小並提高塊處理時間
首先,我們來考慮一下,通過移除零活動跨度賬戶和存儲槽位,可以節省多少存儲空間。我們在 Geth 的平面快照(鍵值“狀態”表)上進行了測量,將原始快照與移除這些鍵後的快照(節點已同步到區塊 22,431,083)進行了比較:
原始大小(GiB) | 零活動跨度大小 (GiB) | 刪除後 (GiB) | 儲蓄 | |
---|---|---|---|---|
賬戶 | 13.48 | 2.58 | 10.90 | 19.14% |
插槽 | 94.12 | 57.62 | 36.50 | 61.22% |
全部的 | 107.60 | 60.20 | 47.40 | 55.95% |
僅在快照上,刪除零跨度狀態就會減少約 56% 的存儲空間——這是一個強烈的信號,表明狀態到期可能會大大縮小熱狀態集。
更便宜地部署已存在的字節碼
儘管客戶端通常會通過代碼哈希對磁盤上的字節碼進行重複數據刪除,但重複代碼仍然會在部署過程中浪費 Gas。以下是一些可行的方案:
- 本地重複折扣(相同區塊/時期) :如果字節碼
codehash
在同一個區塊(或短時期)中出現兩次,則第二次部署支付的代碼押金減少。- 優點
- 可以根據合約部署的順序輕鬆地在區塊中進行驗證
- 工廠和 AA 錢包立即節省 gas 成本
- 由於折扣僅適用於區塊內,因此可以限制垃圾郵件的影響
- 缺點
- 不公平:誰應該支付全部金額?
- EL 客戶端實現的複雜性
- 優點
- 全局“codehash 註冊表” :系統合約存儲了
codehash → exists
在其存儲中。所有合約部署都會首先檢查字節碼是否已存在。- 優點:
- AA 錢包、代理和工廠合約的 Gas 節省。
- 如果字節碼已經存在,則可以使用委託合約的機制
- 缺點:
- 激勵垃圾克隆
- 向註冊表添加 DoS 表面——需要謹慎定價,以免折扣被垃圾郵件發送者濫用
- 隨著存儲 trie 大小的增加,字節碼哈希可能會成為狀態增長的問題
- ZKEVM 必須證明代碼哈希值的存在
- 優點:
- 每個庫對應一個合約:不再為同一個庫創建重複合約,而是添加一個新的操作碼
LIBCREATE
,用於部署一個僅使用代碼哈希的“庫合約”。這可能有助於刪除重複庫,但前提是進一步的分析表明這些庫確實佔用了合約的很大一部分。
每個地址的漸進式存儲定價
如今,全球儲能市場由少數幾份合同主導。我們或許希望價格能夠反映邊際負擔。
按地址漸進式存儲定價: SSTORE
基本成本加上當slot_count(address)
超過閾值時產生的階梯式附加費(需要協議內 slot 計數器,類似於 EIP-2027 風格的元數據)。保持成本可預測(階梯式),同時推動大型合約將狀態外部化成本內部化。
優點:
- 與地址的實際存儲增長相關的成本。
- 與按時段重新定價相比,分級定價使成本規劃更加容易。
- 鼓勵更好的存儲設計,避免濫用“無限插槽”。
缺點:
- 需要插槽計數元數據和規則;增加了複雜性。
- 當交易跨越層級中間交易時,氣體估算會變得更加困難。
- 可能會促使開發人員將狀態分散到多個合約中(更多調用、更多表面、更差的用戶體驗)。
- 有用的合約往往有很多空位,而我們卻在懲罰它們的活躍度
臨時存儲模型
許多 slot 只需寫入一次,便不會再被觸及。為開發人員提供一個更便宜、合約可讀的存儲位置,用於存儲可確定性地丟棄的短期數據。
工作原理(高級):
- 每個賬戶的臨時存儲根:將
tempStorageRoot
添加到每個合約賬戶,用於存儲臨時數據 - 全局時間段:所有寫入都進入當前時間段(例如 6-12 個月)
- 確定性到期:一旦一個週期結束,其數據在讀取時邏輯上為零,無論它是否已被 EL 客戶端物理修剪
- 定價:使當前的
SSTORE
更昂貴,並且將數據存儲在臨時存儲中比SSTORE
更便宜
優點:
- 將成本與預期壽命相匹配
- 簡單的思維模型:“使用臨時存儲來存儲大約 N 個月後可能會丟失的數據”
- 與未來狀態到期工作正交(且兼容)
缺點:
- 由於新的帳戶字段,需要遷移帳戶
- 如果開發人員依賴存儲重置之後的值,則工具風險
- 天然氣時間表需要仔細調整以避免濫用
- 已部署合約中的
SSTORE
具有更高的 gas 成本,可能不公平
EL 客戶端的性能改進
關於 EL 客戶如何利用這些信息來提高其績效的一些想法:
- 存儲引擎中的冷/熱狀態隔離:將最近接觸的帳戶/插槽保留在“熱”存儲引擎中,然後以較低的頻率批量壓縮和快照冷帳戶/插槽,以減少寫入放大。
- 槽位計數驅動的提交:優先處理持有超大槽位份額的大型合約的 trie 路徑的數據庫提交。這將優先處理最嚴重的違規者。
- 緩存頻繁出現的代碼哈希並檢查是否存在:對於合同創建,跳過重新保存重複的字節碼,減少寫入放大和壓縮壓力。
結束語
以太坊的問題不僅僅是狀態增長——沒有辦法處理陳舊狀態。
大多數賬戶都是短期賬戶。工廠和模板克隆佔據了部署的主導地位,但獨特的合約佔據了大部分存儲空間。一小部分合約佔據了大部分插槽,而且許多插槽只編寫一次,之後就再也沒有被修改過。如果我們能夠在不破壞可組合性的前提下,找到突破性的解決方案,以太坊就能更快地擴展。
並非所有狀態都是平等的——也許協議不應該將其視為平等。
致謝
特別感謝 ethPandaOps 團隊提供的出色的Xatu基礎設施。
還要感謝 Guillaume、Carlos、Matan 和 Ignacio 審閱本文。
後續步驟
- 強度分析:將跨度與活動強度配對,以區分“安靜長壽”和“短暫但激烈”的狀態。還應考慮氣體消耗。
- 按標籤分組:使用標籤(例如令牌、路由器、保險庫、橋樑、工廠、代理)進行分析,以進一步鞏固推理。
- EIP-6780 後自毀:調查有多少合約原本應該自毀並從狀態樹中移除,但由於 EIP6780 而未能成功。同時調查有多少合約執行了
SENDALL
操作碼。 - 空餘額合約:檢查有多少合約被創建、持有多年,然後被清空。如果這些合約不再使用,或許我們可以刪除它們,這樣就沒人關心了。
- 庫:檢查有多少合約是使用庫部署的。
- 根據獨特/模板劃分工廠/非工廠合同的分配。
- 根據不同的屬性(例如部署者、工廠、代碼哈希、類別)以及它們在 EL 客戶端中代表多少存儲空間來識別集群。
附錄
鏈接到分析代碼庫。