Как устроен учёт в DeFi lending простыми словами: добавление токенов, receipt tokens, debt tokens и NFT как ID позиции

Когда в интерфейсе lending-протокола нажимают Supply, кажется, что произошло что-то простое: токены ушли в протокол, баланс вырос, APY начал капать.

На уровне смарт-контракта это уже другая история.

Протокол должен одновременно:

  • принять underlying asset;
  • выдать пользователю claim на депозит;
  • начать начислять проценты без ручного пересчёта каждого кошелька;
  • связать supply с borrow capacity;
  • при borrow — создать долговую запись, которая тоже растёт со временем;
  • однозначно понять, какая именно позиция принадлежит какому участнику;
  • позже корректно обработать withdraw, repay, liquidation и transfer.

Именно здесь появляются два важных инженерных слоя, о которых реже говорят в «пользовательских» обзорах lending:

  1. token accounting — как протокол представляет депозит и долг в виде токенов или внутренних индексов;
  2. position identity — как протокол идентифицирует позицию: по адресу кошелька, по tokenId NFT или по отдельному loan id.

Если объяснять просто, lending — это не только «залог против займа», но и система бухгалтерии onchain, где каждый депозит и каждый долг должны иметь однозначный идентификатор и понятную механику роста во времени.

Ниже — разбор без лишнего жаргона: что происходит при добавлении токенов, как устроены receipt tokens и debt tokens, зачем позиции оформляют как NFT, где возникают риски и какой automation layer имеет смысл строить вокруг учёта позиций.

Что происходит при добавлении токенов в lending pool

Когда пользователь делает supply, снаружи видно одну транзакцию. Внутри протокола обычно проходит цепочка из четырёх шагов.

Шаг 1. Transfer underlying в pool

Пользователь отправляет, например, USDC или WETH в lending market contract.

После этого актив физически лежит в контракте протокола и становится частью общей ликвидности market.

Шаг 2. Запись claim пользователя

Протокол должен зафиксировать:

  • сколько underlying пользователь внёс;
  • в какой market;
  • с какого момента на этот claim начинают начисляться проценты;
  • можно ли этот claim использовать как collateral для borrow.

Здесь протокол выбирает модель учёта.

Шаг 3. Mint receipt token или обновление internal balance

В большинстве современных протоколов пользователь получает не «просто запись в mapping», а отдельный токен-расписку:

  • aUSDC, aWETH в Aave;
  • cUSDC, cETH в Compound;
  • share token в ERC-4626 vault-подобных market.

Эта расписка означает:

«протокол должен мне underlying плюс накопленный процент».

Шаг 4. Collateral flag и borrow capacity

Если актив разрешён как collateral, supply автоматически увеличивает borrow capacity пользователя.

Теперь система может считать:

  • стоимость collateral;
  • текущий debt;
  • health factor;
  • доступный лимит для нового borrow.

То есть supply — это не passive deposit, а вход в risk account, даже если пользователь ещё ничего не занимал.

Receipt tokens: как протокол представляет депозит

Receipt token — это onchain-представление депозита в lending market.

Если убрать бренды, идея простая:

  • вы отдали протоколу 1 000 USDC;
  • протокол выдал вам token, который подтверждает ваш claim;
  • со временем claim растёт за счёт процентов;
  • когда захотите выйти, сожжёте receipt token и получите underlying обратно.

Две основные модели начисления процентов

1. Rebasing balance

Баланс receipt token у пользователя сам увеличивается со временем.

Например:

  • положили 1 000 aUSDC;
  • через месяц в кошельке уже 1 004 aUSDC без дополнительных транзакций.

Плюсы:

  • интуитивно для пользователя;
  • легко читать баланс в wallet UI.

Минусы:

  • rebasing токены хуже встраиваются в composability;
  • некоторые контракты не ожидают, что balance меняется сам по себе;
  • для automation нужно отдельно отслеживать rebase events.

2. Exchange rate / liquidity index

Баланс receipt token может не меняться, но exchange rate растёт.

Например:

  • положили 1 000 USDC;
  • получили 1 000 cUSDC;
  • через месяц 1 cUSDC redeem’ится уже не в 1 USDC, а в 1.004 USDC.

Плюсы:

  • проще для DeFi composability;
  • transfer и accounting стабильнее;
  • многие vault-like системы так и работают.

Минусы:

  • пользователю сложнее «на глаз» видеть доходность;
  • automation layer должен считать underlying value, а не только token balance.

Что важно для инженера

Receipt token — это не просто UI-абстракция. Это:

  • transferable claim;
  • потенциальный collateral;
  • источник событий для indexers;
  • объект учёта для treasury и vault automation.

