Калькулятор дохода
Калькулятор показывается на главной, на /career и на /career/cards. Считает его
бэкенд — apps/backend/src/calcIncome/. Фронт только шлёт параметры симуляции и рисует
ответ.
Откуда берутся числа
| Что | Источник |
|---|---|
| ЛБ, рефка, партнёрка | diamond-контракт, OptionsFacet |
| Глубина линий матрицы Sandbox | mongo, PROGRAM_LEVELS_CONFIG.programlevelsconfigs |
| Сплит DAI/XMGT | константа DEFAULT_CURRENCY_SPLIT — 85% / 15% |
До этой работы проценты были захардкожены во фронте (PROGRAMS_INFO) и разошлись с
контрактом. Теперь источник правды один — бэкенд, фронт процентов не знает.
Знаменатель процентов — 1000. Значение с контракта делить на 1000 даёт долю.
Сигнатуры геттеров принимают uint8: с uint256 получается другой селектор и ложн ое
«Diamond: Function does not exist».
Сплит валют намеренно константа: getDistributionsPercents возвращает не сплит, а
пакет процентов по уровням, а реальное деление на DAI и XMGT считается в BoostLib от
бустов конкретного получателя — статического геттера не существует.
Как считается
12 уровней, у каждого свои ставки — плоского процента на все уровни нет.
Apex (program 1). ЛБ + партнёрка, сохранено правило «каждый третий не передаёт
деньги» (Math.floor(usersCount / 3)). Партнёрка тянется с контракта с 1 уровня, но
контракт сейчас отдаёт пустой массив, поэтому вклад нулевой — он появится сам, когда
проценты проставят в диаманде.
Sandbox (program 2). ЛБ + рефка + партнёрка.
Реферальный фонд делится между получателями цепочки, поэтому ставка на одного
аплайнера — referralRate / N, где N — число линий матрицы. Основание:
DistributionsLib.sol (baseAmount = totalReferralFund / referralLines) и
Distributions.sol:332 (percentByLine = referralPercents / referralLines).
Цикл cycle из монги индексируется номером матрицы, а не номером уровня: матриц
ровно столько, сколько элементов в массиве. Сейчас [5, 4, 3] — первая матрица
закрывается на 5 линиях, вторая на 4, третья на 3. Сменится конфиг на [4, 3, 2] —
расчёт поедет за ним без правок кода.
Партнёрка остаётся трёхлинейной: контракт отдаёт getPartnerPercents(2, N) = [50,50,50],
у 4-й и 5-й линии процента нет. База при этом собирается с 5 линий — вслед за глубиной
матрицы.
Допущение модели
Одни и те же люди не могут заполнять все три матрицы одновременно — при наивном
суммировании доход утроился бы. Реализовано последовательное заполнение: первая матрица
берёт людей до своей вместимости, остаток уходит следующей. Вместимость матрицы
глубиной N линий считается как 2^(N+1) - 2 — 62, 30 и 14 мест.
Это допущение симуляции, а не факт из контракта. Живёт в
allocateReferralBaseAcrossMatrices и меняется там же.
Из вместимости следует, что реферальный доход перестаёт расти после 62 + 30 + 14 = 106 человек на уровень: команда в 10 000 даст ту же цифру, что и 106. Если продукт ожидает иного, менять надо правило заполнения — это вопрос к маркетингу, а не к коду.
Нагрузка на RPC
Один холодный расчёт — 2 вызова контракта и 1 запрос в монгу.
getDistributionsPercents(program) отдаёт весь пакет по 15 уровням за раз, поэтому
поуровневых вызовов нет. Результаты кэшируются с часовым TTL, параллельные промахи
дедуплицируются. Неуспешные чтения намеренно не кэшируются: иначе разовый сбой RPC
подменил бы проценты нулями на час.