Эта страница переведена с помощью ИИ. За эталон принимается английская версия.Открыть английскую версию →
Когда CPI — правильный инструмент
Пользовательская программа имеет смысл, когда обмен должен произойти атомарно вместе с другими изменениями состояния на цепи, которые может выполнить только ваша программа. Типичные случаи:- Программы эскроу / лимитных ордеров — пользователь депонирует монету в ваше эскроу, ваша программа следит за условием цены, и при срабатывании программа атомарно обменивает через Raydium и зачисляет средства на счёт пользователя.
- Прокси-агрегаторы — единая инструкция, которая маршрутизирует обмен через Raydium плюс один или несколько других DEX с единой проверкой проскальзывания, контролируемой вашей программой.
- Автокомпаундинг-хранилища — депонируйте LP или долю в фарме в ваше хранилище, хранилище собирает награды по расписанию, пополняет ликвидность, выпускает токены долей.
- Стратегические хранилища — позиции LP с плечом, которые перебалансируются путём обмена через CLMM; ликвидаторы, которые закрывают позиции и обменивают залог в одной транзакции.
- Платформы запуска токенов с пользовательским вестингом — ваша программа держит токены вестинга и выпускает их в пул Raydium по расписанию.
Паттерны компоновки
Паттерн 1: тонкий прокси
Ваша программа предоставляет единую инструкцию, которая проверяет некоторую политику (например, монеты в белом списке, скидка комиссии для проверенных пользователей) и затем пересылает в Raydium.Паттерн 2: эскроу
Ваша программа владеет PDA, который держит входную монету пользователя. При срабатывании PDA подписывает CPI в Raydium для обмена собственного баланса.CpiContext::new_with_signer. См. Семена подписантов PDA.
Паттерн 3: составной многоэтапный обмен
Ваша программа выполняет несколько CPI в одной инструкции, обеспечивая единую границу проскальзывания для всех них. Инструкции обмена Raydium имеют свои собственныеminimum_amount_out, но вы устанавливаете их равными 0 (или очень свободному полу) и принудительно проверяете строгий финальный минимум сами после последнего этапа.
Паттерн 4: хранилище / стратегия
Ваша программа держит LP-токены или долю в фарме в PDA. Keeper (или пользователь) вызываетcompound(), что:
- Собирает награды из фарма.
- Обменивает награды на токены пула (CPI в CPMM или CLMM).
- Пополняет LP (ещё один CPI).
- Ставит новый LP (ещё один CPI).
Построение списка учётных записей
СтруктураAccounts вызывающей программы отражает порядок учётных записей программы Raydium, но большинство учётных записей на стороне Raydium — это UncheckedAccount, потому что Raydium валидирует их сама. Вы добавляете ограничения только на учётные записи, которыми владеете вы:
UncheckedAccount на стороне Raydium — это не лень. Получатель валидирует свои; двойная валидация на стороне вызывающего просто сжигает CU и рискует выйти из синхронизации, когда Raydium доставляет новое поле макета struct.
Сам CPI вызов
Семена подписантов PDA
CPI успешен только если PDA, переданный какauthority, совпадает с выводом, который заявляет вызывающий. Оба должны договориться о:
- Последовательности семян (здесь
[b"escrow", user.key().as_ref()]). - Bump.
- ID программы вызывающего (ваша программа, не Raydium).
authority покрывает транзакцию и что входная ATA принадлежит этому authority. Валидация происходит в anchor_spl::token::transfer: поле authority ATA должно равняться подписанту.
Типичная ошибка: передача user как authority (и передача из escrow_input_ata, который принадлежит PDA эскроу). Программа SPL Token отклоняет с owner mismatch. Всегда делайте поле authority совпадающим с владельцем ATA.
Оставшиеся учётные записи
Несколько инструкций Raydium берут список переменной длины учётных записей, добавленных после фиксированных — оставшиеся учётные записи.- CLMM
SwapV2: 1–8 учётных записейTickArrayStateдля массивов ticks, которые может пересечь обмен, в направлении обмена. - Farm v6
Deposit/Harvest/Withdraw: пары(reward_vault, user_reward_ata), одна пара на активный слот награды. - Монеты Token-2022 с transfer-hook: программа transfer-hook плюс все учётные записи, которые нужны хуку.
Бюджет вычислений для составных вызовов
CPI стоит ~1,500 CU для самого фрейма вызова; использование CU самого вызываемого складывается сверху. Примерный бюджет на CPI Raydium:| Вызов | CU (SPL Token) | CU (Token-2022) |
|---|---|---|
| CPMM swap_base_input | ~150,000 | ~200,000 |
| CLMM swap_v2 (single tick array) | ~180,000 | ~230,000 |
| CLMM swap_v2 (crosses 2 ticks) | ~220,000 | ~270,000 |
| Farm v6 deposit | ~120,000 | ~150,000 |
| Farm v6 harvest (per reward slot) | +30,000 | +40,000 |
| AMM v4 swap_base_in | ~140,000 | n/a |
harvest → swap A → swap B → deposit LP → stake LP, легко достигает 700k CU.
Всегда устанавливайте явный ComputeBudgetProgram::set_compute_unit_limit:
Распространение ошибок
Программы Raydium возвращают ошибки Anchor со стабильными кодами ошибок. Ваша вызывающая программа видит их какErr(ProgramError::Custom(code)). По умолчанию пробросьте:
sdk-api/anchor-idl); новые коды добавляются в конец, существующие коды никогда не меняют смысл.
Полный рабочий пример: эскроу лимитного ордера
Поток:open_order— пользователь депонируетamount_inвходнойinput_mintв PDA эскроу; записать целевойmin_amount_outи срок действия.execute_order— любой (keeper) вызывает с текущими учётными записями пула. Программа проверяет текущую цену ≥min_amount_out, затем выполняет CPI обмена Raydium и хранит вывод в эскроу.claim— пользователь снимает выходную монету из эскроу.
Тестирование
Вытягивание программ Raydium в локальный валидатор для интеграционных тестов (изAnchor.toml):
anchor test извлекает их из mainnet при запуске. См. sdk-api/rust-cpi.
Подводные камни, специфичные для компоновки
Reentrancy
Solana не имеет истинной reentrancy — CPI не может вызвать обратно в исходную программу в той же инвокации. Но вы всё ещё можете создать себе логическую reentrancy: CPI, который читает ваше состояние, затем ваш код читает его снова, предполагая, что CPI его не изменил. Для Raydium CPI не трогают ваше состояние, поэтому это менее проблемно, чем, например, flash-loan контексты. Но если вы комбинируете Raydium с протоколом кредитования, будьте осторожны.Дрейф mutability учётных записей
Если ваша программа передаёт учётную запись какmut, но Raydium ожидает её read-only (или наоборот), среда выполнения отклоняет инвокацию с InvalidAccountData. Всегда проверяйте ожидаемую mutability инструкции Raydium в IDL; anchor_cp_swap::cpi::accounts::Swap обеспечивает её через типы полей.
Поле программы Token-2022
Входная и выходная монеты могут быть под разными программами токенов — одна SPL Token, одна Token-2022. CPI имеет отдельные поляinput_token_program и output_token_program по этой причине. Всегда проверяйте поле owner каждой монеты и маршрутизируйте правильную программу в каждый слот.
Версионированные транзакции
Составная tx, выполняющая 2+ CPI Raydium плюс создание ATA, редко умещается в устаревшей (v0-без-LUT) транзакции. Используйте V0 с таблицами поиска адресов; извлеките публичные LUT Raydium черезraydium.getRaydiumLutAddresses().
Указатели
sdk-api/rust-cpi— низкоуровневая механика CPI.integration-guides/priority-fee-tuning— размер бюджета вычислений.products/cpmm/code-demos,products/clmm/code-demos,products/farm-staking/code-demos— фрагменты CPI для каждого продукта.

