Futarchy
✅ Готов к разработке
Спецификация контракта прошла аудит бизнес-логики, проверку межпрограммной интеграции и верификацию модели полномочий. Разработчик может вести реализацию по этому документу.
Ключевые концепции
8 инструкций
Инициализация, создание/одобрение/отмена/исполнение предложения, принятие OT-управления, передача полномочий
2 стейт-аккаунта
FutarchyConfig, Proposal
2 PDA-сида
futarchy_config, proposal
Чем управляют держатели
🪙 Выпуск OT
Создание новых OT-токенов для проекта.CPI →
ownership_token::mint_otИспользуется для: привлечения капитала, вознаграждения контрибьюторов, увеличения предложения.💰 Расходование казны
Перевод любого токена из OT Treasury на любой адрес назначения.CPI →
ownership_token::spend_treasuryИспользуется для: финансирования операций, оплаты расходов, инвестирования.📊 Обновление распределения доходов
Изменение распределения доходов между держателями, казной и Nexus.CPI →
ownership_token::batch_update_destinationsИспользуется для: корректировки экономики протокола.Все OT-проекты равноправны. ARL (Areal Finance) использует тот же контракт Futarchy, что и RCP (Rental Car Pool) или любой другой OT-проект. Никакого особого отношения. Единственное различие — что представляет OT; механика управления идентична.
V1 vs V2
V1: Одобрение авторитетом
Текущая реализация.
- Авторитет (кошелёк команды) создаёт и одобряет предложения
- Единственный подписант = мгновенное одобрение
- Просто и быстро для ранней стадии
- Полномочия передаются Team Multisig после начального этапа
V2: Рынки предсказаний (будущее)
Планируемое обновление.
- Держатели OT создают предложения
- Рынок предсказаний Futarchy определяет: «Увеличит ли это предложение стоимость OT?»
- Принятие решений на основе рынка
- Нет единого авторитета — децентрализованное управление
Инструкции
Инициализация
initialize_futarchy
initialize_futarchy
Создание управления для OT-проекта. Вызывается один раз для каждого OT.
Вызывающий: Деплоер (однократно для каждого OT)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
ot_mint | Pubkey | OT, которым управляет данный экземпляр |
deployer(signer, mut) — оплачивает созданиеot_mint— минт OT-токенаconfig(init) — PDA seed:["futarchy_config", ot_mint]system_program
FutarchyConfigPDA — конфигурация управления для данного OT
authority = deployer(передаётся Team Multisig после начального этапа)ot_mint = ot_mintnext_proposal_id = 0is_active = true
Предложения
create_proposal
create_proposal
Создание нового предложения для управления. V1: создаёт авторитет. V2: любой держатель OT выше порога.
Вызывающий: Авторитет (V1) / Держатель OT выше порога (V2)Аккаунты:
| Parameter | Type | Description |
|---|---|---|
proposal_type | ProposalType | MintOt, SpendTreasury или UpdateDestinations |
amount | u64 | Сумма для выпуска/расходования (0 для UpdateDestinations) |
destination | Pubkey | Получатель для выпуска/расходования, или игнорируется для UpdateDestinations |
token_mint | Pubkey | Минт токена для SpendTreasury (какой токен расходовать) |
params_hash | [u8; 32] | Хеш полных параметров (для UpdateDestinations: хеш массива новых направлений) |
proposer(signer, mut) — оплачивает аккаунт Proposalconfig— проверяет is_activeproposal(init) — PDA seed:["proposal", config, proposal_id]system_program
- Config должен быть активен
- V1: proposer == config.authority
- V2 (будущее): proposer владеет >= threshold OT-токенов (порог TBD — вероятно настраиваемый в FutarchyConfig, например
min_proposal_ot_balance: u64, устанавливается авторитетом) - Для
MintOt/SpendTreasury:amount > 0(запрещены предложения с нулевой суммой) - Для
SpendTreasury/MintOt:destination ≠ Pubkey::default()(запрещены получатели с нулевым адресом) - Для
UpdateDestinations:params_hash ≠ [0; 32](проверка на пустой хеш)
ProposalCreated.Ограничение частоты V1: Не требуется — только авторитет может создавать предложения, что обеспечивает неявный контроль частоты. Ограничение частоты V2 (будущее): Когда любой держатель сможет предлагать, необходимо добавить кулдаун на предлагающего (например, 1 предложение в 24ч на кошелёк) и депозит за предложение (возвращаемый при исполнении/отмене) для предотвращения спама. Это должно быть добавлено при реализации V2.
approve_proposal
approve_proposal
Одобрение предложения. V1: авторитет подписывает. V2: рынок предсказаний определяет.Вызывающий: Авторитет (V1) / Автоматически через разрешение рынка (V2)Аккаунты:
authority(signer) — должен совпадать сconfig.authority(V1)configproposal(mut) — должен иметь статус Active
proposal.status == Active
proposal.status = Approved. Генерирует событие ProposalApproved.cancel_proposal
cancel_proposal
Отмена активного предложения. Только авторитет может отменять. Нельзя отменить уже одобренные или исполненные предложения.Вызывающий: Авторитет (V1)Аккаунты:
authority(signer) — должен совпадать сconfig.authorityconfigproposal(mut) — должен иметь статус Active
proposal.status == Active
proposal.status = Cancelled. Генерирует событие ProposalCancelled.execute_proposal
execute_proposal
Исполнение одобренного предложения. Безразрешительное — кто угодно может запустить исполнение после одобрения. CPI к контракту OT в зависимости от proposal_type.Вызывающий: Безразрешительное (Permissionless)Аккаунты:Логика по ProposalType:MintOt:
Построение хеша: Валидация:
executor(signer, mut) — оплачивает комиссии CPIconfigproposal(mut) — должен иметь статус Approved- Аккаунты контракта OT (зависят от proposal_type — порядок должен соответствовать layout инструкции OT):
- MintOt:
ot_governance(OT governance PDA["ot_governance", ot_mint]),ot_config(mut,["ot_config", ot_mint]),ot_mint(mut),recipient_token_account(mut, ATAproposal.destinationдля OT mint),recipient(proposal.destination),payer(signer, оплачивает инициализацию ATA при необходимости — может быть executor) - SpendTreasury:
ot_governance(["ot_governance", ot_mint]),ot_treasury(["ot_treasury", ot_mint]),treasury_token_account(mut, Treasury ATA дляproposal.token_mint),destination_token_account(mut, ATA по адресуproposal.destination— должен существовать),token_mint(proposal.token_mint) - UpdateDestinations:
ot_governance(["ot_governance", ot_mint]),revenue_config(mut,["revenue_config", ot_mint]),ot_mint, плюсremaining_accounts[0](сериализованныйVec<BatchDestination>)
- MintOt:
ot_program— constraint:key == OT_PROGRAM_IDtoken_program,system_program
Руководство для исполнителя: Все адреса OT PDA выводятся из
config.ot_mint. Исполнитель читает поля предложения (amount, destination, token_mint, proposal_type) и выводит необходимые аккаунты. Для UpdateDestinations исполнитель также должен получить сериализованные данные направлений, соответствующие proposal.params_hash (из офчейн-источника, опубликованного предлагающим).- CPI →
ownership_token::mint_ot(proposal.amount) - Futarchy config PDA подписывает как авторитет OT governance
- Новые OT-токены выпускаются на
proposal.destination
- CPI →
ownership_token::spend_treasury(proposal.amount) - Futarchy config PDA подписывает как авторитет OT governance
- Токены переводятся из OT Treasury на
proposal.destination proposal.token_mintопределяет, какой токен расходовать
- Исполнитель передаёт полный массив направлений через
remaining_accounts - Десериализация:
- Проверка хеша:
sha256(remaining_accounts[0].data) == proposal.params_hash— гарантирует, что исполнитель передал именно те направления, которые были предложены - CPI →
ownership_token::batch_update_destinations(destinations)с направлениями в качестве параметра инструкции - Futarchy config PDA подписывает как авторитет OT governance через seeds
[b"futarchy_config", config.ot_mint.as_ref(), &[config.bump]]
BatchDestination (определена в контракте OT, используется здесь для сериализации):| Field | Type | Description |
|---|---|---|
address | Pubkey | Целевой USDC-токен аккаунт |
allocation_bps | u16 | Аллокация в базисных пунктах (1-10 000) |
label | [u8; 32] | Человекочитаемая метка |
params_hash = sha256(borsh_serialize(destinations_vec)) где destinations_vec: Vec<BatchDestination>. Предлагающий вычисляет хеш офчейн при создании предложения; исполнитель должен передать идентичные сериализованные данные в remaining_accounts[0].Рабочий процесс исполнителя: Предлагающий публикует
params_hash и полные данные направлений офчейн (например, IPFS или API). Исполнитель скачивает данные, передаёт как remaining_accounts[0], контракт проверяет совпадение хешей перед CPI. При несовпадении хешей → откат.proposal.status == Approved- Предложение ещё не исполнено
proposal.status = Executed, executed_ts = now. Генерирует событие ProposalExecuted.Принятие OT Governance
claim_ot_governance
claim_ot_governance
Принятие полномочий OT governance от имени Futarchy config PDA. Вызывается во время деплоя после того, как OT предложил передачу полномочий этому экземпляру Futarchy. Безразрешительное — кто угодно может запустить, когда OT уже предложил передачу.Вызывающий: Безразрешительное (Permissionless)Аккаунты:
executor(signer, mut)config— Futarchy config PDA (подписывает CPI как новый авторитет OT)ot_governance(mut) — аккаунт OT governance (pending_authority должен == config PDA)ot_mint— для вывода PDAot_program— constraint:key == OT_PROGRAM_ID
ot_governance.pending_authority == config.key()(OT должен был предложить именно этот Futarchy)
ownership_token::accept_authority_transfer. Futarchy config PDA подписывает как new_authority. Авторитет OT governance теперь = Futarchy config PDA. Генерирует событие OtGovernanceClaimed.Передача полномочий
Двухшаговая передача полномочий управления. Стандартный паттерн для всех контрактов.propose_authority_transfer
propose_authority_transfer
accept_authority_transfer
accept_authority_transfer
Стейт-аккаунты
FutarchyConfig
Конфигурация управления для конкретного OT. Один на каждый OT-проект.| Field | Type | Description |
|---|---|---|
ot_mint | Pubkey | OT, которым управляет данный экземпляр |
authority | Pubkey | Текущий авторитет управления (кошелёк команды V1, PDA рынка V2) |
pending_authority | Option<Pubkey> | Цель ожидающей передачи полномочий |
next_proposal_id | u64 | Автоинкрементируемый счётчик предложений (также = общее число созданных предложений) |
is_active | bool | Флаг активности управления |
bump | u8 | Bump seed PDA |
["futarchy_config", ot_mint]
FutarchyConfig PDA устанавливается как
ot_governance.authority в контракте OT. Именно так Futarchy контролирует операции OT — подписывая как авторитет OT governance через CPI.Proposal
Отдельное предложение для управления. Создаётся для каждого действия.| Field | Type | Description |
|---|---|---|
proposal_id | u64 | Уникальный ID в рамках данного экземпляра Futarchy |
ot_mint | Pubkey | OT-проект |
proposer | Pubkey | Кто создал это предложение |
proposal_type | ProposalType | MintOt, SpendTreasury или UpdateDestinations |
amount | u64 | Сумма для выпуска/расходования (зависит от контекста) |
destination | Pubkey | Получатель для выпуска/расходования |
token_mint | Pubkey | Минт токена для SpendTreasury |
params_hash | [u8; 32] | Хеш сериализованных параметров (для UpdateDestinations) |
status | ProposalStatus | Active, Approved, Executed, Cancelled |
created_ts | i64 | Временная метка создания |
executed_ts | i64 | Временная метка исполнения (0 если не исполнено) |
bump | u8 | Bump seed PDA |
["proposal", futarchy_config, proposal_id]
Enum ProposalType:
MintOt— выпуск новых OT-токеновSpendTreasury— расходование из OT TreasuryUpdateDestinations— изменение распределения доходов
Active— создано, ожидает одобренияApproved— одобрено, готово к исполнениюExecuted— исполнено успешноCancelled— отменено авторитетом черезcancel_proposal(V1), или рынок определил «против» (V2)
PDA Seeds
| Account | Seeds | Description |
|---|---|---|
| FutarchyConfig | "futarchy_config", ot_mint | Конфигурация управления для конкретного OT |
| Proposal | "proposal", futarchy_config, proposal_id | Отдельное предложение |
Константы
| Constant | Value | Description |
|---|---|---|
OT_PROGRAM_ID | hardcoded | ID программы Ownership Token (валидируется в execute_proposal) |
События
| Event | Fields | When |
|---|---|---|
FutarchyInitialized | ot_mint, authority, timestamp | Управление создано |
ProposalCreated | proposal_id, ot_mint, proposer, proposal_type, amount, destination, timestamp | Предложение создано |
ProposalApproved | proposal_id, approver, timestamp | Предложение одобрено |
ProposalCancelled | proposal_id, authority, timestamp | Предложение отменено |
ProposalExecuted | proposal_id, proposal_type, executor, timestamp | Предложение исполнено через CPI |
OtGovernanceClaimed | ot_mint, futarchy_config, timestamp | Полномочия OT governance приняты |
AuthorityTransferProposed | current_authority, pending_authority, timestamp | Передача полномочий предложена |
AuthorityTransferAccepted | old_authority, new_authority, timestamp | Передача полномочий принята |
Коды ошибок
| Error | Description |
|---|---|
Unauthorized | Подписант не является авторитетом |
GovernancePaused | Config is_active = false |
ProposalNotActive | Статус предложения ≠ Active (для одобрения) |
ProposalNotApproved | Статус предложения ≠ Approved (для исполнения) |
AlreadyExecuted | Предложение уже исполнено |
ProposalCancelled | Предложение было отменено (для исполнения) |
InvalidProposalType | Неизвестный тип предложения |
MathOverflow | Арифметическое переполнение |
SelfTransfer | Нельзя передать полномочия самому себе |
NoPendingAuthority | Нет ожидающей передачи |
InvalidPendingAuthority | Подписант ≠ pending_authority |
Архитектура и руководство по интеграции
Межпрограммная интеграция
→ Ownership Token (исполнение предложений через CPI)
→ Ownership Token (исполнение предложений через CPI)
- Futarchy config PDA =
ot_governance.authorityв контракте OT execute_proposalвыполняет CPI к OT:mint_ot,spend_treasury,batch_update_destinations- Config PDA подписывает как авторитет OT governance
OT_PROGRAM_IDзахардкожен и валидируется
Как Futarchy связан с OT
execute_proposal выполняет CPI к OT, Futarchy config PDA подписывает через seeds — контракт OT валидирует signer == ot_governance.authority. ✓
Модель полномочий
🔧 Обновление программы
Team Multisig (Squads)Может деплоить новые версии контракта.
🏛️ Авторитет управления
Кошелёк команды (V1) / Рынок предсказаний (V2)
create_proposalapprove_proposalpropose_authority_transfer
execute_proposal является безразрешительным — после одобрения кто угодно может запустить исполнение. Это гарантирует, что предложения не могут быть заблокированы после одобрения.Что команда контролирует напрямую (НЕ через Futarchy)
Эти операции протокола управляются кошельками команды напрямую, НЕ через предложения Futarchy:- DEX: конфигурация, создатели пулов, ребалансировщик — через
dex_config.authority(Team Multisig) - RWT Engine: менеджер, конфигурация распределения, административный выпуск, корректировки капитала — через
vault.authority(Team Multisig) - Yield Distribution: конфигурация, полномочия публикации, дистрибьюторы — через
config.authority(Team Multisig) - Liquidity Nexus: управляется напрямую командой (кошелёк менеджера Nexus)
- Pool Rebalancer: конфигурация — через
config.authority(Team Multisig) - Экстренная пауза: все контракты — через
pause_authority(Team Multisig)
Futarchy управляет решениями на уровне проекта OT (выпуск, расходование, распределение). Инфраструктура на уровне протокола управляется командой до тех пор, пока децентрализованное управление (V2) не станет достаточно зрелым для этого.
Чеклист деплоя
- Задеплоить контракт OT и выполнить
initialize_otдля проекта - Вызвать
initialize_futarchyдля OT - Передать OT governance на Futarchy config PDA:
- OT:
propose_authority_transfer(futarchy_config_pda)(деплоер подписывает) - Futarchy:
claim_ot_governance(безразрешительное — Futarchy config PDA принимает через CPI)
- OT:
- Передать полномочия Futarchy на Team Multisig через
propose_authority_transfer+accept_authority_transfer
Сводка потоков токенов
| From | To | Mechanism | Who triggers |
|---|---|---|---|
| — | OT mint → получатель | execute_proposal (MintOt CPI) | Кто угодно (безразрешительное) |
| OT Treasury | адрес назначения | execute_proposal (SpendTreasury CPI) | Кто угодно (безразрешительное) |
| — | OT RevenueConfig | execute_proposal (UpdateDestinations CPI) | Кто угодно (безразрешительное) |