Если automation-система смотрит только на raw underlying balance пользователя в wallet, она не видит реальную lending position.

Что происходит при borrow

Borrow — зеркальная, но более опасная операция.

Шаг 1. Проверка collateral и лимитов

Протокол пересчитывает:

  • стоимость залога;
  • текущий debt;
  • borrow capacity;
  • health factor после новой операции;
  • наличие свободной ликвидности в market.

Шаг 2. Transfer borrowed asset пользователю

Если проверки прошли, протокол отправляет, например, USDC на адрес заёмщика.

Шаг 3. Создание debt record

Здесь протокол должен зафиксировать долг так, чтобы он:

  • рос со временем;
  • учитывался в health factor;
  • мог быть погашен частично или полностью;
  • мог быть ликвидирован;
  • был однозначно привязан к позиции.

Debt tokens

Во многих протоколах долг тоже представлен отдельным токеном:

  • variable debt token;
  • stable debt token;
  • internal debt share.

Логика похожа на receipt tokens, только в обратную сторону:

  • receipt token = «мне должны»;
  • debt token = «я должен».

Проценты увеличивают debt balance или exchange rate debt token.

Почему debt token удобнее plain mapping

Если долг — отдельный onchain asset, протокол получает:

  • прозрачный учёт;
  • возможность composability;
  • события для indexers;
  • иногда — transferability debt position, если это задумано дизайном.

Но transferability debt — отдельный risk design choice. Не каждый протокол это разрешает.

Как протокол идентифицирует lending position

Вот ключевой вопрос: что такое «позиция» в lending?

На практике есть три распространённые модели.

1. Account-based position

Самая частая модель в classic lending markets.

Позиция привязана к адресу:

user address → supplies in market A, B, C
user address → debts in market X, Y
user address → health factor across portfolio

Примеры подхода: Aave-style pooled markets, Compound-style markets.

Как это работает

  • один wallet = одна агрегированная позиция;
  • все supply и borrow внутри протокола сводятся к account state;
  • health factor считается по всему account;
  • liquidation бьёт по account, а не по одному изолированному loan object.

Плюсы

  • простая UX-модель;
  • легко понять «мой account в протоколе»;
  • хорошо работает для fungible collateral;
  • проще automation для типового retail user.

Минусы

  • сложнее изолировать риск между несколькими стратегиями на одном адресе;
  • transfer «части позиции» неестественен;
  • treasury ops на одном адресе быстро смешивают разные mandates.

2. NFT as position ID

Здесь каждая позиция получает уникальный onchain-идентификатор в виде NFT.

Обычно это ERC-721 token:

tokenId #4821 → конкретная borrow position
tokenId #4821 → collateral set, debt amount, market params

NFT не обязательно «картинка». Это просто non-fungible identifier, который можно:

  • хранить в wallet;
  • transferить;
  • использовать как ключ в контракте;
  • индексировать отдельно от user address.

Зачем это делают

Изолированные позиции

Один пользователь может иметь несколько независимых loans:

  • tokenId 101 — ETH collateral / USDC debt;
  • tokenId 102 — wBTC collateral / USDC debt;
  • tokenId 103 — experimental market position.

Liquidation одной не обязана автоматически смешиваться с другой, если протокол так спроектирован.

Transferability позиции

Иногда протокол сознательно делает loan position transferable:

  • новый владелец NFT принимает на себя debt и права на collateral;
  • можно передать позицию без полного close + reopen;
  • появляется secondary market на сами loan positions.

Это мощно, но повышает complexity и risk.

Удобство для automation и portfolio systems

Для vault, fund или strategy manager NFT position ID удобен тем, что:

  • каждая стратегия живёт в отдельном tokenId;
  • легче строить monitoring per position;
  • проще audit trail;
  • можно привязать policy к конкретному NFT, а не ко всему wallet.

Где это встречается

Прямо «NFT как ID borrow position» — не самый массовый паттерн в classic Aave/Compound, но он хорошо виден в:

  • isolated lending designs;
  • некоторых permissioned / structured credit markets;
  • протоколах, где позиция должна быть отдельным объектом учёта;
  • NFT-backed lending, где loan часто имеет собственный id и иногда оформляется через NFT-like ownership model.

Важно не путать два разных смысла NFT в lending:

  1. NFT как collateral — залогом является NFT-коллекция;
  2. NFT как ID позиции — сама loan position идентифицируется tokenId.

Это разные вещи, хотя могут существовать одновременно.

3. Loan ID без NFT

Промежуточная модель: позиция имеет numeric loan id, но не является transferable NFT.

loanId 91822 → borrower, collateral, debt, status

