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

MagnetNftManager

Обзор

MagnetNftManager — управляющий контракт для продуктовых NFT MagnetNftProduct. Он реализует единый on-chain слой для:

  • покупки NFT конкретного продукта за ERC20 (в том числе сразу на целевой уровень);
  • крафта (создания NFT более высокого уровня) через сжигание NFT предыдущего уровня + оплату craft fee;
  • хранения конфигурации цен/разрешенных платежных токенов по каждому продукту.

Технические свойства:

  • upgradeable (UUPS);
  • защита от реентранси (ReentrancyGuardUpgradeable);
  • админские операции через AccessControlUpgradeable.

Контекст и зависимости

  • MagnetNftProduct (контракт продукта): менеджер минтит/сжигает/апгрейдит NFT и читает состояние (ownerOf, tokenLevel, rootToken).
  • Diamond: менеджер проверяет, что пользователь активирован (getUserId(user) != 0).

Роли и права

На стороне MagnetNftManager:

  • DEFAULT_ADMIN_ROLE — настройка продуктов/цен/diamond и апгрейды (UUPS).

Требования к MagnetNftProduct:

  • у менеджера должны быть роли на контракте продукта: MINTER_ROLE и BURNER_ROLE.
    • Без MINTER_ROLE будут падать mintProduct и upgradeTokenLevel.
    • Без BURNER_ROLE будет падать burn в сценариях крафта.

Структуры и конфигурация

PaymentTokenConfig

Используется для покупки NFT уровня 1 за конкретный токен.

  • enabled — можно ли платить этим токеном;
  • pricePerUnit1 — цена 1 NFT уровня 1.

CraftLevelPaymentConfig

Оплата крафта (craft fee) для уровня в конкретном токене.

  • enabled — можно ли платить этим токеном;
  • price — craft fee для уровня.

CraftLevelConfig

Базовые правила крафта для уровня N (где N >= 2).

  • enabled — разрешен ли крафт на этот уровень;
  • requiredPrevCount — сколько NFT предыдущего уровня нужно на 1 крафт;
  • paymentTokens[token] — настройки оплаты крафта для разных токенов.

ProductConfig

Конфигурация продукта по адресу контракта MagnetNftProduct.

  • enabled — можно ли проводить с NFT продукта операции крафтинга и покупки;
  • product — адрес контракта продукта;
  • buyPaymentTokens[token] — настройки оплаты покупки уровня 1;
  • craftLevels[level] — настройки крафта на уровни >= 2.

Ограничение уровней: MAX_LEVEL = 15.

Экономика и потоки средств

  • Оплата в ERC20 списывается с пользователя на баланс самого MagnetNftManager.
  • В текущей реализации нет функций вывода/распределения средств — баланс остается на контракте до апгрейда/операционного решения.

События

ProductBought

Сигнатура:

event ProductBought(address indexed user, address indexed product, uint256 indexed tokenId, uint256 level, address paymentToken, uint256 price, bool isRoot);

Когда эмитится: при успешной покупке в buy.

ProductCrafted

Сигнатура:

event ProductCrafted(address indexed user, address indexed product, uint256[] tokenIds, uint256 targetLevel, bool[] minted, uint256[] burnedTokenIds, address paymentToken, uint256 price);

Когда эмитится: при успешном крафте в craft.

Поля события:

  • tokenIds — итоговые токены (включая root, если он был апгрейжен);
  • minted — флаги: true для доминченного токена, false для апгрейженного root;
  • burnedTokenIds — реально сожженные токены (root сюда не попадает).

Playbook настройки

  1. Деплой прокси и вызов initialize.
  2. Вызов setDiamond.
  3. Вызов registerProduct для адреса MagnetNftProduct конкретного продукта.
  4. Настройка покупки уровня 1 через setBuyPaymentTokenConfig (для каждого ERC20).
  5. Настройка крафта:
    • setCraftLevelConfig для уровней 2..N;
    • setCraftLevelPaymentConfig для каждого уровня и токена оплаты.
  6. На контракте MagnetNftProduct выдать менеджеру роли MINTER_ROLE и BURNER_ROLE.
  7. Проверить, что продукт включен через setProductEnabled.

Функции: инициализация и апгрейд

initialize

Сигнатура:

function initialize() public initializer

Назначение: инициализация upgradeable-контракта и выдача DEFAULT_ADMIN_ROLE деплоеру.

Когда используется: один раз при деплое прокси.

_authorizeUpgrade

Сигнатура:

function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE)

Назначение: ограничить апгрейд реализации UUPS только администраторам.

Функции: чтение конфигурации

getBuyPaymentTokenConfig

Сигнатура:

function getBuyPaymentTokenConfig(address product, address token) public view returns (PaymentTokenConfig memory)

