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 настройки
- Деплой прокси и вызов
initialize. - Вызов
setDiamond. - Вызов
registerProductдля адресаMagnetNftProductконкретного продукта. - Настройка покупки уровня 1 через
setBuyPaymentTokenConfig(для каждого ERC20). - Настройка крафта:
setCraftLevelConfigдля уровней 2..N;setCraftLevelPaymentConfigдля каждого уровня и токена оплаты.
- На контракте
MagnetNftProductвыдать менеджеру ролиMINTER_ROLEиBURNER_ROLE. - Проверить, что продукт включен через
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 должны быть настроены все промежуточные уровни/токены.
Алгоритм:
price = computePrice(product, paymentToken, targetLevel, 1).safeTransferFrom(msg.sender, address(this), price).mintProduct(msg.sender, objectId, false, targetLevel)в контракте продукта.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.