Эта страница переведена с помощью ИИ. За эталон принимается английская версия.Открыть английскую версию →
PDA (адреса, производные от программы) и CPI (кросс-программные вызовы) — два примитива, которые делают возможным Raydium. PDA позволяют программе «владеть» детерминированными адресами без приватных ключей — так работают полномочия пула и хранилища. CPI позволяют одной программе вызывать другую — так Raydium обменивает токены через программу SPL Token и как интеграторы встраивают Raydium в свои потоки. Оба стоит понять перед чтением исходного кода Raydium.
PDA: адреса без ключей
Program-Derived Address — это открытый ключ, который:- Не лежит на кривой ed25519 (для него не существует приватного ключа).
- Производится детерминированно из ID программы и набора seeds.
- Может быть подписан только программой-производителем через
invoke_signed.
Производство
PDA вычисляется путём хеширования ID программы с seeds, затем поиска байта «bump», который выводит результат за пределы кривой. Первый bump (обычно начиная с 255 и уменьшаясь), который производит off-curve адрес, побеждает; это канонический bump.u64 как little-endian байты. Соглашение Raydium — читаемый префикс, за которым следуют уникальные идентификаторы.
Паттерны PDA в Raydium
Распространённые PDA в программах Raydium:
Пользователи и интеграторы могут вычислить эти адреса без выборки чего-либо — учитывая открытые входные данные (ID пула, ID фермы, ключ пользователя), PDA детерминирован.
Канонический bump
Хотя в принципе может быть несколько bumps, производящих off-curve адреса, программы Raydium всегда используют канонический bump (найденный путём уменьшения от 255). Он хранится в данных аккаунта PDA, чтобы последующие транзакции могли передать его и пропустить (дорогостоящий) цикл производства:PoolState вместо этого хранит bump: [u8; 1], поэтому проверьте структуру конкретной программы, а не предполагайте одну форму.)
В последующих транзакциях bump читается из состояния пула, а не пересчитывается.
CPI: вызов других программ
Cross-Program Invocation позволяет программе вызывать инструкции другой программы встроенно в рамках одной транзакции. Raydium использует CPI широко:- Инструкции swap вызывают программу SPL Token для перемещения токенов.
- CLMM вызывает Metaplex для минтинга позиционного NFT.
- Создание пула вызывает System Program для выделения аккаунтов.
- Farm v6 вызывает SPL Token для передачи вознаграждений.
integration-guides/cpi-integration.
invoke vs invoke_signed
Среда выполнения Solana предлагает два примитива CPI:invoke: вызвать другую программу; вызванная программа наследует подписантов внешней транзакции.invoke_signed: вызвать другую программу от имени PDA; среда выполнения проверяет seeds PDA и авторизует подпись.
invoke_signed — это магия, которая позволяет программам держать полномочия над аккаунтами без управления приватными ключами.
Пример: Raydium передаёт из хранилища пула
Хранилище пула — это Token Account, полномочия которого — PDA программы пула. Для передачи токенов во время swap программа пула должна подписать как этот PDA:invoke_signed вызывается программой CPMM, проверяет, что vault_and_lp_mint_auth_seed + bump производит адрес pool_authority при хешировании с ID программы CPMM, и разрешает подпись полномочий на передачу токена. Никаких приватных ключей.
Пример: интегратор вызывает Raydium CPMM
Программа интегратора (например, escrow) может вызватьswap_base_input Raydium через CPI:
integration-guides/cpi-integration для полного примера escrow.
Лимит глубины CPI
Solana ограничивает глубину CPI на 4 уровня. Инструкция верхнего уровня транзакции считается глубиной 0; каждый вызов CPI увеличивает глубину. Практическое следствие: собственный swap Raydium уже использует 1-2 уровня CPI (Raydium → SPL Token). Интегратор, вызывающий Raydium, использует 2. Если этот интегратор вызывается другим интегратором, это 3. 4-й уровень — лимит. Большинство композиций легко остаются под этим, но глубокая вложенность (aggregator → router → Raydium → hook) может его достичь. Проектируйте плоско, а не глубоко.Оставшиеся аккаунты
Когда инструкция Raydium нуждается в переменном количестве аккаунтов (например, CLMM swap, пересекающий неизвестное количество tick arrays), дополнительные аккаунты передаются как оставшиеся аккаунты — добавляются к списку фиксированных аккаунтов, интерпретируются по позиции.SwapV2 CPMM использует оставшиеся аккаунты для дополнительных требуемых аккаунтов программ transfer-hook. Клиенты выбирают нужные аккаунты и добавляют их:
Подводные камни PDA
Неправильные seeds → неправильный адрес
Ошибка, когда seeds в неправильном порядке, неправильной кодировке или включают/исключают дополнительный байт, молча производит другой PDA. Транзакция не выполняется неоднозначно (программа пытается прочитать аккаунт, который не существует). Всегда unit-тестируйте производство seed против известных золотых значений.Не сохранение bump
Если вы пересчитываете bump на каждой транзакции, вы платите compute за цикл производства. Сохраняйте канонический bump в данных PDA и читайте его оттуда.Путаница между каноническим и неканоническим bump
Неканонические bumps (если кто-то найдёт один, который даёт off-curve) разрешеныinvoke_signed, но отклонены программами Raydium через assert_eq!(bump, canonical_bump). Если кто-то попытается заявить PDA с неканоническим bump, tx не выполняется.
Передача PDA как подписанта, когда вы не владеющая программа
Только программа, чей ID находится в производстве PDA, можетinvoke_signed с его seeds. Если вы попытаетесь, среда выполнения отклонит.
Подводные камни CPI
Забывание пересылки remaining_accounts
Если ваша внешняя инструкция передаёт аккаунты transfer-hook в remaining_accounts, но CPI в Raydium их не пересылает, Raydium не выполняется, потому что не может найти аккаунты hook. Всегда включайте with_remaining_accounts в CPI, которые их нуждаются.
Несоответствие флагов writable
Аккаунт, который внешняя инструкция отмечает как writable, должен также быть writable в вызове CPI, если вызванная программа намеревается его писать. Несоответствие → отклонение среды выполнения.Не учёт rent
CPI к программе, которая создаёт аккаунт (например, создание ATA), требует, чтобы payer имел достаточно SOL для rent. Неудачные проверки rent появляются как неясные ошибки.Рабочий пример: вычисление PDA Raydium CPMM
getPoolInfoFromRpc({ poolId }) — он производит связанные PDA без round-trip.
Указатели
solana-fundamentals/account-model— как PDA вписываются в модель аккаунтов.solana-fundamentals/programs-and-anchor— помощники Anchor для объявления PDA.integration-guides/cpi-integration— построение интеграций, которые CPI в Raydium.sdk-api/rust-cpi— типы Rust CPI Raydium.
- Solana PDA docs.
- Solana CPI docs.
- Anchor PDA docs и Anchor CPI docs — у Anchor нет объединённой страницы.

