Статьи · Стейкинг
Удержанное с уходящих раздаётся оставшимся не сразу, а равномерно — и у этой раздачи есть окно. Кажется, что несколько выходов подряд должны складывать окна в цепочку. В контракте это устроено иначе, и разница объясняет, почему доходность пула ведёт себя зубцами.
5 октября 2026 · проверено при публикации
Окно раздачи отсчитывается от каждого выхода отдельно и никогда не превышает семи суток. Три крупных выхода подряд не дают двадцати одних суток: каждый обнуляет окно и отсчитывает семь заново от себя. Окна не выстраиваются в цепочку, а перекрываются.
Оранжевые засечки — моменты выходов, синие прямоугольники — окна раздачи, цифра внутри каждого равна его длине в сутках. Три выхода в первые двое суток закрывают раздачу на девятые, а не на двадцать первые. Позже конца не выйдет и в принципе: каждое окно тянется ровно семь суток от своего выхода.
У пула стейкинга один источник дохода — удержание с тех, кто выходит. Когда стейкер забирает позицию, контракт оставляет в пуле 10% от неё целиком, вместе с накопленным. Проверяется это прямо в исходном коде: счётчик undistributedFees увеличивается ровно в одном месте, внутри функции выхода.
Но удержанное не раздаётся сразу. Оно попадает в «котёл» нераспределённых удержаний и вытекает оттуда равномерно, с постоянной скоростью rewardRate, измеряемой в RNBW за секунду. Пока оно вытекает, растёт цена доли — то есть прибавка достаётся всем, кто в пуле, пропорционально размеру позиции. Новых долей при этом никто не получает: дорожает каждая существующая.
Поэтому доходность стейкинга зависит не от того, сколько людей зашло, а от того, сколько вышло. Приток её снижает: тот же поток монет делится на больший пул. Отток поднимает, причём дважды — пул уменьшается, и в него добавляется десятина ушедшего.
Новое удержание не просто прибавляется к старому. Контракт заново считает, с какой скоростью теперь всё вытечет, и выбирает одну из трёх ветвей. Выбор определяется единственным сравнением: новая скорость больше текущей или нет.
Раздача не идёт — прошлое окно уже истекло
Скорость равна котлу, делённому на семь суток. Всё начинается с чистого листа.
окно — ровно 7 суток
Котёл ÷ 7 суток не меньше текущей скорости — выход крупный
Скорость поднимается, а окно обнуляется и отсчитывается заново от этой секунды.
окно — ровно 7 суток
Котёл ÷ 7 суток меньше текущей скорости — выход мелкий
Скорость не меняется, капля прежняя. Просто капать придётся дольше: остаток окна плюс добавка, делённая на скорость.
окно — меньше 7 суток
Вот почему потолок держится без исключений. В двух ветвях окно равно семи суткам по определению. В третьей оно короче семи — иначе скорость выросла бы и сработала вторая ветвь. Четвёртого варианта в коде нет.
Защита от понижения скорости сделана намеренно и прокомментирована в контракте словами rewardRate can only stay flat or increase. Без неё кто угодно мог бы выходить копеечными порциями и каждым выходом ронять доходность всем остальным.
Три случая в одном масштабе. Оранжевым показано, сколько RNBW лежит в котле: он скачком пополняется на выходе и ровно стекает вниз. Наклон линии и есть скорость раздачи — чем круче, тем быстрее удержания доходят до стейкеров.
A — выход, когда раздача не идёт. Удержано 100 000 RNBW при пустом котле, значит раздавать по 14 286 в сутки. На седьмые сутки не остаётся ничего.
C — крупный выход посреди раздачи. На третьи сутки удержано ещё 100 000. Наклон стал круче, скорость выросла до 22 449 в сутки, окно обнулилось — конец съехал на десятые сутки.
D — три крупных выхода подряд. Конец на девятые сутки, а не на двадцать первые. Зато скорость выросла с 14 286 до 37 026 RNBW в сутки: раздача ускорилась в 2,6 раза.
В случае C и прячется обман зрения. Раздача действительно закончится позже изначальных семи суток — на десятые. Но это не «окно длиной в десять суток»: от выхода на третьи сутки до конца ровно семь. Окно не растянулось, его переставили.
Третья ветвь выглядит незаметной, но именно она срабатывает почти всегда: крупных выходов, поднявших скорость, за всю историю единицы. Разберём её вблизи — на общем графике всё решающее происходит в масштабе, которого там не видно.
Вставка ① увеличена по вертикали в 28 раз, вставка ② по времени в 15 раз. Пунктиром в обеих показано, как шло бы дело без пополнения.
Главное видно во вставке ①: линия до ступеньки и после неё параллельны. Скорость не изменилась, капля осталась прежней — поднялся только уровень в котле. Поэтому и срок продлевается ровно на столько, за сколько капля успеет вынести добавку: 1 000 ÷ 14 286 в сутки = 1 час 41 минута. Это видно во вставке ②.
Конец раздачи уехал с 7,00 на 7,07 суток, а от самого выхода до конца осталось 4,07 — почти на три суток меньше потолка. Но запас в три суток здесь свойство примера, а не ветви: он взялся из того, что выход случился в середине окна и был крошечным.
Тогда та же третья ветвь продлит раздачу почти на семь суток. Её запас зависит не только от размера добавки, но и от того, сколько окна оставалось: продление равно остатку окна плюс добавка, делённая на скорость.
По вертикали — длина окна от выхода, в сутках. Каждая оранжевая линия соответствует одному размеру удержания. Точка B — случай из прошлого раздела: выход на третьи сутки с добавкой 1 000. Правая верхняя точка — выход на 6,9-е сутки с добавкой 95 000.
Линии параллельны и все упираются в синий потолок. Чем позже выход, тем меньше осталось вытечь, но тем больше добавка весит относительно остатка. Крупная добавка в конце окна подходит к семи суткам почти вплотную: при выходе на 6,9-е сутки с удержанием 95 000 остаётся 0,10 плюс 6,65 — 6,75 суток, и это всё ещё ветвь «срок продлён».
| Удержано | В котле | Окно, сут | Конец, сут | Ветвь |
|---|---|---|---|---|
| 1 000 | 2 429 | 0,17 | 7,07 | срок продлён |
| 25 000 | 26 429 | 1,85 | 8,75 | срок продлён |
| 50 000 | 51 429 | 3,60 | 10,50 | срок продлён |
| 95 000 | 96 429 | 6,75 | 13,65 | срок продлён |
| 98 571 | 100 000 | 7,00 | 13,90 | срок продлён |
| 100 000 | 101 429 | 7,00 | 13,90 | скорость выросла |
Посмотрите на две последние строки — это граница ветвей. Удержание 98 571 RNBW ещё попадает в «срок продлён», а 100 000 уже поднимает скорость. И обе дают одно и то же: ровно семь суток. Скачка между ними нет, граница гладкая — ветвь «срок продлён» подходит к семи суткам снизу, ветвь «скорость выросла» стоит на них. Поэтому потолок и держится без исключений.
А вот календарная длина раздачи не ограничена ничем. В колонке «конец от начала раздачи» при удержании 95 000 стоит 13,65 суток — почти две недели. Семисуточный потолок ограничивает окно от каждого отдельного выхода, а не общий срок. Выходы, приходящие под конец окна, могут толкать его сколько угодно, и при живом потоке выходов раздача просто никогда не прерывается — что и происходит на деле: котёл не обнуляется.
Всё описанное — прямое следствие функции _addFees в контракте стейкинга 0x288ea8Bcb51d53aD361D786EFa904C401904aF70. Исходный код опубликован и проверен, читать его можно в обозревателе.
Контракт присваивает dripEndTime = block.timestamp + dripDuration, где dripDuration равна 604800 секундам, то есть ровно семи суткам от текущей секунды. Прошлое окно при этом просто перестаёт существовать.
Здесь dripEndTime = block.timestamp + undistributedFees / rewardRate. Условие входа в эту ветвь само гарантирует, что частное меньше dripDuration, — иначе сработала бы предыдущая ветвь.
Остаток котла и скорость читаются с контракта вызовами undistributedFees() и rewardRate(). Их же, вместе с историей всех выходов и пометкой, какая ветвь сработала на каждом, видно на странице Стейкинг.
Числа в примерах этой статьи получены воспроизведением _syncPool и _addFees шаг за шагом от нулевого состояния, а не взяты с сайта, — чтобы их нельзя было подогнать под вывод.