Это часто встречается в:

  • NFT collateral lending;
  • P2P loan protocols;
  • private credit pools;
  • order-book-like credit markets.

Плюсы

  • проще, чем ERC-721 infrastructure;
  • легче хранить status machine: active / repaid / liquidated / defaulted;
  • удобно для backend automation.

Минусы

  • loan id обычно не transferable;
  • UX беднее для onchain portfolio composition;
  • secondary market сложнее.

NFT как collateral vs NFT как position ID

Это самая частая путаница.

NFT as collateral

Пользователь залогом приносит, например:

  • Bored Ape;
  • Art Blocks;
  • gaming asset;
  • RWAs wrapped as NFT.

Протокол оценивает NFT floor/oracle price и выдаёт loan.

Здесь NFT — asset in the vault, а не identifier всей lending position.

Типичный flow:

  1. пользователь deposit NFT;
  2. протокол создаёт loan record;
  3. loan имеет loanId;
  4. при repay NFT возвращается;
  5. при default NFT уходит на auction / liquidator.

NFT as position ID

Здесь NFT представляет саму loan position.

Transfer NFT может означать:

  • смену debtor;
  • смену owner rights на collateral/debt bundle;
  • transfer structured credit claim.

Это гораздо более чувствительный design choice.

Как выглядит полный lifecycle с token accounting

Возьмём типовой сценарий: supply WETH, borrow USDC, потом частично repay.

Supply WETH

  1. пользователь approve + supply WETH;
  2. протокол забирает WETH;
  3. mint aWETH или аналог;
  4. collateral capacity увеличивается;
  5. indexer фиксирует receipt token mint.

Borrow USDC

  1. протокол проверяет health factor;
  2. transfer USDC пользователю;
  3. mint variable debt token или увеличивает debt record;
  4. health factor уменьшается;
  5. monitoring должен пересчитать liquidation distance.

Interest accrual

Со временем без новых tx:

  • receipt token value растёт;
  • debt value растёт;
  • health factor меняется;
  • utilization pool меняется.

Automation layer обязан это пересчитывать offchain постоянно, потому что onchain state меняется даже когда пользователь «ничего не делает».

Repay

  1. пользователь возвращает USDC;
  2. debt token burn или debt balance уменьшается;
  3. health factor улучшается;
  4. часть collateral можно withdraw, если лимиты позволяют.

Withdraw collateral

  1. протокол проверяет, не станет ли позиция unsafe;
  2. burn receipt token;
  3. transfer underlying пользователю.

Если на любом шаге health factor ломается — withdraw или borrow блокируется.

Что меняется, если позиция — NFT

Если borrow position оформлена как tokenId, lifecycle похож, но identity layer другой.

Mint position NFT

При open loan:

  • создаётся новый tokenId;
  • в storage записываются collateral, debt, market params;
  • NFT mint’ится на wallet пользователя;
  • все последующие actions идут через tokenId.

Operations by tokenId

Вместо «изменить account state» протокол делает:

  • repay(tokenId, amount);
  • addCollateral(tokenId, asset);
  • removeCollateral(tokenId, asset);
  • liquidate(tokenId).

Automation становится проще per strategy, но сложнее на уровне wallet aggregation.

Transfer NFT

Если позиция transferable:

  • старый владелец теряет control;
  • новый владелец принимает debt + collateral bundle;
  • протокол должен проверить, разрешён ли такой transfer;
  • monitoring должен пере-привязать alerts к новому owner.

Это критичный риск: transfer position = transfer liability.

Из чего состоит accounting layer протокола

Если смотреть архитектурно, lending accounting почти всегда включает пять блоков.

1. Market ledger

Общий учёт ликвидности market:

  • total supply;
  • total borrow;
  • available liquidity;
  • utilization;
  • interest rate index.

2. User / position ledger

Учёт конкретных claims:

  • receipt token balances;
  • debt token balances;
  • или storage by tokenId.

3. Price oracle layer

Оценка collateral и debt для risk math.

4. Risk engine

Считает:

  • LTV / health factor;
  • borrow limits;
  • liquidation thresholds;
  • isolation constraints.

5. Event layer

Emit events для indexers:

  • Supply;
  • Withdraw;
  • Borrow;
  • Repay;
  • Liquidation;
  • Transfer;
  • Mint/Burn receipt or debt tokens;
  • Mint/Transfer position NFT.

Без хорошего event layer automation снаружи почти слепая.

Где возникают главные риски

1. Неправильный учёт receipt vs underlying

Automation может ошибиться, если считает:

  • receipt token 1:1 к underlying всегда;
  • debt token balance без interest index;
  • wallet balance вместо protocol claim.

