Перейти к основному содержимому

Калькулятор дохода

Калькулятор показывается на главной, на /career и на /career/cards. Считает его бэкенд — apps/backend/src/calcIncome/. Фронт только шлёт параметры симуляции и рисует ответ.

Откуда берутся числа

ЧтоИсточник
ЛБ, рефка, партнёркаdiamond-контракт, OptionsFacet
Глубина линий матрицы Sandboxmongo, 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 подменил бы проценты нулями на час.