Назначение: прочитать конфиг оплаты покупки уровня 1 для токена token.

Типовое использование: UI/бекенд для отображения доступных токенов и цен.

getCraftLevelConfig

Сигнатура:

function getCraftLevelConfig(address product, uint256 level) public view returns (bool enabled, uint256 requiredPrevCount)

Назначение: прочитать базовый конфиг крафта для уровня level.

getCraftLevelPaymentConfig

Сигнатура:

function getCraftLevelPaymentConfig(address product, uint256 level, address token) external view returns (CraftLevelPaymentConfig memory)

Назначение: прочитать конфиг оплаты крафта на уровне level в токене token.

getMaxLevelToCraft

Сигнатура:

function getMaxLevelToCraft(address product) external view returns (uint8 maxLevel)

Назначение: вернуть максимальный уровень крафта как непрерывную последовательность включенных уровней (2..k).

Функции: администрирование

registerProduct

Сигнатура:

function registerProduct(address product) external onlyRole(DEFAULT_ADMIN_ROLE)

Назначение: зарегистрировать новый продукт в менеджере.

Сайд-эффекты:

  • products[product].product устанавливается в product;
  • products[product].enabled становится true.

setDiamond

Сигнатура:

function setDiamond(address _diamond) external onlyRole(DEFAULT_ADMIN_ROLE)

Назначение: установить адрес diamond, используемый в onlyActivatedUser.

setProductEnabled

Сигнатура:

function setProductEnabled(address product, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE)

Назначение: включить/выключить продукт.

setBuyPaymentTokenConfig

Сигнатура:

function setBuyPaymentTokenConfig(address product, address token, PaymentTokenConfig calldata cfg) external onlyRole(DEFAULT_ADMIN_ROLE)

Назначение: настроить токен оплаты и цену покупки уровня 1.

setCraftLevelConfig

Сигнатура:

function setCraftLevelConfig(address product, uint256 level, uint256 requiredPrevCount, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE)

Назначение: настроить базовые параметры крафта для уровня level.

setCraftLevelPaymentConfig

Сигнатура:

function setCraftLevelPaymentConfig(address product, uint256 level, address token, CraftLevelPaymentConfig calldata cfg) external onlyRole(DEFAULT_ADMIN_ROLE)

Назначение: настроить craft fee для уровня level в токене token.

Функции: прайсинг

computePrice

Сигнатура:

function computePrice(address product, address paymentToken, uint256 targetLevel, uint256 quantity) public view returns (uint256)

Назначение: рассчитать цену в paymentToken для покупки quantity NFT уровня targetLevel.

Правила:

  • targetLevel == 1: цена = pricePerUnit1 * quantity.
  • targetLevel > 1: цена считается итеративно по уровням 2..targetLevel:
    • currentPrice = requiredPrevCount * currentPrice + craftFee(level).

Важно: функция ревертится, если не настроен любой из необходимых промежуточных уровней/токенов.

Функции: покупка и крафт

buy

Сигнатура:

function buy(address product, uint256 targetLevel, address paymentToken) external nonReentrant onlyActivatedUser

Назначение: купить NFT продукта за ERC20.

Проверки (ключевые):

  • пользователь активирован в diamond;
  • продукт зарегистрирован и включен;
  • токен оплаты разрешен;
  • для покупки уровня > 1 должны быть настроены все промежуточные уровни/токены.

Алгоритм:

  1. price = computePrice(product, paymentToken, targetLevel, 1).
  2. safeTransferFrom(msg.sender, address(this), price).
  3. mintProduct(msg.sender, objectId, false, targetLevel) в контракте продукта.
  4. emit ProductBought(...).

craft

Сигнатура:

function craft(address product, uint256 targetLevel, uint256[] calldata tokenIdsToBurn, address paymentToken, uint256 quantity) external nonReentrant onlyActivatedUser

Назначение: скрафтить quantity NFT уровня targetLevel (>= 2) из NFT уровня targetLevel - 1.

Ключевые правила:

  • tokenIdsToBurn.length == requiredPrevCount * quantity.
  • все токены принадлежат пользователю, имеют правильный уровень, без дубликатов.

Правило root NFT:

  • если root NFT присутствует в tokenIdsToBurn, он не сжигается, а апгрейдится; остальные токены сжигаются.
  • если root отсутствует — сжигаются все токены.

Оплата:

  • списывается craftFee(targetLevel) * quantity на баланс менеджера.

Ограничение по газу:

  • проверка уникальности реализована через O(n^2) — не злоупотребляйте большим количеством токенов.

Модификаторы

onlyActivatedUser

Сигнатура:

modifier onlyActivatedUser()

Назначение: запретить buy/craft для неактивированных пользователей.

Проверка: IDiamond(diamond).getUserId(msg.sender) != 0.