2. Смешение account-based и position-based logic

Если один wallet держит несколько NFT positions, нельзя мониторить только aggregate wallet health.

Нужен per-position view.

3. Transferability surprises

Transfer debt NFT или position NFT может:

  • неожиданно сменить debtor;
  • обойти internal compliance assumptions;
  • сломать treasury mandate;
  • передать underwater position новому владельцу.

4. Rebasing / index mismatch

Indexer, treasury dashboard и liquidation bot должны использовать одну и ту же формулу перевода receipt/debt token в underlying value.

5. Collateral enabled flag

Supply receipt token не всегда означает, что asset уже работает как collateral.

Нужно отдельно отслеживать:

  • supplied balance;
  • collateral enabled/disabled;
  • isolated market constraints.

6. NFT appraisal risk

Для NFT collateral lending ошибка oracle или floor price может:

  • создать fake borrow capacity;
  • delay liquidation;
  • оставить протокол с неликвидным залогом.

Position NFT id не решает эту проблему сам по себе.

Какой automation layer нужен вокруг учёта

Если строить ops/control plane для lending, одного health factor alert недостаточно.

1. Position normalizer

Сервис, который приводит разные модели к единому виду:

position_id
owner
supplies[]
debts[]
health_factor
collateral_enabled
market_type: pooled | isolated | nft-collateral

Источник position_id:

  • user address;
  • NFT tokenId;
  • loanId.

2. Receipt/debt valuation engine

Отдельный модуль, который считает:

  • underlying value of receipt tokens;
  • underlying value of debt;
  • net account equity;
  • accrued interest since last sync.

3. Per-position monitoring

Особенно важно для:

  • treasury with multiple strategies;
  • NFT-backed loans;
  • transferable position NFTs;
  • isolated markets.

Alerts:

  • HF below threshold;
  • debt growth anomaly;
  • receipt token depeg from expected index;
  • collateral disabled by governance;
  • ownership change on position NFT.

4. Lifecycle tracker

Нужен state machine:

  • supplied;
  • borrowed;
  • repaid partially;
  • withdrawn;
  • liquidated;
  • transferred;
  • closed.

Для NFT/loanId positions это должно быть привязано к конкретному id, а не только к wallet.

5. Integration adapters

Если вы работаете с несколькими протоколами, adapter layer должен понимать:

  • Aave-style aToken / debtToken;
  • Compound-style exchange rate;
  • ERC-4626 share vault;
  • NFT collateral loan contract;
  • custom position NFT registry.

MVP: lending accounting monitor

Практичный MVP не требует собственного lending protocol.

Формулировка

Indexer + dashboard, который для выбранного протокола показывает normalized positions: receipt token value, debt value, health factor и ownership changes по NFT/loan id.

Минимальные компоненты

1. Indexer

Слушает:

  • Supply / Withdraw;
  • Borrow / Repay;
  • Liquidate;
  • Transfer receipt/debt token;
  • Mint / Transfer position NFT.

2. Valuation worker

Пересчитывает:

  • underlying from receipt token;
  • debt including index;
  • USD value via oracle feed.

3. Position registry

Хранит:

  • position_id;
  • owner;
  • protocol;
  • status;
  • last_health_factor.

4. Alert router

Шлёт:

  • HF warnings;
  • ownership transfer on position NFT;
  • debt growth above baseline;
  • collateral factor change from governance.

Стек:

  • Go для workers и API;
  • PostgreSQL для positions и events;
  • Redis для recalc queues;
  • onchain reads via RPC / indexer.

Что стоит запомнить

Lending в DeFi — это не только механика «положил залог → взял долг». На уровне контрактов это система учёта, где:

  • supply создаёт receipt token или share claim;
  • borrow создаёт debt record, часто тоже в виде token;
  • позиция может жить как account state, loanId или NFT tokenId;
  • interest accrual меняет стоимость claim и debt без новых пользовательских tx;
  • automation должен понимать все три слоя: tokens, position identity и risk.

Если упростить:

  • receipt token = «сколько мне должны»;
  • debt token = «сколько я должен»;
  • position id = «какой именно loan object мы мониторим и кто им владеет»;
  • NFT as position id = способ сделать позицию отдельным onchain-объектом, иногда даже transferable.

Для продуктовой команды, treasury или infrastructure builder это означает простую вещь: monitoring только wallet balances недостаточно. Нужен accounting-aware automation layer, который понимает, как протокол представляет депозит, долг и identity позиции.

Если нужен такой слой — position normalizer, valuation engine, NFT/loanId tracker или lending ops dashboard — можно обсудить архитектуру под ваш протокол или стек интеграций: свяжитесь со мной.