RWT Engine
✅ Готов к разработке
Спецификация контракта прошла аудит бизнес-логики, техническую проверку, проверку кросс-программной интеграции и стандартизацию именования. Разработчик может реализовать контракт по этому документу.
Ключевые концепции
12 инструкций
Минт, административный минт, корректировка капитала, сбор доходности, свопы, управление authority/менеджером/конфигурацией, приостановка
2 стейт-аккаунта
RwtVault, RwtDistributionConfig
2 PDA-сида
rwt_vault, dist_config_rwt
Ценообразование NAV
total_invested_capital— сумма всех депозитов USDC + стоимость позиций OT (в эквиваленте USDC, 6 десятичных знаков)total_rwt_supply— общее количество RWT токенов в обращении- Начальный NAV = $1.00 (1,000,000 лампортов)
- NAV автоматически пересчитывается после каждой операции, изменяющей состояние (минт, сбор доходности, корректировка)
- Защита: если
total_rwt_supply == 0, NAV = INITIAL_NAV (предотвращает деление на ноль) - Защита:
adjust_capitalне может уменьшитьtotal_invested_capitalнижеMIN_CAPITAL_FLOOR(1 лампорт) для предотвращения NAV = 0 при supply > 0 - NAV может уменьшаться только через
adjust_capital(списание). Увеличение происходит от доходности (claim_yield) и комиссий минта - Рост NAV от комиссии минта: 0.5% комиссии хранилища от каждого минта идёт в аккумулятор капитала, увеличивая total_invested_capital, в то время как пользователь получает RWT, рассчитанный только от net_deposit. Это означает, что NAV немного растёт с каждым минтом — что выгодно существующим холдерам
- Нет выкупа/сжигания. RWT нельзя обменять обратно на USDC через контракт. Пользователи продают RWT на DEX. Это проектное решение — капитал хранилища остаётся задействованным
- Балансовая стоимость, а не рыночная. NAV отражает бухгалтерскую стоимость, а не рыночные цены в реальном времени.
vault_swapне изменяет total_invested_capital (своп нейтрален по стоимости в бухгалтерском учёте). Расхождение рыночной цены корректируется черезadjust_capital(списание), инициируемое управлением на основе офчейн-анализа цен
Цикл составного роста
Позиции OT приносят доходность
OT-токены, удерживаемые хранилищем, получают доходность через потоки Yield Distribution. Выручка OT (USDC) конвертируется в RWT контрактом YD (свопом на DEX + минт по NAV), затем распределяется как потоки RWT. Хранилище является OT-холдером, как и любой другой кошелёк.
Получение RWT из YD
Безразрешительный крэнк вызывает
claim_yield. PDA хранилища подписывает merkle-клейм → получает RWT.Разделение полученных RWT
70% остаётся в хранилище (рост NAV). 15% → ARL Treasury. 15% → Liquidity Nexus.Увеличение NAV:
total_invested_capital += book_value_share * nav_book_value / 1,000,000Три роли
🏛️ Authority
Team Multisig (после бутстрапа)
admin_mint_rwt— минт, обеспеченный OTadjust_capital— уменьшение NAVupdate_vault_manager— смена менеджераupdate_distribution_config— изменение разделения доходностиpropose_authority_transfer
🤖 Manager
Кошелёк AI-агента (автоматизированный торговый бот)
vault_swap— свопы любых токенов через DEX
update_vault_manager.🛑 Pause Authority
Team Multisig (Squads)
pause_mint— экстренная остановкаunpause_mint— возобновление
Инструкции
Инициализация
initialize_vault
initialize_vault
Создаёт хранилище RWT со всеми необходимыми аккаунтами. Вызывается один раз.Параметры:
Вызывающий: Деплоер (однократно)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
initial_authority | Pubkey | Начальный управляющий (деплоер, затем передаётся Team Multisig) |
pause_authority | Pubkey | Подписант экстренной приостановки (Team Multisig, неизменяемый) |
areal_fee_destination | Pubkey | Куда идёт 0.5% комиссии минта — Areal Finance (статический, неизменяемый) |
liquidity_destination | Pubkey | Куда идёт 15% доходности (RWT ATA крэнка — крэнк затем направляет в Nexus через nexus_deposit) |
protocol_revenue_destination | Pubkey | Куда идёт 15% доходности (ARL Treasury ATA) |
deployer(signer, mut) — оплачивает создание всех аккаунтовrwt_vault(init) — PDA seed:["rwt_vault"]dist_config(init) — PDA seed:["dist_config_rwt"]rwt_mint(init) — новый SPL token mint, authority = rwt_vault PDAcapital_accumulator_ata(init) — USDC ATA, authority = rwt_vault PDAusdc_mint(readonly) — адрес минта USDC (для создания Capital Accumulator ATA)token_program,system_program,associated_token_program
RwtVaultPDA — основное состояние хранилища, mint authority для RWTRwtDistributionConfigPDA — конфигурация разделения доходности- RWT SPL Token Mint — токен RWT (6 десятичных знаков), mint authority = RwtVault PDA
- Capital Accumulator ATA — USDC ATA, принадлежащий RwtVault PDA
nav_book_value = 1,000,000($1.00)total_invested_capital = 0total_rwt_supply = 0mint_paused = false- Распределение: 70% балансовая стоимость / 15% ликвидность / 15% протокольная выручка
Пользовательский минт
mint_rwt
mint_rwt
Пользователь вносит USDC и получает RWT по текущей цене NAV. Комиссия 1% делится: 0.5% остаётся в хранилище (увеличивает NAV), 0.5% идёт в areal_fee_destination (Areal Finance).
Вызывающий: Безразрешительный — любой может минтитьАккаунты:
| Parameter | Type | Description |
|---|---|---|
amount | u64 | Сумма USDC для депозита (6 десятичных знаков) |
user(signer) — депозиторrwt_vault(mut) — обновляет эмиссию, капитал, NAVrwt_mint(mut) — минт RWT, authority = vault PDAuser_deposit(mut) — USDC ATA пользователя, constraint:owner == user,mint == usdc_mint(захардкоженный минт USDC)user_rwt(mut) — RWT ATA пользователя, constraint:mint == vault.rwt_mintcapital_acc(mut) — USDC ATA хранилища, constraint:key == vault.capital_accumulator_atadao_fee_account(mut) — constraint:key == vault.areal_fee_destination
amount > 0mint_paused == false
- Расчёт комиссии:
fee_total = amount * 100 / 10,000(1%) dao_fee = fee_total / 2(0.5% в Areal Finance)vault_fee = fee_total - dao_fee(0.5% остаётся в хранилище)net_deposit = amount - fee_totalrwt_out = net_deposit * 1,000,000 / nav_book_value- Перевод
net_deposit + vault_fee→ capital_acc - Перевод
dao_fee→ dao_fee_account - Минт
rwt_outRWT пользователю (vault PDA подписывает как mint authority) - Обновление:
total_invested_capital += net_deposit + vault_fee - Обновление:
total_rwt_supply += rwt_out - Пересчёт NAV
- Эмит
RwtMinted
Операции Authority
admin_mint_rwt
admin_mint_rwt
Минт RWT, обеспеченного позициями OT, уже находящимися в хранилище. Депозит USDC не требуется — используется, когда хранилище приобретает OT другими способами (например, решение управления об обеспечении RWT существующими OT).
Вызывающий: Authority (Team Multisig)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
rwt_amount | u64 | Количество RWT-токенов для минта |
backing_capital_usd | u64 | USD-стоимость обеспечения (6 десятичных знаков) |
authority(signer) — должен совпадать сvault.authorityrwt_vault(mut)rwt_mint(mut) — constraint:key == vault.rwt_mintrecipient_rwt(mut) — constraint:mint == vault.rwt_mint
rwt_amount > 0backing_capital_usd > 0(первый admin_mint должен установить начальный NAV — предотвращает NAV = 0 при supply > 0)
RwtMinted.Первый минт: Когда
total_rwt_supply == 0, защита NAV возвращает INITIAL_NAV (1.00` для соответствия начальному NAV.adjust_capital
adjust_capital
Уменьшение total_invested_capital (списание, амортизация, признание убытков). NAV уменьшается пропорционально. Разрешены только уменьшения — ручное повышение невозможно.
Вызывающий: Authority (Team Multisig)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
writedown_amount | u64 | USDC-стоимость для вычитания из капитала (6 десятичных знаков) |
authority(signer) — должен совпадать сvault.authorityrwt_vault(mut)
writedown_amount > 0writedown_amount ≤ total_invested_capital - MIN_CAPITAL_FLOOR
total_invested_capital -= writedown_amount, пересчитывает NAV. Эмитит CapitalAdjusted.update_vault_manager
update_vault_manager
Смена кошелька менеджера, который может выполнять vault_swap.
Вызывающий: Authority (Team Multisig)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
new_manager | Pubkey | Новый кошелёк менеджера |
authority(signer) — должен совпадать сvault.authorityrwt_vault(mut)
vault.manager = new_manager. Эмитит VaultManagerUpdated.update_distribution_config
update_distribution_config
Изменение пропорций разделения доходности и/или адресов назначения.
Вызывающий: Authority (Team Multisig)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
book_value_bps | u16 | % роста NAV |
liquidity_bps | u16 | % в Nexus |
protocol_revenue_bps | u16 | % в ARL Treasury |
liquidity_destination | Pubkey | RWT ATA крэнка (крэнк направляет в Nexus через nexus_deposit) |
protocol_revenue_destination | Pubkey | RWT ATA ARL Treasury |
authority(signer) — должен совпадать сvault.authorityrwt_vault— для верификации authoritydist_config(mut) — перезаписывается новыми значениями
book_value_bps + liquidity_bps + protocol_revenue_bps == 10,000
DistributionConfigUpdated.Передача Authority
Двухэтапная передача управления. Тот же паттерн propose+accept, как в контракте OT, но authority хранится непосредственно в RwtVault (а не в отдельном PDA, как OtGovernance). Это связано с тем, что RwtVault — синглтон: хранилище только одно, поэтому отдельный PDA для управления не добавляет ценности.propose_authority_transfer
propose_authority_transfer
accept_authority_transfer
accept_authority_transfer
Операции менеджера
vault_swap
vault_swap
Свопы любых токенов хранилища через Native DEX. PDA хранилища подписывает как пользователь свопа. Менеджер определяет направление, сумму и допустимое проскальзывание.
Вызывающий: Только менеджерАккаунты:Валидация:
| Parameter | Type | Description |
|---|---|---|
amount_in | u64 | Сумма входного токена |
min_amount_out | u64 | Минимальный выход (защита от проскальзывания) |
a_to_b | bool | Направление свопа в пуле DEX |
manager(signer) — должен совпадать сvault.managerrwt_vault— PDA, подписывает CPI-свопvault_token_in(mut) — исходный ATA, constraint:owner == rwt_vault.key()vault_token_out(mut) — целевой ATA, constraint:owner == rwt_vault.key()- Аккаунты DEX CPI (все UncheckedAccount — валидируются программой DEX внутренне):
pool_state(mut) — DEX PoolState PDA для пары свопа["pool", token_a_mint, token_b_mint]dex_config— глобальная конфигурация DEX["dex_config"]vault_in(mut) — токен-аккаунт пула для входной стороны (authority = pool_state PDA)vault_out(mut) — токен-аккаунт пула для выходной стороны (authority = pool_state PDA)areal_fee_account(mut) —dex_config.areal_fee_destinationDEX (RWT ATA, получает протокольную комиссию)bin_array(mut, optional) — требуется только для Concentrated-пулов["bins", pool_state]
dex_program— constraint:key == DEX_PROGRAM_ID(захардкожен, предотвращает подмену программы)token_program
CPI-подпись: PDA хранилища подписывает своп DEX, используя сиды
[b"rwt_vault", &[vault.bump]]. Программа DEX видит rwt_vault как пользователя свопа. Порядок аккаунтов должен точно совпадать с инструкцией DEX swap — см. спецификацию Native DEX для канонической раскладки аккаунтов.amount_in > 0min_amount_out > 0(защита от проскальзывания обязательна)
native_dex::swap. PDA хранилища подписывает через сиды. Эмитит VaultSwapExecuted.Сценарии использования (каждый — один вызов vault_swap):- USDC → OT (инвестирование капитала в реальные активы)
- OT → USDC (выход из позиции)
- RWT → USDC (продажа полученной доходности)
- USDC → OT (покупка OT на полученные средства)
Сбор доходности
claim_yield
claim_yield
Получение RWT-вознаграждений из merkle-потока Yield Distribution с последующим разделением согласно конфигурации распределения. PDA хранилища подписывает CPI-клейм YD.
Вызывающий: Безразрешительный (крэнк)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
cumulative_amount | u64 | Кумулятивная сумма клейма (из листа merkle-дерева) |
proof | Vec<[u8; 32]> | Путь merkle-доказательства |
crank(signer, mut) — оплачивает инициализацию ClaimStatus при первом клеймеrwt_vault(mut) — подписывает CPI-клейм YD, обновляет капитал/NAVdist_config— чтение пропорций разделения доходностиrwt_claim_ata(mut) — RWT ATA, принадлежащий хранилищу (получает полученные токены)liquidity_dest(mut) — constraint:key == dist_config.liquidity_destinationprotocol_revenue_dest(mut) — constraint:key == dist_config.protocol_revenue_destination- Аккаунты YD CPI:
yd_distributor,yd_claim_status,yd_reward_vault(все UncheckedAccount) yield_distribution_program— constraint:key == YD_PROGRAM_ID(захардкожен, предотвращает подмену программы)token_program,system_program
- Снапшот
rwt_claim_ata.amountдо клейма - CPI →
yield_distribution::claim(cumulative_amount, proof)с PDA хранилища как клеймер - Перезагрузка
rwt_claim_ata, расчётclaimed = new_balance - old_balance - Если claimed == 0, возврат (нечего распределять)
- Расчёт долей (в токенах RWT):
book_value_share = claimed * book_value_bps / 10,000(остаётся в хранилище)liquidity_share = claimed * liquidity_bps / 10,000(→ liquidity_dest, затем направляется в Nexus черезnexus_deposit)protocol_revenue_share = claimed - book_value_share - liquidity_share(→ ARL Treasury, рассчитывается как остаток — не изprotocol_revenue_bps— для предотвращения потери пыли от целочисленного деления. Хранимыйprotocol_revenue_bpsиспользуется только для валидации вupdate_distribution_configи для отображения.)
- Перевод
liquidity_shareRWT → liquidity_dest (PDA хранилища подписывает) - Перевод
protocol_revenue_shareRWT → protocol_revenue_dest (PDA хранилища подписывает) - Конвертация book_value_share в USD:
usd_value = (book_value_share as u128) * (nav_book_value as u128) / 1,000,000(приведение к u128 перед умножением для предотвращения переполнения) total_invested_capital += usd_value- Пересчёт NAV
- Эмит
YieldDistributed
Экстренные операции
pause_mint
pause_mint
Экстренная приостановка — останавливает весь пользовательский минт. НЕ влияет на свопы менеджера, сбор доходности или операции authority.Вызывающий: Только Pause Authority (Team Multisig)Аккаунты:
pause_authority(signer) — должен совпадать сvault.pause_authorityrwt_vault(mut)
vault.mint_paused = true. Эмитит MintPauseToggled(true).unpause_mint
unpause_mint
Возобновление минта после экстренной приостановки.Вызывающий: Только Pause Authority (Team Multisig)Аккаунты:
pause_authority(signer) — должен совпадать сvault.pause_authorityrwt_vault(mut)
vault.mint_paused = false. Эмитит MintPauseToggled(false).Стейт-аккаунты
RwtVault
Основное состояние хранилища. Является mint authority для RWT SPL Mint. Подписывает CPI-свопы и YD-клеймы через PDA-сиды.| Field | Type | Description |
|---|---|---|
total_invested_capital | u128 | Общий капитал в эквиваленте USDC (6 десятичных знаков) |
total_rwt_supply | u64 | Общее количество RWT-токенов в обращении |
nav_book_value | u64 | NAV за RWT: capital * 1,000,000 / supply |
capital_accumulator_ata | Pubkey | USDC ATA, принадлежащий хранилищу (создаётся при инициализации) |
rwt_mint | Pubkey | Минт SPL-токена RWT |
authority | Pubkey | Управляющий (Team Multisig) |
pending_authority | Option<Pubkey> | Целевой адрес ожидающей передачи authority |
manager | Pubkey | Кошелёк менеджера (может выполнять vault_swap) |
pause_authority | Pubkey | Подписант экстренной приостановки (Team Multisig, неизменяемый) |
mint_paused | bool | Флаг приостановки минта |
areal_fee_destination | Pubkey | Куда идёт 0.5% комиссии минта (статический, неизменяемый) |
bump | u8 | Bump-сид PDA |
["rwt_vault"]
RwtVault не хранит балансы отдельных позиций OT. OT ATA создаются динамически, когда менеджер покупает OT. Токен-холдинги хранилища считываются из ончейн-балансов ATA при запросе.
RwtDistributionConfig
Настраивает разделение полученной доходности RWT.| Field | Type | Description |
|---|---|---|
book_value_bps | u16 | % остающийся в хранилище для роста NAV (по умолчанию: 7,000 = 70%) |
liquidity_bps | u16 | % отправляемый в Liquidity Nexus (по умолчанию: 1,500 = 15%) |
protocol_revenue_bps | u16 | % отправляемый в ARL Treasury (по умолчанию: 1,500 = 15%) |
liquidity_destination | Pubkey | RWT ATA крэнка (крэнк направляет в Nexus через nexus_deposit для отслеживания основного долга) |
protocol_revenue_destination | Pubkey | Адрес RWT ATA ARL Treasury |
bump | u8 | Bump-сид PDA |
["dist_config_rwt"]
BPS всегда должны суммироваться до 10,000.
update_distribution_config валидирует это.PDA-сиды
| Account | Seeds | Description |
|---|---|---|
| RwtVault | "rwt_vault" | Основное состояние хранилища, mint authority для RWT |
| RwtDistributionConfig | "dist_config_rwt" | Конфигурация разделения доходности |
Оба PDA являются глобальными синглтонами (без деривации по проекту). Существует ровно одно хранилище RWT на деплоймент протокола.
Константы
| Constant | Value | Description |
|---|---|---|
BPS_DENOMINATOR | 10,000 | 100% в базисных пунктах |
MINT_FEE_BPS | 100 | 1% общая комиссия на пользовательский минт |
DEFAULT_BOOK_VALUE_BPS | 7,000 | 70% доходности → рост NAV |
DEFAULT_LIQUIDITY_BPS | 1,500 | 15% доходности → Liquidity Nexus |
DEFAULT_PROTOCOL_REVENUE_BPS | 1,500 | 15% доходности → ARL Treasury |
INITIAL_NAV | 1,000,000 | $1.00 в лампортах USDC (6 десятичных знаков) |
MIN_CAPITAL_FLOOR | 1 | Минимальный total_invested_capital (1 лампорт, предотвращает NAV = 0) |
RWT_DECIMALS | 6 | Десятичные знаки токена RWT (совпадает с USDC) |
YD_PROGRAM_ID | hardcoded | ID программы Yield Distribution (валидируется в claim_yield) |
DEX_PROGRAM_ID | hardcoded | ID программы Native DEX (валидируется в vault_swap) |
События
| Event | Fields | When |
|---|---|---|
VaultInitialized | authority, rwt_mint, nav, timestamp | Хранилище создано |
RwtMinted | user, deposit_amount, rwt_amount, fee_vault, fee_dao, nav_after, is_admin, timestamp | Пользователь или админ минтит RWT (is_admin различает оба случая) |
YieldDistributed | total_yield, book_value_share, liquidity_share, protocol_revenue_share, nav_after, timestamp | Доходность получена и распределена |
CapitalAdjusted | old_capital, new_capital, writedown_amount, old_nav, new_nav, timestamp | Списание NAV |
VaultSwapExecuted | token_in_mint, token_out_mint, amount_in, amount_out, timestamp | Менеджер выполнил своп |
VaultManagerUpdated | old_manager, new_manager, timestamp | Менеджер изменён |
DistributionConfigUpdated | book_value_bps, liquidity_bps, protocol_revenue_bps, timestamp | Разделение доходности изменено |
AuthorityTransferProposed | current_authority, pending_authority, timestamp | Передача предложена |
AuthorityTransferAccepted | old_authority, new_authority, timestamp | Передача принята |
MintPauseToggled | paused, timestamp | Состояние приостановки изменено |
Коды ошибок
| Error | Description |
|---|---|
Unauthorized | Подписант не является требуемым authority/менеджером/pause_authority |
MintPaused | Минт приостановлен (экстренная ситуация) |
ZeroAmount | Сумма должна быть > 0 |
ZeroSlippage | min_amount_out должен быть > 0 (защита от проскальзывания обязательна) |
MathOverflow | Арифметическое переполнение |
InsufficientCapital | Списание уменьшит капитал ниже MIN_CAPITAL_FLOOR |
InvalidDistributionRatios | BPS не суммируются до 10,000 |
SelfTransfer | Нельзя передать authority самому себе |
NoPendingAuthority | Нет ожидающей передачи authority для принятия |
InvalidPendingAuthority | Подписант ≠ pending_authority |
Архитектура и руководство по интеграции
Кросс-программная интеграция
→ Yield Distribution (получение доходности RWT)
→ Yield Distribution (получение доходности RWT)
- Выручка OT (USDC) поступает в YD → YD конвертирует USDC в RWT (своп + минт) → создаёт merkle-потоки
claim_yieldделает CPI кyield_distribution::claimдля получения RWT- PDA хранилища является листом в merkle-дереве YD (удерживает OT-токены = имеет право на доходность)
- Полученные RWT разделяются: 70% NAV / 15% Nexus / 15% ARL Treasury
- Крэнк должен предоставить валидное merkle-доказательство из последнего опубликованного корня
→ Native DEX (свопы менеджера)
→ Native DEX (свопы менеджера)
vault_swapделает CPI кnative_dex::swap- PDA хранилища подписывает как пользователь свопа
- Менеджер контролирует направление и проскальзывание
- Поддерживает любой пул: RWT/USDC, OT/RWT, OT/USDC
→ Ownership Token (удержание позиций OT)
→ Ownership Token (удержание позиций OT)
- Хранилище покупает OT через свопы DEX
- Баланс OT в хранилище вносит вклад в NAV
- Больше OT = больше доходности от YD = составной рост
→ Team Multisig (протокольный authority)
→ Team Multisig (протокольный authority)
Допущения о доверии
Чеклист деплоя
Пререквизиты: Контракт OT должен быть задеплоен первым (предоставляет адрес OT Treasury). Nexus управляется командой отдельно.- Вызов
initialize_vaultс параметрами:pause_authority= адрес Team Multisigareal_fee_destination= USDC ATA Areal Finance (тот же адрес, что иareal_fee_destinationконтракта OT)liquidity_destination= RWT ATA кошелька крэнка (крэнк направляет в Nexus черезnexus_deposit)protocol_revenue_destination= RWT ATA ARL Treasury (контракт OT)
- Административный минт начальных RWT через
admin_mint_rwt, обеспеченных начальными позициями OT - Передача authority в Team Multisig через
propose_authority_transfer+accept_authority_transfer
Authority RWT Engine = Team Multisig (протокольные операции). RWT — это актив протокольного уровня, управляемый командой напрямую, а не через предложения Futarchy.
Сводка потоков токенов
| From | To | Mechanism | Who triggers |
|---|---|---|---|
| USDC пользователя | Capital Accumulator ATA (99.5%) | mint_rwt (net_deposit + vault_fee) | Пользователь |
| USDC пользователя | Areal Fee (0.5%) | mint_rwt (dao_fee) | Пользователь |
| Vault PDA | RWT ATA пользователя | mint_rwt (минтит RWT) | Пользователь |
| YD Stream | Vault RWT Claim ATA | claim_yield (CPI claim) | Крэнк |
| Vault RWT | RWT ATA крэнка (15%) → Nexus через nexus_deposit | claim_yield + nexus_deposit | Крэнк |
| Vault RWT | ARL Treasury ATA (15%) | claim_yield | Крэнк |
| Vault any token | Vault any token | vault_swap (CPI DEX) | Менеджер |
| Authority | Vault state | admin_mint_rwt, adjust_capital | Team Multisig |