EUnitCoin (EUNIT)
Нетрансферабельный UUPS-апгрейдабельный ERC20 (contracts/tokens/EUnitCoin.sol).
Паттерн — как у UnitCoin: AccessControl + TransferManager (в айтлисты/блэклисты,
transferEnabled = false по умолчанию). Минт под MINTER_ROLE, саплай не ограничен.
Обмен UNIT ↔ EUNIT
exchangeUnit(amount)— переводит UNIT пользователя на баланс контракта (лок) и минтит EUNIT 1:1. ТребуетсяapproveUNIT на адрес EUnitCoin.exchangeBack(amount)— сжигает EUNIT и возвращает залоченные UNIT 1:1. Ограничен квотойunitExchanged[user]: вернуть можно не больше, чем вложил. Квота — числовой счётчик; трата EUNIT её не уменьшает, минт наградами не даёт. Сжигание идёт в обходcheckBurnAccessи не зависит отburnEnabled.
События
UnitExchanged(user, amount, timestamp)UnitExchangedBack(user, amount, timestamp)
Деплой
deploy/eunit.deploy.ts (тег EUnitCoin, треб ует UNIT_COIN_ADDRESS в env).
Обязательный шаг (зашит в скрипт): добавить EUnitCoin в transferWhitelist
UnitCoin — иначе exchangeUnit ревертится, т.к. checkTransferAccess в UnitCoin
пропускает transferFrom только для вайтлистнутого msg.sender.
Тесты
test/EUnitCoin.test.ts — деплой/инициализация, ограничения transfer/burn,
обе стороны обмена, семантика квоты, роли и апгрейд:
npx hardhat test test/EUnitCoin.test.ts